@andrew.ng/symbion 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +125 -0
- package/apps/daemon/dist/boot/menu.d.ts +11 -0
- package/apps/daemon/dist/boot/menu.d.ts.map +1 -0
- package/apps/daemon/dist/boot/menu.js +32 -0
- package/apps/daemon/dist/boot/menu.js.map +1 -0
- package/apps/daemon/dist/fs/folderPick.d.ts +13 -0
- package/apps/daemon/dist/fs/folderPick.d.ts.map +1 -0
- package/apps/daemon/dist/fs/folderPick.js +4 -0
- package/apps/daemon/dist/fs/folderPick.js.map +1 -0
- package/apps/daemon/dist/fs/listDir.d.ts +13 -0
- package/apps/daemon/dist/fs/listDir.d.ts.map +1 -0
- package/apps/daemon/dist/fs/listDir.js +121 -0
- package/apps/daemon/dist/fs/listDir.js.map +1 -0
- package/apps/daemon/dist/fs/readTargetFiles.d.ts +20 -0
- package/apps/daemon/dist/fs/readTargetFiles.d.ts.map +1 -0
- package/apps/daemon/dist/fs/readTargetFiles.js +76 -0
- package/apps/daemon/dist/fs/readTargetFiles.js.map +1 -0
- package/apps/daemon/dist/fs/writeFiles.d.ts +32 -0
- package/apps/daemon/dist/fs/writeFiles.d.ts.map +1 -0
- package/apps/daemon/dist/fs/writeFiles.js +93 -0
- package/apps/daemon/dist/fs/writeFiles.js.map +1 -0
- package/apps/daemon/dist/git/status.d.ts +11 -0
- package/apps/daemon/dist/git/status.d.ts.map +1 -0
- package/apps/daemon/dist/git/status.js +30 -0
- package/apps/daemon/dist/git/status.js.map +1 -0
- package/apps/daemon/dist/index.d.ts +2 -0
- package/apps/daemon/dist/index.d.ts.map +1 -0
- package/apps/daemon/dist/index.js +69 -0
- package/apps/daemon/dist/index.js.map +1 -0
- package/apps/daemon/dist/llm/anthropicProvider.d.ts +26 -0
- package/apps/daemon/dist/llm/anthropicProvider.d.ts.map +1 -0
- package/apps/daemon/dist/llm/anthropicProvider.js +90 -0
- package/apps/daemon/dist/llm/anthropicProvider.js.map +1 -0
- package/apps/daemon/dist/llm/geminiProvider.d.ts +25 -0
- package/apps/daemon/dist/llm/geminiProvider.d.ts.map +1 -0
- package/apps/daemon/dist/llm/geminiProvider.js +89 -0
- package/apps/daemon/dist/llm/geminiProvider.js.map +1 -0
- package/apps/daemon/dist/llm/installInstructions.d.ts +40 -0
- package/apps/daemon/dist/llm/installInstructions.d.ts.map +1 -0
- package/apps/daemon/dist/llm/installInstructions.js +80 -0
- package/apps/daemon/dist/llm/installInstructions.js.map +1 -0
- package/apps/daemon/dist/llm/ollamaProvider.d.ts +55 -0
- package/apps/daemon/dist/llm/ollamaProvider.d.ts.map +1 -0
- package/apps/daemon/dist/llm/ollamaProvider.js +213 -0
- package/apps/daemon/dist/llm/ollamaProvider.js.map +1 -0
- package/apps/daemon/dist/llm/openaiProvider.d.ts +25 -0
- package/apps/daemon/dist/llm/openaiProvider.d.ts.map +1 -0
- package/apps/daemon/dist/llm/openaiProvider.js +90 -0
- package/apps/daemon/dist/llm/openaiProvider.js.map +1 -0
- package/apps/daemon/dist/llm/providerStatus.d.ts +60 -0
- package/apps/daemon/dist/llm/providerStatus.d.ts.map +1 -0
- package/apps/daemon/dist/llm/providerStatus.js +88 -0
- package/apps/daemon/dist/llm/providerStatus.js.map +1 -0
- package/apps/daemon/dist/llm/registry.d.ts +14 -0
- package/apps/daemon/dist/llm/registry.d.ts.map +1 -0
- package/apps/daemon/dist/llm/registry.js +35 -0
- package/apps/daemon/dist/llm/registry.js.map +1 -0
- package/apps/daemon/dist/llm/remoteProvider.d.ts +25 -0
- package/apps/daemon/dist/llm/remoteProvider.d.ts.map +1 -0
- package/apps/daemon/dist/llm/remoteProvider.js +88 -0
- package/apps/daemon/dist/llm/remoteProvider.js.map +1 -0
- package/apps/daemon/dist/llm/secrets.d.ts +55 -0
- package/apps/daemon/dist/llm/secrets.d.ts.map +1 -0
- package/apps/daemon/dist/llm/secrets.js +167 -0
- package/apps/daemon/dist/llm/secrets.js.map +1 -0
- package/apps/daemon/dist/llm/types.d.ts +50 -0
- package/apps/daemon/dist/llm/types.d.ts.map +1 -0
- package/apps/daemon/dist/llm/types.js +16 -0
- package/apps/daemon/dist/llm/types.js.map +1 -0
- package/apps/daemon/dist/net/findOpenPort.d.ts +26 -0
- package/apps/daemon/dist/net/findOpenPort.d.ts.map +1 -0
- package/apps/daemon/dist/net/findOpenPort.js +37 -0
- package/apps/daemon/dist/net/findOpenPort.js.map +1 -0
- package/apps/daemon/dist/rpc/contract.d.ts +10 -0
- package/apps/daemon/dist/rpc/contract.d.ts.map +1 -0
- package/apps/daemon/dist/rpc/contract.js +2 -0
- package/apps/daemon/dist/rpc/contract.js.map +1 -0
- package/apps/daemon/dist/rpc/guard.d.ts +18 -0
- package/apps/daemon/dist/rpc/guard.d.ts.map +1 -0
- package/apps/daemon/dist/rpc/guard.js +75 -0
- package/apps/daemon/dist/rpc/guard.js.map +1 -0
- package/apps/daemon/dist/rpc/handlers.d.ts +118 -0
- package/apps/daemon/dist/rpc/handlers.d.ts.map +1 -0
- package/apps/daemon/dist/rpc/handlers.js +694 -0
- package/apps/daemon/dist/rpc/handlers.js.map +1 -0
- package/apps/daemon/dist/rpc/pathStyle.d.ts +27 -0
- package/apps/daemon/dist/rpc/pathStyle.d.ts.map +1 -0
- package/apps/daemon/dist/rpc/pathStyle.js +43 -0
- package/apps/daemon/dist/rpc/pathStyle.js.map +1 -0
- package/apps/daemon/dist/rpc/rpcError.d.ts +13 -0
- package/apps/daemon/dist/rpc/rpcError.d.ts.map +1 -0
- package/apps/daemon/dist/rpc/rpcError.js +16 -0
- package/apps/daemon/dist/rpc/rpcError.js.map +1 -0
- package/apps/daemon/dist/server.d.ts +18 -0
- package/apps/daemon/dist/server.d.ts.map +1 -0
- package/apps/daemon/dist/server.js +208 -0
- package/apps/daemon/dist/server.js.map +1 -0
- package/apps/daemon/dist/store/publishLog.d.ts +6 -0
- package/apps/daemon/dist/store/publishLog.d.ts.map +1 -0
- package/apps/daemon/dist/store/publishLog.js +27 -0
- package/apps/daemon/dist/store/publishLog.js.map +1 -0
- package/apps/daemon/dist/store/store.d.ts +35 -0
- package/apps/daemon/dist/store/store.d.ts.map +1 -0
- package/apps/daemon/dist/store/store.js +129 -0
- package/apps/daemon/dist/store/store.js.map +1 -0
- package/apps/daemon/dist/templates/githubFetch.d.ts +49 -0
- package/apps/daemon/dist/templates/githubFetch.d.ts.map +1 -0
- package/apps/daemon/dist/templates/githubFetch.js +168 -0
- package/apps/daemon/dist/templates/githubFetch.js.map +1 -0
- package/apps/web/out/404.html +1 -0
- package/apps/web/out/_next/static/IIw50lkv8jQuwHOBZJifS/_buildManifest.js +1 -0
- package/apps/web/out/_next/static/IIw50lkv8jQuwHOBZJifS/_ssgManifest.js +1 -0
- package/apps/web/out/_next/static/chunks/1dd3208c-6a745fea7bd2124a.js +1 -0
- package/apps/web/out/_next/static/chunks/426-deb95a16531d3b4b.js +25 -0
- package/apps/web/out/_next/static/chunks/528-6e64508688610bfe.js +2 -0
- package/apps/web/out/_next/static/chunks/656-fd1628581ad77b01.js +1 -0
- package/apps/web/out/_next/static/chunks/777-d587bc7cc0fc6be8.js +1 -0
- package/apps/web/out/_next/static/chunks/990-f2445c68c17fa077.js +1 -0
- package/apps/web/out/_next/static/chunks/app/_not-found/page-e0d77774c4eb9aef.js +1 -0
- package/apps/web/out/_next/static/chunks/app/layout-e5023aba6634ce53.js +1 -0
- package/apps/web/out/_next/static/chunks/app/page-a545c902535ac2e3.js +1 -0
- package/apps/web/out/_next/static/chunks/app/settings/page-a68ed4ae2ccb8840.js +1 -0
- package/apps/web/out/_next/static/chunks/app/templates/page-e5f0cefb905e5b72.js +1 -0
- package/apps/web/out/_next/static/chunks/baeaa4ff-28d2e32e4dd119b9.js +1 -0
- package/apps/web/out/_next/static/chunks/eb0351e1-329719bc902eaf71.js +1 -0
- package/apps/web/out/_next/static/chunks/framework-3664cab31236a9fa.js +1 -0
- package/apps/web/out/_next/static/chunks/main-app-d515c42ecb17b8e8.js +1 -0
- package/apps/web/out/_next/static/chunks/main-ff24788874d4ec1b.js +1 -0
- package/apps/web/out/_next/static/chunks/pages/_app-10a93ab5b7c32eb3.js +1 -0
- package/apps/web/out/_next/static/chunks/pages/_error-2d792b2a41857be4.js +1 -0
- package/apps/web/out/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/apps/web/out/_next/static/chunks/webpack-ae7ee8fe97fdceba.js +1 -0
- package/apps/web/out/_next/static/css/0a42edcfc2beb916.css +3 -0
- package/apps/web/out/_next/static/css/a275cc2b185e04f8.css +1 -0
- package/apps/web/out/_next/static/media/011e180705008d6f-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/20535187d867b7b9-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/26d4368bf94c0ec4-s.p.woff2 +0 -0
- package/apps/web/out/_next/static/media/2801417b65625cf5-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/28793f5c5e3d822d-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/5356a6a4f2c8c8d8-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/58f386aa6b1a2a92-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/73cb51aac9c97f90-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/7b19b489dc6743ba-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/7ba5fb2a8c88521c-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/92eeb95d069020cc-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/98e207f02528a563-s.p.woff2 +0 -0
- package/apps/web/out/_next/static/media/99dcf268bda04fe5-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/b3bf17a9041d9433-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/c9c3823090ec8b55-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/d26bbd13d6b70f89-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/d29838c109ef09b4-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/d3ebbfd689654d3a-s.p.woff2 +0 -0
- package/apps/web/out/_next/static/media/db96af6b531dc71f-s.p.woff2 +0 -0
- package/apps/web/out/_next/static/media/e40af3453d7c920a-s.woff2 +0 -0
- package/apps/web/out/_next/static/media/ef4d5661765d0e49-s.woff2 +0 -0
- package/apps/web/out/index.html +1 -0
- package/apps/web/out/index.txt +6 -0
- package/apps/web/out/settings.html +1 -0
- package/apps/web/out/settings.txt +6 -0
- package/apps/web/out/templates.html +1 -0
- package/apps/web/out/templates.txt +6 -0
- package/bin/symbion.mjs +41 -0
- package/node_modules/@symbion/core/dist/adapters/claude.d.ts +3 -0
- package/node_modules/@symbion/core/dist/adapters/claude.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/adapters/claude.js +43 -0
- package/node_modules/@symbion/core/dist/adapters/claude.js.map +1 -0
- package/node_modules/@symbion/core/dist/adapters/codex.d.ts +3 -0
- package/node_modules/@symbion/core/dist/adapters/codex.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/adapters/codex.js +58 -0
- package/node_modules/@symbion/core/dist/adapters/codex.js.map +1 -0
- package/node_modules/@symbion/core/dist/adapters/registry.d.ts +5 -0
- package/node_modules/@symbion/core/dist/adapters/registry.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/adapters/registry.js +14 -0
- package/node_modules/@symbion/core/dist/adapters/registry.js.map +1 -0
- package/node_modules/@symbion/core/dist/adapters/types.d.ts +34 -0
- package/node_modules/@symbion/core/dist/adapters/types.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/adapters/types.js +2 -0
- package/node_modules/@symbion/core/dist/adapters/types.js.map +1 -0
- package/node_modules/@symbion/core/dist/diff/conflict.d.ts +19 -0
- package/node_modules/@symbion/core/dist/diff/conflict.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/diff/conflict.js +20 -0
- package/node_modules/@symbion/core/dist/diff/conflict.js.map +1 -0
- package/node_modules/@symbion/core/dist/diff/diff.d.ts +44 -0
- package/node_modules/@symbion/core/dist/diff/diff.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/diff/diff.js +124 -0
- package/node_modules/@symbion/core/dist/diff/diff.js.map +1 -0
- package/node_modules/@symbion/core/dist/generate/bodyPrompt.d.ts +27 -0
- package/node_modules/@symbion/core/dist/generate/bodyPrompt.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/generate/bodyPrompt.js +55 -0
- package/node_modules/@symbion/core/dist/generate/bodyPrompt.js.map +1 -0
- package/node_modules/@symbion/core/dist/generate/description.d.ts +18 -0
- package/node_modules/@symbion/core/dist/generate/description.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/generate/description.js +164 -0
- package/node_modules/@symbion/core/dist/generate/description.js.map +1 -0
- package/node_modules/@symbion/core/dist/index.d.ts +23 -0
- package/node_modules/@symbion/core/dist/index.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/index.js +24 -0
- package/node_modules/@symbion/core/dist/index.js.map +1 -0
- package/node_modules/@symbion/core/dist/ir/refs.d.ts +11 -0
- package/node_modules/@symbion/core/dist/ir/refs.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/ir/refs.js +25 -0
- package/node_modules/@symbion/core/dist/ir/refs.js.map +1 -0
- package/node_modules/@symbion/core/dist/ir/types.d.ts +107 -0
- package/node_modules/@symbion/core/dist/ir/types.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/ir/types.js +19 -0
- package/node_modules/@symbion/core/dist/ir/types.js.map +1 -0
- package/node_modules/@symbion/core/dist/ir/validate.d.ts +13 -0
- package/node_modules/@symbion/core/dist/ir/validate.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/ir/validate.js +114 -0
- package/node_modules/@symbion/core/dist/ir/validate.js.map +1 -0
- package/node_modules/@symbion/core/dist/parse/scan.d.ts +47 -0
- package/node_modules/@symbion/core/dist/parse/scan.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/parse/scan.js +109 -0
- package/node_modules/@symbion/core/dist/parse/scan.js.map +1 -0
- package/node_modules/@symbion/core/dist/render/frontmatter.d.ts +29 -0
- package/node_modules/@symbion/core/dist/render/frontmatter.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/render/frontmatter.js +99 -0
- package/node_modules/@symbion/core/dist/render/frontmatter.js.map +1 -0
- package/node_modules/@symbion/core/dist/render/marker.d.ts +30 -0
- package/node_modules/@symbion/core/dist/render/marker.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/render/marker.js +48 -0
- package/node_modules/@symbion/core/dist/render/marker.js.map +1 -0
- package/node_modules/@symbion/core/dist/render/render.d.ts +12 -0
- package/node_modules/@symbion/core/dist/render/render.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/render/render.js +10 -0
- package/node_modules/@symbion/core/dist/render/render.js.map +1 -0
- package/node_modules/@symbion/core/dist/runcommand/render.d.ts +13 -0
- package/node_modules/@symbion/core/dist/runcommand/render.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/runcommand/render.js +19 -0
- package/node_modules/@symbion/core/dist/runcommand/render.js.map +1 -0
- package/node_modules/@symbion/core/dist/templates/authorSource.d.ts +55 -0
- package/node_modules/@symbion/core/dist/templates/authorSource.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/templates/authorSource.js +24 -0
- package/node_modules/@symbion/core/dist/templates/authorSource.js.map +1 -0
- package/node_modules/@symbion/core/dist/templates/matchAuthorFolders.d.ts +28 -0
- package/node_modules/@symbion/core/dist/templates/matchAuthorFolders.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/templates/matchAuthorFolders.js +54 -0
- package/node_modules/@symbion/core/dist/templates/matchAuthorFolders.js.map +1 -0
- package/node_modules/@symbion/core/dist/templates/parseTemplate.d.ts +42 -0
- package/node_modules/@symbion/core/dist/templates/parseTemplate.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/templates/parseTemplate.js +56 -0
- package/node_modules/@symbion/core/dist/templates/parseTemplate.js.map +1 -0
- package/node_modules/@symbion/core/dist/templates/templateListItem.d.ts +39 -0
- package/node_modules/@symbion/core/dist/templates/templateListItem.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/templates/templateListItem.js +2 -0
- package/node_modules/@symbion/core/dist/templates/templateListItem.js.map +1 -0
- package/node_modules/@symbion/core/dist/util/sha256.d.ts +9 -0
- package/node_modules/@symbion/core/dist/util/sha256.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/util/sha256.js +91 -0
- package/node_modules/@symbion/core/dist/util/sha256.js.map +1 -0
- package/node_modules/@symbion/core/dist/version/semver.d.ts +18 -0
- package/node_modules/@symbion/core/dist/version/semver.d.ts.map +1 -0
- package/node_modules/@symbion/core/dist/version/semver.js +53 -0
- package/node_modules/@symbion/core/dist/version/semver.js.map +1 -0
- package/node_modules/@symbion/core/package.json +14 -0
- package/node_modules/@symbion/rpc-types/dist/index.d.ts +444 -0
- package/node_modules/@symbion/rpc-types/dist/index.d.ts.map +1 -0
- package/node_modules/@symbion/rpc-types/dist/index.js +2 -0
- package/node_modules/@symbion/rpc-types/dist/index.js.map +1 -0
- package/node_modules/@symbion/rpc-types/package.json +11 -0
- package/node_modules/yaml/dist/cli.d.ts +8 -0
- package/node_modules/yaml/dist/cli.mjs +201 -0
- package/node_modules/yaml/dist/compose/compose-collection.d.ts +11 -0
- package/node_modules/yaml/dist/compose/compose-collection.js +90 -0
- package/node_modules/yaml/dist/compose/compose-doc.d.ts +7 -0
- package/node_modules/yaml/dist/compose/compose-doc.js +45 -0
- package/node_modules/yaml/dist/compose/compose-node.d.ts +29 -0
- package/node_modules/yaml/dist/compose/compose-node.js +112 -0
- package/node_modules/yaml/dist/compose/compose-scalar.d.ts +5 -0
- package/node_modules/yaml/dist/compose/compose-scalar.js +88 -0
- package/node_modules/yaml/dist/compose/composer.d.ts +63 -0
- package/node_modules/yaml/dist/compose/composer.js +224 -0
- package/node_modules/yaml/dist/compose/resolve-block-map.d.ts +6 -0
- package/node_modules/yaml/dist/compose/resolve-block-map.js +117 -0
- package/node_modules/yaml/dist/compose/resolve-block-scalar.d.ts +11 -0
- package/node_modules/yaml/dist/compose/resolve-block-scalar.js +200 -0
- package/node_modules/yaml/dist/compose/resolve-block-seq.d.ts +6 -0
- package/node_modules/yaml/dist/compose/resolve-block-seq.js +51 -0
- package/node_modules/yaml/dist/compose/resolve-end.d.ts +6 -0
- package/node_modules/yaml/dist/compose/resolve-end.js +39 -0
- package/node_modules/yaml/dist/compose/resolve-flow-collection.d.ts +7 -0
- package/node_modules/yaml/dist/compose/resolve-flow-collection.js +209 -0
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.d.ts +10 -0
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +227 -0
- package/node_modules/yaml/dist/compose/resolve-props.d.ts +23 -0
- package/node_modules/yaml/dist/compose/resolve-props.js +148 -0
- package/node_modules/yaml/dist/compose/util-contains-newline.d.ts +2 -0
- package/node_modules/yaml/dist/compose/util-contains-newline.js +36 -0
- package/node_modules/yaml/dist/compose/util-empty-scalar-position.d.ts +2 -0
- package/node_modules/yaml/dist/compose/util-empty-scalar-position.js +28 -0
- package/node_modules/yaml/dist/compose/util-flow-indent-check.d.ts +3 -0
- package/node_modules/yaml/dist/compose/util-flow-indent-check.js +17 -0
- package/node_modules/yaml/dist/compose/util-map-includes.d.ts +4 -0
- package/node_modules/yaml/dist/compose/util-map-includes.js +15 -0
- package/node_modules/yaml/dist/doc/Document.d.ts +141 -0
- package/node_modules/yaml/dist/doc/Document.js +337 -0
- package/node_modules/yaml/dist/doc/anchors.d.ts +24 -0
- package/node_modules/yaml/dist/doc/anchors.js +76 -0
- package/node_modules/yaml/dist/doc/applyReviver.d.ts +9 -0
- package/node_modules/yaml/dist/doc/applyReviver.js +57 -0
- package/node_modules/yaml/dist/doc/createNode.d.ts +17 -0
- package/node_modules/yaml/dist/doc/createNode.js +90 -0
- package/node_modules/yaml/dist/doc/directives.d.ts +49 -0
- package/node_modules/yaml/dist/doc/directives.js +178 -0
- package/node_modules/yaml/dist/errors.d.ts +21 -0
- package/node_modules/yaml/dist/errors.js +62 -0
- package/node_modules/yaml/dist/index.d.ts +25 -0
- package/node_modules/yaml/dist/index.js +50 -0
- package/node_modules/yaml/dist/log.d.ts +3 -0
- package/node_modules/yaml/dist/log.js +19 -0
- package/node_modules/yaml/dist/nodes/Alias.d.ts +29 -0
- package/node_modules/yaml/dist/nodes/Alias.js +118 -0
- package/node_modules/yaml/dist/nodes/Collection.d.ts +73 -0
- package/node_modules/yaml/dist/nodes/Collection.js +151 -0
- package/node_modules/yaml/dist/nodes/Node.d.ts +53 -0
- package/node_modules/yaml/dist/nodes/Node.js +40 -0
- package/node_modules/yaml/dist/nodes/Pair.d.ts +22 -0
- package/node_modules/yaml/dist/nodes/Pair.js +39 -0
- package/node_modules/yaml/dist/nodes/Scalar.d.ts +47 -0
- package/node_modules/yaml/dist/nodes/Scalar.js +27 -0
- package/node_modules/yaml/dist/nodes/YAMLMap.d.ts +53 -0
- package/node_modules/yaml/dist/nodes/YAMLMap.js +147 -0
- package/node_modules/yaml/dist/nodes/YAMLSeq.d.ts +60 -0
- package/node_modules/yaml/dist/nodes/YAMLSeq.js +115 -0
- package/node_modules/yaml/dist/nodes/addPairToJSMap.d.ts +4 -0
- package/node_modules/yaml/dist/nodes/addPairToJSMap.js +65 -0
- package/node_modules/yaml/dist/nodes/identity.d.ts +23 -0
- package/node_modules/yaml/dist/nodes/identity.js +53 -0
- package/node_modules/yaml/dist/nodes/toJS.d.ts +29 -0
- package/node_modules/yaml/dist/nodes/toJS.js +39 -0
- package/node_modules/yaml/dist/options.d.ts +350 -0
- package/node_modules/yaml/dist/parse/cst-scalar.d.ts +64 -0
- package/node_modules/yaml/dist/parse/cst-scalar.js +218 -0
- package/node_modules/yaml/dist/parse/cst-stringify.d.ts +8 -0
- package/node_modules/yaml/dist/parse/cst-stringify.js +63 -0
- package/node_modules/yaml/dist/parse/cst-visit.d.ts +39 -0
- package/node_modules/yaml/dist/parse/cst-visit.js +99 -0
- package/node_modules/yaml/dist/parse/cst.d.ts +109 -0
- package/node_modules/yaml/dist/parse/cst.js +112 -0
- package/node_modules/yaml/dist/parse/lexer.d.ts +87 -0
- package/node_modules/yaml/dist/parse/lexer.js +723 -0
- package/node_modules/yaml/dist/parse/line-counter.d.ts +22 -0
- package/node_modules/yaml/dist/parse/line-counter.js +41 -0
- package/node_modules/yaml/dist/parse/parser.d.ts +84 -0
- package/node_modules/yaml/dist/parse/parser.js +980 -0
- package/node_modules/yaml/dist/public-api.d.ts +44 -0
- package/node_modules/yaml/dist/public-api.js +107 -0
- package/node_modules/yaml/dist/schema/Schema.d.ts +17 -0
- package/node_modules/yaml/dist/schema/Schema.js +39 -0
- package/node_modules/yaml/dist/schema/common/map.d.ts +2 -0
- package/node_modules/yaml/dist/schema/common/map.js +19 -0
- package/node_modules/yaml/dist/schema/common/null.d.ts +4 -0
- package/node_modules/yaml/dist/schema/common/null.js +17 -0
- package/node_modules/yaml/dist/schema/common/seq.d.ts +2 -0
- package/node_modules/yaml/dist/schema/common/seq.js +19 -0
- package/node_modules/yaml/dist/schema/common/string.d.ts +2 -0
- package/node_modules/yaml/dist/schema/common/string.js +16 -0
- package/node_modules/yaml/dist/schema/core/bool.d.ts +4 -0
- package/node_modules/yaml/dist/schema/core/bool.js +21 -0
- package/node_modules/yaml/dist/schema/core/float.d.ts +4 -0
- package/node_modules/yaml/dist/schema/core/float.js +47 -0
- package/node_modules/yaml/dist/schema/core/int.d.ts +4 -0
- package/node_modules/yaml/dist/schema/core/int.js +42 -0
- package/node_modules/yaml/dist/schema/core/schema.d.ts +1 -0
- package/node_modules/yaml/dist/schema/core/schema.js +25 -0
- package/node_modules/yaml/dist/schema/json/schema.d.ts +2 -0
- package/node_modules/yaml/dist/schema/json/schema.js +64 -0
- package/node_modules/yaml/dist/schema/json-schema.d.ts +69 -0
- package/node_modules/yaml/dist/schema/tags.d.ts +48 -0
- package/node_modules/yaml/dist/schema/tags.js +99 -0
- package/node_modules/yaml/dist/schema/types.d.ts +92 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/binary.d.ts +2 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/binary.js +70 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/bool.d.ts +7 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/bool.js +29 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/float.d.ts +4 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/float.js +50 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/int.d.ts +5 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/int.js +76 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/merge.d.ts +9 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/merge.js +71 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/omap.d.ts +22 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/omap.js +77 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/pairs.d.ts +10 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +82 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/schema.d.ts +1 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/schema.js +41 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/set.d.ts +28 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/set.js +96 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/timestamp.d.ts +6 -0
- package/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +105 -0
- package/node_modules/yaml/dist/stringify/foldFlowLines.d.ts +34 -0
- package/node_modules/yaml/dist/stringify/foldFlowLines.js +151 -0
- package/node_modules/yaml/dist/stringify/stringify.d.ts +21 -0
- package/node_modules/yaml/dist/stringify/stringify.js +132 -0
- package/node_modules/yaml/dist/stringify/stringifyCollection.d.ts +17 -0
- package/node_modules/yaml/dist/stringify/stringifyCollection.js +155 -0
- package/node_modules/yaml/dist/stringify/stringifyComment.d.ts +10 -0
- package/node_modules/yaml/dist/stringify/stringifyComment.js +24 -0
- package/node_modules/yaml/dist/stringify/stringifyDocument.d.ts +4 -0
- package/node_modules/yaml/dist/stringify/stringifyDocument.js +87 -0
- package/node_modules/yaml/dist/stringify/stringifyNumber.d.ts +2 -0
- package/node_modules/yaml/dist/stringify/stringifyNumber.js +27 -0
- package/node_modules/yaml/dist/stringify/stringifyPair.d.ts +3 -0
- package/node_modules/yaml/dist/stringify/stringifyPair.js +152 -0
- package/node_modules/yaml/dist/stringify/stringifyString.d.ts +9 -0
- package/node_modules/yaml/dist/stringify/stringifyString.js +338 -0
- package/node_modules/yaml/dist/test-events.d.ts +4 -0
- package/node_modules/yaml/dist/test-events.js +134 -0
- package/node_modules/yaml/dist/util.d.ts +16 -0
- package/node_modules/yaml/dist/util.js +28 -0
- package/node_modules/yaml/dist/visit.d.ts +102 -0
- package/node_modules/yaml/dist/visit.js +236 -0
- package/node_modules/yaml/package.json +20 -0
- package/node_modules/yaml/util.js +2 -0
- package/package.json +39 -0
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
import { existsSync, statSync } from "node:fs";
|
|
2
|
+
import { accessSync, constants } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { AUTHOR_REGISTRY, computeDiff as coreComputeDiff, parseClaudeDir, renderArtifacts, renderRunCommand as coreRenderRunCommand, validateAllArtifacts, } from "@symbion/core";
|
|
5
|
+
import { fetchAuthorTemplatesFromGithub } from "../templates/githubFetch.js";
|
|
6
|
+
import { createProjectStore, loadGlobalConfig, loadProjectStore, projectStoreExists, saveGlobalConfig, saveProjectStore, } from "../store/store.js";
|
|
7
|
+
import { appendPublishLogEntry } from "../store/publishLog.js";
|
|
8
|
+
import { gitStatus as coreGitStatus } from "../git/status.js";
|
|
9
|
+
import { browseFolder as nativeBrowseFolder } from "../fs/folderPick.js";
|
|
10
|
+
import { listDir as listDirImpl, makeDir as makeDirImpl } from "../fs/listDir.js";
|
|
11
|
+
import { extractForeignAgentsMdContent, readAgentsMd, readClaudeDirFilemap, readTargetFiles, } from "../fs/readTargetFiles.js";
|
|
12
|
+
import { writeFiles } from "../fs/writeFiles.js";
|
|
13
|
+
import { buildBodyGenerationPrompt } from "@symbion/core";
|
|
14
|
+
import { getProvider, listProviderDescriptors } from "../llm/registry.js";
|
|
15
|
+
import { LlmError } from "../llm/types.js";
|
|
16
|
+
import { checkApiKeyProviderReachable, checkOllamaReachable, resolveOllamaBaseUrlForStatusCheck, } from "../llm/providerStatus.js";
|
|
17
|
+
import { detectHostEnvironment, getOllamaInstallInstructions } from "../llm/installInstructions.js";
|
|
18
|
+
import { clearProviderKey as secretsClearProviderKey, loadProvidersConfig, maskKey, ProviderNotConfiguredError, setActiveProvider as secretsSetActiveProvider, setProviderKey as secretsSetProviderKey, } from "../llm/secrets.js";
|
|
19
|
+
import { RpcError } from "./rpcError.js";
|
|
20
|
+
import { isUncPath } from "./pathStyle.js";
|
|
21
|
+
export { RpcError };
|
|
22
|
+
function randomId() {
|
|
23
|
+
const hex = () => Math.floor(Math.random() * 16).toString(16);
|
|
24
|
+
const tpl = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
|
|
25
|
+
return tpl.replace(/[xy]/g, (c) => c === "y" ? ((Math.floor(Math.random() * 4) + 8) % 16).toString(16) : hex());
|
|
26
|
+
}
|
|
27
|
+
function findProjectPath(projectId) {
|
|
28
|
+
const config = loadGlobalConfig();
|
|
29
|
+
const entry = config.projects.find((p) => p.id === projectId);
|
|
30
|
+
if (!entry) {
|
|
31
|
+
throw new Error(`Không tìm thấy project: ${projectId}`);
|
|
32
|
+
}
|
|
33
|
+
return entry.path;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Runtime guards for the LLM RPC surface (generateBody/listModels). TypeScript's
|
|
37
|
+
* `"agent" | "command"` / the 4-id provider unions give zero enforcement once
|
|
38
|
+
* JSON is parsed off the wire — an unrecognized value must fail cleanly with
|
|
39
|
+
* RpcError("invalid-params", ...) BEFORE reaching the prompt builder or provider
|
|
40
|
+
* registry, never fall through to the generic 500/internal-error catch-all (see
|
|
41
|
+
* docs/loops/auto-generate-body-STATE.md §13, MEDIUM finding).
|
|
42
|
+
*/
|
|
43
|
+
const VALID_KINDS = new Set(["agent", "command"]);
|
|
44
|
+
const VALID_PROVIDER_IDS = new Set(["ollama", "openai", "anthropic", "gemini"]);
|
|
45
|
+
const VALID_API_KEY_PROVIDER_IDS = new Set(["openai", "anthropic", "gemini"]);
|
|
46
|
+
function assertValidKind(kind) {
|
|
47
|
+
if (typeof kind !== "string" || !VALID_KINDS.has(kind)) {
|
|
48
|
+
throw new RpcError("invalid-params", `Tham số "kind" không hợp lệ: phải là "agent" hoặc "command".`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function assertValidProviderId(providerId) {
|
|
52
|
+
if (typeof providerId !== "string" || !VALID_PROVIDER_IDS.has(providerId)) {
|
|
53
|
+
throw new RpcError("invalid-params", `Tham số "providerId" không hợp lệ: phải là một trong "ollama", "openai", "anthropic", "gemini".`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function assertValidApiKeyProviderId(providerId) {
|
|
57
|
+
if (typeof providerId !== "string" || !VALID_API_KEY_PROVIDER_IDS.has(providerId)) {
|
|
58
|
+
throw new RpcError("invalid-params", `Tham số "providerId" không hợp lệ: phải là một trong "openai", "anthropic", "gemini".`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const PROVIDER_LABELS = {
|
|
62
|
+
ollama: "Ollama",
|
|
63
|
+
openai: "OpenAI",
|
|
64
|
+
anthropic: "Anthropic",
|
|
65
|
+
gemini: "Gemini",
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* buildProviderDescriptors — projects the daemon-internal ProvidersConfig
|
|
69
|
+
* (raw apiKey, never crosses the RPC boundary) into the masked
|
|
70
|
+
* `ProviderDescriptor[]` shape every provider-settings RPC returns. Single
|
|
71
|
+
* call site for this projection so `maskKey()` is applied exactly once, in
|
|
72
|
+
* exactly one place, never as an afterthought filter on an already-built
|
|
73
|
+
* response object (STATE §3.2's `listProviders` handler note + §9 security note).
|
|
74
|
+
*/
|
|
75
|
+
function buildProviderDescriptors() {
|
|
76
|
+
const config = loadProvidersConfig();
|
|
77
|
+
return listProviderDescriptors().map((descriptor) => {
|
|
78
|
+
if (descriptor.kind === "local") {
|
|
79
|
+
return {
|
|
80
|
+
id: descriptor.id,
|
|
81
|
+
label: descriptor.id === "ollama" ? "Ollama" : PROVIDER_LABELS[descriptor.id],
|
|
82
|
+
kind: descriptor.kind,
|
|
83
|
+
configured: true, // ollama needs no key — always "configured" in the sense of "usable"
|
|
84
|
+
active: config.activeProviderId === descriptor.id,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const stored = config.providers[descriptor.id];
|
|
88
|
+
return {
|
|
89
|
+
id: descriptor.id,
|
|
90
|
+
label: PROVIDER_LABELS[descriptor.id],
|
|
91
|
+
kind: descriptor.kind,
|
|
92
|
+
configured: Boolean(stored),
|
|
93
|
+
active: config.activeProviderId === descriptor.id,
|
|
94
|
+
maskedKey: stored ? maskKey(stored.apiKey) : undefined,
|
|
95
|
+
model: stored?.model,
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
export const handlers = {
|
|
100
|
+
ping(_params, ctx) {
|
|
101
|
+
return { ok: true, version: ctx.version, port: ctx.port };
|
|
102
|
+
},
|
|
103
|
+
async browseFolder(params) {
|
|
104
|
+
const result = await nativeBrowseFolder(params.startPath);
|
|
105
|
+
if (result.cancelled || !result.path)
|
|
106
|
+
return { cancelled: true };
|
|
107
|
+
return { path: result.path };
|
|
108
|
+
},
|
|
109
|
+
validatePath(params) {
|
|
110
|
+
const { path } = params;
|
|
111
|
+
if (isUncPath(path)) {
|
|
112
|
+
return {
|
|
113
|
+
exists: false,
|
|
114
|
+
isDir: false,
|
|
115
|
+
isGitRepo: false,
|
|
116
|
+
hasClaudeDir: false,
|
|
117
|
+
hasAgentsMd: false,
|
|
118
|
+
writable: false,
|
|
119
|
+
reason: "unc-unsupported",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const exists = existsSync(path);
|
|
123
|
+
let isDir = false;
|
|
124
|
+
let writable = false;
|
|
125
|
+
if (exists) {
|
|
126
|
+
const stat = statSync(path);
|
|
127
|
+
isDir = stat.isDirectory();
|
|
128
|
+
try {
|
|
129
|
+
accessSync(path, constants.W_OK);
|
|
130
|
+
writable = true;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
writable = false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const isGitRepo = exists && isDir && existsSync(join(path, ".git"));
|
|
137
|
+
const hasClaudeDir = exists && isDir && existsSync(join(path, ".claude"));
|
|
138
|
+
const hasAgentsMd = exists && isDir && existsSync(join(path, "AGENTS.md"));
|
|
139
|
+
return { exists, isDir, isGitRepo, hasClaudeDir, hasAgentsMd, writable };
|
|
140
|
+
},
|
|
141
|
+
listDir(params) {
|
|
142
|
+
return listDirImpl(params.path);
|
|
143
|
+
},
|
|
144
|
+
makeDir(params) {
|
|
145
|
+
return makeDirImpl(params.path);
|
|
146
|
+
},
|
|
147
|
+
listProjects(_params) {
|
|
148
|
+
const config = loadGlobalConfig();
|
|
149
|
+
return { projects: config.projects };
|
|
150
|
+
},
|
|
151
|
+
createProject(params) {
|
|
152
|
+
const { name, path } = params;
|
|
153
|
+
if (!existsSync(path) || !statSync(path).isDirectory()) {
|
|
154
|
+
throw new RpcError("invalid-path", "Đường dẫn không tồn tại hoặc không phải thư mục.");
|
|
155
|
+
}
|
|
156
|
+
if (projectStoreExists(path)) {
|
|
157
|
+
throw new RpcError("already-a-project", "Thư mục này đã là một dự án Symbion.");
|
|
158
|
+
}
|
|
159
|
+
const id = randomId();
|
|
160
|
+
const project = createProjectStore(path, name, id);
|
|
161
|
+
const config = loadGlobalConfig();
|
|
162
|
+
config.projects.push({ id, name, path });
|
|
163
|
+
config.lastProjectId = id;
|
|
164
|
+
saveGlobalConfig(config);
|
|
165
|
+
return { project };
|
|
166
|
+
},
|
|
167
|
+
loadProject(params) {
|
|
168
|
+
const path = findProjectPath(params.id);
|
|
169
|
+
return { project: loadProjectStore(path) };
|
|
170
|
+
},
|
|
171
|
+
saveArtifact(params) {
|
|
172
|
+
const path = findProjectPath(params.projectId);
|
|
173
|
+
const store = loadProjectStore(path);
|
|
174
|
+
const idx = store.artifacts.findIndex((a) => a.id === params.artifact.id);
|
|
175
|
+
const now = new Date().toISOString();
|
|
176
|
+
const artifact = {
|
|
177
|
+
...params.artifact,
|
|
178
|
+
meta: { ...params.artifact.meta, updatedAt: now },
|
|
179
|
+
};
|
|
180
|
+
// Server-side validation (defense in depth — never trust the client).
|
|
181
|
+
// Validate against the *would-be* full artifact set (siblings + this
|
|
182
|
+
// artifact updated/inserted) so duplicate-name etc. checks are accurate.
|
|
183
|
+
const candidateSiblings = store.artifacts.filter((a) => a.id !== artifact.id);
|
|
184
|
+
const issues = validateAllArtifacts([...candidateSiblings, artifact]);
|
|
185
|
+
const blocking = issues.filter((i) => i.level === "error" && i.artifactId === artifact.id);
|
|
186
|
+
if (blocking.length > 0) {
|
|
187
|
+
throw new RpcError("validation-failed", `Không thể lưu — vi phạm lint: ${blocking.map((i) => i.message).join("; ")}`);
|
|
188
|
+
}
|
|
189
|
+
if (idx >= 0) {
|
|
190
|
+
store.artifacts[idx] = artifact;
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
store.artifacts.push(artifact);
|
|
194
|
+
}
|
|
195
|
+
saveProjectStore(path, store);
|
|
196
|
+
return { project: store };
|
|
197
|
+
},
|
|
198
|
+
deleteArtifact(params) {
|
|
199
|
+
const path = findProjectPath(params.projectId);
|
|
200
|
+
const store = loadProjectStore(path);
|
|
201
|
+
// v1: removing an artifact in Studio does NOT delete its on-disk file silently
|
|
202
|
+
// (STATE §7B) — this only removes it from the in-app store.
|
|
203
|
+
store.artifacts = store.artifacts.filter((a) => a.id !== params.artifactId);
|
|
204
|
+
saveProjectStore(path, store);
|
|
205
|
+
return { project: store };
|
|
206
|
+
},
|
|
207
|
+
updateSettings(params) {
|
|
208
|
+
if (params.projectId && params.settings) {
|
|
209
|
+
const path = findProjectPath(params.projectId);
|
|
210
|
+
const store = loadProjectStore(path);
|
|
211
|
+
store.settings = params.settings;
|
|
212
|
+
saveProjectStore(path, store);
|
|
213
|
+
return { project: store };
|
|
214
|
+
}
|
|
215
|
+
if (params.globalConfig) {
|
|
216
|
+
const config = loadGlobalConfig();
|
|
217
|
+
Object.assign(config, params.globalConfig);
|
|
218
|
+
saveGlobalConfig(config);
|
|
219
|
+
}
|
|
220
|
+
return {};
|
|
221
|
+
},
|
|
222
|
+
scanClaudeDir(params) {
|
|
223
|
+
const filemap = readClaudeDirFilemap(params.path);
|
|
224
|
+
const parsed = parseClaudeDir(filemap);
|
|
225
|
+
return { parsed };
|
|
226
|
+
},
|
|
227
|
+
importArtifacts(params) {
|
|
228
|
+
const path = findProjectPath(params.projectId);
|
|
229
|
+
const store = loadProjectStore(path);
|
|
230
|
+
const selected = params.scanned.filter((a) => params.selectedIds.includes(a.id));
|
|
231
|
+
// Server-side validation (defense in depth) over the resulting merged
|
|
232
|
+
// artifact set. Imported artifacts that would create blocking lint
|
|
233
|
+
// errors (e.g. duplicate name, missing required fields) are rejected
|
|
234
|
+
// wholesale rather than silently persisted invalid.
|
|
235
|
+
const existingOthers = store.artifacts.filter((a) => !selected.some((s) => s.id === a.id));
|
|
236
|
+
const merged = [...existingOthers, ...selected];
|
|
237
|
+
const issues = validateAllArtifacts(merged);
|
|
238
|
+
const selectedIds = new Set(selected.map((a) => a.id));
|
|
239
|
+
const blocking = issues.filter((i) => i.level === "error" && i.artifactId && selectedIds.has(i.artifactId));
|
|
240
|
+
if (blocking.length > 0) {
|
|
241
|
+
throw new RpcError("validation-failed", `Không thể nhập — vi phạm lint: ${blocking.map((i) => i.message).join("; ")}`);
|
|
242
|
+
}
|
|
243
|
+
for (const artifact of selected) {
|
|
244
|
+
const idx = store.artifacts.findIndex((a) => a.id === artifact.id);
|
|
245
|
+
if (idx >= 0) {
|
|
246
|
+
store.artifacts[idx] = artifact;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
store.artifacts.push(artifact);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
saveProjectStore(path, store);
|
|
253
|
+
return { project: store };
|
|
254
|
+
},
|
|
255
|
+
/**
|
|
256
|
+
* applyTemplate — stages one bundled template (apps/web's static gallery)
|
|
257
|
+
* into a project's store as a new draft artifact, with server-side
|
|
258
|
+
* auto-suffix collision resolution. See packages/rpc-types's
|
|
259
|
+
* ApplyTemplateParams doc comment + docs/loops/templates-marketplace-STATE.md
|
|
260
|
+
* PLAN §0(b)/§2 for the full rationale (new RPC vs. extending
|
|
261
|
+
* importArtifacts; TOCTOU; never-block-never-overwrite collision policy).
|
|
262
|
+
*
|
|
263
|
+
* Defense-in-depth shape re-validation: the daemon never trusts the
|
|
264
|
+
* client's `template.kind`/`name`/`description` even though this content
|
|
265
|
+
* originates from the web app's own bundled, build-time-reviewed data
|
|
266
|
+
* (not arbitrary external input) — same posture as every other mutating
|
|
267
|
+
* handler in this file (see saveArtifact/importArtifacts comments above).
|
|
268
|
+
*/
|
|
269
|
+
applyTemplate(params) {
|
|
270
|
+
const { projectId, template } = params;
|
|
271
|
+
if (!template || (template.kind !== "agent" && template.kind !== "command")) {
|
|
272
|
+
throw new RpcError("invalid-kind", "Chỉ Agent/Command hỗ trợ Áp dụng.");
|
|
273
|
+
}
|
|
274
|
+
if (typeof template.name !== "string" || template.name.trim().length === 0) {
|
|
275
|
+
throw new RpcError("invalid-template", "Template thiếu name.");
|
|
276
|
+
}
|
|
277
|
+
if (typeof template.description !== "string" || template.description.trim().length === 0) {
|
|
278
|
+
throw new RpcError("invalid-template", "Template thiếu description.");
|
|
279
|
+
}
|
|
280
|
+
if (typeof template.body !== "string" || template.body.trim().length === 0) {
|
|
281
|
+
throw new RpcError("invalid-template", "Template thiếu nội dung (body).");
|
|
282
|
+
}
|
|
283
|
+
if (typeof template.sourceTemplateId !== "string" || template.sourceTemplateId.trim().length === 0) {
|
|
284
|
+
throw new RpcError("invalid-template", "Template thiếu sourceTemplateId.");
|
|
285
|
+
}
|
|
286
|
+
// templates-authors PLAN §P6: server-side defense-in-depth mirror of the
|
|
287
|
+
// client-side license/attribution acknowledgment gate. The daemon looks
|
|
288
|
+
// up authorId in AUTHOR_REGISTRY itself (never trusts a client-asserted
|
|
289
|
+
// boolean about WHETHER the gate applies) — only an unrecognized/absent
|
|
290
|
+
// authorId or one whose registry entry is kind:"bundled" (Symbion) is
|
|
291
|
+
// treated as not-third-party, matching "never trust the client" posture.
|
|
292
|
+
const authorId = template.authorId ?? "symbion";
|
|
293
|
+
const author = AUTHOR_REGISTRY.find((a) => a.id === authorId);
|
|
294
|
+
// Conservative default: unknown authorId → treat as third-party (requires
|
|
295
|
+
// acknowledgment). A known "bundled" (Symbion) entry is the ONLY exempt case.
|
|
296
|
+
// This ensures "never trust the client about WHETHER the gate applies" even if a
|
|
297
|
+
// future registry entry is misspelled or a hand-crafted RPC sends an unrecognized id.
|
|
298
|
+
const isThirdParty = author === undefined || author.kind === "github";
|
|
299
|
+
if (isThirdParty && template.acknowledgedThirdParty !== true) {
|
|
300
|
+
throw new RpcError("license-not-acknowledged", "Cần xác nhận đã đọc thông báo về bản quyền nội dung của tác giả khác trước khi áp dụng.");
|
|
301
|
+
}
|
|
302
|
+
const path = findProjectPath(projectId);
|
|
303
|
+
const store = loadProjectStore(path); // fresh read — closes the TOCTOU gap a client-side calc would have
|
|
304
|
+
// Auto-suffix algorithm (THINK #4): first free "<name>", "<name>-2", "<name>-3", ...
|
|
305
|
+
// scoped to (kind, name) pairs, matching validate.ts's own duplicate rule
|
|
306
|
+
// (same kind + same name) so the suffix algorithm and the lint rule it's
|
|
307
|
+
// dodging stay in lockstep by construction.
|
|
308
|
+
const existingNames = new Set(store.artifacts.filter((a) => a.kind === template.kind).map((a) => a.name));
|
|
309
|
+
let finalName = template.name;
|
|
310
|
+
let n = 2;
|
|
311
|
+
while (existingNames.has(finalName)) {
|
|
312
|
+
finalName = `${template.name}-${n}`;
|
|
313
|
+
n++;
|
|
314
|
+
}
|
|
315
|
+
const wasRenamed = finalName !== template.name;
|
|
316
|
+
const now = new Date().toISOString();
|
|
317
|
+
const artifact = {
|
|
318
|
+
id: randomId(),
|
|
319
|
+
kind: template.kind,
|
|
320
|
+
name: finalName,
|
|
321
|
+
description: template.description,
|
|
322
|
+
tools: template.tools,
|
|
323
|
+
body: template.body,
|
|
324
|
+
meta: {
|
|
325
|
+
version: "draft",
|
|
326
|
+
status: "draft",
|
|
327
|
+
createdAt: now,
|
|
328
|
+
updatedAt: now,
|
|
329
|
+
sourceTemplateId: template.sourceTemplateId,
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
// Defense-in-depth: re-validate the FULL resulting set, same posture as
|
|
333
|
+
// importArtifacts/saveArtifact. Should never actually fail given the
|
|
334
|
+
// auto-suffix loop above already guarantees no name collision, but keeps
|
|
335
|
+
// the same "never trust, always re-check before persist" invariant as
|
|
336
|
+
// every other write path (a real safety net if e.g. FILENAME_SAFE_RE
|
|
337
|
+
// rejects a name with a space/slash the bundle author typo'd).
|
|
338
|
+
const merged = [...store.artifacts, artifact];
|
|
339
|
+
const issues = validateAllArtifacts(merged);
|
|
340
|
+
const blocking = issues.filter((i) => i.level === "error" && i.artifactId === artifact.id);
|
|
341
|
+
if (blocking.length > 0) {
|
|
342
|
+
throw new RpcError("validation-failed", `Không thể áp dụng — vi phạm lint: ${blocking.map((i) => i.message).join("; ")}`);
|
|
343
|
+
}
|
|
344
|
+
store.artifacts.push(artifact);
|
|
345
|
+
saveProjectStore(path, store);
|
|
346
|
+
return { project: store, appliedArtifactId: artifact.id, finalName, wasRenamed };
|
|
347
|
+
},
|
|
348
|
+
/**
|
|
349
|
+
* fetchAuthorTemplates — templates-authors v2 extension (PLAN §P2). Looks
|
|
350
|
+
* up `authorId` in AUTHOR_REGISTRY (packages/core) server-side — never
|
|
351
|
+
* trusts/reads any client-supplied owner/repo fields, even if a
|
|
352
|
+
* hand-crafted request sends them (PLAN §P8 SSRF finding #1(a)). An
|
|
353
|
+
* unknown authorId, or one resolving to a `kind: "bundled"` entry (e.g.
|
|
354
|
+
* "symbion" — the bundled author can't be routed through the network-fetch
|
|
355
|
+
* path), is a programming/client-bug error -> thrown RpcError, NOT a
|
|
356
|
+
* well-formed outcome. A known `kind: "github"` author always resolves
|
|
357
|
+
* (never throws) to a `FetchAuthorTemplatesOutcome` — every external
|
|
358
|
+
* failure mode (network/rate-limit/not-found/per-file/parse) is a
|
|
359
|
+
* well-formed, expected-to-sometimes-fail result, not a daemon bug.
|
|
360
|
+
*/
|
|
361
|
+
async fetchAuthorTemplates(params) {
|
|
362
|
+
if (typeof params?.authorId !== "string" || params.authorId.trim().length === 0) {
|
|
363
|
+
throw new RpcError("invalid-author", "Thiếu authorId.");
|
|
364
|
+
}
|
|
365
|
+
const author = AUTHOR_REGISTRY.find((a) => a.id === params.authorId);
|
|
366
|
+
if (!author || author.kind !== "github") {
|
|
367
|
+
throw new RpcError("invalid-author", `authorId không hợp lệ hoặc không phải nguồn GitHub: "${params.authorId}".`);
|
|
368
|
+
}
|
|
369
|
+
const outcome = await fetchAuthorTemplatesFromGithub(author);
|
|
370
|
+
return { outcome };
|
|
371
|
+
},
|
|
372
|
+
render(params) {
|
|
373
|
+
const path = findProjectPath(params.projectId);
|
|
374
|
+
const store = loadProjectStore(path);
|
|
375
|
+
const files = [];
|
|
376
|
+
for (const target of params.targets) {
|
|
377
|
+
const existingForeignContent = target === "codex" ? extractForeignAgentsMdContent(readAgentsMd(path)) : undefined;
|
|
378
|
+
files.push(...renderArtifacts(store.artifacts, target, { version: params.version, existingForeignContent }));
|
|
379
|
+
}
|
|
380
|
+
return { files };
|
|
381
|
+
},
|
|
382
|
+
computeDiff(params) {
|
|
383
|
+
const path = findProjectPath(params.projectId);
|
|
384
|
+
const store = loadProjectStore(path);
|
|
385
|
+
const allFiles = [];
|
|
386
|
+
for (const target of params.targets) {
|
|
387
|
+
const existingForeignContent = target === "codex" ? extractForeignAgentsMdContent(readAgentsMd(path)) : undefined;
|
|
388
|
+
const rendered = renderArtifacts(store.artifacts, target, {
|
|
389
|
+
version: params.version,
|
|
390
|
+
existingForeignContent,
|
|
391
|
+
});
|
|
392
|
+
const mergedTargetRelPaths = target === "codex" ? new Set(rendered.map((f) => f.relPath)) : new Set();
|
|
393
|
+
const onDisk = readTargetFiles(path, rendered.map((f) => f.relPath), mergedTargetRelPaths);
|
|
394
|
+
allFiles.push(...coreComputeDiff(rendered, onDisk));
|
|
395
|
+
}
|
|
396
|
+
const conflicts = allFiles.filter((f) => f.status === "conflict").length;
|
|
397
|
+
return { files: allFiles, conflicts };
|
|
398
|
+
},
|
|
399
|
+
write(params) {
|
|
400
|
+
const path = findProjectPath(params.projectId);
|
|
401
|
+
const store = loadProjectStore(path);
|
|
402
|
+
const diffResult = handlers.computeDiff({
|
|
403
|
+
projectId: params.projectId,
|
|
404
|
+
targets: params.targets,
|
|
405
|
+
version: params.version,
|
|
406
|
+
});
|
|
407
|
+
const selectionByPath = new Map(params.files.map((f) => [f.relPath, f.resolution]));
|
|
408
|
+
const renderedByPath = new Map();
|
|
409
|
+
for (const target of params.targets) {
|
|
410
|
+
const existingForeignContent = target === "codex" ? extractForeignAgentsMdContent(readAgentsMd(path)) : undefined;
|
|
411
|
+
for (const file of renderArtifacts(store.artifacts, target, {
|
|
412
|
+
version: params.version,
|
|
413
|
+
existingForeignContent,
|
|
414
|
+
})) {
|
|
415
|
+
renderedByPath.set(file.relPath, file);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
const tasks = [];
|
|
419
|
+
for (const diffFile of diffResult.files) {
|
|
420
|
+
if (!selectionByPath.has(diffFile.relPath))
|
|
421
|
+
continue; // not selected by user -> skip
|
|
422
|
+
const rendered = renderedByPath.get(diffFile.relPath);
|
|
423
|
+
if (!rendered)
|
|
424
|
+
continue;
|
|
425
|
+
// Determine which target this relPath belongs to (best-effort: Claude paths are
|
|
426
|
+
// .claude/...; everything else (AGENTS.md) is codex).
|
|
427
|
+
const target = diffFile.relPath.startsWith(".claude/") ? "claude" : "codex";
|
|
428
|
+
tasks.push({
|
|
429
|
+
target,
|
|
430
|
+
relPath: diffFile.relPath,
|
|
431
|
+
content: rendered.content,
|
|
432
|
+
contentHash: rendered.contentHash,
|
|
433
|
+
artifactId: rendered.artifactIds[0],
|
|
434
|
+
resolution: selectionByPath.get(diffFile.relPath),
|
|
435
|
+
isSame: diffFile.status === "same",
|
|
436
|
+
isUnresolvedConflict: diffFile.status === "conflict",
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
const outcome = writeFiles(tasks, {
|
|
440
|
+
projectRoot: path,
|
|
441
|
+
version: params.version,
|
|
442
|
+
backupBeforeWrite: store.settings.backupBeforeWrite,
|
|
443
|
+
});
|
|
444
|
+
appendPublishLogEntry(path, {
|
|
445
|
+
version: params.version,
|
|
446
|
+
timestamp: new Date().toISOString(),
|
|
447
|
+
targets: params.targets,
|
|
448
|
+
results: outcome.results,
|
|
449
|
+
backupDir: outcome.backupDir,
|
|
450
|
+
});
|
|
451
|
+
// update meta.publishedHashes + status for artifacts that were successfully written
|
|
452
|
+
for (const result of outcome.results) {
|
|
453
|
+
if (result.action !== "created" && result.action !== "updated")
|
|
454
|
+
continue;
|
|
455
|
+
if (!result.artifactId)
|
|
456
|
+
continue;
|
|
457
|
+
const artifact = store.artifacts.find((a) => a.id === result.artifactId);
|
|
458
|
+
if (!artifact)
|
|
459
|
+
continue;
|
|
460
|
+
artifact.meta.publishedHashes = {
|
|
461
|
+
...artifact.meta.publishedHashes,
|
|
462
|
+
[result.target]: result.contentHash,
|
|
463
|
+
};
|
|
464
|
+
artifact.meta.status = "published";
|
|
465
|
+
artifact.meta.version = params.version;
|
|
466
|
+
}
|
|
467
|
+
saveProjectStore(path, store);
|
|
468
|
+
return { results: outcome.results, backupDir: outcome.backupDir, logEntryWritten: true };
|
|
469
|
+
},
|
|
470
|
+
gitStatus(params) {
|
|
471
|
+
return coreGitStatus(params.path);
|
|
472
|
+
},
|
|
473
|
+
renderRunCommand(params) {
|
|
474
|
+
return { prompt: coreRenderRunCommand(params) };
|
|
475
|
+
},
|
|
476
|
+
/**
|
|
477
|
+
* listModels — single source of truth for a provider's model list, so apps/web
|
|
478
|
+
* never hand-duplicates it (resolves STATE §10.7 Risk R1 per the user's
|
|
479
|
+
* amendment). For the 3 cloud providers this is still a static, hardcoded,
|
|
480
|
+
* zero-network-call list (unchanged, AC5). For Ollama, this now performs a real
|
|
481
|
+
* `GET /api/tags` network call against the local Ollama instance (per
|
|
482
|
+
* docs/loops/ollama-dynamic-models-STATE.md §6.2/§6.4) — bounded by a 3000ms
|
|
483
|
+
* timeout, never hangs indefinitely (AC6).
|
|
484
|
+
*
|
|
485
|
+
* Three resolved outcomes (never a thrown RpcError for these, since both are
|
|
486
|
+
* well-formed, expected facts about a reachable Ollama, not daemon bugs):
|
|
487
|
+
* - `{models, outcome:"ok"}` — non-empty list (cloud providers always land here).
|
|
488
|
+
* - `{models:[], outcome:"empty"}` — Ollama reachable, zero models pulled.
|
|
489
|
+
* - `{models:[], outcome:"fetch-failed", errorMessage}` — Ollama reachable but
|
|
490
|
+
* `/api/tags` itself failed (malformed JSON / non-2xx / missing `models` field).
|
|
491
|
+
*
|
|
492
|
+
* `provider-not-running` (Ollama unreachable) is the ONE case that still THROWS
|
|
493
|
+
* `RpcError("llm-provider-not-running", ...)` — unchanged shape, AC4.
|
|
494
|
+
*/
|
|
495
|
+
async listModels(params) {
|
|
496
|
+
assertValidProviderId(params.providerId);
|
|
497
|
+
const provider = getProvider(params.providerId);
|
|
498
|
+
try {
|
|
499
|
+
const models = await provider.listModels();
|
|
500
|
+
return { models, outcome: models.length === 0 ? "empty" : "ok" };
|
|
501
|
+
}
|
|
502
|
+
catch (err) {
|
|
503
|
+
if (err instanceof LlmError) {
|
|
504
|
+
if (err.code === "provider-not-running") {
|
|
505
|
+
// unchanged existing path — Ollama unreachable still throws, AC4.
|
|
506
|
+
throw new RpcError(`llm-${err.code}`, humanMessageForLlmError(err));
|
|
507
|
+
}
|
|
508
|
+
// invalid-response (malformed JSON / non-2xx from /api/tags), or any other
|
|
509
|
+
// LlmError code reaching here — resolve with outcome:"fetch-failed" rather
|
|
510
|
+
// than throw, so the web layer can render a distinct, non-generic message.
|
|
511
|
+
return { models: [], outcome: "fetch-failed", errorMessage: humanMessageForLlmError(err) };
|
|
512
|
+
}
|
|
513
|
+
throw new RpcError("llm-unknown", "Lỗi không xác định khi lấy danh sách mô hình.");
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
/**
|
|
517
|
+
* generateBody — the first async, slow, externally-fallible RPC handler in the
|
|
518
|
+
* codebase (STATE §10.1/§10.2). Touches neither disk nor git: pure inference in,
|
|
519
|
+
* text out. Deliberately does NOT call findProjectPath/loadProjectStore — the
|
|
520
|
+
* request already carries all needed context inline (STATE §10.2).
|
|
521
|
+
*/
|
|
522
|
+
async generateBody(params) {
|
|
523
|
+
// Runtime-validate the two "TS union, zero runtime enforcement" fields BEFORE
|
|
524
|
+
// touching the prompt builder or provider registry (STATE §13 MEDIUM finding) —
|
|
525
|
+
// an unrecognized kind/providerId must fail as a clean invalid-params RpcError,
|
|
526
|
+
// not a bare Error that falls through to the generic 500/internal-error path.
|
|
527
|
+
assertValidKind(params.kind);
|
|
528
|
+
assertValidProviderId(params.providerId);
|
|
529
|
+
// Defensive input-size cap (no daemon crash / runaway prompt on malformed/huge input —
|
|
530
|
+
// there is no pre-existing size-cap precedent elsewhere in the RPC surface to match,
|
|
531
|
+
// so this is a new, narrowly-scoped guard for this handler only).
|
|
532
|
+
const MAX_FIELD_LEN = 50_000;
|
|
533
|
+
for (const field of ["name", "description", "existingBody"]) {
|
|
534
|
+
const value = params[field];
|
|
535
|
+
if (typeof value !== "string" || value.length > MAX_FIELD_LEN) {
|
|
536
|
+
throw new RpcError("invalid-params", `Trường "${field}" không hợp lệ hoặc quá lớn.`);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (typeof params.modelId !== "string" || params.modelId.length === 0 || params.modelId.length > 200) {
|
|
540
|
+
throw new RpcError("invalid-params", "modelId không hợp lệ.");
|
|
541
|
+
}
|
|
542
|
+
const { system, user } = buildBodyGenerationPrompt({
|
|
543
|
+
kind: params.kind,
|
|
544
|
+
name: params.name,
|
|
545
|
+
description: params.description,
|
|
546
|
+
existingBody: params.existingBody,
|
|
547
|
+
});
|
|
548
|
+
try {
|
|
549
|
+
// getProvider/the provider constructor can itself throw an LlmError (e.g. an
|
|
550
|
+
// env-var-sourced Ollama base URL that fails the loopback check — STATE §13 HIGH
|
|
551
|
+
// finding) — kept inside this try so any LlmError, whether thrown at construction
|
|
552
|
+
// time or during generate(), maps through the same clean RpcError taxonomy below
|
|
553
|
+
// instead of leaking via the generic 500/internal-error path.
|
|
554
|
+
const provider = getProvider(params.providerId);
|
|
555
|
+
const result = await provider.generate({
|
|
556
|
+
systemPrompt: system,
|
|
557
|
+
userPrompt: user,
|
|
558
|
+
model: params.modelId,
|
|
559
|
+
timeoutMs: 45_000,
|
|
560
|
+
});
|
|
561
|
+
return { body: result.text };
|
|
562
|
+
}
|
|
563
|
+
catch (err) {
|
|
564
|
+
if (err instanceof LlmError) {
|
|
565
|
+
throw new RpcError(`llm-${err.code}`, humanMessageForLlmError(err));
|
|
566
|
+
}
|
|
567
|
+
throw new RpcError("llm-unknown", "Lỗi không xác định khi gọi mô hình AI.");
|
|
568
|
+
}
|
|
569
|
+
},
|
|
570
|
+
/**
|
|
571
|
+
* checkProviderStatus — read-only liveness/auth check, widened per
|
|
572
|
+
* docs/loops/multi-provider-settings-STATE.md §3.2/§4b from the literal
|
|
573
|
+
* "ollama" to the 4-id union. Ollama keeps its exact existing path
|
|
574
|
+
* (checkOllamaReachable, unchanged). The 3 api-key providers: first check
|
|
575
|
+
* secrets.ts has a stored key (if not, short-circuit to
|
|
576
|
+
* { reachable:false, errorCode:"not-configured" } with ZERO network calls —
|
|
577
|
+
* never attempt a guaranteed-auth-failure round-trip); if a key exists,
|
|
578
|
+
* construct the provider (reads its own key internally) and perform ONE
|
|
579
|
+
* cheap authenticated call via checkApiKeyProviderReachable. Never throws
|
|
580
|
+
* on "not reachable" — that is a valid resolved result, not a server error.
|
|
581
|
+
*/
|
|
582
|
+
async checkProviderStatus(params) {
|
|
583
|
+
assertValidProviderId(params?.providerId);
|
|
584
|
+
const { providerId } = params;
|
|
585
|
+
if (providerId === "ollama") {
|
|
586
|
+
const baseUrl = resolveOllamaBaseUrlForStatusCheck();
|
|
587
|
+
const reachable = await checkOllamaReachable(baseUrl, 3000);
|
|
588
|
+
const install = getOllamaInstallInstructions(detectHostEnvironment());
|
|
589
|
+
return { reachable, checkedBaseUrl: baseUrl, install, kind: "local" };
|
|
590
|
+
}
|
|
591
|
+
const config = loadProvidersConfig();
|
|
592
|
+
const hasKey = Boolean(config.providers[providerId]);
|
|
593
|
+
if (!hasKey) {
|
|
594
|
+
return { reachable: false, errorCode: "not-configured", kind: "api-key" };
|
|
595
|
+
}
|
|
596
|
+
const provider = getProvider(providerId);
|
|
597
|
+
const result = await checkApiKeyProviderReachable(provider, 3000);
|
|
598
|
+
return { reachable: result.reachable, errorCode: result.errorCode, kind: "api-key" };
|
|
599
|
+
},
|
|
600
|
+
/**
|
|
601
|
+
* listProviders — returns all 4 providers' static descriptor + current
|
|
602
|
+
* persisted state (masked key, model, configured/active) in one call,
|
|
603
|
+
* backing the Settings page's initial render and useActiveProvider()'s
|
|
604
|
+
* one-call-per-mount resolution (STATE §3.2/§4a/§4d).
|
|
605
|
+
*/
|
|
606
|
+
listProviders(_params) {
|
|
607
|
+
return { providers: buildProviderDescriptors() };
|
|
608
|
+
},
|
|
609
|
+
/**
|
|
610
|
+
* saveProviderKey — validates providerId against the 3 api-key ids (ollama
|
|
611
|
+
* never has a stored key) and apiKey under a size cap (mirrors
|
|
612
|
+
* generateBody's MAX_FIELD_LEN pattern), then upserts via secrets.ts.
|
|
613
|
+
* Returns the masked descriptors — apiKey is NEVER serialized raw.
|
|
614
|
+
*/
|
|
615
|
+
saveProviderKey(params) {
|
|
616
|
+
const providerIdRaw = params?.providerId;
|
|
617
|
+
assertValidApiKeyProviderId(providerIdRaw);
|
|
618
|
+
const providerId = providerIdRaw;
|
|
619
|
+
const model = params.model;
|
|
620
|
+
const MAX_API_KEY_LEN = 4000;
|
|
621
|
+
const apiKey = params.apiKey;
|
|
622
|
+
if (typeof apiKey !== "string" || apiKey.trim().length === 0 || apiKey.length > MAX_API_KEY_LEN) {
|
|
623
|
+
throw new RpcError("invalid-params", `Tham số "apiKey" không hợp lệ hoặc quá lớn.`);
|
|
624
|
+
}
|
|
625
|
+
if (model !== undefined && (typeof model !== "string" || model.length > 200)) {
|
|
626
|
+
throw new RpcError("invalid-params", `Tham số "model" không hợp lệ.`);
|
|
627
|
+
}
|
|
628
|
+
secretsSetProviderKey(providerId, apiKey, model ?? "");
|
|
629
|
+
return { providers: buildProviderDescriptors() };
|
|
630
|
+
},
|
|
631
|
+
/** clearProviderKey — removes one provider's stored key/model. If it was active,
|
|
632
|
+
* activeProviderId resets to null (no automatic fallback to ollama). */
|
|
633
|
+
clearProviderKey(params) {
|
|
634
|
+
const providerIdRaw = params?.providerId;
|
|
635
|
+
assertValidApiKeyProviderId(providerIdRaw);
|
|
636
|
+
const providerId = providerIdRaw;
|
|
637
|
+
secretsClearProviderKey(providerId);
|
|
638
|
+
return { providers: buildProviderDescriptors() };
|
|
639
|
+
},
|
|
640
|
+
/** setActiveProvider — rejects (invalid-params) if the target api-key provider has no
|
|
641
|
+
* stored key; ollama always succeeds (needs none). Never a silent no-op. */
|
|
642
|
+
setActiveProvider(params) {
|
|
643
|
+
assertValidProviderId(params?.providerId);
|
|
644
|
+
try {
|
|
645
|
+
secretsSetActiveProvider(params.providerId);
|
|
646
|
+
}
|
|
647
|
+
catch (err) {
|
|
648
|
+
if (err instanceof ProviderNotConfiguredError) {
|
|
649
|
+
throw new RpcError("invalid-params", "Chưa cấu hình API key cho nhà cung cấp này.");
|
|
650
|
+
}
|
|
651
|
+
throw err;
|
|
652
|
+
}
|
|
653
|
+
return { providers: buildProviderDescriptors() };
|
|
654
|
+
},
|
|
655
|
+
};
|
|
656
|
+
/**
|
|
657
|
+
* EC-4's exact error-code -> human-readable-Vietnamese-message taxonomy (STATE §10.5).
|
|
658
|
+
*
|
|
659
|
+
* Per-code fallback strings are used when `err.message` is empty, or for codes
|
|
660
|
+
* ("provider-not-running" / "timeout" / "auth" / "rate-limit" / "not-configured")
|
|
661
|
+
* whose throw sites across all 4 provider adapters (ollamaProvider.ts,
|
|
662
|
+
* openaiProvider.ts, anthropicProvider.ts, geminiProvider.ts) already construct a
|
|
663
|
+
* message that says exactly what the fallback says (provider name aside) — no extra
|
|
664
|
+
* detail to preserve there.
|
|
665
|
+
*
|
|
666
|
+
* For "invalid-response" and "network", the throw sites DO carry extra,
|
|
667
|
+
* user-relevant detail the generic fallback would otherwise discard (e.g. a 404
|
|
668
|
+
* "model not pulled" message naming the missing model) — confirmed root cause of
|
|
669
|
+
* the Generate Body 404 loop (docs/learnings.md). For those codes (and "unknown"),
|
|
670
|
+
* prefer the original `err.message` when present.
|
|
671
|
+
*/
|
|
672
|
+
function humanMessageForLlmError(err) {
|
|
673
|
+
const code = err.code;
|
|
674
|
+
switch (code) {
|
|
675
|
+
case "provider-not-running":
|
|
676
|
+
return "Không thể kết nối tới Ollama — đảm bảo Ollama đang chạy trên máy.";
|
|
677
|
+
case "timeout":
|
|
678
|
+
return "Quá thời gian chờ (45s) — thử lại.";
|
|
679
|
+
case "auth":
|
|
680
|
+
return "Thiếu hoặc sai cấu hình API key cho nhà cung cấp AI.";
|
|
681
|
+
case "rate-limit":
|
|
682
|
+
return "Bị giới hạn tần suất gọi — thử lại sau.";
|
|
683
|
+
case "not-configured":
|
|
684
|
+
return "Chưa cấu hình nhà cung cấp AI nào — vào Cài đặt để thêm.";
|
|
685
|
+
case "invalid-response":
|
|
686
|
+
return err.message || "Phản hồi không hợp lệ từ mô hình.";
|
|
687
|
+
case "network":
|
|
688
|
+
case "unknown":
|
|
689
|
+
default:
|
|
690
|
+
return err.message || "Lỗi không xác định, thử lại.";
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
export { validateAllArtifacts };
|
|
694
|
+
//# sourceMappingURL=handlers.js.map
|