nodedex 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/adapters/claude-code-watcher.mjs +336 -0
- package/adapters/hermes-statedb-watcher.mjs +234 -0
- package/adapters/nodedex-capture-core.mjs +129 -0
- package/adapters/nodedex-capture.mjs +169 -0
- package/dist/agent-protocol.d.ts +7 -0
- package/dist/agent-protocol.d.ts.map +1 -0
- package/dist/agent-protocol.js +38 -0
- package/dist/agent-protocol.js.map +1 -0
- package/dist/api-server.d.ts +5 -0
- package/dist/api-server.d.ts.map +1 -0
- package/dist/api-server.js +351 -0
- package/dist/api-server.js.map +1 -0
- package/dist/boot-env.d.ts +2 -0
- package/dist/boot-env.d.ts.map +1 -0
- package/dist/boot-env.js +12 -0
- package/dist/boot-env.js.map +1 -0
- package/dist/engine/__tests__/search-core.test.d.ts +2 -0
- package/dist/engine/__tests__/search-core.test.d.ts.map +1 -0
- package/dist/engine/__tests__/search-core.test.js +139 -0
- package/dist/engine/__tests__/search-core.test.js.map +1 -0
- package/dist/engine/ai-provider.d.ts +45 -0
- package/dist/engine/ai-provider.d.ts.map +1 -0
- package/dist/engine/ai-provider.js +5 -0
- package/dist/engine/ai-provider.js.map +1 -0
- package/dist/engine/embeddings.d.ts +51 -0
- package/dist/engine/embeddings.d.ts.map +1 -0
- package/dist/engine/embeddings.js +89 -0
- package/dist/engine/embeddings.js.map +1 -0
- package/dist/engine/providers/__tests__/failure-policy.test.d.ts +2 -0
- package/dist/engine/providers/__tests__/failure-policy.test.d.ts.map +1 -0
- package/dist/engine/providers/__tests__/failure-policy.test.js +134 -0
- package/dist/engine/providers/__tests__/failure-policy.test.js.map +1 -0
- package/dist/engine/providers/__tests__/model-caps.test.d.ts +2 -0
- package/dist/engine/providers/__tests__/model-caps.test.d.ts.map +1 -0
- package/dist/engine/providers/__tests__/model-caps.test.js +38 -0
- package/dist/engine/providers/__tests__/model-caps.test.js.map +1 -0
- package/dist/engine/providers/__tests__/openai-structured.test.d.ts +2 -0
- package/dist/engine/providers/__tests__/openai-structured.test.d.ts.map +1 -0
- package/dist/engine/providers/__tests__/openai-structured.test.js +73 -0
- package/dist/engine/providers/__tests__/openai-structured.test.js.map +1 -0
- package/dist/engine/providers/__tests__/usage-ledger.test.d.ts +2 -0
- package/dist/engine/providers/__tests__/usage-ledger.test.d.ts.map +1 -0
- package/dist/engine/providers/__tests__/usage-ledger.test.js +108 -0
- package/dist/engine/providers/__tests__/usage-ledger.test.js.map +1 -0
- package/dist/engine/providers/anthropic.d.ts +17 -0
- package/dist/engine/providers/anthropic.d.ts.map +1 -0
- package/dist/engine/providers/anthropic.js +125 -0
- package/dist/engine/providers/anthropic.js.map +1 -0
- package/dist/engine/providers/failure-policy.d.ts +56 -0
- package/dist/engine/providers/failure-policy.d.ts.map +1 -0
- package/dist/engine/providers/failure-policy.js +120 -0
- package/dist/engine/providers/failure-policy.js.map +1 -0
- package/dist/engine/providers/gemini.d.ts +22 -0
- package/dist/engine/providers/gemini.d.ts.map +1 -0
- package/dist/engine/providers/gemini.js +180 -0
- package/dist/engine/providers/gemini.js.map +1 -0
- package/dist/engine/providers/index.d.ts +8 -0
- package/dist/engine/providers/index.d.ts.map +1 -0
- package/dist/engine/providers/index.js +67 -0
- package/dist/engine/providers/index.js.map +1 -0
- package/dist/engine/providers/local.d.ts +12 -0
- package/dist/engine/providers/local.d.ts.map +1 -0
- package/dist/engine/providers/local.js +46 -0
- package/dist/engine/providers/local.js.map +1 -0
- package/dist/engine/providers/model-caps.d.ts +6 -0
- package/dist/engine/providers/model-caps.d.ts.map +1 -0
- package/dist/engine/providers/model-caps.js +49 -0
- package/dist/engine/providers/model-caps.js.map +1 -0
- package/dist/engine/providers/openai.d.ts +30 -0
- package/dist/engine/providers/openai.d.ts.map +1 -0
- package/dist/engine/providers/openai.js +309 -0
- package/dist/engine/providers/openai.js.map +1 -0
- package/dist/engine/providers/usage-ledger.d.ts +69 -0
- package/dist/engine/providers/usage-ledger.d.ts.map +1 -0
- package/dist/engine/providers/usage-ledger.js +209 -0
- package/dist/engine/providers/usage-ledger.js.map +1 -0
- package/dist/engine/search-core.d.ts +40 -0
- package/dist/engine/search-core.d.ts.map +1 -0
- package/dist/engine/search-core.js +109 -0
- package/dist/engine/search-core.js.map +1 -0
- package/dist/engine/vector-math.d.ts +5 -0
- package/dist/engine/vector-math.d.ts.map +1 -0
- package/dist/engine/vector-math.js +25 -0
- package/dist/engine/vector-math.js.map +1 -0
- package/dist/home-env.d.ts +26 -0
- package/dist/home-env.d.ts.map +1 -0
- package/dist/home-env.js +87 -0
- package/dist/home-env.js.map +1 -0
- package/dist/mcp-server.d.ts +13 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +79 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/middleware/auth.d.ts +23 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +104 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/auto-recall.d.ts +7 -0
- package/dist/middleware/auto-recall.d.ts.map +1 -0
- package/dist/middleware/auto-recall.js +257 -0
- package/dist/middleware/auto-recall.js.map +1 -0
- package/dist/middleware/auto-reflect.d.ts +4 -0
- package/dist/middleware/auto-reflect.d.ts.map +1 -0
- package/dist/middleware/auto-reflect.js +5 -0
- package/dist/middleware/auto-reflect.js.map +1 -0
- package/dist/middleware/reflect/apply-flag-verdict.d.ts +27 -0
- package/dist/middleware/reflect/apply-flag-verdict.d.ts.map +1 -0
- package/dist/middleware/reflect/apply-flag-verdict.js +57 -0
- package/dist/middleware/reflect/apply-flag-verdict.js.map +1 -0
- package/dist/middleware/reflect/arc-entity-resolve.d.ts +29 -0
- package/dist/middleware/reflect/arc-entity-resolve.d.ts.map +1 -0
- package/dist/middleware/reflect/arc-entity-resolve.js +356 -0
- package/dist/middleware/reflect/arc-entity-resolve.js.map +1 -0
- package/dist/middleware/reflect/arc-inactivity-timer.d.ts +47 -0
- package/dist/middleware/reflect/arc-inactivity-timer.d.ts.map +1 -0
- package/dist/middleware/reflect/arc-inactivity-timer.js +175 -0
- package/dist/middleware/reflect/arc-inactivity-timer.js.map +1 -0
- package/dist/middleware/reflect/arc-pipeline.d.ts +33 -0
- package/dist/middleware/reflect/arc-pipeline.d.ts.map +1 -0
- package/dist/middleware/reflect/arc-pipeline.js +498 -0
- package/dist/middleware/reflect/arc-pipeline.js.map +1 -0
- package/dist/middleware/reflect/comprehend-pergroup.d.ts +100 -0
- package/dist/middleware/reflect/comprehend-pergroup.d.ts.map +1 -0
- package/dist/middleware/reflect/comprehend-pergroup.js +610 -0
- package/dist/middleware/reflect/comprehend-pergroup.js.map +1 -0
- package/dist/middleware/reflect/comprehend.d.ts +237 -0
- package/dist/middleware/reflect/comprehend.d.ts.map +1 -0
- package/dist/middleware/reflect/comprehend.js +706 -0
- package/dist/middleware/reflect/comprehend.js.map +1 -0
- package/dist/middleware/reflect/config.d.ts +34 -0
- package/dist/middleware/reflect/config.d.ts.map +1 -0
- package/dist/middleware/reflect/config.js +131 -0
- package/dist/middleware/reflect/config.js.map +1 -0
- package/dist/middleware/reflect/context.d.ts +138 -0
- package/dist/middleware/reflect/context.d.ts.map +1 -0
- package/dist/middleware/reflect/context.js +619 -0
- package/dist/middleware/reflect/context.js.map +1 -0
- package/dist/middleware/reflect/cost-breakdown.d.ts +69 -0
- package/dist/middleware/reflect/cost-breakdown.d.ts.map +1 -0
- package/dist/middleware/reflect/cost-breakdown.js +63 -0
- package/dist/middleware/reflect/cost-breakdown.js.map +1 -0
- package/dist/middleware/reflect/cost-guard.d.ts +102 -0
- package/dist/middleware/reflect/cost-guard.d.ts.map +1 -0
- package/dist/middleware/reflect/cost-guard.js +243 -0
- package/dist/middleware/reflect/cost-guard.js.map +1 -0
- package/dist/middleware/reflect/cost-pricing.d.ts +54 -0
- package/dist/middleware/reflect/cost-pricing.d.ts.map +1 -0
- package/dist/middleware/reflect/cost-pricing.js +148 -0
- package/dist/middleware/reflect/cost-pricing.js.map +1 -0
- package/dist/middleware/reflect/cross-group-link.d.ts +61 -0
- package/dist/middleware/reflect/cross-group-link.d.ts.map +1 -0
- package/dist/middleware/reflect/cross-group-link.js +212 -0
- package/dist/middleware/reflect/cross-group-link.js.map +1 -0
- package/dist/middleware/reflect/dedup-by-source-and-value.d.ts +70 -0
- package/dist/middleware/reflect/dedup-by-source-and-value.d.ts.map +1 -0
- package/dist/middleware/reflect/dedup-by-source-and-value.js +0 -0
- package/dist/middleware/reflect/dedup-by-source-and-value.js.map +1 -0
- package/dist/middleware/reflect/describe-roots.d.ts +58 -0
- package/dist/middleware/reflect/describe-roots.d.ts.map +1 -0
- package/dist/middleware/reflect/describe-roots.js +266 -0
- package/dist/middleware/reflect/describe-roots.js.map +1 -0
- package/dist/middleware/reflect/flag-reviewer-startup.d.ts +16 -0
- package/dist/middleware/reflect/flag-reviewer-startup.d.ts.map +1 -0
- package/dist/middleware/reflect/flag-reviewer-startup.js +107 -0
- package/dist/middleware/reflect/flag-reviewer-startup.js.map +1 -0
- package/dist/middleware/reflect/flag-reviewer.d.ts +69 -0
- package/dist/middleware/reflect/flag-reviewer.d.ts.map +1 -0
- package/dist/middleware/reflect/flag-reviewer.js +520 -0
- package/dist/middleware/reflect/flag-reviewer.js.map +1 -0
- package/dist/middleware/reflect/inline-dedup.d.ts +26 -0
- package/dist/middleware/reflect/inline-dedup.d.ts.map +1 -0
- package/dist/middleware/reflect/inline-dedup.js +131 -0
- package/dist/middleware/reflect/inline-dedup.js.map +1 -0
- package/dist/middleware/reflect/justify-decisions.d.ts +37 -0
- package/dist/middleware/reflect/justify-decisions.d.ts.map +1 -0
- package/dist/middleware/reflect/justify-decisions.js +159 -0
- package/dist/middleware/reflect/justify-decisions.js.map +1 -0
- package/dist/middleware/reflect/nl-accept.d.ts +35 -0
- package/dist/middleware/reflect/nl-accept.d.ts.map +1 -0
- package/dist/middleware/reflect/nl-accept.js +167 -0
- package/dist/middleware/reflect/nl-accept.js.map +1 -0
- package/dist/middleware/reflect/pass0.d.ts +20 -0
- package/dist/middleware/reflect/pass0.d.ts.map +1 -0
- package/dist/middleware/reflect/pass0.js +423 -0
- package/dist/middleware/reflect/pass0.js.map +1 -0
- package/dist/middleware/reflect/pass1.d.ts +17 -0
- package/dist/middleware/reflect/pass1.d.ts.map +1 -0
- package/dist/middleware/reflect/pass1.js +241 -0
- package/dist/middleware/reflect/pass1.js.map +1 -0
- package/dist/middleware/reflect/pass2-quarantine.d.ts +129 -0
- package/dist/middleware/reflect/pass2-quarantine.d.ts.map +1 -0
- package/dist/middleware/reflect/pass2-quarantine.js +272 -0
- package/dist/middleware/reflect/pass2-quarantine.js.map +1 -0
- package/dist/middleware/reflect/pass2-seams.d.ts +205 -0
- package/dist/middleware/reflect/pass2-seams.d.ts.map +1 -0
- package/dist/middleware/reflect/pass2-seams.js +279 -0
- package/dist/middleware/reflect/pass2-seams.js.map +1 -0
- package/dist/middleware/reflect/pass2-split-orchestrator.d.ts +37 -0
- package/dist/middleware/reflect/pass2-split-orchestrator.d.ts.map +1 -0
- package/dist/middleware/reflect/pass2-split-orchestrator.js +531 -0
- package/dist/middleware/reflect/pass2-split-orchestrator.js.map +1 -0
- package/dist/middleware/reflect/pass2.d.ts +17 -0
- package/dist/middleware/reflect/pass2.d.ts.map +1 -0
- package/dist/middleware/reflect/pass2.js +324 -0
- package/dist/middleware/reflect/pass2.js.map +1 -0
- package/dist/middleware/reflect/pass2a.d.ts +141 -0
- package/dist/middleware/reflect/pass2a.d.ts.map +1 -0
- package/dist/middleware/reflect/pass2a.js +404 -0
- package/dist/middleware/reflect/pass2a.js.map +1 -0
- package/dist/middleware/reflect/pass2b.d.ts +108 -0
- package/dist/middleware/reflect/pass2b.d.ts.map +1 -0
- package/dist/middleware/reflect/pass2b.js +480 -0
- package/dist/middleware/reflect/pass2b.js.map +1 -0
- package/dist/middleware/reflect/pass2c.d.ts +113 -0
- package/dist/middleware/reflect/pass2c.d.ts.map +1 -0
- package/dist/middleware/reflect/pass2c.js +360 -0
- package/dist/middleware/reflect/pass2c.js.map +1 -0
- package/dist/middleware/reflect/pass3-batch.d.ts +62 -0
- package/dist/middleware/reflect/pass3-batch.d.ts.map +1 -0
- package/dist/middleware/reflect/pass3-batch.js +139 -0
- package/dist/middleware/reflect/pass3-batch.js.map +1 -0
- package/dist/middleware/reflect/pass3.d.ts +23 -0
- package/dist/middleware/reflect/pass3.d.ts.map +1 -0
- package/dist/middleware/reflect/pass3.js +371 -0
- package/dist/middleware/reflect/pass3.js.map +1 -0
- package/dist/middleware/reflect/pass4-slice.d.ts +25 -0
- package/dist/middleware/reflect/pass4-slice.d.ts.map +1 -0
- package/dist/middleware/reflect/pass4-slice.js +315 -0
- package/dist/middleware/reflect/pass4-slice.js.map +1 -0
- package/dist/middleware/reflect/pass4.d.ts +30 -0
- package/dist/middleware/reflect/pass4.d.ts.map +1 -0
- package/dist/middleware/reflect/pass4.js +193 -0
- package/dist/middleware/reflect/pass4.js.map +1 -0
- package/dist/middleware/reflect/pass5.d.ts +22 -0
- package/dist/middleware/reflect/pass5.d.ts.map +1 -0
- package/dist/middleware/reflect/pass5.js +178 -0
- package/dist/middleware/reflect/pass5.js.map +1 -0
- package/dist/middleware/reflect/pass_judge.d.ts +44 -0
- package/dist/middleware/reflect/pass_judge.d.ts.map +1 -0
- package/dist/middleware/reflect/pass_judge.js +263 -0
- package/dist/middleware/reflect/pass_judge.js.map +1 -0
- package/dist/middleware/reflect/pipeline-flags.d.ts +140 -0
- package/dist/middleware/reflect/pipeline-flags.d.ts.map +1 -0
- package/dist/middleware/reflect/pipeline-flags.js +314 -0
- package/dist/middleware/reflect/pipeline-flags.js.map +1 -0
- package/dist/middleware/reflect/pipeline.d.ts +237 -0
- package/dist/middleware/reflect/pipeline.d.ts.map +1 -0
- package/dist/middleware/reflect/pipeline.js +3114 -0
- package/dist/middleware/reflect/pipeline.js.map +1 -0
- package/dist/middleware/reflect/promptOverride.d.ts +14 -0
- package/dist/middleware/reflect/promptOverride.d.ts.map +1 -0
- package/dist/middleware/reflect/promptOverride.js +28 -0
- package/dist/middleware/reflect/promptOverride.js.map +1 -0
- package/dist/middleware/reflect/provenance-check.d.ts +48 -0
- package/dist/middleware/reflect/provenance-check.d.ts.map +1 -0
- package/dist/middleware/reflect/provenance-check.js +180 -0
- package/dist/middleware/reflect/provenance-check.js.map +1 -0
- package/dist/middleware/reflect/provenance-reviewer.d.ts +52 -0
- package/dist/middleware/reflect/provenance-reviewer.d.ts.map +1 -0
- package/dist/middleware/reflect/provenance-reviewer.js +253 -0
- package/dist/middleware/reflect/provenance-reviewer.js.map +1 -0
- package/dist/middleware/reflect/prune-collapsed-types.d.ts +11 -0
- package/dist/middleware/reflect/prune-collapsed-types.d.ts.map +1 -0
- package/dist/middleware/reflect/prune-collapsed-types.js +32 -0
- package/dist/middleware/reflect/prune-collapsed-types.js.map +1 -0
- package/dist/middleware/reflect/recognize-root.d.ts +75 -0
- package/dist/middleware/reflect/recognize-root.d.ts.map +1 -0
- package/dist/middleware/reflect/recognize-root.js +204 -0
- package/dist/middleware/reflect/recognize-root.js.map +1 -0
- package/dist/middleware/reflect/render-agent-flag.d.ts +25 -0
- package/dist/middleware/reflect/render-agent-flag.d.ts.map +1 -0
- package/dist/middleware/reflect/render-agent-flag.js +39 -0
- package/dist/middleware/reflect/render-agent-flag.js.map +1 -0
- package/dist/middleware/reflect/retrieve-graph-slice.d.ts +54 -0
- package/dist/middleware/reflect/retrieve-graph-slice.d.ts.map +1 -0
- package/dist/middleware/reflect/retrieve-graph-slice.js +173 -0
- package/dist/middleware/reflect/retrieve-graph-slice.js.map +1 -0
- package/dist/middleware/reflect/root-relatedness.d.ts +31 -0
- package/dist/middleware/reflect/root-relatedness.d.ts.map +1 -0
- package/dist/middleware/reflect/root-relatedness.js +92 -0
- package/dist/middleware/reflect/root-relatedness.js.map +1 -0
- package/dist/middleware/reflect/schema-heal.d.ts +22 -0
- package/dist/middleware/reflect/schema-heal.d.ts.map +1 -0
- package/dist/middleware/reflect/schema-heal.js +119 -0
- package/dist/middleware/reflect/schema-heal.js.map +1 -0
- package/dist/middleware/reflect/schema-validator.d.ts +85 -0
- package/dist/middleware/reflect/schema-validator.d.ts.map +1 -0
- package/dist/middleware/reflect/schema-validator.js +196 -0
- package/dist/middleware/reflect/schema-validator.js.map +1 -0
- package/dist/middleware/reflect/stage-audit-graph.d.ts +115 -0
- package/dist/middleware/reflect/stage-audit-graph.d.ts.map +1 -0
- package/dist/middleware/reflect/stage-audit-graph.js +563 -0
- package/dist/middleware/reflect/stage-audit-graph.js.map +1 -0
- package/dist/middleware/reflect/stage-d-resolve-graph.d.ts +87 -0
- package/dist/middleware/reflect/stage-d-resolve-graph.d.ts.map +1 -0
- package/dist/middleware/reflect/stage-d-resolve-graph.js +256 -0
- package/dist/middleware/reflect/stage-d-resolve-graph.js.map +1 -0
- package/dist/middleware/reflect/synthesizeFromSceneCard.d.ts +15 -0
- package/dist/middleware/reflect/synthesizeFromSceneCard.d.ts.map +1 -0
- package/dist/middleware/reflect/synthesizeFromSceneCard.js +91 -0
- package/dist/middleware/reflect/synthesizeFromSceneCard.js.map +1 -0
- package/dist/middleware/reflect/types.d.ts +261 -0
- package/dist/middleware/reflect/types.d.ts.map +1 -0
- package/dist/middleware/reflect/types.js +3 -0
- package/dist/middleware/reflect/types.js.map +1 -0
- package/dist/middleware/reflect/v2-integrate.d.ts +120 -0
- package/dist/middleware/reflect/v2-integrate.d.ts.map +1 -0
- package/dist/middleware/reflect/v2-integrate.js +388 -0
- package/dist/middleware/reflect/v2-integrate.js.map +1 -0
- package/dist/middleware/reflect/v2-judge.d.ts +44 -0
- package/dist/middleware/reflect/v2-judge.d.ts.map +1 -0
- package/dist/middleware/reflect/v2-judge.js +191 -0
- package/dist/middleware/reflect/v2-judge.js.map +1 -0
- package/dist/relation-sets.d.ts +2 -0
- package/dist/relation-sets.d.ts.map +1 -0
- package/dist/relation-sets.js +35 -0
- package/dist/relation-sets.js.map +1 -0
- package/dist/routes/__tests__/flags.test.d.ts +2 -0
- package/dist/routes/__tests__/flags.test.d.ts.map +1 -0
- package/dist/routes/__tests__/flags.test.js +257 -0
- package/dist/routes/__tests__/flags.test.js.map +1 -0
- package/dist/routes/__tests__/models-catalog.test.d.ts +2 -0
- package/dist/routes/__tests__/models-catalog.test.d.ts.map +1 -0
- package/dist/routes/__tests__/models-catalog.test.js +130 -0
- package/dist/routes/__tests__/models-catalog.test.js.map +1 -0
- package/dist/routes/__tests__/reflect-pause-drain.test.d.ts +2 -0
- package/dist/routes/__tests__/reflect-pause-drain.test.d.ts.map +1 -0
- package/dist/routes/__tests__/reflect-pause-drain.test.js +38 -0
- package/dist/routes/__tests__/reflect-pause-drain.test.js.map +1 -0
- package/dist/routes/__tests__/spend-pause-drain.test.d.ts +2 -0
- package/dist/routes/__tests__/spend-pause-drain.test.d.ts.map +1 -0
- package/dist/routes/__tests__/spend-pause-drain.test.js +38 -0
- package/dist/routes/__tests__/spend-pause-drain.test.js.map +1 -0
- package/dist/routes/admin.d.ts +49 -0
- package/dist/routes/admin.d.ts.map +1 -0
- package/dist/routes/admin.js +471 -0
- package/dist/routes/admin.js.map +1 -0
- package/dist/routes/blocks.d.ts +4 -0
- package/dist/routes/blocks.d.ts.map +1 -0
- package/dist/routes/blocks.js +893 -0
- package/dist/routes/blocks.js.map +1 -0
- package/dist/routes/chat-proxy.d.ts +5 -0
- package/dist/routes/chat-proxy.d.ts.map +1 -0
- package/dist/routes/chat-proxy.js +225 -0
- package/dist/routes/chat-proxy.js.map +1 -0
- package/dist/routes/conversations.d.ts +4 -0
- package/dist/routes/conversations.d.ts.map +1 -0
- package/dist/routes/conversations.js +139 -0
- package/dist/routes/conversations.js.map +1 -0
- package/dist/routes/flags.d.ts +4 -0
- package/dist/routes/flags.d.ts.map +1 -0
- package/dist/routes/flags.js +151 -0
- package/dist/routes/flags.js.map +1 -0
- package/dist/routes/inject.d.ts +4 -0
- package/dist/routes/inject.d.ts.map +1 -0
- package/dist/routes/inject.js +183 -0
- package/dist/routes/inject.js.map +1 -0
- package/dist/routes/mcp-http.d.ts +5 -0
- package/dist/routes/mcp-http.d.ts.map +1 -0
- package/dist/routes/mcp-http.js +94 -0
- package/dist/routes/mcp-http.js.map +1 -0
- package/dist/routes/quarantine.d.ts +4 -0
- package/dist/routes/quarantine.d.ts.map +1 -0
- package/dist/routes/quarantine.js +66 -0
- package/dist/routes/quarantine.js.map +1 -0
- package/dist/routes/recall.d.ts +5 -0
- package/dist/routes/recall.d.ts.map +1 -0
- package/dist/routes/recall.js +573 -0
- package/dist/routes/recall.js.map +1 -0
- package/dist/routes/reflect.d.ts +5 -0
- package/dist/routes/reflect.d.ts.map +1 -0
- package/dist/routes/reflect.js +231 -0
- package/dist/routes/reflect.js.map +1 -0
- package/dist/routes/session.d.ts +4 -0
- package/dist/routes/session.d.ts.map +1 -0
- package/dist/routes/session.js +418 -0
- package/dist/routes/session.js.map +1 -0
- package/dist/routes/state.d.ts +116 -0
- package/dist/routes/state.d.ts.map +1 -0
- package/dist/routes/state.js +621 -0
- package/dist/routes/state.js.map +1 -0
- package/dist/routes/usage.d.ts +3 -0
- package/dist/routes/usage.d.ts.map +1 -0
- package/dist/routes/usage.js +141 -0
- package/dist/routes/usage.js.map +1 -0
- package/dist/routes/workspace.d.ts +5 -0
- package/dist/routes/workspace.d.ts.map +1 -0
- package/dist/routes/workspace.js +435 -0
- package/dist/routes/workspace.js.map +1 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +298 -0
- package/dist/server.js.map +1 -0
- package/dist/store/__tests__/backup.test.d.ts +2 -0
- package/dist/store/__tests__/backup.test.d.ts.map +1 -0
- package/dist/store/__tests__/backup.test.js +53 -0
- package/dist/store/__tests__/backup.test.js.map +1 -0
- package/dist/store/__tests__/quality.test.d.ts +2 -0
- package/dist/store/__tests__/quality.test.d.ts.map +1 -0
- package/dist/store/__tests__/quality.test.js +75 -0
- package/dist/store/__tests__/quality.test.js.map +1 -0
- package/dist/store/backup.d.ts +14 -0
- package/dist/store/backup.d.ts.map +1 -0
- package/dist/store/backup.js +95 -0
- package/dist/store/backup.js.map +1 -0
- package/dist/store/database.d.ts +407 -0
- package/dist/store/database.d.ts.map +1 -0
- package/dist/store/database.js +2004 -0
- package/dist/store/database.js.map +1 -0
- package/dist/store/quality.d.ts +25 -0
- package/dist/store/quality.d.ts.map +1 -0
- package/dist/store/quality.js +48 -0
- package/dist/store/quality.js.map +1 -0
- package/dist/tools/__tests__/assemble-block-chains.test.d.ts +2 -0
- package/dist/tools/__tests__/assemble-block-chains.test.d.ts.map +1 -0
- package/dist/tools/__tests__/assemble-block-chains.test.js +118 -0
- package/dist/tools/__tests__/assemble-block-chains.test.js.map +1 -0
- package/dist/tools/__tests__/filter-roots-by-concepts.test.d.ts +2 -0
- package/dist/tools/__tests__/filter-roots-by-concepts.test.d.ts.map +1 -0
- package/dist/tools/__tests__/filter-roots-by-concepts.test.js +68 -0
- package/dist/tools/__tests__/filter-roots-by-concepts.test.js.map +1 -0
- package/dist/tools/__tests__/flag-surface.test.d.ts +2 -0
- package/dist/tools/__tests__/flag-surface.test.d.ts.map +1 -0
- package/dist/tools/__tests__/flag-surface.test.js +130 -0
- package/dist/tools/__tests__/flag-surface.test.js.map +1 -0
- package/dist/tools/core.d.ts +5 -0
- package/dist/tools/core.d.ts.map +1 -0
- package/dist/tools/core.js +962 -0
- package/dist/tools/core.js.map +1 -0
- package/dist/tools/derive.d.ts +5 -0
- package/dist/tools/derive.d.ts.map +1 -0
- package/dist/tools/derive.js +182 -0
- package/dist/tools/derive.js.map +1 -0
- package/dist/tools/flag-surface.d.ts +26 -0
- package/dist/tools/flag-surface.d.ts.map +1 -0
- package/dist/tools/flag-surface.js +59 -0
- package/dist/tools/flag-surface.js.map +1 -0
- package/dist/tools/helpers.d.ts +99 -0
- package/dist/tools/helpers.d.ts.map +1 -0
- package/dist/tools/helpers.js +243 -0
- package/dist/tools/helpers.js.map +1 -0
- package/dist/tools/projects.d.ts +5 -0
- package/dist/tools/projects.d.ts.map +1 -0
- package/dist/tools/projects.js +175 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/system.d.ts +5 -0
- package/dist/tools/system.d.ts.map +1 -0
- package/dist/tools/system.js +1361 -0
- package/dist/tools/system.js.map +1 -0
- package/dist/tools/tasks.d.ts +5 -0
- package/dist/tools/tasks.d.ts.map +1 -0
- package/dist/tools/tasks.js +289 -0
- package/dist/tools/tasks.js.map +1 -0
- package/package.json +69 -0
- package/scripts/nodedex-entry.mjs +396 -0
- package/tui-dist/App.js +185 -0
- package/tui-dist/api.js +197 -0
- package/tui-dist/cli.js +53 -0
- package/tui-dist/components.js +63 -0
- package/tui-dist/config.js +242 -0
- package/tui-dist/connect-snippets.js +98 -0
- package/tui-dist/feed.js +51 -0
- package/tui-dist/health.js +465 -0
- package/tui-dist/hooks.js +23 -0
- package/tui-dist/memory.js +220 -0
- package/tui-dist/onboarding.js +498 -0
- package/tui-dist/review.js +193 -0
- package/tui-dist/servers.js +556 -0
- package/tui-dist/smoke.js +15 -0
- package/tui-dist/theme.js +106 -0
package/tui-dist/api.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// api.ts — read-only REST client for a running NodeDex server.
|
|
2
|
+
// The TUI NEVER touches the DB; it only reads endpoints that already exist
|
|
3
|
+
// (session, reflect/status, session/events, flags, blocks). Every fetch is
|
|
4
|
+
// failure-tolerant: a down/501 endpoint yields null, the rest still render.
|
|
5
|
+
// Force IPv4 for the loopback host. On Windows, "localhost" resolves to IPv6 ::1
|
|
6
|
+
// FIRST; servers bind IPv4 (127.0.0.1), so every localhost request pays a ~200ms
|
|
7
|
+
// ::1-refused → IPv4-retry penalty — the TUI's lag (measured: localhost 0.21s vs
|
|
8
|
+
// 127.0.0.1 0.001s per request). 127.0.0.1 also works when a server binds 0.0.0.0.
|
|
9
|
+
export function prefer127(url) {
|
|
10
|
+
return url.replace(/\/\/localhost(?=[:/]|$)/i, "//127.0.0.1");
|
|
11
|
+
}
|
|
12
|
+
// The active server. Mutable so the Servers pane can switch which graph the
|
|
13
|
+
// TUI reads at runtime (was a const — multi-server switching needs it live).
|
|
14
|
+
let currentBase = prefer127((process.env.NODEDEX_TUI_API || "http://localhost:3001").replace(/\/$/, ""));
|
|
15
|
+
// Per-server API tokens. A server launched with NODEDEX_API_TOKEN gates its WHOLE REST API,
|
|
16
|
+
// so the TUI must send the token to read it. Keyed by normalized base url; setBase picks the
|
|
17
|
+
// active one up automatically, so every connect path authenticates without extra plumbing.
|
|
18
|
+
const tokenByBase = new Map();
|
|
19
|
+
let currentToken = process.env.NODEDEX_TUI_TOKEN || "";
|
|
20
|
+
export function registerToken(url, token) {
|
|
21
|
+
const u = prefer127(url.replace(/\/$/, ""));
|
|
22
|
+
if (token)
|
|
23
|
+
tokenByBase.set(u, token);
|
|
24
|
+
else
|
|
25
|
+
tokenByBase.delete(u);
|
|
26
|
+
}
|
|
27
|
+
function authHeaders(forUrl) {
|
|
28
|
+
const t = forUrl ? (tokenByBase.get(prefer127(forUrl.replace(/\/$/, ""))) || "") : currentToken;
|
|
29
|
+
return t ? { "x-nodedex-token": t } : {};
|
|
30
|
+
}
|
|
31
|
+
export function getBase() {
|
|
32
|
+
return currentBase;
|
|
33
|
+
}
|
|
34
|
+
export function setBase(url) {
|
|
35
|
+
currentBase = prefer127(url.replace(/\/$/, ""));
|
|
36
|
+
currentToken = tokenByBase.get(currentBase) || (process.env.NODEDEX_TUI_TOKEN || "");
|
|
37
|
+
}
|
|
38
|
+
async function getJSON(path, timeoutMs = 4000) {
|
|
39
|
+
try {
|
|
40
|
+
const r = await fetch(`${currentBase}${path}`, { headers: authHeaders(), signal: AbortSignal.timeout(timeoutMs) });
|
|
41
|
+
if (!r.ok)
|
|
42
|
+
return null;
|
|
43
|
+
return (await r.json());
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Probe an ARBITRARY url (not the active base) for a NodeDex server's identity —
|
|
50
|
+
// used by server discovery. One cheap GET /api/session; short timeout so a dead
|
|
51
|
+
// port fails fast.
|
|
52
|
+
export async function probeServer(url) {
|
|
53
|
+
const base = prefer127(url.replace(/\/$/, ""));
|
|
54
|
+
try {
|
|
55
|
+
const r = await fetch(`${base}/api/session`, { headers: authHeaders(url), signal: AbortSignal.timeout(1200) });
|
|
56
|
+
if (r.ok) {
|
|
57
|
+
const j = (await r.json());
|
|
58
|
+
return { up: true, db: j.db, blocks: j.total_blocks };
|
|
59
|
+
}
|
|
60
|
+
// Responded but gated (token required, not supplied) → still confirm liveness via the
|
|
61
|
+
// always-open /api/health route, so a token-protected server shows UP (without its data).
|
|
62
|
+
if (r.status === 401 || r.status === 403) {
|
|
63
|
+
const h = await fetch(`${base}/api/health`, { signal: AbortSignal.timeout(1200) });
|
|
64
|
+
if (h.ok)
|
|
65
|
+
return { up: true };
|
|
66
|
+
}
|
|
67
|
+
return { up: false };
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return { up: false }; // connection refused / timeout = genuinely down
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Fast poll — all LOCAL endpoints (no external calls), safe to hit every couple seconds.
|
|
74
|
+
export async function fetchDashboard() {
|
|
75
|
+
const [session, reflect, eventsRes, agentFlagsRes, flagSummary, reviewRes, relatedRes, budget, passes, alertsRes] = await Promise.all([
|
|
76
|
+
getJSON("/api/session"),
|
|
77
|
+
getJSON("/api/reflect/status"),
|
|
78
|
+
getJSON("/api/session/events"),
|
|
79
|
+
getJSON("/api/flags/agent-pending"),
|
|
80
|
+
getJSON("/api/flags/summary"),
|
|
81
|
+
getJSON("/api/blocks/review-queue"),
|
|
82
|
+
getJSON("/api/roots/related"),
|
|
83
|
+
// budget hits OpenRouter only when a credit floor is set, and that fetch is
|
|
84
|
+
// cached ~60s server-side, so it's safe on the 2s poll.
|
|
85
|
+
getJSON("/api/usage/budget"),
|
|
86
|
+
getJSON("/api/usage/passes"),
|
|
87
|
+
getJSON("/api/alerts"),
|
|
88
|
+
]);
|
|
89
|
+
const reads = Array.isArray(eventsRes?.events)
|
|
90
|
+
? eventsRes.events.filter((e) => e?.type === "recall").slice(-8).reverse()
|
|
91
|
+
: [];
|
|
92
|
+
return {
|
|
93
|
+
ok: !!session || !!reflect,
|
|
94
|
+
base: currentBase,
|
|
95
|
+
fetchedAt: new Date(),
|
|
96
|
+
session,
|
|
97
|
+
reflect,
|
|
98
|
+
reads,
|
|
99
|
+
agentFlags: Array.isArray(agentFlagsRes?.flags) ? agentFlagsRes.flags : [],
|
|
100
|
+
flagSummary,
|
|
101
|
+
reviewQueue: Array.isArray(reviewRes?.blocks) ? reviewRes.blocks : [],
|
|
102
|
+
related: relatedRes,
|
|
103
|
+
budget,
|
|
104
|
+
passes,
|
|
105
|
+
alerts: Array.isArray(alertsRes?.alerts) ? alertsRes.alerts.slice(-20).reverse() : [],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export async function fetchConfig() {
|
|
109
|
+
return getJSON("/api/admin/config", 6000);
|
|
110
|
+
}
|
|
111
|
+
/** Persist a config patch (model/fallback/breaker). Applies live + writes .env. */
|
|
112
|
+
export async function postConfig(patch) {
|
|
113
|
+
try {
|
|
114
|
+
const r = await fetch(`${currentBase}/api/admin/config`, {
|
|
115
|
+
method: "POST",
|
|
116
|
+
headers: { "content-type": "application/json", ...authHeaders() },
|
|
117
|
+
body: JSON.stringify(patch),
|
|
118
|
+
signal: AbortSignal.timeout(8000),
|
|
119
|
+
});
|
|
120
|
+
return r.ok;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** Pause or resume the reflect pipeline (capture). */
|
|
127
|
+
export async function setReflectPausedRemote(paused) {
|
|
128
|
+
try {
|
|
129
|
+
const r = await fetch(`${currentBase}/api/reflect/${paused ? "pause" : "resume"}`, {
|
|
130
|
+
method: "POST", headers: authHeaders(), signal: AbortSignal.timeout(6000),
|
|
131
|
+
});
|
|
132
|
+
return r.ok;
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export async function fetchTree() {
|
|
139
|
+
const r = await getJSON("/api/tree?depth=1");
|
|
140
|
+
return Array.isArray(r?.tree) ? r.tree : [];
|
|
141
|
+
}
|
|
142
|
+
export async function fetchProjectBlocks(projectLabel) {
|
|
143
|
+
const r = await getJSON(`/api/blocks?project=${encodeURIComponent(projectLabel)}&limit=500`);
|
|
144
|
+
return Array.isArray(r) ? r : [];
|
|
145
|
+
}
|
|
146
|
+
export async function searchMemory(q, limit = 6) {
|
|
147
|
+
if (!q.trim())
|
|
148
|
+
return [];
|
|
149
|
+
const r = await getJSON(`/api/search?q=${encodeURIComponent(q)}&limit=${limit}`);
|
|
150
|
+
return Array.isArray(r) ? r : [];
|
|
151
|
+
}
|
|
152
|
+
export async function fetchBlockDetail(id) {
|
|
153
|
+
return getJSON(`/api/blocks/${encodeURIComponent(id)}?detail=relations`);
|
|
154
|
+
}
|
|
155
|
+
// ─── Chains pane ─────────────────────────────────────────────────────────────
|
|
156
|
+
// chain blocks (type:"chain") — content carries unique.arc + unique.conclusion
|
|
157
|
+
export async function fetchChains() {
|
|
158
|
+
const r = await getJSON(`/api/blocks?type=chain&limit=200`);
|
|
159
|
+
return Array.isArray(r) ? r : [];
|
|
160
|
+
}
|
|
161
|
+
export async function fetchChainMembers(chainBlockId) {
|
|
162
|
+
const r = await getJSON(`/api/chains/${encodeURIComponent(chainBlockId)}`);
|
|
163
|
+
return Array.isArray(r?.blocks) ? r.blocks : [];
|
|
164
|
+
}
|
|
165
|
+
export async function fetchUnreviewedFlags() {
|
|
166
|
+
const r = await getJSON(`/api/flags?reviewed=false&limit=50`);
|
|
167
|
+
return Array.isArray(r?.flags) ? r.flags : [];
|
|
168
|
+
}
|
|
169
|
+
export async function fetchFlagDetail(id) {
|
|
170
|
+
return getJSON(`/api/flags/${encodeURIComponent(id)}`);
|
|
171
|
+
}
|
|
172
|
+
// the ONE write: POST a verdict (merge can execute: archive loser + wire
|
|
173
|
+
// superseded_by). Returns the server's reply verbatim so errors surface in UI.
|
|
174
|
+
export async function postFlagReview(id, body) {
|
|
175
|
+
try {
|
|
176
|
+
const r = await fetch(`${currentBase}/api/flags/${encodeURIComponent(id)}/review`, {
|
|
177
|
+
method: "POST",
|
|
178
|
+
headers: { "content-type": "application/json", ...authHeaders() },
|
|
179
|
+
body: JSON.stringify(body),
|
|
180
|
+
signal: AbortSignal.timeout(8000),
|
|
181
|
+
});
|
|
182
|
+
return (await r.json());
|
|
183
|
+
}
|
|
184
|
+
catch (e) {
|
|
185
|
+
return { error: String(e) };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Slow poll — balance hits OpenRouter (external), so it runs on its own ~30s cadence.
|
|
189
|
+
export async function fetchBalance() {
|
|
190
|
+
const b = await getJSON("/api/usage/balance", 8000);
|
|
191
|
+
if (!b || typeof b.remaining === "undefined")
|
|
192
|
+
return { remaining: null, available: false };
|
|
193
|
+
return { remaining: b.remaining, available: true };
|
|
194
|
+
}
|
|
195
|
+
// back-compat: a few callers imported BASE for display. getBase() is the live
|
|
196
|
+
// value; this stays as the INITIAL base only.
|
|
197
|
+
export const BASE = currentBase;
|
package/tui-dist/cli.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
// cli.tsx — entry point. `npm run dev` (tsx) renders the app.
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { render } from "ink";
|
|
6
|
+
import { App } from "./App.js";
|
|
7
|
+
import { Onboarding } from "./onboarding.js";
|
|
8
|
+
import { needsOnboarding } from "./config.js";
|
|
9
|
+
// Own the terminal via the ALTERNATE SCREEN BUFFER (like vim / htop / less): the TUI
|
|
10
|
+
// renders into a dedicated buffer with NO scrollback, so a frame taller than the
|
|
11
|
+
// window — or a tab switch — can't push a copy of the UI up into shell history (the
|
|
12
|
+
// "duplicate TUI on tab switch" bug). Pair with the height-pinned root in App.tsx so
|
|
13
|
+
// every frame is the same height and ink repaints in place.
|
|
14
|
+
//
|
|
15
|
+
// CRITICAL: restore the user's normal screen on EVERY exit path. A process that exits
|
|
16
|
+
// while still in the alt buffer leaves the shell looking broken. `process.on("exit")`
|
|
17
|
+
// is the catch-all — it fires on normal unmount, on process.exit() (App's signal
|
|
18
|
+
// handlers), and after ink's own Ctrl-C exit. It must be synchronous (a raw write is).
|
|
19
|
+
const enterAltScreen = () => process.stdout.write("\x1b[?1049h\x1b[2J\x1b[H");
|
|
20
|
+
const leaveAltScreen = () => process.stdout.write("\x1b[?1049l");
|
|
21
|
+
enterAltScreen();
|
|
22
|
+
process.on("exit", leaveAltScreen);
|
|
23
|
+
// A crash (a render throw or an unhandled promise rejection) while the terminal is in
|
|
24
|
+
// RAW MODE + the alt-screen buffer would otherwise leave it FROZEN — no input handler, so
|
|
25
|
+
// q / Ctrl-C / esc all do nothing and you can't close it. Restore raw mode + the screen and
|
|
26
|
+
// exit hard, so the user always gets their shell (and the error) back instead of a dead TUI.
|
|
27
|
+
const crashOut = (label) => (err) => {
|
|
28
|
+
try {
|
|
29
|
+
if (process.stdin.isTTY)
|
|
30
|
+
process.stdin.setRawMode?.(false);
|
|
31
|
+
}
|
|
32
|
+
catch { /* */ }
|
|
33
|
+
try {
|
|
34
|
+
leaveAltScreen();
|
|
35
|
+
}
|
|
36
|
+
catch { /* */ }
|
|
37
|
+
try {
|
|
38
|
+
process.stderr.write(`\nNodedex TUI ${label}: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`);
|
|
39
|
+
}
|
|
40
|
+
catch { /* */ }
|
|
41
|
+
process.exit(1);
|
|
42
|
+
};
|
|
43
|
+
process.on("uncaughtException", crashOut("crashed"));
|
|
44
|
+
process.on("unhandledRejection", crashOut("unhandled rejection"));
|
|
45
|
+
// First run (no model configured yet) → the setup wizard, which starts a server and
|
|
46
|
+
// hands off to the TUI. Otherwise, straight into the app. `--onboard` (npm run onboard)
|
|
47
|
+
// FORCES the wizard even when already set up — to switch provider/model/db or re-run setup.
|
|
48
|
+
const forceOnboard = process.argv.includes("--onboard");
|
|
49
|
+
function Root() {
|
|
50
|
+
const [done, setDone] = useState(() => !forceOnboard && !needsOnboarding());
|
|
51
|
+
return done ? _jsx(App, {}) : _jsx(Onboarding, { onDone: () => setDone(true) });
|
|
52
|
+
}
|
|
53
|
+
render(_jsx(Root, {}));
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import Gradient from "ink-gradient";
|
|
4
|
+
import { theme, glyph, fmtNum, fmtMoney } from "./theme.js";
|
|
5
|
+
const WORDMARK = [
|
|
6
|
+
" ███╗ ██╗ ██████╗ ██████╗ ███████╗██████╗ ███████╗██╗ ██╗",
|
|
7
|
+
" ████╗ ██║██╔═══██╗██╔══██╗██╔════╝██╔══██╗██╔════╝╚██╗██╔╝",
|
|
8
|
+
" ██╔██╗ ██║██║ ██║██║ ██║█████╗ ██║ ██║█████╗ ╚███╔╝ ",
|
|
9
|
+
" ██║╚██╗██║██║ ██║██║ ██║██╔══╝ ██║ ██║██╔══╝ ██╔██╗ ",
|
|
10
|
+
" ██║ ╚████║╚██████╔╝██████╔╝███████╗██████╔╝███████╗██╔╝ ██╗",
|
|
11
|
+
" ╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚══════╝╚═════╝ ╚══════╝╚═╝ ╚═╝",
|
|
12
|
+
].join("\n");
|
|
13
|
+
const TAGLINE = "the memory your agent lives in";
|
|
14
|
+
// Wordmark gradient — already the cool family (teal→sky→indigo).
|
|
15
|
+
const MARK_COLORS = ["#5eead4", "#38bdf8", "#818cf8"];
|
|
16
|
+
// Full logo — onboarding's welcome only. The running app uses <Brand/>.
|
|
17
|
+
export function Logo() {
|
|
18
|
+
return (_jsxs(Box, { flexDirection: "column", alignItems: "center", children: [_jsx(Gradient, { colors: MARK_COLORS, children: _jsx(Text, { children: WORDMARK }) }), _jsx(Text, { color: theme.dim, italic: true, children: `◇ ${TAGLINE} ◇` })] }));
|
|
19
|
+
}
|
|
20
|
+
// One-line brand for the app shell: wordmark-as-word, nothing shouting.
|
|
21
|
+
export function Brand() {
|
|
22
|
+
return (_jsx(Box, { children: _jsx(Gradient, { colors: MARK_COLORS, children: _jsx(Text, { bold: true, children: " nodedex" }) }) }));
|
|
23
|
+
}
|
|
24
|
+
// The view switcher — plain text, the active view carries the accent.
|
|
25
|
+
export function ViewBar({ views, active }) {
|
|
26
|
+
return (_jsx(Box, { children: views.map((v, i) => (_jsxs(Box, { marginRight: 1, children: [_jsx(Text, { color: i === active ? theme.accent : theme.dim, bold: i === active, children: `${i + 1} ${v}` }), i < views.length - 1 ? _jsx(Text, { color: theme.dim, children: " ·" }) : null] }, v))) }));
|
|
27
|
+
}
|
|
28
|
+
// Caps section header — replaces bordered panels for in-flow content.
|
|
29
|
+
export function Section({ title, hot, right, children }) {
|
|
30
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { color: hot ? theme.accent : theme.title, bold: true, children: title.toUpperCase() }), right ?? null] }), children] }));
|
|
31
|
+
}
|
|
32
|
+
// A selectable row: focused = accent cursor + bright text; blurred list keeps a
|
|
33
|
+
// quiet cursor so pane focus is always legible.
|
|
34
|
+
export function Row({ selected, focused = true, children }) {
|
|
35
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: selected ? (focused ? theme.accent : theme.dim) : undefined, children: selected ? "▸ " : " " }), children] }));
|
|
36
|
+
}
|
|
37
|
+
// Bordered panel — kept for overlays (search, review) that float over a view.
|
|
38
|
+
export function Panel({ title, children, hot, width, flexGrow, minHeight }) {
|
|
39
|
+
return (_jsxs(Box, { borderStyle: "round", borderColor: hot ? theme.borderHot : theme.border, flexDirection: "column", paddingX: 1, width: width, flexGrow: flexGrow, minHeight: minHeight, children: [_jsx(Box, { marginTop: -1, children: _jsx(Text, { color: theme.title, children: ` ${title} ` }) }), children] }));
|
|
40
|
+
}
|
|
41
|
+
// The ONE home for connection / graph / pipeline / spend state — bottom line,
|
|
42
|
+
// every view. Views must not repeat these numbers (one value, one home).
|
|
43
|
+
export function StatusLine({ dash, balance, captureDots }) {
|
|
44
|
+
const ok = !!dash?.ok;
|
|
45
|
+
const s = dash?.session;
|
|
46
|
+
const r = dash?.reflect;
|
|
47
|
+
const spend24 = dash?.budget?.observed?.spend24h;
|
|
48
|
+
return (_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Box, { children: [_jsx(Text, { color: ok ? theme.ok : theme.danger, children: `${glyph.up} ` }), _jsx(Text, { color: theme.value, children: s?.db ?? "no server" }), _jsx(Text, { color: theme.dim, children: ` ${fmtNum(s?.total_blocks)} blocks` }), r ? (_jsx(Text, { color: r.paused || r.spend_paused ? theme.warn : theme.dim, children: r.paused ? " ‖ capture paused" : r.spend_paused ? " ‖ spend paused" : r.queue_depth > 0 ? ` ⟳ extracting (${r.queue_depth})` : "" })) : null] }), _jsxs(Box, { marginLeft: 2, children: [captureDots.map((c) => (_jsxs(Box, { marginRight: 1, children: [_jsx(Text, { color: c.on ? theme.ok : theme.dim, children: glyph.up }), _jsx(Text, { color: theme.dim, children: ` ${c.name}` })] }, c.name))), typeof spend24 === "number" ? _jsx(Text, { color: theme.dim, children: ` ${fmtMoney(spend24)}/24h` }) : null, balance.available ? _jsx(Text, { color: theme.dim, children: ` bal ${fmtMoney(balance.remaining)}` }) : null] })] }));
|
|
49
|
+
}
|
|
50
|
+
// Viewport windowing for selectable lists: slides a cap-sized window so the
|
|
51
|
+
// cursor stays visible (the fix for "cursor moves past the fold but the list
|
|
52
|
+
// doesn't scroll"). Centers the selection where possible; callers render
|
|
53
|
+
// `↑ N more / ↓ N more` from above/below.
|
|
54
|
+
export function windowSlice(items, sel, cap) {
|
|
55
|
+
if (items.length <= cap)
|
|
56
|
+
return { visible: items, start: 0, above: 0, below: 0 };
|
|
57
|
+
const start = Math.min(Math.max(0, sel - Math.floor(cap / 2)), items.length - cap);
|
|
58
|
+
return { visible: items.slice(start, start + cap), start, above: start, below: items.length - start - cap };
|
|
59
|
+
}
|
|
60
|
+
// Bottom-of-view key hints. Quiet; the keycap carries the accent.
|
|
61
|
+
export function Keys({ items }) {
|
|
62
|
+
return (_jsx(Box, { children: items.map(([k, label], i) => (_jsxs(Box, { marginRight: 2, children: [_jsx(Text, { color: theme.accent, children: `[${k}]` }), _jsx(Text, { color: theme.dim, children: ` ${label}` })] }, `${k}-${i}`))) }));
|
|
63
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
// config.ts — the onboarding config home (~/.nodedex/config.json).
|
|
2
|
+
//
|
|
3
|
+
// The TUI owns onboarding config here (user's decision); it is injected into any
|
|
4
|
+
// server the TUI launches as env overrides (see servers.ts), so the secret never
|
|
5
|
+
// lives in the git repo. This is also the natural seat for the forward-compat
|
|
6
|
+
// auth/identity config later.
|
|
7
|
+
//
|
|
8
|
+
// Provider: OpenRouter (BYO key) — one platform for every model, spoken via the
|
|
9
|
+
// server's openai-compatible path.
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import { resolve } from "path";
|
|
12
|
+
import { mkdirSync, readFileSync, writeFileSync, readdirSync } from "fs";
|
|
13
|
+
const NODEDEX_HOME = resolve(homedir(), ".nodedex");
|
|
14
|
+
const CONFIG_FILE = resolve(NODEDEX_HOME, "config.json");
|
|
15
|
+
export const DEFAULT_PORT = 3001;
|
|
16
|
+
export const DEFAULT_DB = resolve(NODEDEX_HOME, "workspace.db");
|
|
17
|
+
export const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
|
|
18
|
+
export const OPENROUTER_DEFAULT_MODEL = "google/gemini-2.5-flash";
|
|
19
|
+
// Local / self-hosted default (Ollama). LM Studio = :1234/v1, vLLM = your host. Used by the
|
|
20
|
+
// "Local / self-hosted" onboarding path — no API key, fully offline, $0.
|
|
21
|
+
export const DEFAULT_LOCAL_BASE_URL = "http://localhost:11434/v1";
|
|
22
|
+
export const RECOMMENDED_MODELS = [
|
|
23
|
+
{ id: "google/gemini-2.5-flash-lite", label: "Gemini 2.5 Flash Lite", note: "recommended — cheapest, 1M ctx" },
|
|
24
|
+
{ id: "google/gemini-2.5-flash", label: "Gemini 2.5 Flash", note: "cheap + capable" },
|
|
25
|
+
{ id: "openai/gpt-4o-mini", label: "GPT-4o mini", note: "cheap, reliable" },
|
|
26
|
+
];
|
|
27
|
+
// Models that train on submitted prompts — used to warn the user that their inputs may
|
|
28
|
+
// be used to improve the model. Matched case-insensitively as a substring so
|
|
29
|
+
// provider-prefixed and bare ids both hit. ":free" catches OpenRouter free variants,
|
|
30
|
+
// whose provider data policies generally allow prompt training.
|
|
31
|
+
const TRAINS_ON_PROMPTS = ["owl-alpha", ":free"];
|
|
32
|
+
export function isTrainsOnPrompts(model) {
|
|
33
|
+
const m = (model || "").toLowerCase();
|
|
34
|
+
return TRAINS_ON_PROMPTS.some((needle) => m.includes(needle));
|
|
35
|
+
}
|
|
36
|
+
/** Verify an OpenRouter key before saving — a typo fails here, not at first
|
|
37
|
+
* extraction. GET /key returns the key's usage/limit for a valid key.
|
|
38
|
+
* Shared by onboarding and the Health provider picker. */
|
|
39
|
+
export async function validateOpenRouterKey(key) {
|
|
40
|
+
try {
|
|
41
|
+
const r = await fetch(`${OPENROUTER_BASE_URL}/key`, {
|
|
42
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
43
|
+
signal: AbortSignal.timeout(8000),
|
|
44
|
+
});
|
|
45
|
+
if (r.ok)
|
|
46
|
+
return { ok: true };
|
|
47
|
+
if (r.status === 401)
|
|
48
|
+
return { ok: false, error: "Key rejected (401) — check it and try again." };
|
|
49
|
+
return { ok: false, error: `OpenRouter returned ${r.status}.` };
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
return { ok: false, error: `Couldn't reach OpenRouter (${e?.message ?? e}).` };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export const DEFAULT_HERMES_SOURCES = ["tui"];
|
|
56
|
+
/** Existing Nodedex DBs in ~/.nodedex (so the user can reuse one instead of a new graph). */
|
|
57
|
+
export function listDbs() {
|
|
58
|
+
try {
|
|
59
|
+
return readdirSync(NODEDEX_HOME)
|
|
60
|
+
.filter((f) => f.endsWith(".db"))
|
|
61
|
+
.map((f) => ({ name: f.replace(/\.db$/, ""), path: resolve(NODEDEX_HOME, f) }));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return []; // home not created yet / unreadable → no existing DBs
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Resolve a user-typed DB name to a ~/.nodedex/<name>.db path (sanitized). */
|
|
68
|
+
export function dbPathForName(name) {
|
|
69
|
+
const safe = (name || "workspace").trim().replace(/[^a-z0-9_-]/gi, "-").replace(/^-+|-+$/g, "") || "workspace";
|
|
70
|
+
return resolve(NODEDEX_HOME, `${safe}.db`);
|
|
71
|
+
}
|
|
72
|
+
export function loadConfig() {
|
|
73
|
+
try {
|
|
74
|
+
return JSON.parse(readFileSync(CONFIG_FILE, "utf8"));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export function saveConfig(patch) {
|
|
81
|
+
const cfg = { ...loadConfig(), ...patch };
|
|
82
|
+
try {
|
|
83
|
+
mkdirSync(NODEDEX_HOME, { recursive: true });
|
|
84
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2));
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
/* read-only home → no persistence; the in-session launch env still works */
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** The current Hermes-capture config, with defaults filled in. */
|
|
91
|
+
export function loadHermesCapture() {
|
|
92
|
+
const h = loadConfig().hermesCapture ?? {};
|
|
93
|
+
const sources = Array.isArray(h.sources) && h.sources.length ? h.sources.map(String) : [...DEFAULT_HERMES_SOURCES];
|
|
94
|
+
return {
|
|
95
|
+
// Default ON: capture should "just work" for a normal user. The watcher idles harmlessly
|
|
96
|
+
// when no Hermes state.db exists, and the user can turn it off in Settings (sets enabled:false).
|
|
97
|
+
enabled: h.enabled !== false,
|
|
98
|
+
sources,
|
|
99
|
+
pollMs: Number.isFinite(h.pollMs) && h.pollMs >= 500 ? h.pollMs : 4000,
|
|
100
|
+
stateDbPath: h.stateDbPath ?? "",
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/** Patch the Hermes-capture block (nested merge — saveConfig is a shallow merge, so do it here). */
|
|
104
|
+
export function setHermesCapture(patch) {
|
|
105
|
+
const current = loadConfig().hermesCapture ?? {};
|
|
106
|
+
saveConfig({ hermesCapture: { ...current, ...patch } });
|
|
107
|
+
}
|
|
108
|
+
/** The current Claude Code-capture config, with defaults filled in. */
|
|
109
|
+
export function loadClaudeCapture() {
|
|
110
|
+
const c = loadConfig().claudeCapture ?? {};
|
|
111
|
+
const projects = Array.isArray(c.projects) && c.projects.length ? c.projects.map(String) : ["*"];
|
|
112
|
+
return {
|
|
113
|
+
// Default ON, same rationale as Hermes: the watcher idles harmlessly when no
|
|
114
|
+
// ~/.claude/projects exists; the onboarding capture step + Settings are the off switch.
|
|
115
|
+
enabled: c.enabled !== false,
|
|
116
|
+
projects,
|
|
117
|
+
pollMs: Number.isFinite(c.pollMs) && c.pollMs >= 1000 ? c.pollMs : 5000,
|
|
118
|
+
idleFlushMs: Number.isFinite(c.idleFlushMs) && c.idleFlushMs >= 15000 ? c.idleFlushMs : 120000,
|
|
119
|
+
projectsDir: c.projectsDir ?? "",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/** Patch the Claude Code-capture block (nested merge). */
|
|
123
|
+
export function setClaudeCapture(patch) {
|
|
124
|
+
const current = loadConfig().claudeCapture ?? {};
|
|
125
|
+
saveConfig({ claudeCapture: { ...current, ...patch } });
|
|
126
|
+
}
|
|
127
|
+
export function scanCaptureHosts() {
|
|
128
|
+
const out = [];
|
|
129
|
+
// Hermes: the state.db the watcher reads.
|
|
130
|
+
const hermesDb = process.platform === "win32" && process.env.LOCALAPPDATA
|
|
131
|
+
? resolve(process.env.LOCALAPPDATA, "hermes", "state.db")
|
|
132
|
+
: resolve(homedir(), ".local", "share", "hermes", "state.db");
|
|
133
|
+
let hermesFound = false;
|
|
134
|
+
try {
|
|
135
|
+
hermesFound = readdirSync(resolve(hermesDb, "..")).includes("state.db");
|
|
136
|
+
}
|
|
137
|
+
catch { /* absent */ }
|
|
138
|
+
out.push({ host: "hermes", label: "Hermes / Owl", found: hermesFound, detail: hermesFound ? "state.db present" : "not installed" });
|
|
139
|
+
// Claude Code: the transcripts dir.
|
|
140
|
+
const ccDir = resolve(homedir(), ".claude", "projects");
|
|
141
|
+
let projects = 0, sessions = 0;
|
|
142
|
+
try {
|
|
143
|
+
for (const d of readdirSync(ccDir, { withFileTypes: true })) {
|
|
144
|
+
if (!d.isDirectory())
|
|
145
|
+
continue;
|
|
146
|
+
let n = 0;
|
|
147
|
+
try {
|
|
148
|
+
n = readdirSync(resolve(ccDir, d.name)).filter((f) => f.endsWith(".jsonl")).length;
|
|
149
|
+
}
|
|
150
|
+
catch { /* skip */ }
|
|
151
|
+
if (n > 0) {
|
|
152
|
+
projects++;
|
|
153
|
+
sessions += n;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch { /* absent */ }
|
|
158
|
+
out.push({
|
|
159
|
+
host: "claude-code", label: "Claude Code", found: projects > 0,
|
|
160
|
+
detail: projects > 0 ? `${sessions} session${sessions === 1 ? "" : "s"} across ${projects} project${projects === 1 ? "" : "s"}` : "not installed",
|
|
161
|
+
});
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
/** Parse a user-typed source list ("tui, telegram" or "*") into a clean array. */
|
|
165
|
+
export function parseSources(input) {
|
|
166
|
+
const parts = (input || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
167
|
+
if (parts.includes("*"))
|
|
168
|
+
return ["*"];
|
|
169
|
+
return parts.length ? Array.from(new Set(parts)) : [...DEFAULT_HERMES_SOURCES];
|
|
170
|
+
}
|
|
171
|
+
/** First run = not yet onboarded, or the chosen provider isn't usable yet. */
|
|
172
|
+
export function needsOnboarding() {
|
|
173
|
+
const c = loadConfig();
|
|
174
|
+
if (!c.onboarded)
|
|
175
|
+
return true;
|
|
176
|
+
if (c.provider === "openrouter")
|
|
177
|
+
return !c.openrouter_key;
|
|
178
|
+
if (c.provider === "local")
|
|
179
|
+
return !c.base_url || !c.model; // local needs an endpoint + model, no key
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
/** The provider env a TUI-launched server needs. Both paths speak the openai-compatible API:
|
|
183
|
+
* OpenRouter (cloud, BYO key) or a local/self-hosted endpoint (Ollama/LM Studio/vLLM, no key).
|
|
184
|
+
* Empty when un-configured (so launchServer stays a no-op for un-onboarded use). These WIN over
|
|
185
|
+
* the server's .env because node --env-file won't override set vars. */
|
|
186
|
+
export function providerEnv() {
|
|
187
|
+
const c = loadConfig();
|
|
188
|
+
if (c.provider === "openrouter" && c.openrouter_key) {
|
|
189
|
+
return {
|
|
190
|
+
AI_PROVIDER: "openai-compatible",
|
|
191
|
+
OPENAI_BASE_URL: OPENROUTER_BASE_URL,
|
|
192
|
+
OPENAI_API_KEY: c.openrouter_key,
|
|
193
|
+
AI_MODEL: c.model || OPENROUTER_DEFAULT_MODEL,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (c.provider === "local" && c.base_url && c.model) {
|
|
197
|
+
return {
|
|
198
|
+
AI_PROVIDER: "openai-compatible",
|
|
199
|
+
OPENAI_BASE_URL: c.base_url,
|
|
200
|
+
OPENAI_API_KEY: "local", // local servers ignore the key; a non-empty value keeps clients happy
|
|
201
|
+
AI_MODEL: c.model,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
return {};
|
|
205
|
+
}
|
|
206
|
+
/** Probe the usual local LLM servers (Ollama, LM Studio, vLLM) and list the models they serve,
|
|
207
|
+
* so onboarding can show a pick-list instead of asking for a URL + model id. Each server that
|
|
208
|
+
* isn't running just fails fast and is skipped. Returns [] if nothing local is up. */
|
|
209
|
+
export async function scanLocalModels() {
|
|
210
|
+
const out = [];
|
|
211
|
+
const seen = new Set();
|
|
212
|
+
const add = (baseUrl, model, server) => {
|
|
213
|
+
const m = (model || "").trim();
|
|
214
|
+
const key = `${baseUrl}::${m}`;
|
|
215
|
+
if (m && !seen.has(key)) {
|
|
216
|
+
seen.add(key);
|
|
217
|
+
out.push({ label: `${m} · ${server}`, baseUrl, model: m });
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
const get = async (url) => {
|
|
221
|
+
try {
|
|
222
|
+
const r = await fetch(url, { signal: AbortSignal.timeout(1500) });
|
|
223
|
+
return r.ok ? await r.json() : null;
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
// Ollama's native tags API (richest model list).
|
|
230
|
+
const tags = await get("http://localhost:11434/api/tags");
|
|
231
|
+
for (const m of tags?.models ?? [])
|
|
232
|
+
add("http://localhost:11434/v1", m?.name ?? m?.model, "Ollama");
|
|
233
|
+
// OpenAI-compatible /v1/models on the common local ports.
|
|
234
|
+
for (const [port, server] of [[1234, "LM Studio"], [8000, "vLLM"], [11434, "Ollama"]]) {
|
|
235
|
+
const base = `http://localhost:${port}/v1`;
|
|
236
|
+
const j = await get(`${base}/models`);
|
|
237
|
+
for (const m of j?.data ?? [])
|
|
238
|
+
add(base, m?.id, server);
|
|
239
|
+
}
|
|
240
|
+
return out;
|
|
241
|
+
}
|
|
242
|
+
export { NODEDEX_HOME, CONFIG_FILE };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// connect-snippets.ts — ready-to-paste "connect your agent" config, per host.
|
|
2
|
+
//
|
|
3
|
+
// The wizard's last screen can only show one-liners (a TUI frame is a bad place to
|
|
4
|
+
// copy multi-line JSON from), so the full snippets are WRITTEN TO A FILE the user
|
|
5
|
+
// can open and paste from: ~/.nodedex/connect-snippets.md. Regenerated on every
|
|
6
|
+
// server start from the wizard, so the port/token in it always match the live server.
|
|
7
|
+
//
|
|
8
|
+
// Two wires per host (the model users must internalize): READ = the MCP endpoint
|
|
9
|
+
// below; CAPTURE = how finished turns reach the pipeline (without it the graph
|
|
10
|
+
// stays empty). Hermes capture is automatic (the state.db watcher); other hosts
|
|
11
|
+
// point at the README's capture section.
|
|
12
|
+
import { homedir } from "os";
|
|
13
|
+
import { resolve } from "path";
|
|
14
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
15
|
+
const NODEDEX_HOME = resolve(homedir(), ".nodedex");
|
|
16
|
+
export const SNIPPETS_FILE = resolve(NODEDEX_HOME, "connect-snippets.md");
|
|
17
|
+
export function buildConnectSnippets(p) {
|
|
18
|
+
const auth = p.token ? ` --header "Authorization: Bearer ${p.token}"` : "";
|
|
19
|
+
const authJsonLine = p.token ? `\n "headers": { "Authorization": "Bearer ${p.token}" },` : "";
|
|
20
|
+
return `# Connect your agent to NodeDex
|
|
21
|
+
|
|
22
|
+
MCP endpoint (Streamable HTTP): ${p.mcpUrl}
|
|
23
|
+
${p.token ? `Auth header: Authorization: Bearer ${p.token}\n` : ""}
|
|
24
|
+
The ONE token rule: connections from THIS machine never need the token; Docker/remote
|
|
25
|
+
connections always do. Lost? \`nodedex connect\` prints the right URL + test command
|
|
26
|
+
for every client location.
|
|
27
|
+
|
|
28
|
+
Two wires, set up separately:
|
|
29
|
+
1. **READ** — connect your agent to the MCP endpoint below (gives it the memory tools).
|
|
30
|
+
2. **CAPTURE** — wire finished turns into the pipeline (without this the graph stays EMPTY).
|
|
31
|
+
Hermes: automatic (the state.db watcher, on by default in the TUI).
|
|
32
|
+
Everything else: see ${p.readmeUrl}
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Claude Code
|
|
37
|
+
|
|
38
|
+
\`\`\`bash
|
|
39
|
+
claude mcp add --transport http nodedex ${p.mcpUrl}${auth}
|
|
40
|
+
\`\`\`
|
|
41
|
+
|
|
42
|
+
Then in any session the workspace_* read tools are available. Capture: Claude Code's
|
|
43
|
+
own transcripts can feed NodeDex — see the README's capture section.
|
|
44
|
+
|
|
45
|
+
Note: Claude Code's built-in auto-memory is per-project notes for Claude Code alone.
|
|
46
|
+
NodeDex complements it: one structured, self-maintaining graph that EVERY agent you
|
|
47
|
+
run (Claude Code, Hermes, Codex, custom) reads and feeds — your experience follows
|
|
48
|
+
you across tools instead of living in one.
|
|
49
|
+
|
|
50
|
+
## Hermes
|
|
51
|
+
|
|
52
|
+
Add to Hermes's MCP config (Settings → MCP servers):
|
|
53
|
+
|
|
54
|
+
\`\`\`json
|
|
55
|
+
{
|
|
56
|
+
"mcpServers": {
|
|
57
|
+
"nodedex": {
|
|
58
|
+
"url": "${p.mcpUrl}"${authJsonLine}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
\`\`\`
|
|
63
|
+
|
|
64
|
+
Use 127.0.0.1, not localhost (localhost can resolve to IPv6 ::1 and fail).
|
|
65
|
+
Capture is automatic — the NodeDex TUI watches Hermes's state.db (Settings → hermes capture).
|
|
66
|
+
|
|
67
|
+
## Codex CLI
|
|
68
|
+
|
|
69
|
+
In \`~/.codex/config.toml\` (needs a Codex version with HTTP MCP support):
|
|
70
|
+
|
|
71
|
+
\`\`\`toml
|
|
72
|
+
[mcp_servers.nodedex]
|
|
73
|
+
url = "${p.mcpUrl}"
|
|
74
|
+
${p.token ? `http_headers = { "Authorization" = "Bearer ${p.token}" }\n` : ""}\`\`\`
|
|
75
|
+
|
|
76
|
+
## Any other MCP host
|
|
77
|
+
|
|
78
|
+
Point it at the Streamable-HTTP endpoint: ${p.mcpUrl}
|
|
79
|
+
${p.token ? `Send header: Authorization: Bearer ${p.token}` : "(no auth needed on localhost)"}
|
|
80
|
+
|
|
81
|
+
Docker'd agent on this machine? Use http://host.docker.internal:<port>/mcp instead.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
Full connect + capture reference: ${p.readmeUrl}
|
|
86
|
+
`;
|
|
87
|
+
}
|
|
88
|
+
/** Write the snippets file; returns its path. Never throws (best-effort). */
|
|
89
|
+
export function writeConnectSnippets(p) {
|
|
90
|
+
try {
|
|
91
|
+
mkdirSync(NODEDEX_HOME, { recursive: true });
|
|
92
|
+
writeFileSync(SNIPPETS_FILE, buildConnectSnippets(p), "utf8");
|
|
93
|
+
return SNIPPETS_FILE;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|