@ai-zen/cli 1.0.0-alpha.2 → 1.0.0-alpha.22
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 +514 -0
- package/README.md +136 -39
- package/README.zh.md +136 -39
- package/dist/agent-creator.d.ts +11 -0
- package/dist/agent-creator.d.ts.map +1 -1
- package/dist/agent-creator.js +52 -21
- package/dist/agent-creator.js.map +1 -1
- package/dist/agent-switch.d.ts +61 -0
- package/dist/agent-switch.d.ts.map +1 -0
- package/dist/agent-switch.js +83 -0
- package/dist/agent-switch.js.map +1 -0
- package/dist/agents-store.d.ts +6 -1
- package/dist/agents-store.d.ts.map +1 -1
- package/dist/agents-store.js +46 -4
- package/dist/agents-store.js.map +1 -1
- package/dist/auto-migrate-confirm.d.ts +43 -0
- package/dist/auto-migrate-confirm.d.ts.map +1 -0
- package/dist/auto-migrate-confirm.js +47 -0
- package/dist/auto-migrate-confirm.js.map +1 -0
- package/dist/config-editor.d.ts +13 -5
- package/dist/config-editor.d.ts.map +1 -1
- package/dist/config-editor.js +36 -27
- package/dist/config-editor.js.map +1 -1
- package/dist/config.d.ts +24 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +55 -15
- package/dist/config.js.map +1 -1
- package/dist/conversation-commands/registry.d.ts +123 -4
- package/dist/conversation-commands/registry.d.ts.map +1 -1
- package/dist/conversation-commands/registry.js +92 -14
- package/dist/conversation-commands/registry.js.map +1 -1
- package/dist/conversation-persist-plugin.d.ts +0 -1
- package/dist/conversation-persist-plugin.d.ts.map +1 -1
- package/dist/conversation-persist-plugin.js +7 -16
- package/dist/conversation-persist-plugin.js.map +1 -1
- package/dist/conversation-repository.d.ts +71 -13
- package/dist/conversation-repository.d.ts.map +1 -1
- package/dist/conversation-repository.js +81 -24
- package/dist/conversation-repository.js.map +1 -1
- package/dist/cwd-tracker-plugin.d.ts.map +1 -1
- package/dist/cwd-tracker-plugin.js +6 -2
- package/dist/cwd-tracker-plugin.js.map +1 -1
- package/dist/format-time.d.ts +17 -6
- package/dist/format-time.d.ts.map +1 -1
- package/dist/format-time.js +32 -14
- package/dist/format-time.js.map +1 -1
- package/dist/handoff-service.d.ts +19 -0
- package/dist/handoff-service.d.ts.map +1 -0
- package/dist/handoff-service.js +89 -0
- package/dist/handoff-service.js.map +1 -0
- package/dist/hook.d.ts.map +1 -1
- package/dist/hook.js +69 -15
- package/dist/hook.js.map +1 -1
- package/dist/i18n/en-wizard.d.ts +8 -0
- package/dist/i18n/en-wizard.d.ts.map +1 -0
- package/dist/i18n/en-wizard.js +290 -0
- package/dist/i18n/en-wizard.js.map +1 -0
- package/dist/i18n/en.d.ts +7 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +224 -0
- package/dist/i18n/en.js.map +1 -0
- package/dist/i18n/index.d.ts +57 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +109 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/preferences.d.ts +34 -0
- package/dist/i18n/preferences.d.ts.map +1 -0
- package/dist/i18n/preferences.js +69 -0
- package/dist/i18n/preferences.js.map +1 -0
- package/dist/i18n/zh-wizard.d.ts +11 -0
- package/dist/i18n/zh-wizard.d.ts.map +1 -0
- package/dist/i18n/zh-wizard.js +293 -0
- package/dist/i18n/zh-wizard.js.map +1 -0
- package/dist/i18n/zh.d.ts +10 -0
- package/dist/i18n/zh.d.ts.map +1 -0
- package/dist/i18n/zh.js +228 -0
- package/dist/i18n/zh.js.map +1 -0
- package/dist/index.js +61 -25
- package/dist/index.js.map +1 -1
- package/dist/mcp-command.d.ts +32 -0
- package/dist/mcp-command.d.ts.map +1 -0
- package/dist/mcp-command.js +37 -0
- package/dist/mcp-command.js.map +1 -0
- package/dist/mode.d.ts +31 -4
- package/dist/mode.d.ts.map +1 -1
- package/dist/mode.js +42 -7
- package/dist/mode.js.map +1 -1
- package/dist/model-switch.d.ts +58 -0
- package/dist/model-switch.d.ts.map +1 -0
- package/dist/model-switch.js +62 -0
- package/dist/model-switch.js.map +1 -0
- package/dist/reasoning-effort.d.ts +96 -0
- package/dist/reasoning-effort.d.ts.map +1 -0
- package/dist/reasoning-effort.js +164 -0
- package/dist/reasoning-effort.js.map +1 -0
- package/dist/reasoning-presets.d.ts +34 -0
- package/dist/reasoning-presets.d.ts.map +1 -0
- package/dist/reasoning-presets.js +85 -0
- package/dist/reasoning-presets.js.map +1 -0
- package/dist/sdk-log.d.ts +55 -0
- package/dist/sdk-log.d.ts.map +1 -0
- package/dist/sdk-log.js +91 -0
- package/dist/sdk-log.js.map +1 -0
- package/dist/session-pointer.d.ts +22 -9
- package/dist/session-pointer.d.ts.map +1 -1
- package/dist/session-pointer.js +55 -23
- package/dist/session-pointer.js.map +1 -1
- package/dist/stdio-runner.d.ts.map +1 -1
- package/dist/stdio-runner.js +32 -30
- package/dist/stdio-runner.js.map +1 -1
- package/dist/sub-agent-guard-plugin.d.ts +5 -5
- package/dist/sub-agent-guard-plugin.d.ts.map +1 -1
- package/dist/sub-agent-guard-plugin.js +7 -7
- package/dist/sub-agent-guard-plugin.js.map +1 -1
- package/dist/tui/chat-session.d.ts +110 -59
- package/dist/tui/chat-session.d.ts.map +1 -1
- package/dist/tui/chat-session.js +268 -120
- package/dist/tui/chat-session.js.map +1 -1
- package/dist/tui/components.d.ts +48 -2
- package/dist/tui/components.d.ts.map +1 -1
- package/dist/tui/components.js +96 -3
- package/dist/tui/components.js.map +1 -1
- package/dist/tui/config-wizard/detail.d.ts +12 -6
- package/dist/tui/config-wizard/detail.d.ts.map +1 -1
- package/dist/tui/config-wizard/detail.js +33 -32
- package/dist/tui/config-wizard/detail.js.map +1 -1
- package/dist/tui/config-wizard/engine.d.ts.map +1 -1
- package/dist/tui/config-wizard/engine.js +27 -21
- package/dist/tui/config-wizard/engine.js.map +1 -1
- package/dist/tui/config-wizard/fields.d.ts +3 -1
- package/dist/tui/config-wizard/fields.d.ts.map +1 -1
- package/dist/tui/config-wizard/fields.js +216 -152
- package/dist/tui/config-wizard/fields.js.map +1 -1
- package/dist/tui/config-wizard/kv-editor.d.ts.map +1 -1
- package/dist/tui/config-wizard/kv-editor.js +9 -5
- package/dist/tui/config-wizard/kv-editor.js.map +1 -1
- package/dist/tui/config-wizard/list.d.ts.map +1 -1
- package/dist/tui/config-wizard/list.js +2 -1
- package/dist/tui/config-wizard/list.js.map +1 -1
- package/dist/tui/config-wizard/mcp-helpers.d.ts.map +1 -1
- package/dist/tui/config-wizard/mcp-helpers.js +4 -3
- package/dist/tui/config-wizard/mcp-helpers.js.map +1 -1
- package/dist/tui/config-wizard/menu.d.ts.map +1 -1
- package/dist/tui/config-wizard/menu.js +33 -21
- package/dist/tui/config-wizard/menu.js.map +1 -1
- package/dist/tui/config-wizard/prompt-editor.d.ts.map +1 -1
- package/dist/tui/config-wizard/prompt-editor.js +4 -3
- package/dist/tui/config-wizard/prompt-editor.js.map +1 -1
- package/dist/tui/config-wizard/prompt.d.ts.map +1 -1
- package/dist/tui/config-wizard/prompt.js +8 -7
- package/dist/tui/config-wizard/prompt.js.map +1 -1
- package/dist/tui/config-wizard/step-agents.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-agents.js +29 -20
- package/dist/tui/config-wizard/step-agents.js.map +1 -1
- package/dist/tui/config-wizard/step-defaults.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-defaults.js +24 -27
- package/dist/tui/config-wizard/step-defaults.js.map +1 -1
- package/dist/tui/config-wizard/step-endpoints.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-endpoints.js +16 -6
- package/dist/tui/config-wizard/step-endpoints.js.map +1 -1
- package/dist/tui/config-wizard/step-mcp.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-mcp.js +17 -19
- package/dist/tui/config-wizard/step-mcp.js.map +1 -1
- package/dist/tui/config-wizard/step-models.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-models.js +41 -26
- package/dist/tui/config-wizard/step-models.js.map +1 -1
- package/dist/tui/config-wizard/step-new-endpoint.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-new-endpoint.js +16 -12
- package/dist/tui/config-wizard/step-new-endpoint.js.map +1 -1
- package/dist/tui/config-wizard/step-simple.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-simple.js +18 -17
- package/dist/tui/config-wizard/step-simple.js.map +1 -1
- package/dist/tui/config-wizard/types.d.ts +18 -3
- package/dist/tui/config-wizard/types.d.ts.map +1 -1
- package/dist/tui/config-wizard/wizard.d.ts.map +1 -1
- package/dist/tui/config-wizard/wizard.js +4 -3
- package/dist/tui/config-wizard/wizard.js.map +1 -1
- package/dist/tui/highlight.d.ts +3 -2
- package/dist/tui/highlight.d.ts.map +1 -1
- package/dist/tui/highlight.js +3 -2
- package/dist/tui/highlight.js.map +1 -1
- package/dist/tui/index.d.ts +18 -1
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +107 -49
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/input-editing.d.ts +52 -0
- package/dist/tui/input-editing.d.ts.map +1 -0
- package/dist/tui/input-editing.js +91 -0
- package/dist/tui/input-editing.js.map +1 -0
- package/dist/tui/key-debug.d.ts +62 -0
- package/dist/tui/key-debug.d.ts.map +1 -0
- package/dist/tui/key-debug.js +92 -0
- package/dist/tui/key-debug.js.map +1 -0
- package/dist/tui/mouse.d.ts +55 -0
- package/dist/tui/mouse.d.ts.map +1 -0
- package/dist/tui/mouse.js +74 -0
- package/dist/tui/mouse.js.map +1 -0
- package/dist/tui/phase.d.ts +64 -0
- package/dist/tui/phase.d.ts.map +1 -0
- package/dist/tui/phase.js +112 -0
- package/dist/tui/phase.js.map +1 -0
- package/dist/tui/screens/chat-commands.d.ts +64 -3
- package/dist/tui/screens/chat-commands.d.ts.map +1 -1
- package/dist/tui/screens/chat-commands.js +483 -157
- package/dist/tui/screens/chat-commands.js.map +1 -1
- package/dist/tui/screens/chat.d.ts +10 -5
- package/dist/tui/screens/chat.d.ts.map +1 -1
- package/dist/tui/screens/chat.js +464 -301
- package/dist/tui/screens/chat.js.map +1 -1
- package/dist/tui/screens/common-keys.d.ts +36 -0
- package/dist/tui/screens/common-keys.d.ts.map +1 -0
- package/dist/tui/screens/common-keys.js +67 -0
- package/dist/tui/screens/common-keys.js.map +1 -0
- package/dist/tui/screens/frame-buffer.d.ts +47 -0
- package/dist/tui/screens/frame-buffer.d.ts.map +1 -0
- package/dist/tui/screens/frame-buffer.js +78 -0
- package/dist/tui/screens/frame-buffer.js.map +1 -0
- package/dist/tui/screens/handoff-choice.d.ts +37 -0
- package/dist/tui/screens/handoff-choice.d.ts.map +1 -0
- package/dist/tui/screens/handoff-choice.js +42 -0
- package/dist/tui/screens/handoff-choice.js.map +1 -0
- package/dist/tui/screens/index.d.ts +8 -8
- package/dist/tui/screens/index.d.ts.map +1 -1
- package/dist/tui/screens/index.js +7 -7
- package/dist/tui/screens/index.js.map +1 -1
- package/dist/tui/screens/input-row.d.ts +64 -0
- package/dist/tui/screens/input-row.d.ts.map +1 -0
- package/dist/tui/screens/input-row.js +67 -0
- package/dist/tui/screens/input-row.js.map +1 -0
- package/dist/tui/screens/input.d.ts +46 -1
- package/dist/tui/screens/input.d.ts.map +1 -1
- package/dist/tui/screens/input.js +83 -8
- package/dist/tui/screens/input.js.map +1 -1
- package/dist/tui/screens/layout.d.ts +21 -3
- package/dist/tui/screens/layout.d.ts.map +1 -1
- package/dist/tui/screens/layout.js +20 -9
- package/dist/tui/screens/layout.js.map +1 -1
- package/dist/tui/screens/lines.d.ts +12 -41
- package/dist/tui/screens/lines.d.ts.map +1 -1
- package/dist/tui/screens/lines.js +90 -98
- package/dist/tui/screens/lines.js.map +1 -1
- package/dist/tui/screens/messages-view.d.ts +10 -0
- package/dist/tui/screens/messages-view.d.ts.map +1 -0
- package/dist/tui/screens/messages-view.js +116 -0
- package/dist/tui/screens/messages-view.js.map +1 -0
- package/dist/tui/screens/notice-bar.d.ts +5 -0
- package/dist/tui/screens/notice-bar.d.ts.map +1 -0
- package/dist/tui/screens/notice-bar.js +15 -0
- package/dist/tui/screens/notice-bar.js.map +1 -0
- package/dist/tui/screens/notice.d.ts +64 -0
- package/dist/tui/screens/notice.d.ts.map +1 -0
- package/dist/tui/screens/notice.js +106 -0
- package/dist/tui/screens/notice.js.map +1 -0
- package/dist/tui/screens/overlays.d.ts.map +1 -1
- package/dist/tui/screens/overlays.js +3 -2
- package/dist/tui/screens/overlays.js.map +1 -1
- package/dist/tui/screens/render.d.ts +2 -4
- package/dist/tui/screens/render.d.ts.map +1 -1
- package/dist/tui/screens/render.js +7 -43
- package/dist/tui/screens/render.js.map +1 -1
- package/dist/tui/screens/splash.d.ts +1 -1
- package/dist/tui/screens/splash.d.ts.map +1 -1
- package/dist/tui/screens/splash.js +12 -3
- package/dist/tui/screens/splash.js.map +1 -1
- package/dist/tui/screens/use-chat-viewport.d.ts +22 -0
- package/dist/tui/screens/use-chat-viewport.d.ts.map +1 -0
- package/dist/tui/screens/use-chat-viewport.js +50 -0
- package/dist/tui/screens/use-chat-viewport.js.map +1 -0
- package/dist/tui/screens/use-input-editing.d.ts +44 -0
- package/dist/tui/screens/use-input-editing.d.ts.map +1 -0
- package/dist/tui/screens/use-input-editing.js +192 -0
- package/dist/tui/screens/use-input-editing.js.map +1 -0
- package/dist/tui/screens/use-overlays.d.ts +25 -0
- package/dist/tui/screens/use-overlays.d.ts.map +1 -0
- package/dist/tui/screens/use-overlays.js +32 -0
- package/dist/tui/screens/use-overlays.js.map +1 -0
- package/dist/tui/screens/use-wizard-host.d.ts +35 -0
- package/dist/tui/screens/use-wizard-host.d.ts.map +1 -0
- package/dist/tui/screens/use-wizard-host.js +103 -0
- package/dist/tui/screens/use-wizard-host.js.map +1 -0
- package/dist/tui/status-segments.d.ts +37 -0
- package/dist/tui/status-segments.d.ts.map +1 -0
- package/dist/tui/status-segments.js +166 -0
- package/dist/tui/status-segments.js.map +1 -0
- package/dist/tui/text.d.ts +20 -7
- package/dist/tui/text.d.ts.map +1 -1
- package/dist/tui/text.js +111 -37
- package/dist/tui/text.js.map +1 -1
- package/dist/tui/theme.d.ts +7 -1
- package/dist/tui/theme.d.ts.map +1 -1
- package/dist/tui/theme.js +15 -3
- package/dist/tui/theme.js.map +1 -1
- package/dist/tui/token-rate.d.ts +83 -0
- package/dist/tui/token-rate.d.ts.map +1 -0
- package/dist/tui/token-rate.js +195 -0
- package/dist/tui/token-rate.js.map +1 -0
- package/dist/types.d.ts +7 -8
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -3
- package/dist/auto-migrate-confirm-plugin.d.ts +0 -58
- package/dist/auto-migrate-confirm-plugin.d.ts.map +0 -1
- package/dist/auto-migrate-confirm-plugin.js +0 -61
- package/dist/auto-migrate-confirm-plugin.js.map +0 -1
- package/dist/migration-service.d.ts +0 -15
- package/dist/migration-service.d.ts.map +0 -1
- package/dist/migration-service.js +0 -59
- package/dist/migration-service.js.map +0 -1
- package/dist/tui/screens/state.d.ts +0 -13
- package/dist/tui/screens/state.d.ts.map +0 -1
- package/dist/tui/screens/state.js +0 -75
- package/dist/tui/screens/state.js.map +0 -1
package/dist/tui/index.js
CHANGED
|
@@ -11,12 +11,17 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
11
11
|
*/
|
|
12
12
|
import { render, useApp } from "ink";
|
|
13
13
|
import { AgentRepository } from "@ai-zen/agents-sdk";
|
|
14
|
-
import { AGENTS_DIR, readConfig, saveConfig } from "../config.js";
|
|
14
|
+
import { AGENTS_DIR, ensureConfigDir, readConfig, saveConfig } from "../config.js";
|
|
15
15
|
import { resolveCredential } from "../config-editor.js";
|
|
16
16
|
import { ConfigWizard } from "./config-wizard/index.js";
|
|
17
17
|
import { conversationRepository } from "../conversation-repository.js";
|
|
18
18
|
import { readLastSession } from "../session-pointer.js";
|
|
19
19
|
import { Splash, Chat } from "./screens/index.js";
|
|
20
|
+
import { keyDebugStart } from "./key-debug.js";
|
|
21
|
+
import { disableMouseTracking, enableMouseTracking } from "./mouse.js";
|
|
22
|
+
import { installSdkLogger } from "../sdk-log.js";
|
|
23
|
+
import { initLocale } from "../i18n/preferences.js";
|
|
24
|
+
import { t } from "../i18n/index.js";
|
|
20
25
|
/** ANSI:清屏 + 光标归位 + 显示光标(屏幕间切换时从头绘制,避免行偏移) */
|
|
21
26
|
const CLEAR_SCREEN = "\x1b[2J\x1b[H\x1b[?25h";
|
|
22
27
|
/**
|
|
@@ -27,6 +32,13 @@ const CLEAR_SCREEN = "\x1b[2J\x1b[H\x1b[?25h";
|
|
|
27
32
|
* 保证退出即干净交还 shell(Windows 下 TTY 写为异步,用回调 + 兜底定时器双保险)。
|
|
28
33
|
*/
|
|
29
34
|
function farewell() {
|
|
35
|
+
// 先关鼠标上报:不关的话,回到 shell 后每次滚轮都会在提示符前吐一串 `^[[<64;…M`
|
|
36
|
+
try {
|
|
37
|
+
disableMouseTracking();
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
/* ignore */
|
|
41
|
+
}
|
|
30
42
|
try {
|
|
31
43
|
process.stdin.setRawMode?.(false);
|
|
32
44
|
}
|
|
@@ -40,7 +52,7 @@ function farewell() {
|
|
|
40
52
|
/* ignore */
|
|
41
53
|
}
|
|
42
54
|
const finish = () => process.exit(0);
|
|
43
|
-
process.stdout.write(
|
|
55
|
+
process.stdout.write(`\x1b[?25h${t("app.goodbye")}`, finish);
|
|
44
56
|
setTimeout(finish, 250);
|
|
45
57
|
}
|
|
46
58
|
/** 当前活动的 Ink 实例(用于信号处理时兜底卸载) */
|
|
@@ -62,13 +74,20 @@ let activeInstance = null;
|
|
|
62
74
|
export const RENDER_OPTIONS = {
|
|
63
75
|
exitOnCtrlC: false,
|
|
64
76
|
alternateScreen: false,
|
|
77
|
+
// 只重绘「变化的行」,而不是每帧擦除 + 重写整屏(Ink 默认 false,走 createStandard)。
|
|
78
|
+
// 流式高频下把每帧输出量从 O(屏幕大小) 降到 O(变化量),显著减轻 SSH 带宽 / 背压压力。
|
|
79
|
+
incrementalRendering: true,
|
|
65
80
|
kittyKeyboard: { mode: "disabled" },
|
|
66
81
|
};
|
|
67
82
|
function mount(node) {
|
|
68
83
|
process.stdout.write(CLEAR_SCREEN);
|
|
84
|
+
// 鼠标上报随实例生命周期开关:本 TUI 会反复挂载(启动屏 → 对话 → 配置向导),
|
|
85
|
+
// 每个实例结束时都关掉,避免「已卸载却仍在收鼠标事件」。
|
|
86
|
+
enableMouseTracking();
|
|
69
87
|
const instance = render(node, RENDER_OPTIONS);
|
|
70
88
|
activeInstance = instance;
|
|
71
89
|
return instance.waitUntilExit().finally(() => {
|
|
90
|
+
disableMouseTracking();
|
|
72
91
|
if (activeInstance === instance)
|
|
73
92
|
activeInstance = null;
|
|
74
93
|
});
|
|
@@ -97,37 +116,48 @@ async function ensureCredential(modelId) {
|
|
|
97
116
|
const result = (await mount(_jsx(CredentialGate, { config: config, endpointId: credential.endpointId })));
|
|
98
117
|
return Boolean(result?.dirty);
|
|
99
118
|
}
|
|
100
|
-
|
|
119
|
+
/**
|
|
120
|
+
* 解析某个 Agent 绑定模型(**唯一真相源**)。
|
|
121
|
+
*
|
|
122
|
+
* 配置层已无 `defaultModel`、会话存档也不再记模型,所以这里缺失就直接报错,
|
|
123
|
+
* 绝不做任何隐式回退——回退正是「状态栏显示 A、实发请求 B」的根源。
|
|
124
|
+
*/
|
|
125
|
+
async function modelOf(agentId) {
|
|
126
|
+
const id = agentId || "default";
|
|
127
|
+
// 先跑一次配置初始化:bootstrap 会为缺 modelId 的历史 Agent 补上并落盘。
|
|
128
|
+
// 不做这一步就会读到“未绑定模型”的旧定义,把一次正常的升级变成启动错误。
|
|
101
129
|
const config = await readConfig();
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
const agentId = config.defaultAgent ?? agents[0]?.id;
|
|
105
|
-
let modelId;
|
|
106
|
-
if (agentId) {
|
|
107
|
-
const agent = await repository.read(agentId);
|
|
108
|
-
modelId = agent?.modelId;
|
|
109
|
-
}
|
|
110
|
-
modelId = modelId ?? config.defaultModel ?? config.models[0]?.id;
|
|
130
|
+
const agent = await new AgentRepository(AGENTS_DIR).read(id);
|
|
131
|
+
const modelId = agent?.modelId;
|
|
111
132
|
if (!modelId) {
|
|
112
|
-
throw new Error("
|
|
133
|
+
throw new Error(t("app.agentNoModel", { id }));
|
|
134
|
+
}
|
|
135
|
+
if (!config.models.some((model) => model.id === modelId)) {
|
|
136
|
+
throw new Error(t("app.agentModelGone", { id, model: modelId }));
|
|
113
137
|
}
|
|
114
|
-
return
|
|
138
|
+
return modelId;
|
|
115
139
|
}
|
|
116
|
-
|
|
117
|
-
async function modelExists(modelId) {
|
|
140
|
+
async function resolveTarget() {
|
|
118
141
|
const config = await readConfig();
|
|
119
|
-
|
|
142
|
+
const repository = new AgentRepository(AGENTS_DIR);
|
|
143
|
+
const agents = await repository.list();
|
|
144
|
+
const agentId = config.defaultAgent ?? agents[0]?.id;
|
|
145
|
+
if (!agentId)
|
|
146
|
+
throw new Error(t("app.noAgents"));
|
|
147
|
+
// 校验模型可用(缺失时抛出可读错误,交给启动屏展示)
|
|
148
|
+
await modelOf(agentId);
|
|
149
|
+
return { agentId };
|
|
120
150
|
}
|
|
121
151
|
/** 读取「上一轮会话 id」指针,续接该会话(指针缺失或会话不存在时返回 null) */
|
|
122
152
|
async function resumeLastSession() {
|
|
123
|
-
const pointer = await readLastSession();
|
|
153
|
+
const pointer = await readLastSession(process.cwd());
|
|
124
154
|
if (!pointer)
|
|
125
155
|
return null;
|
|
126
156
|
const conversation = await conversationRepository.read(pointer.id);
|
|
127
157
|
if (!conversation)
|
|
128
158
|
return null;
|
|
159
|
+
// 模型由 Agent 定义决定(旧存档里的 modelId 一律忽略)
|
|
129
160
|
return targetFromMessages({
|
|
130
|
-
modelId: conversation.modelId,
|
|
131
161
|
agentId: conversation.agentId,
|
|
132
162
|
messages: conversation.messages,
|
|
133
163
|
conversationId: conversation.id,
|
|
@@ -137,7 +167,6 @@ async function resumeLastSession() {
|
|
|
137
167
|
/** 从已保存对话构造对话目标 */
|
|
138
168
|
function targetFromMessages(input) {
|
|
139
169
|
return {
|
|
140
|
-
modelId: input.modelId,
|
|
141
170
|
agentId: input.agentId,
|
|
142
171
|
messages: input.messages,
|
|
143
172
|
conversationId: input.conversationId,
|
|
@@ -149,65 +178,77 @@ function targetFromMessages(input) {
|
|
|
149
178
|
* 运行对话屏;`/clear`、`/back`、`/load` 通过重挂载(新 Ink 实例 + 原生清屏)实现,
|
|
150
179
|
* 其余退出(退出)直接结束。
|
|
151
180
|
*/
|
|
152
|
-
async function runChat(target) {
|
|
181
|
+
async function runChat(target, options = {}) {
|
|
153
182
|
let current = target;
|
|
154
183
|
let messages = target.messages;
|
|
155
184
|
let initialInput;
|
|
156
|
-
|
|
185
|
+
/** 重挂载后要展示一次的系统提示(如 `/model` 的写入结果,旧实例已卸载,只能传给新屏) */
|
|
186
|
+
let initialNotice;
|
|
187
|
+
/**
|
|
188
|
+
* shell 钩子(`ai --tui --new "$@"`)带来的首条消息。
|
|
189
|
+
* **只发一次**:挂载后置空,否则重挂载(切模型 / `/lang` 等)会反复重发。
|
|
190
|
+
*/
|
|
191
|
+
let initialPrompt = options.initialPrompt;
|
|
157
192
|
while (true) {
|
|
158
|
-
const result = await mount(_jsx(Chat, {
|
|
193
|
+
const result = await mount(_jsx(Chat, { agentId: current.agentId, messages: messages, conversationId: current.conversationId, conversationName: current.conversationName, initialInput: initialInput, initialNotice: initialNotice, initialPrompt: initialPrompt }));
|
|
194
|
+
initialPrompt = undefined;
|
|
159
195
|
if (result && typeof result === "object") {
|
|
160
196
|
const value = result;
|
|
161
197
|
if (value.load) {
|
|
162
|
-
// /load
|
|
198
|
+
// /load:整体切换到另一段已保存对话(含 Agent),重挂载对话屏
|
|
163
199
|
current = {
|
|
164
|
-
modelId: value.load.modelId,
|
|
165
200
|
agentId: value.load.agentId,
|
|
166
201
|
conversationId: value.load.conversationId,
|
|
167
202
|
conversationName: value.load.conversationName,
|
|
168
203
|
};
|
|
169
204
|
messages = value.load.messages;
|
|
170
205
|
initialInput = undefined;
|
|
171
|
-
|
|
206
|
+
initialNotice = undefined;
|
|
172
207
|
continue;
|
|
173
208
|
}
|
|
174
209
|
if (value.restart) {
|
|
175
210
|
messages = value.messages ?? messages;
|
|
176
211
|
initialInput = value.initialInput;
|
|
177
|
-
|
|
212
|
+
initialNotice = value.notice;
|
|
213
|
+
// `/model` 换模型:Agent 定义里的 modelId 已变,需重建会话(阈值、工具集跟着变)
|
|
214
|
+
if (value.agentId) {
|
|
215
|
+
current = { ...current, agentId: value.agentId };
|
|
216
|
+
}
|
|
178
217
|
continue;
|
|
179
218
|
}
|
|
180
219
|
}
|
|
181
220
|
return;
|
|
182
221
|
}
|
|
183
222
|
}
|
|
184
|
-
|
|
185
|
-
export async function runTui() {
|
|
223
|
+
export async function runTui(options = {}) {
|
|
186
224
|
const uninstallSignals = installSignalSafety();
|
|
225
|
+
// 接管 SDK 全局日志:关掉库自带的 console 兜底。必须在任何 Ink 渲染之前——
|
|
226
|
+
// SDK 的 logger 默认是 console.log,直写 stdout 会把界面撕坏
|
|
227
|
+
// (`load_mcp` 甚至会把整份 manifest 打出来)。AIZ_DEBUG_SDK=1 可改为落盘。
|
|
228
|
+
installSdkLogger();
|
|
229
|
+
// 界面语言必须在任何渲染之前定下来:首次启动按系统语言识别并落盘,
|
|
230
|
+
// 之后以 preferences.json 为准(`/lang` 可随时改)。
|
|
231
|
+
await initLocale();
|
|
232
|
+
// 按键诊断:AIZ_DEBUG_KEYS=1 时把终端环境 + 每次按键写进日志(默认关闭,零开销)
|
|
233
|
+
keyDebugStart();
|
|
187
234
|
try {
|
|
235
|
+
// 配置初始化必须早于任何 Agent 读取:
|
|
236
|
+
// - 目录 / config.json / 默认 Agent / 出厂策略表都在这里释放;
|
|
237
|
+
// - 历史 Agent 缺 `modelId` 的补齐也在这里(`ensureDefaultAgent`)。
|
|
238
|
+
// 而「续接上一轮会话」这条路径不会经过 `resolveTarget()`,少了这一步,
|
|
239
|
+
// 升级后第一次启动会直接报「Agent 未绑定模型」。
|
|
240
|
+
await ensureConfigDir();
|
|
188
241
|
// 启动界面(一次性)
|
|
189
242
|
await mount(_jsx(Splash, {}));
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
// 避免卡在「模型不存在」的报错屏而无处可走
|
|
195
|
-
const fallback = await resolveTarget().catch(() => null);
|
|
196
|
-
target = fallback
|
|
197
|
-
? {
|
|
198
|
-
...fallback,
|
|
199
|
-
messages: target.messages,
|
|
200
|
-
conversationId: target.conversationId,
|
|
201
|
-
conversationName: target.conversationName,
|
|
202
|
-
}
|
|
203
|
-
: null;
|
|
204
|
-
}
|
|
205
|
-
else if (!target) {
|
|
243
|
+
// 直达对话:优先续接「上一轮会话」,否则新建对话。
|
|
244
|
+
// `--new` 跳过续接(不读 last-session.json 指针),直接走新建路径
|
|
245
|
+
let target = options.newSession ? null : await resumeLastSession();
|
|
246
|
+
if (!target) {
|
|
206
247
|
try {
|
|
207
248
|
target = await resolveTarget();
|
|
208
249
|
}
|
|
209
250
|
catch (error) {
|
|
210
|
-
process.stderr.write(`\n
|
|
251
|
+
process.stderr.write(`\n${t("app.bootFailed", { error: error?.message ?? error })}\n`);
|
|
211
252
|
}
|
|
212
253
|
}
|
|
213
254
|
if (!target) {
|
|
@@ -215,11 +256,21 @@ export async function runTui() {
|
|
|
215
256
|
return;
|
|
216
257
|
}
|
|
217
258
|
// 端点凭据预检:未配置 API Key 时先弹出输入屏,而不是「报错即退出」
|
|
218
|
-
|
|
259
|
+
// 模型从 Agent 定义读 —— 与 SDK 实发请求用的是同一个值
|
|
260
|
+
let modelId = null;
|
|
261
|
+
try {
|
|
262
|
+
modelId = await modelOf(target.agentId);
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
process.stderr.write(`\n${t("app.bootFailed", { error: error?.message ?? error })}\n`);
|
|
219
266
|
farewell();
|
|
220
267
|
return;
|
|
221
268
|
}
|
|
222
|
-
await
|
|
269
|
+
if (!(await ensureCredential(modelId))) {
|
|
270
|
+
farewell();
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
await runChat(target, options);
|
|
223
274
|
farewell();
|
|
224
275
|
}
|
|
225
276
|
finally {
|
|
@@ -236,6 +287,13 @@ function installSignalSafety() {
|
|
|
236
287
|
catch {
|
|
237
288
|
/* 忽略 */
|
|
238
289
|
}
|
|
290
|
+
// 任何异常退出路径都要把终端鼠标上报关掉,否则终端会持续把滚轮写成转义序列
|
|
291
|
+
try {
|
|
292
|
+
disableMouseTracking();
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
/* 忽略 */
|
|
296
|
+
}
|
|
239
297
|
activeInstance = null;
|
|
240
298
|
};
|
|
241
299
|
const onSigint = () => {
|
|
@@ -248,7 +306,7 @@ function installSignalSafety() {
|
|
|
248
306
|
};
|
|
249
307
|
const onFatal = (error) => {
|
|
250
308
|
restore();
|
|
251
|
-
process.stderr.write(`\n
|
|
309
|
+
process.stderr.write(`\n${t("app.unexpected", { error: error?.message ?? error })}\n`);
|
|
252
310
|
process.exit(1);
|
|
253
311
|
};
|
|
254
312
|
process.on("SIGINT", onSigint);
|
package/dist/tui/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tui/index.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAiB,MAAM,KAAK,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tui/index.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 输入框编辑的纯逻辑层 —— 与 Claude Code / readline 的键位语义对齐。
|
|
3
|
+
*
|
|
4
|
+
* 全部函数都是「(文本, 光标) → (文本, 光标)」的纯变换,便于单测;
|
|
5
|
+
* chat.tsx 只负责把按键映射过来、套用结果、维护 kill ring 与撤销栈。
|
|
6
|
+
*
|
|
7
|
+
* 光标单位是**字素簇**(`graphemes`),不是 code point:`✌️` 是 2 个 code point
|
|
8
|
+
* 但只是 1 个簇,按 code point 删会只删掉半个 emoji、留下孤立的变体选择符。
|
|
9
|
+
*
|
|
10
|
+
* 键位语义依据 Claude Code 官方文档(interactive-mode.md):
|
|
11
|
+
* - `Ctrl+W` 删到**前一空白**(标点不切分:`src/utils/foo.ts` 一次删掉整条路径)
|
|
12
|
+
* - `Alt+B/F/D` 按「词」处理(标点算分隔符:停在 `ts` / `foo` / `utils` / `src`)
|
|
13
|
+
*/
|
|
14
|
+
/** 一段可撤销的编辑状态(文本 + 字素簇光标) */
|
|
15
|
+
export interface EditState {
|
|
16
|
+
text: string;
|
|
17
|
+
cursor: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 删到前一空白(Ctrl+W):先吃掉光标前的空白,再吃掉一整段非空白。
|
|
21
|
+
*
|
|
22
|
+
* 与 `deleteWordLeft` 的区别在于标点:本函数只认空白为边界,
|
|
23
|
+
* 因此 `src/utils/foo.ts` 会被整段删除,而不是只删 `foo.ts`。
|
|
24
|
+
*/
|
|
25
|
+
export declare function deleteToPrevWhitespace(text: string, cursor: number): EditState;
|
|
26
|
+
/**
|
|
27
|
+
* 求「被删掉的文本」:比较编辑前后,取中间不重合的那一段。
|
|
28
|
+
*
|
|
29
|
+
* 用于把 `Ctrl+W/U/K` 删掉的内容放进 kill ring 供 `Ctrl+Y` 取回,
|
|
30
|
+
* 免去每个删除函数各自返回「删了什么」。
|
|
31
|
+
*/
|
|
32
|
+
export declare function killedText(before: string, after: string): string;
|
|
33
|
+
export declare const KILL_RING_SIZE = 5;
|
|
34
|
+
/**
|
|
35
|
+
* 记录一段被删除的文本。
|
|
36
|
+
*
|
|
37
|
+
* 空串不入环;与栈顶相同的连续删除(如连按 `Ctrl+W` 删同一个词)不重复入栈,
|
|
38
|
+
* 否则 `Alt+Y` 会在同一段文本上空转。
|
|
39
|
+
*/
|
|
40
|
+
export declare function pushKill(ring: string[], killed: string, limit?: number): string[];
|
|
41
|
+
/** 取环中第 index 段(越界返回 undefined);index 0 为最近一次删除 */
|
|
42
|
+
export declare function killAt(ring: string[], index: number): string | undefined;
|
|
43
|
+
/** 撤销栈上限:编辑很密,但每条只是一个短字符串,100 条足够 */
|
|
44
|
+
export declare const UNDO_LIMIT = 100;
|
|
45
|
+
/** 入栈一条编辑前快照;与栈顶相同的状态不重复入栈 */
|
|
46
|
+
export declare function pushUndo(stack: EditState[], state: EditState, limit?: number): EditState[];
|
|
47
|
+
/** 出栈一条快照(无历史时返回 null) */
|
|
48
|
+
export declare function popUndo(stack: EditState[]): {
|
|
49
|
+
state: EditState;
|
|
50
|
+
rest: EditState[];
|
|
51
|
+
} | null;
|
|
52
|
+
//# sourceMappingURL=input-editing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-editing.d.ts","sourceRoot":"","sources":["../../src/tui/input-editing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,6BAA6B;AAC7B,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAMD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAU9E;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhE;AAGD,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,SAAiB,GAAG,MAAM,EAAE,CAIzF;AAED,mDAAmD;AACnD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGxE;AAID,qCAAqC;AACrC,eAAO,MAAM,UAAU,MAAM,CAAC;AAE9B,8BAA8B;AAC9B,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,SAAa,GAAG,SAAS,EAAE,CAI9F;AAED,0BAA0B;AAC1B,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,SAAS,EAAE,CAAA;CAAE,GAAG,IAAI,CAG1F"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 输入框编辑的纯逻辑层 —— 与 Claude Code / readline 的键位语义对齐。
|
|
3
|
+
*
|
|
4
|
+
* 全部函数都是「(文本, 光标) → (文本, 光标)」的纯变换,便于单测;
|
|
5
|
+
* chat.tsx 只负责把按键映射过来、套用结果、维护 kill ring 与撤销栈。
|
|
6
|
+
*
|
|
7
|
+
* 光标单位是**字素簇**(`graphemes`),不是 code point:`✌️` 是 2 个 code point
|
|
8
|
+
* 但只是 1 个簇,按 code point 删会只删掉半个 emoji、留下孤立的变体选择符。
|
|
9
|
+
*
|
|
10
|
+
* 键位语义依据 Claude Code 官方文档(interactive-mode.md):
|
|
11
|
+
* - `Ctrl+W` 删到**前一空白**(标点不切分:`src/utils/foo.ts` 一次删掉整条路径)
|
|
12
|
+
* - `Alt+B/F/D` 按「词」处理(标点算分隔符:停在 `ts` / `foo` / `utils` / `src`)
|
|
13
|
+
*/
|
|
14
|
+
import { graphemes } from "./text.js";
|
|
15
|
+
function clampCursor(chars, cursor) {
|
|
16
|
+
return Math.max(0, Math.min(cursor, chars.length));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 删到前一空白(Ctrl+W):先吃掉光标前的空白,再吃掉一整段非空白。
|
|
20
|
+
*
|
|
21
|
+
* 与 `deleteWordLeft` 的区别在于标点:本函数只认空白为边界,
|
|
22
|
+
* 因此 `src/utils/foo.ts` 会被整段删除,而不是只删 `foo.ts`。
|
|
23
|
+
*/
|
|
24
|
+
export function deleteToPrevWhitespace(text, cursor) {
|
|
25
|
+
const chars = graphemes(text);
|
|
26
|
+
const cp = clampCursor(chars, cursor);
|
|
27
|
+
let from = cp;
|
|
28
|
+
while (from > 0 && /\s/.test(chars[from - 1]))
|
|
29
|
+
from -= 1;
|
|
30
|
+
while (from > 0 && !/\s/.test(chars[from - 1]))
|
|
31
|
+
from -= 1;
|
|
32
|
+
return {
|
|
33
|
+
text: chars.slice(0, from).join("") + chars.slice(cp).join(""),
|
|
34
|
+
cursor: from,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 求「被删掉的文本」:比较编辑前后,取中间不重合的那一段。
|
|
39
|
+
*
|
|
40
|
+
* 用于把 `Ctrl+W/U/K` 删掉的内容放进 kill ring 供 `Ctrl+Y` 取回,
|
|
41
|
+
* 免去每个删除函数各自返回「删了什么」。
|
|
42
|
+
*/
|
|
43
|
+
export function killedText(before, after) {
|
|
44
|
+
const a = graphemes(before);
|
|
45
|
+
const b = graphemes(after);
|
|
46
|
+
let prefix = 0;
|
|
47
|
+
while (prefix < b.length && a[prefix] === b[prefix])
|
|
48
|
+
prefix += 1;
|
|
49
|
+
let suffix = 0;
|
|
50
|
+
while (suffix < b.length - prefix && a[a.length - 1 - suffix] === b[b.length - 1 - suffix])
|
|
51
|
+
suffix += 1;
|
|
52
|
+
return a.slice(prefix, a.length - suffix).join("");
|
|
53
|
+
}
|
|
54
|
+
// ==================== kill ring(Ctrl+Y / Alt+Y)====================/** kill ring 最大长度(够 Alt+Y 循环几次即可,不做无限历史) */
|
|
55
|
+
export const KILL_RING_SIZE = 5;
|
|
56
|
+
/**
|
|
57
|
+
* 记录一段被删除的文本。
|
|
58
|
+
*
|
|
59
|
+
* 空串不入环;与栈顶相同的连续删除(如连按 `Ctrl+W` 删同一个词)不重复入栈,
|
|
60
|
+
* 否则 `Alt+Y` 会在同一段文本上空转。
|
|
61
|
+
*/
|
|
62
|
+
export function pushKill(ring, killed, limit = KILL_RING_SIZE) {
|
|
63
|
+
if (!killed)
|
|
64
|
+
return ring;
|
|
65
|
+
if (ring[0] === killed)
|
|
66
|
+
return ring;
|
|
67
|
+
return [killed, ...ring].slice(0, limit);
|
|
68
|
+
}
|
|
69
|
+
/** 取环中第 index 段(越界返回 undefined);index 0 为最近一次删除 */
|
|
70
|
+
export function killAt(ring, index) {
|
|
71
|
+
if (index < 0 || index >= ring.length)
|
|
72
|
+
return undefined;
|
|
73
|
+
return ring[index];
|
|
74
|
+
}
|
|
75
|
+
// ==================== 撤销栈(Ctrl+_)====================
|
|
76
|
+
/** 撤销栈上限:编辑很密,但每条只是一个短字符串,100 条足够 */
|
|
77
|
+
export const UNDO_LIMIT = 100;
|
|
78
|
+
/** 入栈一条编辑前快照;与栈顶相同的状态不重复入栈 */
|
|
79
|
+
export function pushUndo(stack, state, limit = UNDO_LIMIT) {
|
|
80
|
+
const top = stack[stack.length - 1];
|
|
81
|
+
if (top && top.text === state.text && top.cursor === state.cursor)
|
|
82
|
+
return stack;
|
|
83
|
+
return [...stack, state].slice(-limit);
|
|
84
|
+
}
|
|
85
|
+
/** 出栈一条快照(无历史时返回 null) */
|
|
86
|
+
export function popUndo(stack) {
|
|
87
|
+
if (stack.length === 0)
|
|
88
|
+
return null;
|
|
89
|
+
return { state: stack[stack.length - 1], rest: stack.slice(0, -1) };
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=input-editing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-editing.js","sourceRoot":"","sources":["../../src/tui/input-editing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 按键诊断 —— 不同终端的按键编码差异极大(macOS 的 Option、PageUp/PageDown 尤其),
|
|
3
|
+
* 「某个键在我机器上没反应」这类问题靠猜毫无意义:要么键被 OS/终端吞掉,
|
|
4
|
+
* 要么终端发来的序列和我们认的不一样。两种情况的判定方法是同一个 —— 看日志里有几行。
|
|
5
|
+
*
|
|
6
|
+
* 用法:
|
|
7
|
+
* AIZ_DEBUG_KEYS=1 aiz → 追加写到 <tmp>/aiz-key-debug.log
|
|
8
|
+
* AIZ_DEBUG_KEYS=1 AIZ_KEYS_FILE=/path aiz → 写到指定文件
|
|
9
|
+
*
|
|
10
|
+
* 判读:
|
|
11
|
+
* - 按了键但日志里没有新增行 → 键根本没到应用(被 OS 快捷键或终端键位映射截走)
|
|
12
|
+
* - 有行但 `input`/`flags` 与预期不符 → 序列认识,是匹配逻辑的问题
|
|
13
|
+
* - 有行且 `raw=false` → 终端没处于 raw mode,键被终端自己解释了(如滚动 scrollback)
|
|
14
|
+
*
|
|
15
|
+
* 刻意在 `useInput` 回调里记录而不是直接监听 `stdin` 的 data 事件:
|
|
16
|
+
* 后者会把流切成 flowing 模式,与 Ink 内部的 readable 读取抢数据。
|
|
17
|
+
*/
|
|
18
|
+
export declare const KEY_DEBUG_ENABLED: boolean;
|
|
19
|
+
/** 日志落盘路径(测试与用户都可能改写,故导出常量) */
|
|
20
|
+
export declare const KEY_DEBUG_FILE: string;
|
|
21
|
+
/** Ink `useInput` 的 key 对象里我们关心的字段(结构化,避免依赖 ink 的类型导出) */
|
|
22
|
+
export interface KeyFlags {
|
|
23
|
+
upArrow?: boolean;
|
|
24
|
+
downArrow?: boolean;
|
|
25
|
+
leftArrow?: boolean;
|
|
26
|
+
rightArrow?: boolean;
|
|
27
|
+
pageUp?: boolean;
|
|
28
|
+
pageDown?: boolean;
|
|
29
|
+
return?: boolean;
|
|
30
|
+
escape?: boolean;
|
|
31
|
+
tab?: boolean;
|
|
32
|
+
backspace?: boolean;
|
|
33
|
+
delete?: boolean;
|
|
34
|
+
home?: boolean;
|
|
35
|
+
end?: boolean;
|
|
36
|
+
ctrl?: boolean;
|
|
37
|
+
shift?: boolean;
|
|
38
|
+
meta?: boolean;
|
|
39
|
+
}
|
|
40
|
+
/** 终端环境摘要:排查「只在某终端失灵」时先看这一行 */
|
|
41
|
+
export declare function terminalSummary(env?: NodeJS.ProcessEnv): string;
|
|
42
|
+
/**
|
|
43
|
+
* 把一次按键格式化成单行记录。
|
|
44
|
+
*
|
|
45
|
+
* `input` 用 JSON.stringify 转义(控制字符会变成 `\u001b` 这类可读形式),
|
|
46
|
+
* `raw` 表示按下时 stdin 是否仍在 raw mode —— 为 false 时键会被终端自己吃掉。
|
|
47
|
+
*/
|
|
48
|
+
export declare function formatKeyEvent(input: string, key: KeyFlags, raw: boolean): string;
|
|
49
|
+
/** 当前 stdin 是否处于 raw mode(非 TTY 或平台不支持时视为 false) */
|
|
50
|
+
export declare function stdinIsRaw(): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* 记录一行到诊断日志;仅在 `AIZ_DEBUG_KEYS=1` 时生效。
|
|
53
|
+
*
|
|
54
|
+
* 同步写:调试期每次按键都写一行,量小;异步写反而可能在崩溃时丢最后几行
|
|
55
|
+
* —— 而那几行往往正是要看的那几行。
|
|
56
|
+
*/
|
|
57
|
+
export declare function keyDebugLog(line: string): void;
|
|
58
|
+
/** 会话开始时写一行环境摘要(同一次运行只写一次) */
|
|
59
|
+
export declare function keyDebugStart(): void;
|
|
60
|
+
/** 记录一次按键 */
|
|
61
|
+
export declare function keyDebug(input: string, key: KeyFlags): void;
|
|
62
|
+
//# sourceMappingURL=key-debug.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-debug.d.ts","sourceRoot":"","sources":["../../src/tui/key-debug.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,eAAO,MAAM,iBAAiB,SAAqC,CAAC;AAEpE,+BAA+B;AAC/B,eAAO,MAAM,cAAc,QAAwD,CAAC;AAEpF,0DAA0D;AAC1D,MAAM,WAAW,QAAQ;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,+BAA+B;AAC/B,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAK5E;AAqBD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,GAAG,MAAM,CAGjF;AAED,oDAAoD;AACpD,wBAAgB,UAAU,IAAI,OAAO,CAGpC;AAID;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAO9C;AAED,8BAA8B;AAC9B,wBAAgB,aAAa,IAAI,IAAI,CAIpC;AAED,aAAa;AACb,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,CAG3D"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 按键诊断 —— 不同终端的按键编码差异极大(macOS 的 Option、PageUp/PageDown 尤其),
|
|
3
|
+
* 「某个键在我机器上没反应」这类问题靠猜毫无意义:要么键被 OS/终端吞掉,
|
|
4
|
+
* 要么终端发来的序列和我们认的不一样。两种情况的判定方法是同一个 —— 看日志里有几行。
|
|
5
|
+
*
|
|
6
|
+
* 用法:
|
|
7
|
+
* AIZ_DEBUG_KEYS=1 aiz → 追加写到 <tmp>/aiz-key-debug.log
|
|
8
|
+
* AIZ_DEBUG_KEYS=1 AIZ_KEYS_FILE=/path aiz → 写到指定文件
|
|
9
|
+
*
|
|
10
|
+
* 判读:
|
|
11
|
+
* - 按了键但日志里没有新增行 → 键根本没到应用(被 OS 快捷键或终端键位映射截走)
|
|
12
|
+
* - 有行但 `input`/`flags` 与预期不符 → 序列认识,是匹配逻辑的问题
|
|
13
|
+
* - 有行且 `raw=false` → 终端没处于 raw mode,键被终端自己解释了(如滚动 scrollback)
|
|
14
|
+
*
|
|
15
|
+
* 刻意在 `useInput` 回调里记录而不是直接监听 `stdin` 的 data 事件:
|
|
16
|
+
* 后者会把流切成 flowing 模式,与 Ink 内部的 readable 读取抢数据。
|
|
17
|
+
*/
|
|
18
|
+
import { appendFileSync } from "node:fs";
|
|
19
|
+
export const KEY_DEBUG_ENABLED = process.env.AIZ_DEBUG_KEYS === "1";
|
|
20
|
+
/** 日志落盘路径(测试与用户都可能改写,故导出常量) */
|
|
21
|
+
export const KEY_DEBUG_FILE = process.env.AIZ_KEYS_FILE || "/tmp/aiz-key-debug.log";
|
|
22
|
+
/** 终端环境摘要:排查「只在某终端失灵」时先看这一行 */
|
|
23
|
+
export function terminalSummary(env = process.env) {
|
|
24
|
+
const keys = ["TERM", "TERM_PROGRAM", "TERM_PROGRAM_VERSION", "COLORTERM", "LANG", "COLUMNS", "LINES"];
|
|
25
|
+
const parts = keys.map((k) => `${k}=${env[k] ?? "-"}`);
|
|
26
|
+
parts.push(`platform=${process.platform}`, `node=${process.version}`);
|
|
27
|
+
return parts.join(" ");
|
|
28
|
+
}
|
|
29
|
+
const FLAG_NAMES = [
|
|
30
|
+
"ctrl",
|
|
31
|
+
"meta",
|
|
32
|
+
"shift",
|
|
33
|
+
"upArrow",
|
|
34
|
+
"downArrow",
|
|
35
|
+
"leftArrow",
|
|
36
|
+
"rightArrow",
|
|
37
|
+
"pageUp",
|
|
38
|
+
"pageDown",
|
|
39
|
+
"home",
|
|
40
|
+
"end",
|
|
41
|
+
"return",
|
|
42
|
+
"escape",
|
|
43
|
+
"tab",
|
|
44
|
+
"backspace",
|
|
45
|
+
"delete",
|
|
46
|
+
];
|
|
47
|
+
/**
|
|
48
|
+
* 把一次按键格式化成单行记录。
|
|
49
|
+
*
|
|
50
|
+
* `input` 用 JSON.stringify 转义(控制字符会变成 `\u001b` 这类可读形式),
|
|
51
|
+
* `raw` 表示按下时 stdin 是否仍在 raw mode —— 为 false 时键会被终端自己吃掉。
|
|
52
|
+
*/
|
|
53
|
+
export function formatKeyEvent(input, key, raw) {
|
|
54
|
+
const flags = FLAG_NAMES.filter((name) => key[name]).join(",");
|
|
55
|
+
return `key input=${JSON.stringify(input ?? "")} flags=[${flags}] raw=${raw}`;
|
|
56
|
+
}
|
|
57
|
+
/** 当前 stdin 是否处于 raw mode(非 TTY 或平台不支持时视为 false) */
|
|
58
|
+
export function stdinIsRaw() {
|
|
59
|
+
const stdin = process.stdin;
|
|
60
|
+
return Boolean(stdin.isRaw);
|
|
61
|
+
}
|
|
62
|
+
let started = false;
|
|
63
|
+
/**
|
|
64
|
+
* 记录一行到诊断日志;仅在 `AIZ_DEBUG_KEYS=1` 时生效。
|
|
65
|
+
*
|
|
66
|
+
* 同步写:调试期每次按键都写一行,量小;异步写反而可能在崩溃时丢最后几行
|
|
67
|
+
* —— 而那几行往往正是要看的那几行。
|
|
68
|
+
*/
|
|
69
|
+
export function keyDebugLog(line) {
|
|
70
|
+
if (!KEY_DEBUG_ENABLED)
|
|
71
|
+
return;
|
|
72
|
+
try {
|
|
73
|
+
appendFileSync(KEY_DEBUG_FILE, `${new Date().toISOString()} ${line}\n`);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
/* 诊断失败绝不能影响主流程 */
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** 会话开始时写一行环境摘要(同一次运行只写一次) */
|
|
80
|
+
export function keyDebugStart() {
|
|
81
|
+
if (!KEY_DEBUG_ENABLED || started)
|
|
82
|
+
return;
|
|
83
|
+
started = true;
|
|
84
|
+
keyDebugLog(`=== start ${terminalSummary()} file=${KEY_DEBUG_FILE} ===`);
|
|
85
|
+
}
|
|
86
|
+
/** 记录一次按键 */
|
|
87
|
+
export function keyDebug(input, key) {
|
|
88
|
+
if (!KEY_DEBUG_ENABLED)
|
|
89
|
+
return;
|
|
90
|
+
keyDebugLog(formatKeyEvent(input, key, stdinIsRaw()));
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=key-debug.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-debug.js","sourceRoot":"","sources":["../../src/tui/key-debug.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,GAAG,CAAC;AAEpE,+BAA+B;AAC/B,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,wBAAwB,CAAC;AAsBpF,+BAA+B;AAC/B,MAAM,UAAU,eAAe,CAAC,MAAyB,OAAO,CAAC,GAAG;IAClE,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACvG,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;IACvD,KAAK,CAAC,IAAI,CAAC,YAAY,OAAO,CAAC,QAAQ,EAAE,EAAE,QAAQ,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACtE,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,GAAuB;IACrC,MAAM;IACN,MAAM;IACN,OAAO;IACP,SAAS;IACT,WAAW;IACX,WAAW;IACX,YAAY;IACZ,QAAQ;IACR,UAAU;IACV,MAAM;IACN,KAAK;IACL,QAAQ;IACR,QAAQ;IACR,KAAK;IACL,WAAW;IACX,QAAQ;CACT,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,GAAa,EAAE,GAAY;IACvE,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/D,OAAO,aAAa,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,WAAW,KAAK,SAAS,GAAG,EAAE,CAAC;AAChF,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,UAAU;IACxB,MAAM,KAAK,GAAG,OAAO,CAAC,KAA4B,CAAC;IACnD,OAAO,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED,IAAI,OAAO,GAAG,KAAK,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,CAAC,iBAAiB;QAAE,OAAO;IAC/B,IAAI,CAAC;QACH,cAAc,CAAC,cAAc,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;AACH,CAAC;AAED,8BAA8B;AAC9B,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC,iBAAiB,IAAI,OAAO;QAAE,OAAO;IAC1C,OAAO,GAAG,IAAI,CAAC;IACf,WAAW,CAAC,aAAa,eAAe,EAAE,SAAS,cAAc,MAAM,CAAC,CAAC;AAC3E,CAAC;AAED,aAAa;AACb,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,GAAa;IACnD,IAAI,CAAC,iBAAiB;QAAE,OAAO;IAC/B,WAAW,CAAC,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;AACxD,CAAC"}
|