├── .gitignore ├── .vscode └── settings.json ├── LICENSE.txt ├── README.md ├── demo.gif ├── node-gpt ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[javascript]": { 3 | "editor.maxTokenizationLineLength": 2500, 4 | "editor.formatOnSave": true 5 | } 6 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Rory Bradford 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-gpt-repl 2 | 3 | ## About 4 | 5 | `node-gpt(1)` is a Node.js REPL with built-in GPT3 completion and _auto-npm install_ on `require()` so you can experiment at the speed of thought! 6 | 7 | ## Installation 8 | 9 | ``` 10 | $ npm install -g node-gpt-repl 11 | ``` 12 | 13 | ## Usage 14 | 15 | Get an OpenAI API key and expose it in your environment: 16 | 17 | ``` 18 | export OPENAI_API_KEY=xxxxxxxxxxxx 19 | ``` 20 | 21 | In your terminal: 22 | 23 | ``` 24 | $ node-gpt 25 | ``` 26 | 27 | As you can see from the demo below the results are pretty crappy at the moment, but I think the idea is solid. Contributions welcome! 28 | 29 | ![Demo](demo.gif) -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roryrjb/node-gpt-repl/884870dac79883845cb7b5c8b9e5dbaccd1cdd95/demo.gif -------------------------------------------------------------------------------- /node-gpt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const HISTORY_FILE = ".node-gpt-history"; 4 | 5 | const { existsSync, readFileSync, writeFileSync } = require("fs"); 6 | 7 | // Node.js somewhere will require the 'debug' module at runtime, I guess 8 | // it just checks for its existence, for now it's just easier to include 9 | // this as a dependency 10 | const debug = require("debug")("node-gpt"); 11 | 12 | const { execSync } = require("child_process"); 13 | const repl = require("repl"); 14 | const { Configuration, OpenAIApi } = require("openai"); 15 | const Module = require("module"); 16 | const originalRequire = Module.prototype.require; 17 | 18 | // override the built-in 'require' so if we get a MODULE_NOT_FOUND error 19 | // then just npm install and hope for the best! 20 | Module.prototype.require = function () { 21 | try { 22 | return originalRequire.apply(this, arguments); 23 | } catch (e) { 24 | if (e.code === "MODULE_NOT_FOUND") { 25 | const command = `npm install ${arguments[0]}`; 26 | execSync(command); 27 | return originalRequire.apply(this, arguments); 28 | } else { 29 | throw e; 30 | } 31 | } 32 | }; 33 | 34 | const configuration = new Configuration({ apiKey: process.env.OPENAI_API_KEY }); 35 | const openai = new OpenAIApi(configuration); 36 | 37 | function getHistory() { 38 | try { 39 | return readFileSync(HISTORY_FILE, "utf8"); 40 | } catch (_) { 41 | return ""; 42 | } 43 | } 44 | 45 | // TODO: needs a lot of tweaking 46 | function generatePrompt(linePartial, history) { 47 | return `With the following code already entered: "${history}". Suggest a completion for the following partial JavaScript code in the Node.js REPL: ${linePartial}`; 48 | } 49 | 50 | // due to the auto-npm install feature, it's just easier to write these 51 | // files upfront, it just saves on some debug output 52 | if (!existsSync("./package.json")) { 53 | writeFileSync("./package.json", JSON.stringify({})); 54 | } 55 | 56 | if (!existsSync("./package-lock.json")) { 57 | writeFileSync("./package-lock.json", JSON.stringify({})); 58 | } 59 | 60 | const server = repl.start({ 61 | prompt: "node-gpt> ", 62 | preview: false, 63 | completer(linePartial, callback) { 64 | openai 65 | .createCompletion({ 66 | // TODO: make this configurable 67 | model: "text-davinci-003", 68 | prompt: generatePrompt(linePartial, getHistory()), 69 | temperature: 0.6, 70 | }) 71 | .then((res) => { 72 | callback(null, [ 73 | res.data.choices.map((c) => linePartial + c.text.trim()), 74 | linePartial, 75 | ]); 76 | }) 77 | .catch((err) => { 78 | console.error(); 79 | console.error(err.message); 80 | callback(null, [[], linePartial]); 81 | }); 82 | }, 83 | }); 84 | 85 | server.setupHistory(HISTORY_FILE, () => {}); 86 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-gpt-repl", 3 | "version": "1.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "asynckit": { 8 | "version": "0.4.0", 9 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 10 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 11 | }, 12 | "axios": { 13 | "version": "0.26.1", 14 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", 15 | "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", 16 | "requires": { 17 | "follow-redirects": "^1.14.8" 18 | } 19 | }, 20 | "combined-stream": { 21 | "version": "1.0.8", 22 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 23 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 24 | "requires": { 25 | "delayed-stream": "~1.0.0" 26 | } 27 | }, 28 | "debug": { 29 | "version": "4.3.4", 30 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 31 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 32 | "requires": { 33 | "ms": "2.1.2" 34 | } 35 | }, 36 | "delayed-stream": { 37 | "version": "1.0.0", 38 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 39 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" 40 | }, 41 | "follow-redirects": { 42 | "version": "1.15.2", 43 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 44 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" 45 | }, 46 | "form-data": { 47 | "version": "4.0.0", 48 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 49 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 50 | "requires": { 51 | "asynckit": "^0.4.0", 52 | "combined-stream": "^1.0.8", 53 | "mime-types": "^2.1.12" 54 | } 55 | }, 56 | "mime-db": { 57 | "version": "1.52.0", 58 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 59 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 60 | }, 61 | "mime-types": { 62 | "version": "2.1.35", 63 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 64 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 65 | "requires": { 66 | "mime-db": "1.52.0" 67 | } 68 | }, 69 | "ms": { 70 | "version": "2.1.2", 71 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 72 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 73 | }, 74 | "openai": { 75 | "version": "3.1.0", 76 | "resolved": "https://registry.npmjs.org/openai/-/openai-3.1.0.tgz", 77 | "integrity": "sha512-v5kKFH5o+8ld+t0arudj833Mgm3GcgBnbyN9946bj6u7bvel4Yg6YFz2A4HLIYDzmMjIo0s6vSG9x73kOwvdCg==", 78 | "requires": { 79 | "axios": "^0.26.0", 80 | "form-data": "^4.0.0" 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-gpt-repl", 3 | "bin": { 4 | "node-gpt": "node-gpt" 5 | }, 6 | "version": "1.0.2", 7 | "license": "ISC", 8 | "author": { 9 | "name": "Rory Bradford", 10 | "email": "roryrjb@gmail.com" 11 | }, 12 | "dependencies": { 13 | "debug": "^4.3.4", 14 | "openai": "^3.1.0" 15 | } 16 | } 17 | --------------------------------------------------------------------------------