openhorse 0.1.22 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -427
- package/dist/cli.d.ts +1 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +125 -1028
- package/dist/cli.js.map +1 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +91 -19
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/types.d.ts +1 -0
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/framework/index.d.ts +2 -2
- package/dist/framework/index.d.ts.map +1 -1
- package/dist/framework/index.js +6 -1
- package/dist/framework/index.js.map +1 -1
- package/dist/framework/query.d.ts +2 -0
- package/dist/framework/query.d.ts.map +1 -1
- package/dist/framework/query.js +5 -2
- package/dist/framework/query.js.map +1 -1
- package/dist/harness/assembler.d.ts +14 -2
- package/dist/harness/assembler.d.ts.map +1 -1
- package/dist/harness/assembler.js +167 -32
- package/dist/harness/assembler.js.map +1 -1
- package/dist/harness/capsule.d.ts +2 -1
- package/dist/harness/capsule.d.ts.map +1 -1
- package/dist/harness/capsule.js +64 -0
- package/dist/harness/capsule.js.map +1 -1
- package/dist/harness/context-harness.d.ts +23 -3
- package/dist/harness/context-harness.d.ts.map +1 -1
- package/dist/harness/context-harness.js +97 -7
- package/dist/harness/context-harness.js.map +1 -1
- package/dist/harness/contract.d.ts +2 -2
- package/dist/harness/contract.d.ts.map +1 -1
- package/dist/harness/contract.js +7 -3
- package/dist/harness/contract.js.map +1 -1
- package/dist/harness/evidence.d.ts +15 -0
- package/dist/harness/evidence.d.ts.map +1 -0
- package/dist/harness/evidence.js +171 -0
- package/dist/harness/evidence.js.map +1 -0
- package/dist/harness/index.d.ts +9 -3
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +15 -1
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/intent.d.ts +4 -0
- package/dist/harness/intent.d.ts.map +1 -0
- package/dist/harness/intent.js +139 -0
- package/dist/harness/intent.js.map +1 -0
- package/dist/harness/state.d.ts +12 -0
- package/dist/harness/state.d.ts.map +1 -0
- package/dist/harness/state.js +84 -0
- package/dist/harness/state.js.map +1 -0
- package/dist/harness/turn-summary.d.ts +25 -0
- package/dist/harness/turn-summary.d.ts.map +1 -0
- package/dist/harness/turn-summary.js +120 -0
- package/dist/harness/turn-summary.js.map +1 -0
- package/dist/harness/types.d.ts +103 -0
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/ink-ui/App.d.ts +6 -0
- package/dist/ink-ui/App.d.ts.map +1 -0
- package/dist/ink-ui/App.js +12 -0
- package/dist/ink-ui/App.js.map +1 -0
- package/dist/ink-ui/components/Markdown.d.ts +9 -0
- package/dist/ink-ui/components/Markdown.d.ts.map +1 -0
- package/dist/ink-ui/components/Markdown.js +188 -0
- package/dist/ink-ui/components/Markdown.js.map +1 -0
- package/dist/ink-ui/components/PixelHorseBanner.d.ts +8 -0
- package/dist/ink-ui/components/PixelHorseBanner.d.ts.map +1 -0
- package/dist/ink-ui/components/PixelHorseBanner.js +97 -0
- package/dist/ink-ui/components/PixelHorseBanner.js.map +1 -0
- package/dist/ink-ui/components/PromptInput.d.ts +9 -0
- package/dist/ink-ui/components/PromptInput.d.ts.map +1 -0
- package/dist/ink-ui/components/PromptInput.js +24 -0
- package/dist/ink-ui/components/PromptInput.js.map +1 -0
- package/dist/ink-ui/components/SelectList.d.ts +15 -0
- package/dist/ink-ui/components/SelectList.d.ts.map +1 -0
- package/dist/ink-ui/components/SelectList.js +39 -0
- package/dist/ink-ui/components/SelectList.js.map +1 -0
- package/dist/ink-ui/components/StatusLine.d.ts +9 -0
- package/dist/ink-ui/components/StatusLine.d.ts.map +1 -0
- package/dist/ink-ui/components/StatusLine.js +71 -0
- package/dist/ink-ui/components/StatusLine.js.map +1 -0
- package/dist/ink-ui/components/TerminalCursor.d.ts +13 -0
- package/dist/ink-ui/components/TerminalCursor.d.ts.map +1 -0
- package/dist/ink-ui/components/TerminalCursor.js +74 -0
- package/dist/ink-ui/components/TerminalCursor.js.map +1 -0
- package/dist/ink-ui/components/Transcript.d.ts +8 -0
- package/dist/ink-ui/components/Transcript.d.ts.map +1 -0
- package/dist/ink-ui/components/Transcript.js +29 -0
- package/dist/ink-ui/components/Transcript.js.map +1 -0
- package/dist/ink-ui/controllers/chat-controller.d.ts +15 -0
- package/dist/ink-ui/controllers/chat-controller.d.ts.map +1 -0
- package/dist/ink-ui/controllers/chat-controller.js +410 -0
- package/dist/ink-ui/controllers/chat-controller.js.map +1 -0
- package/dist/ink-ui/launch.d.ts +3 -0
- package/dist/ink-ui/launch.d.ts.map +1 -0
- package/dist/ink-ui/launch.js +14 -0
- package/dist/ink-ui/launch.js.map +1 -0
- package/dist/ink-ui/runtime/prompt-layout.d.ts +11 -0
- package/dist/ink-ui/runtime/prompt-layout.d.ts.map +1 -0
- package/dist/ink-ui/runtime/prompt-layout.js +51 -0
- package/dist/ink-ui/runtime/prompt-layout.js.map +1 -0
- package/dist/ink-ui/screens/ReplScreen.d.ts +14 -0
- package/dist/ink-ui/screens/ReplScreen.d.ts.map +1 -0
- package/dist/ink-ui/screens/ReplScreen.js +470 -0
- package/dist/ink-ui/screens/ReplScreen.js.map +1 -0
- package/dist/ink-ui/types.d.ts +43 -0
- package/dist/ink-ui/types.d.ts.map +1 -0
- package/dist/ink-ui/types.js +3 -0
- package/dist/ink-ui/types.js.map +1 -0
- package/dist/services/compact/auto-compact.d.ts +3 -1
- package/dist/services/compact/auto-compact.d.ts.map +1 -1
- package/dist/services/compact/auto-compact.js +10 -0
- package/dist/services/compact/auto-compact.js.map +1 -1
- package/dist/services/compact/compact.d.ts +5 -1
- package/dist/services/compact/compact.d.ts.map +1 -1
- package/dist/services/compact/compact.js +11 -1
- package/dist/services/compact/compact.js.map +1 -1
- package/dist/services/config-dir.d.ts +2 -0
- package/dist/services/config-dir.d.ts.map +1 -1
- package/dist/services/config-dir.js +5 -0
- package/dist/services/config-dir.js.map +1 -1
- package/dist/services/config.js +2 -2
- package/dist/services/config.js.map +1 -1
- package/dist/services/global-config.d.ts +1 -1
- package/dist/services/global-config.d.ts.map +1 -1
- package/dist/services/session-storage.d.ts +4 -1
- package/dist/services/session-storage.d.ts.map +1 -1
- package/dist/services/session-storage.js +76 -6
- package/dist/services/session-storage.js.map +1 -1
- package/dist/tools/mcp.js +1 -1
- package/dist/ui-v2/components/session-picker.d.ts +2 -0
- package/dist/ui-v2/components/session-picker.d.ts.map +1 -1
- package/dist/ui-v2/components/session-picker.js +34 -5
- package/dist/ui-v2/components/session-picker.js.map +1 -1
- package/dist/ui-v2/state/sessions.d.ts +1 -0
- package/dist/ui-v2/state/sessions.d.ts.map +1 -1
- package/dist/ui-v2/state/sessions.js +18 -0
- package/dist/ui-v2/state/sessions.js.map +1 -1
- package/package.json +5 -1
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* openhorse -
|
|
4
|
-
*
|
|
5
|
-
* 简洁版 REPL,使用 readline + keypress 事件处理
|
|
3
|
+
* openhorse - Ink/React CLI entry
|
|
6
4
|
*/
|
|
7
5
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
6
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -10,79 +8,53 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
10
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
9
|
require("dotenv/config");
|
|
12
10
|
const chalk_1 = __importDefault(require("chalk"));
|
|
13
|
-
const readline_1 = __importDefault(require("readline"));
|
|
14
11
|
const fs_1 = require("fs");
|
|
15
12
|
const path_1 = require("path");
|
|
16
13
|
const init_1 = require("./init");
|
|
17
14
|
const llm_1 = require("./services/llm");
|
|
18
|
-
const tools_1 = require("./tools");
|
|
19
|
-
const mcp_1 = require("./tools/mcp");
|
|
20
15
|
const config_1 = require("./services/config");
|
|
21
16
|
const config_dir_1 = require("./services/config-dir");
|
|
22
17
|
const global_config_1 = require("./services/global-config");
|
|
23
|
-
const model_context_1 = require("./services/model-context");
|
|
24
18
|
const session_storage_1 = require("./services/session-storage");
|
|
25
19
|
const storage_1 = require("./memory/storage");
|
|
26
20
|
const skills_1 = require("./skills");
|
|
27
21
|
const framework_1 = require("./framework");
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const ui_v2_1 = require("./ui-v2");
|
|
38
|
-
const turn_controller_1 = require("./runtime/turn-controller");
|
|
39
|
-
// Get version from package.json
|
|
22
|
+
const tools_1 = require("./tools");
|
|
23
|
+
const mcp_1 = require("./tools/mcp");
|
|
24
|
+
const model_context_1 = require("./services/model-context");
|
|
25
|
+
const launch_1 = require("./ink-ui/launch");
|
|
26
|
+
const BRAND = chalk_1.default.hex('#FF6B35');
|
|
27
|
+
const ACCENT = chalk_1.default.hex('#00D4AA');
|
|
28
|
+
const DIM = chalk_1.default.dim;
|
|
29
|
+
const ERROR = chalk_1.default.red;
|
|
30
|
+
const WARN = chalk_1.default.yellow;
|
|
40
31
|
const VERSION = (() => {
|
|
41
32
|
try {
|
|
42
33
|
const pkgPath = (0, path_1.join)(__dirname, '..', 'package.json');
|
|
43
34
|
const pkg = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf-8'));
|
|
44
|
-
return pkg.version || '0.
|
|
35
|
+
return pkg.version || '0.2.0';
|
|
45
36
|
}
|
|
46
37
|
catch {
|
|
47
|
-
return '0.
|
|
38
|
+
return '0.2.0';
|
|
48
39
|
}
|
|
49
40
|
})();
|
|
50
|
-
// ============================================================================
|
|
51
|
-
// 颜色常量
|
|
52
|
-
// ============================================================================
|
|
53
|
-
const BRAND = chalk_1.default.hex('#FF6B35');
|
|
54
|
-
const ACCENT = chalk_1.default.hex('#00D4AA');
|
|
55
|
-
const DIM = chalk_1.default.dim;
|
|
56
|
-
const ERROR = chalk_1.default.red;
|
|
57
|
-
const WARN = chalk_1.default.yellow;
|
|
58
|
-
const SUCCESS = chalk_1.default.green;
|
|
59
|
-
// ============================================================================
|
|
60
|
-
// CLI Help
|
|
61
|
-
// ============================================================================
|
|
62
41
|
function showCliHelp() {
|
|
63
42
|
console.log();
|
|
64
43
|
console.log(BRAND('openhorse') + DIM(` v${VERSION}`));
|
|
65
44
|
console.log(DIM(' Universal Agent Harness Framework'));
|
|
66
45
|
console.log();
|
|
67
46
|
console.log(ACCENT('Usage:'));
|
|
68
|
-
console.log(' openhorse
|
|
69
|
-
console.log(' openhorse --help
|
|
70
|
-
console.log(' openhorse --version
|
|
71
|
-
console.log(' openhorse --ui
|
|
47
|
+
console.log(' openhorse Start the Ink/React interactive UI');
|
|
48
|
+
console.log(' openhorse --help Show this help message');
|
|
49
|
+
console.log(' openhorse --version Show version');
|
|
50
|
+
console.log(' openhorse --ui ink Start the Ink UI explicitly');
|
|
72
51
|
console.log();
|
|
73
52
|
console.log(ACCENT('Options:'));
|
|
74
53
|
console.log(' -h, --help Show help');
|
|
75
54
|
console.log(' -v, --version Show version');
|
|
76
|
-
console.log(' --ui <mode> UI renderer:
|
|
55
|
+
console.log(' --ui <mode> UI renderer: ink');
|
|
77
56
|
console.log();
|
|
78
|
-
console.log(
|
|
79
|
-
console.log(' /help Show available slash commands');
|
|
80
|
-
console.log(' /status Show system status');
|
|
81
|
-
console.log(' /model [name] Show or change model');
|
|
82
|
-
console.log(' /chat <msg> Send message to LLM');
|
|
83
|
-
console.log(' /exit Exit the REPL');
|
|
84
|
-
console.log();
|
|
85
|
-
console.log(DIM('Type /help in REPL for full command list.'));
|
|
57
|
+
console.log(DIM('Legacy readline/v2 renderers were removed in v0.2.0.'));
|
|
86
58
|
console.log();
|
|
87
59
|
}
|
|
88
60
|
function parseCliUIRenderer(args) {
|
|
@@ -95,1023 +67,148 @@ function parseCliUIRenderer(args) {
|
|
|
95
67
|
: undefined;
|
|
96
68
|
if (value === undefined)
|
|
97
69
|
continue;
|
|
98
|
-
if (value === '
|
|
99
|
-
return
|
|
70
|
+
if (value === 'ink')
|
|
71
|
+
return 'ink';
|
|
72
|
+
if (value === 'legacy' || value === 'v2') {
|
|
73
|
+
console.log(WARN(`Renderer "${value}" was removed in v0.2.0; starting Ink UI instead.`));
|
|
74
|
+
return 'ink';
|
|
75
|
+
}
|
|
100
76
|
console.error(ERROR(`Invalid --ui value: ${value}`));
|
|
101
|
-
console.error(DIM('Expected
|
|
77
|
+
console.error(DIM('Expected: ink'));
|
|
102
78
|
process.exit(1);
|
|
103
79
|
}
|
|
104
|
-
return
|
|
105
|
-
}
|
|
106
|
-
// ============================================================================
|
|
107
|
-
// 全局状态
|
|
108
|
-
// ============================================================================
|
|
109
|
-
let llm = null;
|
|
110
|
-
let store;
|
|
111
|
-
let currentSession = null;
|
|
112
|
-
let runtime;
|
|
113
|
-
const turnController = new turn_controller_1.TurnController();
|
|
114
|
-
let isShuttingDown = false;
|
|
115
|
-
// 输入状态
|
|
116
|
-
let currentInput = '';
|
|
117
|
-
let inputHistory = [];
|
|
118
|
-
let historyIndex = -1;
|
|
119
|
-
let historyMode = 'none';
|
|
120
|
-
let searchQuery = '';
|
|
121
|
-
let resumePickerState = null;
|
|
122
|
-
let resumePickerLines = [];
|
|
123
|
-
let resumePickerReservedHeight = 0;
|
|
124
|
-
function ensureCurrentSession() {
|
|
125
|
-
if (!currentSession) {
|
|
126
|
-
currentSession = (0, session_storage_1.createSession)(process.cwd(), store.getSnapshot().currentModel || store.getSnapshot().config.model);
|
|
127
|
-
(0, global_config_1.incrementSessionCount)();
|
|
128
|
-
}
|
|
129
|
-
return currentSession;
|
|
130
|
-
}
|
|
131
|
-
function setCurrentSession(session) {
|
|
132
|
-
currentSession = session;
|
|
133
|
-
}
|
|
134
|
-
function getCurrentSession() {
|
|
135
|
-
return currentSession;
|
|
136
|
-
}
|
|
137
|
-
function echoSubmittedInput(input) {
|
|
138
|
-
(0, command_panel_1.clearRenderedInput)();
|
|
139
|
-
const rendered = isV2UI()
|
|
140
|
-
? (0, user_input_1.renderUserInputEchoFrame)(input)
|
|
141
|
-
: (0, user_input_1.renderUserInputEcho)(input);
|
|
142
|
-
(0, command_panel_1.writeLinePreservingInput)(rendered);
|
|
143
|
-
if (isV2UI()) {
|
|
144
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
function isExitCommandInput(input) {
|
|
148
|
-
const parsed = (0, parser_1.parseInput)(input.trim());
|
|
149
|
-
if (!parsed.isCommand)
|
|
150
|
-
return false;
|
|
151
|
-
const cmd = (0, commands_1.findCommand)(parsed.name);
|
|
152
|
-
return cmd?.name === 'exit';
|
|
153
|
-
}
|
|
154
|
-
function submitInput(input) {
|
|
155
|
-
const submittedInput = input;
|
|
156
|
-
currentInput = '';
|
|
157
|
-
echoSubmittedInput(submittedInput);
|
|
158
|
-
(0, global_config_1.addToInputHistory)(submittedInput);
|
|
159
|
-
inputHistory = (0, global_config_1.getInputHistory)();
|
|
160
|
-
if (turnController.hasActiveTurn()) {
|
|
161
|
-
const text = submittedInput.trim();
|
|
162
|
-
const parsed = (0, parser_1.parseInput)(text);
|
|
163
|
-
if (isExitCommandInput(text)) {
|
|
164
|
-
void shutdownCli();
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
if (parsed.isCommand) {
|
|
168
|
-
console.log(DIM('Command ignored while agent is running. Press Ctrl+C to interrupt first.'));
|
|
169
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
if (text) {
|
|
173
|
-
turnController.clearExitIntent();
|
|
174
|
-
turnController.requestRevision(submittedInput);
|
|
175
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
176
|
-
}
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
void runInputTurn(submittedInput);
|
|
180
|
-
}
|
|
181
|
-
async function runInputTurn(input) {
|
|
182
|
-
let nextInput = input;
|
|
183
|
-
while (nextInput) {
|
|
184
|
-
const turn = turnController.beginTurn(nextInput);
|
|
185
|
-
store.setProcessing(true);
|
|
186
|
-
try {
|
|
187
|
-
await handleInput(nextInput, turn.abortSignal, { redrawPrompt: false, updateStatus: false });
|
|
188
|
-
}
|
|
189
|
-
catch (err) {
|
|
190
|
-
console.log(ERROR(`Input error: ${err.message || String(err)}`));
|
|
191
|
-
}
|
|
192
|
-
finally {
|
|
193
|
-
const revision = turnController.finishTurn(turn.id);
|
|
194
|
-
store.setProcessing(false);
|
|
195
|
-
if (revision && revision.trim()) {
|
|
196
|
-
(0, command_panel_1.writeLinePreservingInput)(DIM('Interrupted. Restarting with latest instruction...'));
|
|
197
|
-
nextInput = revision;
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
nextInput = undefined;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
updateStatusBar();
|
|
205
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
206
|
-
}
|
|
207
|
-
function parseKey(char, key) {
|
|
208
|
-
const sequence = key?.sequence || char || '';
|
|
209
|
-
if (sequence === '\x1b[13;2u') {
|
|
210
|
-
return { name: 'enter', ctrl: false, shift: true, meta: false, sequence };
|
|
211
|
-
}
|
|
212
|
-
if (sequence === '\x1b[13;3u') {
|
|
213
|
-
return { name: 'enter', ctrl: false, shift: false, meta: true, sequence };
|
|
214
|
-
}
|
|
215
|
-
if (!key) {
|
|
216
|
-
// 手动解析
|
|
217
|
-
if (char === '\r' || char === '\n')
|
|
218
|
-
return { name: 'enter', ctrl: false, shift: false, meta: false, sequence: char || '' };
|
|
219
|
-
if (char === '\x1b')
|
|
220
|
-
return { name: 'escape', ctrl: false, shift: false, meta: false, sequence: char };
|
|
221
|
-
if (char === '\t')
|
|
222
|
-
return { name: 'tab', ctrl: false, shift: false, meta: false, sequence: char };
|
|
223
|
-
if (char === '\x7f' || char === '\b')
|
|
224
|
-
return { name: 'backspace', ctrl: false, shift: false, meta: false, sequence: char };
|
|
225
|
-
return { name: char || '', ctrl: false, shift: false, meta: false, sequence: char || '' };
|
|
226
|
-
}
|
|
227
|
-
// 统一 "return" 和 "enter"
|
|
228
|
-
if (key.name === 'return') {
|
|
229
|
-
key.name = 'enter';
|
|
230
|
-
}
|
|
231
|
-
if (key.name === 'enter' && (key.shift || key.meta)) {
|
|
232
|
-
return key;
|
|
233
|
-
}
|
|
234
|
-
// 如果 key.name 为空,使用 char 作为 name
|
|
235
|
-
if (!key.name && char) {
|
|
236
|
-
key.name = char;
|
|
237
|
-
}
|
|
238
|
-
return key;
|
|
239
|
-
}
|
|
240
|
-
function handleKeypress(char, key) {
|
|
241
|
-
const k = parseKey(char, key);
|
|
242
|
-
if (k.ctrl && k.name === 'l') {
|
|
243
|
-
clearTerminalView();
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
if (k.ctrl && k.name === 'c') {
|
|
247
|
-
void handleCtrlC();
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
// 命令面板模式
|
|
251
|
-
if ((0, command_panel_1.isPanelVisible)()) {
|
|
252
|
-
handlePanelKeypress(k, char);
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
// 文件补全模式
|
|
256
|
-
if ((0, file_completion_1.isFileCompletionVisible)()) {
|
|
257
|
-
handleFileCompletionKeypress(k, char);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
// Resume session picker mode
|
|
261
|
-
if (resumePickerState?.visible) {
|
|
262
|
-
handleResumePickerKeypress(k, char);
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
// 历史导航模式
|
|
266
|
-
if (historyMode !== 'none') {
|
|
267
|
-
handleHistoryKeypress(k, char);
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
// 正常输入模式
|
|
271
|
-
handleNormalKeypress(k, char);
|
|
80
|
+
return 'ink';
|
|
272
81
|
}
|
|
273
|
-
function
|
|
274
|
-
switch (k.name) {
|
|
275
|
-
case 'up':
|
|
276
|
-
(0, command_panel_1.navigatePanel)('up');
|
|
277
|
-
break;
|
|
278
|
-
case 'down':
|
|
279
|
-
(0, command_panel_1.navigatePanel)('down');
|
|
280
|
-
break;
|
|
281
|
-
case 'tab':
|
|
282
|
-
const completedCmd = (0, command_panel_1.completeSelectedCommand)();
|
|
283
|
-
if (completedCmd) {
|
|
284
|
-
currentInput = completedCmd;
|
|
285
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
286
|
-
}
|
|
287
|
-
break;
|
|
288
|
-
case 'enter':
|
|
289
|
-
const cmd = (0, command_panel_1.selectCommand)();
|
|
290
|
-
if (cmd) {
|
|
291
|
-
submitInput(cmd);
|
|
292
|
-
(0, command_panel_1.clearPendingCommand)();
|
|
293
|
-
}
|
|
294
|
-
break;
|
|
295
|
-
case 'escape':
|
|
296
|
-
(0, command_panel_1.hideCommandPanel)();
|
|
297
|
-
currentInput = '';
|
|
298
|
-
(0, command_panel_1.redrawInputWithPrompt)('');
|
|
299
|
-
break;
|
|
300
|
-
case 'backspace':
|
|
301
|
-
currentInput = currentInput.slice(0, -1);
|
|
302
|
-
if (currentInput.length <= 1) {
|
|
303
|
-
(0, command_panel_1.hideCommandPanel)();
|
|
304
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
305
|
-
}
|
|
306
|
-
else {
|
|
307
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
308
|
-
(0, command_panel_1.updatePanelFilter)(currentInput.slice(1));
|
|
309
|
-
}
|
|
310
|
-
break;
|
|
311
|
-
default:
|
|
312
|
-
// 添加字符到过滤
|
|
313
|
-
if (char && char.length === 1 && !k.ctrl) {
|
|
314
|
-
currentInput += char;
|
|
315
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
316
|
-
(0, command_panel_1.updatePanelFilter)(currentInput.slice(1));
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
function handleHistoryKeypress(k, char) {
|
|
321
|
-
if (historyMode === 'search') {
|
|
322
|
-
// 搜索模式:输入搜索词
|
|
323
|
-
switch (k.name) {
|
|
324
|
-
case 'enter':
|
|
325
|
-
if (inputHistory.length > 0 && historyIndex >= 0) {
|
|
326
|
-
currentInput = inputHistory[historyIndex]?.content || '';
|
|
327
|
-
}
|
|
328
|
-
historyMode = 'none';
|
|
329
|
-
searchQuery = '';
|
|
330
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
331
|
-
break;
|
|
332
|
-
case 'escape':
|
|
333
|
-
historyMode = 'none';
|
|
334
|
-
searchQuery = '';
|
|
335
|
-
currentInput = '';
|
|
336
|
-
(0, command_panel_1.redrawInputWithPrompt)('');
|
|
337
|
-
break;
|
|
338
|
-
case 'backspace':
|
|
339
|
-
searchQuery = searchQuery.slice(0, -1);
|
|
340
|
-
updateHistorySearch();
|
|
341
|
-
break;
|
|
342
|
-
default:
|
|
343
|
-
if (char && char.length === 1 && !k.ctrl) {
|
|
344
|
-
searchQuery += char;
|
|
345
|
-
updateHistorySearch();
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
else {
|
|
350
|
-
// 导航模式
|
|
351
|
-
switch (k.name) {
|
|
352
|
-
case 'up':
|
|
353
|
-
historyIndex = Math.min(inputHistory.length - 1, historyIndex + 1);
|
|
354
|
-
currentInput = inputHistory[historyIndex]?.content || '';
|
|
355
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
356
|
-
break;
|
|
357
|
-
case 'down':
|
|
358
|
-
historyIndex = Math.max(-1, historyIndex - 1);
|
|
359
|
-
if (historyIndex < 0) {
|
|
360
|
-
currentInput = '';
|
|
361
|
-
historyMode = 'none';
|
|
362
|
-
}
|
|
363
|
-
else {
|
|
364
|
-
currentInput = inputHistory[historyIndex]?.content || '';
|
|
365
|
-
}
|
|
366
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
367
|
-
break;
|
|
368
|
-
case 'enter':
|
|
369
|
-
historyMode = 'none';
|
|
370
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
371
|
-
break;
|
|
372
|
-
case 'escape':
|
|
373
|
-
historyMode = 'none';
|
|
374
|
-
currentInput = '';
|
|
375
|
-
historyIndex = -1;
|
|
376
|
-
(0, command_panel_1.redrawInputWithPrompt)('');
|
|
377
|
-
break;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
function handleFileCompletionKeypress(k, char) {
|
|
382
|
-
switch (k.name) {
|
|
383
|
-
case 'up':
|
|
384
|
-
(0, file_completion_1.navigateFiles)('up');
|
|
385
|
-
break;
|
|
386
|
-
case 'down':
|
|
387
|
-
(0, file_completion_1.navigateFiles)('down');
|
|
388
|
-
break;
|
|
389
|
-
case 'tab':
|
|
390
|
-
const completedPath = (0, file_completion_1.completeFile)();
|
|
391
|
-
if (completedPath) {
|
|
392
|
-
currentInput = (0, file_completion_1.getBaseInput)() + '@' + completedPath;
|
|
393
|
-
(0, file_completion_1.hideFileCompletion)();
|
|
394
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
395
|
-
}
|
|
396
|
-
else {
|
|
397
|
-
// 目录:继续显示面板,更新 query
|
|
398
|
-
currentInput = (0, file_completion_1.getBaseInput)() + '@' + (0, file_completion_1.getFileQuery)();
|
|
399
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
400
|
-
}
|
|
401
|
-
break;
|
|
402
|
-
case 'enter':
|
|
403
|
-
const selectedPath = (0, file_completion_1.selectFile)();
|
|
404
|
-
if (selectedPath) {
|
|
405
|
-
currentInput = (0, file_completion_1.getBaseInput)() + '@' + selectedPath;
|
|
406
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
407
|
-
}
|
|
408
|
-
break;
|
|
409
|
-
case 'escape':
|
|
410
|
-
(0, file_completion_1.hideFileCompletion)();
|
|
411
|
-
currentInput = (0, file_completion_1.getBaseInput)() + '@' + (0, file_completion_1.getFileQuery)();
|
|
412
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
413
|
-
break;
|
|
414
|
-
case 'backspace':
|
|
415
|
-
const query = (0, file_completion_1.getFileQuery)();
|
|
416
|
-
if (query.length > 0) {
|
|
417
|
-
(0, file_completion_1.updateFileQuery)(query.slice(0, -1));
|
|
418
|
-
currentInput = (0, file_completion_1.getBaseInput)() + '@' + (0, file_completion_1.getFileQuery)();
|
|
419
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
420
|
-
}
|
|
421
|
-
else {
|
|
422
|
-
(0, file_completion_1.hideFileCompletion)();
|
|
423
|
-
currentInput = (0, file_completion_1.getBaseInput)();
|
|
424
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
425
|
-
}
|
|
426
|
-
break;
|
|
427
|
-
default:
|
|
428
|
-
// 添加字符到路径查询
|
|
429
|
-
if (char && char.length === 1 && !k.ctrl) {
|
|
430
|
-
const newQuery = (0, file_completion_1.getFileQuery)() + char;
|
|
431
|
-
(0, file_completion_1.updateFileQuery)(newQuery);
|
|
432
|
-
currentInput = (0, file_completion_1.getBaseInput)() + '@' + newQuery;
|
|
433
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
function handleResumePickerKeypress(k, char) {
|
|
438
|
-
if (!resumePickerState?.visible)
|
|
439
|
-
return;
|
|
440
|
-
switch (k.name) {
|
|
441
|
-
case 'up':
|
|
442
|
-
resumePickerState.selectedIndex = Math.max(0, resumePickerState.selectedIndex - 1);
|
|
443
|
-
renderResumePicker();
|
|
444
|
-
break;
|
|
445
|
-
case 'down':
|
|
446
|
-
resumePickerState.selectedIndex = Math.min(resumePickerState.sessions.length - 1, resumePickerState.selectedIndex + 1);
|
|
447
|
-
renderResumePicker();
|
|
448
|
-
break;
|
|
449
|
-
case 'enter':
|
|
450
|
-
restoreSelectedResumeSession();
|
|
451
|
-
break;
|
|
452
|
-
case 'escape':
|
|
453
|
-
hideResumePicker();
|
|
454
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
455
|
-
break;
|
|
456
|
-
default:
|
|
457
|
-
if (char && /^[1-9]$/.test(char)) {
|
|
458
|
-
const index = Number(char) - 1;
|
|
459
|
-
if (index >= 0 && index < resumePickerState.sessions.length) {
|
|
460
|
-
resumePickerState.selectedIndex = index;
|
|
461
|
-
restoreSelectedResumeSession();
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
function showResumePicker(options) {
|
|
467
|
-
resumePickerState = {
|
|
468
|
-
visible: true,
|
|
469
|
-
sessions: options.sessions,
|
|
470
|
-
selectedIndex: 0,
|
|
471
|
-
title: options.title,
|
|
472
|
-
showProject: options.showProject,
|
|
473
|
-
moreCount: options.moreCount,
|
|
474
|
-
allProjects: options.allProjects,
|
|
475
|
-
};
|
|
476
|
-
renderResumePicker();
|
|
477
|
-
}
|
|
478
|
-
function renderResumePicker() {
|
|
479
|
-
if (!resumePickerState?.visible)
|
|
480
|
-
return;
|
|
481
|
-
clearResumePicker({ release: false });
|
|
482
|
-
const lines = (0, ui_v2_1.renderSessionPicker)({
|
|
483
|
-
title: resumePickerState.title,
|
|
484
|
-
sessions: resumePickerState.sessions,
|
|
485
|
-
selectedIndex: resumePickerState.selectedIndex,
|
|
486
|
-
width: process.stdout.columns || 80,
|
|
487
|
-
showProject: resumePickerState.showProject,
|
|
488
|
-
moreCount: resumePickerState.moreCount,
|
|
489
|
-
footer: ' ↑↓ Select Enter Resume 1-9 Quick Esc Cancel',
|
|
490
|
-
theme: {
|
|
491
|
-
accent: ACCENT,
|
|
492
|
-
dim: DIM,
|
|
493
|
-
selected: text => chalk_1.default.bgHex('#1E293B').hex('#E2E8F0')(text),
|
|
494
|
-
},
|
|
495
|
-
});
|
|
496
|
-
const offset = getResumePickerOffsetRows();
|
|
497
|
-
reserveResumePickerSpace(lines.length + offset);
|
|
498
|
-
resumePickerLines = lines;
|
|
499
|
-
process.stdout.write('\x1b7');
|
|
500
|
-
if (offset > 0) {
|
|
501
|
-
process.stdout.write(`\x1b[${offset}B\r`);
|
|
502
|
-
}
|
|
503
|
-
process.stdout.write('\x1b[J');
|
|
504
|
-
for (let index = 0; index < lines.length; index++) {
|
|
505
|
-
if (index > 0 || offset === 0) {
|
|
506
|
-
process.stdout.write('\n');
|
|
507
|
-
}
|
|
508
|
-
process.stdout.write('\r' + lines[index]);
|
|
509
|
-
}
|
|
510
|
-
process.stdout.write('\x1b8');
|
|
511
|
-
}
|
|
512
|
-
function hideResumePicker() {
|
|
513
|
-
clearResumePicker({ release: true });
|
|
514
|
-
resumePickerState = null;
|
|
515
|
-
}
|
|
516
|
-
function clearResumePicker(options = {}) {
|
|
517
|
-
const height = Math.max(resumePickerReservedHeight, resumePickerLines.length);
|
|
518
|
-
if (height <= 0)
|
|
519
|
-
return;
|
|
520
|
-
process.stdout.write('\x1b7');
|
|
521
|
-
const offset = getResumePickerOffsetRows();
|
|
522
|
-
if (offset > 0) {
|
|
523
|
-
process.stdout.write(`\x1b[${offset}B\r`);
|
|
524
|
-
}
|
|
525
|
-
process.stdout.write('\x1b[J');
|
|
526
|
-
process.stdout.write('\x1b8');
|
|
527
|
-
resumePickerLines = [];
|
|
528
|
-
if (options.release) {
|
|
529
|
-
resumePickerReservedHeight = 0;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
function reserveResumePickerSpace(requiredHeight) {
|
|
533
|
-
if (requiredHeight <= resumePickerReservedHeight)
|
|
534
|
-
return;
|
|
535
|
-
const extraLines = requiredHeight - resumePickerReservedHeight;
|
|
536
|
-
process.stdout.write('\n'.repeat(extraLines));
|
|
537
|
-
process.stdout.write(`\x1b[${extraLines}A`);
|
|
538
|
-
resumePickerReservedHeight = requiredHeight;
|
|
539
|
-
}
|
|
540
|
-
function getResumePickerOffsetRows() {
|
|
541
|
-
return isV2UI() ? 2 : 1;
|
|
542
|
-
}
|
|
543
|
-
function restoreSelectedResumeSession() {
|
|
544
|
-
if (!resumePickerState?.visible)
|
|
545
|
-
return;
|
|
546
|
-
const session = resumePickerState.sessions[resumePickerState.selectedIndex];
|
|
547
|
-
const suffix = resumePickerState.allProjects ? ' --all' : '';
|
|
548
|
-
hideResumePicker();
|
|
549
|
-
currentInput = '';
|
|
550
|
-
(0, command_panel_1.clearRenderedInput)();
|
|
551
|
-
runInputTurn(`/resume ${session.id}${suffix}`).catch(err => {
|
|
552
|
-
console.log(ERROR(`Resume error: ${err.message || String(err)}`));
|
|
553
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
function handleNormalKeypress(k, char) {
|
|
557
|
-
switch (k.name) {
|
|
558
|
-
case 'enter':
|
|
559
|
-
if (k.shift || k.meta) {
|
|
560
|
-
if (isV2UI()) {
|
|
561
|
-
insertInputNewline();
|
|
562
|
-
}
|
|
563
|
-
return;
|
|
564
|
-
}
|
|
565
|
-
// 多行模式:添加行
|
|
566
|
-
if ((0, multiline_input_1.isMultilineActive)()) {
|
|
567
|
-
if ((0, multiline_input_1.shouldEnterMultiline)(currentInput)) {
|
|
568
|
-
(0, multiline_input_1.addMultilineLine)(currentInput);
|
|
569
|
-
currentInput = '';
|
|
570
|
-
if (isV2UI()) {
|
|
571
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
572
|
-
}
|
|
573
|
-
else {
|
|
574
|
-
process.stdout.write('\r\x1b[2K');
|
|
575
|
-
process.stdout.write((0, multiline_input_1.renderContinuationPrompt)());
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
else {
|
|
579
|
-
// 结束多行,发送完整输入
|
|
580
|
-
(0, multiline_input_1.addMultilineLine)(currentInput);
|
|
581
|
-
const fullInput = (0, multiline_input_1.getMultilineInput)();
|
|
582
|
-
(0, multiline_input_1.resetMultiline)();
|
|
583
|
-
if (fullInput.trim()) {
|
|
584
|
-
submitInput(fullInput);
|
|
585
|
-
}
|
|
586
|
-
currentInput = '';
|
|
587
|
-
}
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
// 检查是否进入多行模式
|
|
591
|
-
if ((0, multiline_input_1.shouldEnterMultiline)(currentInput)) {
|
|
592
|
-
(0, multiline_input_1.enterMultiline)(currentInput);
|
|
593
|
-
currentInput = '';
|
|
594
|
-
if (isV2UI()) {
|
|
595
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
596
|
-
}
|
|
597
|
-
else {
|
|
598
|
-
process.stdout.write('\r\x1b[2K');
|
|
599
|
-
process.stdout.write((0, multiline_input_1.renderContinuationPrompt)());
|
|
600
|
-
}
|
|
601
|
-
return;
|
|
602
|
-
}
|
|
603
|
-
// 正常发送输入
|
|
604
|
-
if (currentInput.trim()) {
|
|
605
|
-
submitInput(currentInput);
|
|
606
|
-
}
|
|
607
|
-
break;
|
|
608
|
-
case 'backspace':
|
|
609
|
-
currentInput = currentInput.slice(0, -1);
|
|
610
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
611
|
-
break;
|
|
612
|
-
case 'up':
|
|
613
|
-
// 进入历史导航
|
|
614
|
-
if (currentInput === '' && inputHistory.length > 0) {
|
|
615
|
-
historyMode = 'navigate';
|
|
616
|
-
historyIndex = 0;
|
|
617
|
-
currentInput = inputHistory[0]?.content || '';
|
|
618
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
619
|
-
}
|
|
620
|
-
break;
|
|
621
|
-
case 'r':
|
|
622
|
-
if (k.ctrl) {
|
|
623
|
-
// Ctrl+R:搜索历史
|
|
624
|
-
historyMode = 'search';
|
|
625
|
-
searchQuery = '';
|
|
626
|
-
showHistorySearchPrompt();
|
|
627
|
-
}
|
|
628
|
-
else if (char) {
|
|
629
|
-
currentInput += char;
|
|
630
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
631
|
-
}
|
|
632
|
-
break;
|
|
633
|
-
case 'c':
|
|
634
|
-
if (k.ctrl) {
|
|
635
|
-
// Ctrl+C:取消多行或退出
|
|
636
|
-
if ((0, multiline_input_1.isMultilineActive)()) {
|
|
637
|
-
(0, multiline_input_1.resetMultiline)();
|
|
638
|
-
currentInput = '';
|
|
639
|
-
(0, command_panel_1.redrawInputWithPrompt)('');
|
|
640
|
-
console.log(DIM('(cancelled)'));
|
|
641
|
-
}
|
|
642
|
-
else {
|
|
643
|
-
handleCtrlC();
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
else if (char) {
|
|
647
|
-
currentInput += char;
|
|
648
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
649
|
-
}
|
|
650
|
-
break;
|
|
651
|
-
case 'd':
|
|
652
|
-
if (k.ctrl && currentInput === '') {
|
|
653
|
-
// Ctrl+D:退出(空输入时)
|
|
654
|
-
void shutdownCli();
|
|
655
|
-
}
|
|
656
|
-
else if (char) {
|
|
657
|
-
currentInput += char;
|
|
658
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
659
|
-
}
|
|
660
|
-
break;
|
|
661
|
-
case '/':
|
|
662
|
-
// Issue #30 fix: 只在输入为空时触发命令面板(即 `/` 是第一个字符)
|
|
663
|
-
// 避免在 URL(http://)、路径(src/)、正则等场景误触发
|
|
664
|
-
if (currentInput === '' && !turnController.hasActiveTurn()) {
|
|
665
|
-
currentInput = '/';
|
|
666
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
667
|
-
(0, command_panel_1.showCommandPanel)('');
|
|
668
|
-
}
|
|
669
|
-
else {
|
|
670
|
-
// 正常添加 `/` 到输入
|
|
671
|
-
currentInput += '/';
|
|
672
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
673
|
-
}
|
|
674
|
-
break;
|
|
675
|
-
case '@':
|
|
676
|
-
// 显示文件补全
|
|
677
|
-
if (turnController.hasActiveTurn()) {
|
|
678
|
-
currentInput += '@';
|
|
679
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
680
|
-
}
|
|
681
|
-
else {
|
|
682
|
-
const baseInput = currentInput;
|
|
683
|
-
currentInput += '@';
|
|
684
|
-
(0, file_completion_1.showFileCompletion)('', baseInput);
|
|
685
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
686
|
-
}
|
|
687
|
-
break;
|
|
688
|
-
case '?':
|
|
689
|
-
if (currentInput === '' && store.getSnapshot().config.ui?.renderer === 'v2' && !turnController.hasActiveTurn()) {
|
|
690
|
-
showV2Shortcuts();
|
|
691
|
-
}
|
|
692
|
-
else {
|
|
693
|
-
currentInput += '?';
|
|
694
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
695
|
-
}
|
|
696
|
-
break;
|
|
697
|
-
default:
|
|
698
|
-
// 普通字符
|
|
699
|
-
if (char && char.length === 1 && !k.ctrl && !k.meta) {
|
|
700
|
-
currentInput += char;
|
|
701
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
function insertInputNewline() {
|
|
706
|
-
currentInput += '\n';
|
|
707
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
708
|
-
}
|
|
709
|
-
function updateHistorySearch() {
|
|
710
|
-
if (searchQuery) {
|
|
711
|
-
const allHistory = (0, global_config_1.getInputHistory)();
|
|
712
|
-
const matches = allHistory.filter(h => h.content.toLowerCase().includes(searchQuery.toLowerCase()));
|
|
713
|
-
historyIndex = 0;
|
|
714
|
-
currentInput = matches[0]?.content || '';
|
|
715
|
-
// Don't replace inputHistory — just display filtered results
|
|
716
|
-
}
|
|
717
|
-
else {
|
|
718
|
-
historyIndex = -1;
|
|
719
|
-
currentInput = '';
|
|
720
|
-
}
|
|
721
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput, `[Search: ${searchQuery}]`);
|
|
722
|
-
}
|
|
723
|
-
function showHistorySearchPrompt() {
|
|
724
|
-
(0, command_panel_1.clearRenderedInput)();
|
|
725
|
-
(0, command_panel_1.redrawInputWithPrompt)('', '[Search: ]');
|
|
726
|
-
}
|
|
727
|
-
function isV2UI() {
|
|
728
|
-
return store.getSnapshot().config.ui?.renderer === 'v2';
|
|
729
|
-
}
|
|
730
|
-
function showV2Shortcuts() {
|
|
731
|
-
console.log();
|
|
732
|
-
console.log((0, ui_v2_1.renderV2Shortcuts)(process.stdout.columns || 80));
|
|
733
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
734
|
-
}
|
|
735
|
-
function clearTerminalView() {
|
|
736
|
-
(0, command_panel_1.hideCommandPanel)();
|
|
737
|
-
(0, file_completion_1.hideFileCompletion)();
|
|
738
|
-
(0, command_panel_1.resetRenderLength)();
|
|
739
|
-
process.stdout.write('\x1Bc');
|
|
740
|
-
showBanner();
|
|
741
|
-
if (isV2UI()) {
|
|
742
|
-
console.log(DIM(' View cleared. Conversation context is preserved.'));
|
|
743
|
-
console.log((0, ui_v2_1.renderV2FooterHint)(process.stdout.columns || 80));
|
|
744
|
-
}
|
|
745
|
-
else {
|
|
746
|
-
console.log(DIM(' View cleared. Conversation context is preserved.'));
|
|
747
|
-
}
|
|
748
|
-
console.log();
|
|
749
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
750
|
-
}
|
|
751
|
-
function cancelActiveInputMode() {
|
|
752
|
-
if ((0, multiline_input_1.isMultilineActive)()) {
|
|
753
|
-
(0, multiline_input_1.resetMultiline)();
|
|
754
|
-
currentInput = '';
|
|
755
|
-
console.log(DIM('(cancelled)'));
|
|
756
|
-
(0, command_panel_1.redrawInputWithPrompt)('');
|
|
757
|
-
return true;
|
|
758
|
-
}
|
|
759
|
-
if ((0, command_panel_1.isPanelVisible)()) {
|
|
760
|
-
(0, command_panel_1.hideCommandPanel)();
|
|
761
|
-
(0, command_panel_1.clearPendingCommand)();
|
|
762
|
-
currentInput = '';
|
|
763
|
-
(0, command_panel_1.redrawInputWithPrompt)('');
|
|
764
|
-
return true;
|
|
765
|
-
}
|
|
766
|
-
if ((0, file_completion_1.isFileCompletionVisible)()) {
|
|
767
|
-
(0, file_completion_1.hideFileCompletion)();
|
|
768
|
-
currentInput = (0, file_completion_1.getBaseInput)() + '@' + (0, file_completion_1.getFileQuery)();
|
|
769
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
770
|
-
return true;
|
|
771
|
-
}
|
|
772
|
-
if (resumePickerState?.visible) {
|
|
773
|
-
hideResumePicker();
|
|
774
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
775
|
-
return true;
|
|
776
|
-
}
|
|
777
|
-
if (historyMode !== 'none') {
|
|
778
|
-
historyMode = 'none';
|
|
779
|
-
searchQuery = '';
|
|
780
|
-
historyIndex = -1;
|
|
781
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
782
|
-
return true;
|
|
783
|
-
}
|
|
784
|
-
return false;
|
|
785
|
-
}
|
|
786
|
-
async function shutdownCli() {
|
|
787
|
-
if (isShuttingDown)
|
|
788
|
-
return;
|
|
789
|
-
isShuttingDown = true;
|
|
790
|
-
// 保存会话摘要后退出
|
|
791
|
-
if (currentSession) {
|
|
792
|
-
const messages = (0, session_storage_1.readSessionMessages)(currentSession.id);
|
|
793
|
-
if (messages.length > 0) {
|
|
794
|
-
(0, session_storage_1.updateSessionSummary)(currentSession.id, messages);
|
|
795
|
-
}
|
|
796
|
-
(0, session_storage_1.endSession)(currentSession.id);
|
|
797
|
-
}
|
|
798
|
-
// 关闭 stdin raw mode 并退出
|
|
799
|
-
if (process.stdin.isTTY) {
|
|
800
|
-
process.stdin.setRawMode(false);
|
|
801
|
-
}
|
|
802
|
-
process.stdin.pause();
|
|
803
|
-
console.log();
|
|
804
|
-
console.log(DIM('Shutting down...'));
|
|
805
|
-
mcp_1.mcpManager.disconnectAll();
|
|
806
|
-
await runtime.shutdown();
|
|
807
|
-
console.log(SUCCESS('Goodbye! 🐴'));
|
|
808
|
-
process.exit(0);
|
|
809
|
-
}
|
|
810
|
-
async function handleCtrlC() {
|
|
811
|
-
if (cancelActiveInputMode()) {
|
|
812
|
-
turnController.clearExitIntent();
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
815
|
-
if (turnController.hasActiveTurn()) {
|
|
816
|
-
const shouldExit = turnController.registerExitIntent();
|
|
817
|
-
turnController.interruptActiveTurn();
|
|
818
|
-
if (shouldExit) {
|
|
819
|
-
await shutdownCli();
|
|
820
|
-
return;
|
|
821
|
-
}
|
|
822
|
-
(0, command_panel_1.writeLinePreservingInput)(DIM('Interrupted. Press Ctrl+C again to exit.'));
|
|
823
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
824
|
-
return;
|
|
825
|
-
}
|
|
826
|
-
if (turnController.registerExitIntent()) {
|
|
827
|
-
await shutdownCli();
|
|
828
|
-
return;
|
|
829
|
-
}
|
|
830
|
-
(0, command_panel_1.writeLinePreservingInput)(DIM('Press Ctrl+C again to exit.'));
|
|
831
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
832
|
-
}
|
|
833
|
-
// ============================================================================
|
|
834
|
-
// Banner
|
|
835
|
-
// ============================================================================
|
|
836
|
-
function showBanner() {
|
|
837
|
-
const config = store.getSnapshot().config;
|
|
838
|
-
const baseUrl = config.apiBaseUrl || '';
|
|
839
|
-
const provider = baseUrl.includes('anthropic') ? 'Anthropic'
|
|
840
|
-
: baseUrl.includes('openai') ? 'OpenAI'
|
|
841
|
-
: baseUrl.includes('dashscope') ? 'Alibaba Cloud'
|
|
842
|
-
: 'Custom';
|
|
843
|
-
if (config.ui?.renderer === 'v2') {
|
|
844
|
-
console.log();
|
|
845
|
-
console.log((0, ui_v2_1.renderV2ShellHeader)({
|
|
846
|
-
provider,
|
|
847
|
-
model: config.model,
|
|
848
|
-
projectPath: process.cwd(),
|
|
849
|
-
status: llm ? 'ready' : 'loading',
|
|
850
|
-
statusText: llm ? 'ready' : 'Set OPENHORSE_API_KEY',
|
|
851
|
-
version: VERSION,
|
|
852
|
-
}));
|
|
853
|
-
return;
|
|
854
|
-
}
|
|
855
|
-
console.log();
|
|
856
|
-
console.log((0, box_1.renderHeaderBox)({
|
|
857
|
-
provider,
|
|
858
|
-
model: config.model,
|
|
859
|
-
endpoint: baseUrl,
|
|
860
|
-
status: llm ? 'ready' : 'loading',
|
|
861
|
-
statusText: llm ? undefined : 'Set OPENHORSE_API_KEY in .env',
|
|
862
|
-
version: VERSION,
|
|
863
|
-
}));
|
|
864
|
-
}
|
|
865
|
-
// ============================================================================
|
|
866
|
-
// 输入处理
|
|
867
|
-
// ============================================================================
|
|
868
|
-
function getPrompt() {
|
|
869
|
-
const mode = store.getSnapshot().permissionMode;
|
|
870
|
-
const modeText = (0, types_1.getModeDisplayText)(mode);
|
|
871
|
-
const modeIndicator = modeText ? `[${modeText}] ` : '';
|
|
872
|
-
return ACCENT('❯ ') + DIM(modeIndicator);
|
|
873
|
-
}
|
|
874
|
-
async function handleInput(input, abortSignal, options = {}) {
|
|
875
|
-
const redrawPrompt = options.redrawPrompt !== false;
|
|
876
|
-
const shouldUpdateStatus = options.updateStatus !== false;
|
|
877
|
-
const text = input.trim();
|
|
878
|
-
if (!text)
|
|
879
|
-
return;
|
|
880
|
-
// 不在这里打印输入,readline 已经显示了
|
|
881
|
-
// 直接交给 executeChat 处理,它有自己的 spinner 和流式输出
|
|
882
|
-
const ctx = {
|
|
883
|
-
cwd: process.cwd(),
|
|
884
|
-
config: store.getSnapshot().config,
|
|
885
|
-
store,
|
|
886
|
-
llm,
|
|
887
|
-
runtime,
|
|
888
|
-
sessionId: currentSession?.id,
|
|
889
|
-
ensureSession: ensureCurrentSession,
|
|
890
|
-
setSession: setCurrentSession,
|
|
891
|
-
getSession: getCurrentSession,
|
|
892
|
-
abortSignal,
|
|
893
|
-
writeOutput: command_panel_1.writeOutputPreservingInput,
|
|
894
|
-
writeLine: command_panel_1.writeLinePreservingInput,
|
|
895
|
-
};
|
|
896
|
-
let pendingSessionPicker;
|
|
897
|
-
try {
|
|
898
|
-
const parsed = (0, parser_1.parseInput)(text);
|
|
899
|
-
if (parsed.isCommand) {
|
|
900
|
-
const cmd = (0, commands_1.findCommand)(parsed.name);
|
|
901
|
-
if (cmd) {
|
|
902
|
-
const result = await cmd.execute(ctx, parsed.args);
|
|
903
|
-
pendingSessionPicker = result.sessionPicker;
|
|
904
|
-
if (result.continueAsChat) {
|
|
905
|
-
await (0, commands_1.executeChat)(ctx, result.chatInput ?? parsed.args);
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
else {
|
|
909
|
-
if ((0, skills_1.hasMatchingSkill)(text)) {
|
|
910
|
-
await (0, commands_1.executeChat)(ctx, text);
|
|
911
|
-
}
|
|
912
|
-
else {
|
|
913
|
-
console.log();
|
|
914
|
-
console.log(ERROR(`Unknown command: /${parsed.name}`));
|
|
915
|
-
const suggestions = (0, parser_1.buildCommandSuggestions)(parsed.name);
|
|
916
|
-
if (suggestions.length > 0) {
|
|
917
|
-
console.log(DIM(`Did you mean: ${suggestions.map(s => `/${s}`).join(', ')}?`));
|
|
918
|
-
}
|
|
919
|
-
console.log();
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
else {
|
|
924
|
-
// 直接 chat - executeChat 有自己的 spinner 和流式输出
|
|
925
|
-
await (0, commands_1.executeChat)(ctx, text);
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
catch (err) {
|
|
929
|
-
console.log(ERROR(`Error: ${err.message || String(err)}`));
|
|
930
|
-
}
|
|
931
|
-
// 显示状态栏
|
|
932
|
-
if (shouldUpdateStatus) {
|
|
933
|
-
updateStatusBar();
|
|
934
|
-
}
|
|
935
|
-
// 重新显示 prompt
|
|
936
|
-
if (redrawPrompt) {
|
|
937
|
-
(0, command_panel_1.redrawInputWithPrompt)(currentInput);
|
|
938
|
-
}
|
|
939
|
-
if (pendingSessionPicker) {
|
|
940
|
-
showResumePicker(pendingSessionPicker);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
/**
|
|
944
|
-
* 更新状态栏显示
|
|
945
|
-
*/
|
|
946
|
-
function updateStatusBar() {
|
|
947
|
-
const snapshot = store.getSnapshot();
|
|
948
|
-
const usage = snapshot.tokenUsage;
|
|
949
|
-
const costStats = snapshot.costTracker.getSessionStats();
|
|
950
|
-
const mcpStatus = mcp_1.mcpManager.getStatus();
|
|
951
|
-
const stats = {
|
|
952
|
-
model: snapshot.currentModel,
|
|
953
|
-
tokens: usage ? usage.promptTokens + usage.completionTokens : 0,
|
|
954
|
-
promptTokens: usage?.promptTokens || 0,
|
|
955
|
-
completionTokens: usage?.completionTokens || 0,
|
|
956
|
-
cost: costStats.totalCost,
|
|
957
|
-
ctxPercent: (0, model_context_1.calculateCtxPercent)(usage ? usage.promptTokens + usage.completionTokens : 0, snapshot.currentModel || 'gpt-4o'),
|
|
958
|
-
mcpConnected: mcpStatus.filter(s => s.connected).length,
|
|
959
|
-
mcpTotal: mcpStatus.length,
|
|
960
|
-
};
|
|
961
|
-
// 在 prompt 上一行显示状态栏
|
|
962
|
-
if (snapshot.config.ui?.renderer === 'v2') {
|
|
963
|
-
(0, command_panel_1.setInputStatusText)((0, ui_v2_1.renderV2StatusBadge)({
|
|
964
|
-
...stats,
|
|
965
|
-
sessionId: currentSession?.id,
|
|
966
|
-
modeText: (0, types_1.getModeDisplayText)(snapshot.permissionMode) || undefined,
|
|
967
|
-
width: process.stdout.columns || 80,
|
|
968
|
-
}));
|
|
969
|
-
}
|
|
970
|
-
else {
|
|
971
|
-
console.log((0, status_bar_1.renderStatusBar)(stats));
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
// 接口变量(用于兼容性,主要逻辑通过 keypress 处理)
|
|
975
|
-
let rl = null;
|
|
976
|
-
// ============================================================================
|
|
977
|
-
// 主入口
|
|
978
|
-
// ============================================================================
|
|
979
|
-
async function main() {
|
|
980
|
-
// Parse command line arguments
|
|
981
|
-
const args = process.argv.slice(2);
|
|
982
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
983
|
-
showCliHelp();
|
|
984
|
-
process.exit(0);
|
|
985
|
-
}
|
|
986
|
-
if (args.includes('--version') || args.includes('-v')) {
|
|
987
|
-
console.log(`openhorse v${VERSION}`);
|
|
988
|
-
process.exit(0);
|
|
989
|
-
}
|
|
82
|
+
async function bootstrapRuntime(uiRenderer) {
|
|
990
83
|
(0, config_dir_1.ensureConfigDir)();
|
|
991
84
|
(0, global_config_1.recordFirstStartTime)();
|
|
992
|
-
const
|
|
993
|
-
const
|
|
994
|
-
const
|
|
995
|
-
(0, command_panel_1.setInputPromptRenderer)(cliConfig.ui?.renderer === 'v2' ? 'v2' : 'legacy');
|
|
996
|
-
(0, command_panel_1.setInputRenderContextProvider)(() => (cliConfig.ui?.renderer === 'v2' && (0, multiline_input_1.isMultilineActive)()
|
|
997
|
-
? { prefixLines: (0, multiline_input_1.getMultilineLines)() }
|
|
998
|
-
: {}));
|
|
999
|
-
(0, file_completion_1.setFileCompletionPromptRenderer)(cliConfig.ui?.renderer === 'v2' ? 'v2' : 'legacy');
|
|
1000
|
-
// Load project memory
|
|
1001
|
-
const memories = (0, storage_1.loadAllMemories)(projectPath);
|
|
85
|
+
const cwd = process.cwd();
|
|
86
|
+
const config = (0, config_1.loadConfig)({ ui: { renderer: uiRenderer } });
|
|
87
|
+
const memories = (0, storage_1.loadAllMemories)(cwd);
|
|
1002
88
|
const memoryContent = memories.length > 0
|
|
1003
|
-
? memories.map(
|
|
89
|
+
? memories.map(memory => `## ${memory.name} (${memory.type})\n${memory.content}`).join('\n\n')
|
|
1004
90
|
: '';
|
|
1005
|
-
// Load skills (builtin + user + project) and render the prompt section
|
|
1006
91
|
let skillsContent = '';
|
|
1007
92
|
try {
|
|
1008
93
|
const registry = (0, skills_1.getSkillsRegistry)();
|
|
1009
94
|
skillsContent = registry.generateSystemPromptInjection();
|
|
1010
95
|
}
|
|
1011
|
-
catch
|
|
1012
|
-
|
|
96
|
+
catch {
|
|
97
|
+
skillsContent = '';
|
|
1013
98
|
}
|
|
1014
|
-
store = new framework_1.Store({
|
|
1015
|
-
config
|
|
99
|
+
const store = new framework_1.Store({
|
|
100
|
+
config,
|
|
1016
101
|
tools: (0, tools_1.getRuntimeTools)(),
|
|
1017
|
-
currentModel:
|
|
102
|
+
currentModel: config.model,
|
|
1018
103
|
memoryContent,
|
|
1019
104
|
skillsContent,
|
|
1020
105
|
});
|
|
1021
|
-
// Mirror tool-state (todos/plan) into Store so the UI can observe it
|
|
1022
106
|
(0, framework_1.resetToolState)();
|
|
1023
|
-
(0, framework_1.subscribeToolState)(
|
|
107
|
+
(0, framework_1.subscribeToolState)(state => {
|
|
1024
108
|
store.setState({
|
|
1025
|
-
todos:
|
|
1026
|
-
planMode:
|
|
1027
|
-
currentPlan:
|
|
109
|
+
todos: state.todos,
|
|
110
|
+
planMode: state.planMode,
|
|
111
|
+
currentPlan: state.currentPlan,
|
|
1028
112
|
});
|
|
1029
113
|
});
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
(0, model_context_1.discoverModelContexts)(cliConfig.apiBaseUrl, cliConfig.apiKey)
|
|
1041
|
-
.then(models => {
|
|
1042
|
-
if (models.length > 0) {
|
|
1043
|
-
console.log(` Discovered ${models.length} models from API`);
|
|
1044
|
-
}
|
|
1045
|
-
})
|
|
1046
|
-
.catch(() => { }); // 静默失败,回退到内置数据库
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
catch (err) {
|
|
1050
|
-
console.log(WARN(`⚠ LLM initialization warning: ${err.message}`));
|
|
114
|
+
let llm = null;
|
|
115
|
+
if ((0, config_1.isConfigured)(config)) {
|
|
116
|
+
llm = new llm_1.LLMService({
|
|
117
|
+
apiKey: config.apiKey,
|
|
118
|
+
baseUrl: config.apiBaseUrl,
|
|
119
|
+
model: config.model,
|
|
120
|
+
fallbackModel: config.fallbackModel,
|
|
121
|
+
});
|
|
122
|
+
if (config.apiBaseUrl) {
|
|
123
|
+
(0, model_context_1.discoverModelContexts)(config.apiBaseUrl, config.apiKey).catch(() => undefined);
|
|
1051
124
|
}
|
|
1052
125
|
}
|
|
1053
|
-
const
|
|
1054
|
-
runtime = await (0, init_1.init)({
|
|
126
|
+
const runtime = await (0, init_1.init)({
|
|
1055
127
|
name: config.name,
|
|
1056
128
|
mode: config.mode,
|
|
1057
129
|
logLevel: config.logLevel,
|
|
1058
130
|
});
|
|
1059
131
|
await runtime.start();
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
store.setState({ tools: (0, tools_1.getRuntimeTools)() });
|
|
1064
|
-
})
|
|
1065
|
-
.catch(err => {
|
|
1066
|
-
console.error(WARN(`⚠ MCP startup error: ${err.message}`));
|
|
1067
|
-
});
|
|
1068
|
-
// Banner
|
|
1069
|
-
showBanner();
|
|
1070
|
-
// 提示
|
|
1071
|
-
console.log(SUCCESS('✔ System initialized'));
|
|
1072
|
-
console.log(DIM(' Type /help for commands, /exit to quit'));
|
|
1073
|
-
if (cliConfig.ui?.renderer === 'v2') {
|
|
1074
|
-
console.log(DIM(' UI v2 enabled: command palette and session picker'));
|
|
1075
|
-
console.log((0, ui_v2_1.renderV2FooterHint)(process.stdout.columns || 80));
|
|
1076
|
-
}
|
|
1077
|
-
if (!(0, config_1.isConfigured)(cliConfig)) {
|
|
1078
|
-
console.log(WARN(' ⚠ LLM not configured — set OPENHORSE_API_KEY'));
|
|
1079
|
-
}
|
|
1080
|
-
console.log();
|
|
1081
|
-
// 加载输入历史
|
|
1082
|
-
inputHistory = (0, global_config_1.getInputHistory)();
|
|
1083
|
-
// 启用 keypress 事件处理
|
|
1084
|
-
// 使用 emitKeypressEvents + setRawMode 实现交互式功能
|
|
1085
|
-
readline_1.default.emitKeypressEvents(process.stdin);
|
|
1086
|
-
if (process.stdin.isTTY) {
|
|
132
|
+
void (async () => {
|
|
133
|
+
const originalLog = console.log;
|
|
134
|
+
const originalError = console.error;
|
|
1087
135
|
try {
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
136
|
+
console.log = () => undefined;
|
|
137
|
+
console.error = () => undefined;
|
|
138
|
+
await mcp_1.mcpManager.connectAll();
|
|
139
|
+
store.setState({ tools: (0, tools_1.getRuntimeTools)() });
|
|
1092
140
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
console.log(WARN('⚠ stdin is not TTY - interactive features disabled'));
|
|
1096
|
-
}
|
|
1097
|
-
process.stdin.resume();
|
|
1098
|
-
// 监听 keypress 事件
|
|
1099
|
-
process.stdin.on('keypress', (char, key) => {
|
|
1100
|
-
try {
|
|
1101
|
-
handleKeypress(char, key);
|
|
141
|
+
catch {
|
|
142
|
+
// MCP failures are surfaced through /mcp and tool errors.
|
|
1102
143
|
}
|
|
1103
|
-
|
|
1104
|
-
console.
|
|
144
|
+
finally {
|
|
145
|
+
console.log = originalLog;
|
|
146
|
+
console.error = originalError;
|
|
147
|
+
}
|
|
148
|
+
})();
|
|
149
|
+
let currentSession = null;
|
|
150
|
+
let shuttingDown = false;
|
|
151
|
+
const ensureSession = () => {
|
|
152
|
+
if (!currentSession) {
|
|
153
|
+
currentSession = (0, session_storage_1.createSession)(cwd, store.getSnapshot().currentModel || store.getSnapshot().config.model);
|
|
154
|
+
(0, global_config_1.incrementSessionCount)();
|
|
155
|
+
}
|
|
156
|
+
return currentSession;
|
|
157
|
+
};
|
|
158
|
+
const setSession = (session) => {
|
|
159
|
+
currentSession = session;
|
|
160
|
+
};
|
|
161
|
+
const getSession = () => currentSession;
|
|
162
|
+
const shutdown = async () => {
|
|
163
|
+
if (shuttingDown)
|
|
164
|
+
return;
|
|
165
|
+
shuttingDown = true;
|
|
166
|
+
if (currentSession) {
|
|
167
|
+
const messages = (0, session_storage_1.readSessionMessages)(currentSession.id);
|
|
168
|
+
if (messages.length > 0) {
|
|
169
|
+
(0, session_storage_1.updateSessionSummary)(currentSession.id, messages);
|
|
170
|
+
}
|
|
171
|
+
(0, session_storage_1.endSession)(currentSession.id);
|
|
1105
172
|
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
173
|
+
await mcp_1.mcpManager.disconnectAll();
|
|
174
|
+
await runtime.shutdown();
|
|
175
|
+
};
|
|
176
|
+
return {
|
|
177
|
+
cwd,
|
|
178
|
+
version: VERSION,
|
|
179
|
+
config,
|
|
180
|
+
store,
|
|
181
|
+
llm,
|
|
182
|
+
runtime,
|
|
183
|
+
isConfigured: (0, config_1.isConfigured)(config),
|
|
184
|
+
ensureSession,
|
|
185
|
+
setSession,
|
|
186
|
+
getSession,
|
|
187
|
+
shutdown,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
async function main() {
|
|
191
|
+
const args = process.argv.slice(2);
|
|
192
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
193
|
+
showCliHelp();
|
|
194
|
+
process.exit(0);
|
|
1110
195
|
}
|
|
1111
|
-
(
|
|
196
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
197
|
+
console.log(`openhorse v${VERSION}`);
|
|
198
|
+
process.exit(0);
|
|
199
|
+
}
|
|
200
|
+
const uiRenderer = parseCliUIRenderer(args);
|
|
201
|
+
const runtime = await bootstrapRuntime(uiRenderer);
|
|
202
|
+
await (0, launch_1.launchInkUI)(runtime);
|
|
1112
203
|
}
|
|
1113
|
-
main().catch(
|
|
1114
|
-
console.error(ERROR('[OpenHorse] Fatal error:'),
|
|
204
|
+
main().catch(async (error) => {
|
|
205
|
+
console.error(ERROR('[OpenHorse] Fatal error:'), error);
|
|
206
|
+
try {
|
|
207
|
+
await mcp_1.mcpManager.disconnectAll();
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// ignore shutdown errors
|
|
211
|
+
}
|
|
1115
212
|
process.exit(1);
|
|
1116
213
|
});
|
|
1117
214
|
//# sourceMappingURL=cli.js.map
|