remove command and action extension
This commit is contained in:
parent
1e09a858a5
commit
dc1836510d
473
package-lock.json
generated
473
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "devchat",
|
||||
"version": "0.1.22",
|
||||
"version": "0.1.33",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "devchat",
|
||||
"version": "0.1.22",
|
||||
"version": "0.1.33",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.8",
|
||||
"@mantine/core": "^6.0.10",
|
||||
@ -31,7 +31,6 @@
|
||||
"ncp": "^2.0.0",
|
||||
"node-fetch": "^3.3.1",
|
||||
"nonce": "^1.0.4",
|
||||
"openai": "^3.2.1",
|
||||
"quote": "^0.4.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
@ -41,6 +40,7 @@
|
||||
"unified": "^11.0.3",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"uuid": "^9.0.0",
|
||||
"xmlrpc": "^1.3.2",
|
||||
"yaml": "^2.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -90,7 +90,8 @@
|
||||
"vscode-test": "^1.6.1",
|
||||
"webpack": "^5.76.3",
|
||||
"webpack-cli": "^5.0.1",
|
||||
"webpack-dev-server": "^4.13.3"
|
||||
"webpack-dev-server": "^4.13.3",
|
||||
"xmlrpc": "^1.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.75.0"
|
||||
@ -109,16 +110,81 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.21.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
|
||||
"integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz",
|
||||
"integrity": "sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA==",
|
||||
"dependencies": {
|
||||
"@babel/highlight": "^7.18.6"
|
||||
"@babel/highlight": "^7.23.4",
|
||||
"chalk": "^2.4.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/ansi-styles": {
|
||||
"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==",
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/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=="
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame/node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.21.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.7.tgz",
|
||||
@ -170,11 +236,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.5.tgz",
|
||||
"integrity": "sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==",
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.4.tgz",
|
||||
"integrity": "sha512-esuS49Cga3HcThFNebGhlgsrVLkvhqvYDTzgjfFFlHJcIfLe5jFmRRfCQ1KuBfc4Jrtn3ndLgKWAKjBE+IraYQ==",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.21.5",
|
||||
"@babel/types": "^7.23.4",
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jsesc": "^2.5.1"
|
||||
@ -331,31 +397,31 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-environment-visitor": {
|
||||
"version": "7.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz",
|
||||
"integrity": "sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-function-name": {
|
||||
"version": "7.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
|
||||
"integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.20.7",
|
||||
"@babel/types": "^7.21.0"
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/types": "^7.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-hoist-variables": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
|
||||
"integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
|
||||
"integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.18.6"
|
||||
"@babel/types": "^7.22.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@ -481,28 +547,28 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-split-export-declaration": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
|
||||
"integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
|
||||
"version": "7.22.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
||||
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.18.6"
|
||||
"@babel/types": "^7.22.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz",
|
||||
"integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==",
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
|
||||
"integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
|
||||
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@ -544,12 +610,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
|
||||
"integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
|
||||
"integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.18.6",
|
||||
"chalk": "^2.0.0",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -621,9 +687,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.21.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.8.tgz",
|
||||
"integrity": "sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==",
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.4.tgz",
|
||||
"integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==",
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
},
|
||||
@ -1925,31 +1991,31 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
|
||||
"integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
"@babel/parser": "^7.20.7",
|
||||
"@babel/types": "^7.20.7"
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/types": "^7.22.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.5.tgz",
|
||||
"integrity": "sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==",
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.4.tgz",
|
||||
"integrity": "sha512-IYM8wSUwunWTB6tFC2dkKZhxbIjHoWemdK+3f8/wq8aKhbUscxD5MX72ubd90fxvFknaLPeGw5ycU84V1obHJg==",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.21.4",
|
||||
"@babel/generator": "^7.21.5",
|
||||
"@babel/helper-environment-visitor": "^7.21.5",
|
||||
"@babel/helper-function-name": "^7.21.0",
|
||||
"@babel/helper-hoist-variables": "^7.18.6",
|
||||
"@babel/helper-split-export-declaration": "^7.18.6",
|
||||
"@babel/parser": "^7.21.5",
|
||||
"@babel/types": "^7.21.5",
|
||||
"@babel/code-frame": "^7.23.4",
|
||||
"@babel/generator": "^7.23.4",
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-function-name": "^7.23.0",
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"@babel/parser": "^7.23.4",
|
||||
"@babel/types": "^7.23.4",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
@ -1966,12 +2032,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.5.tgz",
|
||||
"integrity": "sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==",
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.4.tgz",
|
||||
"integrity": "sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ==",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.21.5",
|
||||
"@babel/helper-validator-identifier": "^7.19.1",
|
||||
"@babel/helper-string-parser": "^7.23.4",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -4775,9 +4841,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.6.tgz",
|
||||
"integrity": "sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg==",
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
|
||||
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
@ -7264,9 +7330,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/get-func-name": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
|
||||
"integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
|
||||
"integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
@ -11259,23 +11325,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/openai": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-3.2.1.tgz",
|
||||
"integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==",
|
||||
"dependencies": {
|
||||
"axios": "^0.26.0",
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/openai/node_modules/axios": {
|
||||
"version": "0.26.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
|
||||
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.8"
|
||||
}
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
||||
@ -11593,9 +11642,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.23",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
|
||||
"integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==",
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -12955,6 +13004,12 @@
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.23.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
||||
@ -14952,6 +15007,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xmlbuilder": {
|
||||
"version": "8.2.2",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz",
|
||||
"integrity": "sha512-eKRAFz04jghooy8muekqzo8uCSVNeyRedbuJrp0fovbLIi7wlsYtdUn3vBAAPq2Y3/0xMz2WMEUQ8yhVVO9Stw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlrpc": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/xmlrpc/-/xmlrpc-1.3.2.tgz",
|
||||
"integrity": "sha512-jQf5gbrP6wvzN71fgkcPPkF4bF/Wyovd7Xdff8d6/ihxYmgETQYSuTc+Hl+tsh/jmgPLro/Aro48LMFlIyEKKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"sax": "1.2.x",
|
||||
"xmlbuilder": "8.2.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8",
|
||||
"npm": ">=1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
@ -15067,11 +15145,63 @@
|
||||
}
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.21.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
|
||||
"integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz",
|
||||
"integrity": "sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA==",
|
||||
"requires": {
|
||||
"@babel/highlight": "^7.18.6"
|
||||
"@babel/highlight": "^7.23.4",
|
||||
"chalk": "^2.4.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"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==",
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/compat-data": {
|
||||
@ -15114,11 +15244,11 @@
|
||||
}
|
||||
},
|
||||
"@babel/generator": {
|
||||
"version": "7.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.5.tgz",
|
||||
"integrity": "sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==",
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.4.tgz",
|
||||
"integrity": "sha512-esuS49Cga3HcThFNebGhlgsrVLkvhqvYDTzgjfFFlHJcIfLe5jFmRRfCQ1KuBfc4Jrtn3ndLgKWAKjBE+IraYQ==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.21.5",
|
||||
"@babel/types": "^7.23.4",
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jsesc": "^2.5.1"
|
||||
@ -15241,25 +15371,25 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-environment-visitor": {
|
||||
"version": "7.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz",
|
||||
"integrity": "sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ=="
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA=="
|
||||
},
|
||||
"@babel/helper-function-name": {
|
||||
"version": "7.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
|
||||
"integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||
"requires": {
|
||||
"@babel/template": "^7.20.7",
|
||||
"@babel/types": "^7.21.0"
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/types": "^7.23.0"
|
||||
}
|
||||
},
|
||||
"@babel/helper-hoist-variables": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
|
||||
"integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
|
||||
"integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.18.6"
|
||||
"@babel/types": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/helper-member-expression-to-functions": {
|
||||
@ -15352,22 +15482,22 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-split-export-declaration": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
|
||||
"integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
|
||||
"version": "7.22.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
||||
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.18.6"
|
||||
"@babel/types": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/helper-string-parser": {
|
||||
"version": "7.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz",
|
||||
"integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w=="
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
|
||||
"integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ=="
|
||||
},
|
||||
"@babel/helper-validator-identifier": {
|
||||
"version": "7.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
|
||||
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w=="
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A=="
|
||||
},
|
||||
"@babel/helper-validator-option": {
|
||||
"version": "7.21.0",
|
||||
@ -15397,12 +15527,12 @@
|
||||
}
|
||||
},
|
||||
"@babel/highlight": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
|
||||
"integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
|
||||
"integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.18.6",
|
||||
"chalk": "^2.0.0",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -15458,9 +15588,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.21.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.8.tgz",
|
||||
"integrity": "sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA=="
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.4.tgz",
|
||||
"integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ=="
|
||||
},
|
||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
|
||||
"version": "7.18.6",
|
||||
@ -16331,28 +16461,28 @@
|
||||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
|
||||
"integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
"@babel/parser": "^7.20.7",
|
||||
"@babel/types": "^7.20.7"
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/types": "^7.22.15"
|
||||
}
|
||||
},
|
||||
"@babel/traverse": {
|
||||
"version": "7.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.5.tgz",
|
||||
"integrity": "sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==",
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.4.tgz",
|
||||
"integrity": "sha512-IYM8wSUwunWTB6tFC2dkKZhxbIjHoWemdK+3f8/wq8aKhbUscxD5MX72ubd90fxvFknaLPeGw5ycU84V1obHJg==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.21.4",
|
||||
"@babel/generator": "^7.21.5",
|
||||
"@babel/helper-environment-visitor": "^7.21.5",
|
||||
"@babel/helper-function-name": "^7.21.0",
|
||||
"@babel/helper-hoist-variables": "^7.18.6",
|
||||
"@babel/helper-split-export-declaration": "^7.18.6",
|
||||
"@babel/parser": "^7.21.5",
|
||||
"@babel/types": "^7.21.5",
|
||||
"@babel/code-frame": "^7.23.4",
|
||||
"@babel/generator": "^7.23.4",
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-function-name": "^7.23.0",
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"@babel/parser": "^7.23.4",
|
||||
"@babel/types": "^7.23.4",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
@ -16365,12 +16495,12 @@
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.5.tgz",
|
||||
"integrity": "sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==",
|
||||
"version": "7.23.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.4.tgz",
|
||||
"integrity": "sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ==",
|
||||
"requires": {
|
||||
"@babel/helper-string-parser": "^7.21.5",
|
||||
"@babel/helper-validator-identifier": "^7.19.1",
|
||||
"@babel/helper-string-parser": "^7.23.4",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
@ -18554,9 +18684,9 @@
|
||||
"integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.6.tgz",
|
||||
"integrity": "sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg==",
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
|
||||
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
@ -20403,9 +20533,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"get-func-name": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
|
||||
"integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
|
||||
"integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
|
||||
"dev": true
|
||||
},
|
||||
"get-intrinsic": {
|
||||
@ -23136,25 +23266,6 @@
|
||||
"is-wsl": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"openai": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-3.2.1.tgz",
|
||||
"integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==",
|
||||
"requires": {
|
||||
"axios": "^0.26.0",
|
||||
"form-data": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": {
|
||||
"version": "0.26.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
|
||||
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionator": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
||||
@ -23399,9 +23510,9 @@
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.23",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
|
||||
"integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==",
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.3.6",
|
||||
@ -24418,6 +24529,12 @@
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"dev": true
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
|
||||
"dev": true
|
||||
},
|
||||
"scheduler": {
|
||||
"version": "0.23.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
||||
|
11
package.json
11
package.json
@ -2,7 +2,7 @@
|
||||
"name": "devchat",
|
||||
"displayName": "DevChat",
|
||||
"description": "Write prompts, not code",
|
||||
"version": "0.1.33",
|
||||
"version": "0.1.55",
|
||||
"icon": "assets/devchat.png",
|
||||
"publisher": "merico",
|
||||
"engines": {
|
||||
@ -630,7 +630,7 @@
|
||||
"title": "DevChat"
|
||||
},
|
||||
{
|
||||
"command": "devchat.addConext",
|
||||
"command": "devchat.addContext",
|
||||
"title": "Add to DevChat"
|
||||
},
|
||||
{
|
||||
@ -727,7 +727,7 @@
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "devchat.addConext",
|
||||
"command": "devchat.addContext",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
@ -759,7 +759,7 @@
|
||||
},
|
||||
{
|
||||
"when": "!isChineseLocale && resourceLangId != 'git'",
|
||||
"command": "devchat.addConext",
|
||||
"command": "devchat.addContext",
|
||||
"group": "navigation"
|
||||
}
|
||||
],
|
||||
@ -874,7 +874,6 @@
|
||||
"ncp": "^2.0.0",
|
||||
"node-fetch": "^3.3.1",
|
||||
"nonce": "^1.0.4",
|
||||
"openai": "^3.2.1",
|
||||
"quote": "^0.4.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
@ -886,4 +885,4 @@
|
||||
"uuid": "^9.0.0",
|
||||
"yaml": "^2.3.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,211 +0,0 @@
|
||||
import fs from 'fs';
|
||||
|
||||
import { Action, CustomActions, getActionInstruction } from './customAction';
|
||||
|
||||
import { CommandResult } from '../util/commonUtil';
|
||||
import { logger } from '../util/logger';
|
||||
|
||||
import { SymbolRefAction } from './symbolRefAction';
|
||||
import { SymbolDefAction } from './symbolDefAction';
|
||||
import { AskInputAction } from './askInputAction';
|
||||
import { SymbolInFileAction } from './symbolInFileAction';
|
||||
import { CurrentDocumentAction } from './currentDocumentAction';
|
||||
import { SelectTextAction, SelectBlockAction } from './selectContextAction';
|
||||
import { RunVSCodeCommandAction } from './runVSCodeCommand';
|
||||
|
||||
|
||||
// extend Action
|
||||
export class CommandRunAction implements Action {
|
||||
name: string;
|
||||
description: string;
|
||||
type: string[];
|
||||
action: string;
|
||||
handler: string[];
|
||||
args: { "name": string, "description": string, "type": string, "as"?: string, "from": string }[];
|
||||
|
||||
constructor() {
|
||||
this.name = 'command_run';
|
||||
this.description = 'run command';
|
||||
this.type = ['command'];
|
||||
this.action = 'command_run';
|
||||
this.handler = [];
|
||||
this.args = [
|
||||
{"name": "content", "description": "command json to run", "type": "string", "from": "content.content"},
|
||||
];
|
||||
}
|
||||
|
||||
async handlerAction(args: {[key: string]: any}): Promise<CommandResult> {
|
||||
try {
|
||||
const commandData = JSON.parse(args.content);
|
||||
const result = await ActionManager.getInstance().applyCommandAction(commandData.name, commandData.arguments);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.channel()?.error('Failed to parse code file content: ' + error);
|
||||
logger.channel()?.show();
|
||||
return {exitCode: -1, stdout: '', stderr: `Failed to parse code file content: ${error}`};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default class ActionManager {
|
||||
private static instance: ActionManager;
|
||||
private actions: Action[] = [];
|
||||
|
||||
private constructor() { }
|
||||
|
||||
public static getInstance(): ActionManager {
|
||||
if (!ActionManager.instance) {
|
||||
ActionManager.instance = new ActionManager();
|
||||
|
||||
ActionManager.instance.registerAction(new CommandRunAction());
|
||||
ActionManager.instance.registerAction(new SymbolRefAction());
|
||||
ActionManager.instance.registerAction(new SymbolDefAction());
|
||||
ActionManager.instance.registerAction(new AskInputAction());
|
||||
ActionManager.instance.registerAction(new SymbolInFileAction());
|
||||
ActionManager.instance.registerAction(new CurrentDocumentAction());
|
||||
ActionManager.instance.registerAction(new SelectTextAction());
|
||||
ActionManager.instance.registerAction(new SelectBlockAction());
|
||||
ActionManager.instance.registerAction(new RunVSCodeCommandAction());
|
||||
}
|
||||
|
||||
return ActionManager.instance;
|
||||
}
|
||||
|
||||
public registerAction(action: Action): void {
|
||||
const existAction = this.actions.find(a => a.name === action.name);
|
||||
if (existAction) {
|
||||
return ;
|
||||
}
|
||||
this.actions.push(action);
|
||||
}
|
||||
|
||||
public getActionList(): Action[] {
|
||||
return this.actions;
|
||||
}
|
||||
|
||||
public async applyAction(actionName: string, content: { "command": string, content: string, fileName: string }): Promise<CommandResult> {
|
||||
const action = this.actions.find(action => action.name.trim() === actionName.trim());
|
||||
if (!action) {
|
||||
logger.channel()?.info(`Action not found: ${actionName}`);
|
||||
return {exitCode: -1, stdout: '', stderr: `${actionName} not found in action list: ${this.actions.map(action => action.name)}`};
|
||||
}
|
||||
|
||||
logger.channel()?.info(`Apply action: ${actionName}`);
|
||||
|
||||
// action.args define what args should be passed to handler
|
||||
// for example:
|
||||
// action.args = [
|
||||
// {"name": "arg1", "description": "arg1 description", "type": "string", "from": "content.fileName"},
|
||||
// {"name": "arg2", "description": "arg2 description", "type": "string", "from": "content.content.v1"}]
|
||||
// then:
|
||||
// arg1 = content.fileName
|
||||
// arg2 = content.content.v1
|
||||
// before use content.content.v1, we should parse content.content first as json
|
||||
|
||||
if (action.args === undefined || action.args.length === 0) {
|
||||
// every action should have args, if not, then it is invalid
|
||||
logger.channel()?.error(`Action ${actionName} has no args`);
|
||||
logger.channel()?.show();
|
||||
return {exitCode: -1, stdout: '', stderr: `Action ${actionName} has no args`};
|
||||
}
|
||||
|
||||
// construct args for handler
|
||||
let args: {[key: string]: any} = {};
|
||||
|
||||
// check whether action.args has x.x.x like from value
|
||||
let hasDotDotFrom = false;
|
||||
for (const arg of action.args) {
|
||||
if (arg.from !== undefined) {
|
||||
// if arg.from has two or more dot, then it is x.x.x
|
||||
if (arg.from.split('.').length >= 3) {
|
||||
hasDotDotFrom = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if hasDotDotFrom is true, then parse content as json
|
||||
if (hasDotDotFrom) {
|
||||
try {
|
||||
content.content = JSON.parse(content.content);
|
||||
} catch (error) {
|
||||
logger.channel()?.info(`Parse content as json failed: ${error}`);
|
||||
return {exitCode: -1, stdout: '', stderr: `Parse content as json failed: ${error}`};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (const arg of action.args) {
|
||||
let argValue = '';
|
||||
if (arg.from !== undefined) {
|
||||
// visit arg.from, it is string
|
||||
let argFromValue: any = content;
|
||||
const argFrom = arg.from.split('.');
|
||||
// first item of argFrom is content, so skip it
|
||||
for (const argFromItem of argFrom.slice(1)) {
|
||||
argFromValue = argFromValue[argFromItem];
|
||||
}
|
||||
// if argFromValue is undefined, then it is invalid
|
||||
if (argFromValue === undefined) {
|
||||
logger.channel()?.error(`Action ${actionName} arg ${arg.name} from ${arg.from} is undefined`);
|
||||
logger.channel()?.show();
|
||||
return {exitCode: -1, stdout: '', stderr: `Action ${actionName} arg ${arg.name} from ${arg.from} is undefined`};
|
||||
}
|
||||
argValue = argFromValue;
|
||||
}
|
||||
args[arg.name] = argValue;
|
||||
}
|
||||
|
||||
return await action.handlerAction(args);
|
||||
}
|
||||
|
||||
public async applyCommandAction(command: string, args: {[key: string]: any}) : Promise<CommandResult> {
|
||||
const action = this.actions.find(action => action.name.trim() === command.trim());
|
||||
if (!action) {
|
||||
logger.channel()?.info(`Action not found: ${command}`);
|
||||
return {exitCode: -1, stdout: '', stderr: `${command} not found in action list: ${this.actions.map(action => action.name)}`};
|
||||
}
|
||||
|
||||
logger.channel()?.info(`Apply command action: ${command}`);
|
||||
|
||||
return await action.handlerAction(args);
|
||||
}
|
||||
|
||||
public loadCustomActions(workflowsDir: string): void {
|
||||
const customActionsInstance = CustomActions.getInstance();
|
||||
customActionsInstance.parseActions(workflowsDir);
|
||||
|
||||
for (const customAction of customActionsInstance.getActions()) {
|
||||
const chatAction: Action = customAction;
|
||||
this.registerAction(chatAction);
|
||||
}
|
||||
}
|
||||
|
||||
public actionInstruction(): string {
|
||||
let functionsDefList : {[key: string]: any}[] = [];
|
||||
for (const action of this.actions) {
|
||||
try {
|
||||
if (action.name === "command_run") {
|
||||
continue;
|
||||
}
|
||||
functionsDefList.push(getActionInstruction(action));
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`Failed to get action instruction: ${error}`);
|
||||
logger.channel()?.show();
|
||||
}
|
||||
}
|
||||
|
||||
// return as json string
|
||||
return JSON.stringify(functionsDefList, null, 4);
|
||||
}
|
||||
|
||||
public saveActionInstructionFile(tarFile: string): void {
|
||||
try {
|
||||
fs.writeFileSync(tarFile, this.actionInstruction());
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`Failed to save action instruction file: ${error}`);
|
||||
logger.channel()?.show();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
|
||||
import { Action, CustomActions } from './customAction';
|
||||
|
||||
import { CommandResult } from '../util/commonUtil';
|
||||
import { logger } from '../util/logger';
|
||||
import { UiUtilVscode } from '../util/uiUtil_vscode';
|
||||
import { UiUtilWrapper } from '../util/uiUtil';
|
||||
|
||||
|
||||
export class AskInputAction implements Action {
|
||||
name: string;
|
||||
description: string;
|
||||
type: string[];
|
||||
action: string;
|
||||
handler: string[];
|
||||
args: { "name": string, "description": string, "type": string, "as"?: string, "from": string }[];
|
||||
|
||||
constructor() {
|
||||
this.name = 'ask_input';
|
||||
this.description = 'Ask user a question to when you need the user to input something';
|
||||
this.type = ['question'];
|
||||
this.action = 'ask_input';
|
||||
this.handler = [];
|
||||
this.args = [
|
||||
{"name": "question", "description": "The question you asked.", "type": "string", "from": "content.content.question"},
|
||||
];
|
||||
}
|
||||
|
||||
async handlerAction(args: {[key: string]: any}): Promise<CommandResult> {
|
||||
try {
|
||||
const question = args.question;
|
||||
|
||||
const answer: string | undefined = await UiUtilWrapper.showInputBox({
|
||||
title: question,
|
||||
placeHolder: "Please input your answer here."
|
||||
});
|
||||
if (answer === undefined) {
|
||||
return {exitCode: -1, stdout: '', stderr: ``};
|
||||
} else {
|
||||
return {exitCode: 0, stdout: answer, stderr: ""};
|
||||
}
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`${this.name} handle error: ${error}`);
|
||||
logger.channel()?.show();
|
||||
return {exitCode: -1, stdout: '', stderr: `${this.name} handle error: ${error}`};
|
||||
}
|
||||
}
|
||||
};
|
@ -1,51 +0,0 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { Action } from './customAction';
|
||||
import { CommandResult } from '../util/commonUtil';
|
||||
import { logger } from '../util/logger';
|
||||
import * as fs from 'fs';
|
||||
|
||||
export class CurrentDocumentAction implements Action {
|
||||
name: string;
|
||||
description: string;
|
||||
type: string[];
|
||||
action: string;
|
||||
handler: string[];
|
||||
args: { "name": string, "description": string, "type": string, "as"?: string, "required": boolean, "from": string }[];
|
||||
|
||||
constructor() {
|
||||
this.name = 'current_document';
|
||||
this.description = 'Get current active document';
|
||||
this.type = ['None'];
|
||||
this.action = 'current_document';
|
||||
this.handler = [];
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
async handlerAction(args: {[key: string]: any}): Promise<CommandResult> {
|
||||
try {
|
||||
const editors = vscode.window.visibleTextEditors;
|
||||
// get editor with file existed in file system
|
||||
const editor = editors.find(editor => fs.existsSync(editor.document.fileName));
|
||||
|
||||
if (editor) {
|
||||
const documentText = editor.document.getText();
|
||||
|
||||
const data = {
|
||||
path: editor.document.fileName,
|
||||
content: documentText
|
||||
};
|
||||
const jsonData = JSON.stringify(data);
|
||||
|
||||
return {exitCode: 0, stdout: JSON.stringify(data), stderr: ""};
|
||||
} else {
|
||||
return {exitCode: -1, stdout: "", stderr: "No active editor"};
|
||||
}
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`${this.name} handle error: ${error}`);
|
||||
logger.channel()?.show();
|
||||
return {exitCode: -1, stdout: '', stderr: `${this.name} handle error: ${error}`};
|
||||
}
|
||||
}
|
||||
};
|
@ -1,155 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { logger } from '../util/logger';
|
||||
import { CommandResult, createTempSubdirectory, runCommandAndWriteOutput, runCommandStringAndWriteOutput } from '../util/commonUtil';
|
||||
import { UiUtilWrapper } from '../util/uiUtil';
|
||||
|
||||
export interface Action {
|
||||
name: string;
|
||||
description: string;
|
||||
type: string[];
|
||||
action: string;
|
||||
handler: string[];
|
||||
args: { "name": string, "description": string, "type": string, "as"?: string, "from": string }[];
|
||||
|
||||
handlerAction: (args: { [key: string]: string }) => Promise<CommandResult>;
|
||||
}
|
||||
|
||||
// generate instruction for action
|
||||
export function getActionInstruction(action: Action): { name: string, description: string, parameters: any } {
|
||||
logger.channel()?.info(`Action Name: ${action.name}`);
|
||||
const actionSchema = {
|
||||
name: action.name,
|
||||
description: action.description,
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: action.args.reduce((obj: any, arg: any) => {
|
||||
obj[arg.name] = {
|
||||
type: arg.type,
|
||||
description: arg.description
|
||||
};
|
||||
return obj;
|
||||
}, {}),
|
||||
required: action.args.filter((arg: any) => arg.required).map((arg: any) => arg.name)
|
||||
}
|
||||
};
|
||||
|
||||
return actionSchema;
|
||||
}
|
||||
|
||||
export class CustomActions {
|
||||
private static instance: CustomActions | null = null;
|
||||
private actions: Action[] = [];
|
||||
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
public static getInstance(): CustomActions {
|
||||
if (!CustomActions.instance) {
|
||||
CustomActions.instance = new CustomActions();
|
||||
}
|
||||
return CustomActions.instance;
|
||||
}
|
||||
|
||||
public parseActions(workflowsDir: string): void {
|
||||
this.actions = [];
|
||||
|
||||
try {
|
||||
const extensionDirs = fs.readdirSync(workflowsDir, { withFileTypes: true })
|
||||
.filter(dirent => dirent.isDirectory())
|
||||
.map(dirent => dirent.name);
|
||||
|
||||
for (const extensionDir of extensionDirs) {
|
||||
const actionDir = path.join(workflowsDir, extensionDir, 'action');
|
||||
if (fs.existsSync(actionDir)) {
|
||||
const actionSubDirs = fs.readdirSync(actionDir, { withFileTypes: true })
|
||||
.filter(dirent => dirent.isDirectory())
|
||||
.map(dirent => dirent.name);
|
||||
|
||||
for (const actionSubDir of actionSubDirs) {
|
||||
const settingsPath = path.join(actionDir, actionSubDir, '_setting_.json');
|
||||
if (fs.existsSync(settingsPath)) {
|
||||
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
||||
const action: Action = {
|
||||
name: settings.name,
|
||||
description: settings.description,
|
||||
type: settings.type,
|
||||
action: settings.action,
|
||||
args: settings.args,
|
||||
handler: settings.handler.map((handler: string) => handler.replace('${CurDir}', path.join(actionDir, actionSubDir))),
|
||||
|
||||
handlerAction: async (args: { [key: string]: string }) => {
|
||||
// Implement the handler logic for the custom action
|
||||
const tempDir = await createTempSubdirectory('devchat/action');
|
||||
const tempFile = path.join(tempDir, 'apply.json');
|
||||
|
||||
if (UiUtilWrapper.getConfiguration('DevChat', 'PythonForCommands')) {
|
||||
args['PythonForCommands'] = UiUtilWrapper.getConfiguration('DevChat', 'PythonForCommands')!;
|
||||
} else {
|
||||
args['PythonForCommands'] = 'python';
|
||||
}
|
||||
|
||||
const contextMap = {
|
||||
'codeBlock': args,
|
||||
'workspaceDir': UiUtilWrapper.workspaceFoldersFirstPath(),
|
||||
'activeFile': UiUtilWrapper.activeFilePath(),
|
||||
'selectRang': UiUtilWrapper.selectRange(),
|
||||
'secectText': UiUtilWrapper.selectText(),
|
||||
};
|
||||
|
||||
// Save contextMap to temp file
|
||||
await UiUtilWrapper.writeFile(tempFile, JSON.stringify(contextMap));
|
||||
|
||||
// replace ${contextFile} with tempFile for arg in handler
|
||||
let handlerArgs = action.handler.map(arg => arg.replace('${contextFile}', tempFile));
|
||||
if (args !== undefined) {
|
||||
// visit args, it is {[key: string]: string}
|
||||
for (const arg in args) {
|
||||
let argValue = args[arg];
|
||||
const argDefine = action.args.find(v => v.name === arg);
|
||||
if (argDefine !== undefined && argDefine.as !== undefined) {
|
||||
// save argValue to temp file
|
||||
const tempFile = path.join(tempDir, argDefine.as);
|
||||
await UiUtilWrapper.writeFile(tempFile, argValue);
|
||||
argValue = tempFile;
|
||||
}
|
||||
// replace ${arg} with commandObj.args[arg]
|
||||
handlerArgs = handlerArgs.map(v => { if (v === '${' + arg + '}') { return argValue; } else { return v; } });
|
||||
}
|
||||
}
|
||||
handlerArgs = handlerArgs.flat();
|
||||
|
||||
// run handler
|
||||
let result: CommandResult = { exitCode: -1, stdout: '', stderr: '' };
|
||||
if (handlerArgs.length === 1) {
|
||||
result = await runCommandStringAndWriteOutput(handlerArgs[0], undefined);
|
||||
} else if (handlerArgs.length > 1) {
|
||||
result = await runCommandAndWriteOutput(handlerArgs[0], handlerArgs.slice(1), undefined);
|
||||
}
|
||||
logger.channel()?.info(`Apply action: ${action.name} exit code:`, result.exitCode);
|
||||
logger.channel()?.info(`stdout:`, result.stdout);
|
||||
logger.channel()?.info(`stderr:`, result.stderr);
|
||||
|
||||
// remove temp file
|
||||
if (fs.existsSync(tempFile)) {
|
||||
fs.unlinkSync(tempFile);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
};
|
||||
this.actions.push(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Show error message
|
||||
logger.channel()?.error(`Failed to parse actions: ${error}`);
|
||||
logger.channel()?.show();
|
||||
}
|
||||
}
|
||||
|
||||
public getActions(): Action[] {
|
||||
return this.actions;
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
执行vscode command,类名:RunVSCommandAction
|
||||
*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Action } from './customAction';
|
||||
import { CommandResult } from '../util/commonUtil';
|
||||
import { logger } from '../util/logger';
|
||||
|
||||
export class RunVSCodeCommandAction implements Action {
|
||||
name: string;
|
||||
description: string;
|
||||
type: string[];
|
||||
action: string;
|
||||
handler: string[];
|
||||
args: { "name": string, "description": string, "type": string, "as"?: string, "required": boolean, "from": string }[];
|
||||
|
||||
constructor() {
|
||||
this.name = 'run_vscode_command';
|
||||
this.description = 'Run VSCode command';
|
||||
this.type = ['command'];
|
||||
this.action = 'run_vscode_command';
|
||||
this.handler = [];
|
||||
this.args = [
|
||||
{
|
||||
"name": "command",
|
||||
"description": 'VSCode command to run.',
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"from": "content.content.command"
|
||||
},
|
||||
{
|
||||
"name": "args",
|
||||
"description": 'Arguments for the command, separated by comma.',
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"from": "content.content.args"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
async handlerAction(args: {[key: string]: any}): Promise<CommandResult> {
|
||||
try {
|
||||
const commandArgs = args.args ? args.args.split(',') : [];
|
||||
if (args.command === 'vscode.open' && commandArgs.length > 0) {
|
||||
commandArgs[0] = vscode.Uri.file(commandArgs[0]);
|
||||
}
|
||||
const result = await vscode.commands.executeCommand(args.command, ...commandArgs);
|
||||
return {exitCode: 0, stdout: JSON.stringify(result), stderr: ""};
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`Failed to run VSCode command: ${error}`);
|
||||
logger.channel()?.show();
|
||||
return {exitCode: -1, stdout: '', stderr: `Failed to run VSCode command: ${error}`};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { Action } from './customAction';
|
||||
import { CommandResult } from '../util/commonUtil';
|
||||
import { logger } from '../util/logger';
|
||||
import * as fs from 'fs';
|
||||
|
||||
export class SelectTextAction implements Action {
|
||||
name: string;
|
||||
description: string;
|
||||
type: string[];
|
||||
action: string;
|
||||
handler: string[];
|
||||
args: { "name": string, "description": string, "type": string, "as"?: string, "required": boolean, "from": string }[];
|
||||
|
||||
constructor() {
|
||||
this.name = 'select_text';
|
||||
this.description = 'Selected text in active document, only include text user selected.';
|
||||
this.type = ['None'];
|
||||
this.action = 'select_text';
|
||||
this.handler = [];
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
async handlerAction(args: {[key: string]: any}): Promise<CommandResult> {
|
||||
try {
|
||||
const editors = vscode.window.visibleTextEditors;
|
||||
// get editor with file existed in file system
|
||||
const editor = editors.find(editor => fs.existsSync(editor.document.fileName));
|
||||
|
||||
|
||||
if (editor) {
|
||||
const selectedText = editor.document.getText(editor.selection);
|
||||
|
||||
const data = {
|
||||
path: editor.document.fileName,
|
||||
startLine: editor.selection.start.line,
|
||||
startColumn: editor.selection.start.character,
|
||||
endLine: editor.selection.end.line,
|
||||
endColumn: editor.selection.end.character,
|
||||
content: selectedText
|
||||
};
|
||||
|
||||
return {exitCode: 0, stdout: JSON.stringify(data), stderr: ""};
|
||||
} else {
|
||||
return {exitCode: -1, stdout: "", stderr: "No active editor"};
|
||||
}
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`${this.name} handle error: ${error}`);
|
||||
logger.channel()?.show();
|
||||
return {exitCode: -1, stdout: '', stderr: `${this.name} handle error: ${error}`};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export class SelectBlockAction implements Action {
|
||||
name: string;
|
||||
description: string;
|
||||
type: string[];
|
||||
action: string;
|
||||
handler: string[];
|
||||
args: { "name": string, "description": string, "type": string, "as"?: string, "required": boolean, "from": string }[];
|
||||
|
||||
constructor() {
|
||||
this.name = 'select_block';
|
||||
this.description = 'Select block in active document. For example, select a function name, then return the whole function.';
|
||||
this.type = ['None'];
|
||||
this.action = 'select_block';
|
||||
this.handler = [];
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
async handlerAction(args: {[key: string]: any}): Promise<CommandResult> {
|
||||
try {
|
||||
const editors = vscode.window.visibleTextEditors;
|
||||
// get editor with file existed in file system
|
||||
const editor:vscode.TextEditor | undefined = editors.find(editor => fs.existsSync(editor.document.fileName));
|
||||
if (!editor) {
|
||||
return {exitCode: -1, stdout: "", stderr: "No active editor"};
|
||||
}
|
||||
|
||||
// get all symbols in active document
|
||||
const symbolsT: vscode.DocumentSymbol[] = await vscode.commands.executeCommand<vscode.DocumentSymbol[]>(
|
||||
'vscode.executeDocumentSymbolProvider',
|
||||
editor.document.uri
|
||||
);
|
||||
if (!symbolsT) {
|
||||
return {exitCode: -1, stdout: "", stderr: "No parent block found"};
|
||||
}
|
||||
|
||||
let symbolsList: vscode.DocumentSymbol[] = [];
|
||||
const visitSymbol = (symbol: vscode.DocumentSymbol) => {
|
||||
symbolsList.push(symbol);
|
||||
if (symbol.children) {
|
||||
for (const child of symbol.children) {
|
||||
visitSymbol(child);
|
||||
}
|
||||
}
|
||||
};
|
||||
for (const symbol of symbolsT) {
|
||||
visitSymbol(symbol);
|
||||
}
|
||||
|
||||
// visit symbolsList, and find the symbol which contains the selected text
|
||||
let symbol: vscode.DocumentSymbol | undefined = undefined;
|
||||
for (const symbolT of symbolsList.reverse()) {
|
||||
if (symbolT.range.contains(editor.selection)) {
|
||||
symbol = symbolT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!symbol) {
|
||||
return {exitCode: -1, stdout: "", stderr: "No parent block found"};
|
||||
}
|
||||
|
||||
const data = {
|
||||
path: editor.document.fileName,
|
||||
startLine: symbol.range.start.line,
|
||||
startColumn: symbol.range.start.character,
|
||||
endLine: symbol.range.end.line,
|
||||
endColumn: symbol.range.end.character,
|
||||
content: editor.document.getText(symbol.range)
|
||||
};
|
||||
return {exitCode: 0, stdout: JSON.stringify(data), stderr: ""};
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`${this.name} handle error: ${error}`);
|
||||
logger.channel()?.show();
|
||||
return {exitCode: -1, stdout: '', stderr: `${this.name} handle error: ${error}`};
|
||||
}
|
||||
}
|
||||
};
|
@ -1,179 +0,0 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { Action, CustomActions } from './customAction';
|
||||
|
||||
import { CommandResult } from '../util/commonUtil';
|
||||
import { logger } from '../util/logger';
|
||||
import { handleCodeSelected } from '../context/contextCodeSelected';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { UiUtilWrapper } from '../util/uiUtil';
|
||||
|
||||
import path from 'path';
|
||||
import { getSymbolPosition } from './symbolRefAction';
|
||||
|
||||
const readFile = util.promisify(fs.readFile);
|
||||
|
||||
|
||||
async function findSymbolInWorkspace(symbolName: string, symbolline: number, symbolFile: string): Promise<string[]> {
|
||||
const symbolPosition = await getSymbolPosition(symbolName, symbolline, symbolFile);
|
||||
if (!symbolPosition) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// get definition of symbol
|
||||
const refLocations = await vscode.commands.executeCommand<vscode.Location[] | vscode.LocationLink[]>(
|
||||
'vscode.executeDefinitionProvider',
|
||||
vscode.Uri.file(symbolFile),
|
||||
symbolPosition
|
||||
);
|
||||
if (!refLocations) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// get related source lines
|
||||
const contextListPromises = refLocations.map(async (refLocation) => {
|
||||
let refLocationFile: string;
|
||||
let targetRange: vscode.Range;
|
||||
|
||||
if (refLocation instanceof vscode.Location) {
|
||||
refLocationFile = refLocation.uri.fsPath;
|
||||
targetRange = refLocation.range;
|
||||
|
||||
// get symbols in file
|
||||
const symbols = await vscode.commands.executeCommand<vscode.DocumentSymbol[]>(
|
||||
'vscode.executeDocumentSymbolProvider',
|
||||
vscode.Uri.file(refLocationFile)
|
||||
);
|
||||
|
||||
// find the smallest symbol definition that contains the target range
|
||||
let smallestSymbol: vscode.DocumentSymbol | undefined;
|
||||
for (const symbol of symbols) {
|
||||
smallestSymbol = findSmallestSymbol(symbol, targetRange, smallestSymbol);
|
||||
}
|
||||
|
||||
if (smallestSymbol) {
|
||||
targetRange = smallestSymbol.range;
|
||||
}
|
||||
} else {
|
||||
refLocationFile = refLocation.targetUri.fsPath;
|
||||
targetRange = refLocation.targetRange;
|
||||
}
|
||||
|
||||
const documentNew = await vscode.workspace.openTextDocument(refLocationFile);
|
||||
|
||||
const data = {
|
||||
path: refLocationFile,
|
||||
start_line: targetRange.start.line,
|
||||
end_line: targetRange.end.line,
|
||||
content: documentNew.getText(targetRange)
|
||||
};
|
||||
return JSON.stringify(data);
|
||||
});
|
||||
|
||||
const contextList = await Promise.all(contextListPromises);
|
||||
|
||||
return contextList;
|
||||
}
|
||||
|
||||
function findSmallestSymbol(symbol: vscode.DocumentSymbol, targetRange: vscode.Range, smallestSymbol: vscode.DocumentSymbol | undefined): vscode.DocumentSymbol | undefined {
|
||||
if (symbol.range.contains(targetRange) &&
|
||||
(!smallestSymbol || smallestSymbol.range.contains(symbol.range))) {
|
||||
smallestSymbol = symbol;
|
||||
}
|
||||
|
||||
for (const child of symbol.children) {
|
||||
smallestSymbol = findSmallestSymbol(child, targetRange, smallestSymbol);
|
||||
}
|
||||
|
||||
return smallestSymbol;
|
||||
}
|
||||
|
||||
export class SymbolDefAction implements Action {
|
||||
name: string;
|
||||
description: string;
|
||||
type: string[];
|
||||
action: string;
|
||||
handler: string[];
|
||||
args: { "name": string, "description": string, "type": string, "as"?: string, "required": boolean, "from": string }[];
|
||||
|
||||
constructor() {
|
||||
this.name = 'symbol_def';
|
||||
this.description = `
|
||||
Function Purpose: This function retrieves the definition information for a given symbol.
|
||||
Input: The symbol should not be in string format or in 'a.b' format. To find the definition of 'a.b', simply refer to 'b'.
|
||||
Output: The function returns a dictionary with the following keys:
|
||||
'exitCode': If 'exitCode' is 0, the function execution was successful. If 'exitCode' is not 0, the function execution failed.
|
||||
'stdout': If the function executes successfully, 'stdout' is a list of JSON strings. Each JSON string contains:
|
||||
'path': The file path.
|
||||
'start_line': The start line of the content.
|
||||
'end_line': The end line of the content.
|
||||
'content': The source code related to the definition.
|
||||
'stderr': Error output if any.
|
||||
Error Handling: If the function execution fails, 'exitCode' will not be 0 and 'stderr' will contain the error information.`;
|
||||
this.type = ['symbol'];
|
||||
this.action = 'symbol_def';
|
||||
this.handler = [];
|
||||
this.args = [
|
||||
{
|
||||
"name": "symbol",
|
||||
"description": "The symbol variable specifies the symbol for which definition information is to be retrieved.",
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"from": "content.content.symbol"
|
||||
}, {
|
||||
"name": "line",
|
||||
"description": 'The line variable specifies the line number of the symbol for which definition information is to be retrieved.',
|
||||
"type": "number",
|
||||
"required": true,
|
||||
"from": "content.content.line"
|
||||
}, {
|
||||
"name": "file",
|
||||
"description": 'File contain that symbol.',
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"from": "content.content.file"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
async handlerAction(args: {[key: string]: any}): Promise<CommandResult> {
|
||||
try {
|
||||
const symbolName = args.symbol;
|
||||
const symbolLine = args.line;
|
||||
let symbolFile = args.file;
|
||||
|
||||
// Check if the symbol name is valid
|
||||
if (!symbolName || typeof symbolName !== 'string') {
|
||||
throw new Error('Invalid symbol name. It should be a non-empty string.');
|
||||
}
|
||||
|
||||
// Check if the symbol line is valid
|
||||
if (!symbolLine || typeof symbolLine !== 'number' || symbolLine < 0) {
|
||||
throw new Error('Invalid symbol line. It should be a non-negative number.');
|
||||
}
|
||||
|
||||
// Check if the symbol file is valid
|
||||
if (!symbolFile || typeof symbolFile !== 'string') {
|
||||
throw new Error('Invalid symbol file. It should be a non-empty string.');
|
||||
}
|
||||
|
||||
// if symbolFile is not absolute path, then get it's absolute path
|
||||
if (!path.isAbsolute(symbolFile)) {
|
||||
const basePath = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
symbolFile = path.join(basePath!, symbolFile);
|
||||
}
|
||||
|
||||
// get reference information
|
||||
const refList = await findSymbolInWorkspace(symbolName, symbolLine, symbolFile);
|
||||
|
||||
return {exitCode: 0, stdout: JSON.stringify(refList), stderr: ""};
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`${this.name} handle error: ${error}`);
|
||||
logger.channel()?.show();
|
||||
return {exitCode: -1, stdout: '', stderr: `${this.name} handle error: ${error}`};
|
||||
}
|
||||
}
|
||||
};
|
@ -1,96 +0,0 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { Action } from './customAction';
|
||||
|
||||
import { CommandResult } from '../util/commonUtil';
|
||||
import { logger } from '../util/logger';
|
||||
import * as path from 'path';
|
||||
import { UiUtilWrapper } from '../util/uiUtil';
|
||||
|
||||
|
||||
export class SymbolInFileAction implements Action {
|
||||
name: string;
|
||||
description: string;
|
||||
type: string[];
|
||||
action: string;
|
||||
handler: string[];
|
||||
args: { "name": string, "description": string, "type": string, "as"?: string, "required": boolean, "from": string }[];
|
||||
|
||||
constructor() {
|
||||
this.name = 'symbol_in_file';
|
||||
this.description = 'Retrieve definitions in file';
|
||||
this.type = ['symbol'];
|
||||
this.action = 'symbol_ref';
|
||||
this.handler = [];
|
||||
this.args = [
|
||||
{
|
||||
"name": "file",
|
||||
"description": 'Specify which file to load.',
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"from": "content.content.file"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
async handlerAction(args: {[key: string]: any}): Promise<CommandResult> {
|
||||
try {
|
||||
let symbolFile = args.file;
|
||||
|
||||
// if symbolFile is not absolute path, then get it's absolute path
|
||||
if (!path.isAbsolute(symbolFile)) {
|
||||
const basePath = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
symbolFile = path.join(basePath!, symbolFile);
|
||||
}
|
||||
|
||||
// load symbols in file
|
||||
const symbolsT: vscode.DocumentSymbol[] = await vscode.commands.executeCommand<vscode.DocumentSymbol[]>(
|
||||
'vscode.executeDocumentSymbolProvider',
|
||||
vscode.Uri.file(symbolFile)
|
||||
);
|
||||
let symbolsList: vscode.DocumentSymbol[] = [];
|
||||
const visitSymbol = (symbol: vscode.DocumentSymbol) => {
|
||||
symbolsList.push(symbol);
|
||||
for (const child of symbol.children) {
|
||||
visitSymbol(child);
|
||||
}
|
||||
}
|
||||
for (const symbol of symbolsT) {
|
||||
visitSymbol(symbol);
|
||||
}
|
||||
|
||||
// convert symbolsList to json object list
|
||||
|
||||
let sysbolsOjbs: {[key: string]: any}[] = [];
|
||||
symbolsList.forEach(symbol => {
|
||||
sysbolsOjbs.push({
|
||||
"name": symbol.name,
|
||||
"kind": vscode.SymbolKind[symbol.kind],
|
||||
"startPosition": {
|
||||
"line": symbol.range.start.line,
|
||||
"column": symbol.range.start.character
|
||||
},
|
||||
"endPosition": {
|
||||
"line": symbol.range.end.line,
|
||||
"column": symbol.range.end.character
|
||||
},
|
||||
"selectRangeStartPosition": {
|
||||
"line": symbol.selectionRange.start.line,
|
||||
"column": symbol.selectionRange.start.character
|
||||
},
|
||||
"selectRangeEndPosition": {
|
||||
"line": symbol.selectionRange.end.line,
|
||||
"column": symbol.selectionRange.end.character
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return {exitCode: 0, stdout: JSON.stringify(sysbolsOjbs), stderr: ""};
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`${this.name} handle error: ${error}`);
|
||||
logger.channel()?.show();
|
||||
return {exitCode: -1, stdout: '', stderr: `${this.name} handle error: ${error}`};
|
||||
}
|
||||
}
|
||||
};
|
@ -1,257 +0,0 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { Action, CustomActions } from './customAction';
|
||||
|
||||
import { CommandResult } from '../util/commonUtil';
|
||||
import { logger } from '../util/logger';
|
||||
import { handleCodeSelected } from '../context/contextCodeSelected';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { UiUtilWrapper } from '../util/uiUtil';
|
||||
import path from 'path';
|
||||
|
||||
const readFile = util.promisify(fs.readFile);
|
||||
|
||||
|
||||
async function isCorrectIndexSymbol(filename: string, position: vscode.Position, symbolName: string): Promise< boolean > {
|
||||
let defLocations = await vscode.commands.executeCommand<any[]>(
|
||||
'vscode.executeDefinitionProvider',
|
||||
vscode.Uri.file(filename),
|
||||
position
|
||||
);
|
||||
|
||||
if (!defLocations || defLocations.length === 0) {
|
||||
defLocations = await vscode.commands.executeCommand<any[]>(
|
||||
'vscode.executeReferenceProvider',
|
||||
vscode.Uri.file(filename),
|
||||
position
|
||||
);
|
||||
}
|
||||
|
||||
if (!defLocations) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const defLocation of defLocations) {
|
||||
let range = undefined;
|
||||
let uri = undefined;
|
||||
if (defLocation.targetSelectionRange) {
|
||||
range = defLocation.targetSelectionRange;
|
||||
uri = defLocation.targetUri;
|
||||
} else if (defLocation.targetRange) {
|
||||
range = defLocation.targetRange;
|
||||
uri = defLocation.targetUri;
|
||||
} else {
|
||||
range = defLocation.range;
|
||||
uri = defLocation.uri;
|
||||
}
|
||||
if (!range) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const documentNew = await vscode.workspace.openTextDocument(uri);
|
||||
const sbName = await documentNew.getText(range);
|
||||
if (sbName === symbolName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function getSymbolPosition(symbolName: string, symbolLine: number, symbolFile: string): Promise<vscode.Position | undefined> {
|
||||
// Read the file
|
||||
let content = await readFile(symbolFile, 'utf-8');
|
||||
|
||||
// Split the content into lines
|
||||
let lines = content.split('\n');
|
||||
|
||||
// Check if the line number is valid
|
||||
if (symbolLine < 0 || symbolLine >= lines.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Get the line text
|
||||
let symbolIndex = -1;
|
||||
const maxLine = lines.length < symbolLine + 6 ? lines.length : symbolLine + 6;
|
||||
for (let i = symbolLine; i < maxLine; i++) {
|
||||
let lineText = lines[i];
|
||||
|
||||
// Find the symbol in the line
|
||||
let lineOffsetPos = -1;
|
||||
while (true) {
|
||||
symbolIndex = lineText.indexOf(symbolName, lineOffsetPos+1);
|
||||
if (symbolIndex > -1 && await isCorrectIndexSymbol(symbolFile, new vscode.Position(i, symbolIndex), symbolName)) {
|
||||
return new vscode.Position(i, symbolIndex);
|
||||
}
|
||||
if (symbolIndex === -1) {
|
||||
break;
|
||||
}
|
||||
lineOffsetPos = symbolIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function findSymbolInWorkspace(symbolName: string, symbolline: number, symbolFile: string): Promise<string[]> {
|
||||
const symbolPosition = await getSymbolPosition(symbolName, symbolline, symbolFile);
|
||||
if (!symbolPosition) {
|
||||
throw new Error(`Symbol "${symbolName}" not found in file "${symbolFile}" at line ${symbolline}.`);
|
||||
}
|
||||
|
||||
logger.channel()?.info(`symbol position: ${symbolPosition.line}:${symbolPosition.character}`);
|
||||
|
||||
// get all references of symbol
|
||||
const refLocations = await vscode.commands.executeCommand<vscode.Location[]>(
|
||||
'vscode.executeReferenceProvider',
|
||||
vscode.Uri.file(symbolFile),
|
||||
symbolPosition
|
||||
);
|
||||
if (!refLocations || refLocations.length === 0) {
|
||||
throw new Error(`No references found for symbol "${symbolName}" in file "${symbolFile}" at line ${symbolline}.`);
|
||||
}
|
||||
|
||||
// get related source lines
|
||||
let contextList: Set<string> = new Set();
|
||||
for (const refLocation of refLocations) {
|
||||
const refLocationFile = refLocation.uri.fsPath;
|
||||
|
||||
// calculate the line number, if refLocation.range.start.line - 2 < 0, then set it to 0
|
||||
const startLine = refLocation.range.start.line - 2 < 0 ? 0 : refLocation.range.start.line - 2;
|
||||
|
||||
const documentNew = await vscode.workspace.openTextDocument(refLocationFile);
|
||||
const rangeNew = new vscode.Range(startLine, 0, refLocation.range.end.line + 2, 10000);
|
||||
|
||||
// get symbol define in symbolFile
|
||||
const symbolsT: vscode.DocumentSymbol[] = await vscode.commands.executeCommand<vscode.DocumentSymbol[]>(
|
||||
'vscode.executeDocumentSymbolProvider',
|
||||
refLocation.uri
|
||||
);
|
||||
let symbolsList: vscode.DocumentSymbol[] = [];
|
||||
const visitSymbol = (symbol: vscode.DocumentSymbol) => {
|
||||
symbolsList.push(symbol);
|
||||
if (symbol.children) {
|
||||
for (const child of symbol.children) {
|
||||
visitSymbol(child);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (symbolsT) {
|
||||
for (const symbol of symbolsT) {
|
||||
visitSymbol(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
let symbol: vscode.DocumentSymbol | undefined = undefined;
|
||||
for (const symbolT of symbolsList.reverse()) {
|
||||
if (symbolT.range.contains(refLocation.range)) {
|
||||
symbol = symbolT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const symbolName = symbol ? symbol.name : '';
|
||||
const symbolLine = symbol ? symbol.range.start.line : 0;
|
||||
|
||||
const data = {
|
||||
path: refLocationFile,
|
||||
start_line: startLine,
|
||||
ref_line: refLocation.range.start.line,
|
||||
content: documentNew.getText(rangeNew),
|
||||
parentDefine: symbolName,
|
||||
parentDefineStartLine: symbolLine
|
||||
};
|
||||
contextList.add(JSON.stringify(data));
|
||||
}
|
||||
|
||||
return Array.from(contextList);
|
||||
}
|
||||
export class SymbolRefAction implements Action {
|
||||
name: string;
|
||||
description: string;
|
||||
type: string[];
|
||||
action: string;
|
||||
handler: string[];
|
||||
args: { "name": string, "description": string, "type": string, "as"?: string, "required": boolean, "from": string }[];
|
||||
|
||||
constructor() {
|
||||
this.name = 'symbol_ref';
|
||||
this.description = `
|
||||
Function Purpose: This function retrieves the reference information for a given symbol.
|
||||
Input: The symbol should not be in string format or in 'a.b' format. To find the reference of 'a.b', simply refer to 'b'.
|
||||
Output: The function returns a dictionary with the following keys:
|
||||
'exitCode': If 'exitCode' is 0, the function execution was successful. If 'exitCode' is not 0, the function execution failed.
|
||||
'stdout': If the function executes successfully, 'stdout' is a list of JSON strings. Each JSON string contains:
|
||||
'path': The file path.
|
||||
'ref_line': The ref line of the symbol.
|
||||
'content': The source code related to the reference.
|
||||
'parentDefine': The parent symbol name of the reference, which is always a function name or class name.
|
||||
'parentDefineStartLine': The start line of the parent symbol name of the reference.
|
||||
'stderr': Error output if any.
|
||||
Error Handling: If the function execution fails, 'exitCode' will not be 0 and 'stderr' will contain the error information.`;
|
||||
this.type = ['symbol'];
|
||||
this.action = 'symbol_ref';
|
||||
this.handler = [];
|
||||
this.args = [
|
||||
{
|
||||
"name": "symbol",
|
||||
"description": "The symbol variable specifies the symbol for which reference information is to be retrieved.",
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"from": "content.content.symbol"
|
||||
}, {
|
||||
"name": "line",
|
||||
"description": 'The line variable specifies the line number of the symbol for which reference information is to be retrieved.',
|
||||
"type": "number",
|
||||
"required": true,
|
||||
"from": "content.content.line"
|
||||
}, {
|
||||
"name": "file",
|
||||
"description": 'File contain that symbol.',
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"from": "content.content.file"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
async handlerAction(args: {[key: string]: any}): Promise<CommandResult> {
|
||||
try {
|
||||
const symbolName = args.symbol;
|
||||
const symbolLine = args.line;
|
||||
let symbolFile = args.file;
|
||||
|
||||
// Check if the symbol name is valid
|
||||
if (!symbolName || typeof symbolName !== 'string') {
|
||||
throw new Error('Invalid symbol name. It should be a non-empty string.');
|
||||
}
|
||||
|
||||
// Check if the symbol line is valid
|
||||
if (!symbolLine || typeof symbolLine !== 'number' || symbolLine < 0) {
|
||||
throw new Error('Invalid symbol line. It should be a non-negative number.');
|
||||
}
|
||||
|
||||
// Check if the symbol file is valid
|
||||
if (!symbolFile || typeof symbolFile !== 'string') {
|
||||
throw new Error('Invalid symbol file. It should be a non-empty string.');
|
||||
}
|
||||
|
||||
// if symbolFile is not absolute path, then get it's absolute path
|
||||
if (!path.isAbsolute(symbolFile)) {
|
||||
const basePath = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
symbolFile = path.join(basePath!, symbolFile);
|
||||
}
|
||||
|
||||
// get reference information
|
||||
const refList = await findSymbolInWorkspace(symbolName, symbolLine, symbolFile);
|
||||
|
||||
return {exitCode: 0, stdout: JSON.stringify(refList), stderr: ""};
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`${this.name} handle error: ${error}`);
|
||||
logger.channel()?.show();
|
||||
return {exitCode: -1, stdout: '', stderr: `${this.name} handle error: ${error}`};
|
||||
}
|
||||
}
|
||||
};
|
@ -1,145 +0,0 @@
|
||||
import { createTempSubdirectory } from "../util/commonUtil";
|
||||
import DevChat from "../toolwrapper/devchat";
|
||||
import { FT } from "../util/feature_flags/feature_toggles";
|
||||
import CustomCommands from "./customCommand";
|
||||
import * as path from "path";
|
||||
import * as fs from 'fs';
|
||||
|
||||
export interface Command {
|
||||
name: string;
|
||||
pattern: string;
|
||||
description: string;
|
||||
args: number;
|
||||
handler: (commandName: string, userInput: string) => Promise<string>;
|
||||
}
|
||||
|
||||
class CommandManager {
|
||||
private static instance: CommandManager;
|
||||
private commands: Command[] = [];
|
||||
|
||||
private constructor() { }
|
||||
|
||||
public static getInstance(): CommandManager {
|
||||
if (!CommandManager.instance) {
|
||||
CommandManager.instance = new CommandManager();
|
||||
}
|
||||
|
||||
return CommandManager.instance;
|
||||
}
|
||||
|
||||
registerCommand(command: Command): void {
|
||||
this.commands.push(command);
|
||||
}
|
||||
|
||||
getCommandList(includeHide: boolean = false): Command[] {
|
||||
// load commands from CustomCommands
|
||||
let newCommands: Command[] = [...this.commands];
|
||||
const customCommands = CustomCommands.getInstance();
|
||||
const commands = customCommands.getCommands();
|
||||
commands.forEach(command => {
|
||||
const commandObj: Command = {
|
||||
name: command.name,
|
||||
pattern: command.pattern,
|
||||
description: command.description,
|
||||
args: command.args,
|
||||
handler: async (commandName: string, userInput: string) => {
|
||||
return CustomCommands.getInstance().handleCommand(commandName, userInput);
|
||||
}
|
||||
};
|
||||
if (command.show || includeHide) {
|
||||
newCommands.push(commandObj);
|
||||
}
|
||||
});
|
||||
return newCommands;
|
||||
}
|
||||
|
||||
async getCommandListByDevChatRun(includeHide: boolean = false): Promise<Command[]> {
|
||||
// load commands from CustomCommands
|
||||
let newCommands: Command[] = [...this.commands];
|
||||
|
||||
const devChat = new DevChat();
|
||||
const commandList = await devChat.commands();
|
||||
commandList.forEach(command => {
|
||||
const commandObj: Command = {
|
||||
name: command.name,
|
||||
pattern: command.name,
|
||||
description: command.description,
|
||||
args: 0,
|
||||
handler: async (commandName: string, userInput: string) => {
|
||||
const tempDir = await createTempSubdirectory('devchat/command');
|
||||
const tempFile = path.join(tempDir, command.name);
|
||||
const stdout = await devChat.commandPrompt(command.name);
|
||||
fs.writeFileSync(tempFile, stdout);
|
||||
return `[instruction|${tempFile}] `;
|
||||
}
|
||||
};
|
||||
newCommands.push(commandObj);
|
||||
});
|
||||
|
||||
return newCommands;
|
||||
}
|
||||
|
||||
async processTextBak(text: string): Promise<string> {
|
||||
// 定义一个异步函数来处理单个命令
|
||||
const processCommand = async (commandObj: Command, userInput: string) => {
|
||||
// 转义特殊字符
|
||||
let commandPattern: RegExp;
|
||||
if (commandObj.pattern.indexOf("{{") > 0) {
|
||||
const escapedPattern = commandObj.pattern.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
commandPattern = new RegExp(
|
||||
`\\/(${escapedPattern.replace('\\{\\{prompt\\}\\}', '\\{\\{(.+?)\\}\\}')})`,
|
||||
'g'
|
||||
);
|
||||
} else {
|
||||
const escapedPattern = commandObj.pattern.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
// Update the regex pattern to match commands ending with space or newline
|
||||
commandPattern = new RegExp(
|
||||
`\\/(?<command>${escapedPattern.replace('{{prompt}}', '(?<userInput>.+?)')})(?=\\s|\\n|$)`,
|
||||
'g'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const matches = Array.from(text.matchAll(commandPattern));
|
||||
const replacements = await Promise.all(
|
||||
matches.map(async (match) => {
|
||||
const matchedUserInput = commandObj.pattern.indexOf("{{") > 0 ? match[2] : match.groups!.userInput;
|
||||
return await commandObj.handler(commandObj.name, matchedUserInput);
|
||||
})
|
||||
);
|
||||
|
||||
let result = userInput;
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
result = result.replace(matches[i][0], replacements[i]);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// 处理所有命令
|
||||
let result = text;
|
||||
for (const commandObj of await this.getCommandListByDevChatRun()) {
|
||||
result = await processCommand(commandObj, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async processText(text: string): Promise<string> {
|
||||
let result = text.trim();
|
||||
const messageTextArr = result.split(/ |\n|\t/);
|
||||
const commandName = messageTextArr[0];
|
||||
|
||||
for (const commandObj of await this.getCommandListByDevChatRun()) {
|
||||
const commandObjNamePattern = "/" + commandObj.name + " ";
|
||||
if (commandObjNamePattern === commandName) {
|
||||
const newInstructFile = await commandObj.handler(commandObj.name, "");
|
||||
result = newInstructFile + result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export default CommandManager;
|
@ -1,130 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { logger } from '../util/logger';
|
||||
|
||||
export interface Command {
|
||||
name: string;
|
||||
pattern: string;
|
||||
description: string;
|
||||
message: string;
|
||||
default: boolean;
|
||||
show: boolean;
|
||||
args: number;
|
||||
instructions: string[];
|
||||
}
|
||||
|
||||
class CustomCommands {
|
||||
private static instance: CustomCommands | null = null;
|
||||
private commands: Command[] = [];
|
||||
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
public static getInstance(): CustomCommands {
|
||||
if (!CustomCommands.instance) {
|
||||
CustomCommands.instance = new CustomCommands();
|
||||
}
|
||||
return CustomCommands.instance;
|
||||
}
|
||||
|
||||
public parseCommands(workflowsDir: string): void {
|
||||
this.commands = [];
|
||||
|
||||
try {
|
||||
const extensionDirs = fs.readdirSync(workflowsDir, { withFileTypes: true })
|
||||
.filter(dirent => dirent.isDirectory())
|
||||
.map(dirent => dirent.name);
|
||||
|
||||
for (const extensionDir of extensionDirs) {
|
||||
const commandDir = path.join(workflowsDir, extensionDir, 'command');
|
||||
if (fs.existsSync(commandDir)) {
|
||||
const commandSubDirs = fs.readdirSync(commandDir, { withFileTypes: true })
|
||||
.filter(dirent => dirent.isDirectory())
|
||||
.map(dirent => dirent.name);
|
||||
|
||||
for (const commandSubDir of commandSubDirs) {
|
||||
const settingsPath = path.join(commandDir, commandSubDir, '_setting_.json');
|
||||
if (fs.existsSync(settingsPath)) {
|
||||
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
||||
const command: Command = {
|
||||
name: commandSubDir,
|
||||
pattern: settings.pattern,
|
||||
description: settings.description,
|
||||
message: settings.message,
|
||||
default: settings.default,
|
||||
args: settings.args === undefined ? 0 : settings.args,
|
||||
show: settings.show === undefined ? "true" : settings.show,
|
||||
instructions: settings.instructions.map((instruction: string) => path.join(commandDir, commandSubDir, instruction))
|
||||
};
|
||||
this.commands.push(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 显示错误消息
|
||||
logger.channel()?.error(`Failed to parse commands due to error: ${error}`);
|
||||
logger.channel()?.show();
|
||||
}
|
||||
}
|
||||
|
||||
public regCommand(command: Command) {
|
||||
this.commands.push(command);
|
||||
}
|
||||
|
||||
public getCommands(): Command[] {
|
||||
return this.commands;
|
||||
}
|
||||
|
||||
public getCommand(commandName: string): Command | null {
|
||||
const foundCommand = this.commands.find(command => command.name === commandName);
|
||||
return foundCommand ? foundCommand : null;
|
||||
}
|
||||
|
||||
|
||||
public handleCommand(commandName: string, userInput: string): string {
|
||||
// 获取命令对象,这里假设您已经有一个方法或属性可以获取到命令对象
|
||||
const command = this.getCommand(commandName);
|
||||
if (!command) {
|
||||
logger.channel()?.error(`Command ${commandName} not found!`);
|
||||
logger.channel()?.show();
|
||||
return '';
|
||||
}
|
||||
|
||||
let commandMessage = command.message;
|
||||
if (userInput && userInput.length > 0) {
|
||||
// userInput is "['aa', 'bb]" like string
|
||||
// parse userInput to array
|
||||
// handle eval exception
|
||||
|
||||
try {
|
||||
const userInputArray = eval(userInput);
|
||||
|
||||
// replace command message $1 with userInputArray[0], $2 with userInputArray[1] and so on
|
||||
for (let i = 0; i < userInputArray.length; i++) {
|
||||
commandMessage = commandMessage.replace(`$${i + 1}`, userInputArray[i]);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`Failed to parse user input '${userInput}' due to error: ${error}. A valid input should be in the format: ['aa', 'bb']`);
|
||||
logger.channel()?.show();
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// replace ${Name} with enviroment var Name
|
||||
const envVarRegex = /\${(\w+)}/g;
|
||||
commandMessage = commandMessage.replace(envVarRegex, (match, p1) => {
|
||||
return process.env[p1] || '';
|
||||
});
|
||||
|
||||
// build instrctions
|
||||
const instructions = command!.instructions
|
||||
.map((instruction: string) => `[instruction|${instruction}]`)
|
||||
.join(' ');
|
||||
|
||||
// 返回结果字符串
|
||||
return `${instructions} ${commandMessage}`;
|
||||
}
|
||||
}
|
||||
|
||||
export default CustomCommands;
|
@ -1,6 +0,0 @@
|
||||
import CommandManager from './commandManager';
|
||||
|
||||
const commandManager = CommandManager.getInstance();
|
||||
|
||||
// 注册命令
|
||||
|
@ -12,7 +12,7 @@ export interface ChatContext {
|
||||
handler: () => Promise<string[]>;
|
||||
}
|
||||
|
||||
class ChatContextManager {
|
||||
export class ChatContextManager {
|
||||
private static instance: ChatContextManager;
|
||||
private contexts: ChatContext[] = [];
|
||||
|
||||
@ -66,7 +66,7 @@ export interface ChatContext {
|
||||
return this.contexts;
|
||||
}
|
||||
|
||||
async processText(command: string): Promise<string[]> {
|
||||
async handleContextSelected(command: string): Promise<string[]> {
|
||||
for (const contextObj of this.contexts) {
|
||||
if (contextObj.name === command) {
|
||||
return await contextObj.handler();
|
||||
@ -77,4 +77,3 @@ export interface ChatContext {
|
||||
}
|
||||
}
|
||||
|
||||
export default ChatContextManager;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import ChatContextManager from './contextManager';
|
||||
import { ChatContextManager } from './contextManager';
|
||||
import { gitDiffCachedContext } from './contextGitDiffCached';
|
||||
import { gitDiffContext } from './contextGitDiff';
|
||||
import { customCommandContext } from './contextCustomCommand';
|
||||
|
@ -2,7 +2,7 @@ import * as vscode from 'vscode';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import { sendFileSelectMessage, sendCodeSelectMessage } from './util';
|
||||
import ExtensionContextHolder from '../util/extensionContext';
|
||||
import { ExtensionContextHolder } from '../util/extensionContext';
|
||||
import { TopicManager } from '../topic/topicManager';
|
||||
import { TopicTreeDataProvider, TopicTreeItem } from '../panel/topicView';
|
||||
import { FilePairManager } from '../util/diffFilePairs';
|
||||
@ -11,15 +11,15 @@ import { UiUtilWrapper } from '../util/uiUtil';
|
||||
import { isValidApiKey } from '../handler/historyMessagesBase';
|
||||
|
||||
import { logger } from '../util/logger';
|
||||
import { CommandRun} from '../util/commonUtil';
|
||||
|
||||
import path from 'path';
|
||||
|
||||
import { sendCommandListByDevChatRun, updateChatModels } from '../handler/regCommandList';
|
||||
import { sendCommandListByDevChatRun, updateChatModels } from '../handler/workflowCommandHandler';
|
||||
import DevChat from "../toolwrapper/devchat";
|
||||
import { createEnvByConda, createEnvByMamba } from '../util/python_installer/app_install';
|
||||
import { installRequirements } from '../util/python_installer/package_install';
|
||||
|
||||
|
||||
function registerOpenChatPanelCommand(context: vscode.ExtensionContext) {
|
||||
let disposable = vscode.commands.registerCommand('devchat.openChatPanel', async () => {
|
||||
await vscode.commands.executeCommand('devchat-view.focus');
|
||||
@ -40,7 +40,7 @@ function registerAddContextCommand(context: vscode.ExtensionContext) {
|
||||
|
||||
await sendFileSelectMessage(ExtensionContextHolder.provider?.view()!, uri.fsPath);
|
||||
};
|
||||
context.subscriptions.push(vscode.commands.registerCommand('devchat.addConext', callback));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('devchat.addContext', callback));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('devchat.addConext_chinese', callback));
|
||||
}
|
||||
|
||||
@ -266,7 +266,6 @@ export function registerInstallCommandsPython(context: vscode.ExtensionContext)
|
||||
// 2. check requirements.txt in ~/.chat dir
|
||||
// 3. install requirements.txt
|
||||
|
||||
|
||||
// 1. install python >= 3.11
|
||||
logger.channel()?.info(`create env for python ...`);
|
||||
logger.channel()?.info(`try to create env by mamba ...`);
|
||||
|
@ -3,7 +3,7 @@ import * as vscode from 'vscode';
|
||||
import { handleCodeSelected } from '../context/contextCodeSelected';
|
||||
import { handleFileSelected } from '../context/contextFileSelected';
|
||||
import { MessageHandler } from '../handler/messageHandler';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
import { regOutMessage } from '../util/reg_messages';
|
||||
import { logger } from '../util/logger';
|
||||
|
||||
regOutMessage({command: 'appendContext', context: ''});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { DevChatViewProvider } from '../panel/devchatView';
|
||||
import { TopicTreeDataProvider } from '../panel/topicView';
|
||||
import ExtensionContextHolder from '../util/extensionContext';
|
||||
import { ExtensionContextHolder } from '../util/extensionContext';
|
||||
|
||||
|
||||
export function regDevChatView(context: vscode.ExtensionContext) {
|
||||
|
@ -21,14 +21,14 @@ import {
|
||||
import { regLanguageContext } from './contributes/context';
|
||||
import { regDevChatView, regTopicView } from './contributes/views';
|
||||
|
||||
import ExtensionContextHolder from './util/extensionContext';
|
||||
import { ExtensionContextHolder } from './util/extensionContext';
|
||||
import { logger } from './util/logger';
|
||||
import { LoggerChannelVscode } from './util/logger_vscode';
|
||||
import { createStatusBarItem } from './panel/statusBarView';
|
||||
import { UiUtilWrapper } from './util/uiUtil';
|
||||
import { UiUtilVscode } from './util/uiUtil_vscode';
|
||||
import { FT } from './util/feature_flags/feature_toggles';
|
||||
import { ApiKeyManager } from './util/apiKey';
|
||||
import { startRpcServer } from './ide_services/services';
|
||||
|
||||
async function isProviderHasSetted() {
|
||||
try {
|
||||
@ -241,5 +241,7 @@ async function activate(context: vscode.ExtensionContext) {
|
||||
regApplyDiffResultCommand(context);
|
||||
|
||||
regPythonPathCommand(context);
|
||||
|
||||
startRpcServer();
|
||||
}
|
||||
exports.activate = activate;
|
@ -14,18 +14,22 @@ export async function getUserAccessKey(message: any, panel: vscode.WebviewPanel|
|
||||
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
const llmModelData = await ApiKeyManager.llmModel();
|
||||
if (!llmModelData || !llmModelData.api_key) {
|
||||
MessageHandler.sendMessage(panel, {"command": "getUserAccessKey", "accessKey": "", "keyType": "", "endPoint": ""});
|
||||
MessageHandler.sendMessage(panel,
|
||||
{
|
||||
"command": "getUserAccessKey",
|
||||
"accessKey": "",
|
||||
"keyType": "",
|
||||
"endPoint": ""
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let keyType: string = "others";
|
||||
if (llmModelData.api_key?.startsWith("DC.")) {
|
||||
keyType = "DevChat";
|
||||
}
|
||||
|
||||
let openAiApiBase = llmModelData.api_base;
|
||||
if (!openAiApiBase) {
|
||||
openAiApiBase = "";
|
||||
}
|
||||
MessageHandler.sendMessage(panel, {"command": "getUserAccessKey", "accessKey": llmModelData.api_key, "keyType": keyType, "endPoint": openAiApiBase});
|
||||
const keyData = {
|
||||
"command": "getUserAccessKey",
|
||||
"accessKey": llmModelData.api_key,
|
||||
"keyType": llmModelData.api_key?.startsWith("DC.") ? "DevChat" : "others",
|
||||
"endPoint": llmModelData.api_base ? llmModelData.api_base : ""
|
||||
};
|
||||
MessageHandler.sendMessage(panel, keyData);
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import * as vscode from 'vscode';
|
||||
import ChatContextManager from '../context/contextManager';
|
||||
|
||||
import { MessageHandler } from './messageHandler';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
|
||||
|
||||
regInMessage({command: 'addContext', selected: ''});
|
||||
regOutMessage({command: 'appendContext', context: ''});
|
||||
export async function addConext(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
const contextStrList = await ChatContextManager.getInstance().processText(message.selected);
|
||||
for (const contextStr of contextStrList) {
|
||||
MessageHandler.sendMessage(panel, { command: 'appendContext', context: contextStr });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { handleRefCommand } from '../context/contextRef';
|
||||
import { MessageHandler } from './messageHandler';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
|
||||
|
||||
regInMessage({command: 'addRefCommandContext', refCommand: ''});
|
||||
regOutMessage({command: 'appendContext', context: ''});
|
||||
// message: { command: 'addRefCommandContext', refCommand: string }
|
||||
// User input: /ref ls . then "ls ." will be passed to refCommand
|
||||
export async function addRefCommandContext(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
const contextStr = await handleRefCommand(message.refCommand);
|
||||
MessageHandler.sendMessage(panel, { command: 'appendContext', context: contextStr });
|
||||
return;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
import ActionManager from '../action/actionManager';
|
||||
import { MessageHandler } from './messageHandler';
|
||||
import { sendMessage } from './sendMessage';
|
||||
import { logger } from '../util/logger';
|
||||
|
||||
function compressText(text: string, maxLength: number): string {
|
||||
if (text.length <= maxLength) {
|
||||
return text;
|
||||
}
|
||||
|
||||
const halfLength = Math.floor(maxLength / 2);
|
||||
return text.slice(0, halfLength) + " ... " + text.slice(-halfLength);
|
||||
}
|
||||
|
||||
|
||||
regInMessage({command: 'applyAction', actionName: '', parentHash: '', codeBlock: { type: '', content: '', fileName: ''}});
|
||||
export async function applyAction(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
try {
|
||||
const result = await ActionManager.getInstance().applyAction("command_run", { "command": "", "fileName": message.fileName, "content": message.content });
|
||||
|
||||
// send error message to devchat
|
||||
const commandObj = JSON.parse(message.content)
|
||||
const newMessage = `{"exit_code": ${result.exitCode}, stdout: ${result.stdout}, stderr: ${result.stderr}}`;
|
||||
MessageHandler.sendMessage(panel, { "command": "systemMessage", "text": "waitting command reponse..." });
|
||||
sendMessage({command: 'sendMessage', text: newMessage, parent_hash: message.parentHash}, panel, commandObj.name);
|
||||
|
||||
} catch (error) {
|
||||
logger.channel()?.error('Failed to parse code file content: ' + error);
|
||||
logger.channel()?.show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,34 +0,0 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
|
||||
|
||||
export async function applyCode(text: string) {
|
||||
const validVisibleTextEditors = vscode.window.visibleTextEditors.filter(editor => editor.viewColumn !== undefined);
|
||||
|
||||
if (validVisibleTextEditors.length > 1) {
|
||||
vscode.window.showErrorMessage(`There are more then one visible text editors. Please close all but one and try again.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = validVisibleTextEditors[0];
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selection = editor.selection;
|
||||
const start = selection.start;
|
||||
const end = selection.end;
|
||||
|
||||
await editor.edit((editBuilder: vscode.TextEditorEdit) => {
|
||||
editBuilder.replace(new vscode.Range(start, end), text);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
regInMessage({command: 'code_apply', content: ''});
|
||||
export async function codeApply(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
await applyCode(message.content);
|
||||
return;
|
||||
}
|
||||
|
||||
|
94
src/handler/codeBlockHandler.ts
Normal file
94
src/handler/codeBlockHandler.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
|
||||
|
||||
export async function applyCode(text: string) {
|
||||
const validVisibleTextEditors = vscode.window.visibleTextEditors.filter(editor => editor.viewColumn !== undefined);
|
||||
|
||||
if (validVisibleTextEditors.length > 1) {
|
||||
vscode.window.showErrorMessage(`There are more then one visible text editors. Please close all but one and try again.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = validVisibleTextEditors[0];
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selection = editor.selection;
|
||||
const start = selection.start;
|
||||
const end = selection.end;
|
||||
|
||||
await editor.edit((editBuilder: vscode.TextEditorEdit) => {
|
||||
editBuilder.replace(new vscode.Range(start, end), text);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
export async function applyCodeFile(text: string, fileName: string): Promise<void> {
|
||||
if (fileName) {
|
||||
await replaceFileContent(vscode.Uri.file(fileName), text);
|
||||
return;
|
||||
}
|
||||
|
||||
const validVisibleTextEditors = vscode.window.visibleTextEditors.filter(editor => editor.viewColumn !== undefined);
|
||||
|
||||
if (validVisibleTextEditors.length > 1) {
|
||||
vscode.window.showErrorMessage(`2There are more then one visible text editors. Please close all but one and try again.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = validVisibleTextEditors[0];
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const document = editor.document;
|
||||
const fullRange = new vscode.Range(
|
||||
document.positionAt(0),
|
||||
document.positionAt(document.getText().length)
|
||||
);
|
||||
|
||||
await editor.edit((editBuilder: vscode.TextEditorEdit) => {
|
||||
editBuilder.replace(fullRange, text);
|
||||
});
|
||||
}
|
||||
export async function replaceFileContent(uri: vscode.Uri, newContent: string) {
|
||||
try {
|
||||
// 创建一个 WorkspaceEdit 对象
|
||||
const workspaceEdit = new vscode.WorkspaceEdit();
|
||||
|
||||
// 获取文件的当前内容
|
||||
const document = await vscode.workspace.openTextDocument(uri);
|
||||
|
||||
// 计算文件的完整范围(从文件开始到文件结束)
|
||||
const fullRange = new vscode.Range(
|
||||
document.positionAt(0),
|
||||
document.positionAt(document.getText().length)
|
||||
);
|
||||
|
||||
// 使用 WorkspaceEdit 的 replace 方法替换文件的完整范围内容
|
||||
workspaceEdit.replace(uri, fullRange, newContent);
|
||||
|
||||
// 应用编辑更改
|
||||
await vscode.workspace.applyEdit(workspaceEdit);
|
||||
|
||||
// 显示成功消息
|
||||
vscode.window.showInformationMessage('File content replaced successfully.');
|
||||
} catch (error) {
|
||||
// 显示错误消息
|
||||
vscode.window.showErrorMessage('Failed to replace file content: ' + error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
regInMessage({command: 'code_apply', content: ''});
|
||||
export async function insertCodeBlockToFile(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
await applyCode(message.content);
|
||||
}
|
||||
|
||||
regInMessage({command: 'code_file_apply', content: '', fileName: ''});
|
||||
export async function replaceCodeBlockToFile(message: any, panel: vscode.WebviewPanel | vscode.WebviewView): Promise<void> {
|
||||
await applyCodeFile(message.content, message.fileName);
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
import ActionManager from '../action/actionManager';
|
||||
import { MessageHandler } from './messageHandler';
|
||||
import { sendMessage } from './sendMessage';
|
||||
import { logger } from '../util/logger';
|
||||
|
||||
function compressText(text: string, maxLength: number): string {
|
||||
if (text.length <= maxLength) {
|
||||
return text;
|
||||
}
|
||||
|
||||
const halfLength = Math.floor(maxLength / 2);
|
||||
return text.slice(0, halfLength) + " ... " + text.slice(-halfLength);
|
||||
}
|
||||
|
||||
async function replaceFileContent(uri: vscode.Uri, newContent: string) {
|
||||
try {
|
||||
// 创建一个 WorkspaceEdit 对象
|
||||
const workspaceEdit = new vscode.WorkspaceEdit();
|
||||
|
||||
// 获取文件的当前内容
|
||||
const document = await vscode.workspace.openTextDocument(uri);
|
||||
|
||||
// 计算文件的完整范围(从文件开始到文件结束)
|
||||
const fullRange = new vscode.Range(
|
||||
document.positionAt(0),
|
||||
document.positionAt(document.getText().length)
|
||||
);
|
||||
|
||||
// 使用 WorkspaceEdit 的 replace 方法替换文件的完整范围内容
|
||||
workspaceEdit.replace(uri, fullRange, newContent);
|
||||
|
||||
// 应用编辑更改
|
||||
await vscode.workspace.applyEdit(workspaceEdit);
|
||||
|
||||
// 显示成功消息
|
||||
vscode.window.showInformationMessage('File content replaced successfully.');
|
||||
} catch (error) {
|
||||
// 显示错误消息
|
||||
vscode.window.showErrorMessage('Failed to replace file content: ' + error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function applyCodeFile(text: string, fileName: string): Promise<void> {
|
||||
if (fileName) {
|
||||
await replaceFileContent(vscode.Uri.file(fileName), text);
|
||||
return;
|
||||
}
|
||||
|
||||
const validVisibleTextEditors = vscode.window.visibleTextEditors.filter(editor => editor.viewColumn !== undefined);
|
||||
|
||||
if (validVisibleTextEditors.length > 1) {
|
||||
vscode.window.showErrorMessage(`2There are more then one visible text editors. Please close all but one and try again.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = validVisibleTextEditors[0];
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const document = editor.document;
|
||||
const fullRange = new vscode.Range(
|
||||
document.positionAt(0),
|
||||
document.positionAt(document.getText().length)
|
||||
);
|
||||
|
||||
await editor.edit((editBuilder: vscode.TextEditorEdit) => {
|
||||
editBuilder.replace(fullRange, text);
|
||||
});
|
||||
}
|
||||
|
||||
regInMessage({command: 'code_file_apply', content: '', fileName: ''});
|
||||
export async function codeFileApply(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
await applyCodeFile(message.content, message.fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,20 +1,4 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as fs from 'fs';
|
||||
import { MessageHandler } from './messageHandler';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
import { logger } from '../util/logger';
|
||||
|
||||
|
||||
regInMessage({ command: 'contextDetail', file: '' });
|
||||
regOutMessage({ command: 'contextDetailResponse', file: '', result: '' });
|
||||
// message: { command: 'contextDetail', file: string }
|
||||
// read detail context information from file
|
||||
// return json string
|
||||
export async function contextDetail(message: any, panel: vscode.WebviewPanel | vscode.WebviewView): Promise<void> {
|
||||
try {
|
||||
const fileContent = fs.readFileSync(message.file, 'utf-8');
|
||||
MessageHandler.sendMessage(panel, { command: 'contextDetailResponse', 'file': message.file, result: fileContent });
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`Error reading file ${message.file}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
33
src/handler/contextHandler.ts
Normal file
33
src/handler/contextHandler.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as fs from "fs";
|
||||
|
||||
import { ChatContextManager } from '../context/contextManager';
|
||||
import { MessageHandler } from './messageHandler';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
import { logger } from "../util/logger";
|
||||
|
||||
|
||||
regInMessage({command: 'addContext', selected: ''});
|
||||
regOutMessage({command: 'appendContext', context: ''});
|
||||
export async function addConext(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
const contextStrList = await ChatContextManager.getInstance().handleContextSelected(message.selected);
|
||||
for (const contextStr of contextStrList) {
|
||||
MessageHandler.sendMessage(panel, { command: 'appendContext', context: contextStr });
|
||||
}
|
||||
}
|
||||
|
||||
regInMessage({ command: 'contextDetail', file: '' });
|
||||
regOutMessage({ command: 'contextDetailResponse', file: '', result: '' });
|
||||
// message: { command: 'contextDetail', file: string }
|
||||
// read detail context information from file
|
||||
// return json string
|
||||
export async function getContextDetail(message: any, panel: vscode.WebviewPanel | vscode.WebviewView): Promise<void> {
|
||||
try {
|
||||
const fileContent = fs.readFileSync(message.file, 'utf-8');
|
||||
MessageHandler.sendMessage(panel, { command: 'contextDetailResponse', 'file': message.file, result: fileContent });
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`Error reading file ${message.file}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
import * as vscode from 'vscode';
|
||||
import CommandManager from '../command/commandManager';
|
||||
import { MessageHandler } from './messageHandler';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
|
||||
|
||||
regInMessage({command: 'convertCommand', text: ''});
|
||||
regOutMessage({command: 'convertCommand', result: ''});
|
||||
export async function convertCommand(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
const newText = await CommandManager.getInstance().processText(message.text);
|
||||
MessageHandler.sendMessage(panel, { command: 'convertCommand', result: newText });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ export async function diffView(code: string, tarFile: string) {
|
||||
vscode.commands.executeCommand('vscode.diff', vscode.Uri.file(curFile), vscode.Uri.file(tempFile), 'Diff View');
|
||||
}
|
||||
|
||||
async function getFileContent(fileName: string): Promise<string | undefined> {
|
||||
export async function getFileContent(fileName: string): Promise<string | undefined> {
|
||||
const readFile = util.promisify(fs.readFile);
|
||||
try {
|
||||
// Read file content from fileName
|
||||
@ -122,7 +122,7 @@ async function getNewCode(message: any): Promise<string | undefined> {
|
||||
}
|
||||
|
||||
regInMessage({ command: 'show_diff', content: '', fileName: '' });
|
||||
export async function showDiff(message: any, panel: vscode.WebviewPanel | vscode.WebviewView): Promise<void> {
|
||||
export async function applyCodeWithDiff(message: any, panel: vscode.WebviewPanel | vscode.WebviewView): Promise<void> {
|
||||
const newCode = await getNewCode(message);
|
||||
if (!newCode) {
|
||||
return;
|
||||
@ -131,16 +131,3 @@ export async function showDiff(message: any, panel: vscode.WebviewPanel | vscode
|
||||
diffView(newCode, message.fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
regInMessage({ command: 'block_apply', content: '', fileName: '' });
|
||||
export async function blockApply(message: any, panel: vscode.WebviewPanel | vscode.WebviewView): Promise<void> {
|
||||
const newCode = await getNewCode(message);
|
||||
if (!newCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
diffView(newCode, message.fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ export async function featureToggle(message: any, panel: vscode.WebviewPanel|vsc
|
||||
|
||||
regInMessage({command: 'featureToggles'});
|
||||
regOutMessage({command: 'featureToggles', features: {'feature name': true}});
|
||||
export async function featureToggles(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
export async function getFeatureToggles(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
const featureTaggles = FTs();
|
||||
MessageHandler.sendMessage(panel, {command: 'featureToggles', features: featureTaggles});
|
||||
}
|
@ -1,25 +1,20 @@
|
||||
import { messageHandler } from './messageHandler';
|
||||
import { codeApply } from './codeApply';
|
||||
import { codeFileApply } from './codeFileApply';
|
||||
import { convertCommand } from './convertCommand';
|
||||
import { doCommit } from './doCommit';
|
||||
import { historyMessages } from './historyMessages';
|
||||
import { regCommandList, regCommandListByDevChatRun } from './regCommandList';
|
||||
import { regContextList } from './regContextList';
|
||||
import { insertCodeBlockToFile } from './codeBlockHandler';
|
||||
import { replaceCodeBlockToFile } from './codeBlockHandler';
|
||||
import { doCommit } from './commitHandler';
|
||||
import { getHistoryMessages } from './historyMessagesHandler';
|
||||
import { getWorkflowCommandList } from './workflowCommandHandler';
|
||||
import { getWorkflowContextList } from './workflowContextHandler';
|
||||
import { sendMessage, stopDevChat, regeneration, deleteChatMessage, userInput } from './sendMessage';
|
||||
import { blockApply } from './showDiff';
|
||||
import { showDiff } from './showDiff';
|
||||
import { addConext } from './addContext';
|
||||
import { addRefCommandContext } from './addRefCommandContext';
|
||||
import { contextDetail } from './contextDetail';
|
||||
import { applyCodeWithDiff } from './diffHandler';
|
||||
import { addConext } from './contextHandler';
|
||||
import { getContextDetail } from './contextHandler';
|
||||
import { listAllMessages } from './listMessages';
|
||||
import { regActionList } from './regActionList';
|
||||
import { applyAction } from './applyAction';
|
||||
import { doCommand } from './doCommand';
|
||||
import { getSetting, updateSetting } from './updateConfig';
|
||||
import { featureToggle, featureToggles } from './featureToggle';
|
||||
import { getUserAccessKey } from './userAccessKey';
|
||||
import { regModelList } from './regValidModelList';
|
||||
import { doVscodeCommand } from './vscodeCommandHandler';
|
||||
import { getSetting, updateSetting } from './userSettingHandler';
|
||||
import { featureToggle, getFeatureToggles } from './featureToggleHandler';
|
||||
import { getUserAccessKey } from './accessKeyHandler';
|
||||
import { getValidLlmModelList } from './llmModelHandler';
|
||||
|
||||
|
||||
// According to the context menu selected by the user, add the corresponding context file
|
||||
@ -27,26 +22,23 @@ import { regModelList } from './regValidModelList';
|
||||
messageHandler.registerHandler('addContext', addConext);
|
||||
// Apply the code block replied by AI to the currently active view
|
||||
// Response: none
|
||||
messageHandler.registerHandler('code_apply', codeApply);
|
||||
messageHandler.registerHandler('code_apply', insertCodeBlockToFile);
|
||||
// Apply the code block replied by AI to the currently active view, replacing the current file content
|
||||
// Response: none
|
||||
messageHandler.registerHandler('code_file_apply', codeFileApply);
|
||||
// Convert the command input into a natural language description sent to AI
|
||||
// Response: { command: 'convertCommand', result: <natural language description> }
|
||||
messageHandler.registerHandler('convertCommand', convertCommand);
|
||||
messageHandler.registerHandler('code_file_apply', replaceCodeBlockToFile);
|
||||
// Perform commit operation
|
||||
// Response: none
|
||||
messageHandler.registerHandler('doCommit', doCommit);
|
||||
// Get the history messages, called when the user view is displayed
|
||||
// Response: { command: 'historyMessages', result: <history messages> }
|
||||
// <history messages> is a list, the specific attribute information is determined when the interface is added
|
||||
messageHandler.registerHandler('historyMessages', historyMessages);
|
||||
messageHandler.registerHandler('historyMessages', getHistoryMessages);
|
||||
// Register the command list
|
||||
// Response: { command: 'regCommandList', result: <command list> }
|
||||
messageHandler.registerHandler('regCommandList', regCommandListByDevChatRun);
|
||||
messageHandler.registerHandler('regCommandList', getWorkflowCommandList);
|
||||
// Register the context list
|
||||
// Response: { command: 'regContextList', result: <context list> }
|
||||
messageHandler.registerHandler('regContextList', regContextList);
|
||||
messageHandler.registerHandler('regContextList', getWorkflowContextList);
|
||||
// Send a message, send the message entered by the user to AI
|
||||
// Response:
|
||||
// { command: 'receiveMessagePartial', text: <response message text>, user: <user>, date: <date> }
|
||||
@ -57,42 +49,32 @@ messageHandler.registerHandler('sendMessage', sendMessage);
|
||||
messageHandler.registerHandler('stopDevChat', stopDevChat);
|
||||
// Show diff
|
||||
// Response: none
|
||||
messageHandler.registerHandler('block_apply', blockApply);
|
||||
// Show diff, for historical reasons, the same as above
|
||||
messageHandler.registerHandler('show_diff', showDiff);
|
||||
// Process the ref command entered by the user
|
||||
// Response: { command: 'appendContext', context: <context file> }
|
||||
messageHandler.registerHandler('addRefCommandContext', addRefCommandContext);
|
||||
messageHandler.registerHandler('show_diff', applyCodeWithDiff);
|
||||
// Get context details
|
||||
// Response: { command: 'contextDetailResponse', 'file':<context file>, result: <context file content> }
|
||||
// <context file content> is a JSON string
|
||||
messageHandler.registerHandler('contextDetail', contextDetail);
|
||||
messageHandler.registerHandler('contextDetail', getContextDetail);
|
||||
// Debug handler
|
||||
messageHandler.registerHandler('listAllMessages', listAllMessages);
|
||||
// Regeneration
|
||||
// The response is the same as sendMessage
|
||||
messageHandler.registerHandler('regeneration', regeneration);
|
||||
// Register the action list
|
||||
// Response: { command: 'regActionList', result: <action list> }
|
||||
messageHandler.registerHandler('regActionList', regActionList);
|
||||
// Apply action for code block
|
||||
// Response: none
|
||||
messageHandler.registerHandler('applyAction', applyAction);
|
||||
// Delete chat message
|
||||
// Response: { command: 'deletedChatMessage', result: <message id> }
|
||||
messageHandler.registerHandler('deleteChatMessage', deleteChatMessage);
|
||||
|
||||
// Execute vscode command
|
||||
// Response: none
|
||||
messageHandler.registerHandler('doCommand', doCommand);
|
||||
messageHandler.registerHandler('doCommand', doVscodeCommand);
|
||||
|
||||
messageHandler.registerHandler('updateSetting', updateSetting);
|
||||
messageHandler.registerHandler('getSetting', getSetting);
|
||||
messageHandler.registerHandler('featureToggle', featureToggle);
|
||||
messageHandler.registerHandler('featureToggles', featureToggles);
|
||||
messageHandler.registerHandler('featureToggles', getFeatureToggles);
|
||||
|
||||
messageHandler.registerHandler('getUserAccessKey', getUserAccessKey);
|
||||
|
||||
messageHandler.registerHandler('regModelList', regModelList);
|
||||
messageHandler.registerHandler('regModelList', getValidLlmModelList);
|
||||
|
||||
messageHandler.registerHandler('userInput', userInput);
|
@ -10,7 +10,7 @@ import { UiUtilWrapper } from '../util/uiUtil';
|
||||
|
||||
regInMessage({command: 'historyMessages', page: 0});
|
||||
regOutMessage({command: 'loadHistoryMessages', entries: [{hash: '',user: '',date: '',request: '',response: '',context: [{content: '',role: ''}]}]});
|
||||
export async function historyMessages(message: {command: string, page: number}, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
export async function getHistoryMessages(message: {command: string, page: number}, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
// if history message has load, send it to webview
|
||||
const maxCount = Number(UiUtilWrapper.getConfiguration('DevChat', 'maxLogCount'));
|
||||
const skip = maxCount * (message.page ? message.page : 0);
|
@ -1,5 +1,5 @@
|
||||
import * as vscode from 'vscode';
|
||||
import ChatContextManager from '../context/contextManager';
|
||||
import { ChatContextManager } from '../context/contextManager';
|
||||
import { MessageHandler } from './messageHandler';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
import { ApiKeyManager } from '../util/apiKey';
|
||||
@ -8,7 +8,7 @@ import { UiUtilWrapper } from '../util/uiUtil';
|
||||
|
||||
regInMessage({command: 'regModelList'});
|
||||
regOutMessage({command: 'regModelList', result: [{name: ''}]});
|
||||
export async function regModelList(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
export async function getValidLlmModelList(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
const modelList = await ApiKeyManager.getValidModels();
|
||||
|
||||
MessageHandler.sendMessage(panel, { command: 'regModelList', result: modelList });
|
@ -2,18 +2,12 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import '../command/loadCommands';
|
||||
import '../context/loadContexts';
|
||||
import { logger } from '../util/logger';
|
||||
import { isWaitForApiKey } from './historyMessagesBase';
|
||||
import { onApiKey } from './historyMessages';
|
||||
import { onApiKey } from './historyMessagesHandler';
|
||||
import { ApiKeyManager } from '../util/apiKey';
|
||||
import { regeneration, sendMessage as sendMessageX } from './sendMessage';
|
||||
import { codeFileApply } from './codeFileApply';
|
||||
import { applyAction } from './applyAction';
|
||||
import { FT } from '../util/feature_flags/feature_toggles';
|
||||
|
||||
let autox = false;
|
||||
|
||||
export class MessageHandler {
|
||||
private handlers: { [command: string]: (message: any, panel: vscode.WebviewPanel|vscode.WebviewView) => Promise<void> } = {};
|
||||
@ -50,14 +44,6 @@ export class MessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
autox = false;
|
||||
if (message.command === 'sendMessage') {
|
||||
// if "/autox" in message.text, then flag global variable autox to true
|
||||
if (message.text.indexOf('/autox') !== -1) {
|
||||
autox = true;
|
||||
}
|
||||
}
|
||||
|
||||
const handler = this.handlers[message.command];
|
||||
if (handler) {
|
||||
logger.channel()?.info(`Handling the command "${message.command}"`);
|
||||
@ -79,54 +65,6 @@ export class MessageHandler {
|
||||
}
|
||||
|
||||
panel.webview.postMessage(message);
|
||||
|
||||
if (message.command === 'receiveMessage') {
|
||||
// if message.isError is true, then regenerate message
|
||||
if (message.isError) {
|
||||
if (autox) {
|
||||
regeneration({}, panel);
|
||||
}
|
||||
} else {
|
||||
// if message.text is ```command\n {xxx} ``` then get xxx
|
||||
const messageText = message.text;
|
||||
// if messageText match "```command\n ... ```", then parse block content
|
||||
const reg = /```command\n([\s\S]*)```/;
|
||||
const match = messageText.match(reg);
|
||||
if (match) {
|
||||
const command = match[1];
|
||||
try {
|
||||
const commandObject = JSON.parse(command);
|
||||
if (!(commandObject && commandObject.name)) {
|
||||
logger.channel()?.error(`${command} is not a valid command`);
|
||||
logger.channel()?.show();
|
||||
return ;
|
||||
}
|
||||
|
||||
if (commandObject.name === "fail_task" || commandObject.name === "finish_task") {
|
||||
logger.channel()?.info(`Task has finished.`);
|
||||
logger.channel()?.show();
|
||||
return ;
|
||||
}
|
||||
|
||||
applyAction({"content": command, "fileName": "", parentHash: message.hash}, panel);
|
||||
|
||||
} catch (e) {
|
||||
logger.channel()?.error(`parse ${command} error: ${e}`);
|
||||
logger.channel()?.show();
|
||||
|
||||
if (autox) {
|
||||
MessageHandler.sendMessage(panel, { "command": "systemMessage", "text": "continue. 并且确认你在围绕最初的任务在执行相关步骤。" });
|
||||
sendMessageX({command: 'sendMessage', text: "continue"}, panel);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (autox) {
|
||||
MessageHandler.sendMessage(panel, { "command": "systemMessage", "text": "continue. 并且确认你在围绕最初的任务在执行相关步骤。" });
|
||||
sendMessageX({command: 'sendMessage', text: "continue"}, panel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { MessageHandler } from './messageHandler';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
import ActionManager from '../action/actionManager';
|
||||
|
||||
|
||||
|
||||
regInMessage({command: 'regActionList'});
|
||||
regOutMessage({command: 'regActionList', result: [{name: '', description: '', type: ['', ''], action: ''}]});
|
||||
export async function regActionList(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
const actionList = ActionManager.getInstance().getActionList();
|
||||
MessageHandler.sendMessage(panel, { command: 'regActionList', result: actionList });
|
||||
}
|
||||
|
||||
|
@ -2,22 +2,17 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { MessageHandler } from './messageHandler';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
import { stopDevChatBase, sendMessageBase, deleteChatMessageBase, insertDevChatLog, handleTopic } from './sendMessageBase';
|
||||
import {
|
||||
stopDevChatBase,
|
||||
sendMessageBase,
|
||||
deleteChatMessageBase,
|
||||
sendTextToDevChat
|
||||
} from './sendMessageBase';
|
||||
import { UiUtilWrapper } from '../util/uiUtil';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { ApiKeyManager } from '../util/apiKey';
|
||||
import { logger } from '../util/logger';
|
||||
import { exec as execCb } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
import { CommandResult, CommandRun, createTempSubdirectory } from '../util/commonUtil';
|
||||
import { WorkflowRunner } from './workflowExecutor';
|
||||
import DevChat from '../toolwrapper/devchat';
|
||||
|
||||
const exec = promisify(execCb);
|
||||
|
||||
let commandRunner : WorkflowRunner | null = null;
|
||||
|
||||
let _lastMessage: any = undefined;
|
||||
|
||||
@ -37,68 +32,11 @@ export function deleteTempFiles(fileName: string): void {
|
||||
fs.unlinkSync(fileName);
|
||||
}
|
||||
|
||||
regInMessage({command: 'userInput', text: '{"field": "value", "field2": "value2"}'});;
|
||||
export async function userInput(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
commandRunner?.input(message.text);
|
||||
}
|
||||
function writeContextInfoToTempFiles(message : any) : string[] {
|
||||
let tempFiles: string[] = [];
|
||||
message.old_text = message.old_text || message.text;
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
regInMessage({command: 'sendMessage', text: '', parent_hash: undefined});
|
||||
regOutMessage({ command: 'receiveMessage', text: 'xxxx', hash: 'xxx', user: 'xxx', date: 'xxx'});
|
||||
regOutMessage({ command: 'receiveMessagePartial', text: 'xxxx', user: 'xxx', date: 'xxx'});
|
||||
export async function sendMessage(message: any, panel: vscode.WebviewPanel|vscode.WebviewView, functionName: string|undefined = undefined): Promise<void> {
|
||||
// check whether the message is a command
|
||||
if (functionName !== undefined && functionName !== "") {
|
||||
const messageText = _lastMessage[0].text.trim();
|
||||
if (messageText[0] === '/' && message.text[0] !== '/') {
|
||||
const indexS = messageText.indexOf(' ');
|
||||
let preCommand = messageText;
|
||||
if (indexS !== -1) {
|
||||
preCommand = messageText.substring(0, indexS);
|
||||
}
|
||||
|
||||
message.text = preCommand + ' ' + message.text;
|
||||
}
|
||||
}
|
||||
_lastMessage = [message, functionName];
|
||||
|
||||
const messageText = message.text.trim();
|
||||
if (messageText[0] === '/') {
|
||||
// split messageText by ' ' or '\n' or '\t'
|
||||
const messageTextArr = messageText.split(/ |\n|\t/);
|
||||
// get command name from messageTextArr
|
||||
const commandName = messageTextArr[0].substring(1);
|
||||
// test whether the command is a execute command
|
||||
const devChat = new DevChat();
|
||||
const stdout = await devChat.commandPrompt(commandName);
|
||||
// try parse stdout by json
|
||||
let stdoutJson: any = null;
|
||||
try {
|
||||
stdoutJson = JSON.parse(stdout);
|
||||
} catch (error) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
if (stdoutJson) {
|
||||
// run command
|
||||
try {
|
||||
commandRunner = null;
|
||||
|
||||
commandRunner = new WorkflowRunner();
|
||||
await commandRunner.run(commandName, stdoutJson, message, panel);
|
||||
} finally {
|
||||
commandRunner = null;
|
||||
}
|
||||
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new field to store the names of temporary files
|
||||
let tempFiles: string[] = [];
|
||||
|
||||
// Handle the contextInfo field in the message
|
||||
// Handle the contextInfo field in the message
|
||||
if (Array.isArray(message.contextInfo)) {
|
||||
for (let context of message.contextInfo) {
|
||||
if (typeof context === 'object' && context !== null && 'context' in context) {
|
||||
@ -115,16 +53,34 @@ export async function sendMessage(message: any, panel: vscode.WebviewPanel|vscod
|
||||
}
|
||||
}
|
||||
// Insert the file name into the text field
|
||||
message.text += ` [context|${context.file}]`;
|
||||
message.text = message.old_text + ` [context|${context.file}]`;
|
||||
}
|
||||
}
|
||||
}
|
||||
// clear message.contextInfo
|
||||
message.contextInfo = undefined;
|
||||
|
||||
return tempFiles;
|
||||
}
|
||||
|
||||
regInMessage({command: 'userInput', text: '{"field": "value", "field2": "value2"}'});;
|
||||
export async function userInput(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
sendTextToDevChat(message.text);
|
||||
}
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
regInMessage({command: 'sendMessage', text: '', parent_hash: undefined});
|
||||
regOutMessage({ command: 'receiveMessage', text: 'xxxx', hash: 'xxx', user: 'xxx', date: 'xxx'});
|
||||
regOutMessage({ command: 'receiveMessagePartial', text: 'xxxx', user: 'xxx', date: 'xxx'});
|
||||
export async function sendMessage(message: any, panel: vscode.WebviewPanel|vscode.WebviewView, functionName: string|undefined = undefined): Promise<void> {
|
||||
// check whether the message is a command
|
||||
_lastMessage = message;
|
||||
|
||||
// Add a new field to store the names of temporary files
|
||||
const tempFiles: string[] = writeContextInfoToTempFiles(message);
|
||||
|
||||
const responseMessage = await sendMessageBase(message, (data: { command: string, text: string, user: string, date: string}) => {
|
||||
MessageHandler.sendMessage(panel, data, false);
|
||||
}, functionName);
|
||||
});
|
||||
if (responseMessage) {
|
||||
MessageHandler.sendMessage(panel, responseMessage);
|
||||
}
|
||||
@ -141,18 +97,13 @@ regInMessage({command: 'regeneration'});
|
||||
export async function regeneration(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
// call sendMessage to send last message again
|
||||
if (_lastMessage) {
|
||||
await sendMessage(_lastMessage[0], panel, _lastMessage[1]);
|
||||
await sendMessage(_lastMessage, panel);
|
||||
}
|
||||
}
|
||||
|
||||
regInMessage({command: 'stopDevChat'});
|
||||
export async function stopDevChat(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
stopDevChatBase(message);
|
||||
|
||||
if (commandRunner) {
|
||||
commandRunner.stop();
|
||||
commandRunner = null;
|
||||
}
|
||||
}
|
||||
|
||||
regInMessage({command: 'deleteChatMessage', hash: 'xxx'});
|
||||
|
@ -1,14 +1,100 @@
|
||||
import DevChat, { ChatResponse } from '../toolwrapper/devchat';
|
||||
import CommandManager from '../command/commandManager';
|
||||
import DevChat, { ChatOptions, ChatResponse } from '../toolwrapper/devchat';
|
||||
import { logger } from '../util/logger';
|
||||
import messageHistory from '../util/messageHistory';
|
||||
import { TopicManager } from '../topic/topicManager';
|
||||
import CustomCommands from '../command/customCommand';
|
||||
import { assertValue } from '../util/check';
|
||||
|
||||
|
||||
let waitCreateTopic = false;
|
||||
/**
|
||||
* Class to handle topic updates.
|
||||
*/
|
||||
export class TopicUpdateHandler {
|
||||
/**
|
||||
* Flag to indicate if the topic is being updated from the webview.
|
||||
*/
|
||||
private static isTopicUpdatingFromWebview: boolean = false;
|
||||
|
||||
/**
|
||||
* Checks if the topic change was triggered by the webview.
|
||||
*
|
||||
* @returns {boolean} - Returns true if the topic change was triggered by the webview, false otherwise.
|
||||
*/
|
||||
public static isTopicChangeTriggeredByWebview(): boolean {
|
||||
return TopicUpdateHandler.isTopicUpdatingFromWebview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the topic change after a chat.
|
||||
*
|
||||
* @param {string | undefined} parentHash - The hash of the parent message, if any.
|
||||
* @param {any} message - The message object.
|
||||
* @param {ChatResponse} chatResponse - The chat response object.
|
||||
* @returns {Promise<void>} - A Promise that resolves when the topic change has been processed.
|
||||
*/
|
||||
public static async processTopicChangeAfterChat(parentHash:string | undefined, message: any, chatResponse: ChatResponse): Promise<void> {
|
||||
TopicUpdateHandler.isTopicUpdatingFromWebview = true;
|
||||
try {
|
||||
if (!chatResponse.isError) {
|
||||
let topicId = TopicManager.getInstance().currentTopicId;
|
||||
if (!topicId) {
|
||||
// create new topic
|
||||
const topic = TopicManager.getInstance().createTopic();
|
||||
topicId = topic.topicId;
|
||||
}
|
||||
|
||||
TopicManager.getInstance().updateTopic(
|
||||
topicId!,
|
||||
chatResponse['prompt-hash'],
|
||||
parseDateStringToTimestamp(chatResponse.date),
|
||||
message.text,
|
||||
chatResponse.response
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
TopicUpdateHandler.isTopicUpdatingFromWebview = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to handle user interaction stop events.
|
||||
*/
|
||||
class UserStopHandler {
|
||||
/**
|
||||
* Flag to indicate if user interaction is stopped.
|
||||
*/
|
||||
private static userStop: boolean = false;
|
||||
|
||||
/**
|
||||
* Stops user interaction.
|
||||
*/
|
||||
public static stopUserInteraction(): void {
|
||||
UserStopHandler.userStop = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes user interaction.
|
||||
*/
|
||||
public static resumeUserInteraction(): void {
|
||||
UserStopHandler.userStop = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if user interaction is stopped.
|
||||
*
|
||||
* @returns {boolean} - Returns true if user interaction is stopped, false otherwise.
|
||||
*/
|
||||
public static isUserInteractionStopped(): boolean {
|
||||
return UserStopHandler.userStop;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a date string and returns the corresponding timestamp.
|
||||
*
|
||||
* @param {string} dateString - The date string to be parsed.
|
||||
* @returns {number} - The timestamp corresponding to the date string.
|
||||
*/
|
||||
function parseDateStringToTimestamp(dateString: string): number {
|
||||
const dateS = Date.parse(dateString);
|
||||
if (!isNaN(dateS)) {
|
||||
@ -24,11 +110,12 @@ function parseDateStringToTimestamp(dateString: string): number {
|
||||
return date.getTime();
|
||||
}
|
||||
|
||||
export function getWaitCreateTopic(): boolean {
|
||||
return waitCreateTopic;
|
||||
}
|
||||
|
||||
// Add this function to messageHandler.ts
|
||||
/**
|
||||
* Parses a message and extracts the context, instruction, reference, and text.
|
||||
*
|
||||
* @param {string} message - The message to be parsed.
|
||||
* @returns {{ context: string[]; instruction: string[]; reference: string[]; text: string }} - An object containing the context, instruction, reference, and text extracted from the message.
|
||||
*/
|
||||
export function parseMessage(message: string): { context: string[]; instruction: string[]; reference: string[]; text: string } {
|
||||
const contextRegex = /\[context\|(.*?)\]/g;
|
||||
const instructionRegex = /\[instruction\|(.*?)\]/g;
|
||||
@ -65,148 +152,151 @@ export function parseMessage(message: string): { context: string[]; instruction:
|
||||
return { context: contextPaths, instruction: instructionPaths, reference: referencePaths, text };
|
||||
}
|
||||
|
||||
export function getInstructionFiles(): string[] {
|
||||
const instructionFiles: string[] = [];
|
||||
/**
|
||||
* Parses a message and sets the chat options based on the parsed message.
|
||||
*
|
||||
* @param {any} message - The message object.
|
||||
* @returns {Promise<[{ context: string[]; instruction: string[]; reference: string[]; text: string }, ChatOptions]>} - A Promise that resolves to an array containing the parsed message and the chat options.
|
||||
*/
|
||||
export async function parseMessageAndSetOptions(message: any): Promise<[{ context: string[]; instruction: string[]; reference: string[]; text: string }, ChatOptions]> {
|
||||
const parsedMessage = parseMessage(message.text);
|
||||
|
||||
const customCommands = CustomCommands.getInstance().getCommands();
|
||||
// visit customCommands, get default command
|
||||
for (const command of customCommands) {
|
||||
if (command.default) {
|
||||
for (const instruction of command.instructions) {
|
||||
instructionFiles.push(`${instruction}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
const chatOptions: ChatOptions = {
|
||||
header: [],
|
||||
...message.parent_hash ? { parent: message.parent_hash } : {},
|
||||
...parsedMessage.context.length > 0 ? { context: parsedMessage.context } : {},
|
||||
...parsedMessage.reference.length > 0 ? { reference: parsedMessage.reference } : {},
|
||||
};
|
||||
|
||||
return instructionFiles;
|
||||
return [parsedMessage, chatOptions];
|
||||
}
|
||||
|
||||
const devChat = new DevChat();
|
||||
let userStop = false;
|
||||
|
||||
|
||||
// 将解析消息的部分提取到一个单独的函数中
|
||||
export async function parseMessageAndSetOptions(message: any, chatOptions: any): Promise<{ context: string[]; instruction: string[]; reference: string[]; text: string }> {
|
||||
const newText2 = await CommandManager.getInstance().processText(message.text);
|
||||
const parsedMessage = parseMessage(newText2);
|
||||
|
||||
if (parsedMessage.context.length > 0) {
|
||||
chatOptions.context = parsedMessage.context;
|
||||
}
|
||||
|
||||
chatOptions.header = getInstructionFiles();
|
||||
if ((parsedMessage.instruction && parsedMessage.instruction.length > 0) || newText2 !== message.text) {
|
||||
chatOptions.header = parsedMessage.instruction;
|
||||
}
|
||||
|
||||
if (parsedMessage.reference.length > 0) {
|
||||
chatOptions.reference = parsedMessage.reference;
|
||||
}
|
||||
|
||||
return parsedMessage;
|
||||
/**
|
||||
* Adds a message to the message history.
|
||||
*
|
||||
* @param {any} message - The message object.
|
||||
* @param {ChatResponse} chatResponse - The chat response object.
|
||||
* @param {string | undefined} parentHash - The hash of the parent message, if any.
|
||||
* @returns {void}
|
||||
*/
|
||||
export async function addMessageToHistory(message: any, chatResponse: ChatResponse, parentHash: string | undefined): Promise<void> {
|
||||
messageHistory.add({
|
||||
request: message.text,
|
||||
text: chatResponse.response,
|
||||
parentHash,
|
||||
hash: chatResponse['prompt-hash'],
|
||||
user: chatResponse.user,
|
||||
date: chatResponse.date
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export async function handleTopic(parentHash:string | undefined, message: any, chatResponse: ChatResponse) {
|
||||
waitCreateTopic = true;
|
||||
try {
|
||||
if (!chatResponse.isError) {
|
||||
messageHistory.add({ request: message.text, text: chatResponse.response, parentHash, hash: chatResponse['prompt-hash'], user: chatResponse.user, date: chatResponse.date });
|
||||
|
||||
let topicId = TopicManager.getInstance().currentTopicId;
|
||||
if (!topicId) {
|
||||
// create new topic
|
||||
const topic = TopicManager.getInstance().createTopic();
|
||||
topicId = topic.topicId;
|
||||
}
|
||||
|
||||
TopicManager.getInstance().updateTopic(topicId!, chatResponse['prompt-hash'], parseDateStringToTimestamp(chatResponse.date), message.text, chatResponse.response);
|
||||
}
|
||||
} finally {
|
||||
waitCreateTopic = false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function handlerResponseText(partialDataText: string, chatResponse: ChatResponse) : Promise<string|undefined> {
|
||||
/**
|
||||
* Processes the chat response by replacing certain patterns in the response text.
|
||||
*
|
||||
* @param {ChatResponse} chatResponse - The chat response object.
|
||||
* @returns {string} - The processed response text.
|
||||
*/
|
||||
export function processChatResponse(chatResponse: ChatResponse) : string {
|
||||
let responseText = chatResponse.response.replace(/```\ncommitmsg/g, "```commitmsg");
|
||||
if (userStop) {
|
||||
userStop = false;
|
||||
if (chatResponse.isError) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return responseText;
|
||||
}
|
||||
|
||||
// 重构后的sendMessage函数
|
||||
export async function sendMessageBase(message: any, handlePartialData: (data: { command: string, text: string, user: string, date: string}) => void, function_name: string| undefined = undefined): Promise<{ command: string, text: string, hash: string, user: string, date: string, isError: boolean }|undefined> {
|
||||
userStop = false;
|
||||
const chatOptions: any = {};
|
||||
const parsedMessage = await parseMessageAndSetOptions(message, chatOptions);
|
||||
|
||||
if (message.parent_hash) {
|
||||
chatOptions.parent = message.parent_hash;
|
||||
const devChat = new DevChat();
|
||||
|
||||
/**
|
||||
* Sends a message to the DevChat and handles the response.
|
||||
*
|
||||
* @param {any} message - The message object.
|
||||
* @param {(data: { command: string, text: string, user: string, date: string}) => void} handlePartialData - A function to handle partial data.
|
||||
* @param {string | undefined} function_name - The name of the function, if any.
|
||||
* @returns {Promise<{ command: string, text: string, hash: string, user: string, date: string, isError: boolean } | undefined>} - A Promise that resolves to an object containing the command, text, hash, user, date, and isError properties, or undefined if an error occurred.
|
||||
*/
|
||||
export async function sendMessageBase(message: any, handlePartialData: (data: { command: string, text: string, user: string, date: string}) => void): Promise<{ command: string, text: string, hash: string, user: string, date: string, isError: boolean }|undefined> {
|
||||
try {
|
||||
UserStopHandler.resumeUserInteraction();
|
||||
|
||||
// parse context and others from message
|
||||
const [parsedMessage, chatOptions] = await parseMessageAndSetOptions(message);
|
||||
logger.channel()?.info(`parent hash: ${chatOptions.parent}`);
|
||||
|
||||
// call devchat chat
|
||||
const chatResponse = await devChat.chat(
|
||||
parsedMessage.text,
|
||||
chatOptions,
|
||||
(partialResponse: ChatResponse) => {
|
||||
const partialDataText = partialResponse.response.replace(/```\ncommitmsg/g, "```commitmsg");
|
||||
handlePartialData({ command: 'receiveMessagePartial', text: partialDataText!, user: partialResponse.user, date: partialResponse.date });
|
||||
});
|
||||
|
||||
assertValue(UserStopHandler.isUserInteractionStopped(), "User Stopped");
|
||||
|
||||
await addMessageToHistory(message, chatResponse, message.parent_hash);
|
||||
await TopicUpdateHandler.processTopicChangeAfterChat(message.parent_hash, message, chatResponse);
|
||||
|
||||
return {
|
||||
command: 'receiveMessage',
|
||||
text: processChatResponse(chatResponse),
|
||||
hash: chatResponse['prompt-hash'],
|
||||
user: chatResponse.user,
|
||||
date: chatResponse.date,
|
||||
isError: chatResponse.isError
|
||||
};
|
||||
} catch (error: any) {
|
||||
logger.channel()?.error(`Error occurred while sending response: ${error.message}`);
|
||||
return ;
|
||||
} finally {
|
||||
UserStopHandler.resumeUserInteraction();
|
||||
}
|
||||
logger.channel()?.info(`parent hash: ${chatOptions.parent}`);
|
||||
|
||||
chatOptions.functions = "./.chat/functions.json";
|
||||
if (function_name) {
|
||||
chatOptions.function_name = function_name;
|
||||
chatOptions.role = "function";
|
||||
}
|
||||
|
||||
let partialDataText = '';
|
||||
const onData = (partialResponse: ChatResponse) => {
|
||||
partialDataText = partialResponse.response.replace(/```\ncommitmsg/g, "```commitmsg");
|
||||
handlePartialData({ command: 'receiveMessagePartial', text: partialDataText!, user: partialResponse.user, date: partialResponse.date });
|
||||
};
|
||||
|
||||
const chatResponse = await devChat.chat(parsedMessage.text, chatOptions, onData);
|
||||
await handleTopic(message.parent_hash, message, chatResponse);
|
||||
const responseText = await handlerResponseText(partialDataText, chatResponse);
|
||||
if (responseText === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
return { command: 'receiveMessage', text: responseText, hash: chatResponse['prompt-hash'], user: chatResponse.user, date: chatResponse.date, isError: chatResponse.isError };
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the DevChat and user interaction.
|
||||
*
|
||||
* @param {any} message - The message object.
|
||||
* @returns {Promise<void>} - A Promise that resolves when the DevChat and user interaction have been stopped.
|
||||
*/
|
||||
export async function stopDevChatBase(message: any): Promise<void> {
|
||||
logger.channel()?.info(`Stopping devchat`);
|
||||
userStop = true;
|
||||
UserStopHandler.stopUserInteraction();
|
||||
devChat.stop();
|
||||
}
|
||||
|
||||
export async function insertDevChatLog(message: any, request: string, response: string): Promise<string | undefined> {
|
||||
logger.channel()?.info(`Inserting devchat log`);
|
||||
await devChat.logInsert(request, response, message.parent_hash);
|
||||
const logs = await devChat.log({"maxCount": 1});
|
||||
if (logs && logs.length > 0) {
|
||||
return logs[0]['hash'];
|
||||
} else {
|
||||
return undefined;
|
||||
/**
|
||||
* Deletes a chat message.
|
||||
* Each message is identified by a hash.
|
||||
*
|
||||
* @param {Object} message - The message object.
|
||||
* @param {string} message.hash - The hash of the message.
|
||||
* @returns {Promise<boolean>} - Returns true if the deletion was successful, false otherwise.
|
||||
* @throws Will throw an error if the deletion was unsuccessful.
|
||||
*/
|
||||
export async function deleteChatMessageBase(message:{'hash': string}): Promise<boolean> {
|
||||
try {
|
||||
assertValue(!message.hash, 'Message hash is required');
|
||||
// delete the message from messageHistory
|
||||
messageHistory.delete(message.hash);
|
||||
|
||||
// delete the message by devchat
|
||||
const bSuccess = await devChat.delete(message.hash);
|
||||
assertValue(!bSuccess, "Failed to delete message from devchat");
|
||||
|
||||
TopicManager.getInstance().deleteMessageOnCurrentTopic(message.hash);
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
logger.channel()?.error(`Error: ${error.message}`);
|
||||
logger.channel()?.show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// delete a chat message
|
||||
// each message is identified by hash
|
||||
export async function deleteChatMessageBase(message:{'hash': string}): Promise<boolean> {
|
||||
// if hash is undefined, return
|
||||
if (!message.hash) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// delete the message from messageHistory
|
||||
messageHistory.delete(message.hash);
|
||||
|
||||
// delete the message by devchat
|
||||
const bSuccess = await devChat.delete(message.hash);
|
||||
if (bSuccess) {
|
||||
let topicId = TopicManager.getInstance().currentTopicId;
|
||||
if (topicId) {
|
||||
TopicManager.getInstance().deleteMessage(topicId, message.hash);
|
||||
}
|
||||
}
|
||||
return bSuccess;
|
||||
/**
|
||||
* Sends a text message to the DevChat.
|
||||
*
|
||||
* @param {string} text - The text message to be sent.
|
||||
* @returns {Promise<void>} - A Promise that resolves when the message has been sent.
|
||||
*/
|
||||
export async function sendTextToDevChat(text: string): Promise<void> {
|
||||
return devChat.input(text);
|
||||
}
|
@ -7,7 +7,7 @@ import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
import { logger } from '../util/logger';
|
||||
|
||||
regInMessage({command: 'doCommand', content: ['command', 'arg1', 'arg2']});
|
||||
export async function doCommand(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
export async function doVscodeCommand(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
// execute vscode command
|
||||
// message.content[0]: vscode command
|
||||
// message.content[1:]: args for command
|
@ -1,35 +1,46 @@
|
||||
import * as vscode from 'vscode';
|
||||
import CommandManager from '../command/commandManager';
|
||||
import { MessageHandler } from './messageHandler';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
import { ApiKeyManager } from '../util/apiKey';
|
||||
import DevChat from '../toolwrapper/devchat';
|
||||
|
||||
|
||||
regInMessage({command: 'regCommandList'});
|
||||
regOutMessage({command: 'regCommandList', result: [{name: '', pattern: '', description: ''}]});
|
||||
export async function regCommandList(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
const commandList = CommandManager.getInstance().getCommandList();
|
||||
const commandCovertedList = commandList.map(command => {
|
||||
if (command.args > 0) {
|
||||
// replace {{prompt}} with {{["",""]}}, count of "" is args
|
||||
const prompt = Array.from({length: command.args}, () => "");
|
||||
command.pattern = command.pattern.replace('{{prompt}}', '{{' + JSON.stringify(prompt) + '}}');
|
||||
}
|
||||
return command;
|
||||
export interface Command {
|
||||
name: string;
|
||||
pattern: string;
|
||||
description: string;
|
||||
args: number;
|
||||
handler: (commandName: string, userInput: string) => Promise<string>;
|
||||
}
|
||||
|
||||
async function getCommandListByDevChatRun(includeHide: boolean = false): Promise<Command[]> {
|
||||
// load commands from CustomCommands
|
||||
let newCommands: Command[] = [];
|
||||
|
||||
const devChat = new DevChat();
|
||||
const commandList = await devChat.commands();
|
||||
commandList.forEach(command => {
|
||||
const commandObj: Command = {
|
||||
name: command.name,
|
||||
pattern: command.name,
|
||||
description: command.description,
|
||||
args: 0,
|
||||
handler: async (commandName: string, userInput: string) => { return ''; }
|
||||
};
|
||||
newCommands.push(commandObj);
|
||||
});
|
||||
|
||||
MessageHandler.sendMessage(panel, { command: 'regCommandList', result: commandCovertedList });
|
||||
return;
|
||||
|
||||
return newCommands;
|
||||
}
|
||||
|
||||
let existPannel: vscode.WebviewPanel|vscode.WebviewView|undefined = undefined;
|
||||
|
||||
regInMessage({command: 'regCommandList'});
|
||||
regOutMessage({command: 'regCommandList', result: [{name: '', pattern: '', description: ''}]});
|
||||
export async function regCommandListByDevChatRun(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
export async function getWorkflowCommandList(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
existPannel = panel;
|
||||
|
||||
const commandList = await CommandManager.getInstance().getCommandListByDevChatRun();
|
||||
const commandList = await getCommandListByDevChatRun();
|
||||
const commandCovertedList = commandList.map(command => {
|
||||
if (command.args > 0) {
|
||||
// replace {{prompt}} with {{["",""]}}, count of "" is args
|
||||
@ -45,7 +56,7 @@ export async function regCommandListByDevChatRun(message: any, panel: vscode.Web
|
||||
|
||||
export async function sendCommandListByDevChatRun() {
|
||||
if (existPannel) {
|
||||
regCommandListByDevChatRun({}, existPannel!);
|
||||
getWorkflowCommandList({}, existPannel!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,4 +64,3 @@ export async function updateChatModels() {
|
||||
const modelList = await ApiKeyManager.getValidModels();
|
||||
MessageHandler.sendMessage(existPannel!, { command: 'regModelList', result: modelList });
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as vscode from 'vscode';
|
||||
import ChatContextManager from '../context/contextManager';
|
||||
import { ChatContextManager } from '../context/contextManager';
|
||||
import { MessageHandler } from './messageHandler';
|
||||
import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
|
||||
@ -7,10 +7,8 @@ import { regInMessage, regOutMessage } from '../util/reg_messages';
|
||||
|
||||
regInMessage({command: 'regContextList'});
|
||||
regOutMessage({command: 'regContextList', result: [{name: '', description: ''}]});
|
||||
export async function regContextList(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
export async function getWorkflowContextList(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
const contextList = ChatContextManager.getInstance().getContextList();
|
||||
MessageHandler.sendMessage(panel, { command: 'regContextList', result: contextList });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,283 +0,0 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { UiUtilWrapper } from "../util/uiUtil";
|
||||
import { MessageHandler } from "../handler/messageHandler";
|
||||
import { ApiKeyManager } from "../util/apiKey";
|
||||
import { logger } from "../util/logger";
|
||||
import { CommandResult, CommandRun, saveModelSettings } from "../util/commonUtil";
|
||||
import { handleTopic, insertDevChatLog } from "./sendMessageBase";
|
||||
import { regInMessage } from "@/util/reg_messages";
|
||||
import parseArgsStringToArgv from 'string-argv';
|
||||
|
||||
|
||||
async function handleWorkflowRequest(request): Promise<string | undefined> {
|
||||
/*
|
||||
request: {
|
||||
"command": "some command",
|
||||
"args": {
|
||||
"arg1": "value1",
|
||||
"arg2": "value2"
|
||||
}
|
||||
}
|
||||
response: {
|
||||
"status": "success",
|
||||
"result": "success",
|
||||
"detail": "some detail"
|
||||
}
|
||||
*/
|
||||
if (!request || !request.command) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (request.command === "get_lsp_brige_port") {
|
||||
return JSON.stringify({
|
||||
"status": "success",
|
||||
"result": await UiUtilWrapper.getLSPBrigePort()
|
||||
});
|
||||
} else {
|
||||
return JSON.stringify({
|
||||
"status": "fail",
|
||||
"result": "fail",
|
||||
"detail": "command is not supported"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkflowRunner {
|
||||
private _commandRunner: CommandRun | null = null;
|
||||
private _stop: boolean = false;
|
||||
private _cacheOut: string = "";
|
||||
private _panel: vscode.WebviewPanel|vscode.WebviewView | null = null;
|
||||
|
||||
constructor() {}
|
||||
|
||||
private async _getApiKeyAndApiBase(): Promise<[string | undefined, string | undefined]> {
|
||||
const llmModelData = await ApiKeyManager.llmModel();
|
||||
if (!llmModelData) {
|
||||
logger.channel()?.error('No valid llm model is selected!');
|
||||
logger.channel()?.show();
|
||||
return [undefined, undefined];
|
||||
}
|
||||
|
||||
let openaiApiKey = llmModelData.api_key;
|
||||
if (!openaiApiKey) {
|
||||
logger.channel()?.error('The OpenAI key is invalid!');
|
||||
logger.channel()?.show();
|
||||
return [undefined, undefined];
|
||||
}
|
||||
|
||||
const openAiApiBase = llmModelData.api_base;
|
||||
return [openaiApiKey, openAiApiBase];
|
||||
}
|
||||
|
||||
private _parseCommandOutput(outputStr: string): string {
|
||||
/*
|
||||
output is format as:
|
||||
<<Start>>
|
||||
{"content": "data"}
|
||||
<<End>>
|
||||
*/
|
||||
const outputWitchCache = this._cacheOut + outputStr;
|
||||
this._cacheOut = "";
|
||||
|
||||
let outputResult = "";
|
||||
let curPos = 0;
|
||||
while (true) {
|
||||
const startPos = outputWitchCache.indexOf('<<Start>>', curPos);
|
||||
const startPos2 = outputWitchCache.indexOf('```', curPos);
|
||||
if (startPos === -1 && startPos2 === -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
const isStart = (startPos2 === -1) || (startPos > -1 && startPos < startPos2);
|
||||
|
||||
let endPos = -1;
|
||||
if (isStart) {
|
||||
endPos = outputWitchCache.indexOf('<<End>>', startPos+9);
|
||||
} else {
|
||||
endPos = outputWitchCache.indexOf('```', startPos2+3);
|
||||
}
|
||||
|
||||
if (endPos === -1) {
|
||||
this._cacheOut = outputWitchCache.substring(startPos, outputWitchCache.length);
|
||||
break;
|
||||
}
|
||||
|
||||
let contentStr = "";
|
||||
if (isStart) {
|
||||
contentStr = outputWitchCache.substring(startPos+9, endPos);
|
||||
curPos = endPos+7;
|
||||
|
||||
try {
|
||||
const contentObj = JSON.parse(contentStr);
|
||||
if (contentObj && contentObj.result) {
|
||||
contentStr = contentObj.result;
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
} else {
|
||||
contentStr = outputWitchCache.substring(startPos2, endPos+3);
|
||||
curPos = endPos+3;
|
||||
}
|
||||
|
||||
outputResult += contentStr.trim() + "\n\n";
|
||||
}
|
||||
|
||||
return outputResult;
|
||||
}
|
||||
|
||||
private _parseLastCommandOutput(outputStr: string): string {
|
||||
const startPos = outputStr.lastIndexOf('<<Start>>');
|
||||
const endPos = outputStr.lastIndexOf('<<End>>');
|
||||
|
||||
if (startPos === -1 || endPos === -1 || endPos < startPos) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return outputStr.substring(startPos+9, endPos);
|
||||
}
|
||||
|
||||
private async _runCommand(commandWithArgs: string, commandEnvs: any): Promise<[CommandResult | undefined, string]> {
|
||||
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath() || "";
|
||||
let commandOutput = "";
|
||||
let commandAnswer = "";
|
||||
|
||||
try {
|
||||
const commandAndArgsList = parseArgsStringToArgv(commandWithArgs);
|
||||
this._commandRunner = new CommandRun();
|
||||
await saveModelSettings();
|
||||
const result = await this._commandRunner.spawnAsync(commandAndArgsList[0], commandAndArgsList.slice(1), { env: commandEnvs, cwd: workspaceDir }, async (data) => {
|
||||
// handle command stdout
|
||||
const newData = this._parseCommandOutput(data);
|
||||
// if newData is json string, then process it by handleWorkflowRequest
|
||||
let newDataObj: any = undefined;
|
||||
try {
|
||||
newDataObj = JSON.parse(newData);
|
||||
const result = await handleWorkflowRequest(newDataObj);
|
||||
if (result) {
|
||||
|
||||
this.input(result);
|
||||
} else if (newDataObj!.result) {
|
||||
commandAnswer = newDataObj!.result;
|
||||
commandOutput += newDataObj!.result;
|
||||
logger.channel()?.info(newDataObj!.result);
|
||||
MessageHandler.sendMessage(this._panel!, { command: 'receiveMessagePartial', text: commandOutput, hash:"", user:"", isError: false });
|
||||
}
|
||||
} catch (e) {
|
||||
if (newData.length > 0){
|
||||
commandOutput += newData;
|
||||
logger.channel()?.info(newData);
|
||||
MessageHandler.sendMessage(this._panel!, { command: 'receiveMessagePartial', text: commandOutput, hash:"", user:"", isError: false });
|
||||
}
|
||||
}
|
||||
}, (data) => {
|
||||
// handle command stderr
|
||||
logger.channel()?.error(data);
|
||||
logger.channel()?.show();
|
||||
}, undefined, undefined);
|
||||
|
||||
|
||||
return [result, commandAnswer];
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
logger.channel()?.error(`error: ${error.message}`);
|
||||
} else {
|
||||
logger.channel()?.error(`An unknown error occurred: ${error}`);
|
||||
}
|
||||
logger.channel()?.show();
|
||||
}
|
||||
return [undefined, ""];
|
||||
}
|
||||
|
||||
public stop(): void {
|
||||
this._stop = true;
|
||||
if (this._commandRunner) {
|
||||
this._commandRunner.stop();
|
||||
this._commandRunner = null;
|
||||
}
|
||||
}
|
||||
|
||||
public input(data): void {
|
||||
const userInputWithFlag = `\n<<Start>>\n${data}\n<<End>>\n`;
|
||||
this._commandRunner?.write(userInputWithFlag);
|
||||
}
|
||||
|
||||
public async run(workflow: string, commandDefines: any, message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
this._panel = panel;
|
||||
|
||||
// all workflow run in decchat-command virtual env
|
||||
const pythonVirtualEnv: string | undefined = vscode.workspace.getConfiguration('DevChat').get('PythonForCommands');
|
||||
if (!pythonVirtualEnv) {
|
||||
MessageHandler.sendMessage(panel, { command: 'receiveMessage', text: "Python for Commands is not ready, please see OUTPUT for more detail.", hash: "", user: "", date: 0, isError: true });
|
||||
return ;
|
||||
}
|
||||
|
||||
// devchat-core packages are installed in extensionPath/tools/site-packages
|
||||
const extensionPath = UiUtilWrapper.extensionPath();
|
||||
|
||||
// api_key and api_base
|
||||
const [apiKey, aipBase] = await this._getApiKeyAndApiBase();
|
||||
if (!apiKey) {
|
||||
MessageHandler.sendMessage(panel, { command: 'receiveMessage', text: "The OpenAI key is invalid!", hash: "", user: "", date: 0, isError: true });
|
||||
return ;
|
||||
}
|
||||
|
||||
// envs for Command
|
||||
const workflowEnvs = {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"PYTHONUTF8":1,
|
||||
"DEVCHATPYTHON": UiUtilWrapper.getConfiguration("DevChat", "PythonForChat") || "python3",
|
||||
"PYTHONLIBPATH": `${extensionPath}/tools/site-packages`,
|
||||
"PARENT_HASH": message.parent_hash,
|
||||
...process.env,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
OPENAI_API_KEY: apiKey,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
...(aipBase ? { 'OPENAI_API_BASE': aipBase } : {})
|
||||
};
|
||||
|
||||
const requireInput = commandDefines.input === "required";
|
||||
if (requireInput && message.text.replace("/" + workflow, "").trim() === "") {
|
||||
MessageHandler.sendMessage(panel, { command: 'receiveMessage', text: `The workflow ${workflow} need input!`, hash: "", user: "", date: 0, isError: true });
|
||||
return ;
|
||||
}
|
||||
|
||||
const workflowCommand = commandDefines.steps[0].run.replace(
|
||||
'$command_python', `${pythonVirtualEnv}`).replace(
|
||||
'$input', `${message.text.replace("/" + workflow, "").trim()}`);
|
||||
|
||||
const [commandResult, commandAnswer] = await this._runCommand(workflowCommand, workflowEnvs);
|
||||
|
||||
if (commandResult && commandResult.exitCode === 0) {
|
||||
const lastOutput = this._parseLastCommandOutput(commandResult.stdout);
|
||||
let resultOut = commandAnswer === "" ? "success" : commandAnswer;
|
||||
|
||||
try {
|
||||
const lastOutputObj = JSON.parse(lastOutput);
|
||||
if (lastOutputObj && lastOutputObj.result) {
|
||||
resultOut = lastOutputObj.result;
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
let logHash = await insertDevChatLog(message, message.text, resultOut);
|
||||
if (!logHash) {
|
||||
logHash = "";
|
||||
logger.channel()?.error(`Failed to insert devchat log.`);
|
||||
logger.channel()?.show();
|
||||
}
|
||||
|
||||
MessageHandler.sendMessage(panel, { command: 'receiveMessage', text: resultOut, hash:logHash, user:"", date:0, isError: false });
|
||||
|
||||
const dateStr = Math.floor(Date.now()/1000).toString();
|
||||
await handleTopic(
|
||||
message.parent_hash,
|
||||
{"text": message.text},
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
{ response: resultOut, "prompt-hash": logHash, user: "", "date": dateStr, finish_reason: "", isError: false });
|
||||
} else if (commandResult) {
|
||||
logger.channel()?.info(`${commandResult.stdout}`);
|
||||
if (this._stop === false) {
|
||||
MessageHandler.sendMessage(panel, { command: 'receiveMessage', text: commandResult.stderr, hash: "", user: "", date: 0, isError: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
120
src/ide_services/services.ts
Normal file
120
src/ide_services/services.ts
Normal file
@ -0,0 +1,120 @@
|
||||
import * as http from 'http';
|
||||
import * as url from 'url';
|
||||
import * as querystring from 'querystring';
|
||||
import { logger } from '../util/logger';
|
||||
import { UiUtilWrapper } from '../util/uiUtil';
|
||||
|
||||
|
||||
const functionRegistry: any = {
|
||||
"/hellox": {
|
||||
"keys": [],
|
||||
"handler": async () => {
|
||||
return "111222";
|
||||
}
|
||||
},
|
||||
"/hellox2": {
|
||||
"keys": ["a", "b"],
|
||||
"handler": async (a: string, b: string) => {
|
||||
return a+b;
|
||||
}
|
||||
},
|
||||
"/hellox3": {
|
||||
"keys": [],
|
||||
"handler": async () => {
|
||||
return {
|
||||
"name": "v1",
|
||||
"age": 20,
|
||||
"others": {
|
||||
"address": "sh",
|
||||
"phone": "123456789"
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
"/get_lsp_brige_port": {
|
||||
"keys": [],
|
||||
"handler": async () => {
|
||||
return await UiUtilWrapper.getLSPBrigePort();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
let server: http.Server | null = null;
|
||||
export async function startRpcServer() {
|
||||
server = http.createServer((req, res) => {
|
||||
const parsedUrl = new URL(req.url!, `http://${req.headers.host}`);
|
||||
if (parsedUrl.pathname === '/favicon.ico') {
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
let params: any = {};
|
||||
|
||||
if (req.method === 'POST') {
|
||||
let body = '';
|
||||
req.on('data', chunk => {
|
||||
body += chunk.toString(); // 将Buffer转换为string
|
||||
});
|
||||
|
||||
req.on('end', () => {
|
||||
// 根据不同Content-Type,进行不同方式的解析
|
||||
if (req.headers['content-type'] === 'application/json') {
|
||||
// 解析JSON格式的数据
|
||||
params = JSON.parse(body);
|
||||
|
||||
// 处理postParams
|
||||
} else if (req.headers['content-type'] === 'application/x-www-form-urlencoded') {
|
||||
// 解析URL编码的数据
|
||||
params = querystring.parse(body);
|
||||
// 处理postParams
|
||||
}
|
||||
|
||||
handleRequest(parsedUrl, params, res);
|
||||
});
|
||||
} else if (req.method === 'GET') {
|
||||
const queryParams = parsedUrl.searchParams;
|
||||
for (let param of queryParams) {
|
||||
params[param[0]] = param[1];
|
||||
}
|
||||
|
||||
handleRequest(parsedUrl, params, res);
|
||||
}
|
||||
});
|
||||
|
||||
async function handleRequest(parsedUrl: URL, params: any, res: http.ServerResponse) {
|
||||
let responseResult = {};
|
||||
|
||||
if (functionRegistry[parsedUrl.pathname]) {
|
||||
let keysExist = true;
|
||||
let newParameters: any[] = [];
|
||||
for (let key of functionRegistry[parsedUrl.pathname]['keys']) {
|
||||
if (!params.hasOwnProperty(key)) {
|
||||
keysExist = false;
|
||||
break;
|
||||
}
|
||||
newParameters.push(params[key]);
|
||||
}
|
||||
if (!keysExist) {
|
||||
responseResult['error'] = "Missing required parameters";
|
||||
} else {
|
||||
responseResult['result'] = await functionRegistry[parsedUrl.pathname]['handler'](...newParameters);
|
||||
}
|
||||
} else {
|
||||
responseResult['error'] = "Function not found";
|
||||
}
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify(responseResult));
|
||||
}
|
||||
|
||||
server.listen(3000, () => {
|
||||
const address = server!.address();
|
||||
// `address()`返回的对象包含`port`属性,它是系统分配的端口号
|
||||
const port = typeof address === 'string' ? address : address?.port;
|
||||
logger.channel()?.info(`Server running at http://localhost:${port}/`);
|
||||
process.env.DEVCHAT_IDE_SERVICE_URL = `http://localhost:${port}/`;
|
||||
});
|
||||
}
|
@ -2,12 +2,10 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import '../handler/loadHandlers';
|
||||
import '../handler/handlerRegister';
|
||||
import handleMessage from '../handler/messageHandler';
|
||||
import WebviewManager from './webviewManager';
|
||||
|
||||
import CustomCommands from '../command/customCommand';
|
||||
import CommandManager from '../command/commandManager';
|
||||
import { createChatDirectoryAndCopyInstructionsSync } from '../init/chatConfig';
|
||||
import { UiUtilWrapper } from '../util/uiUtil';
|
||||
|
||||
@ -24,7 +22,6 @@ export default class ChatPanel {
|
||||
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
if (workspaceDir) {
|
||||
const workflowsDir = path.join(workspaceDir!, '.chat', 'workflows');
|
||||
CustomCommands.getInstance().parseCommands(workflowsDir);
|
||||
}
|
||||
|
||||
if (ChatPanel._instance) {
|
||||
|
@ -2,17 +2,14 @@ import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import WebviewManager from './webviewManager';
|
||||
|
||||
import '../handler/loadHandlers';
|
||||
import '../handler/handlerRegister';
|
||||
import handleMessage from '../handler/messageHandler';
|
||||
import { createChatDirectoryAndCopyInstructionsSync } from '../init/chatConfig';
|
||||
import ExtensionContextHolder from '../util/extensionContext';
|
||||
import CustomCommands from '../command/customCommand';
|
||||
import { ExtensionContextHolder } from '../util/extensionContext';
|
||||
import { TopicManager } from '../topic/topicManager';
|
||||
import { UiUtilWrapper } from '../util/uiUtil';
|
||||
import ChatContextManager from '../context/contextManager';
|
||||
import ActionManager from '../action/actionManager';
|
||||
import { getWaitCreateTopic } from '../handler/sendMessageBase';
|
||||
import { CustomActions } from '../action/customAction';
|
||||
import { ChatContextManager } from '../context/contextManager';
|
||||
import { TopicUpdateHandler } from '../handler/sendMessageBase';
|
||||
|
||||
|
||||
export class DevChatViewProvider implements vscode.WebviewViewProvider {
|
||||
@ -33,10 +30,6 @@ export class DevChatViewProvider implements vscode.WebviewViewProvider {
|
||||
if (workspaceDir) {
|
||||
const workflowsDir = path.join(workspaceDir!, '.chat', 'workflows');
|
||||
ChatContextManager.getInstance().loadCustomContexts(workflowsDir);
|
||||
ActionManager.getInstance().loadCustomActions(workflowsDir);
|
||||
|
||||
const actionInstrucFile = path.join(UiUtilWrapper.workspaceFoldersFirstPath()!, './.chat/functions.json');
|
||||
ActionManager.getInstance().saveActionInstructionFile( actionInstrucFile);
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,7 +66,7 @@ export class DevChatViewProvider implements vscode.WebviewViewProvider {
|
||||
);
|
||||
|
||||
TopicManager.getInstance().addOnCurrentTopicIdChangeListener((topicId) => {
|
||||
if (topicId && getWaitCreateTopic()) {
|
||||
if (topicId && TopicUpdateHandler.isTopicChangeTriggeredByWebview()) {
|
||||
return;
|
||||
}
|
||||
this.reloadWebview();
|
||||
|
@ -1,9 +1,8 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { dependencyCheck } from './statusBarViewBase';
|
||||
import { isIndexingStopped, isNeedIndexingCode } from '../util/askCodeUtil';
|
||||
import { ProgressBar } from '../util/progressBar';
|
||||
import ExtensionContextHolder from '../util/extensionContext';
|
||||
import { ExtensionContextHolder } from '../util/extensionContext';
|
||||
|
||||
|
||||
export function createStatusBarItem(context: vscode.ExtensionContext): vscode.StatusBarItem {
|
||||
|
@ -1,14 +1,12 @@
|
||||
// devchat.ts
|
||||
import * as dotenv from 'dotenv';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { logger } from '../util/logger';
|
||||
import { CommandRun, saveModelSettings } from "../util/commonUtil";
|
||||
import ExtensionContextHolder from '../util/extensionContext';
|
||||
import { UiUtilWrapper } from '../util/uiUtil';
|
||||
import { ApiKeyManager } from '../util/apiKey';
|
||||
import { exitCode } from 'process';
|
||||
import { assertValue } from '../util/check';
|
||||
import { getFileContent } from '../handler/diffHandler';
|
||||
|
||||
|
||||
const envPath = path.join(__dirname, '..', '.env');
|
||||
@ -18,9 +16,6 @@ export interface ChatOptions {
|
||||
parent?: string;
|
||||
reference?: string[];
|
||||
header?: string[];
|
||||
functions?: string;
|
||||
role?: string;
|
||||
function_name?: string;
|
||||
context?: string[];
|
||||
}
|
||||
|
||||
@ -48,29 +43,22 @@ export interface CommandEntry {
|
||||
description: string;
|
||||
}
|
||||
|
||||
// define TopicEntry interface
|
||||
/*
|
||||
[
|
||||
{
|
||||
root_prompt: LogEntry,
|
||||
latest_time: 1689849274,
|
||||
hidden: false,
|
||||
title: null
|
||||
}
|
||||
]
|
||||
*/
|
||||
export interface TopicEntry {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
root_prompt: LogEntry;
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
latest_time: number;
|
||||
hidden: boolean;
|
||||
title: string | null;
|
||||
}
|
||||
|
||||
export interface ChatResponse {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"prompt-hash": string;
|
||||
user: string;
|
||||
date: string;
|
||||
response: string;
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
finish_reason: string;
|
||||
isError: boolean;
|
||||
}
|
||||
@ -83,12 +71,25 @@ class DevChat {
|
||||
this.commandRun = new CommandRun();
|
||||
}
|
||||
|
||||
public stop() {
|
||||
this.commandRun.stop();
|
||||
private async loadContextsFromFiles(contexts: string[] | undefined): Promise<string[]> {
|
||||
if (!contexts) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const loadedContexts: string[] = [];
|
||||
for (const context of contexts) {
|
||||
const contextContent = await getFileContent(context);
|
||||
if (!contextContent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
loadedContexts.push(contextContent);
|
||||
}
|
||||
return loadedContexts;
|
||||
}
|
||||
|
||||
async buildArgs(options: ChatOptions): Promise<string[]> {
|
||||
let args = ["prompt"];
|
||||
private async buildArgs(options: ChatOptions): Promise<string[]> {
|
||||
let args = ["-m", "devchat", "prompt"];
|
||||
|
||||
if (options.reference) {
|
||||
for (const reference of options.reference) {
|
||||
@ -106,481 +107,22 @@ class DevChat {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fix openai function calling
|
||||
// const isEnableFunctionCalling = UiUtilWrapper.getConfiguration('DevChat', 'EnableFunctionCalling');
|
||||
// if (options.functions && isEnableFunctionCalling) {
|
||||
// args.push("-f", options.functions);
|
||||
// }
|
||||
|
||||
if (options.function_name) {
|
||||
args.push("-n", options.function_name);
|
||||
}
|
||||
|
||||
if (options.parent) {
|
||||
args.push("-p", options.parent);
|
||||
}
|
||||
|
||||
const llmModelData = await ApiKeyManager.llmModel();
|
||||
if (llmModelData && llmModelData.model) {
|
||||
args.push("-m", llmModelData.model);
|
||||
}
|
||||
assertValue(!llmModelData || !llmModelData.model, 'You must select a LLM model to use for conversations');
|
||||
args.push("-m", llmModelData.model);
|
||||
|
||||
args.push("-ns");
|
||||
args.push("-a");
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
private parseOutData(stdout: string, isPartial: boolean): ChatResponse {
|
||||
const responseLines = stdout.trim().split("\n");
|
||||
|
||||
if (responseLines.length < 2) {
|
||||
return {
|
||||
"prompt-hash": "",
|
||||
user: "",
|
||||
date: "",
|
||||
response: "",
|
||||
finish_reason: "",
|
||||
isError: isPartial ? false : true,
|
||||
};
|
||||
}
|
||||
|
||||
const userLine = responseLines.shift()!;
|
||||
const user = (userLine.match(/User: (.+)/)?.[1]) ?? "";
|
||||
|
||||
const dateLine = responseLines.shift()!;
|
||||
const date = (dateLine.match(/Date: (.+)/)?.[1]) ?? "";
|
||||
|
||||
|
||||
let promptHashLine = "";
|
||||
for (let i = responseLines.length - 1; i >= 0; i--) {
|
||||
if (responseLines[i].startsWith("prompt")) {
|
||||
promptHashLine = responseLines[i];
|
||||
responseLines.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let finishReasonLine = "";
|
||||
for (let i = responseLines.length - 1; i >= 0; i--) {
|
||||
if (responseLines[i].startsWith("finish_reason:")) {
|
||||
finishReasonLine = responseLines[i];
|
||||
responseLines.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!promptHashLine) {
|
||||
return {
|
||||
"prompt-hash": "",
|
||||
user: user,
|
||||
date: date,
|
||||
response: responseLines.join("\n"),
|
||||
finish_reason: "",
|
||||
isError: isPartial ? false : true,
|
||||
};
|
||||
}
|
||||
|
||||
const finishReason = finishReasonLine.split(" ")[1];
|
||||
const promptHash = promptHashLine.split(" ")[1];
|
||||
const response = responseLines.join("\n");
|
||||
|
||||
return {
|
||||
"prompt-hash": promptHash,
|
||||
user,
|
||||
date,
|
||||
response,
|
||||
finish_reason: finishReason,
|
||||
isError: false,
|
||||
};
|
||||
}
|
||||
|
||||
async chat(content: string, options: ChatOptions = {}, onData: (data: ChatResponse) => void): Promise<ChatResponse> {
|
||||
const llmModelData = await ApiKeyManager.llmModel();
|
||||
if (!llmModelData) {
|
||||
return {
|
||||
"prompt-hash": "",
|
||||
user: "",
|
||||
date: "",
|
||||
response: `Error: no valid llm model is selected!`,
|
||||
finish_reason: "",
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
const args = await this.buildArgs(options);
|
||||
args.push("--");
|
||||
args.push(content);
|
||||
|
||||
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
let openaiApiKey = await ApiKeyManager.getApiKey();
|
||||
if (!openaiApiKey) {
|
||||
logger.channel()?.error('The OpenAI key is invalid!');
|
||||
logger.channel()?.show();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const openAiApiBaseObject = llmModelData.api_base? { OPENAI_API_BASE: llmModelData.api_base } : {};
|
||||
const activeLlmModelKey = llmModelData.api_key;
|
||||
|
||||
let devChat: string | undefined = UiUtilWrapper.getConfiguration('DevChat', 'DevChatPath');
|
||||
if (!devChat) {
|
||||
devChat = 'devchat';
|
||||
}
|
||||
|
||||
await saveModelSettings();
|
||||
|
||||
try {
|
||||
|
||||
let receviedStdout = "";
|
||||
const onStdoutPartial = (stdout: string) => {
|
||||
receviedStdout += stdout;
|
||||
const data = this.parseOutData(receviedStdout, true);
|
||||
onData(data);
|
||||
};
|
||||
|
||||
const spawnAsyncOptions = {
|
||||
maxBuffer: 10 * 1024 * 1024, // Set maxBuffer to 10 MB
|
||||
cwd: workspaceDir,
|
||||
env: {
|
||||
PYTHONUTF8:1,
|
||||
PYTHONPATH: UiUtilWrapper.extensionPath() + "/tools/site-packages",
|
||||
...process.env,
|
||||
OPENAI_API_KEY: activeLlmModelKey,
|
||||
...openAiApiBaseObject
|
||||
},
|
||||
};
|
||||
|
||||
// activeLlmModelKey is an api key, I will output it to the log
|
||||
// so, replace mid-sub string with *
|
||||
// for example: sk-1234567890 -> sk-1*****7890, keep first 4 char and last 4 char visible
|
||||
const newActiveLlmModelKey = activeLlmModelKey.replace(/^(.{4})(.*)(.{4})$/, (_, first, middle, last) => first + middle.replace(/./g, '*') + last);
|
||||
const keyInfo = {
|
||||
OPENAI_API_KEY: newActiveLlmModelKey ,
|
||||
...openAiApiBaseObject
|
||||
};
|
||||
const pythonApp = UiUtilWrapper.getConfiguration("DevChat", "PythonForChat") || "python3";
|
||||
|
||||
logger.channel()?.info(`Running devchat with arguments: ${args.join(" ")}`);
|
||||
logger.channel()?.info(`Running devchat with environment: ${JSON.stringify(keyInfo)}`);
|
||||
const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(pythonApp, ["-m", "devchat"].concat(args), spawnAsyncOptions, onStdoutPartial, undefined, undefined, undefined);
|
||||
|
||||
if (stderr) {
|
||||
let newStderr = stderr;
|
||||
if (stderr.indexOf('Failed to verify access key') > 0) {
|
||||
newStderr += `\nPlease check your key data: ${JSON.stringify(keyInfo)}`;
|
||||
}
|
||||
return {
|
||||
"prompt-hash": "",
|
||||
user: "",
|
||||
date: "",
|
||||
response: newStderr,
|
||||
finish_reason: "",
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
const response = this.parseOutData(stdout, false);
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
return {
|
||||
"prompt-hash": "",
|
||||
user: "",
|
||||
date: "",
|
||||
response: `Error: ${error.stderr}\nExit code: ${error.code}`,
|
||||
finish_reason: "",
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async logInsert(request: string, response: string, parent: string | undefined) {
|
||||
let log_data = {
|
||||
"model": "gpt-4",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": request
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": response
|
||||
}
|
||||
],
|
||||
"timestamp": Math.floor(Date.now()/1000),
|
||||
"request_tokens": 1,
|
||||
"response_tokens": 1
|
||||
};
|
||||
if (parent) {
|
||||
log_data["parent"] = parent;
|
||||
}
|
||||
|
||||
|
||||
const args = ["log", "--insert", JSON.stringify(log_data)];
|
||||
const devChat = this.getDevChatPath();
|
||||
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
const openaiApiKey = process.env.OPENAI_API_KEY;
|
||||
|
||||
logger.channel()?.info(`Running devchat with arguments: ${args.join(" ")}`);
|
||||
const spawnOptions = {
|
||||
maxBuffer: 10 * 1024 * 1024, // Set maxBuffer to 10 MB
|
||||
cwd: workspaceDir,
|
||||
env: {
|
||||
PYTHONUTF8:1,
|
||||
PYTHONPATH: UiUtilWrapper.extensionPath() + "/tools/site-packages",
|
||||
...process.env,
|
||||
OPENAI_API_KEY: openaiApiKey,
|
||||
},
|
||||
};
|
||||
const pythonApp = UiUtilWrapper.getConfiguration("DevChat", "PythonForChat") || "python3";
|
||||
const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(pythonApp, ["-m", "devchat"].concat(args), spawnOptions, undefined, undefined, undefined, undefined);
|
||||
|
||||
logger.channel()?.info(`Finish devchat with arguments: ${args.join(" ")}`);
|
||||
if (stderr) {
|
||||
logger.channel()?.error(`Error: ${stderr}`);
|
||||
logger.channel()?.show();
|
||||
return false;
|
||||
}
|
||||
if (stdout.indexOf('Failed to insert log') >= 0) {
|
||||
logger.channel()?.error(`Failed to insert log: ${log_data}`);
|
||||
logger.channel()?.show();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (code !== 0) {
|
||||
logger.channel()?.error(`Exit code: ${code}`);
|
||||
logger.channel()?.show();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async delete(hash: string): Promise<boolean> {
|
||||
const args = ["log", "--delete", hash];
|
||||
const devChat = this.getDevChatPath();
|
||||
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
const openaiApiKey = process.env.OPENAI_API_KEY;
|
||||
|
||||
logger.channel()?.info(`Running devchat with arguments: ${args.join(" ")}`);
|
||||
const spawnOptions = {
|
||||
maxBuffer: 10 * 1024 * 1024, // Set maxBuffer to 10 MB
|
||||
cwd: workspaceDir,
|
||||
env: {
|
||||
PYTHONUTF8:1,
|
||||
PYTHONPATH: UiUtilWrapper.extensionPath() + "/tools/site-packages",
|
||||
...process.env,
|
||||
OPENAI_API_KEY: openaiApiKey,
|
||||
},
|
||||
};
|
||||
const pythonApp = UiUtilWrapper.getConfiguration("DevChat", "PythonForChat") || "python3";
|
||||
const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(pythonApp, ["-m", "devchat"].concat(args), spawnOptions, undefined, undefined, undefined, undefined);
|
||||
|
||||
logger.channel()?.info(`Finish devchat with arguments: ${args.join(" ")}`);
|
||||
if (stderr) {
|
||||
logger.channel()?.error(`Error: ${stderr}`);
|
||||
logger.channel()?.show();
|
||||
return false;
|
||||
}
|
||||
if (stdout.indexOf('Failed to delete prompt') >= 0) {
|
||||
logger.channel()?.error(`Failed to delete prompt: ${hash}`);
|
||||
logger.channel()?.show();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (code !== 0) {
|
||||
logger.channel()?.error(`Exit code: ${code}`);
|
||||
logger.channel()?.show();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async log(options: LogOptions = {}): Promise<LogEntry[]> {
|
||||
const args = this.buildLogArgs(options);
|
||||
const devChat = this.getDevChatPath();
|
||||
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
const openaiApiKey = process.env.OPENAI_API_KEY;
|
||||
|
||||
logger.channel()?.info(`Running devchat with arguments: ${args.join(" ")}`);
|
||||
const spawnOptions = {
|
||||
maxBuffer: 10 * 1024 * 1024, // Set maxBuffer to 10 MB
|
||||
cwd: workspaceDir,
|
||||
env: {
|
||||
PYTHONUTF8:1,
|
||||
PYTHONPATH: UiUtilWrapper.extensionPath() + "/tools/site-packages",
|
||||
...process.env,
|
||||
OPENAI_API_KEY: openaiApiKey,
|
||||
},
|
||||
};
|
||||
const pythonApp = UiUtilWrapper.getConfiguration("DevChat", "PythonForChat") || "python3";
|
||||
const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(pythonApp, ["-m", "devchat"].concat(args), spawnOptions, undefined, undefined, undefined, undefined);
|
||||
|
||||
logger.channel()?.info(`Finish devchat with arguments: ${args.join(" ")}`);
|
||||
if (stderr) {
|
||||
logger.channel()?.error(`Error: ${stderr}`);
|
||||
logger.channel()?.show();
|
||||
return [];
|
||||
}
|
||||
|
||||
const logs = JSON.parse(stdout.trim()).reverse();
|
||||
for (const log of logs) {
|
||||
log.response = log.responses[0];
|
||||
delete log.responses;
|
||||
}
|
||||
return logs;
|
||||
}
|
||||
|
||||
// command devchat run --list
|
||||
// output:
|
||||
// [
|
||||
// {
|
||||
// "name": "code",
|
||||
// "description": "Generate code with a general template embedded into the prompt."
|
||||
// },
|
||||
// {
|
||||
// "name": "code.py",
|
||||
// "description": "Generate code with a Python-specific template embedded into the prompt."
|
||||
// },
|
||||
// {
|
||||
// "name": "commit_message",
|
||||
// "description": "Generate a commit message for the given git diff."
|
||||
// },
|
||||
// {
|
||||
// "name": "release_note",
|
||||
// "description": "Generate a release note for the given commit log."
|
||||
// }
|
||||
// ]
|
||||
async commands(): Promise<CommandEntry[]> {
|
||||
const args = ["run", "--list"];
|
||||
const devChat = this.getDevChatPath();
|
||||
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
|
||||
logger.channel()?.info(`Running devchat with arguments: ${args.join(" ")}`);
|
||||
const spawnOptions = {
|
||||
maxBuffer: 10 * 1024 * 1024, // Set maxBuffer to 10 MB
|
||||
cwd: workspaceDir,
|
||||
env: {
|
||||
PYTHONUTF8:1,
|
||||
PYTHONPATH: UiUtilWrapper.extensionPath() + "/tools/site-packages",
|
||||
...process.env,
|
||||
},
|
||||
};
|
||||
|
||||
const pythonApp = UiUtilWrapper.getConfiguration("DevChat", "PythonForChat") || "python3";
|
||||
const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(pythonApp, ["-m", "devchat"].concat(args), spawnOptions, undefined, undefined, undefined, undefined);
|
||||
logger.channel()?.info(`Finish devchat with arguments: ${args.join(" ")}`);
|
||||
if (stderr) {
|
||||
logger.channel()?.error(`Error: ${stderr}`);
|
||||
logger.channel()?.show();
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const commands = JSON.parse(stdout.trim());
|
||||
return commands;
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`Error parsing JSON: ${error}`);
|
||||
logger.channel()?.show();
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async commandPrompt(command: string): Promise<string> {
|
||||
const args = ["run", command];
|
||||
const devChat = this.getDevChatPath();
|
||||
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
|
||||
logger.channel()?.info(`Running devchat with arguments: ${args.join(" ")}`);
|
||||
const spawnOptions = {
|
||||
maxBuffer: 10 * 1024 * 1024, // Set maxBuffer to 10 MB
|
||||
cwd: workspaceDir,
|
||||
env: {
|
||||
PYTHONUTF8:1,
|
||||
PYTHONPATH: UiUtilWrapper.extensionPath() + "/tools/site-packages",
|
||||
...process.env,
|
||||
},
|
||||
};
|
||||
|
||||
const pythonApp = UiUtilWrapper.getConfiguration("DevChat", "PythonForChat") || "python3";
|
||||
const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(pythonApp, ["-m", "devchat"].concat(args), spawnOptions, undefined, undefined, undefined, undefined);
|
||||
logger.channel()?.info(`Finish devchat with arguments: ${args.join(" ")}`);
|
||||
if (stderr) {
|
||||
logger.channel()?.error(`Error: ${stderr}`);
|
||||
logger.channel()?.show();
|
||||
}
|
||||
return stdout;
|
||||
}
|
||||
|
||||
async updateSysCommand(): Promise<string> {
|
||||
const args = ["run", "--update-sys"];
|
||||
const devChat = this.getDevChatPath();
|
||||
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
|
||||
logger.channel()?.info(`Running devchat with arguments: ${args.join(" ")}`);
|
||||
const spawnOptions = {
|
||||
maxBuffer: 10 * 1024 * 1024, // Set maxBuffer to 10 MB
|
||||
cwd: workspaceDir,
|
||||
env: {
|
||||
PYTHONUTF8:1,
|
||||
PYTHONPATH: UiUtilWrapper.extensionPath() + "/tools/site-packages",
|
||||
...process.env,
|
||||
},
|
||||
};
|
||||
|
||||
const pythonApp = UiUtilWrapper.getConfiguration("DevChat", "PythonForChat") || "python3";
|
||||
const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(pythonApp, ["-m", "devchat"].concat(args), spawnOptions, undefined, undefined, undefined, undefined);
|
||||
logger.channel()?.info(`Finish devchat with arguments: ${args.join(" ")}`);
|
||||
if (stderr) {
|
||||
logger.channel()?.error(`Error: ${stderr}`);
|
||||
logger.channel()?.show();
|
||||
}
|
||||
logger.channel()?.info(`${stdout}`);
|
||||
return stdout;
|
||||
}
|
||||
|
||||
async topics(): Promise<TopicEntry[]> {
|
||||
const args = ["topic", "-l"];
|
||||
const devChat = this.getDevChatPath();
|
||||
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
|
||||
logger.channel()?.info(`Running devchat with arguments: ${args.join(" ")}`);
|
||||
const spawnOptions = {
|
||||
maxBuffer: 10 * 1024 * 1024, // Set maxBuffer to 10 MB
|
||||
cwd: workspaceDir,
|
||||
env: {
|
||||
PYTHONUTF8:1,
|
||||
PYTHONPATH: UiUtilWrapper.extensionPath() + "/tools/site-packages",
|
||||
...process.env,
|
||||
},
|
||||
};
|
||||
|
||||
const pythonApp = UiUtilWrapper.getConfiguration("DevChat", "PythonForChat") || "python3";
|
||||
const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(pythonApp, ["-m", "devchat"].concat(args), spawnOptions, undefined, undefined, undefined, undefined);
|
||||
|
||||
logger.channel()?.info(`Finish devchat with arguments: ${args.join(" ")}`);
|
||||
if (stderr) {
|
||||
logger.channel()?.error(`Error: ${stderr}`);
|
||||
logger.channel()?.show();
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const topics = JSON.parse(stdout.trim()).reverse();
|
||||
// convert responses to respose, and remove responses field
|
||||
// responses is in TopicEntry.root_prompt.responses
|
||||
for (const topic of topics) {
|
||||
if (topic.root_prompt.responses) {
|
||||
topic.root_prompt.response = topic.root_prompt.responses[0];
|
||||
delete topic.root_prompt.responses;
|
||||
}
|
||||
}
|
||||
return topics;
|
||||
} catch (error) {
|
||||
logger.channel()?.error(`Error parsing JSON: ${error}`);
|
||||
logger.channel()?.show();
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private buildLogArgs(options: LogOptions): string[] {
|
||||
let args = ["log"];
|
||||
let args = ["-m", "devchat", "log"];
|
||||
|
||||
if (options.skip) {
|
||||
args.push('--skip', `${options.skip}`);
|
||||
@ -599,12 +141,326 @@ class DevChat {
|
||||
return args;
|
||||
}
|
||||
|
||||
private getDevChatPath(): string {
|
||||
let devChat: string | undefined = UiUtilWrapper.getConfiguration('DevChat', 'DevChatPath');
|
||||
if (!devChat) {
|
||||
devChat = 'devchat';
|
||||
private parseOutData(stdout: string, isPartial: boolean): ChatResponse {
|
||||
const responseLines = stdout.trim().split("\n");
|
||||
|
||||
if (responseLines.length < 2) {
|
||||
return this.createChatResponse("", "", "", "", !isPartial);
|
||||
}
|
||||
|
||||
const [userLine, remainingLines1] = this.extractLine(responseLines, "User: ");
|
||||
const user = this.parseLine(userLine, /User: (.+)/);
|
||||
|
||||
const [dateLine, remainingLines2] = this.extractLine(remainingLines1, "Date: ");
|
||||
const date = this.parseLine(dateLine, /Date: (.+)/);
|
||||
|
||||
const [promptHashLine, remainingLines3] = this.extractLine(remainingLines2, "prompt");
|
||||
const [finishReasonLine, remainingLines4] = this.extractLine(remainingLines3, "finish_reason:");
|
||||
|
||||
if (!promptHashLine) {
|
||||
return this.createChatResponse("", user, date, remainingLines4.join("\n"), !isPartial);
|
||||
}
|
||||
|
||||
const finishReason = finishReasonLine.split(" ")[1];
|
||||
const promptHash = promptHashLine.split(" ")[1];
|
||||
const response = remainingLines4.join("\n");
|
||||
|
||||
return this.createChatResponse(promptHash, user, date, response, false, finishReason);
|
||||
}
|
||||
|
||||
private extractLine(lines: string[], startWith: string): [string, string[]] {
|
||||
const index = lines.findIndex(line => line.startsWith(startWith));
|
||||
const extractedLine = index !== -1 ? lines.splice(index, 1)[0] : "";
|
||||
return [extractedLine, lines];
|
||||
}
|
||||
|
||||
private parseLine(line: string, regex: RegExp): string {
|
||||
return (line.match(regex)?.[1]) ?? "";
|
||||
}
|
||||
|
||||
private createChatResponse(promptHash: string, user: string, date: string, response: string, isError: boolean, finishReason = ""): ChatResponse {
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"prompt-hash": promptHash,
|
||||
user,
|
||||
date,
|
||||
response,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
finish_reason: finishReason,
|
||||
isError,
|
||||
};
|
||||
}
|
||||
|
||||
private async runCommand(args: string[]): Promise<{code: number | null, stdout: string, stderr: string}> {
|
||||
// build env variables for command
|
||||
const envs = {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"PYTHONUTF8":1,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"PYTHONPATH": UiUtilWrapper.extensionPath() + "/tools/site-packages",
|
||||
...process.env
|
||||
};
|
||||
|
||||
const pythonApp = UiUtilWrapper.getConfiguration("DevChat", "PythonForChat") || "python3";
|
||||
logger.channel()?.info(`Running command:${pythonApp} ${args.join(" ")}`);
|
||||
|
||||
// run command
|
||||
const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(
|
||||
pythonApp,
|
||||
args,
|
||||
{
|
||||
maxBuffer: 10 * 1024 * 1024, // Set maxBuffer to 10 MB
|
||||
cwd: UiUtilWrapper.workspaceFoldersFirstPath(),
|
||||
env: envs
|
||||
},
|
||||
undefined, undefined, undefined, undefined
|
||||
);
|
||||
|
||||
return {code, stdout, stderr};
|
||||
}
|
||||
|
||||
public input(data: string) {
|
||||
this.commandRun?.write(data + "\n");
|
||||
}
|
||||
|
||||
public stop() {
|
||||
this.commandRun.stop();
|
||||
}
|
||||
|
||||
async chat(content: string, options: ChatOptions = {}, onData: (data: ChatResponse) => void): Promise<ChatResponse> {
|
||||
try {
|
||||
// build args for devchat prompt command
|
||||
const args = await this.buildArgs(options);
|
||||
args.push("--");
|
||||
args.push(content);
|
||||
|
||||
// build env variables for prompt command
|
||||
const llmModelData = await ApiKeyManager.llmModel();
|
||||
assertValue(!llmModelData, "No valid llm model selected");
|
||||
const envs = {
|
||||
...process.env,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"PYTHONUTF8": 1,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"command_python": UiUtilWrapper.getConfiguration('DevChat', 'PythonForCommands') || "python3",
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"DEVCHATPYTHON": UiUtilWrapper.getConfiguration("DevChat", "PythonForChat") || "python3",
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"PYTHONLIBPATH": UiUtilWrapper.extensionPath() + "/tools/site-packages",
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"PYTHONPATH": UiUtilWrapper.extensionPath() + "/tools/site-packages",
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"OPENAI_API_KEY": llmModelData.api_key,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
...llmModelData.api_base? { "OPENAI_API_BASE": llmModelData.api_base } : {}
|
||||
};
|
||||
|
||||
// build process options
|
||||
const spawnAsyncOptions = {
|
||||
maxBuffer: 10 * 1024 * 1024, // Set maxBuffer to 10 MB
|
||||
cwd: UiUtilWrapper.workspaceFoldersFirstPath(),
|
||||
env: envs
|
||||
};
|
||||
|
||||
// save llm model config
|
||||
await saveModelSettings();
|
||||
|
||||
logger.channel()?.info(`api_key: ${llmModelData.api_key.replace(/^(.{4})(.*)(.{4})$/, (_, first, middle, last) => first + middle.replace(/./g, '*') + last)}`);
|
||||
logger.channel()?.info(`api_base: ${llmModelData.api_base}`);
|
||||
|
||||
// run command
|
||||
// handle stdout as steam mode
|
||||
let receviedStdout = "";
|
||||
const onStdoutPartial = (stdout: string) => {
|
||||
receviedStdout += stdout;
|
||||
const data = this.parseOutData(receviedStdout, true);
|
||||
onData(data);
|
||||
};
|
||||
// run command
|
||||
const pythonApp = UiUtilWrapper.getConfiguration("DevChat", "PythonForChat") || "python3";
|
||||
logger.channel()?.info(`Running devchat:${pythonApp} ${args.join(" ")}`);
|
||||
const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(pythonApp, args, spawnAsyncOptions, onStdoutPartial, undefined, undefined, undefined);
|
||||
// handle result
|
||||
assertValue(code !== 0, stderr || "Command exited with error code");
|
||||
const responseData = this.parseOutData(stdout, false);
|
||||
await this.logInsert(options.context, content, responseData.response, options.parent);
|
||||
const logs = await this.log({"maxCount": 1});
|
||||
assertValue(!logs || !logs.length, "Failed to insert devchat log");
|
||||
// return result
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"prompt-hash": logs[0]['hash'],
|
||||
user: "",
|
||||
date: "",
|
||||
response: stdout,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
finish_reason: "",
|
||||
isError: false,
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"prompt-hash": "",
|
||||
user: "",
|
||||
date: "",
|
||||
response: `Error: ${error.message}`,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
finish_reason: "error",
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async logInsert(contexts: string[] | undefined, request: string, response: string, parent: string | undefined): Promise<boolean> {
|
||||
try {
|
||||
// build log data
|
||||
const llmModelData = await ApiKeyManager.llmModel();
|
||||
const contextContentList = await this.loadContextsFromFiles(contexts);
|
||||
const contextWithRoleList = contextContentList.map(content => {
|
||||
return {
|
||||
"role": "system",
|
||||
"content": `<context>${content}</context>`
|
||||
};
|
||||
});
|
||||
|
||||
let logData = {
|
||||
"model": llmModelData?.model || "gpt-3.5-turbo",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": request
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": response
|
||||
},
|
||||
...contextWithRoleList
|
||||
],
|
||||
"timestamp": Math.floor(Date.now()/1000),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"request_tokens": 1,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"response_tokens": 1,
|
||||
...parent? {"parent": parent} : {}
|
||||
};
|
||||
|
||||
// build args for log insert
|
||||
const args = ["-m", "devchat", "log", "--insert", JSON.stringify(logData)];
|
||||
|
||||
const {code, stdout, stderr} = await this.runCommand(args);
|
||||
|
||||
assertValue(code !== 0, stderr || `Command exited with ${code}`);
|
||||
assertValue(stdout.indexOf('Failed to insert log') >= 0, stdout);
|
||||
assertValue(stderr, stderr);
|
||||
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
logger.channel()?.error(`Failed to insert log: ${error.message}`);
|
||||
logger.channel()?.show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async delete(hash: string): Promise<boolean> {
|
||||
try {
|
||||
// build args for log delete
|
||||
const args = ["-m", "devchat", "log", "--delete", hash];
|
||||
|
||||
const {code, stdout, stderr} = await this.runCommand(args);
|
||||
|
||||
assertValue(code !== 0, stderr || `Command exited with ${code}`);
|
||||
assertValue(stdout.indexOf('Failed to delete prompt') >= 0, stdout);
|
||||
assertValue(stderr, stderr);
|
||||
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
logger.channel()?.error(`Failed to delete log: ${error.message}`);
|
||||
logger.channel()?.show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async log(options: LogOptions = {}): Promise<LogEntry[]> {
|
||||
try {
|
||||
const args = this.buildLogArgs(options);
|
||||
|
||||
const {code, stdout, stderr} = await this.runCommand(args);
|
||||
|
||||
assertValue(code !== 0, stderr || `Command exited with ${code}`);
|
||||
assertValue(stderr, stderr);
|
||||
|
||||
const logs = JSON.parse(stdout.trim()).reverse();
|
||||
for (const log of logs) {
|
||||
log.response = log.responses[0];
|
||||
delete log.responses;
|
||||
}
|
||||
return logs;
|
||||
} catch (error: any) {
|
||||
logger.channel()?.error(`Failed to get logs: ${error.message}`);
|
||||
logger.channel()?.show();
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async commands(): Promise<CommandEntry[]> {
|
||||
try {
|
||||
const args = ["-m", "devchat", "run", "--list"];
|
||||
|
||||
const {code, stdout, stderr} = await this.runCommand(args);
|
||||
|
||||
assertValue(code !== 0, stderr || `Command exited with ${code}`);
|
||||
assertValue(stderr, stderr);
|
||||
|
||||
const commands = JSON.parse(stdout.trim());
|
||||
|
||||
return commands;
|
||||
} catch (error: any) {
|
||||
logger.channel()?.error(`Error: ${error.message}`);
|
||||
logger.channel()?.show();
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async updateSysCommand(): Promise<string> {
|
||||
try {
|
||||
const args = ["-m", "devchat", "run", "--update-sys"];
|
||||
|
||||
const {code, stdout, stderr} = await this.runCommand(args);
|
||||
|
||||
assertValue(code !== 0, stderr || `Command exited with ${code}`);
|
||||
assertValue(stderr, stderr);
|
||||
|
||||
logger.channel()?.info(`${stdout}`);
|
||||
return stdout;
|
||||
} catch (error: any) {
|
||||
logger.channel()?.error(`Error: ${error.message}`);
|
||||
logger.channel()?.show();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
async topics(): Promise<TopicEntry[]> {
|
||||
try {
|
||||
const args = ["-m", "devchat", "topic", "-l"];
|
||||
|
||||
const {code, stdout, stderr} = await this.runCommand(args);
|
||||
|
||||
assertValue(code !== 0, stderr || `Command exited with ${code}`);
|
||||
assertValue(stderr, stderr);
|
||||
|
||||
const topics = JSON.parse(stdout.trim()).reverse();
|
||||
for (const topic of topics) {
|
||||
if (topic.root_prompt.responses) {
|
||||
topic.root_prompt.response = topic.root_prompt.responses[0];
|
||||
delete topic.root_prompt.responses;
|
||||
}
|
||||
}
|
||||
return topics;
|
||||
} catch (error: any) {
|
||||
logger.channel()?.error(`Error: ${error.message}`);
|
||||
logger.channel()?.show();
|
||||
return [];
|
||||
}
|
||||
return devChat;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,3 @@
|
||||
import { spawn } from "child_process";
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import { logger } from "../util/logger";
|
||||
import { CommandRun } from "../util/commonUtil";
|
||||
import { UiUtilWrapper } from "../util/uiUtil";
|
||||
@ -27,7 +23,6 @@ class DtmWrapper {
|
||||
const result = await this.commandRun.spawnAsync("git", ['commit', '-m', commitMsg], { cwd: this.workspaceDir }, undefined, undefined, undefined, undefined);
|
||||
return { status: result.exitCode || 0, message: result.stdout, log: result.stderr };
|
||||
} catch (error) {
|
||||
// 处理 runCommand 中的 reject 错误
|
||||
logger.channel()?.error(`Error: ${error}`);
|
||||
logger.channel()?.show();
|
||||
return error as DtmResponse;
|
||||
@ -40,7 +35,6 @@ class DtmWrapper {
|
||||
const result = await this.commandRun.spawnAsync("git", ['commit', '-am', commitMsg], { cwd: this.workspaceDir }, undefined, undefined, undefined, undefined);
|
||||
return { status: result.exitCode || 0, message: result.stdout, log: result.stderr };
|
||||
} catch (error) {
|
||||
// 处理 runCommand 中的 reject 错误
|
||||
logger.channel()?.error(`Error: ${error}`);
|
||||
logger.channel()?.show();
|
||||
return error as DtmResponse;
|
||||
|
@ -124,6 +124,12 @@ export class TopicManager {
|
||||
}
|
||||
}
|
||||
|
||||
deleteMessageOnCurrentTopic(messageHash: string): void {
|
||||
if (this.currentTopicId) {
|
||||
TopicManager.getInstance().deleteMessage(this.currentTopicId, messageHash);
|
||||
}
|
||||
}
|
||||
|
||||
getTopic(topicId: string): Topic | undefined {
|
||||
/**
|
||||
* 获取topic
|
||||
|
6
src/util/check.ts
Normal file
6
src/util/check.ts
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
export function assertValue(value: any, message: string) {
|
||||
if (value) {
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { DevChatViewProvider } from '../panel/devchatView';
|
||||
|
||||
class ExtensionContextHolder {
|
||||
export class ExtensionContextHolder {
|
||||
private static _context: vscode.ExtensionContext | undefined;
|
||||
private static _provider: DevChatViewProvider | undefined;
|
||||
|
||||
@ -21,5 +21,3 @@ class ExtensionContextHolder {
|
||||
return this._provider;
|
||||
}
|
||||
}
|
||||
|
||||
export default ExtensionContextHolder;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import ExtensionContextHolder from './extensionContext';
|
||||
import { ExtensionContextHolder } from './extensionContext';
|
||||
import { UiUtil } from './uiUtil';
|
||||
import { logger } from './logger';
|
||||
|
||||
|
@ -1,180 +0,0 @@
|
||||
import { expect } from 'chai';
|
||||
import 'mocha';
|
||||
import ActionManager from '../../src/action/actionManager';
|
||||
import { Action } from '../../src/action/customAction';
|
||||
|
||||
describe('ActionManager', () => {
|
||||
const testAction: Action = {
|
||||
name: 'testAction',
|
||||
description: 'Test action for unit testing',
|
||||
type: ['test'],
|
||||
action: 'test',
|
||||
handler: [],
|
||||
args: [],
|
||||
handlerAction: async () => ({ exitCode: 0, stdout: '', stderr: '' }),
|
||||
};
|
||||
|
||||
it('should register and retrieve actions', () => {
|
||||
const actionManager = ActionManager.getInstance();
|
||||
actionManager.registerAction(testAction);
|
||||
const actionList = actionManager.getActionList();
|
||||
expect(actionList).to.contain(testAction);
|
||||
});
|
||||
|
||||
it('should return an error for action with empty args', async () => {
|
||||
const actionManager = ActionManager.getInstance();
|
||||
const testActionWithEmptyArgs: Action = {
|
||||
...testAction,
|
||||
name: 'testActionWithEmptyArgs',
|
||||
args: [],
|
||||
};
|
||||
actionManager.registerAction(testActionWithEmptyArgs);
|
||||
const actionName = 'testActionWithEmptyArgs';
|
||||
const content = {
|
||||
command: 'test',
|
||||
content: 'test content',
|
||||
fileName: 'test.txt',
|
||||
};
|
||||
const result = await actionManager.applyAction(actionName, content);
|
||||
expect(result.exitCode).to.equal(-1);
|
||||
expect(result.stdout).to.equal('');
|
||||
expect(result.stderr).to.equal('Action testActionWithEmptyArgs has no args');
|
||||
});
|
||||
it('should apply action with valid args correctly', async () => {
|
||||
const actionManager = ActionManager.getInstance();
|
||||
const testActionWithArgs: Action = {
|
||||
...testAction,
|
||||
name: 'testActionWithArgs',
|
||||
args: [
|
||||
{
|
||||
name: 'arg1',
|
||||
description: 'arg1 description',
|
||||
type: 'string',
|
||||
from: 'content.fileName',
|
||||
},
|
||||
],
|
||||
};
|
||||
actionManager.registerAction(testActionWithArgs);
|
||||
const actionName = 'testActionWithArgs';
|
||||
const content = {
|
||||
command: 'test',
|
||||
content: 'test content',
|
||||
fileName: 'test.txt',
|
||||
};
|
||||
const result = await actionManager.applyAction(actionName, content);
|
||||
expect(result.exitCode).to.equal(0);
|
||||
expect(result.stdout).to.equal('');
|
||||
expect(result.stderr).to.equal('');
|
||||
});
|
||||
|
||||
it('should apply action with content.content.xxx type args correctly', async () => {
|
||||
const actionManager = ActionManager.getInstance();
|
||||
const testActionWithNestedArgs: Action = {
|
||||
...testAction,
|
||||
name: 'testActionWithNestedArgs',
|
||||
args: [
|
||||
{
|
||||
name: 'arg1',
|
||||
description: 'arg1 description',
|
||||
type: 'string',
|
||||
from: 'content.content.field1',
|
||||
},
|
||||
],
|
||||
handlerAction: async (args) => {
|
||||
if (args.arg1 === 'value1') {
|
||||
return { exitCode: 0, stdout: '', stderr: '' };
|
||||
} else {
|
||||
return { exitCode: -1, stdout: '', stderr: 'Incorrect arg1 value' };
|
||||
}
|
||||
},
|
||||
};
|
||||
actionManager.registerAction(testActionWithNestedArgs);
|
||||
const actionName = 'testActionWithNestedArgs';
|
||||
const content = {
|
||||
command: 'test',
|
||||
content: JSON.stringify({ field1: 'value1' }),
|
||||
fileName: 'test.txt',
|
||||
};
|
||||
const result = await actionManager.applyAction(actionName, content);
|
||||
expect(result.exitCode).to.equal(0);
|
||||
expect(result.stdout).to.equal('');
|
||||
expect(result.stderr).to.equal('');
|
||||
});
|
||||
|
||||
it('should return error when content is missing required args', async () => {
|
||||
const actionManager = ActionManager.getInstance();
|
||||
const testActionWithMissingArgs: Action = {
|
||||
...testAction,
|
||||
name: 'testActionWithMissingArgs',
|
||||
args: [
|
||||
{
|
||||
name: 'arg1',
|
||||
description: 'arg1 description',
|
||||
type: 'string',
|
||||
from: 'content.field1',
|
||||
},
|
||||
],
|
||||
handlerAction: async (args) => {
|
||||
return { exitCode: 0, stdout: '', stderr: '' };
|
||||
},
|
||||
};
|
||||
actionManager.registerAction(testActionWithMissingArgs);
|
||||
const actionName = 'testActionWithMissingArgs';
|
||||
const content = {
|
||||
command: 'test',
|
||||
content: JSON.stringify({}),
|
||||
fileName: 'test.txt',
|
||||
};
|
||||
const result = await actionManager.applyAction(actionName, content);
|
||||
expect(result.exitCode).to.equal(-1);
|
||||
expect(result.stdout).to.equal('');
|
||||
expect(result.stderr).to.equal('Action testActionWithMissingArgs arg arg1 from content.field1 is undefined');
|
||||
});
|
||||
|
||||
it('should trigger handlerAction of CommandRunAction', async () => {
|
||||
class TestAction implements Action {
|
||||
name: string;
|
||||
description: string;
|
||||
type: string[];
|
||||
action: string;
|
||||
handler: string[];
|
||||
args: { name: string; description: string; type: string; as?: string | undefined; from: string; }[];
|
||||
|
||||
constructor() {
|
||||
this.name = 'testAction2';
|
||||
this.description = 'Test action for unit testing';
|
||||
this.type = ['test'];
|
||||
this.action = 'test';
|
||||
this.handler = [];
|
||||
this.args = [];
|
||||
}
|
||||
async handlerAction(content: any): Promise<{ exitCode: number; stdout: string; stderr: string }> {
|
||||
return {
|
||||
exitCode: 0,
|
||||
stdout: 'Test action executed successfully',
|
||||
stderr: '',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const actionManager = ActionManager.getInstance();
|
||||
const testAction = new TestAction();
|
||||
actionManager.registerAction(testAction);
|
||||
|
||||
const commandRunActionContent = {
|
||||
command: 'testAction2',
|
||||
args: {},
|
||||
};
|
||||
|
||||
const content = {
|
||||
command: 'command_run',
|
||||
content: JSON.stringify(commandRunActionContent),
|
||||
fileName: 'test.txt',
|
||||
};
|
||||
|
||||
const result = await actionManager.applyAction('command_run', content);
|
||||
expect(result.exitCode).to.equal(0);
|
||||
expect(result.stdout).to.equal('Test action executed successfully');
|
||||
expect(result.stderr).to.equal('');
|
||||
});
|
||||
});
|
@ -1,122 +0,0 @@
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import CommandManager, { Command } from '../../src/command/commandManager';
|
||||
import CustomCommands, { Command as CCommand } from '../../src/command/customCommand';
|
||||
|
||||
describe('CommandManager', () => {
|
||||
let commandManager: CommandManager;
|
||||
|
||||
beforeEach(() => {
|
||||
commandManager = CommandManager.getInstance();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Reset the command list after each test
|
||||
commandManager['commands'] = [];
|
||||
});
|
||||
|
||||
it('should register a command', () => {
|
||||
const command: Command = {
|
||||
name: 'test',
|
||||
pattern: 'test',
|
||||
description: 'Test command',
|
||||
args: 0,
|
||||
handler: async (commandName: string, userInput: string) => {
|
||||
return 'Test result';
|
||||
},
|
||||
};
|
||||
|
||||
commandManager.registerCommand(command);
|
||||
expect(commandManager['commands']).to.include(command);
|
||||
});
|
||||
|
||||
it('should return the command list', () => {
|
||||
const command: Command = {
|
||||
name: 'test',
|
||||
pattern: 'test',
|
||||
description: 'Test command',
|
||||
args: 0,
|
||||
handler: async (commandName: string, userInput: string) => {
|
||||
return 'Test result';
|
||||
},
|
||||
};
|
||||
|
||||
commandManager.registerCommand(command);
|
||||
expect(commandManager.getCommandList()).to.include(command);
|
||||
});
|
||||
|
||||
it('should process text with a command', async () => {
|
||||
const command: Command = {
|
||||
name: 'test',
|
||||
pattern: 'test',
|
||||
description: 'Test command',
|
||||
args: 0,
|
||||
handler: async (commandName: string, userInput: string) => {
|
||||
return 'Test result';
|
||||
},
|
||||
};
|
||||
|
||||
commandManager.registerCommand(command);
|
||||
const result = await commandManager.processText('/test');
|
||||
expect(result).to.equal('Test result');
|
||||
});
|
||||
|
||||
it('should process text with a custom command', async () => {
|
||||
const customCommand: CCommand = {
|
||||
name: 'customTest',
|
||||
pattern: 'customTest',
|
||||
description: 'Custom test command',
|
||||
message: 'Custom test result',
|
||||
args: 0,
|
||||
show: true,
|
||||
default: false,
|
||||
instructions: []
|
||||
};
|
||||
|
||||
CustomCommands.getInstance().regCommand(customCommand);
|
||||
const result = await commandManager.processText('/customTest');
|
||||
expect(result).to.equal(' Custom test result');
|
||||
});
|
||||
|
||||
it('should match /xxx with space or newline, but not with other characters', async () => {
|
||||
const command: Command = {
|
||||
name: 'xxx',
|
||||
pattern: 'xxx',
|
||||
description: 'Test command',
|
||||
args: 0,
|
||||
handler: async (commandName: string, userInput: string) => {
|
||||
return 'Matched';
|
||||
},
|
||||
};
|
||||
|
||||
commandManager.registerCommand(command);
|
||||
|
||||
const result1 = await commandManager.processText('/xxx someother text');
|
||||
expect(result1).to.equal('Matched someother text');
|
||||
|
||||
const result2 = await commandManager.processText('/xxx\n');
|
||||
expect(result2).to.equal('Matched\n');
|
||||
|
||||
const result3 = await commandManager.processText('/xxx-123');
|
||||
expect(result3).to.equal('/xxx-123');
|
||||
|
||||
const result4 = await commandManager.processText('/xxx123');
|
||||
expect(result4).to.equal('/xxx123');
|
||||
});
|
||||
|
||||
it('should process text with a command containing two arguments', async () => {
|
||||
const command: Command = {
|
||||
name: 'test',
|
||||
pattern: 'xxx {{prompt}}',
|
||||
description: 'Test command with two arguments ${1} and ${2}',
|
||||
args: 2,
|
||||
handler: async (commandName: string, userInput: string) => {
|
||||
return `Test result with argument: ["arg1", "arg2"]`;
|
||||
},
|
||||
};
|
||||
|
||||
commandManager.registerCommand(command);
|
||||
const result = await commandManager.processText('/xxx {{["arg1", "arg2"]}}');
|
||||
expect(result).to.equal('Test result with argument: ["arg1", "arg2"]');
|
||||
});
|
||||
});
|
@ -1,132 +0,0 @@
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import mockFs from 'mock-fs';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import CustomCommands, { Command } from '../../src/command/customCommand';
|
||||
|
||||
|
||||
describe('CustomCommands', () => {
|
||||
let customCommands: CustomCommands;
|
||||
|
||||
beforeEach(() => {
|
||||
customCommands = CustomCommands.getInstance();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Reset the command list after each test
|
||||
customCommands['commands'] = [];
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should parse commands from workflows directory', () => {
|
||||
// Mock the file system with two directories, one with _setting_.json and one without
|
||||
mockFs({
|
||||
'workflows': {
|
||||
"some": {
|
||||
"command": {
|
||||
'command1': {
|
||||
'_setting_.json': JSON.stringify({
|
||||
pattern: 'command1',
|
||||
description: 'Command 1',
|
||||
message: 'Command 1 message',
|
||||
default: false,
|
||||
show: true,
|
||||
instructions: ['instruction1', 'instruction2'],
|
||||
}),
|
||||
},
|
||||
'command2': {
|
||||
// No _setting_.json file
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const workflowsDir = path.join(process.cwd(), 'workflows');
|
||||
customCommands.parseCommands(workflowsDir);
|
||||
|
||||
const expectedResult: Command[] = [
|
||||
{
|
||||
name: 'command1',
|
||||
pattern: 'command1',
|
||||
description: 'Command 1',
|
||||
message: 'Command 1 message',
|
||||
default: false,
|
||||
args: 0,
|
||||
show: true,
|
||||
instructions: ['instruction1', 'instruction2'],
|
||||
},
|
||||
];
|
||||
|
||||
expectedResult[0].instructions = [path.join(workflowsDir, 'some', 'command', 'command1', 'instruction1'), path.join(workflowsDir, 'some', 'command', 'command1', 'instruction2')];
|
||||
expect(customCommands['commands']).to.deep.equal(expectedResult);
|
||||
});
|
||||
|
||||
it('should register a custom command', () => {
|
||||
const command: Command = {
|
||||
name: 'test',
|
||||
pattern: 'test',
|
||||
description: 'Test command',
|
||||
message: 'Test message',
|
||||
default: false,
|
||||
args: 0,
|
||||
show: true,
|
||||
instructions: ['instruction1', 'instruction2'],
|
||||
};
|
||||
|
||||
customCommands.regCommand(command);
|
||||
expect(customCommands['commands']).to.include(command);
|
||||
});
|
||||
|
||||
it('should get a custom command by name', () => {
|
||||
const command: Command = {
|
||||
name: 'test',
|
||||
pattern: 'test',
|
||||
description: 'Test command',
|
||||
message: 'Test message',
|
||||
default: false,
|
||||
args: 0,
|
||||
show: true,
|
||||
instructions: ['instruction1', 'instruction2'],
|
||||
};
|
||||
|
||||
customCommands.regCommand(command);
|
||||
const foundCommand = customCommands.getCommand('test');
|
||||
expect(foundCommand).to.deep.equal(command);
|
||||
});
|
||||
|
||||
it('should handle a custom command', () => {
|
||||
const command: Command = {
|
||||
name: 'test',
|
||||
pattern: 'test',
|
||||
description: 'Test command',
|
||||
message: 'Test message',
|
||||
default: false,
|
||||
args: 0,
|
||||
show: true,
|
||||
instructions: ['instruction1', 'instruction2'],
|
||||
};
|
||||
|
||||
customCommands.regCommand(command);
|
||||
const result = customCommands.handleCommand('test', '');
|
||||
expect(result).to.equal('[instruction|instruction1] [instruction|instruction2] Test message');
|
||||
});
|
||||
|
||||
it('should handle a custom command with args', () => {
|
||||
const command: Command = {
|
||||
name: 'test',
|
||||
pattern: 'test {{prompt}}',
|
||||
description: 'Test command',
|
||||
message: 'Test message "$1","$2"',
|
||||
default: false,
|
||||
args: 0,
|
||||
show: true,
|
||||
instructions: ['instruction1', 'instruction2'],
|
||||
};
|
||||
|
||||
customCommands.regCommand(command);
|
||||
const result = customCommands.handleCommand('test', '["v1", "v2"]');
|
||||
expect(result).to.equal('[instruction|instruction1] [instruction|instruction2] Test message "v1","v2"');
|
||||
});
|
||||
});
|
@ -2,7 +2,6 @@ import { expect } from 'chai';
|
||||
import CustomContexts from '../../src/context/customContext';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import CustomCommands, { Command } from '../../src/command/customCommand';
|
||||
|
||||
describe('CustomContexts', () => {
|
||||
const workflowsDir = path.join(__dirname, 'test-workflows');
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import '../../src/context/loadContexts';
|
||||
import ChatContextManager from '../../src/context/contextManager';
|
||||
import { ChatContextManager } from '../../src/context/contextManager';
|
||||
import { gitDiffCachedContext } from '../../src/context/contextGitDiffCached';
|
||||
import { gitDiffContext } from '../../src/context/contextGitDiff';
|
||||
import { customCommandContext } from '../../src/context/contextCustomCommand';
|
||||
|
@ -1,14 +1,9 @@
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import { Context } from 'mocha';
|
||||
import sinon from 'sinon';
|
||||
import * as path from 'path';
|
||||
import { parseMessage, getInstructionFiles, parseMessageAndSetOptions, handleTopic, handlerResponseText, sendMessageBase, stopDevChatBase } from '../../src/handler/sendMessageBase';
|
||||
import DevChat, { ChatResponse } from '../../src/toolwrapper/devchat';
|
||||
import CommandManager from '../../src/command/commandManager';
|
||||
import messageHistory from '../../src/util/messageHistory';
|
||||
import { TopicManager } from '../../src/topic/topicManager';
|
||||
import CustomCommands from '../../src/command/customCommand';
|
||||
import { parseMessage, parseMessageAndSetOptions, TopicUpdateHandler, processChatResponse, sendMessageBase, stopDevChatBase } from '../../src/handler/sendMessageBase';
|
||||
import { ChatResponse } from '../../src/toolwrapper/devchat';
|
||||
import { UiUtilWrapper } from '../../src/util/uiUtil';
|
||||
|
||||
import * as dotenv from 'dotenv';
|
||||
@ -42,13 +37,6 @@ describe('sendMessageBase', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInstructionFiles', () => {
|
||||
it('should return instruction files', () => {
|
||||
const result = getInstructionFiles();
|
||||
expect(result).to.be.an('array');
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseMessageAndSetOptions', () => {
|
||||
it('should parse message and set options correctly', async () => {
|
||||
const message = {
|
||||
@ -69,7 +57,7 @@ describe('sendMessageBase', () => {
|
||||
});
|
||||
|
||||
|
||||
describe('handleTopic', () => {
|
||||
describe('TopicUpdateHandler.processTopicChangeAfterChat', () => {
|
||||
it('should handle topic correctly', async () => {
|
||||
const parentHash = 'somehash';
|
||||
const message = {
|
||||
@ -84,12 +72,12 @@ describe('sendMessageBase', () => {
|
||||
'prompt-hash': 'responsehash'
|
||||
};
|
||||
|
||||
await handleTopic(parentHash, message, chatResponse);
|
||||
await TopicUpdateHandler.processTopicChangeAfterChat(parentHash, message, chatResponse);
|
||||
// Check if the topic was updated correctly
|
||||
});
|
||||
});
|
||||
|
||||
describe('handlerResponseText', () => {
|
||||
describe('processChatResponse', () => {
|
||||
it('should handle response text correctly when isError is false', async () => {
|
||||
const partialDataText = 'Partial data';
|
||||
const chatResponse: ChatResponse = {
|
||||
@ -101,7 +89,7 @@ describe('sendMessageBase', () => {
|
||||
'prompt-hash': 'responsehash'
|
||||
};
|
||||
|
||||
const result = await handlerResponseText(partialDataText, chatResponse);
|
||||
const result = await processChatResponse(chatResponse);
|
||||
expect(result).to.equal('Hello, user!');
|
||||
});
|
||||
|
||||
@ -116,7 +104,7 @@ describe('sendMessageBase', () => {
|
||||
'prompt-hash': 'responsehash'
|
||||
};
|
||||
|
||||
const result = await handlerResponseText(partialDataText, chatResponse);
|
||||
const result = await processChatResponse(chatResponse);
|
||||
expect(result).to.equal('Error occurred!');
|
||||
});
|
||||
});
|
||||
|
2
tools
2
tools
@ -1 +1 @@
|
||||
Subproject commit d1f8662061e9b857ac78db362077c1d76868377e
|
||||
Subproject commit 314bb32c4790bc3eb8b1044ffc639ff3c85d562c
|
Loading…
x
Reference in New Issue
Block a user