( rawCode: string, actions: Pick<Action, "name">[], )
| 99 | } |
| 100 | |
| 101 | export function parseGeneratedCode( |
| 102 | rawCode: string, |
| 103 | actions: Pick<Action, "name">[], |
| 104 | ): { code: string } | { error: string } | null { |
| 105 | /** Code output means the code is valid |
| 106 | * Error output is an error message to be shown to the AI |
| 107 | * null output means that you need to retry **/ |
| 108 | // Check if it's just an error |
| 109 | const errorMatch = /^\n?throw new Error\((.*)\);?$/.exec(rawCode); |
| 110 | if (errorMatch) { |
| 111 | // slice(1, -1) removes the quotes from the error message |
| 112 | return { error: errorMatch[1].slice(1, -1) }; |
| 113 | } |
| 114 | |
| 115 | // Remove comments (stops false positives from comments containing illegal stuff) & convert from TS to JS |
| 116 | let code = stripBasicTypescriptTypes( |
| 117 | rawCode |
| 118 | .replace(/^\s*(\/\/.*|\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\/)/gm, "") |
| 119 | .trim(), |
| 120 | ); |
| 121 | if (code === "") { |
| 122 | console.error("ERROR: No code (possibly comments) in code string"); |
| 123 | return null; |
| 124 | } |
| 125 | |
| 126 | // Check that fetch(), eval(), new Function() and WebAssembly aren't used |
| 127 | const illegalRegexes = [ |
| 128 | /fetch\([\w\W]*\)/g, |
| 129 | /eval\([\w\W]*\)/g, |
| 130 | /new Function\([\w\W]*\)/g, |
| 131 | /WebAssembly\./g, |
| 132 | ]; |
| 133 | for (const regex of illegalRegexes) { |
| 134 | if (regex.test(code)) { |
| 135 | const error = `ERROR: Illegal code found by ${String( |
| 136 | regex, |
| 137 | )}:\n---\n${code}\n---`; |
| 138 | console.error(error); |
| 139 | return null; |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | // Check that awaited functions are either defined here, action functions or Promise.all |
| 144 | const actionNames = actions.map((a) => snakeToCamel(a.name)); |
| 145 | let awaitableActionNames = ["Promise.all", ...actionNames]; |
| 146 | const definedFnNames = |
| 147 | code |
| 148 | .match(/(async function (\w+)\(|(const|let|var) (\w+)\s*=\s*async\s*\()/g) |
| 149 | ?.map((fnNameMatch) => { |
| 150 | if (fnNameMatch.startsWith("async")) { |
| 151 | // async function (\w+)\( |
| 152 | return fnNameMatch.slice(15, -1); |
| 153 | } else { |
| 154 | // (const|let|var) (\w+)\s*=\s*async\s*\( |
| 155 | return fnNameMatch.split("=")[0].split(" ")[1].trim(); |
| 156 | } |
| 157 | }) ?? []; |
| 158 |
no test coverage detected