mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-08 09:49:34 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddef0df32d | ||
|
|
8ef78c0ad5 | ||
|
|
5e22a51150 | ||
|
|
466acc6eef | ||
|
|
f4ff84a3b8 | ||
|
|
fdc28f5648 | ||
|
|
1371f9407f |
No files matched your search
@@ -5,19 +5,17 @@
|
||||
"": {
|
||||
"name": "zero",
|
||||
"dependencies": {
|
||||
"@opentui/core": "^0.2.16",
|
||||
"@opentui/solid": "^0.2.16",
|
||||
"commander": "^15.0.0",
|
||||
"execa": "^9.6.1",
|
||||
"ink": "^7.0.4",
|
||||
"ink-spinner": "^5.0.0",
|
||||
"ink-text-input": "^6.0.0",
|
||||
"openai": "^6.39.1",
|
||||
"react": "^19.2.6",
|
||||
"shiki": "^4.1.0",
|
||||
"solid-js": "1.9.12",
|
||||
"zod": "^4.4.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"@types/react": "^19.2.15",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5",
|
||||
@@ -25,7 +23,87 @@
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@alcalzone/ansi-tokenize": ["@alcalzone/[email protected]", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-p+CMKJ93HFmLkjXKlXiVGlMQEuRb6H0MokBSwUsX+S6BRX8eV5naFZpQJFfJHjRZY0Hmnqy1/r6UWl3x+19zYA=="],
|
||||
"@ampproject/remapping": ["@ampproject/[email protected]", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
||||
|
||||
"@babel/code-frame": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="],
|
||||
|
||||
"@babel/compat-data": ["@babel/[email protected]", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="],
|
||||
|
||||
"@babel/core": ["@babel/[email protected]", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
|
||||
|
||||
"@babel/generator": ["@babel/[email protected]", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="],
|
||||
|
||||
"@babel/helper-annotate-as-pure": ["@babel/[email protected]", "", { "dependencies": { "@babel/types": "^7.29.7" } }, "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw=="],
|
||||
|
||||
"@babel/helper-compilation-targets": ["@babel/[email protected]", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="],
|
||||
|
||||
"@babel/helper-create-class-features-plugin": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", "@babel/traverse": "^7.29.7", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg=="],
|
||||
|
||||
"@babel/helper-globals": ["@babel/[email protected]", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="],
|
||||
|
||||
"@babel/helper-member-expression-to-functions": ["@babel/[email protected]", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg=="],
|
||||
|
||||
"@babel/helper-module-imports": ["@babel/[email protected]", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="],
|
||||
|
||||
"@babel/helper-module-transforms": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg=="],
|
||||
|
||||
"@babel/helper-optimise-call-expression": ["@babel/[email protected]", "", { "dependencies": { "@babel/types": "^7.29.7" } }, "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong=="],
|
||||
|
||||
"@babel/helper-plugin-utils": ["@babel/[email protected]", "", {}, "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw=="],
|
||||
|
||||
"@babel/helper-replace-supers": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ=="],
|
||||
|
||||
"@babel/helper-skip-transparent-expression-wrappers": ["@babel/[email protected]", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ=="],
|
||||
|
||||
"@babel/helper-string-parser": ["@babel/[email protected]", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="],
|
||||
|
||||
"@babel/helper-validator-identifier": ["@babel/[email protected]", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="],
|
||||
|
||||
"@babel/helper-validator-option": ["@babel/[email protected]", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="],
|
||||
|
||||
"@babel/helpers": ["@babel/[email protected]", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="],
|
||||
|
||||
"@babel/parser": ["@babel/[email protected]", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="],
|
||||
|
||||
"@babel/plugin-syntax-jsx": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A=="],
|
||||
|
||||
"@babel/plugin-syntax-typescript": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA=="],
|
||||
|
||||
"@babel/plugin-transform-modules-commonjs": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ=="],
|
||||
|
||||
"@babel/plugin-transform-typescript": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", "@babel/plugin-syntax-typescript": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw=="],
|
||||
|
||||
"@babel/preset-typescript": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ=="],
|
||||
|
||||
"@babel/template": ["@babel/[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="],
|
||||
|
||||
"@babel/traverse": ["@babel/[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="],
|
||||
|
||||
"@babel/types": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/[email protected]", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||
|
||||
"@jridgewell/resolve-uri": ["@jridgewell/[email protected]", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/[email protected]", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/[email protected]", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@opentui/core": ["@opentui/[email protected]", "", { "dependencies": { "bun-ffi-structs": "0.2.2", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.2.16", "@opentui/core-darwin-x64": "0.2.16", "@opentui/core-linux-arm64": "0.2.16", "@opentui/core-linux-x64": "0.2.16", "@opentui/core-win32-arm64": "0.2.16", "@opentui/core-win32-x64": "0.2.16" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-4vWN15Zc3nsXJlOiHhhpqkBXD+wrNFKxCPtiTiillZYDRre+XsZogVTOOGUDwaBIC23OSxq7imezLmmtShVBEA=="],
|
||||
|
||||
"@opentui/core-darwin-arm64": ["@opentui/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-aFb2Yp+oqDu3h6VCWi7xpQ9yjpKSQcROzGGfHgqC6Nd3U+uiLfPJBkmiI87iK0opCggCFj5TkKI004050DmGjg=="],
|
||||
|
||||
"@opentui/core-darwin-x64": ["@opentui/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-KimiHE0j7EsTB5P8doW0lr1eH5iZKLPKWQO+tmy1VcdYr/TzqhdHSvGuJXrZvfTFi9/rV57Eq0d7964Ri9O0vQ=="],
|
||||
|
||||
"@opentui/core-linux-arm64": ["@opentui/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-4fCwRCfTtUgS/5QcSEkSuBjgQymSOUWXgrXG2ycrf3Swi0QhKDA/pVjwLrUJ6eF+/8mQyQSEV72T8MxMO3M2qg=="],
|
||||
|
||||
"@opentui/core-linux-x64": ["@opentui/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-KgQBGjiucw4e7gM+R8qOzHWBFhjCY1IfCrGjW3Wzxv2hKUlL+mPhelaeJwnEqtNxMUdVTYjlwlu3IHxslXMJWQ=="],
|
||||
|
||||
"@opentui/core-win32-arm64": ["@opentui/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-C6WqEI3VkXatXraMgSFXZjEXq0pzURGjRpFAJZYmuVDmpqE57o7E80Np2UkdZ6m5kpJDt4mRyu3krc/P825iNQ=="],
|
||||
|
||||
"@opentui/core-win32-x64": ["@opentui/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-kCX3CMTns6DMCFDNTDV4sjmBKyA/iEvzaVhl/jYi4JRIVT2zcy1lo+lhXT5mPgYHmJZu8Uye6j3Zi3c7Z2Me5A=="],
|
||||
|
||||
"@opentui/solid": ["@opentui/[email protected]", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.2.16", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-2Q+v1PPpXXr+sALi9Aj6I5Jvo7xDfbmstYjRLL7lW3Hghh9i7ONQKpt/gyDDRbhSsYrhxKYTNenF9OxgoXkTHg=="],
|
||||
|
||||
"@sec-ant/readable-stream": ["@sec-ant/[email protected]", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="],
|
||||
|
||||
@@ -55,87 +133,99 @@
|
||||
|
||||
"@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg=="],
|
||||
|
||||
"@types/react": ["@types/[email protected]", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q=="],
|
||||
|
||||
"@types/unist": ["@types/[email protected]", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||
|
||||
"@ungap/structured-clone": ["@ungap/[email protected]", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="],
|
||||
|
||||
"ansi-escapes": ["[email protected]", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="],
|
||||
|
||||
"ansi-regex": ["[email protected]", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||
"babel-plugin-jsx-dom-expressions": ["[email protected]", "", { "dependencies": { "@babel/helper-module-imports": "7.18.6", "@babel/plugin-syntax-jsx": "^7.18.6", "@babel/types": "^7.20.7", "html-entities": "2.3.3", "parse5": "^7.1.2" }, "peerDependencies": { "@babel/core": "^7.20.12" } }, "sha512-/O6JWUmjv03OI9lL2ry9bUjpD5S3PclM55RRJEyCdcFZ5W2SEA/59d+l2hNsk3gI6kiWRdRPdOtqZmsQzFN1pQ=="],
|
||||
|
||||
"auto-bind": ["[email protected]", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="],
|
||||
"babel-plugin-module-resolver": ["[email protected]", "", { "dependencies": { "find-babel-config": "^2.1.1", "glob": "^9.3.3", "pkg-up": "^3.1.0", "reselect": "^4.1.7", "resolve": "^1.22.8" } }, "sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg=="],
|
||||
|
||||
"babel-preset-solid": ["[email protected]", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.6" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.12" }, "optionalPeers": ["solid-js"] }, "sha512-LLqnuKVDlKpyBlMPcH6qEvs/wmS9a+NczppxJ3ryS/c0O5IiSFOIBQi9GzyiGDSbcJpx4Gr87jyFTos1MyEuWg=="],
|
||||
|
||||
"balanced-match": ["[email protected]", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"baseline-browser-mapping": ["[email protected]", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw=="],
|
||||
|
||||
"brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA=="],
|
||||
|
||||
"browserslist": ["[email protected]", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
|
||||
|
||||
"bun-ffi-structs": ["[email protected]", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-N/ZWtyN0piZlrXQT7TO0V+q952orYqkfhXRXM1Hcbb+R3QSiBH4vLnib187Mrs1H7pWIYECAmPeapGYDOMCl+w=="],
|
||||
|
||||
"bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
|
||||
|
||||
"ccount": ["[email protected]", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
||||
"caniuse-lite": ["[email protected]", "", {}, "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA=="],
|
||||
|
||||
"chalk": ["c[email protected]", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||
"ccount": ["c[email protected]", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
||||
|
||||
"character-entities-html4": ["[email protected]", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
|
||||
|
||||
"character-entities-legacy": ["[email protected]", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
|
||||
|
||||
"cli-boxes": ["[email protected]", "", {}, "sha512-5IOn+jcCEHEraYolBPs/sT4BxYCe2nHg374OPiItB1O96KZFseS2gthU4twyYzeDcFew4DaUM/xwc5BQf08JJw=="],
|
||||
|
||||
"cli-cursor": ["[email protected]", "", { "dependencies": { "restore-cursor": "^4.0.0" } }, "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg=="],
|
||||
|
||||
"cli-spinners": ["[email protected]", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="],
|
||||
|
||||
"cli-truncate": ["[email protected]", "", { "dependencies": { "slice-ansi": "^9.0.0", "string-width": "^8.2.0" } }, "sha512-3+YKIUFsohD9MIoOFPFBldjAlnfCmCDcqe6aYGFqlDTRKg80p4wg35L+j83QQ63iOlKRccEkbn8IuM++HsgEjA=="],
|
||||
|
||||
"code-excerpt": ["[email protected]", "", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="],
|
||||
|
||||
"comma-separated-tokens": ["[email protected]", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
|
||||
|
||||
"commander": ["[email protected]", "", {}, "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg=="],
|
||||
|
||||
"convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="],
|
||||
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||
|
||||
"cross-spawn": ["[email protected]", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"csstype": ["[email protected]", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||
|
||||
"debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"dequal": ["[email protected]", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||
|
||||
"devlop": ["[email protected]", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||
|
||||
"environment": ["[email protected]", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="],
|
||||
"diff": ["[email protected]", "", {}, "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw=="],
|
||||
|
||||
"es-toolkit": ["[email protected]", "", {}, "sha512-n1GuoD0WEQZMBk5tttoZSqwgyLx01oqa5XsBmCHwPyNe1S9jPBEmtR2pSgp2kJuWE3ciFZ6yRHmY4pM4C3OOkw=="],
|
||||
"electron-to-chromium": ["[email protected]", "", {}, "sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw=="],
|
||||
|
||||
"escape-string-regexp": ["e[email protected]", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="],
|
||||
"emoji-regex": ["e[email protected]", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
||||
|
||||
"entities": ["[email protected]", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
|
||||
|
||||
"es-errors": ["[email protected]", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||
|
||||
"escalade": ["[email protected]", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
||||
"execa": ["[email protected]", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="],
|
||||
|
||||
"figures": ["[email protected]", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
|
||||
|
||||
"find-babel-config": ["[email protected]", "", { "dependencies": { "json5": "^2.2.3" } }, "sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg=="],
|
||||
|
||||
"find-up": ["[email protected]", "", { "dependencies": { "locate-path": "^3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="],
|
||||
|
||||
"fs.realpath": ["[email protected]", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
|
||||
|
||||
"function-bind": ["[email protected]", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"gensync": ["[email protected]", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||
|
||||
"get-east-asian-width": ["[email protected]", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="],
|
||||
|
||||
"get-stream": ["[email protected]", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
|
||||
|
||||
"glob": ["[email protected]", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
|
||||
|
||||
"hasown": ["[email protected]", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="],
|
||||
|
||||
"hast-util-to-html": ["[email protected]", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
|
||||
|
||||
"hast-util-whitespace": ["[email protected]", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
|
||||
|
||||
"html-entities": ["[email protected]", "", {}, "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA=="],
|
||||
|
||||
"html-void-elements": ["[email protected]", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
|
||||
|
||||
"human-signals": ["[email protected]", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="],
|
||||
|
||||
"indent-string": ["[email protected]", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
|
||||
|
||||
"ink": ["[email protected]", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.3.0", "ansi-escapes": "^7.3.0", "ansi-styles": "^6.2.3", "auto-bind": "^5.0.1", "chalk": "^5.6.2", "cli-boxes": "^4.0.1", "cli-cursor": "^4.0.0", "cli-truncate": "^6.0.0", "code-excerpt": "^4.0.0", "es-toolkit": "^1.45.1", "indent-string": "^5.0.0", "is-in-ci": "^2.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.33.0", "scheduler": "^0.27.0", "signal-exit": "^3.0.7", "slice-ansi": "^9.0.0", "stack-utils": "^2.0.6", "string-width": "^8.2.0", "terminal-size": "^4.0.1", "type-fest": "^5.5.0", "widest-line": "^6.0.0", "wrap-ansi": "^10.0.0", "ws": "^8.20.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=19.2.0", "react": ">=19.2.0", "react-devtools-core": ">=6.1.2" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-4wsM/gMKOT2ZANNTJibI6I9IcwBfobqv/CgaDcwvOaCREZIQxo3iGQS7qPHa2hmA67NYltZWCMtBDELB/mcbJQ=="],
|
||||
|
||||
"ink-spinner": ["[email protected]", "", { "dependencies": { "cli-spinners": "^2.7.0" }, "peerDependencies": { "ink": ">=4.0.0", "react": ">=18.0.0" } }, "sha512-EYEasbEjkqLGyPOUc8hBJZNuC5GvXGMLu0w5gdTNskPc7Izc5vO3tdQEYnzvshucyGCBXc86ig0ujXPMWaQCdA=="],
|
||||
|
||||
"ink-text-input": ["[email protected]", "", { "dependencies": { "chalk": "^5.3.0", "type-fest": "^4.18.2" }, "peerDependencies": { "ink": ">=5", "react": ">=18" } }, "sha512-Fw64n7Yha5deb1rHY137zHTAbSTNelUKuB5Kkk2HACXEtwIHBCf9OH2tP/LQ9fRYTl1F0dZgbW0zPnZk6FA9Lw=="],
|
||||
|
||||
"is-fullwidth-code-point": ["[email protected]", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
|
||||
|
||||
"is-in-ci": ["[email protected]", "", { "bin": { "is-in-ci": "cli.js" } }, "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w=="],
|
||||
"is-core-module": ["[email protected]", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="],
|
||||
|
||||
"is-plain-obj": ["[email protected]", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
|
||||
|
||||
@@ -145,6 +235,18 @@
|
||||
|
||||
"isexe": ["[email protected]", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"js-tokens": ["[email protected]", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"jsesc": ["[email protected]", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||
|
||||
"json5": ["[email protected]", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||
|
||||
"locate-path": ["[email protected]", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="],
|
||||
|
||||
"lru-cache": ["[email protected]", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"marked": ["[email protected]", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg=="],
|
||||
|
||||
"mdast-util-to-hast": ["[email protected]", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="],
|
||||
|
||||
"micromark-util-character": ["[email protected]", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
|
||||
@@ -157,41 +259,65 @@
|
||||
|
||||
"micromark-util-types": ["[email protected]", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
|
||||
|
||||
"mimic-fn": ["[email protected]", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
|
||||
"minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg=="],
|
||||
|
||||
"minipass": ["[email protected]", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
|
||||
|
||||
"ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"node-releases": ["[email protected]", "", {}, "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ=="],
|
||||
|
||||
"npm-run-path": ["[email protected]", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="],
|
||||
|
||||
"onetime": ["[email protected]", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
|
||||
|
||||
"oniguruma-parser": ["[email protected]", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="],
|
||||
|
||||
"oniguruma-to-es": ["[email protected]", "", { "dependencies": { "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA=="],
|
||||
|
||||
"openai": ["[email protected]", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-z3dO9fEWOXBzlXynVb/xZ/tujzUjFWQWn3C0n0mw6Vo0zJTbEkaN4b2cLWjhJ6haJQx8LlREoafHRl+Gu/Hl+A=="],
|
||||
|
||||
"p-limit": ["[email protected]", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
|
||||
|
||||
"p-locate": ["[email protected]", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],
|
||||
|
||||
"p-try": ["[email protected]", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="],
|
||||
|
||||
"parse-ms": ["[email protected]", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="],
|
||||
|
||||
"patch-console": ["[email protected]", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="],
|
||||
"parse5": ["[email protected]", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
|
||||
|
||||
"path-exists": ["[email protected]", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="],
|
||||
|
||||
"path-key": ["[email protected]", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"path-parse": ["[email protected]", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
||||
|
||||
"path-scurry": ["[email protected]", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||
|
||||
"picocolors": ["[email protected]", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"pkg-up": ["[email protected]", "", { "dependencies": { "find-up": "^3.0.0" } }, "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA=="],
|
||||
|
||||
"pretty-ms": ["[email protected]", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="],
|
||||
|
||||
"property-information": ["[email protected]", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
|
||||
|
||||
"react": ["[email protected]", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="],
|
||||
|
||||
"react-reconciler": ["[email protected]", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA=="],
|
||||
|
||||
"regex": ["[email protected]", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="],
|
||||
|
||||
"regex-recursion": ["[email protected]", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
|
||||
|
||||
"regex-utilities": ["[email protected]", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
|
||||
|
||||
"restore-cursor": ["[email protected]", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="],
|
||||
"reselect": ["[email protected]", "", {}, "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="],
|
||||
|
||||
"scheduler": ["[email protected].0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||
"resolve": ["[email protected]", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="],
|
||||
|
||||
"s-js": ["[email protected]", "", {}, "sha512-RtpOm+cM6O0sHg6IA70wH+UC3FZcND+rccBZpBAHzlUgNO2Bm5BN+FnM8+OBxzXdwpKWFwX11JGF0MFRkhSoIQ=="],
|
||||
|
||||
"semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"seroval": ["[email protected]", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="],
|
||||
|
||||
"seroval-plugins": ["[email protected]", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="],
|
||||
|
||||
"shebang-command": ["[email protected]", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
@@ -201,28 +327,22 @@
|
||||
|
||||
"signal-exit": ["[email protected]", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
"slice-ansi": ["sli[email protected]", "", { "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" } }, "sha512-SO/3iYL5S3W57LLEniscOGPZgOqZUPCx6d3dB+52B80yJ0XstzsC/eV8gnA4tM3MHDrKz+OCFSLNjswdSC+/bA=="],
|
||||
"solid-js": ["soli[email protected]", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.5.0", "seroval-plugins": "~1.5.0" } }, "sha512-QzKaSJq2/iDrWR1As6MHZQ8fQkdOBf8GReYb7L5iKwMGceg7HxDcaOHk0at66tNgn9U2U7dXo8ZZpLIAmGMzgw=="],
|
||||
|
||||
"space-separated-tokens": ["[email protected]", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
|
||||
|
||||
"stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="],
|
||||
|
||||
"string-width": ["[email protected]", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="],
|
||||
"string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||
|
||||
"stringify-entities": ["[email protected]", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
|
||||
"strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||
|
||||
"strip-final-newline": ["[email protected]", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="],
|
||||
|
||||
"tagged-tag": ["[email protected]", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],
|
||||
|
||||
"terminal-size": ["[email protected]", "", {}, "sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ=="],
|
||||
"supports-preserve-symlinks-flag": ["[email protected]", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
||||
|
||||
"trim-lines": ["[email protected]", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
|
||||
|
||||
"type-fest": ["[email protected]", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA=="],
|
||||
|
||||
"typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"undici-types": ["[email protected]", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="],
|
||||
@@ -239,18 +359,20 @@
|
||||
|
||||
"unist-util-visit-parents": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="],
|
||||
|
||||
"update-browserslist-db": ["[email protected]", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
|
||||
|
||||
"vfile": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
|
||||
|
||||
"vfile-message": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
|
||||
|
||||
"web-tree-sitter": ["[email protected]", "", { "peerDependencies": { "@types/emscripten": "^1.40.0" }, "optionalPeers": ["@types/emscripten"] }, "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA=="],
|
||||
|
||||
"which": ["[email protected]", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"widest-line": ["[email protected]", "", { "dependencies": { "string-width": "^8.1.0" } }, "sha512-U89AsyEeAsyoF0zVJBkG9zBgekjgjK7yk9sje3F4IQpXBJ10TF6ByLlIfjMhcmHMJgHZI4KHt4rdNfktzxIAMA=="],
|
||||
|
||||
"wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^6.2.3", "string-width": "^8.2.0", "strip-ansi": "^7.1.2" } }, "sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ=="],
|
||||
|
||||
"ws": ["[email protected]", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="],
|
||||
|
||||
"yallist": ["[email protected]", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
"yoctocolors": ["[email protected]", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="],
|
||||
|
||||
"yoga-layout": ["[email protected]", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
|
||||
@@ -259,12 +381,16 @@
|
||||
|
||||
"zwitch": ["[email protected]", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||
|
||||
"ink/signal-exit": ["[email protected]", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
||||
|
||||
"ink-text-input/type-fest": ["[email protected]", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
||||
"babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/[email protected]", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
|
||||
|
||||
"npm-run-path/path-key": ["[email protected]", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
|
||||
|
||||
"restore-cursor/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
||||
"parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
|
||||
|
||||
"path-scurry/lru-cache": ["[email protected]", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"path-scurry/minipass": ["[email protected]", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
|
||||
|
||||
"string-width/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
|
||||
}
|
||||
}
|
||||
+6
-8
@@ -7,26 +7,24 @@
|
||||
"zero": "src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "bun run src/index.ts",
|
||||
"dev": "bun --preload @opentui/solid/preload src/index.ts",
|
||||
"test": "bun test ./tests --timeout 15000",
|
||||
"build": "bun build src/index.ts --compile --outfile zero"
|
||||
"build": "bun run scripts/build.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"@types/react": "^19.2.15"
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@opentui/core": "^0.2.16",
|
||||
"@opentui/solid": "^0.2.16",
|
||||
"commander": "^15.0.0",
|
||||
"execa": "^9.6.1",
|
||||
"ink": "^7.0.4",
|
||||
"ink-spinner": "^5.0.0",
|
||||
"ink-text-input": "^6.0.0",
|
||||
"openai": "^6.39.1",
|
||||
"react": "^19.2.6",
|
||||
"shiki": "^4.1.0",
|
||||
"solid-js": "1.9.12",
|
||||
"zod": "^4.4.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bun
|
||||
import solidPlugin from '@opentui/solid/bun-plugin';
|
||||
|
||||
const result = await Bun.build({
|
||||
entrypoints: ['./src/index.ts'],
|
||||
target: 'bun',
|
||||
plugins: [solidPlugin],
|
||||
outdir: './dist',
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
for (const log of result.logs) {
|
||||
console.error(log);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
+40
-39
@@ -2,7 +2,7 @@ import type { Provider } from '../providers/types';
|
||||
import type { ToolCall, ToolResult } from '../tools/types';
|
||||
import { toolRegistry } from '../tools';
|
||||
import { DEFAULT_SYSTEM_PROMPT, PLAN_MODE_SYSTEM_PROMPT } from './prompts';
|
||||
import { clearPlan } from '../tools/plan';
|
||||
import { clearPlan, getCurrentPlan, type PlanItem } from '../tools/plan';
|
||||
import { z } from 'zod';
|
||||
|
||||
export interface AgentOptions {
|
||||
@@ -10,9 +10,11 @@ export interface AgentOptions {
|
||||
onText?: (text: string) => void;
|
||||
onToolCall?: (toolCall: ToolCall) => void;
|
||||
onToolResult?: (result: ToolResult) => void;
|
||||
toolsEnabled?: boolean; // allows temporarily disabling tool calling for debugging
|
||||
debug?: boolean; // when true, logs the exact payload sent to the provider
|
||||
planMode?: boolean; // when true, the agent plans without modifying the codebase
|
||||
onUsage?: (usage: { promptTokens: number; completionTokens: number }) => void;
|
||||
onPlanUpdate?: (plan: PlanItem[]) => void;
|
||||
toolsEnabled?: boolean;
|
||||
debug?: boolean;
|
||||
planMode?: boolean;
|
||||
}
|
||||
|
||||
interface PendingToolCall {
|
||||
@@ -26,18 +28,21 @@ export async function runAgent(
|
||||
provider: Provider,
|
||||
options: AgentOptions = {}
|
||||
): Promise<string> {
|
||||
const {
|
||||
maxTurns = 12,
|
||||
onText,
|
||||
onToolCall,
|
||||
const {
|
||||
maxTurns = 12,
|
||||
onText,
|
||||
onToolCall,
|
||||
onToolResult,
|
||||
onUsage,
|
||||
onPlanUpdate,
|
||||
toolsEnabled = true,
|
||||
debug = false,
|
||||
planMode = false
|
||||
planMode = false,
|
||||
} = options;
|
||||
|
||||
// Clear any previous plan when starting a new task
|
||||
clearPlan();
|
||||
onPlanUpdate?.(getCurrentPlan());
|
||||
|
||||
const systemPrompt = planMode ? PLAN_MODE_SYSTEM_PROMPT : DEFAULT_SYSTEM_PROMPT;
|
||||
|
||||
@@ -52,16 +57,9 @@ export async function runAgent(
|
||||
for (let turn = 0; turn < maxTurns; turn++) {
|
||||
const toolDefinitions = (toolsEnabled && tools.length > 0)
|
||||
? tools.map(t => {
|
||||
// Convert Zod schema to proper JSON Schema (critical for many providers).
|
||||
// zod v4 ships this natively — no external package needed.
|
||||
const jsonSchema = z.toJSONSchema(t.parameters, {
|
||||
target: 'draft-7',
|
||||
}) as any;
|
||||
|
||||
// Remove $schema if present (some providers dislike it)
|
||||
const jsonSchema = z.toJSONSchema(t.parameters, { target: 'draft-7' }) as any;
|
||||
delete jsonSchema.$schema;
|
||||
|
||||
// Make it strict by default (good practice)
|
||||
if (jsonSchema.type === 'object' && !('additionalProperties' in jsonSchema)) {
|
||||
jsonSchema.additionalProperties = false;
|
||||
}
|
||||
@@ -80,30 +78,21 @@ export async function runAgent(
|
||||
if (debug) {
|
||||
const red = '\x1b[31m';
|
||||
const reset = '\x1b[0m';
|
||||
const border = '─'.repeat(50);
|
||||
const border = '-'.repeat(50);
|
||||
const toolsList = toolDefinitions.map(t => t.name).join(', ');
|
||||
const preview = String(messages[messages.length - 1]?.content || '').slice(0, 45);
|
||||
|
||||
console.log(`\n${red}┌${border}┐`);
|
||||
console.log(`│ SENDING TO PROVIDER${' '.repeat(31)}│`);
|
||||
console.log(`├${border}┤`);
|
||||
console.log(`│ Messages: ${messages.length}${' '.repeat(40 - String(messages.length).length)}│`);
|
||||
console.log(`│ Tools enabled: ${toolDefinitions.length > 0}${' '.repeat(33)}│`);
|
||||
console.log(`│ Tool count: ${toolDefinitions.length}${' '.repeat(38 - String(toolDefinitions.length).length)}│`);
|
||||
|
||||
if (toolDefinitions.length > 0) {
|
||||
const toolsList = toolDefinitions.map(t => t.name).join(', ');
|
||||
console.log(`│ Tools: ${toolsList.slice(0, 42)}${' '.repeat(Math.max(0, 43 - toolsList.length))}│`);
|
||||
|
||||
// Show a sample of the schema for the first tool (very useful for debugging)
|
||||
const firstTool = toolDefinitions[0];
|
||||
if (firstTool.parameters) {
|
||||
const schemaPreview = JSON.stringify(firstTool.parameters, null, 2).slice(0, 300);
|
||||
console.log(`│ First tool schema sample:\n${schemaPreview}...`);
|
||||
}
|
||||
console.log(`\n${red}+${border}+`);
|
||||
console.log(`| SENDING TO PROVIDER${' '.repeat(31)}|`);
|
||||
console.log(`+${border}+`);
|
||||
console.log(`| Messages: ${messages.length}${' '.repeat(Math.max(0, 40 - String(messages.length).length))}|`);
|
||||
console.log(`| Tools enabled: ${toolDefinitions.length > 0}${' '.repeat(33)}|`);
|
||||
console.log(`| Tool count: ${toolDefinitions.length}${' '.repeat(Math.max(0, 38 - String(toolDefinitions.length).length))}|`);
|
||||
if (toolsList) {
|
||||
console.log(`| Tools: ${toolsList.slice(0, 42)}${' '.repeat(Math.max(0, 43 - toolsList.length))}|`);
|
||||
}
|
||||
|
||||
const preview = String(messages[messages.length-1]?.content || '').slice(0, 45);
|
||||
console.log(`│ Last message: ${preview}${' '.repeat(Math.max(0, 36 - preview.length))}│`);
|
||||
console.log(`└${border}┘${reset}\n`);
|
||||
console.log(`| Last message: ${preview}${' '.repeat(Math.max(0, 36 - preview.length))}|`);
|
||||
console.log(`+${border}+${reset}\n`);
|
||||
}
|
||||
|
||||
// Stream the response
|
||||
@@ -113,6 +102,13 @@ export async function runAgent(
|
||||
if (onText) onText(event.content);
|
||||
}
|
||||
|
||||
if (event.type === 'usage') {
|
||||
onUsage?.({
|
||||
promptTokens: event.promptTokens,
|
||||
completionTokens: event.completionTokens,
|
||||
});
|
||||
}
|
||||
|
||||
if (event.type === 'tool-call-start') {
|
||||
toolCallMap.set(event.id, {
|
||||
id: event.id,
|
||||
@@ -189,6 +185,11 @@ export async function runAgent(
|
||||
onToolResult({ toolCallId: tc.id, result });
|
||||
}
|
||||
|
||||
const effects = tool?.onAfterExecute?.(result);
|
||||
if (effects?.planUpdated) {
|
||||
onPlanUpdate?.(getCurrentPlan());
|
||||
}
|
||||
|
||||
return { toolCallId: tc.id, result };
|
||||
});
|
||||
|
||||
|
||||
+10
-3
@@ -1,14 +1,21 @@
|
||||
import { Command } from 'commander';
|
||||
import { ensureSolidTransformPlugin } from '@opentui/solid/bun-plugin';
|
||||
import packageJson from '../package.json';
|
||||
import { runHeadless } from './cli';
|
||||
import { configManager } from './config/manager';
|
||||
import { startTUI } from './tui';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
async function startInteractiveTUI() {
|
||||
ensureSolidTransformPlugin();
|
||||
const { startTUI } = await import('./tui');
|
||||
startTUI();
|
||||
}
|
||||
|
||||
program
|
||||
.name('zero')
|
||||
.description('A clean terminal AI coding agent')
|
||||
.version('0.1.0');
|
||||
.version(packageJson.version);
|
||||
|
||||
program
|
||||
.option('-p, --prompt <prompt>', 'Run in headless mode with the given prompt')
|
||||
@@ -17,7 +24,7 @@ program
|
||||
await runHeadless(options.prompt);
|
||||
} else {
|
||||
// Launch the interactive TUI (Grok Build style)
|
||||
startTUI();
|
||||
await startInteractiveTUI();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+85
-41
@@ -7,6 +7,28 @@ interface OpenAIProviderOptions {
|
||||
model: string;
|
||||
}
|
||||
|
||||
function isUnsupportedStreamOptionsError(error: unknown): boolean {
|
||||
const candidate = error as {
|
||||
status?: number;
|
||||
code?: string;
|
||||
message?: string;
|
||||
error?: {
|
||||
code?: string;
|
||||
message?: string;
|
||||
};
|
||||
};
|
||||
|
||||
const statusAllowsRetry = candidate.status === 400 || candidate.status === 422;
|
||||
const errorText = [
|
||||
candidate.code,
|
||||
candidate.message,
|
||||
candidate.error?.code,
|
||||
candidate.error?.message,
|
||||
].filter(Boolean).join(' ').toLowerCase();
|
||||
|
||||
return statusAllowsRetry && /stream[ _-]?options?/.test(errorText);
|
||||
}
|
||||
|
||||
export class OpenAIProvider implements Provider {
|
||||
private client: OpenAI;
|
||||
private model: string;
|
||||
@@ -48,23 +70,31 @@ export class OpenAIProvider implements Provider {
|
||||
}))
|
||||
: undefined;
|
||||
|
||||
const createStreamRequest = (includeUsage: boolean) => ({
|
||||
model: this.model,
|
||||
messages: openaiMessages as any,
|
||||
tools: openaiTools,
|
||||
stream: true as const,
|
||||
...(includeUsage ? {
|
||||
stream_options: {
|
||||
include_usage: true,
|
||||
},
|
||||
} : {}),
|
||||
});
|
||||
|
||||
let stream;
|
||||
try {
|
||||
stream = await this.client.chat.completions.create({
|
||||
model: this.model,
|
||||
messages: openaiMessages as any,
|
||||
tools: openaiTools,
|
||||
stream: true,
|
||||
});
|
||||
} catch (err: any) {
|
||||
const message = getDetailedErrorMessage(err);
|
||||
if (message.includes('401') || message.toLowerCase().includes('invalid') || message.toLowerCase().includes('unauthorized')) {
|
||||
throw new Error(`Provider authentication error (check your API key): ${message}`);
|
||||
stream = await this.client.chat.completions.create(createStreamRequest(true));
|
||||
} catch (error) {
|
||||
if (isUnsupportedStreamOptionsError(error)) {
|
||||
try {
|
||||
stream = await this.client.chat.completions.create(createStreamRequest(false));
|
||||
} catch (retryError) {
|
||||
throw formatProviderCreateError(retryError);
|
||||
}
|
||||
} else {
|
||||
throw formatProviderCreateError(error);
|
||||
}
|
||||
if (message.toLowerCase().includes('rate') || message.toLowerCase().includes('quota')) {
|
||||
throw new Error(`Provider rate limit error: ${message}`);
|
||||
}
|
||||
throw new Error(`Provider returned error: ${message}`);
|
||||
}
|
||||
|
||||
const toolCallAccumulators = new Map<number, {
|
||||
@@ -76,11 +106,9 @@ export class OpenAIProvider implements Provider {
|
||||
|
||||
try {
|
||||
for await (const chunk of stream) {
|
||||
// Some OpenAI-compatible servers send errors as special chunks
|
||||
if ((chunk as any).error) {
|
||||
const errData = (chunk as any).error;
|
||||
const msg = errData.message || JSON.stringify(errData);
|
||||
throw new Error(`Provider returned error: ${msg}`);
|
||||
const errorData = (chunk as any).error;
|
||||
throw formatProviderCreateError(errorData);
|
||||
}
|
||||
|
||||
const delta = chunk.choices[0]?.delta;
|
||||
@@ -100,7 +128,7 @@ export class OpenAIProvider implements Provider {
|
||||
toolCallAccumulators.set(tc.index, acc);
|
||||
}
|
||||
|
||||
// If we already had data at this index and now get a new id,
|
||||
// If we already had data at this index and now get a new id,
|
||||
// the previous tool call is complete.
|
||||
if (tc.id && acc.id && acc.id !== tc.id) {
|
||||
if (acc.id) {
|
||||
@@ -155,36 +183,52 @@ export class OpenAIProvider implements Provider {
|
||||
}
|
||||
|
||||
yield { type: 'done' };
|
||||
} catch (err: any) {
|
||||
const message = getDetailedErrorMessage(err);
|
||||
throw new Error(`Provider returned error during streaming: ${message}`);
|
||||
} catch (error) {
|
||||
throw formatProviderCreateError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getDetailedErrorMessage(err: any): string {
|
||||
if (!err) return 'Unknown error';
|
||||
function formatProviderCreateError(error: unknown): Error {
|
||||
const message = getDetailedErrorMessage(error);
|
||||
const lower = message.toLowerCase();
|
||||
|
||||
// Try common places where real error lives (especially for custom gateways)
|
||||
if (err.message && !err.message.includes('Provider returned error')) {
|
||||
return err.message;
|
||||
if (message.includes('401') || lower.includes('invalid') || lower.includes('unauthorized') || lower.includes('api key')) {
|
||||
return new Error(`Provider authentication error (check your API key): ${message}`);
|
||||
}
|
||||
|
||||
if (err.error) {
|
||||
if (typeof err.error === 'string') return err.error;
|
||||
if (err.error.message) return err.error.message;
|
||||
try { return JSON.stringify(err.error); } catch {}
|
||||
if (lower.includes('rate') || lower.includes('quota')) {
|
||||
return new Error(`Provider rate limit error: ${message}`);
|
||||
}
|
||||
|
||||
if (err.response?.data) {
|
||||
const data = err.response.data;
|
||||
if (data.error?.message) return data.error.message;
|
||||
if (typeof data.error === 'string') return data.error;
|
||||
try { return JSON.stringify(data); } catch {}
|
||||
}
|
||||
|
||||
if (err.cause?.message) return err.cause.message;
|
||||
|
||||
return err.message || String(err);
|
||||
return new Error(`Provider returned error: ${message}`);
|
||||
}
|
||||
|
||||
function getDetailedErrorMessage(error: any): string {
|
||||
if (!error) return 'Unknown error';
|
||||
|
||||
if (error.message && !error.message.includes('Provider returned error')) {
|
||||
return error.message;
|
||||
}
|
||||
|
||||
if (error.error) {
|
||||
if (typeof error.error === 'string') return error.error;
|
||||
if (error.error.message) return error.error.message;
|
||||
try {
|
||||
return JSON.stringify(error.error);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (error.response?.data) {
|
||||
const data = error.response.data;
|
||||
if (data.error?.message) return data.error.message;
|
||||
if (typeof data.error === 'string') return data.error;
|
||||
try {
|
||||
return JSON.stringify(data);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (error.cause?.message) return error.cause.message;
|
||||
|
||||
return error.message || String(error);
|
||||
}
|
||||
@@ -59,6 +59,9 @@ The plan should be clear, actionable, and ordered. Keep it up to date as you wor
|
||||
|
||||
return formatPlan(currentPlan);
|
||||
},
|
||||
onAfterExecute() {
|
||||
return { planUpdated: true };
|
||||
},
|
||||
};
|
||||
|
||||
// Helper to get the current plan (can be used by other parts of the system later)
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export interface ToolExecutionEffects {
|
||||
planUpdated?: boolean;
|
||||
}
|
||||
|
||||
export interface Tool {
|
||||
name: string;
|
||||
description: string;
|
||||
parameters: z.ZodObject<any>; // Zod schema for validation
|
||||
execute: (args: any) => Promise<string>; // Returns tool result as string
|
||||
onAfterExecute?: (result: string) => ToolExecutionEffects | void;
|
||||
}
|
||||
|
||||
export interface ToolCall {
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Box, Text, useInput } from 'ink';
|
||||
import TextInput from 'ink-text-input';
|
||||
import { configManager } from '../config/manager';
|
||||
|
||||
type AddMode = 'choose' | 'opengateway' | 'generic';
|
||||
|
||||
interface AddProviderProps {
|
||||
onDone: (providerName?: string) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
export const AddProvider: React.FC<AddProviderProps> = ({ onDone, onCancel }) => {
|
||||
const [mode, setMode] = useState<AddMode>('choose');
|
||||
|
||||
// For the choose menu
|
||||
const [selectedOption, setSelectedOption] = useState(0); // 0 = OpenGateway, 1 = Generic
|
||||
|
||||
// For the OpenGateway form (step based)
|
||||
const [ogwFormStep, setOgwFormStep] = useState(0); // 0 = API Key, 1 = Model, 2 = Done
|
||||
|
||||
// OpenGateway fields
|
||||
const [ogwKey, setOgwKey] = useState('');
|
||||
const [ogwModel, setOgwModel] = useState('mimo-v2.5-pro');
|
||||
|
||||
// Generic fields
|
||||
const [name, setName] = useState('');
|
||||
const [baseURL, setBaseURL] = useState('https://api.openai.com/v1');
|
||||
const [apiKey, setApiKey] = useState('');
|
||||
const [model, setModel] = useState('gpt-4o');
|
||||
|
||||
const [error, setError] = useState('');
|
||||
const [success, setSuccess] = useState(false);
|
||||
|
||||
useInput((input, key) => {
|
||||
if (key.escape) {
|
||||
if (mode === 'choose') {
|
||||
onCancel();
|
||||
} else {
|
||||
// Reset states when going back
|
||||
setMode('choose');
|
||||
setOgwFormStep(0);
|
||||
setError('');
|
||||
setSuccess(false);
|
||||
setSelectedOption(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Arrow key + Enter navigation for the choose menu
|
||||
if (mode === 'choose') {
|
||||
if (key.upArrow) {
|
||||
setSelectedOption((prev) => Math.max(0, prev - 1));
|
||||
return;
|
||||
}
|
||||
if (key.downArrow) {
|
||||
setSelectedOption((prev) => Math.min(1, prev + 1));
|
||||
return;
|
||||
}
|
||||
if (key.return) {
|
||||
if (selectedOption === 0) {
|
||||
setMode('opengateway');
|
||||
setOgwFormStep(0);
|
||||
} else {
|
||||
setMode('generic');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Still support numbers for convenience
|
||||
if (input === '1') {
|
||||
setMode('opengateway');
|
||||
setOgwFormStep(0);
|
||||
}
|
||||
if (input === '2') {
|
||||
setMode('generic');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const saveOpenGateway = () => {
|
||||
if (!ogwKey.trim()) {
|
||||
setError('API key is required');
|
||||
return;
|
||||
}
|
||||
|
||||
const profileName = 'opengateway';
|
||||
|
||||
configManager.addProvider({
|
||||
name: profileName,
|
||||
baseURL: 'https://opengateway.gitlawb.com/v1',
|
||||
apiKey: ogwKey.trim(),
|
||||
model: ogwModel.trim(),
|
||||
description: 'OpenGateway',
|
||||
});
|
||||
|
||||
setSuccess(true);
|
||||
|
||||
// Auto-close after a short delay so the user sees the success message
|
||||
setTimeout(() => {
|
||||
onDone(profileName);
|
||||
}, 1200);
|
||||
};
|
||||
|
||||
const saveGeneric = () => {
|
||||
if (!name.trim() || !baseURL.trim() || !model.trim()) {
|
||||
setError('Name, Base URL, and Model are required');
|
||||
return;
|
||||
}
|
||||
|
||||
configManager.addProvider({
|
||||
name: name.trim(),
|
||||
baseURL: baseURL.trim(),
|
||||
apiKey: apiKey.trim() || undefined,
|
||||
model: model.trim(),
|
||||
description: 'Custom OpenAI-compatible',
|
||||
});
|
||||
|
||||
setSuccess(true);
|
||||
setTimeout(() => onDone(name.trim()), 1200);
|
||||
};
|
||||
|
||||
if (mode === 'choose') {
|
||||
return (
|
||||
<Box flexDirection="column" padding={1}>
|
||||
<Text bold color="cyan">Add New Provider</Text>
|
||||
<Text color="gray">Esc to go back • ↑↓ to navigate • Enter to select</Text>
|
||||
|
||||
<Box marginY={1} flexDirection="column">
|
||||
<Text color={selectedOption === 0 ? 'greenBright' : 'white'}>
|
||||
{selectedOption === 0 ? '› ' : ' '}1. Add OpenGateway (recommended)
|
||||
</Text>
|
||||
{selectedOption === 0 && (
|
||||
<Text color="gray" dimColor>
|
||||
{' '}You'll be asked for your ogw_live_... API key
|
||||
</Text>
|
||||
)}
|
||||
|
||||
<Text marginTop={1} color={selectedOption === 1 ? 'greenBright' : 'white'}>
|
||||
{selectedOption === 1 ? '› ' : ' '}2. Add custom OpenAI-compatible provider
|
||||
</Text>
|
||||
{selectedOption === 1 && (
|
||||
<Text color="gray" dimColor>
|
||||
{' '}For Groq, OpenAI, Ollama, etc.
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (mode === 'opengateway') {
|
||||
if (success) {
|
||||
return (
|
||||
<Box flexDirection="column" padding={1}>
|
||||
<Text color="greenBright" bold>
|
||||
✓ OpenGateway provider added successfully!
|
||||
</Text>
|
||||
<Text color="gray" dimColor>
|
||||
It is now your active provider.
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" padding={1}>
|
||||
<Text bold color="cyan">Add OpenGateway Provider</Text>
|
||||
<Text color="gray">Esc to go back</Text>
|
||||
|
||||
{ogwFormStep === 0 && (
|
||||
<Box marginTop={1} flexDirection="column">
|
||||
<Text color="yellowBright">Step 1/2 — Enter your OpenGateway API key</Text>
|
||||
<Text color="gray" dimColor>
|
||||
You can get one at https://opengateway.gitlawb.com
|
||||
</Text>
|
||||
<Box marginTop={1}>
|
||||
<Text>API Key: </Text>
|
||||
<TextInput
|
||||
value={ogwKey}
|
||||
onChange={setOgwKey}
|
||||
mask="*"
|
||||
placeholder="ogw_live_..."
|
||||
/>
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<Text color="gray" dimColor>Press Enter to continue</Text>
|
||||
</Box>
|
||||
<TextInput
|
||||
value=""
|
||||
onChange={() => {}}
|
||||
onSubmit={() => {
|
||||
if (ogwKey.trim()) {
|
||||
setOgwFormStep(1);
|
||||
setError('');
|
||||
} else {
|
||||
setError('API key cannot be empty');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{ogwFormStep === 1 && (
|
||||
<Box marginTop={1} flexDirection="column">
|
||||
<Text color="yellowBright">Step 2/2 — Model name</Text>
|
||||
<Box marginTop={1}>
|
||||
<Text>Model: </Text>
|
||||
<TextInput value={ogwModel} onChange={setOgwModel} />
|
||||
</Box>
|
||||
{error && <Text color="red">⚠ {error}</Text>}
|
||||
<Box marginTop={1}>
|
||||
<Text color="gray" dimColor>Press Enter to save</Text>
|
||||
</Box>
|
||||
<TextInput
|
||||
value=""
|
||||
onChange={() => {}}
|
||||
onSubmit={saveOpenGateway}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (mode === 'generic') {
|
||||
if (success) {
|
||||
return (
|
||||
<Box flexDirection="column" padding={1}>
|
||||
<Text color="greenBright" bold>
|
||||
✓ Provider added successfully!
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" padding={1}>
|
||||
<Text bold color="cyan">Add Custom Provider</Text>
|
||||
<Text color="gray">Esc to go back</Text>
|
||||
|
||||
<Box marginTop={1}>
|
||||
<Text>Name: </Text>
|
||||
<TextInput value={name} onChange={setName} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text>Base URL: </Text>
|
||||
<TextInput value={baseURL} onChange={setBaseURL} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text>API Key: </Text>
|
||||
<TextInput value={apiKey} onChange={setApiKey} mask="*" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text>Model: </Text>
|
||||
<TextInput value={model} onChange={setModel} />
|
||||
</Box>
|
||||
|
||||
{error && <Text color="red">{error}</Text>}
|
||||
|
||||
<Box marginTop={1}>
|
||||
<Text color="gray">Press Enter to save</Text>
|
||||
</Box>
|
||||
|
||||
<TextInput
|
||||
value=""
|
||||
onChange={() => {}}
|
||||
onSubmit={saveGeneric}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
+1366
-600
File diff suppressed because it is too large.
Load diff
@@ -1,33 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
|
||||
export const Logo: React.FC = () => {
|
||||
return (
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text color="cyanBright" bold>
|
||||
{` ███████╗ ███████╗ ██████╗ ██████╗ `}
|
||||
</Text>
|
||||
<Text color="cyanBright" bold>
|
||||
{` ╚══███╔╝ ██╔════╝ ██╔══██╗ ██╔═══██╗`}
|
||||
</Text>
|
||||
<Text color="cyanBright" bold>
|
||||
{` ███╔╝ █████╗ ██████╔╝ ██║ ██║`}
|
||||
</Text>
|
||||
<Text color="cyanBright" bold>
|
||||
{` ███╔╝ ██╔══╝ ██╔══██╗ ██║ ██║`}
|
||||
</Text>
|
||||
<Text color="cyanBright" bold>
|
||||
{` ███████╗ ███████╗ ██║ ██║ ╚██████╔╝`}
|
||||
</Text>
|
||||
<Text color="cyanBright" bold>
|
||||
{` ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ `}
|
||||
</Text>
|
||||
|
||||
<Box marginTop={1}>
|
||||
<Text color="gray" dimColor>
|
||||
A clean terminal AI coding agent
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -1,218 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { highlightCode } from './highlighter';
|
||||
|
||||
// Formatter for model responses: paragraphs, inline code, and highlighted code blocks
|
||||
interface MessageRendererProps {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export const MessageRenderer: React.FC<MessageRendererProps> = ({ content }) => {
|
||||
const [highlightedBlocks, setHighlightedBlocks] = useState<Map<number, string>>(new Map());
|
||||
|
||||
const codeBlockRegex = /```(\w+)?\n?([\s\S]*?)```/g;
|
||||
const parts: React.ReactNode[] = [];
|
||||
let lastIndex = 0;
|
||||
let match;
|
||||
let blockIndex = 0;
|
||||
|
||||
const matches: Array<{ index: number; lang: string; code: string; fullMatch: string }> = [];
|
||||
|
||||
while ((match = codeBlockRegex.exec(content)) !== null) {
|
||||
matches.push({
|
||||
index: match.index,
|
||||
lang: match[1] || 'text',
|
||||
code: match[2].trim(),
|
||||
fullMatch: match[0],
|
||||
});
|
||||
}
|
||||
|
||||
// Highlight code blocks asynchronously
|
||||
useEffect(() => {
|
||||
const highlightAll = async () => {
|
||||
const newHighlights = new Map<number, string>();
|
||||
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
const m = matches[i];
|
||||
try {
|
||||
const ansi = await highlightCode(m.code, m.lang);
|
||||
newHighlights.set(i, ansi);
|
||||
} catch {
|
||||
newHighlights.set(i, m.code);
|
||||
}
|
||||
}
|
||||
|
||||
setHighlightedBlocks(newHighlights);
|
||||
};
|
||||
|
||||
if (matches.length > 0) {
|
||||
highlightAll();
|
||||
}
|
||||
}, [content]);
|
||||
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
const m = matches[i];
|
||||
|
||||
// Add text before this code block (with paragraph formatting)
|
||||
if (m.index > lastIndex) {
|
||||
const textBefore = content.slice(lastIndex, m.index);
|
||||
parts.push(renderTextBlock(textBefore, `text-${parts.length}`));
|
||||
}
|
||||
|
||||
const highlighted = highlightedBlocks.get(i);
|
||||
|
||||
parts.push(
|
||||
<Box
|
||||
key={`code-${i}`}
|
||||
flexDirection="column"
|
||||
borderStyle="single"
|
||||
borderColor="gray"
|
||||
marginTop={1}
|
||||
marginBottom={1}
|
||||
paddingX={1}
|
||||
>
|
||||
{m.lang && m.lang !== 'text' && (
|
||||
<Text color="cyan" dimColor>{m.lang}</Text>
|
||||
)}
|
||||
{highlighted ? (
|
||||
<Text>{highlighted}</Text>
|
||||
) : (
|
||||
<Text color="gray" dimColor>Highlighting...</Text>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
lastIndex = m.index + m.fullMatch.length;
|
||||
blockIndex++;
|
||||
}
|
||||
|
||||
// Add remaining text after last code block (with paragraph formatting)
|
||||
if (lastIndex < content.length) {
|
||||
const remaining = content.slice(lastIndex);
|
||||
parts.push(renderTextBlock(remaining, `text-${parts.length}`));
|
||||
}
|
||||
|
||||
return <>{parts.length > 0 ? parts : renderTextBlock(content, 'full')}</>;
|
||||
};
|
||||
|
||||
// Stronger formatter for model responses (paragraphs + basic lists + inline formatting)
|
||||
function renderTextBlock(text: string, keyPrefix: string): React.ReactNode {
|
||||
const lines = text.split('\n');
|
||||
|
||||
// Group into logical blocks: paragraphs vs lists
|
||||
const blocks: Array<{ type: 'paragraph' | 'list'; lines: string[] }> = [];
|
||||
let current: { type: 'paragraph' | 'list'; lines: string[] } | null = null;
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
const isListItem = /^\s*(\d+\.|[-*+])\s+/.test(line);
|
||||
|
||||
if (isListItem) {
|
||||
if (!current || current.type !== 'list') {
|
||||
if (current) blocks.push(current);
|
||||
current = { type: 'list', lines: [] };
|
||||
}
|
||||
current.lines.push(line);
|
||||
} else {
|
||||
if (!current || current.type !== 'paragraph') {
|
||||
if (current) blocks.push(current);
|
||||
current = { type: 'paragraph', lines: [] };
|
||||
}
|
||||
current.lines.push(line);
|
||||
}
|
||||
}
|
||||
if (current) blocks.push(current);
|
||||
|
||||
return (
|
||||
<Box key={keyPrefix} flexDirection="column">
|
||||
{blocks.map((block, bIndex) => {
|
||||
const isLastBlock = bIndex === blocks.length - 1;
|
||||
|
||||
if (block.type === 'list') {
|
||||
return (
|
||||
<Box
|
||||
key={`${keyPrefix}-list-${bIndex}`}
|
||||
flexDirection="column"
|
||||
marginBottom={isLastBlock ? 0 : 1}
|
||||
>
|
||||
{block.lines.map((line, lIndex) => (
|
||||
<Text key={`${keyPrefix}-l-${lIndex}`}>
|
||||
{formatInline(line)}
|
||||
</Text>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// Paragraph block — join lines and split on double newlines for sub-paragraphs
|
||||
const paragraphText = block.lines.join('\n').trim();
|
||||
const subParagraphs = paragraphText
|
||||
.split(/\n\s*\n/)
|
||||
.map(p => p.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
return (
|
||||
<Box
|
||||
key={`${keyPrefix}-p-${bIndex}`}
|
||||
flexDirection="column"
|
||||
marginBottom={isLastBlock ? 0 : 1}
|
||||
>
|
||||
{subParagraphs.map((p, pIndex) => (
|
||||
<Box key={`${keyPrefix}-sp-${pIndex}`} marginBottom={pIndex < subParagraphs.length - 1 ? 1 : 0}>
|
||||
<Text>{formatInline(p)}</Text>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// Handles inline formatting: **bold**, `inline code`
|
||||
function formatInline(text: string): React.ReactNode {
|
||||
const elements: React.ReactNode[] = [];
|
||||
let remaining = text;
|
||||
let idx = 0;
|
||||
|
||||
// Simple sequential parser for **bold** and `code`
|
||||
while (remaining.length > 0) {
|
||||
// Try bold first
|
||||
const boldMatch = remaining.match(/^\*\*([^*]+)\*\*/);
|
||||
if (boldMatch) {
|
||||
elements.push(
|
||||
<Text key={`bold-${idx++}`} bold color="white">
|
||||
{boldMatch[1]}
|
||||
</Text>
|
||||
);
|
||||
remaining = remaining.slice(boldMatch[0].length);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try inline code
|
||||
const codeMatch = remaining.match(/^`([^`]+)`/);
|
||||
if (codeMatch) {
|
||||
elements.push(
|
||||
<Text key={`code-${idx++}`} color="cyan" backgroundColor="#2d2d2d">
|
||||
{codeMatch[1]}
|
||||
</Text>
|
||||
);
|
||||
remaining = remaining.slice(codeMatch[0].length);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Take plain text until next special char
|
||||
const nextSpecial = remaining.search(/(\*\*|`)/);
|
||||
const plain = nextSpecial === -1 ? remaining : remaining.slice(0, nextSpecial);
|
||||
if (plain) {
|
||||
elements.push(<Text key={`plain-${idx++}`}>{plain}</Text>);
|
||||
remaining = remaining.slice(plain.length);
|
||||
} else {
|
||||
// Fallback single char
|
||||
elements.push(<Text key={`plain-${idx++}`}>{remaining[0]}</Text>);
|
||||
remaining = remaining.slice(1);
|
||||
}
|
||||
}
|
||||
|
||||
return <>{elements}</>;
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Box, Text, useInput } from 'ink';
|
||||
import { configManager } from '../config/manager';
|
||||
import type { ProviderProfile } from '../config/types';
|
||||
|
||||
interface ProviderPickerProps {
|
||||
onSelect: (name: string) => void;
|
||||
onCancel: () => void;
|
||||
onAddNew: () => void;
|
||||
}
|
||||
|
||||
export const ProviderPicker: React.FC<ProviderPickerProps> = ({ onSelect, onCancel, onAddNew }) => {
|
||||
const providers = configManager.listProviders();
|
||||
const activeProvider = configManager.getActiveProvider()?.name;
|
||||
|
||||
const totalItems = providers.length + 1; // +1 for "Add new"
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
|
||||
useInput((input, key) => {
|
||||
if (key.escape || (key.ctrl && input === 'c')) {
|
||||
onCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.upArrow) {
|
||||
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.downArrow) {
|
||||
setSelectedIndex((prev) => Math.min(totalItems - 1, prev + 1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.return) {
|
||||
if (selectedIndex < providers.length) {
|
||||
const selected = providers[selectedIndex];
|
||||
onSelect(selected.name);
|
||||
} else {
|
||||
// Last item = Add new
|
||||
onAddNew();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Quick select by number
|
||||
const num = parseInt(input, 10);
|
||||
if (!isNaN(num) && num >= 1 && num <= totalItems) {
|
||||
if (num <= providers.length) {
|
||||
const selected = providers[num - 1];
|
||||
onSelect(selected.name);
|
||||
} else {
|
||||
onAddNew();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" padding={1}>
|
||||
<Text bold color="cyan">
|
||||
Select Provider
|
||||
</Text>
|
||||
<Text color="gray" dimColor>
|
||||
↑↓ to navigate • Enter to select • Esc to cancel
|
||||
</Text>
|
||||
|
||||
<Box marginY={1} flexDirection="column">
|
||||
{providers.map((provider, index) => {
|
||||
const isSelected = index === selectedIndex;
|
||||
const isActive = provider.name === activeProvider;
|
||||
|
||||
return (
|
||||
<Box key={provider.name} paddingLeft={1}>
|
||||
<Text color={isSelected ? 'green' : 'white'}>
|
||||
{isSelected ? '› ' : ' '}
|
||||
{provider.name}
|
||||
{isActive && <Text color="blue"> (current)</Text>}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Add new option */}
|
||||
<Box paddingLeft={1}>
|
||||
<Text color={selectedIndex === providers.length ? 'green' : 'cyan'}>
|
||||
{selectedIndex === providers.length ? '› ' : ' '}
|
||||
+ Add new provider...
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{providers[selectedIndex] && (
|
||||
<Box flexDirection="column" marginLeft={2} borderStyle="round" paddingX={1}>
|
||||
<Text>
|
||||
<Text bold>Model:</Text> {providers[selectedIndex].model}
|
||||
</Text>
|
||||
<Text>
|
||||
<Text bold>Base URL:</Text> {providers[selectedIndex].baseURL}
|
||||
</Text>
|
||||
{providers[selectedIndex].description && (
|
||||
<Text>
|
||||
<Text bold>Description:</Text> {providers[selectedIndex].description}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Box marginTop={1}>
|
||||
<Text color="gray" dimColor>
|
||||
Press 1-{totalItems} for quick selection
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Text } from 'ink';
|
||||
import Spinner from 'ink-spinner';
|
||||
|
||||
interface ThinkingSpinnerProps {
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export const ThinkingSpinner: React.FC<ThinkingSpinnerProps> = ({ label = 'thinking' }) => {
|
||||
return (
|
||||
<Text color="gray" dimColor>
|
||||
<Spinner type="dots" /> {label}...
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
@@ -1,202 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { highlightCode } from './highlighter';
|
||||
|
||||
interface ToolCallRendererProps {
|
||||
name: string;
|
||||
args: string;
|
||||
result?: string;
|
||||
status?: 'running' | 'success' | 'error';
|
||||
}
|
||||
|
||||
export const ToolCallRenderer: React.FC<ToolCallRendererProps> = ({
|
||||
name,
|
||||
args,
|
||||
result,
|
||||
status = 'success',
|
||||
}) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const [highlightedArgs, setHighlightedArgs] = useState<string | null>(null);
|
||||
const [highlightedResult, setHighlightedResult] = useState<string | null>(null);
|
||||
|
||||
const hasResult = !!result;
|
||||
const isLongResult = hasResult && (result!.length > 400 || result!.split('\n').length > 12);
|
||||
const [showFullResult, setShowFullResult] = useState(false);
|
||||
|
||||
// Generate a nice one-line summary for collapsed state
|
||||
const summary = getToolSummary(name, args);
|
||||
|
||||
// Highlight arguments (only when expanded)
|
||||
useEffect(() => {
|
||||
if (!isExpanded) return;
|
||||
|
||||
const highlight = async () => {
|
||||
try {
|
||||
let jsonArgs = args;
|
||||
try {
|
||||
const parsed = JSON.parse(args);
|
||||
jsonArgs = JSON.stringify(parsed, null, 2);
|
||||
} catch {
|
||||
// not valid JSON
|
||||
}
|
||||
const ansi = await highlightCode(jsonArgs, 'json');
|
||||
setHighlightedArgs(ansi);
|
||||
} catch {
|
||||
setHighlightedArgs(args);
|
||||
}
|
||||
};
|
||||
highlight();
|
||||
}, [args, isExpanded]);
|
||||
|
||||
// Highlight result (only when expanded)
|
||||
useEffect(() => {
|
||||
if (!isExpanded || !result) return;
|
||||
|
||||
const highlight = async () => {
|
||||
try {
|
||||
const looksLikeCode =
|
||||
result.includes('function') ||
|
||||
result.includes('const ') ||
|
||||
result.includes('import ') ||
|
||||
result.includes('=>') ||
|
||||
result.includes('class ');
|
||||
|
||||
const ansi = await highlightCode(result, looksLikeCode ? 'typescript' : 'text');
|
||||
setHighlightedResult(ansi);
|
||||
} catch {
|
||||
setHighlightedResult(result);
|
||||
}
|
||||
};
|
||||
highlight();
|
||||
}, [result, isExpanded]);
|
||||
|
||||
const borderColor =
|
||||
status === 'running' ? 'yellow' : status === 'error' ? 'red' : 'green';
|
||||
|
||||
const statusIcon =
|
||||
status === 'running' ? '⟳' : status === 'error' ? '✕' : '✓';
|
||||
|
||||
const statusColor =
|
||||
status === 'running' ? 'yellow' : status === 'error' ? 'red' : 'green';
|
||||
|
||||
// === COLLAPSED VIEW — clean formatter for model actions ===
|
||||
if (!isExpanded) {
|
||||
const showToggle = args || hasResult;
|
||||
|
||||
return (
|
||||
<Box flexDirection="row" paddingX={1} paddingY={0}>
|
||||
<Text color={statusColor} bold>
|
||||
{statusIcon} {name}
|
||||
</Text>
|
||||
<Text color="gray" dimColor> {summary}</Text>
|
||||
|
||||
{showToggle && (
|
||||
<Text
|
||||
color="cyan"
|
||||
dimColor
|
||||
{...({ onPress: () => setIsExpanded(true) } as any)}
|
||||
>
|
||||
{' '}[show]
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// === EXPANDED VIEW — formatted details ===
|
||||
return (
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="single"
|
||||
borderColor={borderColor}
|
||||
paddingX={0}
|
||||
paddingY={0}
|
||||
>
|
||||
{/* Header */}
|
||||
<Box paddingX={1} flexDirection="row" justifyContent="space-between">
|
||||
<Text color={statusColor} bold>
|
||||
{statusIcon} {name}
|
||||
</Text>
|
||||
<Text
|
||||
color="cyan"
|
||||
dimColor
|
||||
{...({ onPress: () => setIsExpanded(false) } as any)}
|
||||
>
|
||||
[hide]
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
{/* Arguments */}
|
||||
<Box paddingX={1} paddingTop={0} flexDirection="column">
|
||||
<Text color="gray" dimColor bold>args</Text>
|
||||
{highlightedArgs ? (
|
||||
<Text>{highlightedArgs}</Text>
|
||||
) : (
|
||||
<Text color="gray" dimColor>…</Text>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Result */}
|
||||
{hasResult && (
|
||||
<Box paddingX={1} paddingTop={0} flexDirection="column">
|
||||
<Text color="gray" dimColor bold>result</Text>
|
||||
{highlightedResult || result ? (
|
||||
<Text color="green" dimColor>
|
||||
{isLongResult && !showFullResult
|
||||
? (highlightedResult || result!).slice(0, 200) + '...'
|
||||
: (highlightedResult || result)}
|
||||
</Text>
|
||||
) : null}
|
||||
|
||||
{isLongResult && (
|
||||
<Text
|
||||
color="cyan"
|
||||
dimColor
|
||||
{...({ onPress: () => setShowFullResult(!showFullResult) } as any)}
|
||||
>
|
||||
{showFullResult ? ' [less]' : ' [more]'}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
// Smart one-line summary for collapsed tool calls.
|
||||
// We want: "tool name + the command it actually ran" (e.g. "read src/App.tsx", "ls -la .")
|
||||
function getToolSummary(name: string, args: string): string {
|
||||
try {
|
||||
const parsed = JSON.parse(args);
|
||||
|
||||
if (name === 'bash') {
|
||||
const cmd = parsed.command || '';
|
||||
return cmd.length > 65 ? cmd.slice(0, 62) + '...' : cmd;
|
||||
}
|
||||
|
||||
if (name === 'read_file') {
|
||||
const path = parsed.path || '';
|
||||
const short = path.length > 60 ? path.slice(0, 57) + '...' : path;
|
||||
return `read ${short}`;
|
||||
}
|
||||
|
||||
if (name === 'edit_file') {
|
||||
const path = parsed.path || '';
|
||||
const short = path.length > 60 ? path.slice(0, 57) + '...' : path;
|
||||
return `edit ${short}`;
|
||||
}
|
||||
|
||||
// Fallback for future tools
|
||||
const keys = Object.keys(parsed);
|
||||
if (keys.length > 0) {
|
||||
const firstKey = keys[0]!;
|
||||
const val = String((parsed as any)[firstKey] ?? '');
|
||||
const shortVal = val.length > 50 ? val.slice(0, 47) + '...' : val;
|
||||
return `${firstKey}: ${shortVal}`;
|
||||
}
|
||||
|
||||
return args.length > 65 ? args.slice(0, 62) + '...' : args;
|
||||
} catch {
|
||||
return args.length > 65 ? args.slice(0, 62) + '...' : args;
|
||||
}
|
||||
}
|
||||
+56
-29
@@ -1,44 +1,71 @@
|
||||
import { createHighlighter } from 'shiki'
|
||||
import { createHighlighter } from 'shiki';
|
||||
|
||||
let highlighterPromise: ReturnType<typeof createHighlighter> | null = null
|
||||
export type HighlightedToken = {
|
||||
content: string;
|
||||
color?: string;
|
||||
};
|
||||
|
||||
export async function getHighlighter() {
|
||||
if (!highlighterPromise) {
|
||||
highlighterPromise = createHighlighter({
|
||||
themes: ['github-dark', 'github-light'],
|
||||
export type HighlightedLine = HighlightedToken[];
|
||||
|
||||
let highlighterPromise: ReturnType<typeof createHighlighter> | undefined;
|
||||
const highlightCache = new Map<string, Promise<HighlightedLine[]>>();
|
||||
|
||||
function hashCode(input: string) {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
hash = ((hash << 5) - hash + input.charCodeAt(i)) | 0;
|
||||
}
|
||||
return hash.toString(36);
|
||||
}
|
||||
|
||||
export async function highlightCode(code: string, lang = 'text'): Promise<HighlightedLine[]> {
|
||||
const cacheKey = `${lang}:${code.length}:${hashCode(code)}`;
|
||||
const cached = highlightCache.get(cacheKey);
|
||||
if (cached) return cached;
|
||||
|
||||
const promise = tokenizeCode(code, lang);
|
||||
highlightCache.set(cacheKey, promise);
|
||||
return promise;
|
||||
}
|
||||
|
||||
async function tokenizeCode(code: string, lang: string): Promise<HighlightedLine[]> {
|
||||
try {
|
||||
highlighterPromise ??= createHighlighter({
|
||||
themes: ['github-dark'],
|
||||
langs: [
|
||||
'typescript',
|
||||
'bash',
|
||||
'css',
|
||||
'dockerfile',
|
||||
'go',
|
||||
'html',
|
||||
'javascript',
|
||||
'tsx',
|
||||
'jsx',
|
||||
'json',
|
||||
'jsx',
|
||||
'markdown',
|
||||
'python',
|
||||
'rust',
|
||||
'go',
|
||||
'bash',
|
||||
'shell',
|
||||
'markdown',
|
||||
'css',
|
||||
'html',
|
||||
'sql',
|
||||
'yaml',
|
||||
'toml',
|
||||
'dockerfile',
|
||||
'tsx',
|
||||
'typescript',
|
||||
'yaml',
|
||||
],
|
||||
})
|
||||
}
|
||||
return highlighterPromise
|
||||
}
|
||||
});
|
||||
|
||||
export async function highlightCode(code: string, lang: string = 'text'): Promise<string> {
|
||||
try {
|
||||
const highlighter = await getHighlighter()
|
||||
return highlighter.codeToAnsi(code, {
|
||||
lang: lang || 'text',
|
||||
const highlighter = await highlighterPromise;
|
||||
const result = highlighter.codeToTokens(code, {
|
||||
lang: (lang || 'text') as any,
|
||||
theme: 'github-dark',
|
||||
})
|
||||
} catch (error) {
|
||||
// Fallback to plain text if highlighting fails
|
||||
return code
|
||||
});
|
||||
|
||||
return result.tokens.map((line) =>
|
||||
line.map((token) => ({
|
||||
content: token.content,
|
||||
color: token.color,
|
||||
})),
|
||||
);
|
||||
} catch {
|
||||
return code.split(/\r?\n/).map((line) => [{ content: line }]);
|
||||
}
|
||||
}
|
||||
+31
-9
@@ -1,13 +1,35 @@
|
||||
import React from 'react';
|
||||
import { render } from 'ink';
|
||||
/** @jsxImportSource @opentui/solid */
|
||||
import { createCliRenderer } from '@opentui/core';
|
||||
import { render } from '@opentui/solid';
|
||||
import { App } from './App';
|
||||
import { getHighlighter } from './highlighter';
|
||||
|
||||
// Preload Shiki highlighter early for fast syntax highlighting
|
||||
getHighlighter().catch(() => {
|
||||
// Silently fail - we'll fallback gracefully
|
||||
});
|
||||
|
||||
export function startTUI() {
|
||||
render(<App />);
|
||||
void start();
|
||||
}
|
||||
|
||||
async function start() {
|
||||
const renderer = await createCliRenderer({
|
||||
externalOutputMode: 'passthrough',
|
||||
targetFps: 60,
|
||||
gatherStats: false,
|
||||
exitOnCtrlC: false,
|
||||
useMouse: true,
|
||||
enableMouseMovement: true,
|
||||
useKittyKeyboard: {},
|
||||
openConsoleOnError: false,
|
||||
});
|
||||
|
||||
const exit = () => {
|
||||
if (!renderer.isDestroyed) {
|
||||
renderer.setTerminalTitle('');
|
||||
renderer.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
process.once('SIGINT', exit);
|
||||
renderer.once('destroy', () => {
|
||||
process.off('SIGINT', exit);
|
||||
});
|
||||
|
||||
await render(() => <App onExit={exit} />, renderer);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import { afterEach, describe, expect, it, mock } from 'bun:test';
|
||||
|
||||
const createMock: any = mock(async () => {
|
||||
throw new Error('create mock not configured');
|
||||
});
|
||||
|
||||
mock.module('openai', () => ({
|
||||
default: class MockOpenAI {
|
||||
chat = {
|
||||
completions: {
|
||||
create: createMock,
|
||||
},
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
async function* streamFrom(chunks: any[]) {
|
||||
for (const chunk of chunks) {
|
||||
yield chunk;
|
||||
}
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
createMock.mockReset();
|
||||
});
|
||||
|
||||
describe('OpenAIProvider stream options compatibility', () => {
|
||||
it('requests usage and retries without stream_options when a compatible provider rejects it', async () => {
|
||||
const { OpenAIProvider } = await import('../src/providers/openai');
|
||||
const unsupportedStreamOptions = Object.assign(new Error('unknown parameter: stream_options.include_usage'), {
|
||||
status: 400,
|
||||
});
|
||||
|
||||
createMock
|
||||
.mockRejectedValueOnce(unsupportedStreamOptions)
|
||||
.mockResolvedValueOnce(streamFrom([
|
||||
{
|
||||
choices: [{ delta: { content: 'hi' }, finish_reason: 'stop' }],
|
||||
},
|
||||
]));
|
||||
|
||||
const provider = new OpenAIProvider({ apiKey: 'test', model: 'test-model' });
|
||||
const events = [];
|
||||
|
||||
for await (const event of provider.streamCompletion([{ role: 'user', content: 'hello' }], [])) {
|
||||
events.push(event);
|
||||
}
|
||||
|
||||
expect(createMock).toHaveBeenCalledTimes(2);
|
||||
expect(createMock.mock.calls[0]?.[0]?.stream_options).toEqual({ include_usage: true });
|
||||
expect(createMock.mock.calls[1]?.[0]?.stream_options).toBeUndefined();
|
||||
expect(events).toContainEqual({ type: 'text', content: 'hi' });
|
||||
});
|
||||
|
||||
it('does not retry unrelated unknown-parameter errors', async () => {
|
||||
const { OpenAIProvider } = await import('../src/providers/openai');
|
||||
const unsupportedToolChoice = Object.assign(new Error('unknown parameter: tool_choice'), {
|
||||
status: 400,
|
||||
});
|
||||
|
||||
createMock.mockRejectedValueOnce(unsupportedToolChoice);
|
||||
|
||||
const provider = new OpenAIProvider({ apiKey: 'test', model: 'test-model' });
|
||||
|
||||
await expect((async () => {
|
||||
for await (const _event of provider.streamCompletion([{ role: 'user', content: 'hello' }], [])) {
|
||||
// Drain the stream.
|
||||
}
|
||||
})()).rejects.toThrow('unknown parameter: tool_choice');
|
||||
|
||||
expect(createMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('categorizes mid-stream authentication errors', async () => {
|
||||
const { OpenAIProvider } = await import('../src/providers/openai');
|
||||
|
||||
createMock.mockResolvedValueOnce(streamFrom([
|
||||
{
|
||||
error: {
|
||||
message: 'Incorrect API key provided',
|
||||
},
|
||||
},
|
||||
]));
|
||||
|
||||
const provider = new OpenAIProvider({ apiKey: 'test', model: 'test-model' });
|
||||
|
||||
await expect((async () => {
|
||||
for await (const _event of provider.streamCompletion([{ role: 'user', content: 'hello' }], [])) {
|
||||
// Drain the stream.
|
||||
}
|
||||
})()).rejects.toThrow('Provider authentication error');
|
||||
});
|
||||
|
||||
it('emits usage events from compatible OpenAI streams', async () => {
|
||||
const { OpenAIProvider } = await import('../src/providers/openai');
|
||||
|
||||
createMock.mockResolvedValueOnce(streamFrom([
|
||||
{
|
||||
choices: [{ delta: {}, finish_reason: 'stop' }],
|
||||
usage: {
|
||||
prompt_tokens: 8,
|
||||
completion_tokens: 3,
|
||||
},
|
||||
},
|
||||
]));
|
||||
|
||||
const provider = new OpenAIProvider({ apiKey: 'test', model: 'test-model' });
|
||||
const events = [];
|
||||
|
||||
for await (const event of provider.streamCompletion([{ role: 'user', content: 'hello' }], [])) {
|
||||
events.push(event);
|
||||
}
|
||||
|
||||
expect(createMock.mock.calls[0]?.[0]?.stream_options).toEqual({ include_usage: true });
|
||||
expect(events).toContainEqual({ type: 'usage', promptTokens: 8, completionTokens: 3 });
|
||||
});
|
||||
});
|
||||
+2
-1
@@ -5,7 +5,8 @@
|
||||
"target": "ESNext",
|
||||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "@opentui/solid",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
|
||||
Reference in new issue
Block a user