talos-code 0.0.1 → 0.2.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/CHANGELOG.md +87 -0
- package/LICENSE +661 -0
- package/README.md +116 -3
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/archive/zip.js +73 -0
- package/dist/args.js +100 -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 +244 -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 +46 -0
- package/dist/i18n/en/common.js +9 -0
- package/dist/i18n/en/credentials.js +92 -0
- package/dist/i18n/en/errors.js +267 -0
- package/dist/i18n/en/firstrun.js +101 -0
- package/dist/i18n/en/providers.js +37 -0
- package/dist/i18n/en/screen.js +774 -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 +331 -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/openrouter-login.js +172 -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-archive.js +69 -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 +265 -0
- package/dist/runtime/talos-composition.js +1011 -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/agent-view.js +51 -0
- package/dist/tui/app.js +4098 -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 +560 -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 +38 -0
- package/dist/tui/components/terminal-shell.js +214 -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/descendant-approvals.js +104 -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 +148 -0
- package/dist/tui/line-diff.js +57 -0
- package/dist/tui/live-activity.js +56 -0
- package/dist/tui/metrics.js +123 -0
- package/dist/tui/onboarding.js +36 -0
- package/dist/tui/overlays/agent-tree.js +43 -0
- package/dist/tui/overlays/approval-dialog.js +642 -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 +77 -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 +171 -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 +289 -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 +791 -0
- package/dist/tui/session-export.js +54 -0
- package/dist/tui/setup-wizard.js +46 -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 +82 -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 +27 -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 +64 -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,382 @@
|
|
|
1
|
+
import { mkdir, open, readFile, rename, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { projectId } from "../paths.js";
|
|
4
|
+
import { CREDENTIAL_STORES, CURRENT_UI_THEMES, DEFAULT_CONFIG, MODEL_WINDOW_MAX, MODEL_WINDOW_MIN, REASONING_EFFORTS, UI_BUSY_INPUTS, UI_EXIT_OUTPUTS, UI_REASONING_MODES } from "./types.js";
|
|
5
|
+
import { CURRENT_CONFIG_VERSION, migrateConfigValue } from "./migrations.js";
|
|
6
|
+
const ALLOWED = new Set(['version', 'model', 'reasoningEffort', 'permissionMode', 'outputFormat', 'updateChannel', 'providerRuntime', 'permissions', 'ui', 'extensions', 'credentials', 'models', 'catalog']); /* R5b: catalog.modelsDev */
|
|
7
|
+
/*
|
|
8
|
+
* R5a lane R (Q-R5-3): `models` holds a person's override of a model's window, keyed `provider:model` (a bare OpenRouter id
|
|
9
|
+
* has no prefix). Each value is an object with `contextWindow` and optionally `maxOutputTokens`, whole numbers between
|
|
10
|
+
* MODEL_WINDOW_MIN and MODEL_WINDOW_MAX, the output below the window. Anything else is refused by name: a window typed as
|
|
11
|
+
* "128k", a negative or a fraction would otherwise switch the Context Engine on a number nobody meant.
|
|
12
|
+
*/
|
|
13
|
+
const MODEL_KEY = /^[A-Za-z0-9][A-Za-z0-9._\-/:@+]{0,255}$/u;
|
|
14
|
+
function validModelTokens(value) { return Number.isSafeInteger(value) && Number(value) >= MODEL_WINDOW_MIN && Number(value) <= MODEL_WINDOW_MAX; }
|
|
15
|
+
function validateModels(models) {
|
|
16
|
+
if (!isObj(models))
|
|
17
|
+
throw new Error('CONFIG_INVALID_MODELS');
|
|
18
|
+
for (const [id, row] of Object.entries(models)) {
|
|
19
|
+
if (!MODEL_KEY.test(id) || !isObj(row) || Object.keys(row).some(key => key !== 'contextWindow' && key !== 'maxOutputTokens'))
|
|
20
|
+
throw new Error('CONFIG_INVALID_MODELS');
|
|
21
|
+
if (row.contextWindow !== undefined && !validModelTokens(row.contextWindow))
|
|
22
|
+
throw new Error('CONFIG_INVALID_MODEL_WINDOW');
|
|
23
|
+
if (row.maxOutputTokens !== undefined && (!Number.isSafeInteger(row.maxOutputTokens) || Number(row.maxOutputTokens) < 1 || Number(row.maxOutputTokens) > MODEL_WINDOW_MAX))
|
|
24
|
+
throw new Error('CONFIG_INVALID_MODEL_WINDOW');
|
|
25
|
+
if (row.contextWindow !== undefined && row.maxOutputTokens !== undefined && Number(row.maxOutputTokens) >= Number(row.contextWindow))
|
|
26
|
+
throw new Error('CONFIG_INVALID_MODEL_WINDOW');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const SECRET_KEY = /^(?:api[-_]?key|token|secret|password|authorization)$/iu;
|
|
30
|
+
function isObj(x) { return !!x && typeof x === 'object' && !Array.isArray(x); }
|
|
31
|
+
export function assertNoSecrets(value, path = 'config') {
|
|
32
|
+
if (Array.isArray(value)) {
|
|
33
|
+
value.forEach((v, i) => assertNoSecrets(v, `${path}[${i}]`));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (!isObj(value))
|
|
37
|
+
return;
|
|
38
|
+
for (const [k, v] of Object.entries(value)) {
|
|
39
|
+
if (SECRET_KEY.test(k))
|
|
40
|
+
throw new Error(`SECRET_FIELD_FORBIDDEN:${path}.${k}`);
|
|
41
|
+
assertNoSecrets(v, `${path}.${k}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function validate(obj) {
|
|
45
|
+
if (!isObj(obj))
|
|
46
|
+
throw new Error('CONFIG_INVALID');
|
|
47
|
+
assertNoSecrets(obj);
|
|
48
|
+
for (const k of Object.keys(obj))
|
|
49
|
+
if (!ALLOWED.has(k))
|
|
50
|
+
throw new Error(`CONFIG_UNKNOWN_KEY:${k}`);
|
|
51
|
+
const out = structuredClone(obj);
|
|
52
|
+
if (out.version !== undefined && out.version !== CURRENT_CONFIG_VERSION)
|
|
53
|
+
throw new Error('CONFIG_VERSION_INVALID');
|
|
54
|
+
if (out.permissionMode && !['default', 'acceptEdits', 'plan', 'auto', 'dontAsk', 'bypassPermissions'].includes(out.permissionMode))
|
|
55
|
+
throw new Error('CONFIG_INVALID_PERMISSION_MODE');
|
|
56
|
+
if (out.outputFormat && !['text', 'json', 'stream-json'].includes(out.outputFormat))
|
|
57
|
+
throw new Error('CONFIG_INVALID_OUTPUT_FORMAT');
|
|
58
|
+
if (out.reasoningEffort !== undefined && !REASONING_EFFORTS.includes(out.reasoningEffort))
|
|
59
|
+
throw new Error('CONFIG_INVALID_REASONING_EFFORT');
|
|
60
|
+
/* R2 (gate E13): `credentials.store` is `system` or `memory`, nothing else; the key names the store, never holds a secret. */
|
|
61
|
+
const credentials = out.credentials;
|
|
62
|
+
if (credentials !== undefined) {
|
|
63
|
+
if (!isObj(credentials) || Object.keys(credentials).some(key => key !== 'store'))
|
|
64
|
+
throw new Error('CONFIG_INVALID_CREDENTIALS');
|
|
65
|
+
if (credentials.store !== undefined && !CREDENTIAL_STORES.includes(credentials.store))
|
|
66
|
+
throw new Error('CONFIG_INVALID_CREDENTIAL_STORE');
|
|
67
|
+
}
|
|
68
|
+
/* R5b: the models.dev switch (catalog.modelsDev, a boolean). */
|
|
69
|
+
const catalog = out.catalog;
|
|
70
|
+
if (catalog !== undefined && (!isObj(catalog) || Object.keys(catalog).some(key => key !== 'modelsDev') || (catalog.modelsDev !== undefined && typeof catalog.modelsDev !== 'boolean')))
|
|
71
|
+
throw new Error('CONFIG_INVALID_CATALOG');
|
|
72
|
+
const ui = out.ui;
|
|
73
|
+
if (ui !== undefined) {
|
|
74
|
+
if (!isObj(ui))
|
|
75
|
+
throw new Error('CONFIG_INVALID_UI');
|
|
76
|
+
if (ui.theme !== undefined && (!CURRENT_UI_THEMES.includes(ui.theme)))
|
|
77
|
+
throw new Error('CONFIG_INVALID_THEME');
|
|
78
|
+
if (ui.exitOutput !== undefined && !UI_EXIT_OUTPUTS.includes(ui.exitOutput))
|
|
79
|
+
throw new Error('CONFIG_INVALID_EXIT_OUTPUT');
|
|
80
|
+
/* R5a lane R (Q-R5-7, Q-R5-9): the busy-input mode and how reasoning is shown. */
|
|
81
|
+
if (ui.busyInput !== undefined && !UI_BUSY_INPUTS.includes(ui.busyInput))
|
|
82
|
+
throw new Error('CONFIG_INVALID_BUSY_INPUT');
|
|
83
|
+
if (ui.reasoning !== undefined && !UI_REASONING_MODES.includes(ui.reasoning))
|
|
84
|
+
throw new Error('CONFIG_INVALID_REASONING_DISPLAY');
|
|
85
|
+
/* R5a (owner 2026-09-24): the busy-input tip is shown once per install, then this is true. */
|
|
86
|
+
if (ui.busyTipSeen !== undefined && typeof ui.busyTipSeen !== 'boolean')
|
|
87
|
+
throw new Error('CONFIG_INVALID_UI');
|
|
88
|
+
/* R5b D5 (Q-R5-15): `ui.history.persist` turns the kept prompt history off. */
|
|
89
|
+
if (ui.history !== undefined && (!isObj(ui.history) || Object.keys(ui.history).some(key => key !== 'persist') || (ui.history.persist !== undefined && typeof ui.history.persist !== 'boolean')))
|
|
90
|
+
throw new Error('CONFIG_INVALID_UI');
|
|
91
|
+
const keymap = ui.keymap;
|
|
92
|
+
if (keymap !== undefined) {
|
|
93
|
+
if (!isObj(keymap))
|
|
94
|
+
throw new Error('CONFIG_INVALID_KEYMAP');
|
|
95
|
+
for (const [id, value] of Object.entries(keymap)) {
|
|
96
|
+
if (!id || !Array.isArray(value) || !value.every(chord => typeof chord === 'string'))
|
|
97
|
+
throw new Error('CONFIG_INVALID_KEYMAP');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const models = out.models;
|
|
102
|
+
if (models !== undefined)
|
|
103
|
+
validateModels(models);
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
async function readJson(file) { try {
|
|
107
|
+
const parsed = JSON.parse(await readFile(file, 'utf8'));
|
|
108
|
+
return validate(migrateConfigValue(parsed).value);
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
if (e?.code === 'ENOENT')
|
|
112
|
+
return {};
|
|
113
|
+
throw e;
|
|
114
|
+
} }
|
|
115
|
+
/*
|
|
116
|
+
* ⛔ R0 (forensics B row 8, never green since `27d699a2b`): an origin is recorded for the key a layer sets AND for every path beneath
|
|
117
|
+
* it, after dropping what an earlier layer recorded beneath it. Before, a subtree that first appeared in a layer (the user's
|
|
118
|
+
* `ui.keymap`) recorded only its root, so `talos config origins` reported the user's own keymap overrides as `default`.
|
|
119
|
+
*/
|
|
120
|
+
function recordOrigin(origins, key, value, origin) {
|
|
121
|
+
for (const known of [...origins.keys()])
|
|
122
|
+
if (known.startsWith(`${key}.`))
|
|
123
|
+
origins.delete(known);
|
|
124
|
+
origins.set(key, origin);
|
|
125
|
+
if (isObj(value))
|
|
126
|
+
for (const [k, v] of Object.entries(value))
|
|
127
|
+
recordOrigin(origins, `${key}.${k}`, v, origin);
|
|
128
|
+
}
|
|
129
|
+
function deepMerge(base, overlay, origin, origins, prefix = '') {
|
|
130
|
+
const out = { ...base };
|
|
131
|
+
for (const [k, v] of Object.entries(overlay)) {
|
|
132
|
+
const key = prefix ? `${prefix}.${k}` : k;
|
|
133
|
+
if (isObj(v) && isObj(out[k]))
|
|
134
|
+
out[k] = deepMerge(out[k], v, origin, origins, key);
|
|
135
|
+
else {
|
|
136
|
+
out[k] = structuredClone(v);
|
|
137
|
+
recordOrigin(origins, key, v, origin);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
function files(paths, projectRoot) { return { user: join(paths.configRoot, 'config.json'), project: join(projectRoot, '.talos-cli', 'config.json'), projectUser: join(paths.projectOverridesRoot, projectId(projectRoot), 'config.json') }; }
|
|
143
|
+
const PERMISSION_LISTS = ['allow', 'ask', 'deny'];
|
|
144
|
+
/*
|
|
145
|
+
* B1 slice 23 — EVERY SCOPE ADDS PERMISSION RULES; NO SCOPE REMOVES ONE.
|
|
146
|
+
*
|
|
147
|
+
* Measured at 5e05de8e: a list or a `null` in a later file replaced the earlier value, so a project's
|
|
148
|
+
* `.talos-cli/config.json` setting `permissions.deny` (even to `[]`) switched off the person's own deny rules, a
|
|
149
|
+
* project-user file written before slice 21 (`deny: []`) switched off the project's, and `permissions: null` became
|
|
150
|
+
* three empty lists in main.ts. `cat secrets.txt`, denied in the user file, ran.
|
|
151
|
+
*
|
|
152
|
+
* Primary source, Claude Code documentation read through Context7 (`/websites/code_claude`) on 2026-09-17: "Arrays
|
|
153
|
+
* merge across layers; scalars at a higher layer override lower ones" (glossary, Settings layers); "If a tool is
|
|
154
|
+
* denied at any level, no other level can allow it" (permissions, Settings precedence).
|
|
155
|
+
*
|
|
156
|
+
* So `allow`, `ask` and `deny` are each the union of every scope's list, an entry listed twice is kept once, and
|
|
157
|
+
* each entry keeps every scope that lists it, for `talos config origins`. An absent or `null` value holds no rule and
|
|
158
|
+
* contributes nothing.
|
|
159
|
+
* ⛔ A MALFORMED value is not dropped: slice 21 refuses to build the engine on it, by name, because a skipped deny
|
|
160
|
+
* rule allows what it denied. It is handed on unchanged — a whole `permissions` that is not an object, or one list
|
|
161
|
+
* that is not a list, the highest-precedence one first — and the engine stops the start. No valid rule is allowed
|
|
162
|
+
* in its place.
|
|
163
|
+
* Entries are ordered highest precedence first (project-user, project, user): `locate` in security/persist.ts names
|
|
164
|
+
* the file of an unreadable entry by its index in the highest-precedence file that defines the list.
|
|
165
|
+
* Writing a scope (`writeConfigScope`, `replaceConfigScope`, the slice 21 writer) keeps its per-file semantics:
|
|
166
|
+
* only the reading side merges.
|
|
167
|
+
*/
|
|
168
|
+
function mergePermissions(layers, origins) {
|
|
169
|
+
const byPrecedence = [...layers].reverse();
|
|
170
|
+
const entries = { allow: new Map(), ask: new Map(), deny: new Map() };
|
|
171
|
+
const badList = {};
|
|
172
|
+
let badListRank = Infinity;
|
|
173
|
+
let whole = null;
|
|
174
|
+
for (const [rank, layer] of byPrecedence.entries()) {
|
|
175
|
+
const permissions = layer.value.permissions;
|
|
176
|
+
if (permissions === undefined || permissions === null)
|
|
177
|
+
continue;
|
|
178
|
+
if (!isObj(permissions)) {
|
|
179
|
+
whole ??= { value: permissions, origin: layer.origin, rank };
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
for (const list of PERMISSION_LISTS) {
|
|
183
|
+
const values = permissions[list];
|
|
184
|
+
if (values === undefined || values === null)
|
|
185
|
+
continue;
|
|
186
|
+
if (!Array.isArray(values)) {
|
|
187
|
+
if (!badList[list]) {
|
|
188
|
+
badList[list] = { value: values, origin: layer.origin };
|
|
189
|
+
badListRank = Math.min(badListRank, rank);
|
|
190
|
+
}
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
for (const value of values) {
|
|
194
|
+
const key = typeof value === 'string' ? `rule:${value}` : `json:${JSON.stringify(value)}`;
|
|
195
|
+
const known = entries[list].get(key);
|
|
196
|
+
if (!known)
|
|
197
|
+
entries[list].set(key, { value: structuredClone(value), origin: layer.origin, scopes: [layer.origin] });
|
|
198
|
+
else if (!known.scopes.includes(layer.origin))
|
|
199
|
+
known.scopes.push(layer.origin);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (whole && whole.rank < badListRank) {
|
|
204
|
+
recordOrigin(origins, 'permissions', whole.value, whole.origin);
|
|
205
|
+
return structuredClone(whole.value);
|
|
206
|
+
}
|
|
207
|
+
// Keys other than the three lists are not rules; they keep the scalar precedence of every other key.
|
|
208
|
+
let merged = {};
|
|
209
|
+
for (const layer of layers) {
|
|
210
|
+
const permissions = layer.value.permissions;
|
|
211
|
+
if (!isObj(permissions))
|
|
212
|
+
continue;
|
|
213
|
+
merged = deepMerge(merged, Object.fromEntries(Object.entries(permissions).filter(([key]) => !PERMISSION_LISTS.includes(key))), layer.origin, origins, 'permissions');
|
|
214
|
+
}
|
|
215
|
+
for (const list of PERMISSION_LISTS) {
|
|
216
|
+
const bad = badList[list];
|
|
217
|
+
if (bad) {
|
|
218
|
+
merged[list] = structuredClone(bad.value);
|
|
219
|
+
origins.set(`permissions.${list}`, bad.origin);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const kept = [...entries[list].values()];
|
|
223
|
+
merged[list] = kept.map(entry => structuredClone(entry.value));
|
|
224
|
+
origins.set(`permissions.${list}`, kept);
|
|
225
|
+
}
|
|
226
|
+
return merged;
|
|
227
|
+
}
|
|
228
|
+
/*
|
|
229
|
+
* B1 slice 23 — A REPOSITORY CANNOT SWITCH THE PERMISSION BOUNDARY OFF.
|
|
230
|
+
*
|
|
231
|
+
* Measured at 5e05de8e: `permissionMode: "bypassPermissions"` in a project's `.talos-cli/config.json` was the mode
|
|
232
|
+
* of every run started in it. That file is part of the repository; the project-user file lives in the person's
|
|
233
|
+
* profile and keeps its effect, as do the user file and `--permission-mode`.
|
|
234
|
+
*
|
|
235
|
+
* Primary source, Claude Code documentation via Context7 (`/websites/code_claude`), 2026-09-17, settings reference,
|
|
236
|
+
* `permissions.defaultMode`: "`auto` and `bypassPermissions` don't take effect from project or local settings … Before
|
|
237
|
+
* v2.1.257, `bypassPermissions` took effect from any file." Measured in the Claude Code 2.1.274 binary on this host on
|
|
238
|
+
* the same day: it writes `settings defaultMode "bypassPermissions" ignored — only policy/user/flag settings may grant
|
|
239
|
+
* bypass mode (projectSettings and localSettings are repo-controllable)` to its debug log only. TALOS says it where
|
|
240
|
+
* the person looks (contract of slice 23): main.ts prints it before the screen starts and puts it in a
|
|
241
|
+
* non-interactive result. The wording follows Claude Code's own "Ignoring 2 permissions.allow entries from
|
|
242
|
+
* .claude/settings.local.json: …" (errors reference, same Context7 read).
|
|
243
|
+
*
|
|
244
|
+
* The value is dropped from the project layer, so the run uses the next scope's value. It is reported only when it
|
|
245
|
+
* would otherwise have been the effective one: a project-user value or the command line decides regardless.
|
|
246
|
+
*/
|
|
247
|
+
const REPOSITORY_CANNOT_SELECT = new Set(['bypassPermissions', 'auto']);
|
|
248
|
+
function describeOrigin(origin, userFile) { return origin === 'user' ? `the user configuration ${userFile}` : 'the built-in default'; }
|
|
249
|
+
export async function loadEffectiveConfig({ paths, projectRoot, cli = {} }) {
|
|
250
|
+
const f = files(paths, projectRoot);
|
|
251
|
+
const origins = new Map();
|
|
252
|
+
const layers = [
|
|
253
|
+
{ origin: 'default', file: null, value: structuredClone(DEFAULT_CONFIG) },
|
|
254
|
+
{ origin: 'user', file: f.user, value: await readJson(f.user) },
|
|
255
|
+
{ origin: 'project', file: f.project, value: await readJson(f.project) },
|
|
256
|
+
{ origin: 'project-user', file: f.projectUser, value: await readJson(f.projectUser) },
|
|
257
|
+
{ origin: 'cli', file: null, value: validate(cli) },
|
|
258
|
+
];
|
|
259
|
+
let held = null;
|
|
260
|
+
let projectReasoning = null;
|
|
261
|
+
let projectKeymap = null;
|
|
262
|
+
let projectStore = null;
|
|
263
|
+
let projectModels = null;
|
|
264
|
+
let value = {};
|
|
265
|
+
for (const layer of layers) {
|
|
266
|
+
const rest = { ...layer.value };
|
|
267
|
+
// The defaults keep `permissions` so the key keeps its place; its value is replaced by the union below.
|
|
268
|
+
if (layer.origin !== 'default')
|
|
269
|
+
delete rest.permissions;
|
|
270
|
+
if (layer.origin === 'project' && typeof rest.permissionMode === 'string' && REPOSITORY_CANNOT_SELECT.has(rest.permissionMode)) {
|
|
271
|
+
held = { value: rest.permissionMode, file: layer.file };
|
|
272
|
+
delete rest.permissionMode;
|
|
273
|
+
}
|
|
274
|
+
if (layer.origin === 'project' && typeof rest.reasoningEffort === 'string') {
|
|
275
|
+
projectReasoning = { value: rest.reasoningEffort, file: layer.file };
|
|
276
|
+
delete rest.reasoningEffort;
|
|
277
|
+
}
|
|
278
|
+
if (layer.origin === 'project' && isObj(rest.ui) && Object.hasOwn(rest.ui, 'keymap')) {
|
|
279
|
+
const ui = { ...rest.ui };
|
|
280
|
+
projectKeymap = { value: structuredClone(ui.keymap), file: layer.file };
|
|
281
|
+
delete ui.keymap;
|
|
282
|
+
if (Object.keys(ui).length)
|
|
283
|
+
rest.ui = ui;
|
|
284
|
+
else
|
|
285
|
+
delete rest.ui;
|
|
286
|
+
}
|
|
287
|
+
/* R2 owner decision 2026-09-24 (gate E13): a repository cannot choose where TALOS keeps the person's keys; the user
|
|
288
|
+
file, the project-user file (in the person's profile) and TALOS_CREDENTIAL_STORE can. Same shape as ui.keymap. */
|
|
289
|
+
if (layer.origin === 'project' && isObj(rest.credentials) && Object.hasOwn(rest.credentials, 'store')) {
|
|
290
|
+
const credentials = { ...rest.credentials };
|
|
291
|
+
projectStore = { value: credentials.store, file: layer.file };
|
|
292
|
+
delete credentials.store;
|
|
293
|
+
if (Object.keys(credentials).length)
|
|
294
|
+
rest.credentials = credentials;
|
|
295
|
+
else
|
|
296
|
+
delete rest.credentials;
|
|
297
|
+
}
|
|
298
|
+
/* R5a lane R (Q-R5-3): a model's window decides when the Context Engine compacts and how much every request may carry, so,
|
|
299
|
+
like reasoningEffort, a repository cannot set it for the person; the user and project-user files can. */
|
|
300
|
+
if (layer.origin === 'project' && Object.hasOwn(rest, 'models')) {
|
|
301
|
+
projectModels = { value: structuredClone(rest.models), file: layer.file };
|
|
302
|
+
delete rest.models;
|
|
303
|
+
}
|
|
304
|
+
value = deepMerge(value, rest, layer.origin, origins);
|
|
305
|
+
}
|
|
306
|
+
value.permissions = mergePermissions(layers, origins);
|
|
307
|
+
const ignored = [];
|
|
308
|
+
if (projectKeymap)
|
|
309
|
+
ignored.push({
|
|
310
|
+
code: 'PROJECT_KEYMAP_IGNORED', path: 'ui.keymap', value: projectKeymap.value, origin: 'project', file: projectKeymap.file,
|
|
311
|
+
message: `Ignoring ui.keymap from ${projectKeymap.file}: repository configuration cannot remap keyboard controls; use user or project-user configuration.`,
|
|
312
|
+
});
|
|
313
|
+
if (projectStore)
|
|
314
|
+
ignored.push({
|
|
315
|
+
code: 'PROJECT_CREDENTIAL_STORE_IGNORED', path: 'credentials.store', value: projectStore.value, origin: 'project', file: projectStore.file,
|
|
316
|
+
message: `Ignoring credentials.store from ${projectStore.file}: repository configuration cannot choose where TALOS keeps your keys; use user or project-user configuration, or TALOS_CREDENTIAL_STORE.`,
|
|
317
|
+
});
|
|
318
|
+
if (projectModels)
|
|
319
|
+
ignored.push({
|
|
320
|
+
code: 'PROJECT_MODELS_IGNORED', path: 'models', value: projectModels.value, origin: 'project', file: projectModels.file,
|
|
321
|
+
message: `Ignoring models from ${projectModels.file}: repository configuration cannot set a model's context window; use user or project-user configuration.`,
|
|
322
|
+
});
|
|
323
|
+
if (projectReasoning)
|
|
324
|
+
ignored.push({
|
|
325
|
+
code: 'PROJECT_REASONING_EFFORT_IGNORED', path: 'reasoningEffort', value: projectReasoning.value, origin: 'project', file: projectReasoning.file,
|
|
326
|
+
message: `Ignoring reasoningEffort "${projectReasoning.value}" from ${projectReasoning.file}: repository configuration cannot force reasoning spend; use user, project-user, or an explicit session choice.`,
|
|
327
|
+
});
|
|
328
|
+
const modeOrigin = origins.get('permissionMode');
|
|
329
|
+
if (held && (modeOrigin === 'default' || modeOrigin === 'user')) {
|
|
330
|
+
const used = typeof value.permissionMode === 'string' ? value.permissionMode : 'default';
|
|
331
|
+
ignored.push({ code: 'PERMISSION_MODE_IGNORED', path: 'permissionMode', value: held.value, origin: 'project', file: held.file,
|
|
332
|
+
message: `Ignoring permissionMode "${held.value}" from ${held.file}: a project file is part of the repository, so it cannot select "bypassPermissions" or "auto" (the user configuration, the project-user configuration or --permission-mode can). This run uses "${used}" from ${describeOrigin(modeOrigin, f.user)}.` });
|
|
333
|
+
}
|
|
334
|
+
return { value: value, origins, ignored };
|
|
335
|
+
}
|
|
336
|
+
async function atomicWriteText(file, text) { await mkdir(dirname(file), { recursive: true, mode: 0o700 }); const tmp = `${file}.tmp-${process.pid}-${Date.now()}`; await writeFile(tmp, text, { encoding: 'utf8', mode: 0o600 }); const h = await open(tmp, 'r+'); try {
|
|
337
|
+
await h.sync();
|
|
338
|
+
}
|
|
339
|
+
finally {
|
|
340
|
+
await h.close();
|
|
341
|
+
} await rename(tmp, file); }
|
|
342
|
+
async function atomicWrite(file, value) { const current = validate(migrateConfigValue(value).value); await atomicWriteText(file, `${JSON.stringify(current, null, 2)}\n`); }
|
|
343
|
+
export async function writeConfigScope({ paths, projectRoot, scope, patch }) { assertNoSecrets(patch); const f = files(paths, projectRoot); const file = scope === 'user' ? f.user : scope === 'project' ? f.project : f.projectUser; const current = await readJson(file); const origins = new Map(); const merged = deepMerge(current, validate(patch), scope === 'project-user' ? 'project-user' : scope, origins); await atomicWrite(file, merged); }
|
|
344
|
+
export async function readConfigScope({ paths, projectRoot, scope }) { const f = files(paths, projectRoot); return readJson(scope === 'user' ? f.user : scope === 'project' ? f.project : f.projectUser); }
|
|
345
|
+
export async function replaceConfigScope({ paths, projectRoot, scope, value }) { const f = files(paths, projectRoot); const file = scope === 'user' ? f.user : scope === 'project' ? f.project : f.projectUser; await atomicWrite(file, value); }
|
|
346
|
+
function scopeFile(paths, projectRoot, scope) { const f = files(paths, projectRoot); return scope === 'user' ? f.user : scope === 'project' ? f.project : f.projectUser; }
|
|
347
|
+
export async function migrateConfigScope({ paths, projectRoot, scope }) {
|
|
348
|
+
const file = scopeFile(paths, projectRoot, scope);
|
|
349
|
+
let raw;
|
|
350
|
+
try {
|
|
351
|
+
raw = await readFile(file, 'utf8');
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
if (error?.code === 'ENOENT')
|
|
355
|
+
raw = '{}\n';
|
|
356
|
+
else
|
|
357
|
+
throw error;
|
|
358
|
+
}
|
|
359
|
+
const result = migrateConfigValue(JSON.parse(raw));
|
|
360
|
+
const backupFile = `${file}.migration-backup`;
|
|
361
|
+
if (result.changed) {
|
|
362
|
+
await atomicWriteText(backupFile, raw);
|
|
363
|
+
await atomicWrite(file, result.value);
|
|
364
|
+
}
|
|
365
|
+
return { ...result, file, backupFile: result.changed ? backupFile : null };
|
|
366
|
+
}
|
|
367
|
+
export async function rollbackConfigMigration({ paths, projectRoot, scope }) {
|
|
368
|
+
const file = scopeFile(paths, projectRoot, scope);
|
|
369
|
+
const backupFile = `${file}.migration-backup`;
|
|
370
|
+
let raw;
|
|
371
|
+
try {
|
|
372
|
+
raw = await readFile(backupFile, 'utf8');
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
if (error?.code === 'ENOENT')
|
|
376
|
+
throw Object.assign(new Error('CONFIG_MIGRATION_BACKUP_NOT_FOUND'), { code: 'CONFIG_MIGRATION_BACKUP_NOT_FOUND' });
|
|
377
|
+
throw error;
|
|
378
|
+
}
|
|
379
|
+
JSON.parse(raw);
|
|
380
|
+
await atomicWriteText(file, raw);
|
|
381
|
+
return { restored: true, file, backupFile };
|
|
382
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const CURRENT_CONFIG_VERSION = 2;
|
|
2
|
+
const V1_THEMES = new Set(['calm', 'ember', 'coral', 'rose', 'violet', 'indigo', 'azure', 'cyan', 'teal', 'jade', 'mono']);
|
|
3
|
+
const LEGACY_THEME = new Map([['auto', 'calm'], ['dark', 'calm'], ['light', 'calm'], ['mono', 'mono']]);
|
|
4
|
+
/* R5b D4 (owner decision Q-R5-14): v2 themes are the product's ten dark themes; each CLI-only accent of v1 moves to the
|
|
5
|
+
nearest official one by hue (coral #FB7185 -> signal #ff6f61, rose #F472B6 -> ember #ff5c62, indigo #818CF8 -> violet
|
|
6
|
+
#b794f6, azure #60A5FA and cyan #22D3EE -> telemetry #6ad4d4, teal #2DD4BF -> aurora #42e7c7, jade #34D399 -> terminal
|
|
7
|
+
#63f08e); calm, ember, violet and mono keep their ids. */
|
|
8
|
+
const V1_TO_V2_THEME = new Map([['coral', 'signal'], ['rose', 'ember'], ['indigo', 'violet'], ['azure', 'telemetry'], ['cyan', 'telemetry'], ['teal', 'aurora'], ['jade', 'terminal']]);
|
|
9
|
+
const V2_THEMES = new Set(['calm', 'forge', 'terminal', 'aurora', 'ember', 'atlas', 'noir', 'signal', 'violet', 'telemetry', 'mono']);
|
|
10
|
+
function objectValue(value) {
|
|
11
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
12
|
+
}
|
|
13
|
+
function fail(code) { throw Object.assign(new Error(code), { code }); }
|
|
14
|
+
export function migrateConfigValue(input) {
|
|
15
|
+
if (input === null || typeof input !== 'object' || Array.isArray(input))
|
|
16
|
+
fail('CONFIG_INVALID');
|
|
17
|
+
const source = structuredClone(input);
|
|
18
|
+
const rawVersion = source.version;
|
|
19
|
+
const fromVersion = rawVersion === undefined ? 0 : typeof rawVersion === 'number' && Number.isInteger(rawVersion) ? rawVersion : fail('CONFIG_VERSION_INVALID');
|
|
20
|
+
if (fromVersion > CURRENT_CONFIG_VERSION)
|
|
21
|
+
fail('CONFIG_VERSION_UNSUPPORTED');
|
|
22
|
+
if (fromVersion < 0)
|
|
23
|
+
fail('CONFIG_VERSION_INVALID');
|
|
24
|
+
if (fromVersion === CURRENT_CONFIG_VERSION)
|
|
25
|
+
return { value: source, fromVersion, toVersion: CURRENT_CONFIG_VERSION, changed: false, steps: [] };
|
|
26
|
+
const next = structuredClone(source);
|
|
27
|
+
const steps = [];
|
|
28
|
+
const withTheme = (change) => {
|
|
29
|
+
const ui = objectValue(next.ui);
|
|
30
|
+
if (next.ui !== undefined || Object.keys(ui).length) {
|
|
31
|
+
const raw = ui.theme;
|
|
32
|
+
if (typeof raw === 'string') {
|
|
33
|
+
const migrated = change(raw);
|
|
34
|
+
if (migrated)
|
|
35
|
+
ui.theme = migrated;
|
|
36
|
+
}
|
|
37
|
+
next.ui = ui;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
if (fromVersion === 0) {
|
|
41
|
+
withTheme(raw => LEGACY_THEME.get(raw) ?? (V1_THEMES.has(raw) ? raw : null));
|
|
42
|
+
steps.push('v0-to-v1');
|
|
43
|
+
}
|
|
44
|
+
withTheme(raw => V1_TO_V2_THEME.get(raw) ?? (V2_THEMES.has(raw) ? raw : null));
|
|
45
|
+
steps.push('v1-to-v2');
|
|
46
|
+
next.version = CURRENT_CONFIG_VERSION;
|
|
47
|
+
return { value: next, fromVersion, toVersion: CURRENT_CONFIG_VERSION, changed: true, steps };
|
|
48
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const CURRENT_UI_THEMES = Object.freeze(['calm', 'forge', 'terminal', 'aurora', 'ember', 'atlas', 'noir', 'signal', 'violet', 'telemetry', 'mono']);
|
|
2
|
+
export const UI_EXIT_OUTPUTS = Object.freeze(['transcript', 'resume']);
|
|
3
|
+
export const CREDENTIAL_STORES = Object.freeze(['system', 'memory']);
|
|
4
|
+
/* R5a lane R: the values config/load.ts accepts, and the owner's defaults (Q-R5-7 queue, Q-R5-9 collapsed). */
|
|
5
|
+
export const UI_BUSY_INPUTS = Object.freeze(['queue', 'steer', 'interrupt']);
|
|
6
|
+
export const UI_REASONING_MODES = Object.freeze(['collapsed', 'visible']);
|
|
7
|
+
/** R5a lane R (Q-R5-3): the bounds of a person's model override; outside them the value is refused by name, never clamped. */
|
|
8
|
+
export const MODEL_WINDOW_MIN = 1_024;
|
|
9
|
+
export const MODEL_WINDOW_MAX = 10_000_000;
|
|
10
|
+
export const REASONING_EFFORTS = Object.freeze(['low', 'medium', 'high', 'xhigh', 'max']);
|
|
11
|
+
export const DEFAULT_CONFIG = { version: 2, permissionMode: 'default', outputFormat: 'text', updateChannel: 'stable', permissions: { allow: [], ask: [], deny: [] }, ui: { theme: 'forge', markdown: true, exitOutput: 'transcript', busyInput: 'queue', reasoning: 'collapsed' }, extensions: { enabled: [] } };
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync } from 'node:fs';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { createRedactor, secretValuesFromEnvironment } from "./redact.js";
|
|
6
|
+
import { CLI_VERSION } from "../version.js";
|
|
7
|
+
const MAX_FILE_BYTES = 8 * 1024 * 1024;
|
|
8
|
+
const MAX_STRING = 16 * 1024;
|
|
9
|
+
const MAX_ARRAY = 128;
|
|
10
|
+
const MAX_KEYS = 128;
|
|
11
|
+
let state = null;
|
|
12
|
+
let hooksInstalled = false;
|
|
13
|
+
function sha256(value) { return createHash('sha256').update(value).digest('hex'); }
|
|
14
|
+
export function developmentTextEvidence(value) { const text = String(value ?? ''); return { length: text.length, bytes: Buffer.byteLength(text, 'utf8'), sha256: sha256(text) }; }
|
|
15
|
+
function truncate(value) { const text = typeof value === 'string' ? value : String(value); if (text.length <= MAX_STRING)
|
|
16
|
+
return text; return `${text.slice(0, MAX_STRING)}…[truncated:${text.length - MAX_STRING}]`; }
|
|
17
|
+
/*
|
|
18
|
+
* ⛔ R0 (forensics B row 4): logging is TOTAL — it never throws, whatever it is handed. `50601493c` put
|
|
19
|
+
* `developmentLogError` first in `main`'s catch; a thrown value with a throwing getter made `Object.entries` throw there, so the
|
|
20
|
+
* catch itself threw and no error envelope was written. Every read of a foreign value goes through `readField`, and a value that
|
|
21
|
+
* cannot be read at all is logged as `[UNREADABLE]` instead of breaking the caller.
|
|
22
|
+
*/
|
|
23
|
+
const UNREADABLE = '[UNREADABLE]';
|
|
24
|
+
function readField(value, key) { try {
|
|
25
|
+
return value[key];
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return UNREADABLE;
|
|
29
|
+
} }
|
|
30
|
+
function safeValue(value, depth = 0, seen = new WeakSet()) {
|
|
31
|
+
try {
|
|
32
|
+
if (depth > 6)
|
|
33
|
+
return '[DEPTH_LIMIT]';
|
|
34
|
+
if (value === null || typeof value === 'number' || typeof value === 'boolean')
|
|
35
|
+
return value;
|
|
36
|
+
if (typeof value === 'bigint')
|
|
37
|
+
return value.toString();
|
|
38
|
+
if (typeof value === 'string')
|
|
39
|
+
return truncate(value);
|
|
40
|
+
if (typeof value === 'function')
|
|
41
|
+
return `[Function:${readField(value, 'name') || 'anonymous'}]`;
|
|
42
|
+
if (typeof value !== 'object')
|
|
43
|
+
return String(value);
|
|
44
|
+
if (seen.has(value))
|
|
45
|
+
return '[CIRCULAR]';
|
|
46
|
+
seen.add(value);
|
|
47
|
+
if (value instanceof Error)
|
|
48
|
+
return serializeError(value, depth + 1, seen);
|
|
49
|
+
if (Array.isArray(value))
|
|
50
|
+
return value.slice(0, MAX_ARRAY).map(item => safeValue(item, depth + 1, seen));
|
|
51
|
+
const out = {};
|
|
52
|
+
for (const key of Object.keys(value).slice(0, MAX_KEYS)) {
|
|
53
|
+
const item = readField(value, key);
|
|
54
|
+
if (/^(?:prompt|text|testo|consegna|consegnaCorta|content|delta|messages|input|output|command|comando)$/iu.test(key) && typeof item === 'string')
|
|
55
|
+
out[key + 'Evidence'] = developmentTextEvidence(item);
|
|
56
|
+
else
|
|
57
|
+
out[key] = safeValue(item, depth + 1, seen);
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return UNREADABLE;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function serializeError(error, depth = 0, seen = new WeakSet()) {
|
|
66
|
+
try {
|
|
67
|
+
if (!(error instanceof Error))
|
|
68
|
+
return { value: safeValue(error, depth + 1, seen) };
|
|
69
|
+
const name = readField(error, 'name');
|
|
70
|
+
if (seen.has(error))
|
|
71
|
+
return { name, message: '[CIRCULAR_ERROR]' };
|
|
72
|
+
seen.add(error);
|
|
73
|
+
const code = readField(error, 'code'), retryable = readField(error, 'retryable'), details = readField(error, 'details'), cause = readField(error, 'cause');
|
|
74
|
+
return { name, message: truncate(readField(error, 'message')), stack: truncate(readField(error, 'stack') ?? ''), ...(typeof code === 'string' ? { code } : {}), ...(typeof retryable === 'boolean' ? { retryable } : {}), ...(details !== undefined ? { details: safeValue(details, depth + 1, seen) } : {}), ...(cause !== undefined ? { cause: serializeError(cause, depth + 1, seen) } : {}) };
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return { value: UNREADABLE };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function dependencySnapshot() {
|
|
81
|
+
try {
|
|
82
|
+
const root = fileURLToPath(new URL('../../', import.meta.url));
|
|
83
|
+
const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
|
|
84
|
+
const lock = JSON.parse(readFileSync(join(root, 'package-lock.json'), 'utf8'));
|
|
85
|
+
const packages = lock?.packages ?? {};
|
|
86
|
+
const version = (name) => packages[`node_modules/${name}`]?.version ?? pkg.dependencies?.[name] ?? pkg.devDependencies?.[name] ?? null;
|
|
87
|
+
return { packageRoot: root, packageVersion: pkg.version ?? CLI_VERSION, typescript: version('typescript'), react: version('react'), ink: version('ink'), mxc: version('@microsoft/mxc-sdk') };
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
return { unavailable: true, error: error instanceof Error ? error.message : String(error) };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function environmentSnapshot() {
|
|
94
|
+
const names = ['TERM', 'COLORTERM', 'CI', 'GITHUB_ACTIONS', 'WT_SESSION', 'TERM_PROGRAM', 'COMSPEC', 'PSModulePath', 'TALOS_CLI_INSPECTION_SNAPSHOT', 'TALOS_CLI_DEV_LOG_DIR', 'TALOS_REDUCED_MOTION'];
|
|
95
|
+
return Object.fromEntries(names.filter(name => process.env[name] !== undefined).map(name => [name, process.env[name]]));
|
|
96
|
+
}
|
|
97
|
+
function nextPath(s) { return join(s.dir, `${s.base}${s.part === 1 ? '' : `.part-${s.part}`}.jsonl`); }
|
|
98
|
+
function rotateIfNeeded(s) { try {
|
|
99
|
+
if (existsSync(s.path) && statSync(s.path).size >= MAX_FILE_BYTES) {
|
|
100
|
+
s.part += 1;
|
|
101
|
+
s.path = nextPath(s);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch { /* best effort */ } }
|
|
105
|
+
function write(record) { const s = state; if (!s || s.closed)
|
|
106
|
+
return; try {
|
|
107
|
+
rotateIfNeeded(s);
|
|
108
|
+
const redacted = s.redactor.value(safeValue(record));
|
|
109
|
+
appendFileSync(s.path, JSON.stringify(redacted) + '\n', { encoding: 'utf8', mode: 0o600 });
|
|
110
|
+
}
|
|
111
|
+
catch { /* logging must never break TALOS */ } }
|
|
112
|
+
function installProcessHooks() {
|
|
113
|
+
if (hooksInstalled)
|
|
114
|
+
return;
|
|
115
|
+
hooksInstalled = true;
|
|
116
|
+
process.on('warning', warning => developmentLog('process.warning', { error: serializeError(warning) }, 'warning', 'process'));
|
|
117
|
+
process.on('unhandledRejection', reason => developmentLog('process.unhandledRejection', { error: serializeError(reason) }, 'error', 'process'));
|
|
118
|
+
process.on('uncaughtExceptionMonitor', error => developmentLog('process.uncaughtExceptionMonitor', { error: serializeError(error) }, 'fatal', 'process'));
|
|
119
|
+
}
|
|
120
|
+
export function initializeDevelopmentLogging(input = {}) {
|
|
121
|
+
if (state && !state.closed) {
|
|
122
|
+
developmentLog('logger.context_update', { projectRoot: input.projectRoot, paths: input.paths, argv: input.argv?.map(developmentTextEvidence), extra: input.extra }, 'debug', input.component ?? state.component);
|
|
123
|
+
return state.path;
|
|
124
|
+
}
|
|
125
|
+
const dir = resolve(input.logDir ?? process.env.TALOS_CLI_DEV_LOG_DIR ?? (input.paths ? join(input.paths.logsRoot, 'development') : ''));
|
|
126
|
+
if (!dir || dir === resolve('.'))
|
|
127
|
+
return null;
|
|
128
|
+
try {
|
|
129
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const started = performance.now(), correlationId = randomUUID(), stamp = new Date().toISOString().replace(/[:.]/gu, '-');
|
|
135
|
+
const s = { dir, base: `talos-dev-${stamp}-${process.pid}-${correlationId.slice(0, 8)}`, part: 1, path: '', started, correlationId, redactor: createRedactor(secretValuesFromEnvironment(process.env)), component: input.component ?? 'cli', closed: false };
|
|
136
|
+
s.path = nextPath(s);
|
|
137
|
+
state = s;
|
|
138
|
+
installProcessHooks();
|
|
139
|
+
developmentLog('process.start', { cliVersion: CLI_VERSION, node: process.version, versions: process.versions, platform: process.platform, arch: process.arch, pid: process.pid, ppid: process.ppid, cwd: process.cwd(), execPath: process.execPath, argv: (input.argv ?? process.argv.slice(2)).map(developmentTextEvidence), projectRoot: input.projectRoot ?? null, terminal: { stdinTTY: Boolean(process.stdin.isTTY), stdoutTTY: Boolean(process.stdout.isTTY), columns: process.stdout.columns ?? null, rows: process.stdout.rows ?? null }, paths: input.paths ?? null, environment: environmentSnapshot(), dependencies: dependencySnapshot(), extra: input.extra ?? null }, 'info', 'process');
|
|
140
|
+
return s.path;
|
|
141
|
+
}
|
|
142
|
+
export function developmentLog(event, data = {}, severity = 'info', component) { const s = state; if (!s || s.closed)
|
|
143
|
+
return; try {
|
|
144
|
+
write({ schema: 'talos.cli.development-log.v1', ts: new Date().toISOString(), elapsedMs: Math.round((performance.now() - s.started) * 1000) / 1000, pid: process.pid, correlationId: s.correlationId, severity, component: component ?? s.component, event, data });
|
|
145
|
+
}
|
|
146
|
+
catch { /* logging must never break TALOS */ } }
|
|
147
|
+
export function developmentLogError(event, error, data = {}, component) { if (!state || state.closed)
|
|
148
|
+
return; try {
|
|
149
|
+
developmentLog(event, { ...data, error: serializeError(error) }, 'error', component);
|
|
150
|
+
}
|
|
151
|
+
catch { /* logging must never break TALOS */ } }
|
|
152
|
+
export function developmentLogPath() { return state?.path ?? null; }
|
|
153
|
+
export function closeDevelopmentLogging(extra = {}) { if (!state || state.closed)
|
|
154
|
+
return; developmentLog('process.stop', extra, 'info', 'process'); state.closed = true; }
|