@armadra/agent 0.5.0 → 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 -294
- 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 +28307 -13241
- 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.d.ts +58 -0
- package/dist/cli/choice-prompt.js +142 -0
- 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.d.ts +1 -1
- package/dist/cli/subcommands/models-cache-probe.js +26 -32
- 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 +2 -2
- package/dist/cli/subcommands/providers.js +48 -59
- 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 +17 -0
- package/dist/modes/commands-core.js +101 -61
- 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 +22 -1
- package/dist/modes/interactive/commands.js +93 -38
- 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.d.ts +49 -0
- package/dist/modes/interactive/confirm-dialog.js +120 -0
- 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 +25 -22
- package/dist/modes/interactive/key-dispatch.d.ts +18 -0
- package/dist/modes/interactive/key-dispatch.js +39 -13
- package/dist/modes/interactive/line/line-editor.js +2 -1
- package/dist/modes/interactive/line/line-mode.d.ts +2 -0
- package/dist/modes/interactive/line/line-mode.js +24 -4
- 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 +41 -29
- 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 +30 -0
- package/dist/permissions/bypass.js +45 -0
- 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 +6 -1
- package/dist/tui/keybindings.js +18 -6
- 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 +3 -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 +189 -21
- package/package.json +8 -3
|
@@ -10,32 +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";
|
|
20
|
+
import { THINKING_LEVELS } from "../../ai/thinking.js";
|
|
16
21
|
import { listSessions } from "../../cli/compose-store.js";
|
|
17
22
|
import { hideFakeProvider } from "../../cli/fake-visibility.js";
|
|
18
|
-
import {
|
|
23
|
+
import { autoLayerText, permissionModeLabel } from "../../permissions/modes.js";
|
|
19
24
|
import { BUILTIN_COMMANDS, parseSlash, runSlashCommand, } from "../commands-core.js";
|
|
20
25
|
import { contentText } from "./message-view.js";
|
|
21
26
|
import { cachePanel, permissionsPanel, sessionPanel } from "./panels.js";
|
|
22
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
|
+
}
|
|
23
37
|
export const INTERACTIVE_COMMANDS = [
|
|
24
|
-
|
|
25
|
-
|
|
38
|
+
command("tree", "treeDescription"),
|
|
39
|
+
command("permissions", "permissionsDescription"),
|
|
26
40
|
];
|
|
27
41
|
/** 补全与 /help 用的完整命令表。 */
|
|
28
42
|
export const ALL_COMMANDS = [...BUILTIN_COMMANDS, ...INTERACTIVE_COMMANDS];
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
+
};
|
|
39
82
|
function homeOf(ui) {
|
|
40
83
|
return ui.home !== undefined ? { home: ui.home } : {};
|
|
41
84
|
}
|
|
@@ -51,25 +94,23 @@ export function activeBranchIds(session) {
|
|
|
51
94
|
return new Set(session.entries.map((e) => e.id));
|
|
52
95
|
}
|
|
53
96
|
export function permissionsText(runtime, session) {
|
|
97
|
+
const m = msg().interactive.commands;
|
|
98
|
+
const p = msg().panels.permissions;
|
|
54
99
|
const rules = runtime.permission.rules;
|
|
55
100
|
const mode = session.state.permissionMode;
|
|
56
|
-
const order = mode === "auto"
|
|
57
|
-
? "deny 规则 → Hook deny → 危险命令确认 → 规则层(受保护路径、项目外写入、网络、删除类)→ Hook ask → allow 规则 / Hook allow / 本会话记忆 → 静态判定(只读、项目内写入、安全名单)→ 模型分类器 → 询问"
|
|
58
|
-
: mode === "allowlist"
|
|
59
|
-
? "deny 规则 → Hook deny → 危险命令(拒绝)→ 只读工具 / allow 规则 / Hook allow 放行 → 其余拒绝(从不询问)"
|
|
60
|
-
: "deny 规则 → Hook deny → 危险命令确认 → 权限模式 → allow 规则 / Hook allow / 本会话记忆 → 询问";
|
|
101
|
+
const order = mode === "auto" ? p.orderAuto : mode === "allowlist" ? p.orderAllowlist : p.orderDefault;
|
|
61
102
|
const lines = [
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
rules.length === 0 ?
|
|
103
|
+
m.permissionsMode(permissionModeLabel(mode), mode),
|
|
104
|
+
m.permissionsOrder(order),
|
|
105
|
+
rules.length === 0 ? m.rulesNone : m.rules(rules.length),
|
|
65
106
|
...rules.map((r) => ` ${r.effect === "deny" ? "deny " : "allow"} ${r.raw} [${r.source}]`),
|
|
66
107
|
];
|
|
67
108
|
const recent = runtime.permission.autoDecisions?.() ?? [];
|
|
68
109
|
if (recent.length > 0) {
|
|
69
|
-
lines.push(
|
|
110
|
+
lines.push(m.recentAuto(recent.length));
|
|
70
111
|
for (const d of recent) {
|
|
71
|
-
const cached = d.cached === true ?
|
|
72
|
-
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}`);
|
|
73
114
|
}
|
|
74
115
|
}
|
|
75
116
|
return lines.join("\n");
|
|
@@ -78,7 +119,7 @@ async function pickTree(ui, title) {
|
|
|
78
119
|
const session = ui.session();
|
|
79
120
|
const items = treeItems(session.entries, activeBranchIds(session), ui.now());
|
|
80
121
|
if (items.length === 0) {
|
|
81
|
-
ui.notice("info",
|
|
122
|
+
ui.notice("info", msg().interactive.commands.noUserMessages);
|
|
82
123
|
return undefined;
|
|
83
124
|
}
|
|
84
125
|
const active = [...items].reverse().find((i) => i.label.includes("● "));
|
|
@@ -94,14 +135,14 @@ async function pickTree(ui, title) {
|
|
|
94
135
|
async function treeCommand(ui) {
|
|
95
136
|
const session = ui.session();
|
|
96
137
|
if (!(session instanceof AgentSessionImpl)) {
|
|
97
|
-
ui.notice("warn",
|
|
138
|
+
ui.notice("warn", msg().interactive.commands.treeUnsupported);
|
|
98
139
|
return;
|
|
99
140
|
}
|
|
100
141
|
if (session.state.isStreaming) {
|
|
101
|
-
ui.notice("warn",
|
|
142
|
+
ui.notice("warn", msg().interactive.commands.treeBusy);
|
|
102
143
|
return;
|
|
103
144
|
}
|
|
104
|
-
const entry = await pickTree(ui,
|
|
145
|
+
const entry = await pickTree(ui, msg().interactive.commands.treeTitle);
|
|
105
146
|
if (entry === undefined)
|
|
106
147
|
return;
|
|
107
148
|
await session.navigate(entry.parentId);
|
|
@@ -110,12 +151,13 @@ async function treeCommand(ui) {
|
|
|
110
151
|
}
|
|
111
152
|
async function handlePick(what, ui) {
|
|
112
153
|
const session = ui.session();
|
|
154
|
+
const m = msg().interactive.commands;
|
|
113
155
|
switch (what) {
|
|
114
156
|
case "model": {
|
|
115
157
|
const current = session.state.model;
|
|
116
158
|
const ref = current === undefined ? undefined : `${current.provider}/${current.id}`;
|
|
117
159
|
const picked = await ui.pick({
|
|
118
|
-
title:
|
|
160
|
+
title: m.modelTitle,
|
|
119
161
|
items: await modelItems(ui.env === undefined
|
|
120
162
|
? ui.runtime.providers
|
|
121
163
|
: hideFakeProvider(ui.runtime.providers, ui.env)),
|
|
@@ -126,7 +168,7 @@ async function handlePick(what, ui) {
|
|
|
126
168
|
if (picked === undefined)
|
|
127
169
|
return;
|
|
128
170
|
await session.setModel(picked.value);
|
|
129
|
-
ui.notice("info",
|
|
171
|
+
ui.notice("info", m.modelSet(picked.value));
|
|
130
172
|
return;
|
|
131
173
|
}
|
|
132
174
|
case "session": {
|
|
@@ -136,26 +178,26 @@ async function handlePick(what, ui) {
|
|
|
136
178
|
});
|
|
137
179
|
const others = items.filter((i) => i.id !== session.state.sessionId);
|
|
138
180
|
if (others.length === 0) {
|
|
139
|
-
ui.notice("info",
|
|
181
|
+
ui.notice("info", m.noOtherSessions);
|
|
140
182
|
return;
|
|
141
183
|
}
|
|
142
184
|
const picked = await ui.pick({
|
|
143
|
-
title:
|
|
185
|
+
title: m.resumeTitle,
|
|
144
186
|
items: sessionItems(others, ui.now()),
|
|
145
187
|
filterable: true,
|
|
146
188
|
});
|
|
147
189
|
if (picked === undefined)
|
|
148
190
|
return;
|
|
149
191
|
const next = await ui.switchSession({ kind: "resume", id: picked.value });
|
|
150
|
-
ui.notice("info",
|
|
192
|
+
ui.notice("info", m.resumed(next.state.sessionId.slice(0, 8)));
|
|
151
193
|
return;
|
|
152
194
|
}
|
|
153
195
|
case "tree": {
|
|
154
|
-
const entry = await pickTree(ui,
|
|
196
|
+
const entry = await pickTree(ui, m.forkTitle);
|
|
155
197
|
if (entry === undefined)
|
|
156
198
|
return;
|
|
157
199
|
const next = await ui.switchSession(entry.parentId === null ? { kind: "new" } : { kind: "fork", entryId: entry.parentId });
|
|
158
|
-
ui.notice("info",
|
|
200
|
+
ui.notice("info", m.forked(next.state.sessionId.slice(0, 8)));
|
|
159
201
|
ui.setEditorText(userEntryText(entry) ?? "");
|
|
160
202
|
return;
|
|
161
203
|
}
|
|
@@ -164,8 +206,13 @@ async function handlePick(what, ui) {
|
|
|
164
206
|
if (picked === undefined)
|
|
165
207
|
return;
|
|
166
208
|
const mode = picked.value;
|
|
209
|
+
const current = session.state.permissionMode;
|
|
210
|
+
if (mode !== current && ui.confirmMode !== undefined && !(await ui.confirmMode(mode))) {
|
|
211
|
+
ui.notice("info", m.modeCancelled(permissionModeLabel(current)));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
167
214
|
session.setPermissionMode(mode);
|
|
168
|
-
ui.notice("info",
|
|
215
|
+
ui.notice("info", m.modeSet(permissionModeLabel(mode)));
|
|
169
216
|
return;
|
|
170
217
|
}
|
|
171
218
|
case "thinking": {
|
|
@@ -173,9 +220,13 @@ async function handlePick(what, ui) {
|
|
|
173
220
|
const found = model === undefined ? undefined : ui.runtime.providers.findModel(formatModelRef(model));
|
|
174
221
|
const reasoning = found?.ok === true ? found.model.reasoning : true;
|
|
175
222
|
const picked = await ui.pick({
|
|
176
|
-
title:
|
|
223
|
+
title: m.thinkingTitle,
|
|
177
224
|
items: thinkingItems(reasoning),
|
|
178
225
|
selected: session.state.thinkingLevel,
|
|
226
|
+
currentValue: session.state.thinkingLevel,
|
|
227
|
+
filterable: false,
|
|
228
|
+
numberKeys: true,
|
|
229
|
+
footer: m.numberFooter("↑↓", THINKING_LEVELS.length),
|
|
179
230
|
});
|
|
180
231
|
if (picked !== undefined)
|
|
181
232
|
session.setThinkingLevel(picked.value);
|
|
@@ -191,6 +242,9 @@ export async function runInteractiveCommand(line, ui) {
|
|
|
191
242
|
try {
|
|
192
243
|
if (ui.extra !== undefined && (await ui.extra(parsed.name, parsed.args)))
|
|
193
244
|
return true;
|
|
245
|
+
const panel = PANEL_COMMANDS[parsed.name];
|
|
246
|
+
if (panel !== undefined && (await panel(ui, parsed.args)))
|
|
247
|
+
return true;
|
|
194
248
|
if (parsed.name === "tree") {
|
|
195
249
|
await treeCommand(ui);
|
|
196
250
|
return true;
|
|
@@ -222,6 +276,7 @@ export async function runInteractiveCommand(line, ui) {
|
|
|
222
276
|
runtime: ui.runtime,
|
|
223
277
|
session: () => ui.session(),
|
|
224
278
|
switchSession: (request) => ui.switchSession(request),
|
|
279
|
+
...(ui.confirmMode !== undefined ? { confirmPermissionMode: ui.confirmMode } : {}),
|
|
225
280
|
});
|
|
226
281
|
if (result === undefined)
|
|
227
282
|
return false;
|
|
@@ -240,7 +295,7 @@ export async function runInteractiveCommand(line, ui) {
|
|
|
240
295
|
ui.reload();
|
|
241
296
|
if (parsed.name === "help") {
|
|
242
297
|
const extra = INTERACTIVE_COMMANDS.map((c) => `/${c.name} ${c.description}`);
|
|
243
|
-
ui.notice("info", [result.message ?? "", ...extra, "",
|
|
298
|
+
ui.notice("info", [result.message ?? "", ...extra, "", keyHints()].join("\n"));
|
|
244
299
|
}
|
|
245
300
|
else if (result.message !== undefined)
|
|
246
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
|
+
}
|