@armadra/agent 0.5.1 → 0.6.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 +177 -304
- package/CHANGELOG.zh-CN.md +442 -0
- package/README.md +408 -364
- package/README.zh-CN.md +705 -0
- package/dist/agent/session-plan.js +2 -2
- package/dist/agent/session-state.d.ts +3 -0
- package/dist/agent/session-state.js +20 -0
- package/dist/agent/session-subagent.d.ts +15 -1
- package/dist/agent/session-subagent.js +26 -3
- package/dist/agent/session-telemetry.d.ts +16 -2
- package/dist/agent/session-telemetry.js +29 -7
- package/dist/agent/session-tools.js +5 -2
- package/dist/agent/session-trace-writer.d.ts +27 -0
- package/dist/agent/session-trace-writer.js +253 -0
- package/dist/agent/session.d.ts +2 -0
- package/dist/agent/session.js +12 -0
- package/dist/agent/subagent-direct.d.ts +20 -0
- package/dist/agent/subagent-direct.js +76 -0
- package/dist/agent/subagent-registry.d.ts +8 -1
- package/dist/agent/subagent-registry.js +28 -1
- package/dist/agent/system-prompt.d.ts +3 -1
- package/dist/agent/system-prompt.js +3 -0
- package/dist/agent/tool-runner.d.ts +2 -1
- package/dist/agent/tool-runner.js +5 -4
- package/dist/agent/types-w6.d.ts +39 -0
- package/dist/agent/types-w6.js +4 -0
- package/dist/agent/types.d.ts +5 -2
- package/dist/agents/external.js +1 -1
- package/dist/agents/task-record.d.ts +74 -1
- package/dist/agents/task-record.js +100 -0
- package/dist/ai/apis/cache-params.js +2 -1
- package/dist/ai/apis/chatgpt-backend.d.ts +52 -0
- package/dist/ai/apis/chatgpt-backend.js +224 -0
- package/dist/ai/apis/chatgpt-rate-limits.d.ts +26 -0
- package/dist/ai/apis/chatgpt-rate-limits.js +91 -0
- package/dist/ai/apis/openai-responses-request.d.ts +2 -1
- package/dist/ai/apis/openai-responses-request.js +8 -2
- package/dist/ai/apis/openai-responses.d.ts +2 -0
- package/dist/ai/apis/openai-responses.js +51 -16
- package/dist/ai/overflow.d.ts +1 -1
- package/dist/ai/overflow.js +10 -7
- package/dist/ai/providers/auth.d.ts +15 -2
- package/dist/ai/providers/auth.js +69 -2
- package/dist/ai/providers/builtin.js +27 -0
- package/dist/ai/providers/catalog-data.js +1 -0
- package/dist/ai/providers/enrich.js +2 -7
- package/dist/ai/providers/models-dev-cache.js +15 -19
- package/dist/ai/providers/models-dev-snapshot.js +8 -7
- package/dist/ai/providers/models-dev.js +5 -15
- package/dist/ai/providers/suggest.js +2 -16
- package/dist/ai/types.d.ts +27 -1
- package/dist/auth/chatgpt/backend-client.d.ts +26 -0
- package/dist/auth/chatgpt/backend-client.js +74 -0
- package/dist/auth/chatgpt/claims.d.ts +17 -0
- package/dist/auth/chatgpt/claims.js +46 -0
- package/dist/auth/chatgpt/cli.d.ts +33 -0
- package/dist/auth/chatgpt/cli.js +252 -0
- package/dist/auth/chatgpt/doctor.d.ts +17 -0
- package/dist/auth/chatgpt/doctor.js +36 -0
- package/dist/auth/chatgpt/host-id.d.ts +7 -0
- package/dist/auth/chatgpt/host-id.js +26 -0
- package/dist/auth/chatgpt/login.d.ts +30 -0
- package/dist/auth/chatgpt/login.js +130 -0
- package/dist/auth/chatgpt/presets.d.ts +66 -0
- package/dist/auth/chatgpt/presets.js +120 -0
- package/dist/auth/chatgpt/quota-text.d.ts +13 -0
- package/dist/auth/chatgpt/quota-text.js +36 -0
- package/dist/auth/oauth/browser.d.ts +12 -0
- package/dist/auth/oauth/browser.js +27 -0
- package/dist/auth/oauth/callback-server.d.ts +47 -0
- package/dist/auth/oauth/callback-server.js +147 -0
- package/dist/auth/oauth/flows.d.ts +62 -0
- package/dist/auth/oauth/flows.js +113 -0
- package/dist/auth/oauth/jwt.d.ts +24 -0
- package/dist/auth/oauth/jwt.js +81 -0
- package/dist/auth/oauth/live.d.ts +21 -0
- package/dist/auth/oauth/live.js +25 -0
- package/dist/auth/oauth/oidc.d.ts +26 -0
- package/dist/auth/oauth/oidc.js +72 -0
- package/dist/auth/oauth/pkce.d.ts +15 -0
- package/dist/auth/oauth/pkce.js +20 -0
- package/dist/auth/oauth/refresh.d.ts +35 -0
- package/dist/auth/oauth/refresh.js +129 -0
- package/dist/auth/oauth/token-client.d.ts +33 -0
- package/dist/auth/oauth/token-client.js +121 -0
- package/dist/auth/oauth/token-store.d.ts +23 -0
- package/dist/auth/oauth/token-store.js +125 -0
- package/dist/auth/testing/fake-oauth.d.ts +73 -0
- package/dist/auth/testing/fake-oauth.js +271 -0
- package/dist/auth/testing/refresh-child.d.ts +5 -0
- package/dist/auth/testing/refresh-child.js +16 -0
- package/dist/bundle/ama.cjs +25928 -11144
- package/dist/checkpoints/backend.js +6 -5
- package/dist/checkpoints/restore.js +3 -2
- package/dist/checkpoints/settings.js +2 -1
- package/dist/checkpoints/shadow-git.js +7 -6
- package/dist/checkpoints/shadow-restore.js +3 -2
- package/dist/checkpoints/tracker.js +4 -3
- package/dist/cli/args.d.ts +9 -2
- package/dist/cli/args.js +74 -27
- package/dist/cli/bootstrap.js +53 -31
- package/dist/cli/choice-prompt.js +8 -6
- package/dist/cli/codemode-notice.js +2 -2
- package/dist/cli/compose-agents.js +2 -1
- package/dist/cli/compose-extensions.js +2 -0
- package/dist/cli/compose-memory.d.ts +53 -0
- package/dist/cli/compose-memory.js +127 -0
- package/dist/cli/compose-providers.d.ts +5 -0
- package/dist/cli/compose-providers.js +32 -2
- package/dist/cli/compose-session.d.ts +5 -0
- package/dist/cli/compose-session.js +27 -13
- package/dist/cli/compose-store.d.ts +2 -0
- package/dist/cli/compose-store.js +9 -5
- package/dist/cli/compose.d.ts +2 -0
- package/dist/cli/compose.js +13 -5
- package/dist/cli/default-model.d.ts +0 -2
- package/dist/cli/default-model.js +5 -8
- package/dist/cli/deps.d.ts +5 -1
- package/dist/cli/exit-codes.d.ts +1 -1
- package/dist/cli/exit-codes.js +7 -16
- package/dist/cli/from-prompt.js +3 -2
- package/dist/cli/help-text.d.ts +2 -1
- package/dist/cli/help-text.js +5 -99
- package/dist/cli/main.d.ts +5 -0
- package/dist/cli/main.js +21 -2
- package/dist/cli/proxy.js +14 -9
- package/dist/cli/runtime.d.ts +5 -0
- package/dist/cli/startup-screen.js +20 -27
- package/dist/cli/startup-steps.js +6 -8
- package/dist/cli/subcommands/auth.d.ts +6 -3
- package/dist/cli/subcommands/auth.js +52 -22
- package/dist/cli/subcommands/config-set.d.ts +21 -0
- package/dist/cli/subcommands/config-set.js +137 -0
- package/dist/cli/subcommands/config.d.ts +2 -1
- package/dist/cli/subcommands/config.js +51 -37
- package/dist/cli/subcommands/doctor.d.ts +2 -1
- package/dist/cli/subcommands/doctor.js +97 -58
- package/dist/cli/subcommands/init.d.ts +1 -1
- package/dist/cli/subcommands/init.js +8 -7
- package/dist/cli/subcommands/memory.d.ts +18 -0
- package/dist/cli/subcommands/memory.js +189 -0
- package/dist/cli/subcommands/model-meta.js +11 -9
- package/dist/cli/subcommands/models-cache-probe.js +23 -20
- package/dist/cli/subcommands/models-discover.d.ts +3 -0
- package/dist/cli/subcommands/models-discover.js +44 -21
- package/dist/cli/subcommands/models.d.ts +1 -1
- package/dist/cli/subcommands/models.js +29 -22
- package/dist/cli/subcommands/probe-runner.js +8 -7
- package/dist/cli/subcommands/providers-list.js +32 -21
- package/dist/cli/subcommands/providers-plan.js +24 -25
- package/dist/cli/subcommands/providers-probe.js +5 -6
- package/dist/cli/subcommands/providers.d.ts +1 -1
- package/dist/cli/subcommands/providers.js +45 -50
- package/dist/cli/subcommands/sessions-export.d.ts +1 -1
- package/dist/cli/subcommands/sessions-export.js +10 -9
- package/dist/cli/subcommands/sessions-search.d.ts +1 -1
- package/dist/cli/subcommands/sessions-search.js +11 -10
- package/dist/cli/subcommands/sessions-trace.d.ts +30 -0
- package/dist/cli/subcommands/sessions-trace.js +156 -0
- package/dist/cli/subcommands/sessions.d.ts +4 -3
- package/dist/cli/subcommands/sessions.js +40 -37
- package/dist/cli/subcommands/stats.d.ts +1 -1
- package/dist/cli/subcommands/stats.js +49 -30
- package/dist/cli/system-prompt-arg.js +3 -2
- package/dist/codemode/capability.js +3 -2
- package/dist/compaction/post-compact.d.ts +1 -0
- package/dist/compaction/post-compact.js +15 -1
- package/dist/compaction/prune-tier.d.ts +1 -1
- package/dist/compaction/prune-tier.js +3 -3
- package/dist/compaction/serialize.js +2 -1
- package/dist/config/auth-file.d.ts +12 -2
- package/dist/config/auth-file.js +32 -10
- package/dist/config/checker.js +12 -11
- package/dist/config/context-files.js +3 -2
- package/dist/config/edit.d.ts +106 -0
- package/dist/config/edit.js +350 -0
- package/dist/config/init.d.ts +4 -3
- package/dist/config/init.js +10 -18
- package/dist/config/json-schema.d.ts +2 -1
- package/dist/config/json-schema.js +101 -68
- package/dist/config/key-docs.d.ts +18 -7
- package/dist/config/key-docs.js +43 -128
- package/dist/config/load.js +7 -6
- package/dist/config/merge.d.ts +4 -1
- package/dist/config/merge.js +42 -22
- package/dist/config/paths.js +6 -5
- package/dist/config/profile.d.ts +5 -1
- package/dist/config/profile.js +7 -2
- package/dist/config/schema-w5.js +3 -2
- package/dist/config/schema-w6.d.ts +19 -0
- package/dist/config/schema-w6.js +115 -0
- package/dist/config/schema.js +34 -20
- package/dist/config/settings-registry.d.ts +78 -0
- package/dist/config/settings-registry.js +184 -0
- package/dist/config/types-w6.d.ts +109 -0
- package/dist/config/types-w6.js +19 -0
- package/dist/config/types.d.ts +11 -8
- package/dist/config/types.js +1 -0
- package/dist/drivers/acp/client.js +1 -1
- package/dist/drivers/acp/driver.js +13 -8
- package/dist/drivers/agents.js +4 -3
- package/dist/drivers/base.js +3 -2
- package/dist/drivers/host-runners.js +2 -1
- package/dist/drivers/native/claude-stream.js +12 -11
- package/dist/drivers/native/codex-app-server.js +17 -12
- package/dist/drivers/native/oneshot.js +8 -7
- package/dist/drivers/pool.js +1 -1
- package/dist/drivers/runner.d.ts +3 -1
- package/dist/drivers/runner.js +68 -20
- package/dist/drivers/turn.js +1 -1
- package/dist/hooks/config.js +3 -2
- package/dist/hooks/protocol.js +18 -12
- package/dist/host/api-impl.js +11 -10
- package/dist/host/loader.js +9 -8
- package/dist/host/types.d.ts +3 -0
- package/dist/i18n/catalog.d.ts +2169 -0
- package/dist/i18n/catalog.js +72 -0
- package/dist/i18n/format.d.ts +21 -0
- package/dist/i18n/format.js +58 -0
- package/dist/i18n/index.d.ts +58 -0
- package/dist/i18n/index.js +72 -0
- package/dist/i18n/messages/agents.d.ts +84 -0
- package/dist/i18n/messages/agents.js +85 -0
- package/dist/i18n/messages/approval.d.ts +99 -0
- package/dist/i18n/messages/approval.js +100 -0
- package/dist/i18n/messages/auth.d.ts +187 -0
- package/dist/i18n/messages/auth.js +189 -0
- package/dist/i18n/messages/cli-args.d.ts +46 -0
- package/dist/i18n/messages/cli-args.js +46 -0
- package/dist/i18n/messages/cli-help.d.ts +12 -0
- package/dist/i18n/messages/cli-help.js +244 -0
- package/dist/i18n/messages/cli.d.ts +336 -0
- package/dist/i18n/messages/cli.js +311 -0
- package/dist/i18n/messages/config-keys.d.ts +289 -0
- package/dist/i18n/messages/config-keys.js +292 -0
- package/dist/i18n/messages/config.d.ts +502 -0
- package/dist/i18n/messages/config.js +239 -0
- package/dist/i18n/messages/drivers.d.ts +123 -0
- package/dist/i18n/messages/drivers.js +124 -0
- package/dist/i18n/messages/errors.d.ts +83 -0
- package/dist/i18n/messages/errors.js +171 -0
- package/dist/i18n/messages/interactive-line.d.ts +61 -0
- package/dist/i18n/messages/interactive-line.js +69 -0
- package/dist/i18n/messages/interactive-startup.d.ts +103 -0
- package/dist/i18n/messages/interactive-startup.js +104 -0
- package/dist/i18n/messages/interactive-view.d.ts +140 -0
- package/dist/i18n/messages/interactive-view.js +141 -0
- package/dist/i18n/messages/interactive.d.ts +493 -0
- package/dist/i18n/messages/interactive.js +248 -0
- package/dist/i18n/messages/memory.d.ts +123 -0
- package/dist/i18n/messages/memory.js +128 -0
- package/dist/i18n/messages/panels.d.ts +190 -0
- package/dist/i18n/messages/panels.js +189 -0
- package/dist/i18n/messages/permissions.d.ts +126 -0
- package/dist/i18n/messages/permissions.js +151 -0
- package/dist/i18n/messages/plan.d.ts +123 -0
- package/dist/i18n/messages/plan.js +124 -0
- package/dist/i18n/messages/print.d.ts +86 -0
- package/dist/i18n/messages/print.js +91 -0
- package/dist/i18n/messages/report.d.ts +391 -0
- package/dist/i18n/messages/report.js +490 -0
- package/dist/i18n/messages/rewind.d.ts +198 -0
- package/dist/i18n/messages/rewind.js +217 -0
- package/dist/i18n/messages/session.d.ts +230 -0
- package/dist/i18n/messages/session.js +258 -0
- package/dist/i18n/messages/settings.d.ts +348 -0
- package/dist/i18n/messages/settings.js +336 -0
- package/dist/i18n/messages/subcommands-config.d.ts +111 -0
- package/dist/i18n/messages/subcommands-config.js +127 -0
- package/dist/i18n/messages/subcommands.d.ts +548 -0
- package/dist/i18n/messages/subcommands.js +474 -0
- package/dist/i18n/messages/trace.d.ts +243 -0
- package/dist/i18n/messages/trace.js +238 -0
- package/dist/i18n/types.d.ts +15 -0
- package/dist/i18n/types.js +7 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +2 -0
- package/dist/memory/edit.d.ts +24 -0
- package/dist/memory/edit.js +63 -0
- package/dist/memory/frontmatter.d.ts +37 -0
- package/dist/memory/frontmatter.js +79 -0
- package/dist/memory/index.d.ts +28 -0
- package/dist/memory/index.js +126 -0
- package/dist/memory/lock.d.ts +17 -0
- package/dist/memory/lock.js +62 -0
- package/dist/memory/paths.d.ts +57 -0
- package/dist/memory/paths.js +175 -0
- package/dist/memory/report.d.ts +36 -0
- package/dist/memory/report.js +86 -0
- package/dist/memory/runtime.d.ts +42 -0
- package/dist/memory/runtime.js +36 -0
- package/dist/memory/secrets.d.ts +8 -0
- package/dist/memory/secrets.js +27 -0
- package/dist/memory/section.d.ts +28 -0
- package/dist/memory/section.js +59 -0
- package/dist/memory/store.d.ts +70 -0
- package/dist/memory/store.js +284 -0
- package/dist/memory/tool.d.ts +31 -0
- package/dist/memory/tool.js +94 -0
- package/dist/modes/acp/acp-events.js +2 -1
- package/dist/modes/acp/acp-server.js +13 -12
- package/dist/modes/commands-core.d.ts +11 -0
- package/dist/modes/commands-core.js +93 -62
- package/dist/modes/image-input.js +20 -6
- package/dist/modes/interactive/agent-bar.d.ts +95 -0
- package/dist/modes/interactive/agent-bar.js +248 -0
- package/dist/modes/interactive/agent-panels.js +27 -26
- package/dist/modes/interactive/agent-transcript.d.ts +47 -0
- package/dist/modes/interactive/agent-transcript.js +191 -0
- package/dist/modes/interactive/agent-ui.d.ts +36 -1
- package/dist/modes/interactive/agent-ui.js +180 -14
- package/dist/modes/interactive/agent-view.d.ts +72 -0
- package/dist/modes/interactive/agent-view.js +281 -0
- package/dist/modes/interactive/approval-dialog.js +46 -34
- package/dist/modes/interactive/clipboard-paste.d.ts +2 -2
- package/dist/modes/interactive/clipboard-paste.js +9 -4
- package/dist/modes/interactive/commands.d.ts +19 -1
- package/dist/modes/interactive/commands.js +84 -40
- package/dist/modes/interactive/completion.js +2 -1
- package/dist/modes/interactive/config-panel.d.ts +70 -0
- package/dist/modes/interactive/config-panel.js +319 -0
- package/dist/modes/interactive/config-ui.d.ts +96 -0
- package/dist/modes/interactive/config-ui.js +397 -0
- package/dist/modes/interactive/confirm-dialog.js +6 -7
- package/dist/modes/interactive/event-notices.js +11 -9
- package/dist/modes/interactive/interactive-mode.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode.js +19 -22
- package/dist/modes/interactive/key-dispatch.d.ts +12 -0
- package/dist/modes/interactive/key-dispatch.js +24 -9
- package/dist/modes/interactive/line/line-editor.js +2 -1
- package/dist/modes/interactive/line/line-mode.js +18 -7
- package/dist/modes/interactive/line/line-render.js +25 -24
- package/dist/modes/interactive/memory-panel.d.ts +64 -0
- package/dist/modes/interactive/memory-panel.js +221 -0
- package/dist/modes/interactive/message-view.d.ts +4 -2
- package/dist/modes/interactive/message-view.js +42 -33
- package/dist/modes/interactive/panels.js +39 -31
- package/dist/modes/interactive/pickers.d.ts +2 -1
- package/dist/modes/interactive/pickers.js +10 -6
- package/dist/modes/interactive/plan-command.d.ts +1 -1
- package/dist/modes/interactive/plan-command.js +24 -27
- package/dist/modes/interactive/plan-dialog.js +18 -17
- package/dist/modes/interactive/plan-flow.js +6 -5
- package/dist/modes/interactive/rewind-command.d.ts +1 -1
- package/dist/modes/interactive/rewind-command.js +18 -16
- package/dist/modes/interactive/rewind-flow.d.ts +0 -1
- package/dist/modes/interactive/rewind-flow.js +13 -14
- package/dist/modes/interactive/rewind-list.js +7 -6
- package/dist/modes/interactive/rewind-panel.js +40 -40
- package/dist/modes/interactive/rewind-text.d.ts +5 -3
- package/dist/modes/interactive/rewind-text.js +48 -38
- package/dist/modes/interactive/run-indicator.js +15 -12
- package/dist/modes/interactive/session-events.js +3 -2
- package/dist/modes/interactive/startup-header.js +22 -23
- package/dist/modes/interactive/startup-ui.js +38 -30
- package/dist/modes/interactive/status-area.d.ts +2 -0
- package/dist/modes/interactive/status-area.js +8 -2
- package/dist/modes/interactive/status-bar.js +4 -3
- package/dist/modes/interactive/subagent-view.js +9 -7
- package/dist/modes/interactive/tasks-report.d.ts +6 -0
- package/dist/modes/interactive/tasks-report.js +28 -31
- package/dist/modes/interactive/terminal-setup.d.ts +9 -0
- package/dist/modes/interactive/terminal-setup.js +23 -0
- package/dist/modes/interactive/tool-summary.js +19 -17
- package/dist/modes/interactive/tool-view.js +7 -4
- package/dist/modes/interactive/trace-view.d.ts +89 -0
- package/dist/modes/interactive/trace-view.js +332 -0
- package/dist/modes/print/print-mode.js +14 -15
- package/dist/modes/rpc/commands.js +7 -3
- package/dist/modes/rpc/rpc-mode.js +9 -3
- package/dist/modes/session-report.d.ts +2 -0
- package/dist/modes/session-report.js +101 -105
- package/dist/modes/startup-ui-text.js +11 -6
- package/dist/permissions/bypass.d.ts +8 -10
- package/dist/permissions/bypass.js +19 -10
- package/dist/permissions/memory-class.d.ts +23 -0
- package/dist/permissions/memory-class.js +54 -0
- package/dist/permissions/modes.d.ts +3 -3
- package/dist/permissions/modes.js +21 -16
- package/dist/permissions/pipeline.d.ts +1 -1
- package/dist/permissions/pipeline.js +9 -2
- package/dist/permissions/preview.js +49 -42
- package/dist/permissions/rules.js +5 -2
- package/dist/permissions/types.d.ts +4 -0
- package/dist/plan/store.js +2 -1
- package/dist/rpc.d.ts +39 -1
- package/dist/rpc.js +3 -0
- package/dist/sandbox/bash.js +11 -8
- package/dist/sandbox/detect.js +14 -11
- package/dist/sdk.d.ts +17 -1
- package/dist/sdk.js +29 -7
- package/dist/session/export.js +31 -30
- package/dist/session/manager.d.ts +1 -1
- package/dist/session/manager.js +5 -3
- package/dist/session/reuse.js +5 -4
- package/dist/session/scan.js +3 -2
- package/dist/session/stats-aggregate.d.ts +3 -1
- package/dist/session/stats-aggregate.js +5 -1
- package/dist/session/stats-index.js +2 -1
- package/dist/session/stats-scan.d.ts +4 -1
- package/dist/session/stats-scan.js +6 -2
- package/dist/session/store.d.ts +11 -0
- package/dist/session/store.js +48 -1
- package/dist/session/types.d.ts +2 -0
- package/dist/skills/builtin.js +2 -1
- package/dist/tools/image-file.d.ts +26 -1
- package/dist/tools/image-file.js +55 -13
- package/dist/tools/presets.d.ts +14 -1
- package/dist/tools/presets.js +3 -3
- package/dist/tools/registry.js +1 -1
- package/dist/tools/truncate.d.ts +1 -1
- package/dist/tools/truncate.js +2 -2
- package/dist/tools/types.d.ts +17 -1
- package/dist/trace/build-index.d.ts +91 -0
- package/dist/trace/build-index.js +127 -0
- package/dist/trace/build-nodes.d.ts +24 -0
- package/dist/trace/build-nodes.js +279 -0
- package/dist/trace/build-util.d.ts +43 -0
- package/dist/trace/build-util.js +120 -0
- package/dist/trace/build.d.ts +82 -0
- package/dist/trace/build.js +382 -0
- package/dist/trace/detail.d.ts +35 -0
- package/dist/trace/detail.js +205 -0
- package/dist/trace/flatten.d.ts +69 -0
- package/dist/trace/flatten.js +173 -0
- package/dist/trace/format.d.ts +47 -0
- package/dist/trace/format.js +329 -0
- package/dist/trace/html-template.d.ts +19 -0
- package/dist/trace/html-template.js +150 -0
- package/dist/trace/html.d.ts +104 -0
- package/dist/trace/html.js +292 -0
- package/dist/trace/preview.d.ts +29 -0
- package/dist/trace/preview.js +59 -0
- package/dist/trace/query-session.d.ts +14 -0
- package/dist/trace/query-session.js +38 -0
- package/dist/trace/query.d.ts +55 -0
- package/dist/trace/query.js +188 -0
- package/dist/trace/session.d.ts +40 -0
- package/dist/trace/session.js +168 -0
- package/dist/trace/types.d.ts +243 -0
- package/dist/trace/types.js +13 -0
- package/dist/tui/components/editor-paste.d.ts +2 -0
- package/dist/tui/components/editor-paste.js +6 -3
- package/dist/tui/components/editor.js +2 -2
- package/dist/tui/components/loader.d.ts +3 -0
- package/dist/tui/components/loader.js +14 -0
- package/dist/tui/components/settings-list.d.ts +61 -0
- package/dist/tui/components/settings-list.js +135 -0
- package/dist/tui/keybindings.d.ts +5 -0
- package/dist/tui/keybindings.js +17 -5
- package/dist/tui.d.ts +1 -0
- package/dist/tui.js +2 -0
- package/docs/en/host-api.md +167 -0
- package/docs/en/permissions.md +214 -0
- package/docs/en/providers.md +625 -0
- package/docs/en/rpc.md +361 -0
- package/docs/en/sessions.md +219 -0
- package/docs/en/tui.md +527 -0
- package/docs/host-api.md +18 -17
- package/docs/memory.md +172 -0
- package/docs/permissions.md +2 -2
- package/docs/providers.md +67 -1
- package/docs/rpc.md +31 -3
- package/docs/session-format.md +8 -2
- package/docs/sessions.md +29 -1
- package/docs/tui.md +132 -3
- package/package.json +8 -3
|
@@ -10,33 +10,75 @@
|
|
|
10
10
|
* - `/rewind`(无参数)打开回滚列表与确认面板(rewind-flow.ts);带参数走 commands-core,对话变了时
|
|
11
11
|
* 重画消息区并回填原消息。
|
|
12
12
|
* - 不是命令(含模板与 `/skill:`)返回 false,调用方把整行当提示发出。
|
|
13
|
+
* - [W6-C0] 面板钩子:`/config`(W6-S)、`/trace`(W6-T1)、`/memory`(W6-M)按 `PANEL_COMMANDS` 表分派到
|
|
14
|
+
* `CommandUi` 的可选钩子;`/tasks` 无参在有 `agentBar` 时聚焦 Agent 栏、`/tasks <id>` 在有 `agentView` 时
|
|
15
|
+
* 直接进子 Agent 视图(W6-A)。钩子没装时回落 commands-core(line 模式同一套文本)。
|
|
13
16
|
*/
|
|
17
|
+
import { msg } from "../../i18n/index.js";
|
|
14
18
|
import { formatModelRef } from "../../ai/providers/channels.js";
|
|
15
19
|
import { AgentSessionImpl } from "../../agent/session.js";
|
|
16
20
|
import { THINKING_LEVELS } from "../../ai/thinking.js";
|
|
17
21
|
import { listSessions } from "../../cli/compose-store.js";
|
|
18
22
|
import { hideFakeProvider } from "../../cli/fake-visibility.js";
|
|
19
|
-
import {
|
|
23
|
+
import { autoLayerText, permissionModeLabel } from "../../permissions/modes.js";
|
|
20
24
|
import { BUILTIN_COMMANDS, parseSlash, runSlashCommand, } from "../commands-core.js";
|
|
21
25
|
import { contentText } from "./message-view.js";
|
|
22
26
|
import { cachePanel, permissionsPanel, sessionPanel } from "./panels.js";
|
|
23
27
|
import { modelItems, permissionPickerSpec, sessionItems, thinkingItems, treeItems, } from "./pickers.js";
|
|
28
|
+
/** 说明随界面语言(取用时求值)。 */
|
|
29
|
+
function command(name, key) {
|
|
30
|
+
return {
|
|
31
|
+
name,
|
|
32
|
+
get description() {
|
|
33
|
+
return msg().interactive.commands[key];
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
24
37
|
export const INTERACTIVE_COMMANDS = [
|
|
25
|
-
|
|
26
|
-
|
|
38
|
+
command("tree", "treeDescription"),
|
|
39
|
+
command("permissions", "permissionsDescription"),
|
|
27
40
|
];
|
|
28
41
|
/** 补全与 /help 用的完整命令表。 */
|
|
29
42
|
export const ALL_COMMANDS = [...BUILTIN_COMMANDS, ...INTERACTIVE_COMMANDS];
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
/** `/help` 末尾的按键说明(多行)。 */
|
|
44
|
+
export function keyHints() {
|
|
45
|
+
return msg().interactive.commands.keyHints;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* [W6-C0] 面板类命令 → `CommandUi` 钩子。钩子存在就调用并返回 true;不存在返回 false(回落 commands-core)。
|
|
49
|
+
*/
|
|
50
|
+
export const PANEL_COMMANDS = {
|
|
51
|
+
config: async (ui, args) => {
|
|
52
|
+
if (ui.configPanel === undefined)
|
|
53
|
+
return false;
|
|
54
|
+
await ui.configPanel(args);
|
|
55
|
+
return true;
|
|
56
|
+
},
|
|
57
|
+
trace: async (ui, args) => {
|
|
58
|
+
if (ui.traceView === undefined)
|
|
59
|
+
return false;
|
|
60
|
+
await ui.traceView(args === "" ? undefined : args);
|
|
61
|
+
return true;
|
|
62
|
+
},
|
|
63
|
+
memory: async (ui, args) => {
|
|
64
|
+
if (ui.memoryPanel === undefined)
|
|
65
|
+
return false;
|
|
66
|
+
await ui.memoryPanel(args);
|
|
67
|
+
return true;
|
|
68
|
+
},
|
|
69
|
+
tasks: async (ui, args) => {
|
|
70
|
+
const parts = args.split(/\s+/).filter((s) => s !== "");
|
|
71
|
+
if (parts.length === 0 && ui.agentBar !== undefined) {
|
|
72
|
+
await ui.agentBar();
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
if (parts.length === 1 && parts[0] !== "stop" && ui.agentView !== undefined) {
|
|
76
|
+
await ui.agentView(parts[0]);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
},
|
|
81
|
+
};
|
|
40
82
|
function homeOf(ui) {
|
|
41
83
|
return ui.home !== undefined ? { home: ui.home } : {};
|
|
42
84
|
}
|
|
@@ -52,25 +94,23 @@ export function activeBranchIds(session) {
|
|
|
52
94
|
return new Set(session.entries.map((e) => e.id));
|
|
53
95
|
}
|
|
54
96
|
export function permissionsText(runtime, session) {
|
|
97
|
+
const m = msg().interactive.commands;
|
|
98
|
+
const p = msg().panels.permissions;
|
|
55
99
|
const rules = runtime.permission.rules;
|
|
56
100
|
const mode = session.state.permissionMode;
|
|
57
|
-
const order = mode === "auto"
|
|
58
|
-
? "deny 规则 → Hook deny → 危险命令确认 → 规则层(受保护路径、项目外写入、网络、删除类)→ Hook ask → allow 规则 / Hook allow / 本会话记忆 → 静态判定(只读、项目内写入、安全名单)→ 模型分类器 → 询问"
|
|
59
|
-
: mode === "allowlist"
|
|
60
|
-
? "deny 规则 → Hook deny → 危险命令(拒绝)→ 只读工具 / allow 规则 / Hook allow 放行 → 其余拒绝(从不询问)"
|
|
61
|
-
: "deny 规则 → Hook deny → 危险命令确认 → 权限模式 → allow 规则 / Hook allow / 本会话记忆 → 询问";
|
|
101
|
+
const order = mode === "auto" ? p.orderAuto : mode === "allowlist" ? p.orderAllowlist : p.orderDefault;
|
|
62
102
|
const lines = [
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
rules.length === 0 ?
|
|
103
|
+
m.permissionsMode(permissionModeLabel(mode), mode),
|
|
104
|
+
m.permissionsOrder(order),
|
|
105
|
+
rules.length === 0 ? m.rulesNone : m.rules(rules.length),
|
|
66
106
|
...rules.map((r) => ` ${r.effect === "deny" ? "deny " : "allow"} ${r.raw} [${r.source}]`),
|
|
67
107
|
];
|
|
68
108
|
const recent = runtime.permission.autoDecisions?.() ?? [];
|
|
69
109
|
if (recent.length > 0) {
|
|
70
|
-
lines.push(
|
|
110
|
+
lines.push(m.recentAuto(recent.length));
|
|
71
111
|
for (const d of recent) {
|
|
72
|
-
const cached = d.cached === true ?
|
|
73
|
-
lines.push(` ${
|
|
112
|
+
const cached = d.cached === true ? p.cached : "";
|
|
113
|
+
lines.push(` ${autoLayerText(d.layer)} ${d.decision} ${d.toolName} ${d.summary} — ${d.reason}${cached}`);
|
|
74
114
|
}
|
|
75
115
|
}
|
|
76
116
|
return lines.join("\n");
|
|
@@ -79,7 +119,7 @@ async function pickTree(ui, title) {
|
|
|
79
119
|
const session = ui.session();
|
|
80
120
|
const items = treeItems(session.entries, activeBranchIds(session), ui.now());
|
|
81
121
|
if (items.length === 0) {
|
|
82
|
-
ui.notice("info",
|
|
122
|
+
ui.notice("info", msg().interactive.commands.noUserMessages);
|
|
83
123
|
return undefined;
|
|
84
124
|
}
|
|
85
125
|
const active = [...items].reverse().find((i) => i.label.includes("● "));
|
|
@@ -95,14 +135,14 @@ async function pickTree(ui, title) {
|
|
|
95
135
|
async function treeCommand(ui) {
|
|
96
136
|
const session = ui.session();
|
|
97
137
|
if (!(session instanceof AgentSessionImpl)) {
|
|
98
|
-
ui.notice("warn",
|
|
138
|
+
ui.notice("warn", msg().interactive.commands.treeUnsupported);
|
|
99
139
|
return;
|
|
100
140
|
}
|
|
101
141
|
if (session.state.isStreaming) {
|
|
102
|
-
ui.notice("warn",
|
|
142
|
+
ui.notice("warn", msg().interactive.commands.treeBusy);
|
|
103
143
|
return;
|
|
104
144
|
}
|
|
105
|
-
const entry = await pickTree(ui,
|
|
145
|
+
const entry = await pickTree(ui, msg().interactive.commands.treeTitle);
|
|
106
146
|
if (entry === undefined)
|
|
107
147
|
return;
|
|
108
148
|
await session.navigate(entry.parentId);
|
|
@@ -111,12 +151,13 @@ async function treeCommand(ui) {
|
|
|
111
151
|
}
|
|
112
152
|
async function handlePick(what, ui) {
|
|
113
153
|
const session = ui.session();
|
|
154
|
+
const m = msg().interactive.commands;
|
|
114
155
|
switch (what) {
|
|
115
156
|
case "model": {
|
|
116
157
|
const current = session.state.model;
|
|
117
158
|
const ref = current === undefined ? undefined : `${current.provider}/${current.id}`;
|
|
118
159
|
const picked = await ui.pick({
|
|
119
|
-
title:
|
|
160
|
+
title: m.modelTitle,
|
|
120
161
|
items: await modelItems(ui.env === undefined
|
|
121
162
|
? ui.runtime.providers
|
|
122
163
|
: hideFakeProvider(ui.runtime.providers, ui.env)),
|
|
@@ -127,7 +168,7 @@ async function handlePick(what, ui) {
|
|
|
127
168
|
if (picked === undefined)
|
|
128
169
|
return;
|
|
129
170
|
await session.setModel(picked.value);
|
|
130
|
-
ui.notice("info",
|
|
171
|
+
ui.notice("info", m.modelSet(picked.value));
|
|
131
172
|
return;
|
|
132
173
|
}
|
|
133
174
|
case "session": {
|
|
@@ -137,26 +178,26 @@ async function handlePick(what, ui) {
|
|
|
137
178
|
});
|
|
138
179
|
const others = items.filter((i) => i.id !== session.state.sessionId);
|
|
139
180
|
if (others.length === 0) {
|
|
140
|
-
ui.notice("info",
|
|
181
|
+
ui.notice("info", m.noOtherSessions);
|
|
141
182
|
return;
|
|
142
183
|
}
|
|
143
184
|
const picked = await ui.pick({
|
|
144
|
-
title:
|
|
185
|
+
title: m.resumeTitle,
|
|
145
186
|
items: sessionItems(others, ui.now()),
|
|
146
187
|
filterable: true,
|
|
147
188
|
});
|
|
148
189
|
if (picked === undefined)
|
|
149
190
|
return;
|
|
150
191
|
const next = await ui.switchSession({ kind: "resume", id: picked.value });
|
|
151
|
-
ui.notice("info",
|
|
192
|
+
ui.notice("info", m.resumed(next.state.sessionId.slice(0, 8)));
|
|
152
193
|
return;
|
|
153
194
|
}
|
|
154
195
|
case "tree": {
|
|
155
|
-
const entry = await pickTree(ui,
|
|
196
|
+
const entry = await pickTree(ui, m.forkTitle);
|
|
156
197
|
if (entry === undefined)
|
|
157
198
|
return;
|
|
158
199
|
const next = await ui.switchSession(entry.parentId === null ? { kind: "new" } : { kind: "fork", entryId: entry.parentId });
|
|
159
|
-
ui.notice("info",
|
|
200
|
+
ui.notice("info", m.forked(next.state.sessionId.slice(0, 8)));
|
|
160
201
|
ui.setEditorText(userEntryText(entry) ?? "");
|
|
161
202
|
return;
|
|
162
203
|
}
|
|
@@ -167,11 +208,11 @@ async function handlePick(what, ui) {
|
|
|
167
208
|
const mode = picked.value;
|
|
168
209
|
const current = session.state.permissionMode;
|
|
169
210
|
if (mode !== current && ui.confirmMode !== undefined && !(await ui.confirmMode(mode))) {
|
|
170
|
-
ui.notice("info",
|
|
211
|
+
ui.notice("info", m.modeCancelled(permissionModeLabel(current)));
|
|
171
212
|
return;
|
|
172
213
|
}
|
|
173
214
|
session.setPermissionMode(mode);
|
|
174
|
-
ui.notice("info",
|
|
215
|
+
ui.notice("info", m.modeSet(permissionModeLabel(mode)));
|
|
175
216
|
return;
|
|
176
217
|
}
|
|
177
218
|
case "thinking": {
|
|
@@ -179,13 +220,13 @@ async function handlePick(what, ui) {
|
|
|
179
220
|
const found = model === undefined ? undefined : ui.runtime.providers.findModel(formatModelRef(model));
|
|
180
221
|
const reasoning = found?.ok === true ? found.model.reasoning : true;
|
|
181
222
|
const picked = await ui.pick({
|
|
182
|
-
title:
|
|
223
|
+
title: m.thinkingTitle,
|
|
183
224
|
items: thinkingItems(reasoning),
|
|
184
225
|
selected: session.state.thinkingLevel,
|
|
185
226
|
currentValue: session.state.thinkingLevel,
|
|
186
227
|
filterable: false,
|
|
187
228
|
numberKeys: true,
|
|
188
|
-
footer:
|
|
229
|
+
footer: m.numberFooter("↑↓", THINKING_LEVELS.length),
|
|
189
230
|
});
|
|
190
231
|
if (picked !== undefined)
|
|
191
232
|
session.setThinkingLevel(picked.value);
|
|
@@ -201,6 +242,9 @@ export async function runInteractiveCommand(line, ui) {
|
|
|
201
242
|
try {
|
|
202
243
|
if (ui.extra !== undefined && (await ui.extra(parsed.name, parsed.args)))
|
|
203
244
|
return true;
|
|
245
|
+
const panel = PANEL_COMMANDS[parsed.name];
|
|
246
|
+
if (panel !== undefined && (await panel(ui, parsed.args)))
|
|
247
|
+
return true;
|
|
204
248
|
if (parsed.name === "tree") {
|
|
205
249
|
await treeCommand(ui);
|
|
206
250
|
return true;
|
|
@@ -251,7 +295,7 @@ export async function runInteractiveCommand(line, ui) {
|
|
|
251
295
|
ui.reload();
|
|
252
296
|
if (parsed.name === "help") {
|
|
253
297
|
const extra = INTERACTIVE_COMMANDS.map((c) => `/${c.name} ${c.description}`);
|
|
254
|
-
ui.notice("info", [result.message ?? "", ...extra, "",
|
|
298
|
+
ui.notice("info", [result.message ?? "", ...extra, "", keyHints()].join("\n"));
|
|
255
299
|
}
|
|
256
300
|
else if (result.message !== undefined)
|
|
257
301
|
ui.notice("info", result.message);
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* 最多 50 项;查询含 glob 字符时按 glob(tools/glob.ts)匹配相对路径,否则按「文件名前缀 → 路径前缀 →
|
|
8
8
|
* 文件名子串 → 路径子串」排序。目录以 `/` 结尾,可继续补全。文件表按 cwd 缓存 15 秒、最多 20 000 项。
|
|
9
9
|
*/
|
|
10
|
+
import { msg } from "../../i18n/index.js";
|
|
10
11
|
import { GlobMatcher } from "../../tools/glob.js";
|
|
11
12
|
import { walk } from "../../tools/ignore.js";
|
|
12
13
|
export const MAX_FILE_ITEMS = 50;
|
|
@@ -82,7 +83,7 @@ export class InteractiveCompletion {
|
|
|
82
83
|
: command.description, command.name);
|
|
83
84
|
}
|
|
84
85
|
for (const prompt of this.source.prompts())
|
|
85
|
-
push(prompt.name,
|
|
86
|
+
push(prompt.name, msg().interactive.completion.promptTemplate, prompt.name);
|
|
86
87
|
for (const skill of this.source.skills()) {
|
|
87
88
|
push(`skill:${skill.name}`, skill.description, `skill:${skill.name}`);
|
|
88
89
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/config` settings panel component (docs/wave6-plan.md §6.3, D24-D26). [W6-S]
|
|
3
|
+
*
|
|
4
|
+
* Bottom overlay with the card bar: title + write target (`Tab` switches user / project), a search line,
|
|
5
|
+
* the grouped `SettingsList`, a rule, the selected key's description (key-docs, dim, wrapped), one notice
|
|
6
|
+
* line and the key hints. What a key press *does* to a setting is the controller's job (config-ui.ts):
|
|
7
|
+
* the panel calls `activate(key)` for Enter / Space, `unset(key)` for a confirmed Backspace / Delete,
|
|
8
|
+
* `commit(key, text)` for the inline number / text editor, `switchScope()` for Tab and `close()` for Esc.
|
|
9
|
+
*
|
|
10
|
+
* Keys: ↑↓ (and Ctrl+P / Ctrl+N) move; `/` starts a search (typing filters, Esc clears it first, a
|
|
11
|
+
* second Esc closes); Backspace / Delete asks once, then unsets; inside the editor Enter saves (an
|
|
12
|
+
* invalid value stays in the box with the error in red), Esc cancels.
|
|
13
|
+
*/
|
|
14
|
+
import { type Component, type Focusable, type Keybindings, type SemanticColor, type SettingsRow, type Theme } from "../../tui.js";
|
|
15
|
+
import { type ConfigSnapshot, type EditScope, type SettingValue } from "../../config/edit.js";
|
|
16
|
+
import { type SettingSpec } from "../../config/settings-registry.js";
|
|
17
|
+
export interface ConfigPanelDeps {
|
|
18
|
+
theme: Theme;
|
|
19
|
+
keybindings?: Keybindings;
|
|
20
|
+
/** Terminal rows (the panel fits in `rows - 1`). */
|
|
21
|
+
rows?(): number;
|
|
22
|
+
/** Write target paths for the title (already shortened with ~). */
|
|
23
|
+
paths: Readonly<Record<EditScope, string>>;
|
|
24
|
+
/** Embedded host (profile): the title says changes go to the user config. */
|
|
25
|
+
embedded?: boolean;
|
|
26
|
+
activate(key: string, scope: EditScope): void | Promise<void>;
|
|
27
|
+
unset(key: string, scope: EditScope): void | Promise<void>;
|
|
28
|
+
/** Inline editor: returns an error message to keep the box open. */
|
|
29
|
+
commit(key: string, text: string, scope: EditScope): Promise<string | undefined>;
|
|
30
|
+
close(): void;
|
|
31
|
+
requestRender(): void;
|
|
32
|
+
}
|
|
33
|
+
/** Display text of a setting value (unset ASCII shows the detected value). */
|
|
34
|
+
export declare function settingValueText(spec: SettingSpec, value: SettingValue, theme: Theme): string;
|
|
35
|
+
/** Why the row cannot be changed in `scope` (undefined: editable). */
|
|
36
|
+
export declare function blockedReason(spec: SettingSpec, value: SettingValue, scope: EditScope): string | undefined;
|
|
37
|
+
/** Panel rows for a snapshot (pure; frame tests use it directly). */
|
|
38
|
+
export declare function settingRows(snapshot: ConfigSnapshot, scope: EditScope, theme: Theme): SettingsRow[];
|
|
39
|
+
/** Hint text for an "edited elsewhere" row id (undefined for settings). */
|
|
40
|
+
export declare function hintOf(id: string): string | undefined;
|
|
41
|
+
export declare class ConfigPanel implements Component, Focusable {
|
|
42
|
+
private snapshot;
|
|
43
|
+
private readonly deps;
|
|
44
|
+
focused: boolean;
|
|
45
|
+
scope: EditScope;
|
|
46
|
+
private readonly list;
|
|
47
|
+
private searching;
|
|
48
|
+
private edit;
|
|
49
|
+
private pendingUnset;
|
|
50
|
+
private notice;
|
|
51
|
+
private busy;
|
|
52
|
+
constructor(snapshot: ConfigSnapshot, deps: ConfigPanelDeps);
|
|
53
|
+
/** Selected row id (tests). */
|
|
54
|
+
get selectedKey(): string | undefined;
|
|
55
|
+
get editing(): boolean;
|
|
56
|
+
refresh(snapshot?: ConfigSnapshot): void;
|
|
57
|
+
select(key: string): void;
|
|
58
|
+
setNotice(text: string | undefined, color?: SemanticColor): void;
|
|
59
|
+
/** Open the inline editor for a number / text key. */
|
|
60
|
+
startEdit(key: string, initial: string): void;
|
|
61
|
+
handleInput(data: string): void;
|
|
62
|
+
private move;
|
|
63
|
+
/** Run a controller action; input is ignored until it settles. */
|
|
64
|
+
private run;
|
|
65
|
+
private editInput;
|
|
66
|
+
render(width: number): string[];
|
|
67
|
+
private body;
|
|
68
|
+
private description;
|
|
69
|
+
invalidate(): void;
|
|
70
|
+
}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/config` settings panel component (docs/wave6-plan.md §6.3, D24-D26). [W6-S]
|
|
3
|
+
*
|
|
4
|
+
* Bottom overlay with the card bar: title + write target (`Tab` switches user / project), a search line,
|
|
5
|
+
* the grouped `SettingsList`, a rule, the selected key's description (key-docs, dim, wrapped), one notice
|
|
6
|
+
* line and the key hints. What a key press *does* to a setting is the controller's job (config-ui.ts):
|
|
7
|
+
* the panel calls `activate(key)` for Enter / Space, `unset(key)` for a confirmed Backspace / Delete,
|
|
8
|
+
* `commit(key, text)` for the inline number / text editor, `switchScope()` for Tab and `close()` for Esc.
|
|
9
|
+
*
|
|
10
|
+
* Keys: ↑↓ (and Ctrl+P / Ctrl+N) move; `/` starts a search (typing filters, Esc clears it first, a
|
|
11
|
+
* second Esc closes); Backspace / Delete asks once, then unsets; inside the editor Enter saves (an
|
|
12
|
+
* invalid value stays in the box with the error in red), Esc cancels.
|
|
13
|
+
*/
|
|
14
|
+
import { Card, SettingsList, defaultKeybindings, isPasteData, isPrintableText, matchesKey, truncateToWidth, unwrapPaste, wrapTextWithAnsi, } from "../../tui.js";
|
|
15
|
+
import { msg } from "../../i18n/index.js";
|
|
16
|
+
import { formatSettingValue, getConfigValue, overriddenFor, } from "../../config/edit.js";
|
|
17
|
+
import { SETTING_HINTS, settingDoc, settingDynamicDefault, settingsRegistry, } from "../../config/settings-registry.js";
|
|
18
|
+
const ELSEWHERE = "elsewhere:";
|
|
19
|
+
/** Display text of a setting value (unset ASCII shows the detected value). */
|
|
20
|
+
export function settingValueText(spec, value, theme) {
|
|
21
|
+
if (value.value === undefined && spec.key === "ui.ascii")
|
|
22
|
+
return msg().settings.auto(String(theme.glyphs.ascii));
|
|
23
|
+
return formatSettingValue(value.value);
|
|
24
|
+
}
|
|
25
|
+
function lockReason(value) {
|
|
26
|
+
const reasons = msg().settings.lockReason;
|
|
27
|
+
if (value.source === "env")
|
|
28
|
+
return reasons.env(value.envName ?? "");
|
|
29
|
+
if (value.source === "cli")
|
|
30
|
+
return reasons.cli;
|
|
31
|
+
if (value.source === "profile")
|
|
32
|
+
return reasons.profile;
|
|
33
|
+
return reasons.project;
|
|
34
|
+
}
|
|
35
|
+
/** Why the row cannot be changed in `scope` (undefined: editable). */
|
|
36
|
+
export function blockedReason(spec, value, scope) {
|
|
37
|
+
const s = msg().settings;
|
|
38
|
+
if (overriddenFor(value, scope) !== undefined)
|
|
39
|
+
return s.errors.locked(s.labels[spec.key] ?? spec.key, lockReason(value));
|
|
40
|
+
if (scope === "project" && spec.project === "deny")
|
|
41
|
+
return s.errors.projectDenied(spec.key);
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
/** Panel rows for a snapshot (pure; frame tests use it directly). */
|
|
45
|
+
export function settingRows(snapshot, scope, theme) {
|
|
46
|
+
const s = msg().settings;
|
|
47
|
+
const labels = s.labels;
|
|
48
|
+
const rows = settingsRegistry().map((spec) => {
|
|
49
|
+
const value = getConfigValue(snapshot, spec.key);
|
|
50
|
+
const locked = overriddenFor(value, scope) !== undefined;
|
|
51
|
+
const tag = spec.prefix === true ? `${s.apply[spec.apply]} · ${s.prefixTag}` : s.apply[spec.apply];
|
|
52
|
+
let note;
|
|
53
|
+
let noteColor;
|
|
54
|
+
if (locked) {
|
|
55
|
+
note = s.locked(value.envName ?? value.source);
|
|
56
|
+
noteColor = "warning";
|
|
57
|
+
}
|
|
58
|
+
else if (scope === "project" && spec.project !== "any")
|
|
59
|
+
note = s.projectNote[spec.project];
|
|
60
|
+
else if (value.source !== "default")
|
|
61
|
+
note = s.source(value.source);
|
|
62
|
+
const row = {
|
|
63
|
+
id: spec.key,
|
|
64
|
+
group: s.groups[spec.group],
|
|
65
|
+
label: labels[spec.key] ?? spec.key,
|
|
66
|
+
value: settingValueText(spec, value, theme),
|
|
67
|
+
tag,
|
|
68
|
+
search: [...(spec.options ?? []), settingDoc(spec.key) ?? ""].join(" "),
|
|
69
|
+
};
|
|
70
|
+
if (value.value === undefined)
|
|
71
|
+
row.valueColor = "dim";
|
|
72
|
+
if (note !== undefined)
|
|
73
|
+
row.note = note;
|
|
74
|
+
if (noteColor !== undefined)
|
|
75
|
+
row.noteColor = noteColor;
|
|
76
|
+
if (scope === "project" && spec.project === "deny")
|
|
77
|
+
row.dim = true;
|
|
78
|
+
return row;
|
|
79
|
+
});
|
|
80
|
+
for (const hint of SETTING_HINTS) {
|
|
81
|
+
rows.push({
|
|
82
|
+
id: `${ELSEWHERE}${hint.key}`,
|
|
83
|
+
group: s.groups.elsewhere,
|
|
84
|
+
label: hint.key,
|
|
85
|
+
value: s.hints[hint.hint],
|
|
86
|
+
dim: true,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return rows;
|
|
90
|
+
}
|
|
91
|
+
/** Hint text for an "edited elsewhere" row id (undefined for settings). */
|
|
92
|
+
export function hintOf(id) {
|
|
93
|
+
if (!id.startsWith(ELSEWHERE))
|
|
94
|
+
return undefined;
|
|
95
|
+
const key = id.slice(ELSEWHERE.length);
|
|
96
|
+
const hint = SETTING_HINTS.find((h) => h.key === key);
|
|
97
|
+
return hint === undefined ? undefined : msg().settings.hints[hint.hint];
|
|
98
|
+
}
|
|
99
|
+
class Lines {
|
|
100
|
+
build;
|
|
101
|
+
constructor(build) {
|
|
102
|
+
this.build = build;
|
|
103
|
+
}
|
|
104
|
+
render(width) {
|
|
105
|
+
return this.build(width);
|
|
106
|
+
}
|
|
107
|
+
invalidate() { }
|
|
108
|
+
}
|
|
109
|
+
/** Fixed lines besides the list: title (+ embedded), search, rule, description (≤ 3), notice, footer. */
|
|
110
|
+
const CHROME = 8;
|
|
111
|
+
export class ConfigPanel {
|
|
112
|
+
snapshot;
|
|
113
|
+
deps;
|
|
114
|
+
focused = false;
|
|
115
|
+
scope = "user";
|
|
116
|
+
list;
|
|
117
|
+
searching = false;
|
|
118
|
+
edit;
|
|
119
|
+
pendingUnset;
|
|
120
|
+
notice;
|
|
121
|
+
busy = false;
|
|
122
|
+
constructor(snapshot, deps) {
|
|
123
|
+
this.snapshot = snapshot;
|
|
124
|
+
this.deps = deps;
|
|
125
|
+
this.list = new SettingsList({ theme: deps.theme, emptyText: msg().settings.noMatches });
|
|
126
|
+
this.refresh(snapshot);
|
|
127
|
+
}
|
|
128
|
+
/** Selected row id (tests). */
|
|
129
|
+
get selectedKey() {
|
|
130
|
+
return this.list.selectedRow?.id;
|
|
131
|
+
}
|
|
132
|
+
get editing() {
|
|
133
|
+
return this.edit !== undefined;
|
|
134
|
+
}
|
|
135
|
+
refresh(snapshot = this.snapshot) {
|
|
136
|
+
this.snapshot = snapshot;
|
|
137
|
+
this.list.setRows(settingRows(snapshot, this.scope, this.deps.theme));
|
|
138
|
+
}
|
|
139
|
+
select(key) {
|
|
140
|
+
this.list.selectId(key);
|
|
141
|
+
}
|
|
142
|
+
setNotice(text, color = "dim") {
|
|
143
|
+
this.notice = text === undefined ? undefined : { text, color };
|
|
144
|
+
}
|
|
145
|
+
/** Open the inline editor for a number / text key. */
|
|
146
|
+
startEdit(key, initial) {
|
|
147
|
+
this.edit = { key, text: initial };
|
|
148
|
+
}
|
|
149
|
+
handleInput(data) {
|
|
150
|
+
if (this.busy)
|
|
151
|
+
return;
|
|
152
|
+
const keys = this.deps.keybindings ?? defaultKeybindings;
|
|
153
|
+
if (this.edit !== undefined)
|
|
154
|
+
return this.editInput(data);
|
|
155
|
+
const row = this.list.selectedRow;
|
|
156
|
+
const unsetKey = matchesKey(data, "backspace") || matchesKey(data, "delete");
|
|
157
|
+
if (!unsetKey || this.searching)
|
|
158
|
+
this.pendingUnset = undefined;
|
|
159
|
+
if (keys.matches(data, "tui.select.up"))
|
|
160
|
+
return this.move(-1);
|
|
161
|
+
if (keys.matches(data, "tui.select.down"))
|
|
162
|
+
return this.move(1);
|
|
163
|
+
if (matchesKey(data, "escape") || keys.matches(data, "app.clear")) {
|
|
164
|
+
if (this.searching || this.list.query !== "") {
|
|
165
|
+
this.searching = false;
|
|
166
|
+
this.list.setQuery("");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
return this.deps.close();
|
|
170
|
+
}
|
|
171
|
+
if (matchesKey(data, "tab")) {
|
|
172
|
+
this.scope = this.scope === "user" ? "project" : "user";
|
|
173
|
+
this.notice = undefined;
|
|
174
|
+
this.refresh();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (this.searching) {
|
|
178
|
+
if (matchesKey(data, "enter") && row !== undefined)
|
|
179
|
+
return this.run(this.deps.activate(row.id, this.scope));
|
|
180
|
+
if (matchesKey(data, "backspace"))
|
|
181
|
+
return this.list.setQuery([...this.list.query].slice(0, -1).join(""));
|
|
182
|
+
const text = isPasteData(data) ? unwrapPaste(data) : isPrintableText(data) ? data : "";
|
|
183
|
+
if (text !== "")
|
|
184
|
+
this.list.setQuery(this.list.query + text.replace(/\s+/g, " "));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (data === "/") {
|
|
188
|
+
this.searching = true;
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (row === undefined)
|
|
192
|
+
return;
|
|
193
|
+
if (matchesKey(data, "enter") || matchesKey(data, "space")) {
|
|
194
|
+
this.notice = undefined;
|
|
195
|
+
const hint = hintOf(row.id);
|
|
196
|
+
if (hint !== undefined)
|
|
197
|
+
return this.setNotice(hint);
|
|
198
|
+
return this.run(this.deps.activate(row.id, this.scope));
|
|
199
|
+
}
|
|
200
|
+
if (unsetKey && hintOf(row.id) === undefined) {
|
|
201
|
+
if (this.pendingUnset === row.id) {
|
|
202
|
+
this.pendingUnset = undefined;
|
|
203
|
+
return this.run(this.deps.unset(row.id, this.scope));
|
|
204
|
+
}
|
|
205
|
+
this.pendingUnset = row.id;
|
|
206
|
+
this.setNotice(msg().settings.confirmUnset(row.id), "warning");
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
move(delta) {
|
|
210
|
+
this.list.move(delta);
|
|
211
|
+
this.pendingUnset = undefined;
|
|
212
|
+
if (!this.searching)
|
|
213
|
+
this.notice = undefined;
|
|
214
|
+
}
|
|
215
|
+
/** Run a controller action; input is ignored until it settles. */
|
|
216
|
+
run(result) {
|
|
217
|
+
if (!(result instanceof Promise))
|
|
218
|
+
return;
|
|
219
|
+
this.busy = true;
|
|
220
|
+
void result.finally(() => {
|
|
221
|
+
this.busy = false;
|
|
222
|
+
this.deps.requestRender();
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
editInput(data) {
|
|
226
|
+
const edit = this.edit;
|
|
227
|
+
if (matchesKey(data, "escape") ||
|
|
228
|
+
(this.deps.keybindings ?? defaultKeybindings).matches(data, "app.clear")) {
|
|
229
|
+
this.edit = undefined;
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (matchesKey(data, "enter")) {
|
|
233
|
+
this.busy = true;
|
|
234
|
+
void this.deps
|
|
235
|
+
.commit(edit.key, edit.text, this.scope)
|
|
236
|
+
.then((error) => {
|
|
237
|
+
if (error === undefined)
|
|
238
|
+
this.edit = undefined;
|
|
239
|
+
else
|
|
240
|
+
edit.error = error;
|
|
241
|
+
})
|
|
242
|
+
.finally(() => {
|
|
243
|
+
this.busy = false;
|
|
244
|
+
this.deps.requestRender();
|
|
245
|
+
});
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (matchesKey(data, "backspace"))
|
|
249
|
+
edit.text = [...edit.text].slice(0, -1).join("");
|
|
250
|
+
else if (isPasteData(data))
|
|
251
|
+
edit.text += unwrapPaste(data).replace(/\s+/g, " ");
|
|
252
|
+
else if (isPrintableText(data))
|
|
253
|
+
edit.text += data;
|
|
254
|
+
else
|
|
255
|
+
return;
|
|
256
|
+
delete edit.error;
|
|
257
|
+
}
|
|
258
|
+
render(width) {
|
|
259
|
+
const { theme } = this.deps;
|
|
260
|
+
const s = msg().settings;
|
|
261
|
+
const rows = this.deps.rows?.();
|
|
262
|
+
const embedded = this.deps.embedded === true ? 1 : 0;
|
|
263
|
+
if (rows !== undefined)
|
|
264
|
+
this.list.maxVisible = rows - 1 - CHROME - embedded;
|
|
265
|
+
const subtitle = `${s.writeTo(s.scope[this.scope], this.deps.paths[this.scope])} ${s.tabSwitch}`;
|
|
266
|
+
const card = new Card(new Lines((w) => this.body(w)), { theme, title: s.title, subtitle });
|
|
267
|
+
return card.render(width);
|
|
268
|
+
}
|
|
269
|
+
body(width) {
|
|
270
|
+
const { theme } = this.deps;
|
|
271
|
+
const s = msg().settings;
|
|
272
|
+
const out = [];
|
|
273
|
+
if (this.deps.embedded === true)
|
|
274
|
+
out.push(theme.fg("warning", s.embedded));
|
|
275
|
+
const query = this.list.query;
|
|
276
|
+
out.push(this.searching || query !== ""
|
|
277
|
+
? `${s.searchPrompt(query)}${this.searching && this.edit === undefined ? theme.fg("accent", "_") : ""}`
|
|
278
|
+
: theme.fg("dim", s.searchPlaceholder));
|
|
279
|
+
out.push(...this.list.render(width));
|
|
280
|
+
out.push(theme.fg("border", theme.glyphs.rule.repeat(Math.max(1, width))));
|
|
281
|
+
out.push(...this.description(width));
|
|
282
|
+
if (this.edit !== undefined) {
|
|
283
|
+
const label = s.labels[this.edit.key] ?? this.edit.key;
|
|
284
|
+
const box = `${s.editPrompt(label)}${this.edit.text}${theme.fg("accent", "_")}`;
|
|
285
|
+
out.push(truncateToWidth(box, width));
|
|
286
|
+
if (this.edit.error !== undefined)
|
|
287
|
+
out.push(theme.fg("error", truncateToWidth(this.edit.error, width)));
|
|
288
|
+
}
|
|
289
|
+
else if (this.notice !== undefined) {
|
|
290
|
+
out.push(...wrapTextWithAnsi(theme.fg(this.notice.color, this.notice.text), width).slice(0, 2));
|
|
291
|
+
}
|
|
292
|
+
const footer = this.edit !== undefined
|
|
293
|
+
? s.footerEdit
|
|
294
|
+
: this.searching
|
|
295
|
+
? s.footerSearch
|
|
296
|
+
: width < 72
|
|
297
|
+
? s.footerCompact
|
|
298
|
+
: s.footer;
|
|
299
|
+
out.push(theme.fg("dim", truncateToWidth(footer, width)));
|
|
300
|
+
return out;
|
|
301
|
+
}
|
|
302
|
+
description(width) {
|
|
303
|
+
const { theme } = this.deps;
|
|
304
|
+
const row = this.list.selectedRow;
|
|
305
|
+
if (row === undefined || hintOf(row.id) !== undefined)
|
|
306
|
+
return [];
|
|
307
|
+
const doc = settingDoc(row.id) ?? "";
|
|
308
|
+
const value = getConfigValue(this.snapshot, row.id);
|
|
309
|
+
const dynamic = value.value === undefined ? settingDynamicDefault(row.id) : undefined;
|
|
310
|
+
const text = dynamic === undefined ? doc : `${doc} · ${dynamic}`;
|
|
311
|
+
const spec = settingsRegistry().find((x) => x.key === row.id);
|
|
312
|
+
const blocked = spec === undefined ? undefined : blockedReason(spec, value, this.scope);
|
|
313
|
+
const lines = blocked === undefined ? [] : [theme.fg("warning", truncateToWidth(blocked, width))];
|
|
314
|
+
if (text !== "")
|
|
315
|
+
lines.push(...wrapTextWithAnsi(theme.fg("dim", text), width));
|
|
316
|
+
return lines.slice(0, 3);
|
|
317
|
+
}
|
|
318
|
+
invalidate() { }
|
|
319
|
+
}
|