aimyapi 1.0.9 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/sandbox.ts CHANGED
@@ -1,277 +1,254 @@
1
- import "quickjs-emscripten";
2
-
3
- import { QuickJSWASMModule } from "quickjs-emscripten";
4
- import * as ts from "typescript";
5
- import {
6
- injectTimingFunctions,
7
- wrap,
8
- wrapObject,
9
- wrapObjectWhitelist,
10
- } from "./sandbox-wrappers";
11
-
12
- // Should we use "noEmitOnError": true ?
13
- function tsCompile(
14
- source: string,
15
- options?: ts.TranspileOptions
16
- ): string {
17
- // Default options -- you could also perform a merge, or use the project tsconfig.json
18
- if (!options) {
19
- options = {
20
- compilerOptions: {
21
- target: ts.ScriptTarget.ES2022,
22
- module: ts.ModuleKind.None,
23
- },
24
- };
25
- }
26
- return ts.transpileModule(source, options).outputText;
27
- }
28
-
29
- export interface GlobalOption {
30
- value: any;
31
- whitelist?: string[];
32
- }
33
-
34
- export interface Globals {
35
- [key: string]: GlobalOption;
36
- }
37
-
38
- export type SandboxLog = {
39
- type: "log" | "error";
40
- args: any[];
41
- source: string
42
- };
43
- export type CodeRunResult = {
44
- success: boolean;
45
- logs: SandboxLog[];
46
- }
47
- type LogSource = "sandbox" | "compiler" | "runner";
48
- class Logger {
49
- logs: SandboxLog[] = [];
50
- debug: boolean = false;
51
-
52
- constructor(debug: boolean = false) {
53
- this.debug = debug;
54
- }
55
-
56
- log(args: any, source: LogSource) {
57
- if(Array.isArray(args)) {
58
- this.logs.push({ type: "log", args, source });
59
- } else {
60
- this.logs.push({ type: "log", args: [args], source });
61
- }
62
- if (this.debug) {
63
- console.log(`[${source}]:`, ...args);
64
- }
65
- }
66
-
67
- error(args: any, source: LogSource) {
68
- if(Array.isArray(args)) {
69
- this.logs.push({ type: "error", args, source });
70
- } else {
71
- this.logs.push({ type: "error", args: [args], source });
72
- }
73
-
74
- console.error(`[${source}]:`, ...args);
75
- }
76
-
77
- clear() {
78
- this.logs = [];
79
- }
80
- }
81
-
82
-
83
-
84
- // function whitelist can be produced using something like:
85
- // const assistantFunctions = Object.getOwnPropertyNames(Object.getPrototypeOf(apiObject))
86
- // .filter((f) => f !== "constructor" && !f.startsWith("_"));
87
-
88
- // Is there some way we can treat the API and globals the same?
89
- // We could just provide an object for all globals (including the API) and also have the whitelist as a parameter..
90
- export default async function createSandbox(QuickJS: QuickJSWASMModule, requireLookup:any = {}, globals: Globals = {}, debug:boolean = false) {
91
- const vm = QuickJS.newContext();
92
-
93
- let errorState = false;
94
- let lastError = "";
95
-
96
- let asyncProcessesRunning = 0;
97
-
98
- let beginAsyncProcess = () => {
99
- asyncProcessesRunning++;
100
- };
101
-
102
- let endAsyncProcess = () => {
103
- if (asyncProcessesRunning > 0) {
104
- asyncProcessesRunning--;
105
- }
106
- };
107
-
108
- let isAsyncProcessRunning = () => {
109
- //console.log("asyncProcessesRunning:", asyncProcessesRunning);
110
- return asyncProcessesRunning > 0;
111
- };
112
-
113
- const logger = new Logger(debug);
114
-
115
-
116
-
117
- // const assistantHandle = wrapObjectWhitelist(vm, apiObject, functionWhitelist, beginAsyncProcess, endAsyncProcess);
118
- // vm.setProp(vm.global, apiGlobalName, assistantHandle)
119
-
120
- // assistantHandle.dispose();
121
-
122
- const logHandle = vm.newFunction("log", (...args:any) => {
123
- const nativeArgs = args.map(vm.dump);
124
- logger.log(nativeArgs, "sandbox");
125
- });
126
- const consoleHandle = vm.newObject();
127
- const exportsHandle = vm.newObject();
128
-
129
- const errorHandle = vm.newFunction("error", (...args:any) => {
130
- try {
131
- const nativeArgs = args.map(vm.dump);
132
-
133
- logger.error(nativeArgs, "sandbox");
134
- lastError = JSON.stringify(nativeArgs);
135
- errorState = true;
136
- } catch (e) {
137
- logger.error(["? Error in error:", e], "sandbox");
138
- }
139
- });
140
-
141
- const globalNames = Object.getOwnPropertyNames(globals);
142
- for (let i = 0; i < globalNames.length; i++) {
143
- const globalOption: GlobalOption = globals[globalNames[i]];
144
- const globalObj = globalOption.whitelist
145
- ? wrapObjectWhitelist(
146
- vm,
147
- globalOption.value,
148
- globalOption.whitelist,
149
- beginAsyncProcess,
150
- endAsyncProcess
151
- )
152
- : wrap(vm, globalOption.value, globalOption, beginAsyncProcess, endAsyncProcess);
153
-
154
- vm.setProp(vm.global, globalNames[i], globalObj);
155
- globalObj.dispose();
156
- }
157
-
158
- injectTimingFunctions(vm, beginAsyncProcess, endAsyncProcess);
159
-
160
- const requireHandle = vm.newFunction("require", (...args) => {
161
- const nativeArgs = args.map(vm.dump);
162
- if(requireLookup[nativeArgs[0] as string]) {
163
- const returnObj = wrapObject(vm, requireLookup[nativeArgs[0] as string], beginAsyncProcess, endAsyncProcess);
164
- return returnObj;
165
- } else {
166
- return vm.undefined;
167
- }
168
- });
169
-
170
- vm.setProp(consoleHandle, "log", logHandle);
171
- vm.setProp(consoleHandle, "error", errorHandle);
172
- vm.setProp(vm.global, "require", requireHandle);
173
- vm.setProp(vm.global, "console", consoleHandle);
174
- vm.setProp(vm.global, "exports", exportsHandle);
175
-
176
- consoleHandle.dispose();
177
- logHandle.dispose();
178
- exportsHandle.dispose();
179
- errorHandle.dispose();
180
- requireHandle.dispose();
181
-
182
- return {
183
- vm,
184
- getLastError: () => {
185
- return lastError;
186
- },
187
- isAsyncProcessRunning,
188
- // make sure code passes the typescript compilation
189
- checkCode: (code: string) => {
190
- logger.clear();
191
- try {
192
- const compiled = tsCompile(code);
193
- if (!compiled || compiled.length === 0) {
194
- logger.error(["Could not compile:", code], "compiler");
195
- return {
196
- success: false,
197
- logs: logger.logs,
198
- };
199
- }
200
- } catch (e) {
201
- logger.error(["Error testing code:", e], "compiler");
202
- return {
203
- success: false,
204
- logs: logger.logs,
205
- };
206
- }
207
-
208
- return {
209
- success: true,
210
- logs: logger.logs,
211
- }
212
- },
213
- runCode: (code: string): CodeRunResult => {
214
- try {
215
- logger.clear();
216
- const compiled = tsCompile(code);
217
-
218
- if (!compiled || compiled.length === 0) {
219
- logger.error(["Could not compile:", code], "compiler");
220
- return {
221
- success: false,
222
- logs: logger.logs,
223
- };
224
- }
225
-
226
- // View compiled typescript code for debugging
227
- // if(debug)
228
- // console.log(compiled);
229
-
230
- errorState = false;
231
- const result = vm.evalCode(compiled);
232
-
233
- if (result.error) {
234
- // log out the compiled program with line numbers
235
- const lines = compiled.split("\n").map((line, index) => `${index + 1}: ${line}`);
236
-
237
-
238
- logger.error(["Execution failed:", lines.join("\n"), vm.dump(result.error)], "runner");
239
-
240
- result.error.dispose();
241
- return {
242
- success: false,
243
- logs: logger.logs,
244
- };
245
- } else if("value" in result){
246
- //console.log("Task Result:", vm.dump(result.value));
247
-
248
- result.value.dispose();
249
-
250
-
251
- if (errorState) {
252
- const lines = compiled.split("\n").map((line, index) => `${index + 1}: ${line}`);
253
- logger.error(["An error was reported during execution. Compiled code with line numbers:\n", lines.join("\n")], "runner");
254
- }
255
-
256
- return {
257
- success: !errorState,
258
- logs: logger.logs,
259
- };
260
- } else {
261
- logger.error(["Unknown execution result:", result], "runner");
262
- return {
263
- success: false,
264
- logs: logger.logs,
265
- };
266
- }
267
- } catch (e) {
268
- logger.error(["Error running code:", e], "runner");
269
- }
270
-
271
- return {
272
- success: false,
273
- logs: logger.logs,
274
- };
275
- },
276
- };
277
- }
1
+ import "quickjs-emscripten";
2
+
3
+ import { QuickJSWASMModule } from "quickjs-emscripten";
4
+ import * as ts from "typescript";
5
+ import { Logger, SandboxLog } from "./logger";
6
+ import {
7
+ injectTimingFunctions,
8
+ wrap,
9
+ wrapObject,
10
+ wrapObjectWhitelist,
11
+ } from "./sandbox-wrappers";
12
+
13
+ // Should we use "noEmitOnError": true ?
14
+ function tsCompile(
15
+ source: string,
16
+ options?: ts.TranspileOptions
17
+ ): string {
18
+ // Default options -- you could also perform a merge, or use the project tsconfig.json
19
+ if (!options) {
20
+ options = {
21
+ compilerOptions: {
22
+ target: ts.ScriptTarget.ES2022,
23
+ module: ts.ModuleKind.None,
24
+ },
25
+ };
26
+ }
27
+ return ts.transpileModule(source, options).outputText;
28
+ }
29
+
30
+ export interface GlobalOption {
31
+ value: any;
32
+ whitelist?: string[];
33
+ }
34
+
35
+ export interface Globals {
36
+ [key: string]: GlobalOption;
37
+ }
38
+ export type CodeRunResult = {
39
+ success: boolean;
40
+ logs: SandboxLog[];
41
+ }
42
+
43
+
44
+ // function whitelist can be produced using something like:
45
+ // const assistantFunctions = Object.getOwnPropertyNames(Object.getPrototypeOf(apiObject))
46
+ // .filter((f) => f !== "constructor" && !f.startsWith("_"));
47
+
48
+ // Is there some way we can treat the API and globals the same?
49
+ // We could just provide an object for all globals (including the API) and also have the whitelist as a parameter..
50
+ export default async function createSandbox(QuickJS: QuickJSWASMModule, requireLookup:any = {}, globals: Globals = {}, debug:boolean = false) {
51
+ const vm = QuickJS.newContext();
52
+
53
+ let errorState = false;
54
+ let lastError = "";
55
+
56
+ let asyncProcessesRunning = 0;
57
+
58
+ let beginAsyncProcess = () => {
59
+ asyncProcessesRunning++;
60
+ };
61
+
62
+ let endAsyncProcess = () => {
63
+ if (asyncProcessesRunning > 0) {
64
+ asyncProcessesRunning--;
65
+ }
66
+ };
67
+
68
+ let isAsyncProcessRunning = () => {
69
+ //console.log("asyncProcessesRunning:", asyncProcessesRunning);
70
+ return asyncProcessesRunning > 0;
71
+ };
72
+
73
+ const logger = new Logger(debug);
74
+
75
+
76
+
77
+ // const assistantHandle = wrapObjectWhitelist(vm, apiObject, functionWhitelist, beginAsyncProcess, endAsyncProcess);
78
+ // vm.setProp(vm.global, apiGlobalName, assistantHandle)
79
+
80
+ // assistantHandle.dispose();
81
+
82
+ const logHandle = vm.newFunction("log", (...args:any) => {
83
+ const nativeArgs = args.map(vm.dump);
84
+ logger.log(nativeArgs, "sandbox");
85
+ });
86
+ const consoleHandle = vm.newObject();
87
+ const exportsHandle = vm.newObject();
88
+
89
+ const errorHandle = vm.newFunction("error", (...args:any) => {
90
+ try {
91
+ const nativeArgs = args.map(vm.dump);
92
+
93
+ logger.error(nativeArgs, "sandbox");
94
+ lastError = JSON.stringify(nativeArgs);
95
+ errorState = true;
96
+ } catch (e) {
97
+ logger.error(["? Error in error:", e], "sandbox");
98
+ }
99
+ });
100
+
101
+ const globalNames = Object.getOwnPropertyNames(globals);
102
+ for (let i = 0; i < globalNames.length; i++) {
103
+ const globalOption: GlobalOption = globals[globalNames[i]];
104
+ const globalObj = globalOption.whitelist
105
+ ? wrapObjectWhitelist(
106
+ vm,
107
+ globalOption.value,
108
+ globalOption.whitelist,
109
+ beginAsyncProcess,
110
+ endAsyncProcess,
111
+ logger
112
+ )
113
+ : wrap(vm, globalOption.value, globalOption, beginAsyncProcess, endAsyncProcess, logger);
114
+
115
+ vm.setProp(vm.global, globalNames[i], globalObj);
116
+ globalObj.dispose();
117
+ }
118
+
119
+ injectTimingFunctions(vm, beginAsyncProcess, endAsyncProcess);
120
+
121
+ const requireHandle = vm.newFunction("require", (...args) => {
122
+ const nativeArgs = args.map(vm.dump);
123
+ if(requireLookup[nativeArgs[0] as string]) {
124
+ const returnObj = wrapObject(vm, requireLookup[nativeArgs[0] as string], beginAsyncProcess, endAsyncProcess, logger);
125
+ return returnObj;
126
+ } else {
127
+ return vm.undefined;
128
+ }
129
+ });
130
+
131
+ vm.setProp(consoleHandle, "log", logHandle);
132
+ vm.setProp(consoleHandle, "error", errorHandle);
133
+ vm.setProp(vm.global, "require", requireHandle);
134
+ vm.setProp(vm.global, "console", consoleHandle);
135
+ vm.setProp(vm.global, "exports", exportsHandle);
136
+
137
+ consoleHandle.dispose();
138
+ logHandle.dispose();
139
+ exportsHandle.dispose();
140
+ errorHandle.dispose();
141
+ requireHandle.dispose();
142
+
143
+ return {
144
+ vm,
145
+ getLastError: () => {
146
+ return lastError;
147
+ },
148
+ isAsyncProcessRunning,
149
+ // make sure code passes the typescript compilation
150
+ checkCode: (code: string) => {
151
+ logger.clear();
152
+ try {
153
+ const compiled = tsCompile(code);
154
+ if (!compiled || compiled.length === 0) {
155
+ logger.error(["Could not compile:", code], "compiler");
156
+ return {
157
+ success: false,
158
+ logs: logger.getLogs(),
159
+ };
160
+ }
161
+ } catch (e) {
162
+ logger.error(["Error testing code:", e], "compiler");
163
+ return {
164
+ success: false,
165
+ logs: logger.getLogs(),
166
+ };
167
+ }
168
+
169
+ return {
170
+ success: true,
171
+ logs: logger.getLogs(),
172
+ }
173
+ },
174
+ runCode: async (code: string): Promise<CodeRunResult> => {
175
+ try {
176
+ logger.clear();
177
+ const compiled = tsCompile(code);
178
+
179
+ if (!compiled || compiled.length === 0) {
180
+ logger.error(["Could not compile:", code], "compiler");
181
+ return {
182
+ success: false,
183
+ logs: logger.getLogs()
184
+ };
185
+ }
186
+
187
+ // View compiled typescript code for debugging
188
+ // if(debug)
189
+ // console.log(compiled);
190
+
191
+ errorState = false;
192
+ const result = vm.evalCode(compiled);
193
+
194
+ if (result.error) {
195
+ // log out the compiled program with line numbers
196
+ const lines = compiled.split("\n").map((line, index) => `${index + 1}: ${line}`);
197
+
198
+ logger.error(["Execution failed:", lines.join("\n"), vm.dump(result.error)], "runner");
199
+
200
+ result.error.dispose();
201
+ return {
202
+ success: false,
203
+ logs: logger.getPrettyLogs(),
204
+ };
205
+ } else if("value" in result) {
206
+ //console.log("Task Result:", vm.dump(result.value), errorState);
207
+ // The wrapped code returns a promise; wait for it to settle.
208
+ const settled = vm.resolvePromise(result.value);
209
+ result.value.dispose();
210
+ vm.runtime.executePendingJobs(); // start the async function running
211
+ const promiseResult = await settled;
212
+
213
+ if (promiseResult.error) {
214
+ const err = vm.dump(promiseResult.error);
215
+ promiseResult.error.dispose();
216
+ const lines = compiled.split("\n").map((l, i) => `${i + 1}: ${l}`);
217
+ logger.error(["Uncaught exception:", err, "\n", lines.join("\n")], "runner");
218
+ return {
219
+ success: false,
220
+ logs: logger.getPrettyLogs(),
221
+ };
222
+ }
223
+ // @ts-ignore this is a success response
224
+ promiseResult.value.dispose();
225
+
226
+
227
+
228
+ if (errorState) {
229
+ const lines = compiled.split("\n").map((line, index) => `${index + 1}: ${line}`);
230
+ logger.error(["An error was reported during execution. Compiled code with line numbers:\n", lines.join("\n")], "runner");
231
+ }
232
+
233
+ return {
234
+ success: !errorState,
235
+ logs: logger.getPrettyLogs(),
236
+ };
237
+ } else {
238
+ logger.error(["Unknown execution result:", result], "runner");
239
+ return {
240
+ success: false,
241
+ logs: logger.getPrettyLogs(),
242
+ };
243
+ }
244
+ } catch (e) {
245
+ logger.error(["Error running code:", e], "runner");
246
+ }
247
+
248
+ return {
249
+ success: false,
250
+ logs: logger.getLogs(),
251
+ };
252
+ },
253
+ };
254
+ }
package/src/utils.ts CHANGED
@@ -1,3 +1,33 @@
1
1
  export function delay(time):Promise<void> {
2
2
  return new Promise<void>(resolve => setTimeout(resolve, time));
3
- }
3
+ }
4
+
5
+ const has = Object.prototype.hasOwnProperty;
6
+
7
+ export function dequal(foo: any, bar: any) {
8
+ let ctor, len;
9
+ if (foo === bar) return true;
10
+
11
+ if (foo && bar && (ctor=foo.constructor) === bar.constructor) {
12
+ if (ctor === Date) return foo.getTime() === bar.getTime();
13
+ if (ctor === RegExp) return foo.toString() === bar.toString();
14
+
15
+ if (ctor === Array) {
16
+ if ((len=foo.length) === bar.length) {
17
+ while (len-- && dequal(foo[len], bar[len]));
18
+ }
19
+ return len === -1;
20
+ }
21
+
22
+ if (!ctor || typeof foo === 'object') {
23
+ len = 0;
24
+ for (ctor in foo) {
25
+ if (has.call(foo, ctor) && ++len && !has.call(bar, ctor)) return false;
26
+ if (!(ctor in bar) || !dequal(foo[ctor], bar[ctor])) return false;
27
+ }
28
+ return Object.keys(bar).length === len;
29
+ }
30
+ }
31
+
32
+ return foo !== foo && bar !== bar;
33
+ }
@@ -1,37 +0,0 @@
1
-
2
- Most recent user query:
3
- {{USER_QUERY}}
4
-
5
- ---
6
-
7
- Based on the prior conversation, write a typescript script that performs the operation requested by the user and call the `submitCode` tool with the script.
8
-
9
- ## API documentation
10
-
11
- You can make use of this global API object:
12
-
13
- `api.ts`:
14
- ```typescript
15
- {{API}}
16
- ```
17
-
18
- ## Instructions
19
-
20
- Use the global api object to make calls to the API.
21
-
22
- You may use top level `await`.
23
-
24
- If needed, you may use `runCode` to run any non-destructive API calls to gather data for your script. Nothing will be visible to you unless you `console.log` it.
25
-
26
- You may use the `checkCode` tool to confirm that your code passes typescript compilation and doesn't have errors before submitting.
27
-
28
- In your code, `console.log` and `console.error` can be used to understand the execution or indicate that an error happened, but debug logs should be removed in the final version (errors can stay).
29
-
30
- When finished, you must call `submitCode` to submit the final version of the code, then respond with "Successfully submitted code" on success. On failure, correct any errors and submit again.
31
-
32
- ---
33
-
34
- {{DOCUMENTATION}}
35
-
36
- {{CONTEXT}}
37
-