mirror of
https://github.com/andrepimenta/claude-code-chat.git
synced 2025-12-08 19:29:43 +00:00
Merge pull request #45 from andrepimenta/feature/permissions
Feature/permissions
This commit is contained in:
@@ -9,3 +9,4 @@ vsc-extension-quickstart.md
|
||||
**/*.map
|
||||
**/*.ts
|
||||
**/.vscode-test.*
|
||||
backup
|
||||
715
package-lock.json
generated
715
package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"version": "0.1.3",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"devDependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.15.0",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/node": "20.x",
|
||||
"@types/vscode": "^1.94.0",
|
||||
@@ -18,7 +19,8 @@
|
||||
"@vscode/test-electron": "^2.5.2",
|
||||
"@vscode/vsce": "^3.5.0",
|
||||
"eslint": "^9.25.1",
|
||||
"typescript": "^5.8.3"
|
||||
"typescript": "^5.8.3",
|
||||
"zod": "^3.25.76"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.94.0"
|
||||
@@ -556,6 +558,30 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.15.0.tgz",
|
||||
"integrity": "sha512-67hnl/ROKdb03Vuu0YOr+baKTvf1/5YBHBm9KnZdjdAh8hjt4FRCPD5ucwxGB237sBpzlqQsLy1PFu7z/ekZ9Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.6",
|
||||
"content-type": "^1.0.5",
|
||||
"cors": "^2.8.5",
|
||||
"cross-spawn": "^7.0.5",
|
||||
"eventsource": "^3.0.2",
|
||||
"eventsource-parser": "^3.0.0",
|
||||
"express": "^5.0.1",
|
||||
"express-rate-limit": "^7.5.0",
|
||||
"pkce-challenge": "^5.0.0",
|
||||
"raw-body": "^3.0.0",
|
||||
"zod": "^3.23.8",
|
||||
"zod-to-json-schema": "^3.24.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -1513,6 +1539,43 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
||||
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-types": "^3.0.0",
|
||||
"negotiator": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts/node_modules/mime-db": {
|
||||
"version": "1.54.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts/node_modules/mime-types": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
|
||||
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "^1.54.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
||||
@@ -1771,6 +1834,27 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
|
||||
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "^3.1.2",
|
||||
"content-type": "^1.0.5",
|
||||
"debug": "^4.4.0",
|
||||
"http-errors": "^2.0.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"on-finished": "^2.4.1",
|
||||
"qs": "^6.14.0",
|
||||
"raw-body": "^3.0.0",
|
||||
"type-is": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/boolbase": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||
@@ -1868,6 +1952,16 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/c8": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz",
|
||||
@@ -2223,6 +2317,50 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
|
||||
"integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition/node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/convert-source-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
@@ -2230,6 +2368,26 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
|
||||
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
@@ -2237,6 +2395,20 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cors": {
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"object-assign": "^4",
|
||||
"vary": "^1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -2393,6 +2565,16 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
|
||||
@@ -2513,6 +2695,13 @@
|
||||
"url": "https://bevry.me/fund"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
@@ -2520,6 +2709,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/encoding-sniffer": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz",
|
||||
@@ -2633,6 +2832,13 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
@@ -2887,6 +3093,39 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/eventsource": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
|
||||
"integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eventsource-parser": "^3.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eventsource-parser": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz",
|
||||
"integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/expand-template": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||
@@ -2897,6 +3136,88 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
|
||||
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.0",
|
||||
"content-disposition": "^1.0.0",
|
||||
"content-type": "^1.0.5",
|
||||
"cookie": "^0.7.1",
|
||||
"cookie-signature": "^1.2.1",
|
||||
"debug": "^4.4.0",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"etag": "^1.8.1",
|
||||
"finalhandler": "^2.1.0",
|
||||
"fresh": "^2.0.0",
|
||||
"http-errors": "^2.0.0",
|
||||
"merge-descriptors": "^2.0.0",
|
||||
"mime-types": "^3.0.0",
|
||||
"on-finished": "^2.4.1",
|
||||
"once": "^1.4.0",
|
||||
"parseurl": "^1.3.3",
|
||||
"proxy-addr": "^2.0.7",
|
||||
"qs": "^6.14.0",
|
||||
"range-parser": "^1.2.1",
|
||||
"router": "^2.2.0",
|
||||
"send": "^1.1.0",
|
||||
"serve-static": "^2.2.0",
|
||||
"statuses": "^2.0.1",
|
||||
"type-is": "^2.0.1",
|
||||
"vary": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/express-rate-limit": {
|
||||
"version": "7.5.1",
|
||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",
|
||||
"integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/express-rate-limit"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express": ">= 4.11"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/mime-db": {
|
||||
"version": "1.54.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/mime-types": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
|
||||
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "^1.54.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -2996,6 +3317,24 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
||||
"integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"on-finished": "^2.4.1",
|
||||
"parseurl": "^1.3.3",
|
||||
"statuses": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/find-up": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
||||
@@ -3077,6 +3416,26 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
||||
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
@@ -3410,6 +3769,33 @@
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depd": "2.0.0",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"toidentifier": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors/node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy-agent": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
||||
@@ -3550,6 +3936,16 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
@@ -3668,6 +4064,13 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-promise": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
||||
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-unicode-supported": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
|
||||
@@ -4152,6 +4555,29 @@
|
||||
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
|
||||
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
@@ -4495,6 +4921,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
||||
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/node-abi": {
|
||||
"version": "3.75.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz",
|
||||
@@ -4576,6 +5012,16 @@
|
||||
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||
@@ -4588,6 +5034,19 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@@ -4927,6 +5386,16 @@
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
@@ -4974,6 +5443,16 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
|
||||
"integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/path-type": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz",
|
||||
@@ -5011,6 +5490,16 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pkce-challenge": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
|
||||
"integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pluralize": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
|
||||
@@ -5064,6 +5553,20 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"forwarded": "0.2.0",
|
||||
"ipaddr.js": "1.9.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/pump": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
|
||||
@@ -5140,6 +5643,32 @@
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
|
||||
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.6.3",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/rc": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
@@ -5294,6 +5823,23 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/router": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
||||
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"depd": "^2.0.0",
|
||||
"is-promise": "^4.0.0",
|
||||
"parseurl": "^1.3.3",
|
||||
"path-to-regexp": "^8.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/run-applescript": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz",
|
||||
@@ -5383,6 +5929,52 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
|
||||
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.5",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"etag": "^1.8.1",
|
||||
"fresh": "^2.0.0",
|
||||
"http-errors": "^2.0.0",
|
||||
"mime-types": "^3.0.1",
|
||||
"ms": "^2.1.3",
|
||||
"on-finished": "^2.4.1",
|
||||
"range-parser": "^1.2.1",
|
||||
"statuses": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/mime-db": {
|
||||
"version": "1.54.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/mime-types": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
|
||||
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "^1.54.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/serialize-javascript": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
|
||||
@@ -5393,6 +5985,22 @@
|
||||
"randombytes": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
|
||||
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"parseurl": "^1.3.3",
|
||||
"send": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
@@ -5400,6 +6008,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
@@ -5622,6 +6237,16 @@
|
||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/stdin-discarder": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz",
|
||||
@@ -6063,6 +6688,16 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-api-utils": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
|
||||
@@ -6129,6 +6764,44 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
||||
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"content-type": "^1.0.5",
|
||||
"media-typer": "^1.1.0",
|
||||
"mime-types": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is/node_modules/mime-db": {
|
||||
"version": "1.54.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is/node_modules/mime-types": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
|
||||
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "^1.54.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typed-rest-client": {
|
||||
"version": "1.8.11",
|
||||
"resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz",
|
||||
@@ -6203,6 +6876,16 @@
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
@@ -6260,6 +6943,16 @@
|
||||
"spdx-expression-parse": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/version-range": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/version-range/-/version-range-4.14.0.tgz",
|
||||
@@ -6587,6 +7280,26 @@
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.25.76",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/zod-to-json-schema": {
|
||||
"version": "3.24.6",
|
||||
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz",
|
||||
"integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"zod": "^3.24.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +180,11 @@
|
||||
],
|
||||
"default": "think",
|
||||
"description": "Thinking mode intensity level. Higher levels provide more detailed reasoning but consume more tokens."
|
||||
},
|
||||
"claudeCodeChat.permissions.yoloMode": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Enable Yolo Mode to skip all permission checks. Use with caution as Claude can execute any command without asking."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,6 +207,8 @@
|
||||
"@vscode/test-electron": "^2.5.2",
|
||||
"@vscode/vsce": "^3.5.0",
|
||||
"eslint": "^9.25.1",
|
||||
"typescript": "^5.8.3"
|
||||
"typescript": "^5.8.3",
|
||||
"@modelcontextprotocol/sdk": "^1.15.0",
|
||||
"zod": "^3.25.76"
|
||||
}
|
||||
}
|
||||
|
||||
500
src/extension.ts
500
src/extension.ts
@@ -94,6 +94,9 @@ class ClaudeChatProvider {
|
||||
private _backupRepoPath: string | undefined;
|
||||
private _commits: Array<{ id: string, sha: string, message: string, timestamp: string }> = [];
|
||||
private _conversationsPath: string | undefined;
|
||||
private _permissionRequestsPath: string | undefined;
|
||||
private _permissionWatcher: vscode.FileSystemWatcher | undefined;
|
||||
private _pendingPermissionResolvers: Map<string, (approved: boolean) => void> | undefined;
|
||||
private _currentConversation: Array<{ timestamp: string, messageType: string, data: any }> = [];
|
||||
private _conversationStartTime: string | undefined;
|
||||
private _conversationIndex: Array<{
|
||||
@@ -117,6 +120,8 @@ class ClaudeChatProvider {
|
||||
// Initialize backup repository and conversations
|
||||
this._initializeBackupRepo();
|
||||
this._initializeConversations();
|
||||
this._initializeMCPConfig();
|
||||
this._initializePermissions();
|
||||
|
||||
// Load conversation index from workspace state
|
||||
this._conversationIndex = this._context.workspaceState.get('claude.conversationIndex', []);
|
||||
@@ -211,6 +216,9 @@ class ClaudeChatProvider {
|
||||
|
||||
// Send platform information to webview
|
||||
this._sendPlatformInfo();
|
||||
|
||||
// Send current settings to webview
|
||||
this._sendCurrentSettings();
|
||||
}
|
||||
|
||||
private _handleWebviewMessage(message: any) {
|
||||
@@ -266,6 +274,18 @@ class ClaudeChatProvider {
|
||||
case 'createImageFile':
|
||||
this._createImageFile(message.imageData, message.imageType);
|
||||
return;
|
||||
case 'permissionResponse':
|
||||
this._handlePermissionResponse(message.id, message.approved, message.alwaysAllow);
|
||||
return;
|
||||
case 'getPermissions':
|
||||
this._sendPermissions();
|
||||
return;
|
||||
case 'removePermission':
|
||||
this._removePermission(message.toolName, message.command);
|
||||
return;
|
||||
case 'addPermission':
|
||||
this._addPermission(message.toolName, message.command);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,10 +413,26 @@ class ClaudeChatProvider {
|
||||
// Build command arguments with session management
|
||||
const args = [
|
||||
'-p',
|
||||
'--output-format', 'stream-json', '--verbose',
|
||||
'--dangerously-skip-permissions'
|
||||
'--output-format', 'stream-json', '--verbose'
|
||||
];
|
||||
|
||||
// Get configuration
|
||||
const config = vscode.workspace.getConfiguration('claudeCodeChat');
|
||||
const yoloMode = config.get<boolean>('permissions.yoloMode', false);
|
||||
|
||||
if (yoloMode) {
|
||||
// Yolo mode: skip all permissions regardless of MCP config
|
||||
args.push('--dangerously-skip-permissions');
|
||||
} else {
|
||||
// Add MCP configuration for permissions
|
||||
const mcpConfigPath = this.getMCPConfigPath();
|
||||
if (mcpConfigPath) {
|
||||
args.push('--mcp-config', mcpConfigPath);
|
||||
args.push('--allowedTools', 'mcp__permissions__approval_prompt');
|
||||
args.push('--permission-prompt-tool', 'mcp__permissions__approval_prompt');
|
||||
}
|
||||
}
|
||||
|
||||
// Add model selection if not using default
|
||||
if (this._selectedModel && this._selectedModel !== 'default') {
|
||||
args.push('--model', this._selectedModel);
|
||||
@@ -412,9 +448,6 @@ class ClaudeChatProvider {
|
||||
}
|
||||
|
||||
console.log('Claude command args:', args);
|
||||
|
||||
// Get configuration
|
||||
const config = vscode.workspace.getConfiguration('claudeCodeChat');
|
||||
const wslEnabled = config.get<boolean>('wsl.enabled', false);
|
||||
const wslDistro = config.get<string>('wsl.distro', 'Ubuntu');
|
||||
const nodePath = config.get<string>('wsl.nodePath', '/usr/bin/node');
|
||||
@@ -970,6 +1003,460 @@ class ClaudeChatProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private async _initializeMCPConfig(): Promise<void> {
|
||||
try {
|
||||
const storagePath = this._context.storageUri?.fsPath;
|
||||
if (!storagePath) {return;}
|
||||
|
||||
// Create MCP config directory
|
||||
const mcpConfigDir = path.join(storagePath, 'mcp');
|
||||
try {
|
||||
await vscode.workspace.fs.stat(vscode.Uri.file(mcpConfigDir));
|
||||
} catch {
|
||||
await vscode.workspace.fs.createDirectory(vscode.Uri.file(mcpConfigDir));
|
||||
console.log(`Created MCP config directory at: ${mcpConfigDir}`);
|
||||
}
|
||||
|
||||
// Create mcp-servers.json with correct path to compiled MCP permissions server
|
||||
const mcpConfigPath = path.join(mcpConfigDir, 'mcp-servers.json');
|
||||
const mcpPermissionsPath = path.join(this._extensionUri.fsPath, 'out', 'permissions', 'mcp-permissions.js');
|
||||
const permissionRequestsPath = path.join(storagePath, 'permission-requests');
|
||||
|
||||
const mcpConfig = {
|
||||
mcpServers: {
|
||||
permissions: {
|
||||
command: 'node',
|
||||
args: [mcpPermissionsPath],
|
||||
env: {
|
||||
CLAUDE_PERMISSIONS_PATH: permissionRequestsPath
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const configContent = new TextEncoder().encode(JSON.stringify(mcpConfig, null, 2));
|
||||
await vscode.workspace.fs.writeFile(vscode.Uri.file(mcpConfigPath), configContent);
|
||||
|
||||
console.log(`Created MCP config at: ${mcpConfigPath}`);
|
||||
} catch (error: any) {
|
||||
console.error('Failed to initialize MCP config:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
private async _initializePermissions(): Promise<void> {
|
||||
try {
|
||||
const storagePath = this._context.storageUri?.fsPath;
|
||||
if (!storagePath) {return;}
|
||||
|
||||
// Create permission requests directory
|
||||
this._permissionRequestsPath = path.join(storagePath, 'permission-requests');
|
||||
try {
|
||||
await vscode.workspace.fs.stat(vscode.Uri.file(this._permissionRequestsPath));
|
||||
} catch {
|
||||
await vscode.workspace.fs.createDirectory(vscode.Uri.file(this._permissionRequestsPath));
|
||||
console.log(`Created permission requests directory at: ${this._permissionRequestsPath}`);
|
||||
}
|
||||
|
||||
// Set up file watcher for *.request files
|
||||
this._permissionWatcher = vscode.workspace.createFileSystemWatcher(
|
||||
new vscode.RelativePattern(this._permissionRequestsPath, '*.request')
|
||||
);
|
||||
|
||||
this._permissionWatcher.onDidCreate(async (uri) => {
|
||||
// Only handle file scheme URIs, ignore vscode-userdata scheme
|
||||
if (uri.scheme === 'file') {
|
||||
await this._handlePermissionRequest(uri);
|
||||
}
|
||||
});
|
||||
|
||||
this._disposables.push(this._permissionWatcher);
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('Failed to initialize permissions:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
private async _handlePermissionRequest(requestUri: vscode.Uri): Promise<void> {
|
||||
try {
|
||||
// Read the request file
|
||||
const content = await vscode.workspace.fs.readFile(requestUri);
|
||||
const request = JSON.parse(new TextDecoder().decode(content));
|
||||
|
||||
// Show permission dialog
|
||||
const approved = await this._showPermissionDialog(request);
|
||||
|
||||
// Write response file
|
||||
const responseFile = requestUri.fsPath.replace('.request', '.response');
|
||||
const response = {
|
||||
id: request.id,
|
||||
approved: approved,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
const responseContent = new TextEncoder().encode(JSON.stringify(response));
|
||||
await vscode.workspace.fs.writeFile(vscode.Uri.file(responseFile), responseContent);
|
||||
|
||||
// Clean up request file
|
||||
await vscode.workspace.fs.delete(requestUri);
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('Failed to handle permission request:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
private async _showPermissionDialog(request: any): Promise<boolean> {
|
||||
const toolName = request.tool || 'Unknown Tool';
|
||||
|
||||
// Generate pattern for Bash commands
|
||||
let pattern = undefined;
|
||||
if (toolName === 'Bash' && request.input?.command) {
|
||||
pattern = this.getCommandPattern(request.input.command);
|
||||
}
|
||||
|
||||
// Send permission request to the UI
|
||||
this._postMessage({
|
||||
type: 'permissionRequest',
|
||||
data: {
|
||||
id: request.id,
|
||||
tool: toolName,
|
||||
input: request.input,
|
||||
pattern: pattern
|
||||
}
|
||||
});
|
||||
|
||||
// Wait for response from UI
|
||||
return new Promise((resolve) => {
|
||||
// Store the resolver so we can call it when we get the response
|
||||
this._pendingPermissionResolvers = this._pendingPermissionResolvers || new Map();
|
||||
this._pendingPermissionResolvers.set(request.id, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
private _handlePermissionResponse(id: string, approved: boolean, alwaysAllow?: boolean): void {
|
||||
if (this._pendingPermissionResolvers && this._pendingPermissionResolvers.has(id)) {
|
||||
const resolver = this._pendingPermissionResolvers.get(id);
|
||||
if (resolver) {
|
||||
resolver(approved);
|
||||
this._pendingPermissionResolvers.delete(id);
|
||||
|
||||
// Handle always allow setting
|
||||
if (alwaysAllow && approved) {
|
||||
void this._saveAlwaysAllowPermission(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _saveAlwaysAllowPermission(requestId: string): Promise<void> {
|
||||
try {
|
||||
// Read the original request to get tool name and input
|
||||
const storagePath = this._context.storageUri?.fsPath;
|
||||
if (!storagePath) return;
|
||||
|
||||
const requestFileUri = vscode.Uri.file(path.join(storagePath, 'permission-requests', `${requestId}.request`));
|
||||
|
||||
let requestContent: Uint8Array;
|
||||
try {
|
||||
requestContent = await vscode.workspace.fs.readFile(requestFileUri);
|
||||
} catch {
|
||||
return; // Request file doesn't exist
|
||||
}
|
||||
|
||||
const request = JSON.parse(new TextDecoder().decode(requestContent));
|
||||
|
||||
// Load existing workspace permissions
|
||||
const permissionsUri = vscode.Uri.file(path.join(storagePath, 'permission-requests', 'permissions.json'));
|
||||
let permissions: any = { alwaysAllow: {} };
|
||||
|
||||
try {
|
||||
const content = await vscode.workspace.fs.readFile(permissionsUri);
|
||||
permissions = JSON.parse(new TextDecoder().decode(content));
|
||||
} catch {
|
||||
// File doesn't exist yet, use default permissions
|
||||
}
|
||||
|
||||
// Add the new permission
|
||||
const toolName = request.tool;
|
||||
if (toolName === 'Bash' && request.input?.command) {
|
||||
// For Bash, store the command pattern
|
||||
if (!permissions.alwaysAllow[toolName]) {
|
||||
permissions.alwaysAllow[toolName] = [];
|
||||
}
|
||||
if (Array.isArray(permissions.alwaysAllow[toolName])) {
|
||||
const command = request.input.command.trim();
|
||||
const pattern = this.getCommandPattern(command);
|
||||
if (!permissions.alwaysAllow[toolName].includes(pattern)) {
|
||||
permissions.alwaysAllow[toolName].push(pattern);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For other tools, allow all instances
|
||||
permissions.alwaysAllow[toolName] = true;
|
||||
}
|
||||
|
||||
// Ensure permissions directory exists
|
||||
const permissionsDir = vscode.Uri.file(path.dirname(permissionsUri.fsPath));
|
||||
try {
|
||||
await vscode.workspace.fs.stat(permissionsDir);
|
||||
} catch {
|
||||
await vscode.workspace.fs.createDirectory(permissionsDir);
|
||||
}
|
||||
|
||||
// Save the permissions
|
||||
const permissionsContent = new TextEncoder().encode(JSON.stringify(permissions, null, 2));
|
||||
await vscode.workspace.fs.writeFile(permissionsUri, permissionsContent);
|
||||
|
||||
console.log(`Saved always-allow permission for ${toolName}`);
|
||||
} catch (error) {
|
||||
console.error('Error saving always-allow permission:', error);
|
||||
}
|
||||
}
|
||||
|
||||
private getCommandPattern(command: string): string {
|
||||
const parts = command.trim().split(/\s+/);
|
||||
if (parts.length === 0) return command;
|
||||
|
||||
const baseCmd = parts[0];
|
||||
const subCmd = parts.length > 1 ? parts[1] : '';
|
||||
|
||||
// Common patterns that should use wildcards
|
||||
const patterns = [
|
||||
// Package managers
|
||||
['npm', 'install', 'npm install *'],
|
||||
['npm', 'i', 'npm i *'],
|
||||
['npm', 'add', 'npm add *'],
|
||||
['npm', 'remove', 'npm remove *'],
|
||||
['npm', 'uninstall', 'npm uninstall *'],
|
||||
['npm', 'update', 'npm update *'],
|
||||
['npm', 'run', 'npm run *'],
|
||||
['yarn', 'add', 'yarn add *'],
|
||||
['yarn', 'remove', 'yarn remove *'],
|
||||
['yarn', 'install', 'yarn install *'],
|
||||
['pnpm', 'install', 'pnpm install *'],
|
||||
['pnpm', 'add', 'pnpm add *'],
|
||||
['pnpm', 'remove', 'pnpm remove *'],
|
||||
|
||||
// Git commands
|
||||
['git', 'add', 'git add *'],
|
||||
['git', 'commit', 'git commit *'],
|
||||
['git', 'push', 'git push *'],
|
||||
['git', 'pull', 'git pull *'],
|
||||
['git', 'checkout', 'git checkout *'],
|
||||
['git', 'branch', 'git branch *'],
|
||||
['git', 'merge', 'git merge *'],
|
||||
['git', 'clone', 'git clone *'],
|
||||
['git', 'reset', 'git reset *'],
|
||||
['git', 'rebase', 'git rebase *'],
|
||||
['git', 'tag', 'git tag *'],
|
||||
|
||||
// Docker commands
|
||||
['docker', 'run', 'docker run *'],
|
||||
['docker', 'build', 'docker build *'],
|
||||
['docker', 'exec', 'docker exec *'],
|
||||
['docker', 'logs', 'docker logs *'],
|
||||
['docker', 'stop', 'docker stop *'],
|
||||
['docker', 'start', 'docker start *'],
|
||||
['docker', 'rm', 'docker rm *'],
|
||||
['docker', 'rmi', 'docker rmi *'],
|
||||
['docker', 'pull', 'docker pull *'],
|
||||
['docker', 'push', 'docker push *'],
|
||||
|
||||
// Build tools
|
||||
['make', '', 'make *'],
|
||||
['cargo', 'build', 'cargo build *'],
|
||||
['cargo', 'run', 'cargo run *'],
|
||||
['cargo', 'test', 'cargo test *'],
|
||||
['cargo', 'install', 'cargo install *'],
|
||||
['mvn', 'compile', 'mvn compile *'],
|
||||
['mvn', 'test', 'mvn test *'],
|
||||
['mvn', 'package', 'mvn package *'],
|
||||
['gradle', 'build', 'gradle build *'],
|
||||
['gradle', 'test', 'gradle test *'],
|
||||
|
||||
// System commands
|
||||
['curl', '', 'curl *'],
|
||||
['wget', '', 'wget *'],
|
||||
['ssh', '', 'ssh *'],
|
||||
['scp', '', 'scp *'],
|
||||
['rsync', '', 'rsync *'],
|
||||
['tar', '', 'tar *'],
|
||||
['zip', '', 'zip *'],
|
||||
['unzip', '', 'unzip *'],
|
||||
|
||||
// Development tools
|
||||
['node', '', 'node *'],
|
||||
['python', '', 'python *'],
|
||||
['python3', '', 'python3 *'],
|
||||
['pip', 'install', 'pip install *'],
|
||||
['pip3', 'install', 'pip3 install *'],
|
||||
['composer', 'install', 'composer install *'],
|
||||
['composer', 'require', 'composer require *'],
|
||||
['bundle', 'install', 'bundle install *'],
|
||||
['gem', 'install', 'gem install *'],
|
||||
];
|
||||
|
||||
// Find matching pattern
|
||||
for (const [cmd, sub, pattern] of patterns) {
|
||||
if (baseCmd === cmd && (sub === '' || subCmd === sub)) {
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
|
||||
// Default: return exact command
|
||||
return command;
|
||||
}
|
||||
|
||||
private async _sendPermissions(): Promise<void> {
|
||||
try {
|
||||
const storagePath = this._context.storageUri?.fsPath;
|
||||
if (!storagePath) {
|
||||
this._postMessage({
|
||||
type: 'permissionsData',
|
||||
data: { alwaysAllow: {} }
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const permissionsUri = vscode.Uri.file(path.join(storagePath, 'permission-requests', 'permissions.json'));
|
||||
let permissions: any = { alwaysAllow: {} };
|
||||
|
||||
try {
|
||||
const content = await vscode.workspace.fs.readFile(permissionsUri);
|
||||
permissions = JSON.parse(new TextDecoder().decode(content));
|
||||
} catch {
|
||||
// File doesn't exist or can't be read, use default permissions
|
||||
}
|
||||
|
||||
this._postMessage({
|
||||
type: 'permissionsData',
|
||||
data: permissions
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error sending permissions:', error);
|
||||
this._postMessage({
|
||||
type: 'permissionsData',
|
||||
data: { alwaysAllow: {} }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _removePermission(toolName: string, command: string | null): Promise<void> {
|
||||
try {
|
||||
const storagePath = this._context.storageUri?.fsPath;
|
||||
if (!storagePath) return;
|
||||
|
||||
const permissionsUri = vscode.Uri.file(path.join(storagePath, 'permission-requests', 'permissions.json'));
|
||||
let permissions: any = { alwaysAllow: {} };
|
||||
|
||||
try {
|
||||
const content = await vscode.workspace.fs.readFile(permissionsUri);
|
||||
permissions = JSON.parse(new TextDecoder().decode(content));
|
||||
} catch {
|
||||
// File doesn't exist or can't be read, nothing to remove
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the permission
|
||||
if (command === null) {
|
||||
// Remove entire tool permission
|
||||
delete permissions.alwaysAllow[toolName];
|
||||
} else {
|
||||
// Remove specific command from tool permissions
|
||||
if (Array.isArray(permissions.alwaysAllow[toolName])) {
|
||||
permissions.alwaysAllow[toolName] = permissions.alwaysAllow[toolName].filter(
|
||||
(cmd: string) => cmd !== command
|
||||
);
|
||||
// If no commands left, remove the tool entirely
|
||||
if (permissions.alwaysAllow[toolName].length === 0) {
|
||||
delete permissions.alwaysAllow[toolName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save updated permissions
|
||||
const permissionsContent = new TextEncoder().encode(JSON.stringify(permissions, null, 2));
|
||||
await vscode.workspace.fs.writeFile(permissionsUri, permissionsContent);
|
||||
|
||||
// Send updated permissions to UI
|
||||
this._sendPermissions();
|
||||
|
||||
console.log(`Removed permission for ${toolName}${command ? ` command: ${command}` : ''}`);
|
||||
} catch (error) {
|
||||
console.error('Error removing permission:', error);
|
||||
}
|
||||
}
|
||||
|
||||
private async _addPermission(toolName: string, command: string | null): Promise<void> {
|
||||
try {
|
||||
const storagePath = this._context.storageUri?.fsPath;
|
||||
if (!storagePath) return;
|
||||
|
||||
const permissionsUri = vscode.Uri.file(path.join(storagePath, 'permission-requests', 'permissions.json'));
|
||||
let permissions: any = { alwaysAllow: {} };
|
||||
|
||||
try {
|
||||
const content = await vscode.workspace.fs.readFile(permissionsUri);
|
||||
permissions = JSON.parse(new TextDecoder().decode(content));
|
||||
} catch {
|
||||
// File doesn't exist, use default permissions
|
||||
}
|
||||
|
||||
// Add the new permission
|
||||
if (command === null || command === '') {
|
||||
// Allow all commands for this tool
|
||||
permissions.alwaysAllow[toolName] = true;
|
||||
} else {
|
||||
// Add specific command pattern
|
||||
if (!permissions.alwaysAllow[toolName]) {
|
||||
permissions.alwaysAllow[toolName] = [];
|
||||
}
|
||||
|
||||
// Convert to array if it's currently set to true
|
||||
if (permissions.alwaysAllow[toolName] === true) {
|
||||
permissions.alwaysAllow[toolName] = [];
|
||||
}
|
||||
|
||||
if (Array.isArray(permissions.alwaysAllow[toolName])) {
|
||||
// For Bash commands, convert to pattern using existing logic
|
||||
let commandToAdd = command;
|
||||
if (toolName === 'Bash') {
|
||||
commandToAdd = this.getCommandPattern(command);
|
||||
}
|
||||
|
||||
// Add if not already present
|
||||
if (!permissions.alwaysAllow[toolName].includes(commandToAdd)) {
|
||||
permissions.alwaysAllow[toolName].push(commandToAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure permissions directory exists
|
||||
const permissionsDir = vscode.Uri.file(path.dirname(permissionsUri.fsPath));
|
||||
try {
|
||||
await vscode.workspace.fs.stat(permissionsDir);
|
||||
} catch {
|
||||
await vscode.workspace.fs.createDirectory(permissionsDir);
|
||||
}
|
||||
|
||||
// Save updated permissions
|
||||
const permissionsContent = new TextEncoder().encode(JSON.stringify(permissions, null, 2));
|
||||
await vscode.workspace.fs.writeFile(permissionsUri, permissionsContent);
|
||||
|
||||
// Send updated permissions to UI
|
||||
this._sendPermissions();
|
||||
|
||||
console.log(`Added permission for ${toolName}${command ? ` command: ${command}` : ' (all commands)'}`);
|
||||
} catch (error) {
|
||||
console.error('Error adding permission:', error);
|
||||
}
|
||||
}
|
||||
|
||||
public getMCPConfigPath(): string | undefined {
|
||||
const storagePath = this._context.storageUri?.fsPath;
|
||||
if (!storagePath) {return undefined;}
|
||||
return path.join(storagePath, 'mcp', 'mcp-servers.json');
|
||||
}
|
||||
|
||||
private _sendAndSaveMessage(message: { type: string, data: any }): void {
|
||||
// Initialize conversation if this is the first message
|
||||
if (this._currentConversation.length === 0) {
|
||||
@@ -1286,7 +1773,8 @@ class ClaudeChatProvider {
|
||||
'wsl.enabled': config.get<boolean>('wsl.enabled', false),
|
||||
'wsl.distro': config.get<string>('wsl.distro', 'Ubuntu'),
|
||||
'wsl.nodePath': config.get<string>('wsl.nodePath', '/usr/bin/node'),
|
||||
'wsl.claudePath': config.get<string>('wsl.claudePath', '/usr/local/bin/claude')
|
||||
'wsl.claudePath': config.get<string>('wsl.claudePath', '/usr/local/bin/claude'),
|
||||
'permissions.yoloMode': config.get<boolean>('permissions.yoloMode', false)
|
||||
};
|
||||
|
||||
this._postMessage({
|
||||
|
||||
212
src/permissions/mcp-permissions.ts
Normal file
212
src/permissions/mcp-permissions.ts
Normal file
@@ -0,0 +1,212 @@
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { z } from "zod";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
const server = new McpServer({
|
||||
name: "Claude Code Permissions MCP Server",
|
||||
version: "0.0.1",
|
||||
});
|
||||
|
||||
// Get permissions directory from environment
|
||||
const PERMISSIONS_PATH = process.env.CLAUDE_PERMISSIONS_PATH;
|
||||
if (!PERMISSIONS_PATH) {
|
||||
console.error("CLAUDE_PERMISSIONS_PATH environment variable not set");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
interface WorkspacePermissions {
|
||||
alwaysAllow: {
|
||||
[toolName: string]: boolean | string[]; // true for all, or array of allowed commands/patterns
|
||||
};
|
||||
}
|
||||
|
||||
function getWorkspacePermissionsPath(): string | null {
|
||||
if (!PERMISSIONS_PATH) return null;
|
||||
return path.join(PERMISSIONS_PATH, 'permissions.json');
|
||||
}
|
||||
|
||||
function loadWorkspacePermissions(): WorkspacePermissions {
|
||||
const permissionsPath = getWorkspacePermissionsPath();
|
||||
if (!permissionsPath || !fs.existsSync(permissionsPath)) {
|
||||
return { alwaysAllow: {} };
|
||||
}
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(permissionsPath, 'utf8');
|
||||
return JSON.parse(content);
|
||||
} catch (error) {
|
||||
console.error(`Error loading workspace permissions: ${error}`);
|
||||
return { alwaysAllow: {} };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function isAlwaysAllowed(toolName: string, input: any): boolean {
|
||||
const permissions = loadWorkspacePermissions();
|
||||
const toolPermission = permissions.alwaysAllow[toolName];
|
||||
|
||||
if (!toolPermission) return false;
|
||||
|
||||
// If it's true, always allow
|
||||
if (toolPermission === true) return true;
|
||||
|
||||
// If it's an array, check for specific commands (mainly for Bash)
|
||||
if (Array.isArray(toolPermission)) {
|
||||
if (toolName === 'Bash' && input.command) {
|
||||
const command = input.command.trim();
|
||||
return toolPermission.some(allowedCmd => {
|
||||
// Support exact match or pattern matching
|
||||
if (allowedCmd.includes('*')) {
|
||||
// Handle patterns like "npm i *" to match both "npm i" and "npm i something"
|
||||
const baseCommand = allowedCmd.replace(' *', '');
|
||||
if (command === baseCommand) {
|
||||
return true; // Exact match for base command
|
||||
}
|
||||
// Pattern match for command with arguments
|
||||
const pattern = allowedCmd.replace(/\*/g, '.*');
|
||||
return new RegExp(`^${pattern}$`).test(command);
|
||||
}
|
||||
return command.startsWith(allowedCmd);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function generateRequestId(): string {
|
||||
return `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
||||
}
|
||||
|
||||
async function requestPermission(tool_name: string, input: any): Promise<{approved: boolean, reason?: string}> {
|
||||
if (!PERMISSIONS_PATH) {
|
||||
console.error("Permissions path not available");
|
||||
return { approved: false, reason: "Permissions path not configured" };
|
||||
}
|
||||
|
||||
// Check if this tool/command is always allowed for this workspace
|
||||
if (isAlwaysAllowed(tool_name, input)) {
|
||||
console.error(`Tool ${tool_name} is always allowed for this workspace`);
|
||||
return { approved: true };
|
||||
}
|
||||
|
||||
const requestId = generateRequestId();
|
||||
const requestFile = path.join(PERMISSIONS_PATH, `${requestId}.request`);
|
||||
const responseFile = path.join(PERMISSIONS_PATH, `${requestId}.response`);
|
||||
|
||||
// Write request file
|
||||
const request = {
|
||||
id: requestId,
|
||||
tool: tool_name,
|
||||
input: input,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
try {
|
||||
fs.writeFileSync(requestFile, JSON.stringify(request, null, 2));
|
||||
|
||||
// Use fs.watch to wait for response file
|
||||
return new Promise<{approved: boolean, reason?: string}>((resolve) => {
|
||||
const timeout = setTimeout(() => {
|
||||
watcher.close();
|
||||
// Clean up request file on timeout
|
||||
if (fs.existsSync(requestFile)) {
|
||||
fs.unlinkSync(requestFile);
|
||||
}
|
||||
console.error(`Permission request ${requestId} timed out`);
|
||||
resolve({ approved: false, reason: "Permission request timed out" });
|
||||
}, 3600000); // 1 hour timeout
|
||||
|
||||
const watcher = fs.watch(PERMISSIONS_PATH, (eventType, filename) => {
|
||||
if (eventType === 'rename' && filename === path.basename(responseFile)) {
|
||||
// Check if file exists (rename event can be for creation or deletion)
|
||||
if (fs.existsSync(responseFile)) {
|
||||
try {
|
||||
const responseContent = fs.readFileSync(responseFile, 'utf8');
|
||||
const response = JSON.parse(responseContent);
|
||||
|
||||
// Clean up response file
|
||||
fs.unlinkSync(responseFile);
|
||||
|
||||
// Clear timeout and close watcher
|
||||
clearTimeout(timeout);
|
||||
watcher.close();
|
||||
|
||||
resolve({
|
||||
approved: response.approved,
|
||||
reason: response.approved ? undefined : "User rejected the request"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error reading response file: ${error}`);
|
||||
// Continue watching in case of read error
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle watcher errors
|
||||
watcher.on('error', (error) => {
|
||||
console.error(`File watcher error: ${error}`);
|
||||
clearTimeout(timeout);
|
||||
watcher.close();
|
||||
resolve({ approved: false, reason: "File watcher error" });
|
||||
});
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Error requesting permission: ${error}`);
|
||||
return { approved: false, reason: `Error processing permission request: ${error}` };
|
||||
}
|
||||
}
|
||||
|
||||
server.tool(
|
||||
"approval_prompt",
|
||||
'Request user permission to execute a tool via VS Code dialog',
|
||||
{
|
||||
tool_name: z.string().describe("The name of the tool requesting permission"),
|
||||
input: z.object({}).passthrough().describe("The input for the tool"),
|
||||
tool_use_id: z.string().optional().describe("The unique tool use request ID"),
|
||||
},
|
||||
async ({ tool_name, input }) => {
|
||||
console.error(`Requesting permission for tool: ${tool_name}`);
|
||||
|
||||
const permissionResult = await requestPermission(tool_name, input);
|
||||
|
||||
const behavior = permissionResult.approved ? "allow" : "deny";
|
||||
console.error(`Permission ${behavior}ed for tool: ${tool_name}`);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: behavior === "allow" ?
|
||||
JSON.stringify({
|
||||
behavior: behavior,
|
||||
updatedInput: input,
|
||||
})
|
||||
:
|
||||
JSON.stringify({
|
||||
behavior: behavior,
|
||||
message: permissionResult.reason || "Permission denied",
|
||||
})
|
||||
,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
async function main() {
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
console.error(`Permissions MCP Server running on stdio`);
|
||||
console.error(`Using permissions directory: ${PERMISSIONS_PATH}`);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("Fatal error in main():", error);
|
||||
process.exit(1);
|
||||
});
|
||||
10
src/permissions/mcp-servers.json
Normal file
10
src/permissions/mcp-servers.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"permissions": {
|
||||
"command": "node",
|
||||
"args": [
|
||||
"./out/permissions/mcp-permissions.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
481
src/ui-styles.ts
481
src/ui-styles.ts
@@ -83,6 +83,436 @@ const styles = `
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Permission Request */
|
||||
.permission-request {
|
||||
margin: 4px 12px 20px 12px;
|
||||
background-color: var(--vscode-inputValidation-warningBackground);
|
||||
border: 1px solid var(--vscode-inputValidation-warningBorder);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
animation: slideUp 0.3s ease;
|
||||
}
|
||||
|
||||
.permission-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--vscode-foreground);
|
||||
}
|
||||
|
||||
.permission-header .icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.permission-content {
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.permission-tool {
|
||||
font-family: var(--vscode-editor-font-family);
|
||||
background-color: var(--vscode-editor-background);
|
||||
border: 1px solid var(--vscode-panel-border);
|
||||
border-radius: 4px;
|
||||
padding: 8px 10px;
|
||||
margin: 8px 0;
|
||||
font-size: 12px;
|
||||
color: var(--vscode-editor-foreground);
|
||||
}
|
||||
|
||||
.permission-buttons {
|
||||
margin-top: 2px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.permission-buttons .btn {
|
||||
font-size: 12px;
|
||||
padding: 6px 12px;
|
||||
min-width: 70px;
|
||||
text-align: center;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 28px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.permission-buttons .btn.allow {
|
||||
background-color: var(--vscode-button-background);
|
||||
color: var(--vscode-button-foreground);
|
||||
border-color: var(--vscode-button-background);
|
||||
}
|
||||
|
||||
.permission-buttons .btn.allow:hover {
|
||||
background-color: var(--vscode-button-hoverBackground);
|
||||
}
|
||||
|
||||
.permission-buttons .btn.deny {
|
||||
background-color: transparent;
|
||||
color: var(--vscode-foreground);
|
||||
border-color: var(--vscode-panel-border);
|
||||
}
|
||||
|
||||
.permission-buttons .btn.deny:hover {
|
||||
background-color: var(--vscode-list-hoverBackground);
|
||||
border-color: var(--vscode-focusBorder);
|
||||
}
|
||||
|
||||
.permission-buttons .btn.always-allow {
|
||||
background-color: rgba(0, 122, 204, 0.1);
|
||||
color: var(--vscode-charts-blue);
|
||||
border-color: rgba(0, 122, 204, 0.3);
|
||||
font-weight: 500;
|
||||
min-width: auto;
|
||||
padding: 6px 14px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.permission-buttons .btn.always-allow:hover {
|
||||
background-color: rgba(0, 122, 204, 0.2);
|
||||
border-color: rgba(0, 122, 204, 0.5);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.permission-buttons .btn.always-allow code {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-family: var(--vscode-editor-font-family);
|
||||
font-size: 11px;
|
||||
color: var(--vscode-editor-foreground);
|
||||
margin-left: 4px;
|
||||
display: inline;
|
||||
line-height: 1;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
.permission-decision {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
padding: 8px 12px;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.permission-decision.allowed {
|
||||
background-color: rgba(0, 122, 204, 0.15);
|
||||
color: var(--vscode-charts-blue);
|
||||
border: 1px solid rgba(0, 122, 204, 0.3);
|
||||
}
|
||||
|
||||
.permission-decision.denied {
|
||||
background-color: rgba(231, 76, 60, 0.15);
|
||||
color: #e74c3c;
|
||||
border: 1px solid rgba(231, 76, 60, 0.3);
|
||||
}
|
||||
|
||||
.permission-decided {
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.permission-decided .permission-buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.permission-decided.allowed {
|
||||
border-color: var(--vscode-inputValidation-infoBackground);
|
||||
background-color: rgba(0, 122, 204, 0.1);
|
||||
}
|
||||
|
||||
.permission-decided.denied {
|
||||
border-color: var(--vscode-inputValidation-errorBorder);
|
||||
background-color: var(--vscode-inputValidation-errorBackground);
|
||||
}
|
||||
|
||||
/* Permissions Management */
|
||||
.permissions-list {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--vscode-panel-border);
|
||||
border-radius: 6px;
|
||||
background-color: var(--vscode-input-background);
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.permission-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
border-bottom: 1px solid var(--vscode-panel-border);
|
||||
transition: background-color 0.2s ease;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.permission-item:hover {
|
||||
background-color: var(--vscode-list-hoverBackground);
|
||||
}
|
||||
|
||||
.permission-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.permission-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.permission-tool {
|
||||
background-color: var(--vscode-badge-background);
|
||||
color: var(--vscode-badge-foreground);
|
||||
padding: 3px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
flex-shrink: 0;
|
||||
height: 18px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.permission-command {
|
||||
font-size: 12px;
|
||||
color: var(--vscode-foreground);
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.permission-command code {
|
||||
background-color: var(--vscode-textCodeBlock-background);
|
||||
padding: 3px 6px;
|
||||
border-radius: 3px;
|
||||
font-family: var(--vscode-editor-font-family);
|
||||
color: var(--vscode-textLink-foreground);
|
||||
font-size: 11px;
|
||||
height: 18px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.permission-desc {
|
||||
color: var(--vscode-descriptionForeground);
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
flex-grow: 1;
|
||||
height: 18px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.permission-remove-btn {
|
||||
background-color: transparent;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
border: none;
|
||||
padding: 4px 8px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
font-size: 10px;
|
||||
transition: all 0.2s ease;
|
||||
font-weight: 500;
|
||||
flex-shrink: 0;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.permission-remove-btn:hover {
|
||||
background-color: rgba(231, 76, 60, 0.1);
|
||||
color: var(--vscode-errorForeground);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.permissions-empty {
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
font-style: italic;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.permissions-empty::before {
|
||||
content: "🔒";
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
margin-bottom: 8px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Add Permission Form */
|
||||
.permissions-add-section {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.permissions-show-add-btn {
|
||||
background-color: transparent;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
border: 1px solid var(--vscode-panel-border);
|
||||
border-radius: 3px;
|
||||
padding: 6px 8px;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-weight: 400;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.permissions-show-add-btn:hover {
|
||||
background-color: var(--vscode-list-hoverBackground);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.permissions-add-form {
|
||||
margin-top: 8px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--vscode-panel-border);
|
||||
border-radius: 6px;
|
||||
background-color: var(--vscode-input-background);
|
||||
animation: slideDown 0.2s ease;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.permissions-form-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.permissions-tool-select {
|
||||
background-color: var(--vscode-input-background);
|
||||
color: var(--vscode-input-foreground);
|
||||
border: 1px solid var(--vscode-panel-border);
|
||||
border-radius: 3px;
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
min-width: 100px;
|
||||
height: 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.permissions-command-input {
|
||||
background-color: var(--vscode-input-background);
|
||||
color: var(--vscode-input-foreground);
|
||||
border: 1px solid var(--vscode-panel-border);
|
||||
border-radius: 3px;
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
flex-grow: 1;
|
||||
height: 24px;
|
||||
font-family: var(--vscode-editor-font-family);
|
||||
}
|
||||
|
||||
.permissions-command-input::placeholder {
|
||||
color: var(--vscode-input-placeholderForeground);
|
||||
}
|
||||
|
||||
.permissions-add-btn {
|
||||
background-color: var(--vscode-button-background);
|
||||
color: var(--vscode-button-foreground);
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
padding: 4px 12px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
height: 24px;
|
||||
font-weight: 500;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.permissions-add-btn:hover {
|
||||
background-color: var(--vscode-button-hoverBackground);
|
||||
}
|
||||
|
||||
.permissions-add-btn:disabled {
|
||||
background-color: var(--vscode-button-secondaryBackground);
|
||||
color: var(--vscode-button-secondaryForeground);
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.permissions-cancel-btn {
|
||||
background-color: transparent;
|
||||
color: var(--vscode-foreground);
|
||||
border: 1px solid var(--vscode-panel-border);
|
||||
border-radius: 3px;
|
||||
padding: 4px 12px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
height: 24px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.permissions-cancel-btn:hover {
|
||||
background-color: var(--vscode-list-hoverBackground);
|
||||
border-color: var(--vscode-focusBorder);
|
||||
}
|
||||
|
||||
.permissions-form-hint {
|
||||
font-size: 11px;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
font-style: italic;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.yolo-mode-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: 12px;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.yolo-mode-section:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.yolo-mode-section input[type="checkbox"] {
|
||||
transform: scale(0.8);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.yolo-mode-section label {
|
||||
font-size: 10px;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
cursor: pointer;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* WSL Alert */
|
||||
.wsl-alert {
|
||||
margin: 8px 12px;
|
||||
@@ -923,13 +1353,52 @@ const styles = `
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.beta-warning {
|
||||
font-size: 11px;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
.yolo-warning {
|
||||
font-size: 12px;
|
||||
color: var(--vscode-inputValidation-errorForeground);
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
background-color: var(--vscode-panel-background);
|
||||
padding: 4px
|
||||
font-weight: 500;
|
||||
background-color: var(--vscode-inputValidation-errorBackground);
|
||||
border: 1px solid var(--vscode-inputValidation-errorBorder);
|
||||
padding: 8px 12px;
|
||||
margin: 4px 12px;
|
||||
border-radius: 4px;
|
||||
animation: slideDown 0.3s ease;
|
||||
}
|
||||
|
||||
.yolo-suggestion {
|
||||
margin-top: 12px;
|
||||
padding: 12px;
|
||||
background-color: rgba(0, 122, 204, 0.1);
|
||||
border: 1px solid rgba(0, 122, 204, 0.3);
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.yolo-suggestion-text {
|
||||
font-size: 12px;
|
||||
color: var(--vscode-foreground);
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.yolo-suggestion-btn {
|
||||
background-color: var(--vscode-button-background);
|
||||
color: var(--vscode-button-foreground);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 6px 12px;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
font-weight: 500;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.yolo-suggestion-btn:hover {
|
||||
background-color: var(--vscode-button-hoverBackground);
|
||||
}
|
||||
|
||||
.file-picker-modal {
|
||||
|
||||
348
src/ui.ts
348
src/ui.ts
@@ -131,8 +131,8 @@ const html = `<!DOCTYPE html>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="beta-warning">
|
||||
In Beta. All Claude Code tools are allowed. Use at your own risk.
|
||||
<div id="yoloWarning" class="yolo-warning" style="display: none;">
|
||||
⚠️ Yolo Mode Active: Claude Code can execute any tool without asking.
|
||||
</div>
|
||||
|
||||
<!-- File picker modal -->
|
||||
@@ -248,6 +248,53 @@ const html = `<!DOCTYPE html>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 style="margin-top: 24px; margin-bottom: 16px; font-size: 14px; font-weight: 600;">Permissions</h3>
|
||||
<div>
|
||||
<p style="font-size: 11px; color: var(--vscode-descriptionForeground); margin: 0;">
|
||||
Manage commands and tools that are automatically allowed without asking for permission.
|
||||
</p>
|
||||
</div>
|
||||
<div class="settings-group">
|
||||
<div id="permissionsList" class="permissions-list">
|
||||
<div class="permissions-loading" style="text-align: center; padding: 20px; color: var(--vscode-descriptionForeground);">
|
||||
Loading permissions...
|
||||
</div>
|
||||
</div>
|
||||
<div class="permissions-add-section">
|
||||
<div id="addPermissionForm" class="permissions-add-form" style="display: none;">
|
||||
<div class="permissions-form-row">
|
||||
<select id="addPermissionTool" class="permissions-tool-select" onchange="toggleCommandInput()">
|
||||
<option value="">Select tool...</option>
|
||||
<option value="Bash">Bash</option>
|
||||
<option value="Read">Read</option>
|
||||
<option value="Edit">Edit</option>
|
||||
<option value="Write">Write</option>
|
||||
<option value="MultiEdit">MultiEdit</option>
|
||||
<option value="Glob">Glob</option>
|
||||
<option value="Grep">Grep</option>
|
||||
<option value="LS">LS</option>
|
||||
<option value="WebSearch">WebSearch</option>
|
||||
<option value="WebFetch">WebFetch</option>
|
||||
</select>
|
||||
<div style="flex-grow: 1; display: flex;">
|
||||
<input type="text" id="addPermissionCommand" class="permissions-command-input" placeholder="Command pattern (e.g., npm i *)" style="display: none;" />
|
||||
</div>
|
||||
<button id="addPermissionBtn" class="permissions-add-btn" onclick="addPermission()">Add</button>
|
||||
</div>
|
||||
<div id="permissionsFormHint" class="permissions-form-hint">
|
||||
Select a tool to add always-allow permission.
|
||||
</div>
|
||||
</div>
|
||||
<button id="showAddPermissionBtn" class="permissions-show-add-btn" onclick="showAddPermissionForm()">
|
||||
+ Add permission
|
||||
</button>
|
||||
<div class="yolo-mode-section">
|
||||
<input type="checkbox" id="yolo-mode" onchange="updateSettings(); updateYoloWarning();">
|
||||
<label for="yolo-mode">Enable Yolo Mode (Skip All Permissions)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 style="margin-top: 24px; margin-bottom: 16px; font-size: 14px; font-weight: 600;">MCP Configuration (coming soon)</h3>
|
||||
<div>
|
||||
<p style="font-size: 11px; color: var(--vscode-descriptionForeground); margin: 0;">
|
||||
@@ -592,6 +639,20 @@ const html = `<!DOCTYPE html>
|
||||
}
|
||||
|
||||
messageDiv.appendChild(contentDiv);
|
||||
|
||||
// Check if this is a permission-related error and add yolo mode button
|
||||
if (type === 'error' && isPermissionError(content)) {
|
||||
const yoloSuggestion = document.createElement('div');
|
||||
yoloSuggestion.className = 'yolo-suggestion';
|
||||
yoloSuggestion.innerHTML = \`
|
||||
<div class="yolo-suggestion-text">
|
||||
<span>💡 This looks like a permission issue. You can enable Yolo Mode to skip all permission checks.</span>
|
||||
</div>
|
||||
<button class="yolo-suggestion-btn" onclick="enableYoloMode()">Enable Yolo Mode</button>
|
||||
\`;
|
||||
messageDiv.appendChild(yoloSuggestion);
|
||||
}
|
||||
|
||||
messagesDiv.appendChild(messageDiv);
|
||||
messagesDiv.scrollTop = messagesDiv.scrollHeight;
|
||||
}
|
||||
@@ -776,6 +837,20 @@ const html = `<!DOCTYPE html>
|
||||
}
|
||||
|
||||
messageDiv.appendChild(contentDiv);
|
||||
|
||||
// Check if this is a permission-related error and add yolo mode button
|
||||
if (data.isError && isPermissionError(content)) {
|
||||
const yoloSuggestion = document.createElement('div');
|
||||
yoloSuggestion.className = 'yolo-suggestion';
|
||||
yoloSuggestion.innerHTML = \`
|
||||
<div class="yolo-suggestion-text">
|
||||
<span>💡 This looks like a permission issue. You can enable Yolo Mode to skip all permission checks.</span>
|
||||
</div>
|
||||
<button class="yolo-suggestion-btn" onclick="enableYoloMode()">Enable Yolo Mode</button>
|
||||
\`;
|
||||
messageDiv.appendChild(yoloSuggestion);
|
||||
}
|
||||
|
||||
messagesDiv.appendChild(messageDiv);
|
||||
messagesDiv.scrollTop = messagesDiv.scrollHeight;
|
||||
}
|
||||
@@ -1423,6 +1498,54 @@ const html = `<!DOCTYPE html>
|
||||
document.getElementById('toolsModal').style.display = 'flex';
|
||||
}
|
||||
|
||||
function updateYoloWarning() {
|
||||
const yoloModeCheckbox = document.getElementById('yolo-mode');
|
||||
const warning = document.getElementById('yoloWarning');
|
||||
|
||||
if (!yoloModeCheckbox || !warning) {
|
||||
return; // Elements not ready yet
|
||||
}
|
||||
|
||||
const yoloMode = yoloModeCheckbox.checked;
|
||||
warning.style.display = yoloMode ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function isPermissionError(content) {
|
||||
const permissionErrorPatterns = [
|
||||
'Error: MCP config file not found',
|
||||
'Error: MCP tool',
|
||||
'Claude requested permissions to use',
|
||||
'permission denied',
|
||||
'Permission denied',
|
||||
'permission request',
|
||||
'Permission request',
|
||||
'EACCES',
|
||||
'permission error',
|
||||
'Permission error'
|
||||
];
|
||||
|
||||
return permissionErrorPatterns.some(pattern =>
|
||||
content.toLowerCase().includes(pattern.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
function enableYoloMode() {
|
||||
// Update the checkbox
|
||||
const yoloModeCheckbox = document.getElementById('yolo-mode');
|
||||
if (yoloModeCheckbox) {
|
||||
yoloModeCheckbox.checked = true;
|
||||
|
||||
// Trigger the settings update
|
||||
updateSettings();
|
||||
|
||||
// Show confirmation message
|
||||
addMessage('✅ Yolo Mode enabled! All permission checks will be bypassed for future commands.', 'system');
|
||||
|
||||
// Update the warning banner
|
||||
updateYoloWarning();
|
||||
}
|
||||
}
|
||||
|
||||
function hideToolsModal() {
|
||||
document.getElementById('toolsModal').style.display = 'none';
|
||||
}
|
||||
@@ -1960,9 +2083,86 @@ const html = `<!DOCTYPE html>
|
||||
// Display notification about checking the terminal
|
||||
addMessage(message.data, 'system');
|
||||
break;
|
||||
case 'permissionRequest':
|
||||
addPermissionRequestMessage(message.data);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Permission request functions
|
||||
function addPermissionRequestMessage(data) {
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = 'message permission-request';
|
||||
|
||||
const toolName = data.tool || 'Unknown Tool';
|
||||
|
||||
// Create always allow button text with command styling for Bash
|
||||
let alwaysAllowText = \`Always allow \${toolName}\`;
|
||||
let alwaysAllowTooltip = '';
|
||||
if (toolName === 'Bash' && data.pattern) {
|
||||
const pattern = data.pattern;
|
||||
// Remove the asterisk for display - show "npm i" instead of "npm i *"
|
||||
const displayPattern = pattern.replace(' *', '');
|
||||
const truncatedPattern = displayPattern.length > 30 ? displayPattern.substring(0, 30) + '...' : displayPattern;
|
||||
alwaysAllowText = \`Always allow <code>\${truncatedPattern}</code>\`;
|
||||
alwaysAllowTooltip = displayPattern.length > 30 ? \`title="\${displayPattern}"\` : '';
|
||||
}
|
||||
|
||||
messageDiv.innerHTML = \`
|
||||
<div class="permission-header">
|
||||
<span class="icon">🔐</span>
|
||||
<span>Permission Required</span>
|
||||
</div>
|
||||
<div class="permission-content">
|
||||
<p>Allow <strong>\${toolName}</strong> to execute the tool call above?</p>
|
||||
<div class="permission-buttons">
|
||||
<button class="btn deny" onclick="respondToPermission('\${data.id}', false)">Deny</button>
|
||||
<button class="btn always-allow" onclick="respondToPermission('\${data.id}', true, true)" \${alwaysAllowTooltip}>\${alwaysAllowText}</button>
|
||||
<button class="btn allow" onclick="respondToPermission('\${data.id}', true)">Allow</button>
|
||||
</div>
|
||||
</div>
|
||||
\`;
|
||||
|
||||
messagesDiv.appendChild(messageDiv);
|
||||
messagesDiv.scrollTop = messagesDiv.scrollHeight;
|
||||
}
|
||||
|
||||
function respondToPermission(id, approved, alwaysAllow = false) {
|
||||
// Send response back to extension
|
||||
vscode.postMessage({
|
||||
type: 'permissionResponse',
|
||||
id: id,
|
||||
approved: approved,
|
||||
alwaysAllow: alwaysAllow
|
||||
});
|
||||
|
||||
// Update the UI to show the decision
|
||||
const permissionMsg = document.querySelector(\`.permission-request:has([onclick*="\${id}"])\`);
|
||||
if (permissionMsg) {
|
||||
const buttons = permissionMsg.querySelector('.permission-buttons');
|
||||
const permissionContent = permissionMsg.querySelector('.permission-content');
|
||||
let decision = approved ? 'You allowed this' : 'You denied this';
|
||||
|
||||
if (alwaysAllow && approved) {
|
||||
decision = 'You allowed this and set it to always allow';
|
||||
}
|
||||
|
||||
const emoji = approved ? '✅' : '❌';
|
||||
const decisionClass = approved ? 'allowed' : 'denied';
|
||||
|
||||
// Hide buttons
|
||||
buttons.style.display = 'none';
|
||||
|
||||
// Add decision div to permission-content
|
||||
const decisionDiv = document.createElement('div');
|
||||
decisionDiv.className = \`permission-decision \${decisionClass}\`;
|
||||
decisionDiv.innerHTML = \`\${emoji} \${decision}\`;
|
||||
permissionContent.appendChild(decisionDiv);
|
||||
|
||||
permissionMsg.classList.add('permission-decided', decisionClass);
|
||||
}
|
||||
}
|
||||
|
||||
// Session management functions
|
||||
function newSession() {
|
||||
vscode.postMessage({
|
||||
@@ -2357,6 +2557,10 @@ const html = `<!DOCTYPE html>
|
||||
vscode.postMessage({
|
||||
type: 'getSettings'
|
||||
});
|
||||
// Request current permissions
|
||||
vscode.postMessage({
|
||||
type: 'getPermissions'
|
||||
});
|
||||
settingsModal.style.display = 'flex';
|
||||
} else {
|
||||
hideSettingsModal();
|
||||
@@ -2374,6 +2578,7 @@ const html = `<!DOCTYPE html>
|
||||
const wslDistro = document.getElementById('wsl-distro').value;
|
||||
const wslNodePath = document.getElementById('wsl-node-path').value;
|
||||
const wslClaudePath = document.getElementById('wsl-claude-path').value;
|
||||
const yoloMode = document.getElementById('yolo-mode').checked;
|
||||
|
||||
// Update WSL options visibility
|
||||
document.getElementById('wslOptions').style.display = wslEnabled ? 'block' : 'none';
|
||||
@@ -2385,11 +2590,139 @@ const html = `<!DOCTYPE html>
|
||||
'wsl.enabled': wslEnabled,
|
||||
'wsl.distro': wslDistro || 'Ubuntu',
|
||||
'wsl.nodePath': wslNodePath || '/usr/bin/node',
|
||||
'wsl.claudePath': wslClaudePath || '/usr/local/bin/claude'
|
||||
'wsl.claudePath': wslClaudePath || '/usr/local/bin/claude',
|
||||
'permissions.yoloMode': yoloMode
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Permissions management functions
|
||||
function renderPermissions(permissions) {
|
||||
const permissionsList = document.getElementById('permissionsList');
|
||||
|
||||
if (!permissions || !permissions.alwaysAllow || Object.keys(permissions.alwaysAllow).length === 0) {
|
||||
permissionsList.innerHTML = \`
|
||||
<div class="permissions-empty">
|
||||
No always-allow permissions set
|
||||
</div>
|
||||
\`;
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
|
||||
for (const [toolName, permission] of Object.entries(permissions.alwaysAllow)) {
|
||||
if (permission === true) {
|
||||
// Tool is always allowed
|
||||
html += \`
|
||||
<div class="permission-item">
|
||||
<div class="permission-info">
|
||||
<span class="permission-tool">\${toolName}</span>
|
||||
<span class="permission-desc">All</span>
|
||||
</div>
|
||||
<button class="permission-remove-btn" onclick="removePermission('\${toolName}', null)">Remove</button>
|
||||
</div>
|
||||
\`;
|
||||
} else if (Array.isArray(permission)) {
|
||||
// Tool has specific commands/patterns
|
||||
for (const command of permission) {
|
||||
const displayCommand = command.replace(' *', ''); // Remove asterisk for display
|
||||
html += \`
|
||||
<div class="permission-item">
|
||||
<div class="permission-info">
|
||||
<span class="permission-tool">\${toolName}</span>
|
||||
<span class="permission-command"><code>\${displayCommand}</code></span>
|
||||
</div>
|
||||
<button class="permission-remove-btn" onclick="removePermission('\${toolName}', '\${escapeHtml(command)}')">Remove</button>
|
||||
</div>
|
||||
\`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
permissionsList.innerHTML = html;
|
||||
}
|
||||
|
||||
function removePermission(toolName, command) {
|
||||
vscode.postMessage({
|
||||
type: 'removePermission',
|
||||
toolName: toolName,
|
||||
command: command
|
||||
});
|
||||
}
|
||||
|
||||
function showAddPermissionForm() {
|
||||
document.getElementById('showAddPermissionBtn').style.display = 'none';
|
||||
document.getElementById('addPermissionForm').style.display = 'block';
|
||||
|
||||
// Focus on the tool select dropdown
|
||||
setTimeout(() => {
|
||||
document.getElementById('addPermissionTool').focus();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function hideAddPermissionForm() {
|
||||
document.getElementById('showAddPermissionBtn').style.display = 'flex';
|
||||
document.getElementById('addPermissionForm').style.display = 'none';
|
||||
|
||||
// Clear form inputs
|
||||
document.getElementById('addPermissionTool').value = '';
|
||||
document.getElementById('addPermissionCommand').value = '';
|
||||
document.getElementById('addPermissionCommand').style.display = 'none';
|
||||
}
|
||||
|
||||
function toggleCommandInput() {
|
||||
const toolSelect = document.getElementById('addPermissionTool');
|
||||
const commandInput = document.getElementById('addPermissionCommand');
|
||||
const hintDiv = document.getElementById('permissionsFormHint');
|
||||
|
||||
if (toolSelect.value === 'Bash') {
|
||||
commandInput.style.display = 'block';
|
||||
hintDiv.textContent = 'Use patterns like "npm i *" or "git add *" for specific commands.';
|
||||
} else if (toolSelect.value === '') {
|
||||
commandInput.style.display = 'none';
|
||||
commandInput.value = '';
|
||||
hintDiv.textContent = 'Select a tool to add always-allow permission.';
|
||||
} else {
|
||||
commandInput.style.display = 'none';
|
||||
commandInput.value = '';
|
||||
hintDiv.textContent = 'This will allow all ' + toolSelect.value + ' commands without asking for permission.';
|
||||
}
|
||||
}
|
||||
|
||||
function addPermission() {
|
||||
const toolSelect = document.getElementById('addPermissionTool');
|
||||
const commandInput = document.getElementById('addPermissionCommand');
|
||||
const addBtn = document.getElementById('addPermissionBtn');
|
||||
|
||||
const toolName = toolSelect.value.trim();
|
||||
const command = commandInput.value.trim();
|
||||
|
||||
if (!toolName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable button during processing
|
||||
addBtn.disabled = true;
|
||||
addBtn.textContent = 'Adding...';
|
||||
|
||||
vscode.postMessage({
|
||||
type: 'addPermission',
|
||||
toolName: toolName,
|
||||
command: command || null
|
||||
});
|
||||
|
||||
// Clear form and hide it
|
||||
toolSelect.value = '';
|
||||
commandInput.value = '';
|
||||
hideAddPermissionForm();
|
||||
|
||||
// Re-enable button
|
||||
setTimeout(() => {
|
||||
addBtn.disabled = false;
|
||||
addBtn.textContent = 'Add';
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// Close settings modal when clicking outside
|
||||
document.getElementById('settingsModal').addEventListener('click', (e) => {
|
||||
@@ -2446,6 +2779,10 @@ const html = `<!DOCTYPE html>
|
||||
document.getElementById('wsl-distro').value = message.data['wsl.distro'] || 'Ubuntu';
|
||||
document.getElementById('wsl-node-path').value = message.data['wsl.nodePath'] || '/usr/bin/node';
|
||||
document.getElementById('wsl-claude-path').value = message.data['wsl.claudePath'] || '/usr/local/bin/claude';
|
||||
document.getElementById('yolo-mode').checked = message.data['permissions.yoloMode'] || false;
|
||||
|
||||
// Update yolo warning visibility
|
||||
updateYoloWarning();
|
||||
|
||||
// Show/hide WSL options
|
||||
document.getElementById('wslOptions').style.display = message.data['wsl.enabled'] ? 'block' : 'none';
|
||||
@@ -2460,6 +2797,11 @@ const html = `<!DOCTYPE html>
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
if (message.type === 'permissionsData') {
|
||||
// Update permissions UI
|
||||
renderPermissions(message.data);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user