talos-code 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +111 -3
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/archive/zip.js +73 -0
- package/dist/args.js +97 -0
- package/dist/automations/history-store.js +34 -0
- package/dist/automations/policy-store.js +43 -0
- package/dist/automations/runner.js +107 -0
- package/dist/automations/schedule.js +85 -0
- package/dist/automations/windows-task.js +53 -0
- package/dist/commands/advanced-cli.js +233 -0
- package/dist/commands/checkpoint-cli.js +45 -0
- package/dist/commands/config-cli.js +107 -0
- package/dist/commands/context.js +17 -0
- package/dist/commands/extensions-cli.js +207 -0
- package/dist/commands/project-cli.js +74 -0
- package/dist/commands/project-commands.js +143 -0
- package/dist/commands/provider-cli.js +185 -0
- package/dist/commands/services-cli.js +248 -0
- package/dist/commands/session-cli.js +171 -0
- package/dist/commands/system-cli.js +405 -0
- package/dist/config/commands.js +60 -0
- package/dist/config/load.js +382 -0
- package/dist/config/migrations.js +48 -0
- package/dist/config/types.js +11 -0
- package/dist/diagnostics/development-log.js +154 -0
- package/dist/diagnostics/doctor.js +104 -0
- package/dist/diagnostics/redact.js +80 -0
- package/dist/diagnostics/zip.js +52 -0
- package/dist/errors.js +156 -0
- package/dist/events/bridge.js +130 -0
- package/dist/extensions/installer.js +178 -0
- package/dist/extensions/package-schema.js +24 -0
- package/dist/headless/result.js +84 -0
- package/dist/headless/run.js +230 -0
- package/dist/i18n/en/approval.js +43 -0
- package/dist/i18n/en/common.js +9 -0
- package/dist/i18n/en/credentials.js +82 -0
- package/dist/i18n/en/errors.js +246 -0
- package/dist/i18n/en/firstrun.js +89 -0
- package/dist/i18n/en/providers.js +37 -0
- package/dist/i18n/en/screen.js +736 -0
- package/dist/i18n/en/tools.js +127 -0
- package/dist/i18n/en.js +14 -0
- package/dist/i18n/error-view.js +307 -0
- package/dist/i18n/index.js +19 -0
- package/dist/i18n/kernel-map.js +139 -0
- package/dist/io.js +45 -0
- package/dist/main.js +330 -0
- package/dist/paths.js +41 -0
- package/dist/protocol/v2/codec.js +9 -0
- package/dist/protocol/v2/events.js +555 -0
- package/dist/protocol/v2/index.js +4 -0
- package/dist/protocol/v2/replay.js +28 -0
- package/dist/protocol/v2/types.js +1 -0
- package/dist/provider/control-plane.js +135 -0
- package/dist/provider/environment-keys.js +275 -0
- package/dist/provider/health.js +110 -0
- package/dist/provider/missing-key.js +48 -0
- package/dist/provider/model-catalog.js +168 -0
- package/dist/provider/provider-text.js +13 -0
- package/dist/provider/store.js +95 -0
- package/dist/provider/system-keyring.js +214 -0
- package/dist/runtime/active-run.js +63 -0
- package/dist/runtime/agent-tree.js +74 -0
- package/dist/runtime/attachments.js +84 -0
- package/dist/runtime/brokered-executor.js +395 -0
- package/dist/runtime/context-status.js +76 -0
- package/dist/runtime/create-runtime.js +111 -0
- package/dist/runtime/model-profile.js +361 -0
- package/dist/runtime/output-store.js +137 -0
- package/dist/runtime/provider-attempts.js +185 -0
- package/dist/runtime/replay-buffer.js +153 -0
- package/dist/runtime/repo.js +95 -0
- package/dist/runtime/session-facade.js +135 -0
- package/dist/runtime/session-summary.js +125 -0
- package/dist/runtime/supervisor.js +262 -0
- package/dist/runtime/talos-composition.js +1006 -0
- package/dist/runtime/types.js +5 -0
- package/dist/runtime/usage-snapshot.js +82 -0
- package/dist/security/auto-classifier.js +38 -0
- package/dist/security/credential-free-environment.js +54 -0
- package/dist/security/evaluate.js +243 -0
- package/dist/security/execution-backends.js +236 -0
- package/dist/security/execution-broker.js +102 -0
- package/dist/security/forge-scan.js +74 -0
- package/dist/security/from-approval.js +39 -0
- package/dist/security/mxc-execution-backend.js +281 -0
- package/dist/security/permission-engine.js +138 -0
- package/dist/security/permission-explanation.js +54 -0
- package/dist/security/persist.js +179 -0
- package/dist/security/plugin-guard.js +230 -0
- package/dist/security/process-tree-evidence-store.js +74 -0
- package/dist/security/process-tree-probe.js +554 -0
- package/dist/security/project-resource-inventory.js +186 -0
- package/dist/security/project-trust-gate.js +38 -0
- package/dist/security/project-trust.js +367 -0
- package/dist/security/rule-parser.js +201 -0
- package/dist/security/shell-segmentation.js +68 -0
- package/dist/security/trust-authority.js +324 -0
- package/dist/security/types.js +1 -0
- package/dist/security/workspace-identity.js +102 -0
- package/dist/services/automation-facade.js +59 -0
- package/dist/services/forge-facade.js +77 -0
- package/dist/services/hook-facade.js +240 -0
- package/dist/services/index.js +16 -0
- package/dist/services/library-facade.js +174 -0
- package/dist/services/mcp-facade.js +348 -0
- package/dist/services/memory-facade.js +126 -0
- package/dist/services/notes-facade.js +157 -0
- package/dist/services/plugin-facade.js +308 -0
- package/dist/services/research-facade.js +110 -0
- package/dist/services/task-board-facade.js +236 -0
- package/dist/services/workflow-catalog.js +246 -0
- package/dist/sessions/export-format.js +98 -0
- package/dist/sessions/metadata-store.js +160 -0
- package/dist/sessions/share.js +122 -0
- package/dist/sessions/transfer.js +16 -0
- package/dist/subcommands.js +62 -0
- package/dist/tui/agent-roster.js +36 -0
- package/dist/tui/app.js +3761 -0
- package/dist/tui/approval.js +36 -0
- package/dist/tui/boot/boot-sequence.js +76 -0
- package/dist/tui/boot/cinematic.js +243 -0
- package/dist/tui/boot/desktop-logo.js +82 -0
- package/dist/tui/boot.js +3 -0
- package/dist/tui/busy-input.js +58 -0
- package/dist/tui/catalog-service.js +559 -0
- package/dist/tui/components/assistant-stream.js +1 -0
- package/dist/tui/components/command-menu.js +68 -0
- package/dist/tui/components/composer.js +219 -0
- package/dist/tui/components/diff.js +35 -0
- package/dist/tui/components/footer.js +48 -0
- package/dist/tui/components/header.js +6 -0
- package/dist/tui/components/markdown.js +305 -0
- package/dist/tui/components/status-indicator.js +32 -0
- package/dist/tui/components/terminal-shell.js +205 -0
- package/dist/tui/components/thinking-row.js +19 -0
- package/dist/tui/components/tool-row.js +97 -0
- package/dist/tui/components/transcript-virtualizer.js +165 -0
- package/dist/tui/components/transcript.js +43 -0
- package/dist/tui/diff-model.js +77 -0
- package/dist/tui/editor-history.js +21 -0
- package/dist/tui/editor.js +210 -0
- package/dist/tui/event-adapter.js +436 -0
- package/dist/tui/exit-output.js +58 -0
- package/dist/tui/external-editor.js +53 -0
- package/dist/tui/file-completion.js +89 -0
- package/dist/tui/focus-manager.js +5 -0
- package/dist/tui/highlight.js +30 -0
- package/dist/tui/input-router.js +18 -0
- package/dist/tui/interrupt.js +99 -0
- package/dist/tui/keybindings.js +194 -0
- package/dist/tui/keymap-resolver.js +91 -0
- package/dist/tui/launch-state.js +139 -0
- package/dist/tui/line-diff.js +57 -0
- package/dist/tui/live-activity.js +45 -0
- package/dist/tui/metrics.js +123 -0
- package/dist/tui/onboarding.js +36 -0
- package/dist/tui/overlays/agent-tree.js +39 -0
- package/dist/tui/overlays/approval-dialog.js +640 -0
- package/dist/tui/overlays/automation-center.js +51 -0
- package/dist/tui/overlays/checkpoint-picker.js +47 -0
- package/dist/tui/overlays/context-inspector.js +36 -0
- package/dist/tui/overlays/effort-line.js +110 -0
- package/dist/tui/overlays/forge-center.js +46 -0
- package/dist/tui/overlays/help-dialog.js +16 -0
- package/dist/tui/overlays/history-picker.js +37 -0
- package/dist/tui/overlays/hook-center.js +70 -0
- package/dist/tui/overlays/library-center.js +55 -0
- package/dist/tui/overlays/mcp-center.js +60 -0
- package/dist/tui/overlays/memory-center.js +63 -0
- package/dist/tui/overlays/model-picker.js +72 -0
- package/dist/tui/overlays/notes-center.js +53 -0
- package/dist/tui/overlays/overlay-host.js +8 -0
- package/dist/tui/overlays/plugin-center.js +76 -0
- package/dist/tui/overlays/provider-picker.js +145 -0
- package/dist/tui/overlays/queue-editor.js +32 -0
- package/dist/tui/overlays/research-center.js +59 -0
- package/dist/tui/overlays/scroll-window.js +234 -0
- package/dist/tui/overlays/session-picker.js +115 -0
- package/dist/tui/overlays/tasks-center.js +83 -0
- package/dist/tui/overlays/theme-picker.js +21 -0
- package/dist/tui/overlays/transcript-search.js +58 -0
- package/dist/tui/overlays/trust-center.js +29 -0
- package/dist/tui/overlays/workflow-center.js +46 -0
- package/dist/tui/project-file-index.js +214 -0
- package/dist/tui/project-references.js +85 -0
- package/dist/tui/project-trust-prompt.js +287 -0
- package/dist/tui/prompt-history-store.js +116 -0
- package/dist/tui/queue-store.js +110 -0
- package/dist/tui/regions/budget.js +59 -0
- package/dist/tui/regions/views.js +96 -0
- package/dist/tui/render-coordinator.js +148 -0
- package/dist/tui/render-scheduler.js +4 -0
- package/dist/tui/run.js +69 -0
- package/dist/tui/selection-list.js +29 -0
- package/dist/tui/session-controller.js +778 -0
- package/dist/tui/session-export.js +54 -0
- package/dist/tui/shell-input.js +35 -0
- package/dist/tui/shell-layout.js +48 -0
- package/dist/tui/shell-model.js +98 -0
- package/dist/tui/slash-commands.js +80 -0
- package/dist/tui/state.js +151 -0
- package/dist/tui/status-bar.js +125 -0
- package/dist/tui/status-view.js +40 -0
- package/dist/tui/terminal-capabilities.js +9 -0
- package/dist/tui/terminal-session.js +11 -0
- package/dist/tui/text-width.js +66 -0
- package/dist/tui/theme-catalog.js +25 -0
- package/dist/tui/theme-store.js +23 -0
- package/dist/tui/theme.js +23 -0
- package/dist/tui/tool-display.js +135 -0
- package/dist/tui/tool-facts.js +1 -0
- package/dist/tui/tools/bash-renderer.js +32 -0
- package/dist/tui/tools/change.js +61 -0
- package/dist/tui/tools/edit-renderer.js +17 -0
- package/dist/tui/tools/generic-renderer.js +13 -0
- package/dist/tui/tools/list-renderer.js +14 -0
- package/dist/tui/tools/read-renderer.js +13 -0
- package/dist/tui/tools/registry.js +20 -0
- package/dist/tui/tools/row-format.js +187 -0
- package/dist/tui/tools/search-renderer.js +38 -0
- package/dist/tui/tools/shared.js +97 -0
- package/dist/tui/tools/write-renderer.js +15 -0
- package/dist/tui/transcript-model.js +441 -0
- package/dist/tui/ui-preferences.js +79 -0
- package/dist/tui/usage-view.js +77 -0
- package/dist/tui/vim-mode.js +99 -0
- package/dist/update/check.js +15 -0
- package/dist/update/npm.js +51 -0
- package/dist/update/run.js +129 -0
- package/dist/version.js +2 -0
- package/dist/workspace/checkpoint-store.js +210 -0
- package/dist/workspace/checkpoint.js +413 -0
- package/dist/workspace/restore.js +232 -0
- package/package.json +63 -5
- package/vendor/context-engine/package.json +11 -0
- package/vendor/context-engine/src/compaction-planner.mjs +57 -0
- package/vendor/context-engine/src/contracts.mjs +48 -0
- package/vendor/context-engine/src/engine.mjs +445 -0
- package/vendor/context-engine/src/node/context-export.mjs +164 -0
- package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
- package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
- package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
- package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
- package/vendor/context-engine/src/profiles.mjs +10 -0
- package/vendor/context-engine/src/retrieval.mjs +104 -0
- package/vendor/context-engine/src/summary.mjs +97 -0
- package/vendor/context-engine/src/usage.mjs +34 -0
- package/vendor/harness-ui/package.json +38 -0
- package/vendor/harness-ui/src/acp-agent.mjs +350 -0
- package/vendor/harness-ui/src/agent-service.mjs +2188 -0
- package/vendor/harness-ui/src/agui-events.mjs +452 -0
- package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
- package/vendor/harness-ui/src/artifact-store.mjs +39 -0
- package/vendor/harness-ui/src/assistenza.mjs +123 -0
- package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
- package/vendor/harness-ui/src/automation-store.mjs +145 -0
- package/vendor/harness-ui/src/browser-annota.mjs +639 -0
- package/vendor/harness-ui/src/browser-frame.mjs +211 -0
- package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
- package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
- package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
- package/vendor/harness-ui/src/browser-stream.mjs +445 -0
- package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
- package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
- package/vendor/harness-ui/src/config.mjs +697 -0
- package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
- package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
- package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
- package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
- package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
- package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
- package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
- package/vendor/harness-ui/src/context-runtime.mjs +118 -0
- package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
- package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
- package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
- package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
- package/vendor/harness-ui/src/custom-task.mjs +171 -0
- package/vendor/harness-ui/src/doctor.mjs +142 -0
- package/vendor/harness-ui/src/document-filename.mjs +97 -0
- package/vendor/harness-ui/src/document-generator.mjs +493 -0
- package/vendor/harness-ui/src/document-report.mjs +331 -0
- package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
- package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
- package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
- package/vendor/harness-ui/src/forge-contract.mjs +221 -0
- package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
- package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
- package/vendor/harness-ui/src/generation-idle.mjs +332 -0
- package/vendor/harness-ui/src/gguf-header.mjs +207 -0
- package/vendor/harness-ui/src/git-service.mjs +626 -0
- package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
- package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
- package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
- package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
- package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
- package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
- package/vendor/harness-ui/src/hook-registry.mjs +186 -0
- package/vendor/harness-ui/src/http-app.mjs +6259 -0
- package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
- package/vendor/harness-ui/src/id-archivio.mjs +27 -0
- package/vendor/harness-ui/src/image-generator.mjs +143 -0
- package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
- package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
- package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
- package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
- package/vendor/harness-ui/src/library-store.mjs +652 -0
- package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
- package/vendor/harness-ui/src/local-model-store.mjs +339 -0
- package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
- package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
- package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
- package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
- package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
- package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
- package/vendor/harness-ui/src/mcp-client.mjs +98 -0
- package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
- package/vendor/harness-ui/src/mcp-session.mjs +177 -0
- package/vendor/harness-ui/src/memory-store.mjs +227 -0
- package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
- package/vendor/harness-ui/src/model-catalog.mjs +129 -0
- package/vendor/harness-ui/src/model-destination.mjs +189 -0
- package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
- package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
- package/vendor/harness-ui/src/notes-store.mjs +250 -0
- package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
- package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
- package/vendor/harness-ui/src/path-policy.mjs +442 -0
- package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
- package/vendor/harness-ui/src/plugin-session.mjs +180 -0
- package/vendor/harness-ui/src/process-policy.mjs +345 -0
- package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
- package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
- package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
- package/vendor/harness-ui/src/provider-probe.mjs +582 -0
- package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
- package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
- package/vendor/harness-ui/src/public-problem.mjs +109 -0
- package/vendor/harness-ui/src/research/card.mjs +235 -0
- package/vendor/harness-ui/src/research/citations.mjs +142 -0
- package/vendor/harness-ui/src/research/collector.mjs +275 -0
- package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
- package/vendor/harness-ui/src/research/dossier.mjs +114 -0
- package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
- package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
- package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
- package/vendor/harness-ui/src/research/independence.mjs +159 -0
- package/vendor/harness-ui/src/research/ledger.mjs +166 -0
- package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
- package/vendor/harness-ui/src/research/narration.mjs +181 -0
- package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
- package/vendor/harness-ui/src/research/opposing.mjs +305 -0
- package/vendor/harness-ui/src/research/outline.mjs +111 -0
- package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
- package/vendor/harness-ui/src/research/pdf.mjs +291 -0
- package/vendor/harness-ui/src/research/plan.mjs +301 -0
- package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
- package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
- package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
- package/vendor/harness-ui/src/research/recheck.mjs +194 -0
- package/vendor/harness-ui/src/research/report.mjs +203 -0
- package/vendor/harness-ui/src/research/run.mjs +527 -0
- package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
- package/vendor/harness-ui/src/research/verification.mjs +572 -0
- package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
- package/vendor/harness-ui/src/research-store.mjs +1133 -0
- package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
- package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
- package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
- package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
- package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
- package/vendor/harness-ui/src/search-source-store.mjs +172 -0
- package/vendor/harness-ui/src/session-registry.mjs +6095 -0
- package/vendor/harness-ui/src/session-store.mjs +220 -0
- package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
- package/vendor/harness-ui/src/setup-stato.mjs +31 -0
- package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
- package/vendor/harness-ui/src/skill-registry.mjs +120 -0
- package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
- package/vendor/harness-ui/src/static-files.mjs +96 -0
- package/vendor/harness-ui/src/stream-partition.mjs +123 -0
- package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
- package/vendor/harness-ui/src/task-catalog.mjs +65 -0
- package/vendor/harness-ui/src/tasks-store.mjs +220 -0
- package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
- package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
- package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
- package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
- package/vendor/harness-ui/src/usage-cache.mjs +315 -0
- package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
- package/vendor/harness-ui/src/workspace-context.mjs +124 -0
- package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
- package/vendor/harness-ui/src/workspace-files.mjs +589 -0
- package/vendor/harness-ui/src/workspace-info.mjs +189 -0
- package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
- package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
- package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
- package/vendor/manifest.json +170 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { rm } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { assertNoUnknownOptions, writeValue } from "./context.js";
|
|
4
|
+
import { installExtension, removeInstalledExtension } from "../extensions/installer.js";
|
|
5
|
+
import { createTrustAuthority } from "../security/trust-authority.js";
|
|
6
|
+
import { createMcpFacade } from "../services/mcp-facade.js";
|
|
7
|
+
import { createHookFacade } from "../services/hook-facade.js";
|
|
8
|
+
import { createPluginFacade } from "../services/plugin-facade.js";
|
|
9
|
+
import { createWorkflowCatalog } from "../services/workflow-catalog.js";
|
|
10
|
+
import { createWorkflowCenterModel, renderWorkflowCenterLines } from "../tui/overlays/workflow-center.js";
|
|
11
|
+
function id(v) { if (!v || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,120}$/u.test(v) || v.includes('..'))
|
|
12
|
+
throw new Error('EXTENSION_ID_INVALID'); return v; }
|
|
13
|
+
function authority(ctx) { return createTrustAuthority({ projectRoot: ctx.projectRoot, trustRoot: ctx.paths.trust.projects }); }
|
|
14
|
+
async function removeMirror(root, value) { const clean = id(value); await rm(join(root, `${clean}.json`), { force: true }); }
|
|
15
|
+
async function mirrorRoot(ctx, kind) { const roots = await authority(ctx).compatibilityRoots(); return kind === 'hook' ? roots.hooks : kind === 'mcp' ? roots.mcp : roots.plugins; }
|
|
16
|
+
export async function runMcpCommand(ctx, args0, facade = createMcpFacade({ projectRoot: ctx.projectRoot, repoRoot: ctx.repoRoot, paths: ctx.paths })) {
|
|
17
|
+
const a = [...args0], op = a.shift() ?? 'list';
|
|
18
|
+
if (op === 'list') {
|
|
19
|
+
assertNoUnknownOptions(a);
|
|
20
|
+
writeValue(ctx, await facade.list());
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
const sid = id(a.shift());
|
|
24
|
+
assertNoUnknownOptions(a);
|
|
25
|
+
if (op === 'trust') {
|
|
26
|
+
const result = await facade.trust(sid);
|
|
27
|
+
writeValue(ctx, { ok: true, id: sid, changed: result.changed }, result.changed ? 'Trusted MCP ' + sid + '\n' : 'MCP ' + sid + ' already trusted by the project authority\n');
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
if (op === 'untrust') {
|
|
31
|
+
const result = await facade.untrust(sid);
|
|
32
|
+
writeValue(ctx, { ok: true, id: sid, changed: result.changed }, 'Untrusted MCP ' + sid + '\n');
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
if (op === 'test') {
|
|
36
|
+
const result = await facade.probe(sid);
|
|
37
|
+
const ok = result.health.state === 'healthy' || result.health.state === 'degraded';
|
|
38
|
+
const output = {
|
|
39
|
+
ok, id: sid, health: result.health, server: result.server, protocol: result.protocol, capabilities: result.capabilities,
|
|
40
|
+
tools: result.permission.effectiveTools, missingAllowlistedTools: result.permission.missingAllowlistedTools,
|
|
41
|
+
trust: result.trust, permission: result.permission, logs: result.logs, error: ok ? null : result.health.message
|
|
42
|
+
};
|
|
43
|
+
writeValue(ctx, output);
|
|
44
|
+
return ok ? 0 : 15;
|
|
45
|
+
}
|
|
46
|
+
throw new Error('Unsupported mcp operation: ' + op);
|
|
47
|
+
}
|
|
48
|
+
export async function runHookCommand(ctx, args0, facade = createHookFacade({ projectRoot: ctx.projectRoot, repoRoot: ctx.repoRoot, paths: ctx.paths })) {
|
|
49
|
+
const a = [...args0], op = a.shift() ?? 'list';
|
|
50
|
+
if (op === 'list') {
|
|
51
|
+
assertNoUnknownOptions(a);
|
|
52
|
+
if (a.length)
|
|
53
|
+
throw new Error('HOOK_LIST_TAKES_NO_ARGUMENTS');
|
|
54
|
+
writeValue(ctx, await facade.list());
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
const hid = id(a.shift());
|
|
58
|
+
if (op === 'trust') {
|
|
59
|
+
assertNoUnknownOptions(a);
|
|
60
|
+
if (a.length)
|
|
61
|
+
throw new Error('HOOK_TRUST_TAKES_ONE_ID');
|
|
62
|
+
const result = await facade.trust(hid);
|
|
63
|
+
writeValue(ctx, { ok: true, id: hid, changed: result.changed }, result.changed ? 'Trusted hook ' + hid + '\n' : 'Hook ' + hid + ' already trusted by the project authority\n');
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
if (op === 'untrust') {
|
|
67
|
+
assertNoUnknownOptions(a);
|
|
68
|
+
if (a.length)
|
|
69
|
+
throw new Error('HOOK_UNTRUST_TAKES_ONE_ID');
|
|
70
|
+
const result = await facade.untrust(hid);
|
|
71
|
+
writeValue(ctx, { ok: true, id: hid, changed: result.changed }, 'Untrusted hook ' + hid + '\n');
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
if (op === 'dry-run') {
|
|
75
|
+
const eventType = a.shift();
|
|
76
|
+
if (!eventType)
|
|
77
|
+
throw new Error('HOOK_EVENT_REQUIRED');
|
|
78
|
+
const action = a.length ? a.join(' ') : null;
|
|
79
|
+
const result = await facade.dryRun(hid, { eventType, action });
|
|
80
|
+
writeValue(ctx, { ok: true, id: hid, ...result }, 'Executed hook ' + hid + ' with a synthetic ' + eventType + ' event through the real hook process path.\n');
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
if (op === 'history') {
|
|
84
|
+
const rawLimit = a.shift();
|
|
85
|
+
assertNoUnknownOptions(a);
|
|
86
|
+
if (a.length)
|
|
87
|
+
throw new Error('HOOK_HISTORY_ARGUMENTS_INVALID');
|
|
88
|
+
let limit;
|
|
89
|
+
if (rawLimit !== undefined) {
|
|
90
|
+
limit = Number(rawLimit);
|
|
91
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 50)
|
|
92
|
+
throw new Error('HOOK_HISTORY_LIMIT_INVALID');
|
|
93
|
+
}
|
|
94
|
+
const rows = await facade.history(hid, limit === undefined ? {} : { limit });
|
|
95
|
+
writeValue(ctx, rows);
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
if (op === 'quarantine') {
|
|
99
|
+
const reason = a.length ? a.join(' ') : null;
|
|
100
|
+
const result = await facade.quarantine(hid, reason);
|
|
101
|
+
writeValue(ctx, { ok: true, id: hid, changed: result.changed, quarantine: result.record }, 'Quarantined hook ' + hid + '. It remains denied until quarantine is explicitly released, and release does not restore trust.\n');
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
if (op === 'release' || op === 'release-quarantine') {
|
|
105
|
+
assertNoUnknownOptions(a);
|
|
106
|
+
if (a.length)
|
|
107
|
+
throw new Error('HOOK_RELEASE_TAKES_ONE_ID');
|
|
108
|
+
const result = await facade.releaseQuarantine(hid);
|
|
109
|
+
writeValue(ctx, { ok: true, id: hid, changed: result.changed }, 'Released hook quarantine for ' + hid + '. The hook remains untrusted until explicitly trusted again.\n');
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
throw new Error('Unsupported hook operation: ' + op);
|
|
113
|
+
}
|
|
114
|
+
export async function runPluginCommand(ctx, args0, facade = createPluginFacade({ projectRoot: ctx.projectRoot, repoRoot: ctx.repoRoot, paths: ctx.paths })) {
|
|
115
|
+
const a = [...args0], op = a.shift() ?? 'list';
|
|
116
|
+
if (op === 'install') {
|
|
117
|
+
const source = a.shift();
|
|
118
|
+
if (!source)
|
|
119
|
+
throw new Error('PLUGIN_SOURCE_REQUIRED');
|
|
120
|
+
assertNoUnknownOptions(a);
|
|
121
|
+
const result = await installExtension({ source, projectRoot: ctx.projectRoot });
|
|
122
|
+
writeValue(ctx, result, 'Installed ' + result.id + ' ' + result.version + '; project trust must cover the new package before it can run\n');
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
if (op === 'remove') {
|
|
126
|
+
const pid = id(a.shift());
|
|
127
|
+
assertNoUnknownOptions(a);
|
|
128
|
+
const result = await removeInstalledExtension({ projectRoot: ctx.projectRoot, id: pid });
|
|
129
|
+
const trust = authority(ctx);
|
|
130
|
+
await trust.untrustScope({ kind: 'plugin', id: pid });
|
|
131
|
+
await removeMirror(await mirrorRoot(ctx, 'plugin'), pid);
|
|
132
|
+
writeValue(ctx, result, 'Removed ' + pid + '\n');
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
if (op === 'list') {
|
|
136
|
+
assertNoUnknownOptions(a);
|
|
137
|
+
if (a.length)
|
|
138
|
+
throw new Error('PLUGIN_LIST_TAKES_NO_ARGUMENTS');
|
|
139
|
+
writeValue(ctx, await facade.list());
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
const pid = id(a.shift());
|
|
143
|
+
if (op === 'trust') {
|
|
144
|
+
assertNoUnknownOptions(a);
|
|
145
|
+
if (a.length)
|
|
146
|
+
throw new Error('PLUGIN_TRUST_TAKES_ONE_ID');
|
|
147
|
+
const result = await facade.trust(pid);
|
|
148
|
+
writeValue(ctx, { ok: true, id: pid, guard: result.guard, changed: result.changed }, result.changed ? 'Trusted plugin ' + pid + '\n' : 'Plugin ' + pid + ' already trusted by the project authority\n');
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
if (op === 'untrust') {
|
|
152
|
+
assertNoUnknownOptions(a);
|
|
153
|
+
if (a.length)
|
|
154
|
+
throw new Error('PLUGIN_UNTRUST_TAKES_ONE_ID');
|
|
155
|
+
const result = await facade.untrust(pid);
|
|
156
|
+
writeValue(ctx, { ok: true, id: pid, changed: result.changed }, 'Untrusted plugin ' + pid + '\n');
|
|
157
|
+
return 0;
|
|
158
|
+
}
|
|
159
|
+
if (op === 'quarantine') {
|
|
160
|
+
const reason = a.length ? a.join(' ') : null;
|
|
161
|
+
const result = await facade.quarantine(pid, reason);
|
|
162
|
+
writeValue(ctx, { ok: true, id: pid, changed: result.changed, quarantine: result.record }, 'Quarantined plugin ' + pid + '. It cannot regain trust until quarantine is explicitly released.\n');
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
165
|
+
if (op === 'release' || op === 'release-quarantine') {
|
|
166
|
+
assertNoUnknownOptions(a);
|
|
167
|
+
if (a.length)
|
|
168
|
+
throw new Error('PLUGIN_RELEASE_TAKES_ONE_ID');
|
|
169
|
+
const result = await facade.releaseQuarantine(pid);
|
|
170
|
+
writeValue(ctx, { ok: true, id: pid, changed: result.changed }, 'Released plugin quarantine for ' + pid + '. The plugin remains untrusted until explicitly trusted again.\n');
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
throw new Error('Unsupported plugin operation: ' + op);
|
|
174
|
+
}
|
|
175
|
+
export async function runCustomCommand(ctx, args0, catalog = createWorkflowCatalog({ projectRoot: ctx.projectRoot, repoRoot: ctx.repoRoot, paths: ctx.paths })) {
|
|
176
|
+
const a = [...args0], op = a.shift() ?? 'list';
|
|
177
|
+
if (op === 'list') {
|
|
178
|
+
assertNoUnknownOptions(a);
|
|
179
|
+
if (a.length)
|
|
180
|
+
throw new Error('COMMAND_LIST_TAKES_NO_ARGUMENTS');
|
|
181
|
+
const rows = await catalog.list();
|
|
182
|
+
const human = renderWorkflowCenterLines(createWorkflowCenterModel(rows, 0)).join('\n') + '\n';
|
|
183
|
+
writeValue(ctx, rows, human);
|
|
184
|
+
return 0;
|
|
185
|
+
}
|
|
186
|
+
if (op === 'show') {
|
|
187
|
+
const selector = a.shift();
|
|
188
|
+
if (!selector)
|
|
189
|
+
throw new Error('COMMAND_NAME_REQUIRED');
|
|
190
|
+
assertNoUnknownOptions(a);
|
|
191
|
+
if (a.length)
|
|
192
|
+
throw new Error('COMMAND_SHOW_TAKES_ONE_NAME');
|
|
193
|
+
const row = await catalog.show(selector);
|
|
194
|
+
const human = renderWorkflowCenterLines(createWorkflowCenterModel([row], 0)).join('\n') + '\n';
|
|
195
|
+
writeValue(ctx, row, human);
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
if (op === 'run') {
|
|
199
|
+
const selector = a.shift();
|
|
200
|
+
if (!selector)
|
|
201
|
+
throw new Error('COMMAND_NAME_REQUIRED');
|
|
202
|
+
const result = await catalog.run(selector, a);
|
|
203
|
+
writeValue(ctx, result, result.prompt.endsWith('\n') ? result.prompt : result.prompt + '\n');
|
|
204
|
+
return 0;
|
|
205
|
+
}
|
|
206
|
+
throw new Error('Unsupported command operation: ' + op);
|
|
207
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { assertNoUnknownOptions, takeFlag, writeValue } from "./context.js";
|
|
2
|
+
import { createTrustAuthority } from "../security/trust-authority.js";
|
|
3
|
+
import { t } from "../i18n/index.js";
|
|
4
|
+
import { inertProjectTrustText } from "../tui/project-trust-prompt.js";
|
|
5
|
+
function publicSnapshot(value, folder) {
|
|
6
|
+
return {
|
|
7
|
+
trusted: value.trusted, reason: value.reason, project: value.workspace.canonicalRoot, identity: value.workspace.identityHash,
|
|
8
|
+
nestedRepository: value.workspace.nestedRepository, nestedRepositories: value.nestedRepositories.map(entry => entry.relativePath),
|
|
9
|
+
resourceCount: value.resources.filter(row => row.current).length,
|
|
10
|
+
storeSchema: value.storeSchema, migrationRequired: value.migrationRequired, rollbackAvailable: value.rollbackAvailable,
|
|
11
|
+
resources: value.resources.map(row => ({ kind: row.kind, id: row.id, path: row.relativePath, state: row.state })),
|
|
12
|
+
/* R4 (gate E2): the folder decision, apart from the per-file snapshot. */
|
|
13
|
+
folder: { decided: folder.decided, source: folder.source, path: folder.path, sessionOnly: folder.sessionOnly }
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** `deps.homeDir` exists for tests: which folder counts as the home folder (default: the user's home). */
|
|
17
|
+
export async function runProjectCommand(ctx, args0, deps = {}) {
|
|
18
|
+
const args = [...args0];
|
|
19
|
+
const operation = args.shift() ?? 'status';
|
|
20
|
+
const includeNested = operation === 'trust' || operation === 'migrate-trust' ? takeFlag(args, '--include-nested') : false;
|
|
21
|
+
assertNoUnknownOptions(args);
|
|
22
|
+
if (args.length)
|
|
23
|
+
throw new Error('PROJECT_COMMAND_ARGUMENT_INVALID');
|
|
24
|
+
const authority = createTrustAuthority({ projectRoot: ctx.projectRoot, trustRoot: ctx.paths.trust.projects, ...(deps.homeDir ? { homeDir: deps.homeDir } : {}) });
|
|
25
|
+
const view = async (snapshot) => publicSnapshot(snapshot, await authority.folderDecision(snapshot));
|
|
26
|
+
if (operation === 'status') {
|
|
27
|
+
writeValue(ctx, await view(await authority.inspect()));
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
if (operation === 'trust') {
|
|
31
|
+
const before = await authority.inspect();
|
|
32
|
+
const folder = await authority.folderDecision(before);
|
|
33
|
+
/* Q-R4-1: the home folder and a drive root are never saved. Their project files still follow the snapshot rule. */
|
|
34
|
+
if (folder.sessionOnly) {
|
|
35
|
+
const sessionLine = t('firstrun.project.sessionOnly', { path: inertProjectTrustText(before.workspace.canonicalRoot) }) + '\n';
|
|
36
|
+
/* Q-R4-17: its files are trusted only by the interactive question, for that session; this command saves nothing. */
|
|
37
|
+
writeValue(ctx, publicSnapshot(before, folder), sessionLine);
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
if (before.trusted && folder.decided) {
|
|
41
|
+
writeValue(ctx, publicSnapshot(before, folder), before.migrationRequired ? `Project already trusted; run talos project migrate-trust to migrate the legacy store explicitly\n` : `Project already trusted ${before.workspace.canonicalRoot}\n`);
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
const verified = await authority.trustProject({ expected: before, includeNested });
|
|
45
|
+
writeValue(ctx, await view(verified), `Trusted project ${verified.workspace.canonicalRoot}\n`);
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
if (operation === 'revoke') {
|
|
49
|
+
const revoked = await authority.revokeProject();
|
|
50
|
+
writeValue(ctx, await view(revoked), `Revoked project ${revoked.workspace.canonicalRoot}\n`);
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
if (operation === 'migrate-trust') {
|
|
54
|
+
const before = await authority.inspect();
|
|
55
|
+
if (before.storeSchema !== 'v1') {
|
|
56
|
+
writeValue(ctx, await view(before), before.storeSchema === 'v2' ? 'Project trust store is already v2; no migration was performed\n' : 'No legacy trust record to migrate\n');
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
const migrated = await authority.migrateLegacy({ includeNested });
|
|
60
|
+
writeValue(ctx, await view(migrated), `Project trust store migrated to v2; rollback backup: ${await authority.migrationBackupPath()}\n`);
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
if (operation === 'rollback-trust-migration') {
|
|
64
|
+
const before = await authority.inspect();
|
|
65
|
+
if (!before.rollbackAvailable) {
|
|
66
|
+
writeValue(ctx, await view(before), 'No migration rollback backup is available\n');
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
const rolledBack = await authority.rollbackMigration();
|
|
70
|
+
writeValue(ctx, await view(rolledBack), `Rolled project trust store back to legacy v1\n`);
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`Unsupported project operation: ${operation}`);
|
|
74
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const NAME = /^[a-z0-9][a-z0-9-]{0,63}$/u;
|
|
4
|
+
const MAX_FILE_BYTES = 65_536;
|
|
5
|
+
export const WORKFLOW_MAX_STEPS = 64;
|
|
6
|
+
export const WORKFLOW_MAX_TEXT_BYTES = 16_384;
|
|
7
|
+
function fail(code, message = code) { throw Object.assign(new Error(message), { code }); }
|
|
8
|
+
function textBytes(value) { return Buffer.byteLength(value, 'utf8'); }
|
|
9
|
+
function assertBoundedText(value, code, { allowEmpty = false } = {}) {
|
|
10
|
+
if (typeof value !== 'string' || (!allowEmpty && !value.trim()) || textBytes(value) > WORKFLOW_MAX_TEXT_BYTES)
|
|
11
|
+
fail(code);
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function exactKeys(value, allowed, code) {
|
|
15
|
+
if (Object.keys(value).some(key => !allowed.includes(key)))
|
|
16
|
+
fail(code);
|
|
17
|
+
}
|
|
18
|
+
function workflowSteps(raw) {
|
|
19
|
+
let value;
|
|
20
|
+
try {
|
|
21
|
+
value = JSON.parse(raw);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
fail('WORKFLOW_STEPS_INVALID');
|
|
25
|
+
}
|
|
26
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
27
|
+
fail('WORKFLOW_STEPS_INVALID');
|
|
28
|
+
if (value.length > WORKFLOW_MAX_STEPS)
|
|
29
|
+
fail('WORKFLOW_TOO_MANY_STEPS');
|
|
30
|
+
const steps = [];
|
|
31
|
+
for (const entry of value) {
|
|
32
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry))
|
|
33
|
+
fail('WORKFLOW_STEP_INVALID');
|
|
34
|
+
const row = entry;
|
|
35
|
+
if (row.type === 'prompt') {
|
|
36
|
+
exactKeys(row, ['type', 'template'], 'WORKFLOW_STEP_UNKNOWN_KEY');
|
|
37
|
+
steps.push({ type: 'prompt', template: assertBoundedText(row.template, 'WORKFLOW_PROMPT_INVALID') });
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (row.type === 'skill') {
|
|
41
|
+
exactKeys(row, ['type', 'name'], 'WORKFLOW_STEP_UNKNOWN_KEY');
|
|
42
|
+
const name = assertBoundedText(row.name, 'WORKFLOW_SKILL_INVALID');
|
|
43
|
+
if (!NAME.test(name))
|
|
44
|
+
fail('WORKFLOW_SKILL_INVALID');
|
|
45
|
+
steps.push({ type: 'skill', name });
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (row.type === 'command') {
|
|
49
|
+
exactKeys(row, ['type', 'name', 'args'], 'WORKFLOW_STEP_UNKNOWN_KEY');
|
|
50
|
+
const name = assertBoundedText(row.name, 'WORKFLOW_COMMAND_INVALID');
|
|
51
|
+
if (!NAME.test(name))
|
|
52
|
+
fail('WORKFLOW_COMMAND_INVALID');
|
|
53
|
+
const args = row.args ?? [];
|
|
54
|
+
if (!Array.isArray(args) || args.length > WORKFLOW_MAX_STEPS || !args.every(arg => typeof arg === 'string' && textBytes(arg) <= WORKFLOW_MAX_TEXT_BYTES))
|
|
55
|
+
fail('WORKFLOW_COMMAND_ARGS_INVALID');
|
|
56
|
+
steps.push({ type: 'command', name, args: [...args] });
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
fail('WORKFLOW_STEP_TYPE_INVALID');
|
|
60
|
+
}
|
|
61
|
+
return steps;
|
|
62
|
+
}
|
|
63
|
+
function parse(text, file) {
|
|
64
|
+
if (Buffer.byteLength(text) > MAX_FILE_BYTES)
|
|
65
|
+
throw new Error('COMMAND_TOO_LARGE');
|
|
66
|
+
const m = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)([\s\S]*)$/u.exec(text);
|
|
67
|
+
if (!m)
|
|
68
|
+
throw new Error(`COMMAND_FRONT_MATTER_INVALID:${file}`);
|
|
69
|
+
const meta = {};
|
|
70
|
+
for (const line of m[1].split(/\r?\n/u)) {
|
|
71
|
+
if (!line.trim())
|
|
72
|
+
continue;
|
|
73
|
+
const mm = /^([A-Za-z]+):\s*(.*)$/u.exec(line);
|
|
74
|
+
if (!mm)
|
|
75
|
+
throw new Error('COMMAND_FRONT_MATTER_INVALID');
|
|
76
|
+
if (!['name', 'description', 'arguments', 'kind', 'steps'].includes(mm[1]))
|
|
77
|
+
throw new Error('COMMAND_FIELD_UNKNOWN');
|
|
78
|
+
if (Object.hasOwn(meta, mm[1]))
|
|
79
|
+
throw new Error('COMMAND_FIELD_DUPLICATE');
|
|
80
|
+
meta[mm[1]] = mm[2].replace(/^['"]|['"]$/gu, '');
|
|
81
|
+
}
|
|
82
|
+
if (!meta.name || !NAME.test(meta.name) || !meta.description)
|
|
83
|
+
throw new Error('COMMAND_FRONT_MATTER_INVALID');
|
|
84
|
+
const kind = meta.kind ?? 'command';
|
|
85
|
+
if (kind !== 'command' && kind !== 'workflow')
|
|
86
|
+
fail('WORKFLOW_KIND_INVALID');
|
|
87
|
+
const body = m[2].trim();
|
|
88
|
+
if (kind === 'command') {
|
|
89
|
+
if (meta.steps !== undefined)
|
|
90
|
+
fail('WORKFLOW_STEPS_FORBIDDEN');
|
|
91
|
+
return { name: meta.name, description: meta.description, arguments: meta.arguments ?? '', body, file, kind: 'command', steps: [] };
|
|
92
|
+
}
|
|
93
|
+
if (meta.steps === undefined)
|
|
94
|
+
fail('WORKFLOW_STEPS_REQUIRED');
|
|
95
|
+
if (body)
|
|
96
|
+
fail('WORKFLOW_BODY_FORBIDDEN');
|
|
97
|
+
return { name: meta.name, description: meta.description, arguments: meta.arguments ?? '', body: '', file, kind: 'workflow', steps: workflowSteps(meta.steps) };
|
|
98
|
+
}
|
|
99
|
+
export async function loadCommandFile(file) { return parse(await readFile(file, 'utf8'), file); }
|
|
100
|
+
async function markdownFiles(root) {
|
|
101
|
+
const out = [];
|
|
102
|
+
async function walk(dir) {
|
|
103
|
+
let entries;
|
|
104
|
+
try {
|
|
105
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
if (e?.code === 'ENOENT')
|
|
109
|
+
return;
|
|
110
|
+
throw e;
|
|
111
|
+
}
|
|
112
|
+
for (const e of entries) {
|
|
113
|
+
const p = join(dir, e.name);
|
|
114
|
+
if (e.isDirectory())
|
|
115
|
+
await walk(p);
|
|
116
|
+
else if (e.isFile() && e.name.endsWith('.md'))
|
|
117
|
+
out.push(p);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
await walk(root);
|
|
121
|
+
return out.sort();
|
|
122
|
+
}
|
|
123
|
+
export async function loadProjectCommands(root) {
|
|
124
|
+
const files = await markdownFiles(join(root, '.talos-cli', 'commands'));
|
|
125
|
+
const out = [];
|
|
126
|
+
const names = new Set();
|
|
127
|
+
for (const f of files) {
|
|
128
|
+
const c = await loadCommandFile(f);
|
|
129
|
+
if (names.has(c.name))
|
|
130
|
+
throw new Error('COMMAND_NAME_DUPLICATE');
|
|
131
|
+
names.add(c.name);
|
|
132
|
+
out.push(c);
|
|
133
|
+
}
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
export function expandCommand(c, vars) {
|
|
137
|
+
return c.body.replace(/\{\{([a-zA-Z0-9_-]+)\}\}/gu, (_m, n) => {
|
|
138
|
+
if (!Object.hasOwn(vars, n))
|
|
139
|
+
throw new Error(`COMMAND_VARIABLE_UNKNOWN:${n}`);
|
|
140
|
+
return vars[n];
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
export const renderProjectCommand = expandCommand;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { kernelProbeDetail } from "../i18n/kernel-map.js";
|
|
2
|
+
import { assertNoUnknownOptions, takeFlag, takeOption, writeValue } from "./context.js";
|
|
3
|
+
import { REASONING_EFFORTS } from "../config/types.js";
|
|
4
|
+
import { importTalosModule } from "../runtime/repo.js";
|
|
5
|
+
import { createEnvironmentKeyConsent, providerOfModel } from "../provider/environment-keys.js";
|
|
6
|
+
import { t } from "../i18n/index.js";
|
|
7
|
+
/*
|
|
8
|
+
* B1 slice 18 — the command line follows the rules of `/provider`: the store sees an environment key only after it
|
|
9
|
+
* was approved (`talos provider env-key <id> --use`; nothing said means No), and `set-key` tests the key against its
|
|
10
|
+
* own provider and saves it only when the test passes. The key is read through the hidden prompt, never from argv.
|
|
11
|
+
*/
|
|
12
|
+
async function runtimeContext(ctx) { return ctx.createRuntimeContext({ projectRoot: ctx.projectRoot, paths: ctx.paths, repoRoot: ctx.repoRoot, model: ctx.invocation.model ?? 'openai:gpt-5-mini', environmentKeys: 'consent' }); }
|
|
13
|
+
function environmentKeyOf(row) { return row && row.environmentVariable ? { variable: String(row.environmentVariable), state: row.keySource === 'environment' ? 'approved' : row.keySource === 'environment-declined' ? 'declined' : 'unanswered' } : null; }
|
|
14
|
+
export async function runProviderCommand(ctx, args0) { const args = [...args0]; const op = args.shift() ?? 'list'; const rc = await runtimeContext(ctx); const store = rc.providerStore; const catalog = rc.tuiCatalog; if (op === 'list') {
|
|
15
|
+
assertNoUnknownOptions(args);
|
|
16
|
+
if (typeof rc.providerControlPlane?.list === 'function') {
|
|
17
|
+
writeValue(ctx, rc.providerControlPlane.list());
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
const rows = store.listPublic();
|
|
21
|
+
let providers = [];
|
|
22
|
+
try {
|
|
23
|
+
providers = await catalog?.listProviders?.() ?? [];
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
providers = [];
|
|
27
|
+
}
|
|
28
|
+
writeValue(ctx, rows.map((row) => ({ ...row, environmentKey: environmentKeyOf(providers.find((p) => p.id === row?.id)) })));
|
|
29
|
+
return 0;
|
|
30
|
+
} const id = args.shift(); if (!id)
|
|
31
|
+
throw new Error('PROVIDER_REQUIRED'); if (op === 'set-key') {
|
|
32
|
+
assertNoUnknownOptions(args);
|
|
33
|
+
if (!ctx.io.readSecret)
|
|
34
|
+
throw new Error('SECRET_INPUT_REQUIRES_TTY');
|
|
35
|
+
if (typeof catalog?.setProviderKey !== 'function')
|
|
36
|
+
throw Object.assign(new Error('PROVIDER_KEY_TEST_UNAVAILABLE'), { code: 'PROVIDER_KEY_TEST_UNAVAILABLE' }); /* a one-token key test is stated before the key is read, so before anything is sent; nothing further is asked */
|
|
37
|
+
const notice = typeof catalog.keyTestNotice === 'function' ? catalog.keyTestNotice(id) : null;
|
|
38
|
+
if (notice)
|
|
39
|
+
ctx.io.writeErr(`${notice}\n`);
|
|
40
|
+
let secret = await ctx.io.readSecret(`Key for ${id}: `);
|
|
41
|
+
let result;
|
|
42
|
+
try {
|
|
43
|
+
result = await catalog.setProviderKey(id, secret);
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
secret = '';
|
|
47
|
+
}
|
|
48
|
+
const saved = result.saved === true;
|
|
49
|
+
writeValue(ctx, { ok: saved, provider: id, outcome: result.outcome, httpStatus: result.httpStatus, saved }, `${result.message} ${saved ? 'Saved.' : 'The key was not saved.'}\n`);
|
|
50
|
+
return saved ? 0 : 11;
|
|
51
|
+
} if (op === 'env-key') {
|
|
52
|
+
const use = takeFlag(args, '--use');
|
|
53
|
+
const decline = takeFlag(args, '--decline');
|
|
54
|
+
assertNoUnknownOptions(args);
|
|
55
|
+
if (use && decline)
|
|
56
|
+
throw new Error('Use either --use or --decline');
|
|
57
|
+
const row = (await catalog.listProviders()).find((p) => p.id === id);
|
|
58
|
+
if (!row)
|
|
59
|
+
throw Object.assign(new Error('PROVIDER_INVALID'), { code: 'PROVIDER_INVALID' });
|
|
60
|
+
const answering = use || decline;
|
|
61
|
+
if (answering && row.environmentVariable)
|
|
62
|
+
await catalog.answerEnvironmentKey(id, use ? 'yes' : 'no');
|
|
63
|
+
const after = answering ? (await catalog.listProviders()).find((p) => p.id === id) : row;
|
|
64
|
+
const state = environmentKeyOf(after);
|
|
65
|
+
if (!state) {
|
|
66
|
+
writeValue(ctx, { ok: !answering, provider: id, environmentKey: null }, `No key for ${after?.label ?? id} was found in the environment.\n`);
|
|
67
|
+
return answering ? 2 : 0;
|
|
68
|
+
}
|
|
69
|
+
writeValue(ctx, { ok: true, provider: id, environmentKey: state }, `${state.variable}: ${state.state === 'approved' ? 'used for ' + after.label : state.state === 'declined' ? 'declined, not used' : 'not used until approved (talos provider env-key ' + id + ' --use)'}\n`);
|
|
70
|
+
return 0;
|
|
71
|
+
} if (op === 'remove-key') {
|
|
72
|
+
assertNoUnknownOptions(args);
|
|
73
|
+
store.clearKey(id);
|
|
74
|
+
rc.providerControlPlane?.invalidate?.(id);
|
|
75
|
+
writeValue(ctx, { ok: true, provider: id }, `Removed key for ${id}\n`);
|
|
76
|
+
return 0;
|
|
77
|
+
} if (op === 'configure') {
|
|
78
|
+
const endpoint = takeOption(args, '--endpoint');
|
|
79
|
+
const timeoutRaw = takeOption(args, '--timeout');
|
|
80
|
+
assertNoUnknownOptions(args);
|
|
81
|
+
const current = store.getRuntime(id) ?? {};
|
|
82
|
+
const timeoutSeconds = timeoutRaw === undefined ? current.timeoutSeconds : Number(timeoutRaw);
|
|
83
|
+
if (timeoutRaw !== undefined && (!Number.isInteger(timeoutSeconds) || timeoutSeconds < 5 || timeoutSeconds > 300))
|
|
84
|
+
throw new Error('PROVIDER_TIMEOUT_INVALID');
|
|
85
|
+
if (endpoint === undefined && timeoutRaw === undefined)
|
|
86
|
+
throw new Error('PROVIDER_CONFIG_REQUIRED');
|
|
87
|
+
store.setRuntime(id, { ...current, ...(endpoint !== undefined ? { endpoint } : {}), ...(timeoutSeconds !== undefined ? { timeoutSeconds } : {}) });
|
|
88
|
+
rc.providerControlPlane?.invalidate?.(id);
|
|
89
|
+
writeValue(ctx, { ok: true, provider: id, runtime: store.getRuntime(id) }, `Configured ${id}\n`);
|
|
90
|
+
return 0;
|
|
91
|
+
} if (op === 'test') {
|
|
92
|
+
assertNoUnknownOptions(args); /* M5-A/M5-C: ordinary health testing never opts into token generation, and remediation is the control-plane decision. */
|
|
93
|
+
if (typeof rc.providerControlPlane?.probe === 'function') {
|
|
94
|
+
const profile = await rc.providerControlPlane.probe(id);
|
|
95
|
+
const health = profile.lastHealth ?? {};
|
|
96
|
+
const latency = Number.isFinite(health.latencyMs) ? ` · ${health.latencyMs} ms` : ''; /* R2 (E8 / Q-R2-7): a missing key is the one sentence, as a line of its own; the probe's free-text reason (the kernel's, Italian) is mapped by the remediation code and not printed. */
|
|
97
|
+
const missingKey = profile.remediation?.code === 'add-key';
|
|
98
|
+
const remedy = missingKey ? `${profile.remediation.message}\n` : profile.remediation?.code && profile.remediation.code !== 'none' ? `Remedy: ${profile.remediation.message}\n` : ''; /* R4 Q-R4-16 (a): the kernel's sentence in English by a pinned template, or not printed. */
|
|
99
|
+
const mapped = health.detail && !missingKey ? kernelProbeDetail(String(health.detail), { label: String(profile.label ?? id) }) : null;
|
|
100
|
+
const detail = mapped ? `${mapped}\n` : '';
|
|
101
|
+
writeValue(ctx, missingKey ? { ...profile, code: 'PROVIDER_KEY_MISSING' } : profile, `${profile.label ?? id}: ${health.state ?? 'unknown'}${latency}\n${detail}${remedy}`);
|
|
102
|
+
return health.state === 'available' ? 0 : 11;
|
|
103
|
+
}
|
|
104
|
+
const probe = typeof rc.providerProbe === 'function' ? rc.providerProbe() : (await importTalosModule(ctx.repoRoot, 'provider-probe.mjs')).createProviderProbe({ leggiChiave: (p) => store.getKey(p), leggiRuntime: (p) => store.getRuntime(p) });
|
|
105
|
+
const result = await probe.prova(id);
|
|
106
|
+
writeValue(ctx, result);
|
|
107
|
+
return result?.esito === 'collegato' ? 0 : 11;
|
|
108
|
+
} throw new Error(`Unsupported provider operation: ${op}`); }
|
|
109
|
+
/*
|
|
110
|
+
* R2 (gate E14, E-03): `talos model list` for a person — one row per model under English columns, never the raw JSON array; the
|
|
111
|
+
* MODEL column is exactly what `talos model use` takes (OpenCode `opencode models` prints one id per line and JSON only with
|
|
112
|
+
* --verbose: `packages/opencode/src/cli/cmd/models.ts:36-46` at 1d6c3c0, 2026-09-24). `--json` keeps the rows unchanged.
|
|
113
|
+
*/
|
|
114
|
+
function contextText(value) {
|
|
115
|
+
if (!Number.isFinite(value) || Number(value) <= 0)
|
|
116
|
+
return t('key.models.unknown');
|
|
117
|
+
const n = Number(value);
|
|
118
|
+
return n >= 1_000_000 ? `${Number((n / 1_000_000).toFixed(1))}M` : n >= 1000 ? `${Math.round(n / 1000)}K` : String(n);
|
|
119
|
+
}
|
|
120
|
+
function flagText(value) { return value === true ? t('key.models.yes') : value === false ? t('key.models.no') : t('key.models.unknown'); }
|
|
121
|
+
function sourceText(value) { return t(value === 'live' ? 'key.models.source.live' : value === 'configured' ? 'key.models.source.configured' : value === 'documented' ? 'key.models.source.documented' : 'key.models.source.unknown'); }
|
|
122
|
+
export function modelListText(rows, notice) {
|
|
123
|
+
const head = notice ? `${notice}\n` : '';
|
|
124
|
+
if (!rows.length)
|
|
125
|
+
return `${head}${t('key.models.none')}\n`;
|
|
126
|
+
const table = [[t('key.models.column.model'), t('key.models.column.context'), t('key.models.column.tools'), t('key.models.column.reasoning'), t('key.models.column.availability')],
|
|
127
|
+
...rows.map(row => [String(row?.id ?? ''), contextText(row?.contextWindow), flagText(row?.toolCalling), flagText(row?.reasoning), sourceText(row?.source ?? row?.available)])];
|
|
128
|
+
const widths = table[0].map((_, column) => Math.max(...table.map(line => line[column].length)));
|
|
129
|
+
const lines = table.map(line => line.map((cell, column) => column === line.length - 1 ? cell : cell.padEnd(widths[column])).join(' '));
|
|
130
|
+
return `${head}${lines.join('\n')}\n${t('key.models.use')}\n`;
|
|
131
|
+
}
|
|
132
|
+
export async function runModelCommand(ctx, args0) { const args = [...args0]; const op = args.shift() ?? 'list'; if (op === 'effort') {
|
|
133
|
+
const effort = args.shift();
|
|
134
|
+
if (!effort)
|
|
135
|
+
throw new Error('REASONING_EFFORT_REQUIRED');
|
|
136
|
+
const scope = (takeOption(args, '--scope') ?? 'user');
|
|
137
|
+
if (!['user', 'project-user'].includes(scope))
|
|
138
|
+
throw new Error('CONFIG_SCOPE_INVALID');
|
|
139
|
+
assertNoUnknownOptions(args);
|
|
140
|
+
const { configSet, configUnset } = await import("../config/commands.js");
|
|
141
|
+
if (effort === 'default') {
|
|
142
|
+
await configUnset({ paths: ctx.paths, projectRoot: ctx.projectRoot, scope, path: 'reasoningEffort' });
|
|
143
|
+
writeValue(ctx, { ok: true, reasoningEffort: null, scope }, `Cleared the ${scope} reasoning-effort override; a lower-precedence setting may still apply, otherwise the model default is used.\n`);
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
if (!REASONING_EFFORTS.includes(effort))
|
|
147
|
+
throw new Error('CONFIG_INVALID_REASONING_EFFORT');
|
|
148
|
+
await configSet({ paths: ctx.paths, projectRoot: ctx.projectRoot, scope, path: 'reasoningEffort', value: effort });
|
|
149
|
+
writeValue(ctx, { ok: true, reasoningEffort: effort, scope }, `Using reasoning effort ${effort} (${scope})\n`);
|
|
150
|
+
return 0;
|
|
151
|
+
} if (op === 'use') {
|
|
152
|
+
const id = args.shift();
|
|
153
|
+
if (!id)
|
|
154
|
+
throw new Error('MODEL_REQUIRED');
|
|
155
|
+
const scope = (takeOption(args, '--scope') ?? 'user');
|
|
156
|
+
if (!['user', 'project', 'project-user'].includes(scope))
|
|
157
|
+
throw new Error('CONFIG_SCOPE_INVALID');
|
|
158
|
+
assertNoUnknownOptions(args);
|
|
159
|
+
const provider = providerOfModel(id);
|
|
160
|
+
const registryModule = await importTalosModule(ctx.repoRoot, 'provider-registry.mjs');
|
|
161
|
+
if (!provider || !registryModule.REGISTRO_FORNITORI?.[provider])
|
|
162
|
+
throw Object.assign(new Error('MODEL_PROVIDER_INVALID'), { code: 'MODEL_PROVIDER_INVALID' });
|
|
163
|
+
const { configSet } = await import("../config/commands.js");
|
|
164
|
+
await configSet({ paths: ctx.paths, projectRoot: ctx.projectRoot, scope, path: 'model', value: id }); /* choosing a model chooses its provider: the interactive screen starts from the same choice */
|
|
165
|
+
createEnvironmentKeyConsent({ dataRoot: ctx.paths.dataRoot, registry: registryModule.REGISTRO_FORNITORI, env: {} }).chooseProvider(provider);
|
|
166
|
+
writeValue(ctx, { ok: true, model: id, scope }, `Using ${id} (${scope})\n`);
|
|
167
|
+
return 0;
|
|
168
|
+
} if (op === 'list') {
|
|
169
|
+
const provider = takeOption(args, '--provider');
|
|
170
|
+
assertNoUnknownOptions(args);
|
|
171
|
+
const rc = await runtimeContext(ctx);
|
|
172
|
+
const catalog = rc.tuiCatalog;
|
|
173
|
+
if (provider) {
|
|
174
|
+
if (typeof catalog?.providerModels !== 'function')
|
|
175
|
+
throw Object.assign(new Error('PROVIDER_CATALOG_UNAVAILABLE'), { code: 'PROVIDER_CATALOG_UNAVAILABLE' });
|
|
176
|
+
const list = await catalog.providerModels(provider);
|
|
177
|
+
writeValue(ctx, list.rows, modelListText(list.rows, list.notice));
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
180
|
+
if (typeof catalog?.listModels !== 'function')
|
|
181
|
+
throw Object.assign(new Error('PROVIDER_CATALOG_UNAVAILABLE'), { code: 'PROVIDER_CATALOG_UNAVAILABLE' });
|
|
182
|
+
const rows = await catalog.listModels();
|
|
183
|
+
writeValue(ctx, rows, modelListText(rows));
|
|
184
|
+
return 0;
|
|
185
|
+
} throw new Error(`Unsupported model operation: ${op}`); }
|