@chances-ai/engine 24.0.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/dist/agents/discover.d.ts +30 -0
- package/dist/agents/discover.d.ts.map +1 -0
- package/dist/agents/discover.js +183 -0
- package/dist/agents/discover.js.map +1 -0
- package/dist/agents/index.d.ts +20 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +52 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/parse.d.ts +61 -0
- package/dist/agents/parse.d.ts.map +1 -0
- package/dist/agents/parse.js +527 -0
- package/dist/agents/parse.js.map +1 -0
- package/dist/agents/types.d.ts +52 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +8 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/ai/adapters/ai-sdk-stream.d.ts +19 -0
- package/dist/ai/adapters/ai-sdk-stream.d.ts.map +1 -0
- package/dist/ai/adapters/ai-sdk-stream.js +125 -0
- package/dist/ai/adapters/ai-sdk-stream.js.map +1 -0
- package/dist/ai/adapters/ai-sdk.d.ts +56 -0
- package/dist/ai/adapters/ai-sdk.d.ts.map +1 -0
- package/dist/ai/adapters/ai-sdk.js +112 -0
- package/dist/ai/adapters/ai-sdk.js.map +1 -0
- package/dist/ai/adapters/mock.d.ts +13 -0
- package/dist/ai/adapters/mock.d.ts.map +1 -0
- package/dist/ai/adapters/mock.js +54 -0
- package/dist/ai/adapters/mock.js.map +1 -0
- package/dist/ai/adapters/openai-compatible.d.ts +23 -0
- package/dist/ai/adapters/openai-compatible.d.ts.map +1 -0
- package/dist/ai/adapters/openai-compatible.js +45 -0
- package/dist/ai/adapters/openai-compatible.js.map +1 -0
- package/dist/ai/cost.d.ts +3 -0
- package/dist/ai/cost.d.ts.map +1 -0
- package/dist/ai/cost.js +5 -0
- package/dist/ai/cost.js.map +1 -0
- package/dist/ai/index.d.ts +12 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +11 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/known-models.d.ts +20 -0
- package/dist/ai/known-models.d.ts.map +1 -0
- package/dist/ai/known-models.js +129 -0
- package/dist/ai/known-models.js.map +1 -0
- package/dist/ai/registry.d.ts +12 -0
- package/dist/ai/registry.d.ts.map +1 -0
- package/dist/ai/registry.js +24 -0
- package/dist/ai/registry.js.map +1 -0
- package/dist/ai/retry.d.ts +11 -0
- package/dist/ai/retry.d.ts.map +1 -0
- package/dist/ai/retry.js +14 -0
- package/dist/ai/retry.js.map +1 -0
- package/dist/ai/router.d.ts +25 -0
- package/dist/ai/router.d.ts.map +1 -0
- package/dist/ai/router.js +36 -0
- package/dist/ai/router.js.map +1 -0
- package/dist/ai/setup.d.ts +23 -0
- package/dist/ai/setup.d.ts.map +1 -0
- package/dist/ai/setup.js +47 -0
- package/dist/ai/setup.js.map +1 -0
- package/dist/ai/summarizer.d.ts +24 -0
- package/dist/ai/summarizer.d.ts.map +1 -0
- package/dist/ai/summarizer.js +56 -0
- package/dist/ai/summarizer.js.map +1 -0
- package/dist/ai/types.d.ts +83 -0
- package/dist/ai/types.d.ts.map +1 -0
- package/dist/ai/types.js +2 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/core/compaction/circuit-breaker.d.ts +32 -0
- package/dist/core/compaction/circuit-breaker.d.ts.map +1 -0
- package/dist/core/compaction/circuit-breaker.js +42 -0
- package/dist/core/compaction/circuit-breaker.js.map +1 -0
- package/dist/core/compaction/compactor.d.ts +75 -0
- package/dist/core/compaction/compactor.d.ts.map +1 -0
- package/dist/core/compaction/compactor.js +261 -0
- package/dist/core/compaction/compactor.js.map +1 -0
- package/dist/core/compaction/estimate.d.ts +39 -0
- package/dist/core/compaction/estimate.d.ts.map +1 -0
- package/dist/core/compaction/estimate.js +74 -0
- package/dist/core/compaction/estimate.js.map +1 -0
- package/dist/core/compaction/index.d.ts +5 -0
- package/dist/core/compaction/index.d.ts.map +1 -0
- package/dist/core/compaction/index.js +5 -0
- package/dist/core/compaction/index.js.map +1 -0
- package/dist/core/compaction/prune.d.ts +43 -0
- package/dist/core/compaction/prune.d.ts.map +1 -0
- package/dist/core/compaction/prune.js +51 -0
- package/dist/core/compaction/prune.js.map +1 -0
- package/dist/core/engine.d.ts +268 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +767 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/task-tool.d.ts +175 -0
- package/dist/core/task-tool.d.ts.map +1 -0
- package/dist/core/task-tool.js +901 -0
- package/dist/core/task-tool.js.map +1 -0
- package/dist/core/workspace-query.d.ts +83 -0
- package/dist/core/workspace-query.d.ts.map +1 -0
- package/dist/core/workspace-query.js +217 -0
- package/dist/core/workspace-query.js.map +1 -0
- package/dist/core/worktree/active-marker.d.ts +31 -0
- package/dist/core/worktree/active-marker.d.ts.map +1 -0
- package/dist/core/worktree/active-marker.js +109 -0
- package/dist/core/worktree/active-marker.js.map +1 -0
- package/dist/core/worktree/create.d.ts +40 -0
- package/dist/core/worktree/create.d.ts.map +1 -0
- package/dist/core/worktree/create.js +121 -0
- package/dist/core/worktree/create.js.map +1 -0
- package/dist/core/worktree/errors.d.ts +7 -0
- package/dist/core/worktree/errors.d.ts.map +1 -0
- package/dist/core/worktree/errors.js +11 -0
- package/dist/core/worktree/errors.js.map +1 -0
- package/dist/core/worktree/gc.d.ts +39 -0
- package/dist/core/worktree/gc.d.ts.map +1 -0
- package/dist/core/worktree/gc.js +146 -0
- package/dist/core/worktree/gc.js.map +1 -0
- package/dist/core/worktree/git.d.ts +53 -0
- package/dist/core/worktree/git.d.ts.map +1 -0
- package/dist/core/worktree/git.js +166 -0
- package/dist/core/worktree/git.js.map +1 -0
- package/dist/core/worktree/index.d.ts +8 -0
- package/dist/core/worktree/index.d.ts.map +1 -0
- package/dist/core/worktree/index.js +8 -0
- package/dist/core/worktree/index.js.map +1 -0
- package/dist/core/worktree/paths.d.ts +26 -0
- package/dist/core/worktree/paths.d.ts.map +1 -0
- package/dist/core/worktree/paths.js +57 -0
- package/dist/core/worktree/paths.js.map +1 -0
- package/dist/core/worktree/slug.d.ts +6 -0
- package/dist/core/worktree/slug.d.ts.map +1 -0
- package/dist/core/worktree/slug.js +21 -0
- package/dist/core/worktree/slug.js.map +1 -0
- package/dist/local-vault/file-store.d.ts +64 -0
- package/dist/local-vault/file-store.d.ts.map +1 -0
- package/dist/local-vault/file-store.js +225 -0
- package/dist/local-vault/file-store.js.map +1 -0
- package/dist/local-vault/index.d.ts +57 -0
- package/dist/local-vault/index.d.ts.map +1 -0
- package/dist/local-vault/index.js +68 -0
- package/dist/local-vault/index.js.map +1 -0
- package/dist/local-vault/keychain.d.ts +58 -0
- package/dist/local-vault/keychain.d.ts.map +1 -0
- package/dist/local-vault/keychain.js +200 -0
- package/dist/local-vault/keychain.js.map +1 -0
- package/dist/local-vault/mutex.d.ts +20 -0
- package/dist/local-vault/mutex.d.ts.map +1 -0
- package/dist/local-vault/mutex.js +44 -0
- package/dist/local-vault/mutex.js.map +1 -0
- package/dist/local-vault/passphrase.d.ts +30 -0
- package/dist/local-vault/passphrase.d.ts.map +1 -0
- package/dist/local-vault/passphrase.js +72 -0
- package/dist/local-vault/passphrase.js.map +1 -0
- package/dist/lsp/config.d.ts +34 -0
- package/dist/lsp/config.d.ts.map +1 -0
- package/dist/lsp/config.js +68 -0
- package/dist/lsp/config.js.map +1 -0
- package/dist/lsp/detect.d.ts +7 -0
- package/dist/lsp/detect.d.ts.map +1 -0
- package/dist/lsp/detect.js +78 -0
- package/dist/lsp/detect.js.map +1 -0
- package/dist/lsp/errors.d.ts +11 -0
- package/dist/lsp/errors.d.ts.map +1 -0
- package/dist/lsp/errors.js +11 -0
- package/dist/lsp/errors.js.map +1 -0
- package/dist/lsp/formatters.d.ts +147 -0
- package/dist/lsp/formatters.d.ts.map +1 -0
- package/dist/lsp/formatters.js +259 -0
- package/dist/lsp/formatters.js.map +1 -0
- package/dist/lsp/index.d.ts +31 -0
- package/dist/lsp/index.d.ts.map +1 -0
- package/dist/lsp/index.js +31 -0
- package/dist/lsp/index.js.map +1 -0
- package/dist/lsp/instance.d.ts +72 -0
- package/dist/lsp/instance.d.ts.map +1 -0
- package/dist/lsp/instance.js +489 -0
- package/dist/lsp/instance.js.map +1 -0
- package/dist/lsp/lazy-load.d.ts +27 -0
- package/dist/lsp/lazy-load.d.ts.map +1 -0
- package/dist/lsp/lazy-load.js +57 -0
- package/dist/lsp/lazy-load.js.map +1 -0
- package/dist/lsp/manager.d.ts +59 -0
- package/dist/lsp/manager.d.ts.map +1 -0
- package/dist/lsp/manager.js +242 -0
- package/dist/lsp/manager.js.map +1 -0
- package/dist/lsp/ops.d.ts +13 -0
- package/dist/lsp/ops.d.ts.map +1 -0
- package/dist/lsp/ops.js +225 -0
- package/dist/lsp/ops.js.map +1 -0
- package/dist/lsp/rpc.d.ts +47 -0
- package/dist/lsp/rpc.d.ts.map +1 -0
- package/dist/lsp/rpc.js +134 -0
- package/dist/lsp/rpc.js.map +1 -0
- package/dist/lsp/safe-uri.d.ts +18 -0
- package/dist/lsp/safe-uri.d.ts.map +1 -0
- package/dist/lsp/safe-uri.js +96 -0
- package/dist/lsp/safe-uri.js.map +1 -0
- package/dist/lsp/types.d.ts +70 -0
- package/dist/lsp/types.d.ts.map +1 -0
- package/dist/lsp/types.js +16 -0
- package/dist/lsp/types.js.map +1 -0
- package/dist/mcp/bridge.d.ts +57 -0
- package/dist/mcp/bridge.d.ts.map +1 -0
- package/dist/mcp/bridge.js +98 -0
- package/dist/mcp/bridge.js.map +1 -0
- package/dist/mcp/category.d.ts +22 -0
- package/dist/mcp/category.d.ts.map +1 -0
- package/dist/mcp/category.js +11 -0
- package/dist/mcp/category.js.map +1 -0
- package/dist/mcp/client.d.ts +228 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +352 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/content.d.ts +86 -0
- package/dist/mcp/content.d.ts.map +1 -0
- package/dist/mcp/content.js +147 -0
- package/dist/mcp/content.js.map +1 -0
- package/dist/mcp/env.d.ts +19 -0
- package/dist/mcp/env.d.ts.map +1 -0
- package/dist/mcp/env.js +50 -0
- package/dist/mcp/env.js.map +1 -0
- package/dist/mcp/host.d.ts +199 -0
- package/dist/mcp/host.d.ts.map +1 -0
- package/dist/mcp/host.js +530 -0
- package/dist/mcp/host.js.map +1 -0
- package/dist/mcp/index.d.ts +18 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +17 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/load-mcp-host.d.ts +32 -0
- package/dist/mcp/load-mcp-host.d.ts.map +1 -0
- package/dist/mcp/load-mcp-host.js +49 -0
- package/dist/mcp/load-mcp-host.js.map +1 -0
- package/dist/mcp/oauth/callback-server.d.ts +73 -0
- package/dist/mcp/oauth/callback-server.d.ts.map +1 -0
- package/dist/mcp/oauth/callback-server.js +280 -0
- package/dist/mcp/oauth/callback-server.js.map +1 -0
- package/dist/mcp/oauth/config-hash.d.ts +24 -0
- package/dist/mcp/oauth/config-hash.d.ts.map +1 -0
- package/dist/mcp/oauth/config-hash.js +55 -0
- package/dist/mcp/oauth/config-hash.js.map +1 -0
- package/dist/mcp/oauth/error-normalize.d.ts +39 -0
- package/dist/mcp/oauth/error-normalize.d.ts.map +1 -0
- package/dist/mcp/oauth/error-normalize.js +91 -0
- package/dist/mcp/oauth/error-normalize.js.map +1 -0
- package/dist/mcp/oauth/provider.d.ts +190 -0
- package/dist/mcp/oauth/provider.d.ts.map +1 -0
- package/dist/mcp/oauth/provider.js +305 -0
- package/dist/mcp/oauth/provider.js.map +1 -0
- package/dist/mcp/oauth/refresh-coalescer.d.ts +46 -0
- package/dist/mcp/oauth/refresh-coalescer.d.ts.map +1 -0
- package/dist/mcp/oauth/refresh-coalescer.js +77 -0
- package/dist/mcp/oauth/refresh-coalescer.js.map +1 -0
- package/dist/mcp/oauth/sdk-shapes.d.ts +77 -0
- package/dist/mcp/oauth/sdk-shapes.d.ts.map +1 -0
- package/dist/mcp/oauth/sdk-shapes.js +21 -0
- package/dist/mcp/oauth/sdk-shapes.js.map +1 -0
- package/dist/mcp/parse.d.ts +28 -0
- package/dist/mcp/parse.d.ts.map +1 -0
- package/dist/mcp/parse.js +209 -0
- package/dist/mcp/parse.js.map +1 -0
- package/dist/mcp/prompts.d.ts +31 -0
- package/dist/mcp/prompts.d.ts.map +1 -0
- package/dist/mcp/prompts.js +71 -0
- package/dist/mcp/prompts.js.map +1 -0
- package/dist/mcp/redact.d.ts +62 -0
- package/dist/mcp/redact.d.ts.map +1 -0
- package/dist/mcp/redact.js +87 -0
- package/dist/mcp/redact.js.map +1 -0
- package/dist/mcp/resources.d.ts +70 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +170 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/types.d.ts +123 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +2 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/memory/frontmatter.d.ts +18 -0
- package/dist/memory/frontmatter.d.ts.map +1 -0
- package/dist/memory/frontmatter.js +81 -0
- package/dist/memory/frontmatter.js.map +1 -0
- package/dist/memory/index.d.ts +5 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +5 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/store.d.ts +44 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +237 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/memory/tools.d.ts +11 -0
- package/dist/memory/tools.d.ts.map +1 -0
- package/dist/memory/tools.js +159 -0
- package/dist/memory/tools.js.map +1 -0
- package/dist/memory/types.d.ts +32 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +20 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/plugin-api/index.d.ts +167 -0
- package/dist/plugin-api/index.d.ts.map +1 -0
- package/dist/plugin-api/index.js +151 -0
- package/dist/plugin-api/index.js.map +1 -0
- package/dist/plugin-logger/index.d.ts +21 -0
- package/dist/plugin-logger/index.d.ts.map +1 -0
- package/dist/plugin-logger/index.js +59 -0
- package/dist/plugin-logger/index.js.map +1 -0
- package/dist/session/index.d.ts +125 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +202 -0
- package/dist/session/index.js.map +1 -0
- package/dist/tools/approval.d.ts +33 -0
- package/dist/tools/approval.d.ts.map +1 -0
- package/dist/tools/approval.js +53 -0
- package/dist/tools/approval.js.map +1 -0
- package/dist/tools/builtins/_shared.d.ts +94 -0
- package/dist/tools/builtins/_shared.d.ts.map +1 -0
- package/dist/tools/builtins/_shared.js +246 -0
- package/dist/tools/builtins/_shared.js.map +1 -0
- package/dist/tools/builtins/ask-user-question.d.ts +27 -0
- package/dist/tools/builtins/ask-user-question.d.ts.map +1 -0
- package/dist/tools/builtins/ask-user-question.js +191 -0
- package/dist/tools/builtins/ask-user-question.js.map +1 -0
- package/dist/tools/builtins/bash.d.ts +3 -0
- package/dist/tools/builtins/bash.d.ts.map +1 -0
- package/dist/tools/builtins/bash.js +158 -0
- package/dist/tools/builtins/bash.js.map +1 -0
- package/dist/tools/builtins/diff.d.ts +3 -0
- package/dist/tools/builtins/diff.d.ts.map +1 -0
- package/dist/tools/builtins/diff.js +83 -0
- package/dist/tools/builtins/diff.js.map +1 -0
- package/dist/tools/builtins/edit.d.ts +3 -0
- package/dist/tools/builtins/edit.d.ts.map +1 -0
- package/dist/tools/builtins/edit.js +40 -0
- package/dist/tools/builtins/edit.js.map +1 -0
- package/dist/tools/builtins/glob.d.ts +3 -0
- package/dist/tools/builtins/glob.d.ts.map +1 -0
- package/dist/tools/builtins/glob.js +37 -0
- package/dist/tools/builtins/glob.js.map +1 -0
- package/dist/tools/builtins/grep.d.ts +3 -0
- package/dist/tools/builtins/grep.d.ts.map +1 -0
- package/dist/tools/builtins/grep.js +81 -0
- package/dist/tools/builtins/grep.js.map +1 -0
- package/dist/tools/builtins/lsp.d.ts +3 -0
- package/dist/tools/builtins/lsp.d.ts.map +1 -0
- package/dist/tools/builtins/lsp.js +102 -0
- package/dist/tools/builtins/lsp.js.map +1 -0
- package/dist/tools/builtins/pty.d.ts +64 -0
- package/dist/tools/builtins/pty.d.ts.map +1 -0
- package/dist/tools/builtins/pty.js +536 -0
- package/dist/tools/builtins/pty.js.map +1 -0
- package/dist/tools/builtins/read.d.ts +3 -0
- package/dist/tools/builtins/read.d.ts.map +1 -0
- package/dist/tools/builtins/read.js +18 -0
- package/dist/tools/builtins/read.js.map +1 -0
- package/dist/tools/builtins/web-fetch.d.ts +4 -0
- package/dist/tools/builtins/web-fetch.d.ts.map +1 -0
- package/dist/tools/builtins/web-fetch.js +353 -0
- package/dist/tools/builtins/web-fetch.js.map +1 -0
- package/dist/tools/builtins/write.d.ts +3 -0
- package/dist/tools/builtins/write.d.ts.map +1 -0
- package/dist/tools/builtins/write.js +48 -0
- package/dist/tools/builtins/write.js.map +1 -0
- package/dist/tools/builtins.d.ts +9 -0
- package/dist/tools/builtins.d.ts.map +1 -0
- package/dist/tools/builtins.js +29 -0
- package/dist/tools/builtins.js.map +1 -0
- package/dist/tools/diff.d.ts +18 -0
- package/dist/tools/diff.d.ts.map +1 -0
- package/dist/tools/diff.js +57 -0
- package/dist/tools/diff.js.map +1 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +9 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/permission.d.ts +120 -0
- package/dist/tools/permission.d.ts.map +1 -0
- package/dist/tools/permission.js +208 -0
- package/dist/tools/permission.js.map +1 -0
- package/dist/tools/registry.d.ts +12 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +19 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/types.d.ts +244 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +15 -0
- package/dist/tools/types.js.map +1 -0
- package/package.json +109 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (3.7) OS keychain adapter wrapping `@napi-rs/keyring`.
|
|
3
|
+
*
|
|
4
|
+
* Loaded via a computed-specifier dynamic import (same `bun build
|
|
5
|
+
* --compile` reason as 3.6 OTel) so the binary still compiles when the
|
|
6
|
+
* optional peer is absent. A consumer that didn't install the keyring
|
|
7
|
+
* package falls back to the file backend (`./file-store.ts`) — same
|
|
8
|
+
* graceful degradation as before, no opaque "module not found" crash
|
|
9
|
+
* at boot.
|
|
10
|
+
*
|
|
11
|
+
* Layout: service name `chances-cli` (fixed), account name = vault key.
|
|
12
|
+
* Values are JSON-stringified before storage; parsed on read.
|
|
13
|
+
*
|
|
14
|
+
* **(claude-code anti-pattern noted)** Don't store full RFC 8414 metadata
|
|
15
|
+
* here — Windows Credential Manager has a 4096-byte limit on stored
|
|
16
|
+
* values. § 4.2 of the design doc enforces "URLs only" at the higher
|
|
17
|
+
* (provider) layer; this adapter doesn't try to enforce shape.
|
|
18
|
+
*/
|
|
19
|
+
import { KeyedMutex } from "./mutex.js";
|
|
20
|
+
const KEYRING_SERVICE = "chances-cli";
|
|
21
|
+
const KEYRING_PROBE_KEY = "__chances_probe__";
|
|
22
|
+
const defaultLoader = async () => {
|
|
23
|
+
try {
|
|
24
|
+
// Computed specifier — Bun's compile pass cannot statically resolve
|
|
25
|
+
// and therefore can't refuse to compile when the optional peer is absent.
|
|
26
|
+
const specifier = ["@napi-rs", "keyring"].join("/");
|
|
27
|
+
const mod = (await import(specifier));
|
|
28
|
+
if (!mod || typeof mod.Entry !== "function")
|
|
29
|
+
return null;
|
|
30
|
+
return mod;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
export class KeychainStore {
|
|
37
|
+
mod;
|
|
38
|
+
mutex = new KeyedMutex();
|
|
39
|
+
constructor(mod) {
|
|
40
|
+
this.mod = mod;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Loads the keyring module + runs an access probe. Returns:
|
|
44
|
+
* - the store, when the module loaded AND the probe succeeded;
|
|
45
|
+
* - `null` with a reason string, otherwise.
|
|
46
|
+
*
|
|
47
|
+
* The probe is critical on macOS — the module loads fine, but the FIRST
|
|
48
|
+
* keychain op can throw with a user-denied dialog. We catch that here so
|
|
49
|
+
* the fallback path runs without hanging the consumer.
|
|
50
|
+
*/
|
|
51
|
+
static async tryCreate(loader = defaultLoader) {
|
|
52
|
+
let mod;
|
|
53
|
+
try {
|
|
54
|
+
mod = await loader();
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
return { store: null, reason: `keychain loader threw: ${e.message || String(e)}` };
|
|
58
|
+
}
|
|
59
|
+
if (!mod)
|
|
60
|
+
return { store: null, reason: "@napi-rs/keyring not installed" };
|
|
61
|
+
// Access probe — try a setPassword + getPassword round-trip against a
|
|
62
|
+
// probe key, then delete. The exact key is reserved.
|
|
63
|
+
try {
|
|
64
|
+
const probe = new mod.Entry(KEYRING_SERVICE, KEYRING_PROBE_KEY);
|
|
65
|
+
await Promise.resolve(probe.setPassword("ok"));
|
|
66
|
+
const got = await Promise.resolve(probe.getPassword());
|
|
67
|
+
if (got !== "ok") {
|
|
68
|
+
return { store: null, reason: `keychain probe value mismatch (got ${JSON.stringify(got)})` };
|
|
69
|
+
}
|
|
70
|
+
await Promise.resolve(probe.deletePassword());
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
return { store: null, reason: `keychain probe failed: ${e.message || String(e)}` };
|
|
74
|
+
}
|
|
75
|
+
return { store: new KeychainStore(mod) };
|
|
76
|
+
}
|
|
77
|
+
async read(key) {
|
|
78
|
+
const release = await this.mutex.acquire(key);
|
|
79
|
+
try {
|
|
80
|
+
const entry = new this.mod.Entry(KEYRING_SERVICE, key);
|
|
81
|
+
const raw = await Promise.resolve(entry.getPassword());
|
|
82
|
+
if (raw === null || raw === undefined)
|
|
83
|
+
return undefined;
|
|
84
|
+
try {
|
|
85
|
+
return JSON.parse(raw);
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
throw new Error(`local-vault keychain: stored value at '${key}' is not valid JSON: ${e.message}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
release();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async list() {
|
|
96
|
+
// @napi-rs/keyring has no enumeration API across platforms. We track
|
|
97
|
+
// keys in a separate index entry stored under the reserved name
|
|
98
|
+
// `__chances_keys__`. This is a small lie — list() is best-effort and
|
|
99
|
+
// only reflects keys we've written THROUGH this adapter. Out-of-band
|
|
100
|
+
// entries (e.g. user-edited Keychain Access) aren't enumerable; this
|
|
101
|
+
// is the same constraint claude-code's LocalVault carries.
|
|
102
|
+
const idx = await this.read("__chances_keys__");
|
|
103
|
+
return idx ?? [];
|
|
104
|
+
}
|
|
105
|
+
async remove(key) {
|
|
106
|
+
const release = await this.mutex.acquire(key);
|
|
107
|
+
try {
|
|
108
|
+
const entry = new this.mod.Entry(KEYRING_SERVICE, key);
|
|
109
|
+
try {
|
|
110
|
+
await Promise.resolve(entry.deletePassword());
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// The keyring throws on "not present" on some platforms. Idempotent
|
|
114
|
+
// remove — swallow and continue.
|
|
115
|
+
}
|
|
116
|
+
// Update the index.
|
|
117
|
+
const idxKey = "__chances_keys__";
|
|
118
|
+
const idxEntry = new this.mod.Entry(KEYRING_SERVICE, idxKey);
|
|
119
|
+
const raw = await Promise.resolve(idxEntry.getPassword());
|
|
120
|
+
if (raw !== null && raw !== undefined) {
|
|
121
|
+
try {
|
|
122
|
+
const idx = JSON.parse(raw);
|
|
123
|
+
const next = idx.filter((k) => k !== key);
|
|
124
|
+
if (next.length === 0) {
|
|
125
|
+
await Promise.resolve(idxEntry.deletePassword()).catch(() => { });
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
await Promise.resolve(idxEntry.setPassword(JSON.stringify(next)));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Corrupt index — leave alone; list() will return [] for it.
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
release();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async mergeMutate(key, mutator, allowDelete = false) {
|
|
141
|
+
const release = await this.mutex.acquire(key);
|
|
142
|
+
try {
|
|
143
|
+
const entry = new this.mod.Entry(KEYRING_SERVICE, key);
|
|
144
|
+
const raw = await Promise.resolve(entry.getPassword());
|
|
145
|
+
let current;
|
|
146
|
+
if (raw !== null && raw !== undefined) {
|
|
147
|
+
try {
|
|
148
|
+
current = JSON.parse(raw);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Treat corrupt as missing — the mutator gets a clean slate.
|
|
152
|
+
current = undefined;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const next = await mutator(current);
|
|
156
|
+
if (next === undefined) {
|
|
157
|
+
if (allowDelete && current !== undefined) {
|
|
158
|
+
await Promise.resolve(entry.deletePassword()).catch(() => { });
|
|
159
|
+
}
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
await Promise.resolve(entry.setPassword(JSON.stringify(next)));
|
|
163
|
+
// Update the index — best-effort, race tolerant.
|
|
164
|
+
await this.recordKey(key);
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
release();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/** (codex Round-2 NICE #3) Update the index under its OWN mutex so
|
|
171
|
+
* concurrent `mergeMutate("a")` / `mergeMutate("b")` can't race on
|
|
172
|
+
* the shared `__chances_keys__` entry. Each holds its per-key lock
|
|
173
|
+
* for the value write, then queues behind the index mutex for the
|
|
174
|
+
* list update. `list()` could otherwise drop entries. */
|
|
175
|
+
async recordKey(key) {
|
|
176
|
+
const idxKey = "__chances_keys__";
|
|
177
|
+
const release = await this.mutex.acquire(idxKey);
|
|
178
|
+
try {
|
|
179
|
+
const idxEntry = new this.mod.Entry(KEYRING_SERVICE, idxKey);
|
|
180
|
+
const raw = await Promise.resolve(idxEntry.getPassword());
|
|
181
|
+
let idx = [];
|
|
182
|
+
if (raw !== null && raw !== undefined) {
|
|
183
|
+
try {
|
|
184
|
+
idx = JSON.parse(raw);
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
idx = [];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (!idx.includes(key)) {
|
|
191
|
+
idx.push(key);
|
|
192
|
+
await Promise.resolve(idxEntry.setPassword(JSON.stringify(idx))).catch(() => { });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
finally {
|
|
196
|
+
release();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=keychain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/local-vault/keychain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (3.7) Per-key async mutex. Used by `mergeMutate` to serialise
|
|
3
|
+
* read-modify-write cycles against a single vault key without
|
|
4
|
+
* blocking unrelated keys (codex Round-1 MUST-FIX #1).
|
|
5
|
+
*
|
|
6
|
+
* Implementation is a promise-chain keyed by the locked resource id.
|
|
7
|
+
* Each acquire returns a release fn; `finally`-release in callers.
|
|
8
|
+
* No timeout / no cancellation — vault writes are short, and a
|
|
9
|
+
* never-released lock would be a real bug, not something to paper over.
|
|
10
|
+
*/
|
|
11
|
+
export declare class KeyedMutex {
|
|
12
|
+
private locks;
|
|
13
|
+
/** Acquires the lock for `key`. Returns a release fn the caller MUST
|
|
14
|
+
* invoke (use try/finally). Concurrent acquires for the same key
|
|
15
|
+
* serialise; different keys do not block each other. */
|
|
16
|
+
acquire(key: string): Promise<() => void>;
|
|
17
|
+
/** Diagnostics — number of distinct keys currently locked. Test seam. */
|
|
18
|
+
size(): number;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=mutex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutex.d.ts","sourceRoot":"","sources":["../../src/local-vault/mutex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAoC;IAEjD;;6DAEyD;IACnD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC;IAwB/C,yEAAyE;IACzE,IAAI,IAAI,MAAM;CAGf"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (3.7) Per-key async mutex. Used by `mergeMutate` to serialise
|
|
3
|
+
* read-modify-write cycles against a single vault key without
|
|
4
|
+
* blocking unrelated keys (codex Round-1 MUST-FIX #1).
|
|
5
|
+
*
|
|
6
|
+
* Implementation is a promise-chain keyed by the locked resource id.
|
|
7
|
+
* Each acquire returns a release fn; `finally`-release in callers.
|
|
8
|
+
* No timeout / no cancellation — vault writes are short, and a
|
|
9
|
+
* never-released lock would be a real bug, not something to paper over.
|
|
10
|
+
*/
|
|
11
|
+
export class KeyedMutex {
|
|
12
|
+
locks = new Map();
|
|
13
|
+
/** Acquires the lock for `key`. Returns a release fn the caller MUST
|
|
14
|
+
* invoke (use try/finally). Concurrent acquires for the same key
|
|
15
|
+
* serialise; different keys do not block each other. */
|
|
16
|
+
async acquire(key) {
|
|
17
|
+
const previous = this.locks.get(key) ?? Promise.resolve();
|
|
18
|
+
let release;
|
|
19
|
+
const next = new Promise((resolve) => {
|
|
20
|
+
release = () => {
|
|
21
|
+
// Only clear the slot if WE are still the head — protects
|
|
22
|
+
// against pathological orderings where release fires twice or
|
|
23
|
+
// after a logically-distinct cycle reused the same key.
|
|
24
|
+
if (this.locks.get(key) === next) {
|
|
25
|
+
this.locks.delete(key);
|
|
26
|
+
}
|
|
27
|
+
resolve();
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
this.locks.set(key, next);
|
|
31
|
+
// Swallow any rejection on the previous lock — every awaiter
|
|
32
|
+
// proceeds once the previous holder has FINISHED, regardless of
|
|
33
|
+
// outcome. (A holder that throws still runs its `finally` and
|
|
34
|
+
// calls release; the resolved next promise here is what callers
|
|
35
|
+
// chain off, so the rejection doesn't propagate to siblings.)
|
|
36
|
+
await previous.catch(() => { });
|
|
37
|
+
return release;
|
|
38
|
+
}
|
|
39
|
+
/** Diagnostics — number of distinct keys currently locked. Test seam. */
|
|
40
|
+
size() {
|
|
41
|
+
return this.locks.size;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=mutex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutex.js","sourceRoot":"","sources":["../../src/local-vault/mutex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,OAAO,UAAU;IACb,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;IAEjD;;6DAEyD;IACzD,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAC1D,IAAI,OAAoB,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACzC,OAAO,GAAG,GAAG,EAAE;gBACb,0DAA0D;gBAC1D,8DAA8D;gBAC9D,wDAAwD;gBACxD,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;oBACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACzB,CAAC;gBACD,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1B,6DAA6D;QAC7D,gEAAgE;QAChE,8DAA8D;QAC9D,gEAAgE;QAChE,8DAA8D;QAC9D,MAAM,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC/B,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,yEAAyE;IACzE,IAAI;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;CACF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (3.7) Passphrase resolution for the AES-GCM file backend.
|
|
3
|
+
*
|
|
4
|
+
* Order:
|
|
5
|
+
* 1. CHANCES_VAULT_PASSPHRASE env var (≥16 chars)
|
|
6
|
+
* 2. <passphraseFile> (0600), if present and ≥16 chars
|
|
7
|
+
* 3. Auto-generate 32 random bytes → hex; persist to passphraseFile@0600
|
|
8
|
+
*
|
|
9
|
+
* The passphrase file lives OUTSIDE the vault directory by default
|
|
10
|
+
* (`~/.chances-vault-passphrase`, sibling to `~/.chances/`). codex
|
|
11
|
+
* Round-1 SHOULD-FIX: storing the key inside the vault dir defeats
|
|
12
|
+
* the encryption against a full-tree backup leak.
|
|
13
|
+
*/
|
|
14
|
+
export declare const MIN_PASSPHRASE_LENGTH = 16;
|
|
15
|
+
export declare const ENV_VAR = "CHANCES_VAULT_PASSPHRASE";
|
|
16
|
+
export interface ResolvePassphraseOptions {
|
|
17
|
+
/** Absolute path. Default caller's `~/.chances-vault-passphrase`. */
|
|
18
|
+
passphraseFile: string;
|
|
19
|
+
/** Test seam — defaults to `process.env`. */
|
|
20
|
+
env?: Record<string, string | undefined>;
|
|
21
|
+
/** Test seam — defaults to `randomBytes`. */
|
|
22
|
+
generateBytes?: (n: number) => Buffer;
|
|
23
|
+
}
|
|
24
|
+
export interface ResolvedPassphrase {
|
|
25
|
+
passphrase: string;
|
|
26
|
+
/** Where the passphrase came from. Surfaced via vault diagnostics. */
|
|
27
|
+
source: "env" | "file" | "generated";
|
|
28
|
+
}
|
|
29
|
+
export declare function resolvePassphrase(opts: ResolvePassphraseOptions): ResolvedPassphrase;
|
|
30
|
+
//# sourceMappingURL=passphrase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passphrase.d.ts","sourceRoot":"","sources":["../../src/local-vault/passphrase.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,OAAO,6BAA6B,CAAC;AAElD,MAAM,WAAW,wBAAwB;IACvC,qEAAqE;IACrE,cAAc,EAAE,MAAM,CAAC;IACvB,6CAA6C;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,6CAA6C;IAC7C,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,WAAW,CAAC;CACtC;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,GAAG,kBAAkB,CAmCpF"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (3.7) Passphrase resolution for the AES-GCM file backend.
|
|
3
|
+
*
|
|
4
|
+
* Order:
|
|
5
|
+
* 1. CHANCES_VAULT_PASSPHRASE env var (≥16 chars)
|
|
6
|
+
* 2. <passphraseFile> (0600), if present and ≥16 chars
|
|
7
|
+
* 3. Auto-generate 32 random bytes → hex; persist to passphraseFile@0600
|
|
8
|
+
*
|
|
9
|
+
* The passphrase file lives OUTSIDE the vault directory by default
|
|
10
|
+
* (`~/.chances-vault-passphrase`, sibling to `~/.chances/`). codex
|
|
11
|
+
* Round-1 SHOULD-FIX: storing the key inside the vault dir defeats
|
|
12
|
+
* the encryption against a full-tree backup leak.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync, readFileSync, writeFileSync, statSync, chmodSync } from "node:fs";
|
|
15
|
+
import { randomBytes } from "node:crypto";
|
|
16
|
+
export const MIN_PASSPHRASE_LENGTH = 16;
|
|
17
|
+
export const ENV_VAR = "CHANCES_VAULT_PASSPHRASE";
|
|
18
|
+
export function resolvePassphrase(opts) {
|
|
19
|
+
const env = opts.env ?? process.env;
|
|
20
|
+
const fromEnv = env[ENV_VAR];
|
|
21
|
+
if (fromEnv !== undefined && fromEnv.length > 0) {
|
|
22
|
+
if (fromEnv.length < MIN_PASSPHRASE_LENGTH) {
|
|
23
|
+
throw new Error(`${ENV_VAR} is too short (got ${fromEnv.length} chars, need ≥${MIN_PASSPHRASE_LENGTH}). ` +
|
|
24
|
+
`Use a longer secret or unset the env var to fall back to the file/generated passphrase.`);
|
|
25
|
+
}
|
|
26
|
+
return { passphrase: fromEnv, source: "env" };
|
|
27
|
+
}
|
|
28
|
+
if (existsSync(opts.passphraseFile)) {
|
|
29
|
+
const fileContents = readFileSync(opts.passphraseFile, "utf8").trim();
|
|
30
|
+
if (fileContents.length < MIN_PASSPHRASE_LENGTH) {
|
|
31
|
+
// Treat short file as missing — regenerate. Don't error here because
|
|
32
|
+
// the user may have hand-edited the file partially; we'd rather
|
|
33
|
+
// recover than refuse to start.
|
|
34
|
+
// (This is documented in tests so a future maintainer sees the choice.)
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
ensure0600(opts.passphraseFile);
|
|
38
|
+
return { passphrase: fileContents, source: "file" };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Generate. 32 random bytes hex = 64 chars; well above 16-char floor and
|
|
42
|
+
// strong enough that KDF cost is irrelevant to security analysis.
|
|
43
|
+
const gen = opts.generateBytes ?? randomBytes;
|
|
44
|
+
const passphrase = gen(32).toString("hex");
|
|
45
|
+
writeFileSync(opts.passphraseFile, passphrase, { mode: 0o600 });
|
|
46
|
+
// POSIX honors `mode` on the open() flag. On Windows, mode is largely
|
|
47
|
+
// a hint; ensure0600 below verifies and warns only on POSIX.
|
|
48
|
+
ensure0600(opts.passphraseFile);
|
|
49
|
+
return { passphrase, source: "generated" };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Best-effort: enforce 0600 mode on POSIX systems. On Windows the bit
|
|
53
|
+
* is largely advisory; a wholly different ACL story applies (out of
|
|
54
|
+
* scope for v1 — would need `windows-sys` or PowerShell hooks).
|
|
55
|
+
*/
|
|
56
|
+
function ensure0600(path) {
|
|
57
|
+
if (process.platform === "win32")
|
|
58
|
+
return;
|
|
59
|
+
try {
|
|
60
|
+
const stat = statSync(path);
|
|
61
|
+
const mode = stat.mode & 0o777;
|
|
62
|
+
if (mode !== 0o600) {
|
|
63
|
+
chmodSync(path, 0o600);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// statSync/chmodSync can fail on a race (file deleted between writeFileSync
|
|
68
|
+
// and stat). Swallow — the caller's next read will surface the real issue
|
|
69
|
+
// with a more actionable error.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=passphrase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passphrase.js","sourceRoot":"","sources":["../../src/local-vault/passphrase.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,CAAC;AACxC,MAAM,CAAC,MAAM,OAAO,GAAG,0BAA0B,CAAC;AAiBlD,MAAM,UAAU,iBAAiB,CAAC,IAA8B;IAC9D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACpC,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7B,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChD,IAAI,OAAO,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CACb,GAAG,OAAO,sBAAsB,OAAO,CAAC,MAAM,iBAAiB,qBAAqB,KAAK;gBACvF,yFAAyF,CAC5F,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAChD,CAAC;IAED,IAAI,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;QACpC,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QACtE,IAAI,YAAY,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;YAChD,qEAAqE;YACrE,gEAAgE;YAChE,gCAAgC;YAChC,wEAAwE;QAC1E,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAChC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QACtD,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,kEAAkE;IAClE,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,IAAI,WAAW,CAAC;IAC9C,MAAM,UAAU,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC3C,aAAa,CAAC,IAAI,CAAC,cAAc,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAChE,sEAAsE;IACtE,6DAA6D;IAC7D,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAChC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO;IACzC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;QAC/B,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YACnB,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,4EAA4E;QAC5E,0EAA0E;QAC1E,gCAAgC;IAClC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (4.2) Built-in default LSP server registry + user-override merger.
|
|
3
|
+
*
|
|
4
|
+
* Selection order (codex Round-1 NICE #1):
|
|
5
|
+
* - User-defined entries are considered BEFORE built-ins.
|
|
6
|
+
* - Within each group, object insertion order wins on extension
|
|
7
|
+
* tie-breaks.
|
|
8
|
+
*
|
|
9
|
+
* Adding a built-in is a one-entry append + extension list. The
|
|
10
|
+
* resolved list is the ordered union, with `enabled: false` filtered
|
|
11
|
+
* out at the call site so the disable flag is visible in surveys.
|
|
12
|
+
*/
|
|
13
|
+
import type { LspServerConfig, ResolvedServer } from "./types.js";
|
|
14
|
+
/** Built-in defaults for the top 4 servers (codex Round-1 NICE #1).
|
|
15
|
+
* Order in this object is the tie-break order for extension claims
|
|
16
|
+
* among built-ins. Insertion order is stable in modern V8/Spidermonkey
|
|
17
|
+
* for string keys. */
|
|
18
|
+
export declare const BUILTIN_DEFAULTS: Readonly<Record<string, LspServerConfig>>;
|
|
19
|
+
/** Merges user-provided servers with built-in defaults.
|
|
20
|
+
*
|
|
21
|
+
* Per-name overlay: a user entry by the same name shallow-merges
|
|
22
|
+
* with the built-in (user fields win). Disabling a built-in is
|
|
23
|
+
* `servers.typescript = { enabled: false }`.
|
|
24
|
+
*
|
|
25
|
+
* Output order: user-defined names first (in user insertion order),
|
|
26
|
+
* then any built-ins not overridden by name (in built-in order).
|
|
27
|
+
* `enabled: false` entries are NOT filtered here — they're surfaced
|
|
28
|
+
* to the caller (e.g. `doctor`) so the disable is visible. */
|
|
29
|
+
export declare function resolveServers(builtIns?: Readonly<Record<string, LspServerConfig>>, userServers?: Readonly<Record<string, LspServerConfig>>): ResolvedServer[];
|
|
30
|
+
/** Picks the first ENABLED server whose `extensions` covers `ext`.
|
|
31
|
+
* Returns undefined when no server claims the extension OR when all
|
|
32
|
+
* matches are disabled. */
|
|
33
|
+
export declare function pickServerForExtension(servers: readonly ResolvedServer[], ext: string): ResolvedServer | undefined;
|
|
34
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lsp/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAElE;;;uBAGuB;AACvB,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAqBtE,CAAC;AAEF;;;;;;;;;+DAS+D;AAC/D,wBAAgB,cAAc,CAC5B,QAAQ,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAoB,EACtE,WAAW,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAM,GAC1D,cAAc,EAAE,CAiBlB;AAED;;4BAE4B;AAC5B,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,GAAG,EAAE,MAAM,GACV,cAAc,GAAG,SAAS,CAS5B"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** Built-in defaults for the top 4 servers (codex Round-1 NICE #1).
|
|
2
|
+
* Order in this object is the tie-break order for extension claims
|
|
3
|
+
* among built-ins. Insertion order is stable in modern V8/Spidermonkey
|
|
4
|
+
* for string keys. */
|
|
5
|
+
export const BUILTIN_DEFAULTS = {
|
|
6
|
+
typescript: {
|
|
7
|
+
command: "typescript-language-server",
|
|
8
|
+
args: ["--stdio"],
|
|
9
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"],
|
|
10
|
+
},
|
|
11
|
+
rust: {
|
|
12
|
+
command: "rust-analyzer",
|
|
13
|
+
args: [],
|
|
14
|
+
extensions: [".rs"],
|
|
15
|
+
},
|
|
16
|
+
python: {
|
|
17
|
+
command: "pyright-langserver",
|
|
18
|
+
args: ["--stdio"],
|
|
19
|
+
extensions: [".py", ".pyi"],
|
|
20
|
+
},
|
|
21
|
+
go: {
|
|
22
|
+
command: "gopls",
|
|
23
|
+
args: ["serve"],
|
|
24
|
+
extensions: [".go"],
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
/** Merges user-provided servers with built-in defaults.
|
|
28
|
+
*
|
|
29
|
+
* Per-name overlay: a user entry by the same name shallow-merges
|
|
30
|
+
* with the built-in (user fields win). Disabling a built-in is
|
|
31
|
+
* `servers.typescript = { enabled: false }`.
|
|
32
|
+
*
|
|
33
|
+
* Output order: user-defined names first (in user insertion order),
|
|
34
|
+
* then any built-ins not overridden by name (in built-in order).
|
|
35
|
+
* `enabled: false` entries are NOT filtered here — they're surfaced
|
|
36
|
+
* to the caller (e.g. `doctor`) so the disable is visible. */
|
|
37
|
+
export function resolveServers(builtIns = BUILTIN_DEFAULTS, userServers = {}) {
|
|
38
|
+
const out = [];
|
|
39
|
+
const seen = new Set();
|
|
40
|
+
for (const [name, userCfg] of Object.entries(userServers)) {
|
|
41
|
+
seen.add(name);
|
|
42
|
+
const builtIn = builtIns[name];
|
|
43
|
+
const merged = builtIn ? { ...builtIn, ...userCfg } : userCfg;
|
|
44
|
+
out.push({ name, config: merged });
|
|
45
|
+
}
|
|
46
|
+
for (const [name, cfg] of Object.entries(builtIns)) {
|
|
47
|
+
if (seen.has(name))
|
|
48
|
+
continue;
|
|
49
|
+
out.push({ name, config: cfg });
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
/** Picks the first ENABLED server whose `extensions` covers `ext`.
|
|
54
|
+
* Returns undefined when no server claims the extension OR when all
|
|
55
|
+
* matches are disabled. */
|
|
56
|
+
export function pickServerForExtension(servers, ext) {
|
|
57
|
+
const norm = ext.toLowerCase();
|
|
58
|
+
for (const s of servers) {
|
|
59
|
+
if (s.config.enabled === false)
|
|
60
|
+
continue;
|
|
61
|
+
for (const e of s.config.extensions) {
|
|
62
|
+
if (e.toLowerCase() === norm)
|
|
63
|
+
return s;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lsp/config.ts"],"names":[],"mappings":"AAcA;;;uBAGuB;AACvB,MAAM,CAAC,MAAM,gBAAgB,GAA8C;IACzE,UAAU,EAAE;QACV,OAAO,EAAE,4BAA4B;QACrC,IAAI,EAAE,CAAC,SAAS,CAAC;QACjB,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;KAC3D;IACD,IAAI,EAAE;QACJ,OAAO,EAAE,eAAe;QACxB,IAAI,EAAE,EAAE;QACR,UAAU,EAAE,CAAC,KAAK,CAAC;KACpB;IACD,MAAM,EAAE;QACN,OAAO,EAAE,oBAAoB;QAC7B,IAAI,EAAE,CAAC,SAAS,CAAC;QACjB,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC;KAC5B;IACD,EAAE,EAAE;QACF,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE,CAAC,OAAO,CAAC;QACf,UAAU,EAAE,CAAC,KAAK,CAAC;KACpB;CACF,CAAC;AAEF;;;;;;;;;+DAS+D;AAC/D,MAAM,UAAU,cAAc,CAC5B,WAAsD,gBAAgB,EACtE,cAAyD,EAAE;IAE3D,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QAC1D,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACf,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAoB,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;QAC/E,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnD,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAC7B,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;4BAE4B;AAC5B,MAAM,UAAU,sBAAsB,CACpC,OAAkC,EAClC,GAAW;IAEX,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,KAAK,KAAK;YAAE,SAAS;QACzC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YACpC,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI;gBAAE,OAAO,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Returns the absolute path of `command` resolved against `cwd`'s
|
|
2
|
+
* project-local bin dirs first, then PATH. Returns `null` (cached)
|
|
3
|
+
* when nothing was found. */
|
|
4
|
+
export declare function detectBinary(command: string, cwd: string, env?: NodeJS.ProcessEnv): string | null;
|
|
5
|
+
/** Test seam — clears the cache. */
|
|
6
|
+
export declare function clearDetectCache(): void;
|
|
7
|
+
//# sourceMappingURL=detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/lsp/detect.ts"],"names":[],"mappings":"AAmBA;;8BAE8B;AAC9B,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,IAAI,CA8B9G;AAyBD,oCAAoC;AACpC,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (4.2) Binary detection (codex Round-1 SHOULD-FIX #3).
|
|
3
|
+
*
|
|
4
|
+
* Resolution order per (cwd, command):
|
|
5
|
+
* 1. `<cwd>/node_modules/.bin/<command>[.exe|.cmd|.bat on Windows]`
|
|
6
|
+
* 2. `<cwd>/.venv/bin/<command>` (POSIX)
|
|
7
|
+
* 3. Each `PATH` entry, with Windows suffix expansion.
|
|
8
|
+
*
|
|
9
|
+
* Cached per `(cwd, command, PATH)` so a `doctor` run that touches
|
|
10
|
+
* every default doesn't repeat work. Cache lifetime is the host
|
|
11
|
+
* instance; explicit `clearDetectCache()` for tests.
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, statSync } from "node:fs";
|
|
14
|
+
import { delimiter as PATH_SEP, join } from "node:path";
|
|
15
|
+
const cache = new Map();
|
|
16
|
+
const WIN_SUFFIXES = process.platform === "win32" ? [".exe", ".cmd", ".bat", ""] : [""];
|
|
17
|
+
/** Returns the absolute path of `command` resolved against `cwd`'s
|
|
18
|
+
* project-local bin dirs first, then PATH. Returns `null` (cached)
|
|
19
|
+
* when nothing was found. */
|
|
20
|
+
export function detectBinary(command, cwd, env = process.env) {
|
|
21
|
+
// Absolute path bypass — caller already gave us a resolved binary.
|
|
22
|
+
if (isAbsolute(command)) {
|
|
23
|
+
return fileExistsExecutable(command) ? command : null;
|
|
24
|
+
}
|
|
25
|
+
const path = env.PATH ?? env.Path ?? "";
|
|
26
|
+
const key = `${cwd}\u0000${command}\u0000${path}`;
|
|
27
|
+
const cached = cache.get(key);
|
|
28
|
+
if (cached !== undefined)
|
|
29
|
+
return cached;
|
|
30
|
+
const candidates = [];
|
|
31
|
+
// Project-local bin dirs.
|
|
32
|
+
candidates.push(...expand(join(cwd, "node_modules", ".bin"), command));
|
|
33
|
+
if (process.platform !== "win32") {
|
|
34
|
+
candidates.push(join(cwd, ".venv", "bin", command));
|
|
35
|
+
}
|
|
36
|
+
// PATH entries.
|
|
37
|
+
for (const dir of path.split(PATH_SEP)) {
|
|
38
|
+
if (!dir)
|
|
39
|
+
continue;
|
|
40
|
+
candidates.push(...expand(dir, command));
|
|
41
|
+
}
|
|
42
|
+
for (const c of candidates) {
|
|
43
|
+
if (fileExistsExecutable(c)) {
|
|
44
|
+
cache.set(key, c);
|
|
45
|
+
return c;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
cache.set(key, null);
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
/** Windows: try every suffix; POSIX: just the bare name. */
|
|
52
|
+
function expand(dir, command) {
|
|
53
|
+
return WIN_SUFFIXES.map((suf) => join(dir, command + suf));
|
|
54
|
+
}
|
|
55
|
+
/** Bun's `existsSync` follows symlinks. We additionally require the
|
|
56
|
+
* entry to be a file (not a directory) — defends against a weird
|
|
57
|
+
* `node_modules/.bin/<name>` being a folder. */
|
|
58
|
+
function fileExistsExecutable(p) {
|
|
59
|
+
if (!existsSync(p))
|
|
60
|
+
return false;
|
|
61
|
+
try {
|
|
62
|
+
const st = statSync(p);
|
|
63
|
+
return st.isFile();
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function isAbsolute(p) {
|
|
70
|
+
if (process.platform === "win32")
|
|
71
|
+
return /^[a-zA-Z]:[\\/]/.test(p) || p.startsWith("\\\\");
|
|
72
|
+
return p.startsWith("/");
|
|
73
|
+
}
|
|
74
|
+
/** Test seam — clears the cache. */
|
|
75
|
+
export function clearDetectCache() {
|
|
76
|
+
cache.clear();
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/lsp/detect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (4.2) LSP error taxonomy. Distinct codes per failure mode so the tool
|
|
3
|
+
* layer renders an actionable message without parsing free-text.
|
|
4
|
+
*/
|
|
5
|
+
export type LspErrorCode = "BINARY_MISSING" | "START_TIMEOUT" | "REQUEST_TIMEOUT" | "REQUEST_FAILED" | "SERVER_CRASHED" | "SERVER_STICKY_FAILED" | "UNSUPPORTED_OP" | "MAX_INSTANCES" | "FILE_TOO_LARGE" | "NO_SERVER_FOR_EXTENSION" | "DISABLED";
|
|
6
|
+
export declare class LspError extends Error {
|
|
7
|
+
readonly code: LspErrorCode;
|
|
8
|
+
readonly cause?: unknown;
|
|
9
|
+
constructor(code: LspErrorCode, message: string, cause?: unknown);
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/lsp/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,YAAY,GACpB,gBAAgB,GAChB,eAAe,GACf,iBAAiB,GACjB,gBAAgB,GAChB,gBAAgB,GAChB,sBAAsB,GACtB,gBAAgB,GAChB,eAAe,GACf,gBAAgB,GAChB,yBAAyB,GACzB,UAAU,CAAC;AAEf,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,SAAkB,KAAK,CAAC,EAAE,OAAO,CAAC;gBACtB,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAMjE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lsp/errors.ts"],"names":[],"mappings":"AAiBA,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,IAAI,CAAe;IACV,KAAK,CAAW;IAClC,YAAY,IAAkB,EAAE,OAAe,EAAE,KAAe;QAC9D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF"}
|