From 6e3c1fe1cd51e695c2972898b04d15a60b766ac1 Mon Sep 17 00:00:00 2001 From: Donnie Adams Date: Thu, 23 May 2024 21:37:42 -0400 Subject: [PATCH] feat: add support for the SDK server With this change, the gptscript SDK server will be fork/exec-ed instead of fork/exec-ing for every gptscript command. This produces enhancements with daemons in gptscript. This change also intentionally removes support for browser-based applications. --- README.md | 95 ++++-- babel.test.cjs | 18 - package-lock.json | 517 +++++----------------------- package.json | 18 +- rollup.config.js | 21 -- src/gptscript.ts | 726 +++++++++++++--------------------------- tests/gptscript.test.ts | 212 ++++++------ 7 files changed, 486 insertions(+), 1121 deletions(-) delete mode 100644 babel.test.cjs delete mode 100644 rollup.config.js diff --git a/README.md b/README.md index b4bc286..71f8ccb 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ running `npm install`. ## Usage -To use the module and run gptscripts, you need to first set the OPENAI_API_KEY environment variable to your OpenAI API -key. +To use the module and run gptscripts, you need to first set the `OPENAI_API_KEY` environment variable to your OpenAI API +key. You can also set the `GPTSCRIPT_BIN` environment variable to change the execution of the gptscripts. To ensure it is working properly, you can run the following command: @@ -31,23 +31,24 @@ You will see "Hello, World!" in the output of the command. ## Client -There are currently a couple "global" options, and the client helps to manage those. A client without any options is -likely what you want. However, here are the current global options: - -- `gptscriptURL`: The URL (including `http(s)://) of an "SDK server" to use instead of the fork/exec model. -- `gptscriptBin`: The path to a `gptscript` binary to use instead of the bundled one. +The client allows the caller to run gptscript files, tools, and other operations (see below). There are currently no +options for this singleton client, so `new gptscript.Client()` is all you need. Although, the intention is that a +single client is all you need for the life of your application, you should call `close()` on the client when you are +done. ## Options These are optional options that can be passed to the various `exec` functions. None of the options is required, and the defaults will reduce the number of calls made to the Model API. -- `disableCache`: Enable or disable caching, default (true) -- `cacheDir`: Specify the cache directory +- `cache`: Enable or disable caching. Default (true). +- `cacheDir`: Specify the cache directory. - `quiet`: No output logging -- `chdir`: Change current working directory - `subTool`: Use tool of this name, not the first tool +- `input`: Input arguments for the tool run - `workspace`: Directory to use for the workspace, if specified it will not be deleted on exit +- `chatState`: The chat state to continue, or null to start a new chat and return the state +- `confirm`: Prompt before running potentially dangerous commands ## Functions @@ -64,6 +65,7 @@ async function listTools() { const client = new gptscript.Client(); const tools = await client.listTools(); console.log(tools); + client.close() } ``` @@ -78,12 +80,13 @@ const gptscript = require('@gptscript-ai/gptscript'); async function listModels() { let models = []; + const client = new gptscript.Client(); try { - const client = new gptscript.Client(); models = await client.listModels(); } catch (error) { console.error(error); } + client.close() } ``` @@ -97,12 +100,13 @@ Get the first of the current `gptscript` binary being used for the calls. const gptscript = require('@gptscript-ai/gptscript'); async function version() { + const client = new gptscript.Client(); try { - const client = new gptscript.Client(); console.log(await client.version()); } catch (error) { console.error(error); } + client.close() } ``` @@ -118,13 +122,14 @@ const t = { instructions: "Who was the president of the united states in 1928?" }; +const client = new gptscript.Client(); try { - const client = new gptscript.Client(); - const run = client.evaluate(t); + const run = await client.evaluate(t); console.log(await run.text()); } catch (error) { console.error(error); } +client.close(); ``` ### run @@ -140,13 +145,14 @@ const opts = { }; async function execFile() { + const client = new gptscript.Client(); try { - const client = new gptscript.Client(); - const run = client.run('./hello.gpt', opts); + const run = await client.run('./hello.gpt', opts); console.log(await run.text()); } catch (e) { console.error(e); } + client.close(); } ``` @@ -156,17 +162,6 @@ The `Run` object exposes event handlers so callers can access the progress event The `Run` object exposes these events with their corresponding event type: -| Event type | Event object | -|---------------------------|-------------------| -| RunEventType.RunStart | RunStartFrame | -| RunEventType.RunFinish | RunFinishFrame | -| RunEventType.CallStart | CallStartFrame | -| RunEventType.CallChat | CallChatFrame | -| RunEventType.CallContinue | CallContinueFrame | -| RunEventType.CallProgress | CallProgressFrame | -| RunEventType.CallFinish | CallFinishFrame | -| RunEventType.Event | Frame | - Subscribing to `RunEventType.Event` gets you all events. ```javascript @@ -178,9 +173,9 @@ const opts = { }; async function streamExecFileWithEvents() { + const client = new gptscript.Client(); try { - const client = new gptscript.Client(); - const run = client.run('./test.gpt', opts); + const run = await client.run('./test.gpt', opts); run.on(gptscript.RunEventType.Event, data => { console.log(`event: ${JSON.stringify(data)}`); @@ -190,6 +185,45 @@ async function streamExecFileWithEvents() { } catch (e) { console.error(e); } + client.close(); +} +``` + +### Confirm + +If a gptscript can run commands, you may want to inspect and confirm/deny the command before they are run. This can be +done with the `confirm` method. A user should listen for the `RunEventType.CallConfirm` event. + +```javascript +const gptscript = require('@gptscript-ai/gptscript'); + +const opts = { + disableCache: true, + input: "--testin how high is that there mouse?", + confirm: true +}; + +async function streamExecFileWithEvents() { + const client = new gptscript.Client(); + try { + const run = await client.run('./test.gpt', opts); + + run.on(gptscript.RunEventType.CallConfirm, async (data: gptscript.CallFrame) => { + // data.Tool has the information for the command being run. + // data.Input has the input for this command + + await client.confirm({ + id: data.id, + accept: true, // false if the command should not be run + message: "", // Explain the denial (ignored if accept is true) + }) + }); + + await run.text(); + } catch (e) { + console.error(e); + } + client.close(); } ``` @@ -219,7 +253,7 @@ const t = { async function streamExecFileWithEvents() { const client = new gptscript.Client(); - let run = client.evaluate(t, opts); + let run = await client.evaluate(t, opts); try { // Wait for the initial run to complete. await run.text(); @@ -238,6 +272,7 @@ async function streamExecFileWithEvents() { console.error(e); } + client.close(); // The state here should either be RunState.Finished (on success) or RunState.Error (on error). console.log(run.state) diff --git a/babel.test.cjs b/babel.test.cjs deleted file mode 100644 index 1fe0b47..0000000 --- a/babel.test.cjs +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - "presets": [ - "@babel/preset-typescript", - [ - "@babel/preset-env", - { - "useBuiltIns": "entry", - "corejs": 3, - "targets": { - "node": "current" - } - } - ] - ], - "plugins": [ - "babel-plugin-transform-import-meta" - ] -}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 47025fa..2396548 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,17 @@ { "name": "@gptscript-ai/gptscript", - "version": "v0.6.1", + "version": "v0.6.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@gptscript-ai/gptscript", - "version": "v0.6.1", + "version": "v0.6.2", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@babel/template": "^7.24.0", - "@babel/types": "^7.24.5", + "@types/sync-fetch": "^0.4.3", "adm-zip": "^0.5.10", - "child_process": "^1.0.2", - "net": "^1.0.2", "node-downloader-helper": "^2.1.9", "tar": "^6.2.0" }, @@ -25,7 +22,6 @@ "@babel/core": "^7.24.5", "@babel/preset-env": "^7.24.5", "@babel/preset-typescript": "^7.24.1", - "@rollup/plugin-typescript": "^11.1.6", "@swc/cli": "^0.3.9", "@swc/core": "^1.4.2", "@types/jest": "^29.5.12", @@ -35,16 +31,10 @@ "copyfiles": "^2.4.1", "jest": "^29.7.0", "npm-run-all": "^4.1.5", - "rollup": "^4.17.2", - "rollup-plugin-commonjs": "^10.1.0", - "rollup-plugin-node-resolve": "^5.2.0", "ts-jest": "^29.1.2", "ts-loader": "^9.5.1", "typescript": "^5.4.5", "url": "^0.11.3" - }, - "peerDependencies": { - "sse.js": "^2.4.1" } }, "node_modules/@ampproject/remapping": { @@ -64,6 +54,7 @@ "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "dev": true, "dependencies": { "@babel/highlight": "^7.24.2", "picocolors": "^1.0.0" @@ -394,6 +385,7 @@ "version": "7.24.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -402,6 +394,7 @@ "version": "7.24.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -447,6 +440,7 @@ "version": "7.24.5", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.24.5", "chalk": "^2.4.2", @@ -461,6 +455,7 @@ "version": "7.24.5", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", + "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -1760,6 +1755,7 @@ "version": "7.24.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.23.5", "@babel/parser": "^7.24.0", @@ -1794,6 +1790,7 @@ "version": "7.24.5", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", + "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.24.1", "@babel/helper-validator-identifier": "^7.24.5", @@ -2575,262 +2572,6 @@ "node": ">= 8" } }, - "node_modules/@rollup/plugin-typescript": { - "version": "11.1.6", - "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.6.tgz", - "integrity": "sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==", - "dev": true, - "dependencies": { - "@rollup/pluginutils": "^5.1.0", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.14.0||^3.0.0||^4.0.0", - "tslib": "*", - "typescript": ">=3.7.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - }, - "tslib": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", - "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", - "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", - "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", - "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", - "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", - "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", - "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", - "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", - "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", - "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", - "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", - "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", - "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", - "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", - "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", - "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -3223,7 +2964,8 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/graceful-fs": { "version": "4.1.9", @@ -3293,18 +3035,17 @@ "version": "20.12.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", - "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, - "node_modules/@types/resolve": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", - "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", - "dev": true, + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", "dependencies": { - "@types/node": "*" + "@types/node": "*", + "form-data": "^4.0.0" } }, "node_modules/@types/responselike": { @@ -3322,6 +3063,14 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, + "node_modules/@types/sync-fetch": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@types/sync-fetch/-/sync-fetch-0.4.3.tgz", + "integrity": "sha512-RfwkmWFd7yi6tRaDcR7KNrxyp6LpO2oXrT6tYkXBLNfp4xf4EKRX7IlLexbtd5X26g34+G6HDD2pMhOZ5srUmQ==", + "dependencies": { + "@types/node-fetch": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -3616,6 +3365,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -3703,6 +3453,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -4226,18 +3981,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cacheable-lookup": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", @@ -4341,6 +4084,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -4359,11 +4103,6 @@ "node": ">=10" } }, - "node_modules/child_process": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", - "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==" - }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -4446,6 +4185,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -4453,7 +4193,19 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } }, "node_modules/commander": { "version": "8.3.0", @@ -4833,6 +4585,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -5050,6 +4810,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -5114,12 +4875,6 @@ "node": ">=4.0" } }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -5376,6 +5131,19 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -5673,6 +5441,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -6013,12 +5782,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "dev": true - }, "node_modules/is-negative-zero": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", @@ -6064,15 +5827,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "dev": true, - "dependencies": { - "@types/estree": "*" - } - }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -7999,7 +7753,8 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "node_modules/js-yaml": { "version": "3.14.1", @@ -8193,15 +7948,6 @@ "yallist": "^3.0.2" } }, - "node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - } - }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -8285,7 +8031,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -8294,8 +8039,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -8402,11 +8145,6 @@ "dev": true, "peer": true }, - "node_modules/net": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/net/-/net-1.0.2.tgz", - "integrity": "sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ==" - }, "node_modules/nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", @@ -8835,7 +8573,8 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", @@ -9390,96 +9129,6 @@ "node": ">=0.10.0" } }, - "node_modules/rollup": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", - "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", - "dev": true, - "dependencies": { - "@types/estree": "1.0.5" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.17.2", - "@rollup/rollup-android-arm64": "4.17.2", - "@rollup/rollup-darwin-arm64": "4.17.2", - "@rollup/rollup-darwin-x64": "4.17.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", - "@rollup/rollup-linux-arm-musleabihf": "4.17.2", - "@rollup/rollup-linux-arm64-gnu": "4.17.2", - "@rollup/rollup-linux-arm64-musl": "4.17.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", - "@rollup/rollup-linux-riscv64-gnu": "4.17.2", - "@rollup/rollup-linux-s390x-gnu": "4.17.2", - "@rollup/rollup-linux-x64-gnu": "4.17.2", - "@rollup/rollup-linux-x64-musl": "4.17.2", - "@rollup/rollup-win32-arm64-msvc": "4.17.2", - "@rollup/rollup-win32-ia32-msvc": "4.17.2", - "@rollup/rollup-win32-x64-msvc": "4.17.2", - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup-plugin-commonjs": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz", - "integrity": "sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==", - "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-commonjs.", - "dev": true, - "dependencies": { - "estree-walker": "^0.6.1", - "is-reference": "^1.1.2", - "magic-string": "^0.25.2", - "resolve": "^1.11.0", - "rollup-pluginutils": "^2.8.1" - }, - "peerDependencies": { - "rollup": ">=1.12.0" - } - }, - "node_modules/rollup-plugin-commonjs/node_modules/estree-walker": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", - "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", - "dev": true - }, - "node_modules/rollup-plugin-node-resolve": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz", - "integrity": "sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==", - "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-node-resolve.", - "dev": true, - "dependencies": { - "@types/resolve": "0.0.8", - "builtin-modules": "^3.1.0", - "is-module": "^1.0.0", - "resolve": "^1.11.1", - "rollup-pluginutils": "^2.8.1" - }, - "peerDependencies": { - "rollup": ">=1.11.0" - } - }, - "node_modules/rollup-pluginutils": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", - "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", - "dev": true, - "dependencies": { - "estree-walker": "^0.6.1" - } - }, - "node_modules/rollup-pluginutils/node_modules/estree-walker": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", - "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", - "dev": true - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -9794,13 +9443,6 @@ "node": ">=0.10.0" } }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead", - "dev": true - }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -9839,12 +9481,6 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "node_modules/sse.js": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/sse.js/-/sse.js-2.4.1.tgz", - "integrity": "sha512-0qXPFZCClp+RPWtldNTYUjDWDlStZq1nyRF7at8WDFotHEyCivEBlR7Z5ftcJnt9KpouRg+NdzGFvPTB2gHl8Q==", - "peer": true - }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -10050,6 +9686,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -10367,6 +10004,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, "engines": { "node": ">=4" } @@ -10712,8 +10350,7 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", diff --git a/package.json b/package.json index ac1bd29..7b45849 100644 --- a/package.json +++ b/package.json @@ -19,27 +19,18 @@ "test": "jest", "postinstall": "node scripts/install-binary.js", "clean": "rm -rf dist", - "build": "tsc && rollup -c" + "build": "tsc" }, "keywords": [ "gptscript", "gpt", "AI" ], - "browser": { - "child_process": false - }, "author": "Bill Maxwell ", "license": "Apache-2.0", - "peerDependencies": { - "sse.js": "^2.4.1" - }, "dependencies": { - "@babel/template": "^7.24.0", - "@babel/types": "^7.24.5", + "@types/sync-fetch": "^0.4.3", "adm-zip": "^0.5.10", - "child_process": "^1.0.2", - "net": "^1.0.2", "node-downloader-helper": "^2.1.9", "tar": "^6.2.0" }, @@ -47,7 +38,6 @@ "@babel/core": "^7.24.5", "@babel/preset-env": "^7.24.5", "@babel/preset-typescript": "^7.24.1", - "@rollup/plugin-typescript": "^11.1.6", "@swc/cli": "^0.3.9", "@swc/core": "^1.4.2", "@types/jest": "^29.5.12", @@ -57,9 +47,6 @@ "copyfiles": "^2.4.1", "jest": "^29.7.0", "npm-run-all": "^4.1.5", - "rollup": "^4.17.2", - "rollup-plugin-commonjs": "^10.1.0", - "rollup-plugin-node-resolve": "^5.2.0", "ts-jest": "^29.1.2", "ts-loader": "^9.5.1", "typescript": "^5.4.5", @@ -70,7 +57,6 @@ "^.+\\.ts?$": [ "ts-jest", { - "babelConfig": "babel.test.cjs", "useESM": true } ] diff --git a/rollup.config.js b/rollup.config.js deleted file mode 100644 index 8d140c8..0000000 --- a/rollup.config.js +++ /dev/null @@ -1,21 +0,0 @@ -import resolve from 'rollup-plugin-node-resolve'; -import commonjs from 'rollup-plugin-commonjs'; -import typescript from '@rollup/plugin-typescript'; - -export default [{ - input: 'dist/gptscript.js', - output: { - name: "GPTScript", - file: "dist/gptscript.browser.js", - format: 'iife', - sourcemap: true, - }, - external: [ - 'net', 'http', 'path', 'child_process', 'sse.js', - ], - plugins: [ - typescript(), - commonjs(), - resolve({preferBuiltins: true}), - ], -}]; diff --git a/src/gptscript.ts b/src/gptscript.ts index 5045c0d..96c7b3a 100644 --- a/src/gptscript.ts +++ b/src/gptscript.ts @@ -1,34 +1,16 @@ -// @ts-ignore -import type {SSE} from "sse.js" +import http from "http" +import path from "path" +import child_process from "child_process" export interface RunOpts { input?: string - cacheDir?: string disableCache?: boolean quiet?: boolean chdir?: string subTool?: string workspace?: string chatState?: string -} - -function toArgs(opts: RunOpts): string[] { - const args: string[] = [] - const optToArg: Record = { - disableCache: "--disable-cache=", - cacheDir: "--cache-dir=", - quiet: "--quiet=", - chdir: "--chdir=", - subTool: "--sub-tool=", - workspace: "--workspace=", - } - for (const [key, value] of Object.entries(opts)) { - if (optToArg[key] && value !== undefined) { - args.push(optToArg[key] + value) - } - } - - return args + confirm?: boolean } export enum RunEventType { @@ -37,18 +19,43 @@ export enum RunEventType { RunFinish = "runFinish", CallStart = "callStart", CallChat = "callChat", + CallSubCalls = "callSubCalls", CallProgress = "callProgress", + CallConfirm = "callConfirm", CallContinue = "callContinue", CallFinish = "callFinish", } +let serverProcess: child_process.ChildProcess +let clientCount: number = 0 + export class Client { - public readonly gptscriptURL?: string - public gptscriptBin?: string + private readonly gptscriptURL: string + private clientReady: boolean + + constructor() { + this.clientReady = false + this.gptscriptURL = "http://" + (process.env.GPTSCRIPT_URL || "127.0.0.1:9090") + clientCount++ + if (clientCount === 1 && process.env.GPTSCRIPT_DISABLE_SERVER !== "true") { + serverProcess = child_process.spawn(getCmdPath(), ["--listen-address", this.gptscriptURL.replace("http://", "").replace("https://", ""), "sdkserver"], { + env: process.env, + stdio: ["pipe"] + }) - constructor(gptscriptURL?: string, gptscriptBin?: string) { - this.gptscriptURL = gptscriptURL - this.gptscriptBin = gptscriptBin + process.on("exit", (code) => { + serverProcess.stdin?.end() + serverProcess.kill(code) + }) + } + } + + close(): void { + clientCount-- + if (clientCount === 0 && serverProcess) { + serverProcess.kill("SIGTERM") + serverProcess.stdin?.end() + } } listTools(): Promise { @@ -64,12 +71,11 @@ export class Client { } async runBasicCommand(cmd: string): Promise { - const r = new RunSubcommand(cmd, "", "", {}, this.gptscriptBin, this.gptscriptURL) - if (this.gptscriptURL) { - r.request(null) - } else { - await r.exec(["--" + cmd]) + if (!this.clientReady) { + this.clientReady = await this.testGPTScriptURL(20) } + const r = new RunSubcommand(cmd, "", "", {}, this.gptscriptURL) + r.requestNoStream(null) return r.text() } @@ -80,8 +86,11 @@ export class Client { * @param {RunOpts} [opts={}] - The options for running the tool. * @return {Run} The Run object representing the running tool. */ - run(toolName: string, opts: RunOpts = {}): Run { - return (new Run("run", toolName, "", opts, this.gptscriptBin, this.gptscriptURL)).nextChat(opts.input) + async run(toolName: string, opts: RunOpts = {}): Promise { + if (!this.clientReady) { + this.clientReady = await this.testGPTScriptURL(20) + } + return (new Run("run", toolName, "", opts, this.gptscriptURL)).nextChat(opts.input) } /** @@ -91,7 +100,10 @@ export class Client { * @param {RunOpts} [opts={}] - Optional options for the evaluation. * @return {Run} The Run object representing the evaluation. */ - evaluate(tool: ToolDef | ToolDef[] | string, opts: RunOpts = {}): Run { + async evaluate(tool: ToolDef | ToolDef[] | string, opts: RunOpts = {}): Promise { + if (!this.clientReady) { + this.clientReady = await this.testGPTScriptURL(20) + } let toolString: string = "" if (Array.isArray(tool)) { @@ -102,30 +114,31 @@ export class Client { toolString = toolDefToString(tool) } - return (new Run("evaluate", "", toolString, opts, this.gptscriptBin, this.gptscriptURL)).nextChat(opts.input) + return (new Run("evaluate", "", toolString, opts, this.gptscriptURL)).nextChat(opts.input) } async parse(fileName: string): Promise { - const r: Run = new RunSubcommand("parse", fileName, "", {}, this.gptscriptBin, this.gptscriptURL) - if (this.gptscriptURL) { - r.request({file: fileName}) - } else { - await r.exec(["parse"]) + if (!this.clientReady) { + this.clientReady = await this.testGPTScriptURL(20) } + const r: Run = new RunSubcommand("parse", fileName, "", {}, this.gptscriptURL) + r.request({file: fileName}) return parseBlocksFromNodes((await r.json()).nodes) } async parseTool(toolContent: string): Promise { - const r: Run = new RunSubcommand("parse", "", toolContent, {}, this.gptscriptBin, this.gptscriptURL) - if (this.gptscriptURL) { - r.request({content: toolContent}) - } else { - await r.exec(["parse"]) + if (!this.clientReady) { + this.clientReady = await this.testGPTScriptURL(20) } + const r: Run = new RunSubcommand("parse", "", toolContent, {}, this.gptscriptURL) + r.request({content: toolContent}) return parseBlocksFromNodes((await r.json()).nodes) } async stringify(blocks: Block[]): Promise { + if (!this.clientReady) { + this.clientReady = await this.testGPTScriptURL(20) + } const nodes: any[] = [] for (const block of blocks) { @@ -144,14 +157,36 @@ export class Client { } } - const r: Run = new RunSubcommand("fmt", "", JSON.stringify({nodes: nodes}), {}, this.gptscriptBin, this.gptscriptURL) - if (this.gptscriptURL) { - r.request({nodes: nodes}) - } else { - await r.exec(["fmt"]) + const r: Run = new RunSubcommand("fmt", "", JSON.stringify({nodes: nodes}), {}, this.gptscriptURL) + r.request({nodes: nodes}) + return r.text() + } + + async confirm(response: AuthResponse): Promise { + if (!this.clientReady) { + this.clientReady = await this.testGPTScriptURL(20) } + const resp = await fetch(`${this.gptscriptURL}/confirm/${response.id}`, { + method: "POST", + body: JSON.stringify(response) + }) - return r.text() + if (resp.status < 200 || resp.status >= 400) { + throw new Error(`Failed to confirm ${response.id}: ${await resp.text()}`) + } + } + + private async testGPTScriptURL(count: number): Promise { + try { + await fetch(`${this.gptscriptURL}/healthz`) + return true + } catch { + if (count === 0) { + throw new Error("Failed to wait for gptscript to be ready") + } + await new Promise(r => setTimeout(r, 500)) + return this.testGPTScriptURL(count - 1) + } } } @@ -161,32 +196,26 @@ export class Run { public readonly filePath: string public readonly content: string public state: RunState = RunState.Creating - public calls: Call[] = [] + public calls: CallFrame[] = [] public err: string = "" protected stdout?: string - private readonly bin?: string private readonly gptscriptURL?: string private readonly requestPath: string = "" private promise?: Promise - private process?: any - private sse?: SSE - private req?: any + private req?: http.ClientRequest private stderr?: string private callbacks: Record void)[]> = {} private chatState?: string - constructor(subCommand: string, path: string, content: string, opts: RunOpts, bin?: string, gptscriptURL?: string) { + constructor(subCommand: string, path: string, content: string, opts: RunOpts, gptscriptURL?: string) { this.id = randomId("run-") this.requestPath = subCommand this.opts = opts this.filePath = path this.content = content - if (bin) { - this.bin = bin - } this.gptscriptURL = gptscriptURL } @@ -197,7 +226,7 @@ export class Run { let run = this if (run.state !== RunState.Creating) { - run = new (this.constructor as any)(this.requestPath, this.filePath, this.content, this.opts, this.bin, this.gptscriptURL) + run = new (this.constructor as any)(this.requestPath, this.filePath, this.content, this.opts, this.gptscriptURL) } if (this.chatState) { @@ -206,144 +235,15 @@ export class Run { run.chatState = this.opts.chatState } run.opts.input = input - if (run.gptscriptURL) { - if (run.content !== "") { - run.request({content: this.content, chatState: run.chatState}) - } else { - run.request({file: this.filePath, chatState: run.chatState}) - } + if (run.content !== "") { + run.request({content: this.content, chatState: run.chatState}) } else { - run.exec() + run.request({file: this.filePath, chatState: run.chatState}) } return run } - exec(extraArgs: string[] = [], env: NodeJS.Dict = process.env) { - extraArgs.push(...toArgs(this.opts)) - extraArgs.push("--chat-state=" + (this.chatState ? this.chatState : "null")) - this.chatState = undefined - - if (this.filePath) { - extraArgs.push(this.filePath) - } - if (this.content) { - extraArgs.push("-") - } - - if (this.opts.input) { - extraArgs.push(this.opts.input) - } - - this.promise = new Promise(async (resolve, reject) => { - const net = await import("net") - const spawnOptions = {env, stdio: ["pipe", "pipe", "pipe"]} - const server = net.createServer((connection) => { - console.debug("Client connected") - - connection.on("data", (data) => { - this.emitEvent(data.toString()) - }) - - connection.on("end", () => { - server.close() - }) - }) - - - // On Windows, the child process doesn't know which file handles are available to it. - // Therefore, we have to use a named pipe. This is set up with a server. - if (process.platform === "win32") { - const namedPipe = "\\\\.\\pipe\\gptscript-" + Math.floor(Math.random() * 1000000) - server.listen(namedPipe, () => { - console.debug("Server is listening on", namedPipe) - }) - - // Add the named pipe for streaming events. - extraArgs.unshift("--events-stream-to=" + namedPipe) - } else { - // For non-Windows systems, we just add an extra stdio pipe and use that for streaming events. - spawnOptions.stdio.push("pipe") - extraArgs.unshift("--events-stream-to=fd://" + (spawnOptions.stdio.length - 1)) - } - - - const child_process = await import("child_process") - - this.process = child_process.spawn(this.bin || await getCmdPath(), extraArgs, spawnOptions as any) - if (process.platform !== "win32") { - // We don't need the named pipe for streaming events. - server.close() - - // If the child process is not a Windows system, we can use the stdio pipe for streaming events. - if (this.process && this.process.stdio) { - const pipe = this.process.stdio[this.process.stdio.length - 1] - if (pipe) { - let frag = "" - pipe.on("data", (data: any) => { - frag = this.emitEvent(frag + data.toString()) - }) - } else { - console.error("Failed to get event stream") - } - } - } - - if (!this.process) { - this.err = "Run failed to start" - this.state = RunState.Error - server.close() - this.promise = Promise.reject(this.err) - return - } - - // Write to stdin if provided - if (this.process && this.process.stdin) { - this.process.stdin.setDefaultEncoding("utf-8") - if (this.content) { - this.process.stdin.write(this.content) - } - this.process.stdin.end() - } - - this.state = RunState.Running - - if (this.process.stdout) { - let frag = "" - this.process.stdout.on("data", (data: any) => { - frag = this.processStdout(frag + data.toString()) - }) - } - - if (this.process.stderr) { - this.process.stderr.on("data", (data: any) => { - this.stderr = (this.stderr || "") + data - }) - } - - this.process!.on("exit", (code: number | null, signal: NodeJS.Signals | null) => { - server.close() - - if (signal) { - this.err = "Run has been aborted" - this.state = RunState.Error - } else if (code !== 0) { - this.err = this.stderr || "" - this.state = RunState.Error - } else if (this.state !== RunState.Continue) { - this.state = RunState.Finished - } - - if (this.err) { - this.state = RunState.Error - reject(this.err) - } else { - resolve(this.stdout || "") - } - }) - }) - } - processStdout(data: string | object): string { if (typeof data === "string") { if (data.trim() === "") { @@ -373,41 +273,44 @@ export class Run { if (!this.gptscriptURL) { throw new Error("request() requires gptscriptURL to be set") } - const postData = JSON.stringify({...tool, ...this.opts}) - const options = this.requestOptions(this.gptscriptURL, this.requestPath, postData, tool) + const options = this.requestOptions(this.gptscriptURL, this.requestPath, tool) + options.headers = {"Transfer-Encoding": "chunked", ...options.headers} as any this.promise = new Promise(async (resolve, reject) => { - // This checks that the code is running in a browser. If it is, then we use SSE. - if (typeof window !== "undefined" && typeof window.document !== "undefined") { - // @ts-ignore - const {SSE} = await import("sse.js") - this.sse = new SSE(this.gptscriptURL + "/" + this.requestPath, { - headers: {"Content-Type": "application/json"}, - payload: tool ? postData : undefined, - method: tool ? "POST" : "GET" - } as any) - - this.sse.addEventListener("open", () => { - this.state = RunState.Running - }) + let frag = "" + this.req = http.request(options, (res: http.IncomingMessage) => { + this.state = RunState.Running + res.on("data", (chunk: any) => { + for (let line of (frag + chunk.toString()).split("\n")) { + const c = line.replace(/^(data: )/, "").trim() + if (!c) { + continue + } - this.sse.addEventListener("message", (data: any) => { - if (data.data === "[DONE]") { - this.sse!.close() - return - } + if (c === "[DONE]") { + return + } + + let e: any + try { + e = JSON.parse(c) + } catch { + frag = c + return + } - const e = JSON.parse(data.data) - if (e.stderr) { - this.stderr = (this.stderr || "") + (typeof e.stderr === "string" ? e.stderr : JSON.stringify(e.stderr)) - } else if (e.stdout) { - this.stdout = (this.stdout || "") + (typeof e.stdout === "string" ? e.stdout : JSON.stringify(e.stdout)) - } else { - this.emitEvent(data.data) + if (e.stderr) { + this.stderr = (this.stderr || "") + (typeof e.stderr === "string" ? e.stderr : JSON.stringify(e.stderr)) + frag = "" + } else if (e.stdout) { + frag = this.processStdout(e.stdout) + } else { + frag = this.emitEvent(c) + } } }) - this.sse.addEventListener("close", () => { + res.on("end", () => { if (this.state === RunState.Running || this.state === RunState.Finished || this.state === RunState.Continue) { if (this.stdout) { if (this.state !== RunState.Continue) { @@ -423,93 +326,51 @@ export class Run { } }) - this.sse.addEventListener("error", (err: any) => { - this.state = RunState.Error - this.err = err - reject(err) - }) - } else { - // If not in the browser, then we use HTTP. - const http = await import("http") - - // Use frag to keep track of partial object writes. - let frag = "" - this.req = http.request(options, (res: any) => { - this.state = RunState.Running - res.on("data", (chunk: any) => { - for (let line of (chunk.toString() + frag).split("\n")) { - const c = line.replace(/^(data: )/, "").trim() - if (!c) { - continue - } - - if (c === "[DONE]") { - return - } - - let e: any - try { - e = JSON.parse(c) - } catch { - frag = c - return - } - frag = "" - - if (e.stderr) { - this.stderr = (this.stderr || "") + (typeof e.stderr === "string" ? e.stderr : JSON.stringify(e.stderr)) - } else if (e.stdout) { - frag = this.processStdout(e.stdout) - } else { - frag = this.emitEvent(c) - } - } - }) - - res.on("end", () => { - if (this.state === RunState.Running || this.state === RunState.Finished || this.state === RunState.Continue) { - if (this.stdout) { - if (this.state !== RunState.Continue) { - this.state = RunState.Finished - } - resolve(this.stdout) - } else { - this.state = RunState.Error - reject(this.stderr) - } - } else if (this.state === RunState.Error) { - reject(this.err) - } - }) - - res.on("aborted", () => { - if (this.state !== RunState.Finished) { - this.state = RunState.Error - this.err = "Run has been aborted" - reject(this.err) - } - }) - - res.on("error", (error: Error) => { + res.on("aborted", () => { + if (this.state !== RunState.Finished) { this.state = RunState.Error - this.err = error.message || "" + this.err = "Run has been aborted" reject(this.err) - }) + } }) - this.req.on("error", (error: Error) => { + res.on("error", (error: Error) => { this.state = RunState.Error this.err = error.message || "" reject(this.err) }) + }) - this.req.write(postData) - this.req.end() - } + this.req.on("error", (error: Error) => { + this.state = RunState.Error + this.err = error.message || "" + reject(this.err) + }) + + this.req.write(JSON.stringify({...tool, ...this.opts})) + this.req.end() }) } - requestOptions(gptscriptURL: string, path: string, postData: string, tool: any) { + requestNoStream(tool: any) { + if (!this.gptscriptURL) { + throw new Error("request() requires gptscriptURL to be set") + } + + const options = this.requestOptions(this.gptscriptURL, this.requestPath, tool) as any + if (tool) { + options.body = {...tool, ...this.opts} + } + const req = new Request(this.gptscriptURL + "/" + this.requestPath, options) + + this.promise = new Promise(async (resolve, reject) => { + fetch(req).then(resp => resp.json()).then(res => resolve(res.stdout)).catch(e => { + reject(e) + }) + }) + } + + requestOptions(gptscriptURL: string, path: string, tool: any) { let method = "GET" if (tool) { method = "POST" @@ -524,8 +385,7 @@ export class Run { path: "/" + path, method: method, headers: { - "Content-Type": "application/json", - "Content-Length": postData.length + "Content-Type": "application/json" }, } } @@ -539,7 +399,14 @@ export class Run { } let f: Frame try { - f = JSON.parse(event) as Frame + const obj = JSON.parse(event) + if (obj.run) { + f = obj.run as Frame + } else if (obj.call) { + f = obj.call as Frame + } else { + return event + } } catch (error) { return event } @@ -551,54 +418,22 @@ export class Run { if (f.type === RunEventType.RunStart) { this.state = RunState.Running } else if (f.type === RunEventType.RunFinish) { - if (f.err) { + if (f.error) { this.state = RunState.Error - this.err = f.err || "" + this.err = f.error || "" } else { this.state = RunState.Finished this.stdout = f.output || "" } - } else if ((f.type as string).startsWith("call")) { - let call = this.calls?.find((x) => x.id === f.callContext.id) - - if (!call) { - call = { - id: f.callContext.id, - parentID: f.callContext.parentID, - tool: f.callContext.tool, - messages: [], - state: RunState.Running, - chatCompletionId: f.callContext.chatCompletionId, - chatRequest: f.callContext.chatRequest, - } - - this.calls?.push(call) - } - - if (f.type === RunEventType.CallStart) { - call.state = RunState.Creating - call.input = f.content || "" - } else if (f.type === RunEventType.CallChat) { - call.state = RunState.Running - - if (f.chatRequest) { - const more = (f.chatRequest.messages || []).slice(call.messages.length) - - call.messages.push(...more) - } + } else { + if (!(f.type as string).startsWith("call")) continue + f = (f as CallFrame) + const idx = this.calls?.findIndex((x) => x.id === f.id) - if (f.chatResponse) { - call.messages.push(f.chatResponse) - } - } else if (f.type === RunEventType.CallContinue) { - call.state = RunState.Running - } else if (f.type === RunEventType.CallProgress) { - call.state = RunState.Running - - call.output = f.content - } else if (f.type === RunEventType.CallFinish) { - call.state = RunState.Finished - call.output = f.content + if (idx === -1) { + this.calls.push(f) + } else { + this.calls[idx] = f } } @@ -609,13 +444,8 @@ export class Run { return "" } - public on(event: RunEventType.RunStart, listener: (data: RunStartFrame) => void): this; - public on(event: RunEventType.RunFinish, listener: (data: RunFinishFrame) => void): this; - public on(event: RunEventType.CallStart, listener: (data: CallStartFrame) => void): this; - public on(event: RunEventType.CallChat, listener: (data: CallChatFrame) => void): this; - public on(event: RunEventType.CallContinue, listener: (data: CallContinueFrame) => void): this; - public on(event: RunEventType.CallProgress, listener: (data: CallProgressFrame) => void): this; - public on(event: RunEventType.CallFinish, listener: (data: CallFinishFrame) => void): this; + public on(event: RunEventType.RunStart | RunEventType.RunFinish, listener: (data: RunFrame) => void): this; + public on(event: RunEventType.CallStart | RunEventType.CallProgress | RunEventType.CallContinue | RunEventType.CallChat | RunEventType.CallConfirm | RunEventType.CallFinish, listener: (data: CallFrame) => void): this; public on(event: RunEventType.Event, listener: (data: Frame) => void): this; public on(event: RunEventType, listener: (data: any) => void): this { if (!this.callbacks[event]) { @@ -648,23 +478,10 @@ export class Run { } public close(): void { - if (this.process) { - if (this.process.exitCode === null) { - this.process.kill("SIGKILL") - } - return - } - if (this.req) { this.req.destroy() return } - - if (this.sse) { - this.sse.close() - return - } - throw new Error("Run not started") } @@ -676,8 +493,8 @@ export class Run { } class RunSubcommand extends Run { - constructor(subCommand: string, path: string, content: string, opts: RunOpts, bin?: string, gptscriptURL?: string) { - super(subCommand, path, content, opts, bin, gptscriptURL) + constructor(subCommand: string, path: string, content: string, opts: RunOpts, gptscriptURL?: string) { + super(subCommand, path, content, opts, gptscriptURL) } processStdout(data: string | object): string { @@ -709,6 +526,7 @@ export interface ArgumentSchema { export interface Program { name: string blocks: Block[] + openAPICache: Record } export interface Property { @@ -745,16 +563,24 @@ export interface ToolDef { instructions: string } +export interface ToolReference { + named: string + reference: string + arg: string + toolID: string +} + export interface Tool extends ToolDef { id: string type: "tool" - toolMapping: Record + toolMapping: Record localTools: Record source: SourceRef workingDir: string } export interface SourceRef { + location: string lineNo: number repo?: Repo } @@ -776,154 +602,72 @@ export enum RunState { Error = "error" } -export interface Call { +export interface RunFrame { id: string - parentID?: string - chatCompletionId?: string - state: RunState - messages: ChatMessage[] - tool?: Tool - chatRequest?: Record - input?: Arguments - output?: string - showSystemMessages?: boolean -} - -interface BaseFrame { - type: RunEventType - time: string - runID: string -} - -interface CallFrame extends BaseFrame { - callContext: Call - input: Arguments -} - -export interface RunStartFrame extends BaseFrame { - type: RunEventType.RunStart + type: RunEventType.RunStart | RunEventType.RunFinish program: Program + input: string + output: string + error: string + start: string + end: string + state: RunState + chatState: any } -export interface RunFinishFrame extends BaseFrame { - type: RunEventType.RunFinish - input: Arguments - - err?: string - output?: string -} - -export interface CallStartFrame extends CallFrame { - type: RunEventType.CallStart - content: string -} - -export interface CallChatFrame extends CallFrame { - type: RunEventType.CallChat - chatCompletionId: string - chatRequest?: ChatRequest - chatResponse?: ChatMessage - chatResponseCached?: boolean -} - -export interface CallProgressFrame extends CallFrame { - type: RunEventType.CallProgress - chatCompletionId: string - content: string +export interface Call { + toolID: string + input?: string } -export interface CallContinueFrame extends CallFrame { - type: RunEventType.CallContinue - toolResults: number +export interface Output { + content?: string + subCalls: Record } -export interface CallFinishFrame extends CallFrame { - type: RunEventType.CallFinish +export interface InputContext { + toolID: string content: string } -export type Frame = - RunStartFrame - | RunFinishFrame - | CallStartFrame - | CallChatFrame - | CallProgressFrame - | CallContinueFrame - | CallFinishFrame - -export interface ChatRequest { - max_tokens: number - messages: ChatMessage[] - model: string - temperature: string - tools?: ChatTool[] -} - -export interface ChatText { - text: string -} - -export interface ChatToolCall { - toolCall: { - id: string - index: number - type: "function" - function: { - name: string - arguments: string - } - } -} - -enum ChatMessageRole { - System = "system", - Assistant = "assistant", - User = "user", - Tool = "tool" -} - -export interface ChatToolMessage { - role: ChatMessageRole - content: string | (ChatToolCall | ChatText)[] -} - -export interface ChatErrorMessage { - err: string +export interface Usage { + promptTokens: number + completionTokens: number + totalTokens: number } -export interface ChatOutputMessage { - output: string -} - -export type ChatMessage = ChatToolMessage | ChatOutputMessage | ChatErrorMessage - -export interface ChatToolFunction { - type: "function" - function: { - name: string - description: string - parameters: { - type: "object" - properties: Record - } - } +export interface CallFrame { + id: string + tool?: Tool + inputContext: InputContext[] + toolCategory?: string + toolName: string + parentID?: string + type: RunEventType.CallStart | RunEventType.CallChat | RunEventType.CallConfirm | RunEventType.CallContinue | RunEventType.CallSubCalls | RunEventType.CallProgress | RunEventType.CallFinish + start: string + end: string + input: Arguments + output: Output[] + error?: string + usage: Usage + llmRequest?: any + llmResponse?: any } -export type ChatTool = ChatToolFunction +export type Frame = RunFrame | CallFrame -export interface ChatProperty { - type: string - description: string +export interface AuthResponse { + id: string + accept: boolean + message?: string } -async function getCmdPath(): Promise { +function getCmdPath(): string { if (process.env.GPTSCRIPT_BIN) { return process.env.GPTSCRIPT_BIN } - const path = await import("path") - const url = await import("url") - return path.join(path.dirname(url.fileURLToPath(import.meta.url)), "..", "..", "bin", "gptscript") + return path.resolve("..", "..", "bin", "gptscript") } function parseBlocksFromNodes(nodes: any[]): Block[] { diff --git a/tests/gptscript.test.ts b/tests/gptscript.test.ts index d367e08..0660331 100644 --- a/tests/gptscript.test.ts +++ b/tests/gptscript.test.ts @@ -1,13 +1,24 @@ import * as gptscript from "../src/gptscript" import path from "path" -const client = new gptscript.Client(process.env.GPTSCRIPT_URL, process.env.GPTSCRIPT_BIN) +let client: gptscript.Client describe("gptscript module", () => { - beforeAll(() => { + beforeAll(async () => { if (!process.env.OPENAI_API_KEY && !process.env.GPTSCRIPT_URL) { throw new Error("neither OPENAI_API_KEY nor GPTSCRIPT_URL is set") } + + client = new gptscript.Client() + }) + afterAll(() => { + client.close() + }) + + test("creating an closing another client should work", async () => { + const other = new gptscript.Client() + await other.version() + other.close() }) test("listTools returns available tools", async () => { @@ -32,7 +43,7 @@ describe("gptscript module", () => { instructions: "who was the president of the united states in 1928?" } - const run = client.evaluate(t as any) + const run = await client.evaluate(t as any) expect(run).toBeDefined() expect(await run.text()).toContain("Calvin Coolidge") }) @@ -47,17 +58,13 @@ describe("gptscript module", () => { disableCache: true, } - try { - const run = client.evaluate(t as any, opts) - run.on(gptscript.RunEventType.CallProgress, data => { - out += `system: ${(data as any).content}` - }) + const run = await client.evaluate(t as any, opts) + run.on(gptscript.RunEventType.CallProgress, (data: gptscript.CallFrame) => { + for (let output of data.output) out += `system: ${output.content}` + }) - await run.text() - err = run.err - } catch (e) { - console.error(e) - } + await run.text() + err = run.err expect(out).toContain("Calvin Coolidge") expect(err).toEqual("") @@ -71,45 +78,20 @@ describe("gptscript module", () => { context: [path.join(__dirname, "fixtures", "acorn-labs-context.gpt")] } - try { - const run = client.evaluate(t as any, {disableCache: true}) - out = await run.text() - err = run.err - } catch (e) { - console.error(e) - } + const run = await client.evaluate(t as any, {disableCache: true}) + out = await run.text() + err = run.err expect(out).toContain("Acorn Labs") expect(err).toEqual("") }) - describe("run with test.gpt fixture", () => { - test("should execute test.gpt correctly", async () => { - const testGptPath = path.join(__dirname, "fixtures", "test.gpt") - - try { - const result = await client.run(testGptPath).text() - expect(result).toBeDefined() - expect(result).toContain("Calvin Coolidge") - } catch (error) { - console.error(error) - fail("run threw an unexpected error.") - } - }) + test("should execute test.gpt correctly", async () => { + const testGptPath = path.join(__dirname, "fixtures", "test.gpt") - test("should execute test.gpt correctly when chdir is set", async () => { - const testGptPath = path.join(__dirname, "fixtures") - - try { - // By changing the directory here, we should be able to find the test.gpt file without prepending the path. - const result = await client.run("test.gpt", {chdir: testGptPath}).text() - expect(result).toBeDefined() - expect(result).toContain("Calvin Coolidge") - } catch (error) { - console.error(error) - fail("run threw an unexpected error.") - } - }) + const result = await (await client.run(testGptPath)).text() + expect(result).toBeDefined() + expect(result).toContain("Calvin Coolidge") }) test("run executes and stream a file correctly", async () => { @@ -120,16 +102,12 @@ describe("gptscript module", () => { disableCache: true, } - try { - const run = client.run(testGptPath, opts) - run.on(gptscript.RunEventType.CallProgress, data => { - out += `system: ${(data as any).content}` - }) - await run.text() - err = run.err - } catch (e) { - console.error(e) - } + const run = await client.run(testGptPath, opts) + run.on(gptscript.RunEventType.CallProgress, data => { + for (let output of data.output) out += `system: ${output.content}` + }) + await run.text() + err = run.err expect(out).toContain("Calvin Coolidge") expect(err).toEqual("") @@ -143,16 +121,12 @@ describe("gptscript module", () => { disableCache: true, } - try { - const run = client.run(testGptPath, opts) - run.on(gptscript.RunEventType.CallProgress, data => { - out += `system: ${(data as any).content}` - }) - await run.text() - err = run.err - } catch (e) { - console.error(e) - } + const run = await client.run(testGptPath, opts) + run.on(gptscript.RunEventType.CallProgress, data => { + for (let output of data.output) out += `system: ${output.content}` + }) + await run.text() + err = run.err expect(out).toContain("Hello!") expect(err).toEqual("") @@ -167,7 +141,7 @@ describe("gptscript module", () => { } try { - const run = client.run(testGptPath, opts) + const run = await client.run(testGptPath, opts) run.on(gptscript.RunEventType.CallProgress, data => { run.close() }) @@ -197,7 +171,7 @@ describe("gptscript module", () => { instructions: "${question}" } - const response = await client.evaluate([t0 as any, t1 as any]).text() + const response = await (await client.evaluate([t0 as any, t1 as any])).text() expect(response).toBeDefined() expect(response).toContain("Calvin Coolidge") }, 30000) @@ -221,7 +195,7 @@ describe("gptscript module", () => { instructions: "${question}" } as any - const response = await client.evaluate([t0, t1, t2], {subTool: "other"}).text() + const response = await (await client.evaluate([t0, t1, t2], {subTool: "other"})).text() expect(response).toBeDefined() expect(response).toContain("Ronald Reagan") }, 30000) @@ -301,7 +275,7 @@ describe("gptscript module", () => { const opts = { disableCache: true, } - let run = client.evaluate(t as any, opts) + let run = await client.evaluate(t as any, opts) const inputs = [ "List the three largest states in the United States by area.", @@ -315,26 +289,22 @@ describe("gptscript module", () => { "Alaska Time Zone" ] - try { - await run.text() - for (let i: number = 0; i < inputs.length; i++) { - run = run.nextChat(inputs[i]) - err = run.err - - if (err) { - break - } + await run.text() + for (let i: number = 0; i < inputs.length; i++) { + run = run.nextChat(inputs[i]) + err = run.err - expect(await run.text()).toContain(expectedOutputs[i]) - expect(run.state).toEqual(gptscript.RunState.Continue) + if (err) { + break } - run = run.nextChat("bye") - await run.text() - } catch (e) { - console.error(e) + expect(await run.text()).toContain(expectedOutputs[i]) + expect(run.state).toEqual(gptscript.RunState.Continue) } + run = run.nextChat("bye") + await run.text() + expect(run.state).toEqual(gptscript.RunState.Finished) expect(err).toEqual("") }, 60000) @@ -344,7 +314,7 @@ describe("gptscript module", () => { const opts = { disableCache: true } - let run = client.run(path.join(__dirname, "fixtures", "chat.gpt"), opts) + let run = await client.run(path.join(__dirname, "fixtures", "chat.gpt"), opts) const inputs = [ "List the 3 largest of the Great Lakes by volume.", @@ -358,39 +328,35 @@ describe("gptscript module", () => { "Lake Huron" ] - try { - await run.text() - for (let i: number = 0; i < inputs.length; i++) { - run = run.nextChat(inputs[i]) - err = run.err - - if (err) { - break - } + await run.text() + for (let i: number = 0; i < inputs.length; i++) { + run = run.nextChat(inputs[i]) + err = run.err - expect(await run.text()).toContain(expectedOutputs[i]) - expect(run.state).toEqual(gptscript.RunState.Continue) + if (err) { + break } - run = run.nextChat("bye") - await run.text() - } catch (e) { - console.error(e) + expect(await run.text()).toContain(expectedOutputs[i]) + expect(run.state).toEqual(gptscript.RunState.Continue) } + run = run.nextChat("bye") + await run.text() + expect(run.state).toEqual(gptscript.RunState.Finished) expect(err).toEqual("") }, 60000) test("nextChat on file providing chat state", async () => { - let run = client.run(path.join(__dirname, "fixtures", "chat.gpt"), {disableCache: true}) + let run = await client.run(path.join(__dirname, "fixtures", "chat.gpt"), {disableCache: true}) run = run.nextChat("List the 3 largest of the Great Lakes by volume.") expect(await run.text()).toContain("Lake Superior") expect(run.err).toEqual("") expect(run.state).toEqual(gptscript.RunState.Continue) - run = client.run(path.join(__dirname, "fixtures", "chat.gpt"), { + run = await client.run(path.join(__dirname, "fixtures", "chat.gpt"), { disableCache: true, input: "What is the total area of the third one in square miles?", chatState: run.currentChatState() @@ -407,14 +373,14 @@ describe("gptscript module", () => { instructions: "You are a chat bot. Don't finish the conversation until I say 'bye'.", tools: ["sys.chat.finish"] } - let run = client.evaluate(t as any, {disableCache: true}) + let run = await client.evaluate(t as any, {disableCache: true}) run = run.nextChat("List the three largest states in the United States by area.") expect(await run.text()).toContain("California") expect(run.err).toEqual("") expect(run.state).toEqual(gptscript.RunState.Continue) - run = client.evaluate(t as any, { + run = await client.evaluate(t as any, { disableCache: true, input: "What is the capital of the second one?", chatState: run.currentChatState() @@ -424,4 +390,40 @@ describe("gptscript module", () => { expect(run.err).toEqual("") expect(run.state).toEqual(gptscript.RunState.Continue) }, 10000) + + test("confirm", async () => { + let confirmFound = false + const t = { + instructions: "List the files in the current working directory.", + tools: ["sys.exec"] + } + const run = await client.evaluate(t as any, {confirm: true}) + run.on(gptscript.RunEventType.CallConfirm, async (data: gptscript.CallFrame) => { + expect(data.input).toContain(`"ls"`) + confirmFound = true + await client.confirm({id: data.id, accept: true}) + }) + + expect(await run.text()).toContain("README.md") + expect(run.err).toEqual("") + expect(confirmFound).toBeTruthy() + }) + + test("do not confirm", async () => { + let confirmFound = false + const t = { + instructions: "List the files in the current working directory.", + tools: ["sys.exec"] + } + const run = await client.evaluate(t as any, {confirm: true}) + run.on(gptscript.RunEventType.CallConfirm, async (data: gptscript.CallFrame) => { + expect(data.input).toContain(`"ls"`) + confirmFound = true + await client.confirm({id: data.id, accept: false, message: "I will not allow it!"}) + }) + + expect(await run.text()).toContain("authorization error") + expect(run.err).toEqual("") + expect(confirmFound).toBeTruthy() + }) })