@robota-sdk/agent-cli 3.0.0-beta.5 → 3.0.0-beta.51
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 +195 -86
- package/dist/node/bin.js +11 -1
- package/dist/node/chunk-7EBJI2DW.js +2548 -0
- package/dist/node/index.cjs +2057 -719
- package/dist/node/index.d.cts +10 -23
- package/dist/node/index.d.ts +10 -23
- package/dist/node/index.js +1 -8
- package/package.json +16 -4
- package/dist/node/bin.cjs +0 -1217
- package/dist/node/bin.d.cts +0 -1
- package/dist/node/chunk-4DYVT4WI.js +0 -1196
package/dist/node/index.cjs
CHANGED
|
@@ -30,192 +30,624 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
Session: () => import_agent_sdk3.Session,
|
|
34
|
-
SessionStore: () => import_agent_sdk3.SessionStore,
|
|
35
|
-
TRUST_TO_MODE: () => import_agent_sdk3.TRUST_TO_MODE,
|
|
36
|
-
query: () => import_agent_sdk3.query,
|
|
37
33
|
startCli: () => startCli
|
|
38
34
|
});
|
|
39
35
|
module.exports = __toCommonJS(index_exports);
|
|
40
|
-
var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
|
|
41
36
|
|
|
42
37
|
// src/cli.ts
|
|
38
|
+
var import_node_fs3 = require("fs");
|
|
39
|
+
var import_node_path5 = require("path");
|
|
40
|
+
var import_node_url = require("url");
|
|
41
|
+
var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
|
|
42
|
+
var import_agent_sessions = require("@robota-sdk/agent-sessions");
|
|
43
|
+
|
|
44
|
+
// src/utils/cli-args.ts
|
|
43
45
|
var import_node_util = require("util");
|
|
46
|
+
var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
47
|
+
function parsePermissionMode(raw) {
|
|
48
|
+
if (raw === void 0) return void 0;
|
|
49
|
+
if (!VALID_MODES.includes(raw)) {
|
|
50
|
+
process.stderr.write(`Invalid --permission-mode "${raw}". Valid: ${VALID_MODES.join(" | ")}
|
|
51
|
+
`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
return raw;
|
|
55
|
+
}
|
|
56
|
+
function parseMaxTurns(raw) {
|
|
57
|
+
if (raw === void 0) return void 0;
|
|
58
|
+
const n = parseInt(raw, 10);
|
|
59
|
+
if (isNaN(n) || n <= 0) {
|
|
60
|
+
process.stderr.write(`Invalid --max-turns "${raw}". Must be a positive integer.
|
|
61
|
+
`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
return n;
|
|
65
|
+
}
|
|
66
|
+
function parseCliArgs() {
|
|
67
|
+
const { values, positionals } = (0, import_node_util.parseArgs)({
|
|
68
|
+
allowPositionals: true,
|
|
69
|
+
options: {
|
|
70
|
+
p: { type: "boolean", short: "p", default: false },
|
|
71
|
+
continue: { type: "boolean", short: "c", default: false },
|
|
72
|
+
resume: { type: "string", short: "r" },
|
|
73
|
+
model: { type: "string" },
|
|
74
|
+
language: { type: "string" },
|
|
75
|
+
"permission-mode": { type: "string" },
|
|
76
|
+
"max-turns": { type: "string" },
|
|
77
|
+
"fork-session": { type: "boolean", default: false },
|
|
78
|
+
name: { type: "string", short: "n" },
|
|
79
|
+
"output-format": { type: "string" },
|
|
80
|
+
"system-prompt": { type: "string" },
|
|
81
|
+
"append-system-prompt": { type: "string" },
|
|
82
|
+
version: { type: "boolean", default: false },
|
|
83
|
+
reset: { type: "boolean", default: false }
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
positional: positionals,
|
|
88
|
+
printMode: values["p"] ?? false,
|
|
89
|
+
continueMode: values["continue"] ?? false,
|
|
90
|
+
resumeId: values["resume"],
|
|
91
|
+
model: values["model"],
|
|
92
|
+
language: values["language"],
|
|
93
|
+
permissionMode: parsePermissionMode(values["permission-mode"]),
|
|
94
|
+
maxTurns: parseMaxTurns(values["max-turns"]),
|
|
95
|
+
forkSession: values["fork-session"] ?? false,
|
|
96
|
+
sessionName: values["name"],
|
|
97
|
+
outputFormat: values["output-format"],
|
|
98
|
+
systemPrompt: values["system-prompt"],
|
|
99
|
+
appendSystemPrompt: values["append-system-prompt"],
|
|
100
|
+
version: values["version"] ?? false,
|
|
101
|
+
reset: values["reset"] ?? false
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/utils/settings-io.ts
|
|
106
|
+
var import_node_fs = require("fs");
|
|
107
|
+
var import_node_path = require("path");
|
|
108
|
+
function getUserSettingsPath() {
|
|
109
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
|
|
110
|
+
return (0, import_node_path.join)(home, ".robota", "settings.json");
|
|
111
|
+
}
|
|
112
|
+
function readSettings(path) {
|
|
113
|
+
if (!(0, import_node_fs.existsSync)(path)) return {};
|
|
114
|
+
return JSON.parse((0, import_node_fs.readFileSync)(path, "utf8"));
|
|
115
|
+
}
|
|
116
|
+
function writeSettings(path, settings) {
|
|
117
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path), { recursive: true });
|
|
118
|
+
(0, import_node_fs.writeFileSync)(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
119
|
+
}
|
|
120
|
+
function updateModelInSettings(settingsPath, modelId) {
|
|
121
|
+
const settings = readSettings(settingsPath);
|
|
122
|
+
const provider = settings.provider ?? {};
|
|
123
|
+
provider.model = modelId;
|
|
124
|
+
settings.provider = provider;
|
|
125
|
+
writeSettings(settingsPath, settings);
|
|
126
|
+
}
|
|
127
|
+
function deleteSettings(path) {
|
|
128
|
+
if ((0, import_node_fs.existsSync)(path)) {
|
|
129
|
+
(0, import_node_fs.unlinkSync)(path);
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/utils/provider-factory.ts
|
|
44
136
|
var import_node_fs2 = require("fs");
|
|
45
137
|
var import_node_path2 = require("path");
|
|
46
|
-
var
|
|
47
|
-
var
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
138
|
+
var import_node_os = require("os");
|
|
139
|
+
var import_agent_provider_anthropic = require("@robota-sdk/agent-provider-anthropic");
|
|
140
|
+
function readProviderSettings(cwd) {
|
|
141
|
+
const paths = [
|
|
142
|
+
(0, import_node_path2.join)(cwd, ".robota", "settings.local.json"),
|
|
143
|
+
(0, import_node_path2.join)(cwd, ".robota", "settings.json"),
|
|
144
|
+
(0, import_node_path2.join)(cwd, ".claude", "settings.local.json"),
|
|
145
|
+
(0, import_node_path2.join)(cwd, ".claude", "settings.json"),
|
|
146
|
+
(0, import_node_path2.join)((0, import_node_os.homedir)(), ".robota", "settings.json"),
|
|
147
|
+
(0, import_node_path2.join)((0, import_node_os.homedir)(), ".claude", "settings.json")
|
|
148
|
+
];
|
|
149
|
+
for (const filePath of paths) {
|
|
150
|
+
if (!(0, import_node_fs2.existsSync)(filePath)) continue;
|
|
151
|
+
try {
|
|
152
|
+
const raw = (0, import_node_fs2.readFileSync)(filePath, "utf8");
|
|
153
|
+
const parsed = JSON.parse(raw);
|
|
154
|
+
const provider = parsed.provider;
|
|
155
|
+
if (provider?.apiKey && provider?.name) {
|
|
156
|
+
return {
|
|
157
|
+
name: provider.name,
|
|
158
|
+
model: provider.model ?? "claude-sonnet-4-6",
|
|
159
|
+
apiKey: provider.apiKey
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
} catch {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
58
165
|
}
|
|
59
|
-
|
|
166
|
+
throw new Error("No provider configuration found. Run `robota` to set up.");
|
|
60
167
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
168
|
+
function createProviderFromSettings(cwd, modelOverride) {
|
|
169
|
+
const settings = readProviderSettings(cwd);
|
|
170
|
+
const model = modelOverride ?? settings.model;
|
|
171
|
+
switch (settings.name) {
|
|
172
|
+
case "anthropic":
|
|
173
|
+
return new import_agent_provider_anthropic.AnthropicProvider({ apiKey: settings.apiKey, defaultModel: model });
|
|
174
|
+
default:
|
|
175
|
+
throw new Error(`Unknown provider: ${settings.name}. Currently supported: anthropic`);
|
|
176
|
+
}
|
|
68
177
|
}
|
|
69
178
|
|
|
179
|
+
// src/cli.ts
|
|
180
|
+
var import_agent_transport_headless = require("@robota-sdk/agent-transport-headless");
|
|
181
|
+
|
|
70
182
|
// src/ui/render.tsx
|
|
71
|
-
var
|
|
183
|
+
var import_ink15 = require("ink");
|
|
72
184
|
|
|
73
185
|
// src/ui/App.tsx
|
|
74
|
-
var
|
|
75
|
-
var
|
|
186
|
+
var import_react13 = require("react");
|
|
187
|
+
var import_ink14 = require("ink");
|
|
188
|
+
var import_agent_core4 = require("@robota-sdk/agent-core");
|
|
189
|
+
|
|
190
|
+
// src/ui/hooks/useInteractiveSession.ts
|
|
191
|
+
var import_react = require("react");
|
|
192
|
+
var import_node_os2 = require("os");
|
|
193
|
+
var import_node_path3 = require("path");
|
|
76
194
|
var import_agent_sdk = require("@robota-sdk/agent-sdk");
|
|
195
|
+
var import_agent_core = require("@robota-sdk/agent-core");
|
|
196
|
+
var import_node_crypto = require("crypto");
|
|
77
197
|
|
|
78
|
-
// src/
|
|
79
|
-
var
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
198
|
+
// src/ui/tui-state-manager.ts
|
|
199
|
+
var MAX_RENDERED_MESSAGES = 100;
|
|
200
|
+
var STREAMING_DEBOUNCE_MS = 300;
|
|
201
|
+
function createDebouncedNotify(notify, ms) {
|
|
202
|
+
let timer = null;
|
|
203
|
+
return {
|
|
204
|
+
schedule() {
|
|
205
|
+
if (!timer) {
|
|
206
|
+
timer = setTimeout(() => {
|
|
207
|
+
timer = null;
|
|
208
|
+
notify();
|
|
209
|
+
}, ms);
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
flush() {
|
|
213
|
+
if (timer) {
|
|
214
|
+
clearTimeout(timer);
|
|
215
|
+
timer = null;
|
|
216
|
+
}
|
|
89
217
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
var TuiStateManager = class {
|
|
221
|
+
// ── Rendering state ───────────────────────────────────────────
|
|
222
|
+
history = [];
|
|
223
|
+
streamingText = "";
|
|
224
|
+
activeTools = [];
|
|
225
|
+
isThinking = false;
|
|
226
|
+
isAborting = false;
|
|
227
|
+
pendingPrompt = null;
|
|
228
|
+
contextState = { percentage: 0, usedTokens: 0, maxTokens: 0 };
|
|
229
|
+
/** Called after any state change. React hook sets this to trigger re-render. */
|
|
230
|
+
onChange = null;
|
|
231
|
+
// ── Internal ──────────────────────────────────────────────────
|
|
232
|
+
streamBuf = "";
|
|
233
|
+
debouncedStreamNotify = createDebouncedNotify(() => this.notify(), STREAMING_DEBOUNCE_MS);
|
|
234
|
+
notify() {
|
|
235
|
+
this.onChange?.();
|
|
93
236
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
237
|
+
// ── Event handlers (InteractiveSession → state) ───────────────
|
|
238
|
+
onTextDelta = (delta) => {
|
|
239
|
+
this.streamBuf += delta;
|
|
240
|
+
this.streamingText = this.streamBuf;
|
|
241
|
+
this.debouncedStreamNotify.schedule();
|
|
242
|
+
};
|
|
243
|
+
onToolStart = (state) => {
|
|
244
|
+
this.activeTools = [...this.activeTools, state];
|
|
245
|
+
this.notify();
|
|
246
|
+
};
|
|
247
|
+
onToolEnd = (state) => {
|
|
248
|
+
const idx = this.activeTools.findIndex((t) => t.toolName === state.toolName && t.isRunning);
|
|
249
|
+
if (idx !== -1) {
|
|
250
|
+
const updated = [...this.activeTools];
|
|
251
|
+
updated[idx] = state;
|
|
252
|
+
this.activeTools = updated;
|
|
253
|
+
}
|
|
254
|
+
this.notify();
|
|
255
|
+
};
|
|
256
|
+
onThinking = (thinking) => {
|
|
257
|
+
this.isThinking = thinking;
|
|
258
|
+
if (thinking) {
|
|
259
|
+
this.debouncedStreamNotify.flush();
|
|
260
|
+
this.streamBuf = "";
|
|
261
|
+
this.streamingText = "";
|
|
262
|
+
this.activeTools = [];
|
|
263
|
+
} else {
|
|
264
|
+
this.isAborting = false;
|
|
103
265
|
}
|
|
104
|
-
|
|
266
|
+
this.notify();
|
|
267
|
+
};
|
|
268
|
+
onComplete = (result) => {
|
|
269
|
+
this.debouncedStreamNotify.flush();
|
|
270
|
+
this.streamBuf = "";
|
|
271
|
+
this.streamingText = "";
|
|
272
|
+
this.activeTools = [];
|
|
273
|
+
this.contextState = {
|
|
274
|
+
percentage: result.contextState.usedPercentage,
|
|
275
|
+
usedTokens: result.contextState.usedTokens,
|
|
276
|
+
maxTokens: result.contextState.maxTokens
|
|
277
|
+
};
|
|
278
|
+
this.notify();
|
|
279
|
+
};
|
|
280
|
+
onInterrupted = () => {
|
|
281
|
+
this.debouncedStreamNotify.flush();
|
|
282
|
+
this.streamBuf = "";
|
|
283
|
+
this.streamingText = "";
|
|
284
|
+
this.activeTools = [];
|
|
285
|
+
this.notify();
|
|
286
|
+
};
|
|
287
|
+
onError = () => {
|
|
288
|
+
this.debouncedStreamNotify.flush();
|
|
289
|
+
this.streamBuf = "";
|
|
290
|
+
this.streamingText = "";
|
|
291
|
+
this.activeTools = [];
|
|
292
|
+
this.notify();
|
|
293
|
+
};
|
|
294
|
+
// ── State updates from external sources ───────────────────────
|
|
295
|
+
/** Sync history from InteractiveSession */
|
|
296
|
+
syncHistory(entries) {
|
|
297
|
+
if (entries.length === 0) return;
|
|
298
|
+
this.history = entries.length > MAX_RENDERED_MESSAGES ? entries.slice(-MAX_RENDERED_MESSAGES) : [...entries];
|
|
299
|
+
this.notify();
|
|
105
300
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
source: "builtin",
|
|
117
|
-
subcommands: [
|
|
118
|
-
{ name: "plan", description: "Plan only, no execution", source: "builtin" },
|
|
119
|
-
{ name: "default", description: "Ask before risky actions", source: "builtin" },
|
|
120
|
-
{ name: "acceptEdits", description: "Auto-approve file edits", source: "builtin" },
|
|
121
|
-
{ name: "bypassPermissions", description: "Skip all permission checks", source: "builtin" }
|
|
122
|
-
]
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
name: "model",
|
|
126
|
-
description: "Select AI model",
|
|
127
|
-
source: "builtin",
|
|
128
|
-
subcommands: [
|
|
129
|
-
{ name: "claude-opus-4-6", description: "Opus 4.6 (highest quality)", source: "builtin" },
|
|
130
|
-
{ name: "claude-sonnet-4-6", description: "Sonnet 4.6 (balanced)", source: "builtin" },
|
|
131
|
-
{ name: "claude-haiku-4-5", description: "Haiku 4.5 (fastest)", source: "builtin" }
|
|
132
|
-
]
|
|
133
|
-
},
|
|
134
|
-
{ name: "compact", description: "Compress context window", source: "builtin" },
|
|
135
|
-
{ name: "cost", description: "Show session info", source: "builtin" },
|
|
136
|
-
{ name: "context", description: "Context window info", source: "builtin" },
|
|
137
|
-
{ name: "permissions", description: "Permission rules", source: "builtin" },
|
|
138
|
-
{ name: "exit", description: "Exit CLI", source: "builtin" }
|
|
139
|
-
];
|
|
140
|
-
}
|
|
141
|
-
var BuiltinCommandSource = class {
|
|
142
|
-
name = "builtin";
|
|
143
|
-
commands;
|
|
144
|
-
constructor() {
|
|
145
|
-
this.commands = createBuiltinCommands();
|
|
301
|
+
/** Add a single history entry */
|
|
302
|
+
addEntry(entry) {
|
|
303
|
+
const updated = [...this.history, entry];
|
|
304
|
+
this.history = updated.length > MAX_RENDERED_MESSAGES ? updated.slice(-MAX_RENDERED_MESSAGES) : updated;
|
|
305
|
+
this.notify();
|
|
306
|
+
}
|
|
307
|
+
/** Update pending prompt state */
|
|
308
|
+
setPendingPrompt(prompt) {
|
|
309
|
+
this.pendingPrompt = prompt;
|
|
310
|
+
this.notify();
|
|
146
311
|
}
|
|
147
|
-
|
|
148
|
-
|
|
312
|
+
/** Set aborting flag */
|
|
313
|
+
setAborting(aborting) {
|
|
314
|
+
this.isAborting = aborting;
|
|
315
|
+
this.notify();
|
|
316
|
+
}
|
|
317
|
+
/** Update context state */
|
|
318
|
+
setContextState(state) {
|
|
319
|
+
this.contextState = state;
|
|
320
|
+
this.notify();
|
|
149
321
|
}
|
|
150
322
|
};
|
|
151
323
|
|
|
152
|
-
// src/
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
324
|
+
// src/ui/hooks/useInteractiveSession.ts
|
|
325
|
+
function initializeSession(props, permissionHandler) {
|
|
326
|
+
const interactiveSession = new import_agent_sdk.InteractiveSession({
|
|
327
|
+
cwd: props.cwd,
|
|
328
|
+
provider: props.provider,
|
|
329
|
+
permissionMode: props.permissionMode,
|
|
330
|
+
maxTurns: props.maxTurns,
|
|
331
|
+
permissionHandler,
|
|
332
|
+
sessionStore: props.sessionStore,
|
|
333
|
+
resumeSessionId: props.resumeSessionId,
|
|
334
|
+
forkSession: props.forkSession,
|
|
335
|
+
sessionName: props.sessionName
|
|
336
|
+
});
|
|
337
|
+
const registry = new import_agent_sdk.CommandRegistry();
|
|
338
|
+
registry.addSource(new import_agent_sdk.BuiltinCommandSource());
|
|
339
|
+
registry.addSource(new import_agent_sdk.SkillCommandSource(props.cwd));
|
|
340
|
+
const pluginsDir = (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".robota", "plugins");
|
|
341
|
+
const loader = new import_agent_sdk.BundlePluginLoader(pluginsDir);
|
|
342
|
+
try {
|
|
343
|
+
const plugins = loader.loadPluginsSync();
|
|
344
|
+
if (plugins.length > 0) {
|
|
345
|
+
registry.addSource(new import_agent_sdk.PluginCommandSource(plugins));
|
|
172
346
|
}
|
|
347
|
+
} catch {
|
|
173
348
|
}
|
|
174
|
-
|
|
349
|
+
const manager = new TuiStateManager();
|
|
350
|
+
return { interactiveSession, registry, manager };
|
|
175
351
|
}
|
|
176
|
-
function
|
|
177
|
-
|
|
178
|
-
const
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
352
|
+
function useInteractiveSession(props) {
|
|
353
|
+
const [, forceRender] = (0, import_react.useState)(0);
|
|
354
|
+
const [permissionRequest, setPermissionRequest] = (0, import_react.useState)(null);
|
|
355
|
+
const permissionQueueRef = (0, import_react.useRef)([]);
|
|
356
|
+
const processingRef = (0, import_react.useRef)(false);
|
|
357
|
+
const processNextPermission = (0, import_react.useCallback)(() => {
|
|
358
|
+
if (processingRef.current) return;
|
|
359
|
+
const next = permissionQueueRef.current[0];
|
|
360
|
+
if (!next) {
|
|
361
|
+
setPermissionRequest(null);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
processingRef.current = true;
|
|
365
|
+
setPermissionRequest({
|
|
366
|
+
toolName: next.toolName,
|
|
367
|
+
toolArgs: next.toolArgs,
|
|
368
|
+
resolve: (result) => {
|
|
369
|
+
permissionQueueRef.current.shift();
|
|
370
|
+
processingRef.current = false;
|
|
371
|
+
setPermissionRequest(null);
|
|
372
|
+
next.resolve(result);
|
|
373
|
+
setTimeout(() => processNextPermission(), 0);
|
|
374
|
+
}
|
|
190
375
|
});
|
|
376
|
+
}, []);
|
|
377
|
+
const permissionHandler = (0, import_react.useCallback)(
|
|
378
|
+
(toolName, toolArgs) => new Promise((resolve) => {
|
|
379
|
+
permissionQueueRef.current.push({ toolName, toolArgs, resolve });
|
|
380
|
+
processNextPermission();
|
|
381
|
+
}),
|
|
382
|
+
[processNextPermission]
|
|
383
|
+
);
|
|
384
|
+
const stateRef = (0, import_react.useRef)(null);
|
|
385
|
+
if (stateRef.current === null) {
|
|
386
|
+
stateRef.current = initializeSession(props, permissionHandler);
|
|
191
387
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
this.cwd = cwd;
|
|
388
|
+
const { interactiveSession, registry, manager } = stateRef.current;
|
|
389
|
+
manager.onChange = () => forceRender((n) => n + 1);
|
|
390
|
+
if (manager.history.length === 0) {
|
|
391
|
+
const restored = interactiveSession.getFullHistory();
|
|
392
|
+
if (restored.length > 0) {
|
|
393
|
+
manager.syncHistory(restored);
|
|
394
|
+
}
|
|
200
395
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
396
|
+
(0, import_react.useEffect)(() => {
|
|
397
|
+
interactiveSession.on("text_delta", manager.onTextDelta);
|
|
398
|
+
interactiveSession.on("tool_start", manager.onToolStart);
|
|
399
|
+
interactiveSession.on("tool_end", manager.onToolEnd);
|
|
400
|
+
interactiveSession.on("thinking", manager.onThinking);
|
|
401
|
+
interactiveSession.on("complete", manager.onComplete);
|
|
402
|
+
interactiveSession.on("interrupted", manager.onInterrupted);
|
|
403
|
+
interactiveSession.on("error", manager.onError);
|
|
404
|
+
const initCheck = setInterval(() => {
|
|
405
|
+
try {
|
|
406
|
+
const ctx = interactiveSession.getContextState();
|
|
407
|
+
manager.setContextState({
|
|
408
|
+
percentage: ctx.usedPercentage,
|
|
409
|
+
usedTokens: ctx.usedTokens,
|
|
410
|
+
maxTokens: ctx.maxTokens
|
|
411
|
+
});
|
|
412
|
+
const restored = interactiveSession.getFullHistory();
|
|
413
|
+
if (restored.length > 0) {
|
|
414
|
+
manager.syncHistory(restored);
|
|
415
|
+
}
|
|
416
|
+
clearInterval(initCheck);
|
|
417
|
+
} catch {
|
|
210
418
|
}
|
|
419
|
+
}, 200);
|
|
420
|
+
return () => {
|
|
421
|
+
clearInterval(initCheck);
|
|
422
|
+
interactiveSession.off("text_delta", manager.onTextDelta);
|
|
423
|
+
interactiveSession.off("tool_start", manager.onToolStart);
|
|
424
|
+
interactiveSession.off("tool_end", manager.onToolEnd);
|
|
425
|
+
interactiveSession.off("thinking", manager.onThinking);
|
|
426
|
+
interactiveSession.off("complete", manager.onComplete);
|
|
427
|
+
interactiveSession.off("interrupted", manager.onInterrupted);
|
|
428
|
+
interactiveSession.off("error", manager.onError);
|
|
429
|
+
};
|
|
430
|
+
}, [interactiveSession, manager]);
|
|
431
|
+
(0, import_react.useEffect)(() => {
|
|
432
|
+
manager.syncHistory(interactiveSession.getFullHistory());
|
|
433
|
+
if (!manager.isThinking) {
|
|
434
|
+
manager.setPendingPrompt(interactiveSession.getPendingPrompt());
|
|
211
435
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
436
|
+
}, [manager.isThinking, interactiveSession, manager]);
|
|
437
|
+
const handleSubmit = (0, import_react.useCallback)(
|
|
438
|
+
async (input) => {
|
|
439
|
+
if (input.startsWith("/")) {
|
|
440
|
+
const parts = input.slice(1).split(/\s+/);
|
|
441
|
+
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
442
|
+
const args = parts.slice(1).join(" ");
|
|
443
|
+
const result = await interactiveSession.executeCommand(cmd, args);
|
|
444
|
+
if (result) {
|
|
445
|
+
manager.addEntry((0, import_agent_core.messageToHistoryEntry)((0, import_agent_core.createSystemMessage)(result.message)));
|
|
446
|
+
const effects = interactiveSession;
|
|
447
|
+
if (result.data?.modelId) {
|
|
448
|
+
effects._pendingModelId = result.data.modelId;
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (result.data?.language) {
|
|
452
|
+
effects._pendingLanguage = result.data.language;
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
if (result.data?.resetRequested) {
|
|
456
|
+
effects._resetRequested = true;
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
if (result.data?.triggerResumePicker) {
|
|
460
|
+
effects._triggerResumePicker = true;
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (result.data?.name) {
|
|
464
|
+
effects._sessionName = result.data.name;
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
const ctx = interactiveSession.getContextState();
|
|
468
|
+
manager.setContextState({
|
|
469
|
+
percentage: ctx.usedPercentage,
|
|
470
|
+
usedTokens: ctx.usedTokens,
|
|
471
|
+
maxTokens: ctx.maxTokens
|
|
472
|
+
});
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
476
|
+
if (skillCmd) {
|
|
477
|
+
manager.addEntry({
|
|
478
|
+
id: (0, import_node_crypto.randomUUID)(),
|
|
479
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
480
|
+
category: "event",
|
|
481
|
+
type: "skill-invocation",
|
|
482
|
+
data: {
|
|
483
|
+
skillName: cmd,
|
|
484
|
+
source: skillCmd.source,
|
|
485
|
+
message: `Invoking ${skillCmd.source}: ${cmd}`
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
const prompt = await (0, import_agent_sdk.buildSkillPrompt)(input, registry);
|
|
489
|
+
if (prompt) {
|
|
490
|
+
const qualifiedName = registry.resolveQualifiedName(cmd);
|
|
491
|
+
const hookInput = qualifiedName ? `/${qualifiedName}${input.slice(1 + cmd.length)}` : input;
|
|
492
|
+
await interactiveSession.submit(prompt, input, hookInput);
|
|
493
|
+
manager.setPendingPrompt(interactiveSession.getPendingPrompt());
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
if (cmd === "exit") {
|
|
498
|
+
interactiveSession._exitRequested = true;
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
if (cmd === "plugin") {
|
|
502
|
+
interactiveSession._triggerPluginTUI = true;
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
manager.addEntry(
|
|
506
|
+
(0, import_agent_core.messageToHistoryEntry)(
|
|
507
|
+
(0, import_agent_core.createSystemMessage)(`Unknown command "/${cmd}". Type /help for help.`)
|
|
508
|
+
)
|
|
509
|
+
);
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
await interactiveSession.submit(input);
|
|
513
|
+
manager.setPendingPrompt(interactiveSession.getPendingPrompt());
|
|
514
|
+
},
|
|
515
|
+
[interactiveSession, registry, manager]
|
|
516
|
+
);
|
|
517
|
+
const handleAbort = (0, import_react.useCallback)(() => {
|
|
518
|
+
manager.setAborting(true);
|
|
519
|
+
interactiveSession.abort();
|
|
520
|
+
}, [interactiveSession, manager]);
|
|
521
|
+
const handleCancelQueue = (0, import_react.useCallback)(() => {
|
|
522
|
+
interactiveSession.cancelQueue();
|
|
523
|
+
manager.setPendingPrompt(null);
|
|
524
|
+
}, [interactiveSession, manager]);
|
|
525
|
+
return {
|
|
526
|
+
interactiveSession,
|
|
527
|
+
registry,
|
|
528
|
+
history: manager.history,
|
|
529
|
+
addEntry: (entry) => manager.addEntry(entry),
|
|
530
|
+
streamingText: manager.streamingText,
|
|
531
|
+
activeTools: manager.activeTools,
|
|
532
|
+
isThinking: manager.isThinking,
|
|
533
|
+
isAborting: manager.isAborting,
|
|
534
|
+
pendingPrompt: manager.pendingPrompt,
|
|
535
|
+
permissionRequest,
|
|
536
|
+
contextState: manager.contextState,
|
|
537
|
+
handleSubmit,
|
|
538
|
+
handleAbort,
|
|
539
|
+
handleCancelQueue
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// src/ui/hooks/usePluginCallbacks.ts
|
|
544
|
+
var import_react2 = require("react");
|
|
545
|
+
var import_node_os3 = require("os");
|
|
546
|
+
var import_node_path4 = require("path");
|
|
547
|
+
var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
|
|
548
|
+
function usePluginCallbacks(cwd) {
|
|
549
|
+
return (0, import_react2.useMemo)(() => {
|
|
550
|
+
const home = (0, import_node_os3.homedir)();
|
|
551
|
+
const pluginsDir = (0, import_node_path4.join)(home, ".robota", "plugins");
|
|
552
|
+
const userSettingsPath = (0, import_node_path4.join)(home, ".robota", "settings.json");
|
|
553
|
+
const settingsStore = new import_agent_sdk2.PluginSettingsStore(userSettingsPath);
|
|
554
|
+
const marketplace = new import_agent_sdk2.MarketplaceClient({ pluginsDir });
|
|
555
|
+
const installer = new import_agent_sdk2.BundlePluginInstaller({
|
|
556
|
+
pluginsDir,
|
|
557
|
+
settingsStore,
|
|
558
|
+
marketplaceClient: marketplace
|
|
559
|
+
});
|
|
560
|
+
const loader = new import_agent_sdk2.BundlePluginLoader(pluginsDir);
|
|
561
|
+
return {
|
|
562
|
+
listInstalled: async () => {
|
|
563
|
+
const plugins = await loader.loadAll();
|
|
564
|
+
const enabledMap = settingsStore.getEnabledPlugins();
|
|
565
|
+
return plugins.map((p) => {
|
|
566
|
+
const parts = p.pluginDir.split("/");
|
|
567
|
+
const cacheIdx = parts.indexOf("cache");
|
|
568
|
+
const marketplaceName = cacheIdx >= 0 ? parts[cacheIdx + 1] : "";
|
|
569
|
+
const fullId = marketplaceName ? `${p.manifest.name}@${marketplaceName}` : p.manifest.name;
|
|
570
|
+
return {
|
|
571
|
+
name: fullId,
|
|
572
|
+
description: p.manifest.description,
|
|
573
|
+
enabled: enabledMap[fullId] !== false && enabledMap[p.manifest.name] !== false
|
|
574
|
+
};
|
|
575
|
+
});
|
|
576
|
+
},
|
|
577
|
+
listAvailablePlugins: async (marketplaceName) => {
|
|
578
|
+
let manifest;
|
|
579
|
+
try {
|
|
580
|
+
manifest = marketplace.fetchManifest(marketplaceName);
|
|
581
|
+
} catch {
|
|
582
|
+
return [];
|
|
583
|
+
}
|
|
584
|
+
const installed = installer.getInstalledPlugins();
|
|
585
|
+
const installedNames = new Set(Object.values(installed).map((r) => r.pluginName));
|
|
586
|
+
return manifest.plugins.map((p) => ({
|
|
587
|
+
name: p.name,
|
|
588
|
+
description: p.description,
|
|
589
|
+
installed: installedNames.has(p.name)
|
|
590
|
+
}));
|
|
591
|
+
},
|
|
592
|
+
install: async (pluginId, scope) => {
|
|
593
|
+
const [name, marketplaceName] = pluginId.split("@");
|
|
594
|
+
if (!name || !marketplaceName) {
|
|
595
|
+
throw new Error("Plugin ID must be in format: name@marketplace");
|
|
596
|
+
}
|
|
597
|
+
if (scope === "project") {
|
|
598
|
+
const projectPluginsDir = (0, import_node_path4.join)(cwd, ".robota", "plugins");
|
|
599
|
+
const projectInstaller = new import_agent_sdk2.BundlePluginInstaller({
|
|
600
|
+
pluginsDir: projectPluginsDir,
|
|
601
|
+
settingsStore,
|
|
602
|
+
marketplaceClient: marketplace
|
|
603
|
+
});
|
|
604
|
+
await projectInstaller.install(name, marketplaceName);
|
|
605
|
+
} else {
|
|
606
|
+
await installer.install(name, marketplaceName);
|
|
607
|
+
}
|
|
608
|
+
},
|
|
609
|
+
uninstall: async (pluginId) => {
|
|
610
|
+
await installer.uninstall(pluginId);
|
|
611
|
+
},
|
|
612
|
+
enable: async (pluginId) => {
|
|
613
|
+
await installer.enable(pluginId);
|
|
614
|
+
},
|
|
615
|
+
disable: async (pluginId) => {
|
|
616
|
+
await installer.disable(pluginId);
|
|
617
|
+
},
|
|
618
|
+
marketplaceAdd: async (source) => {
|
|
619
|
+
if (source.includes("/") && !source.includes(":")) {
|
|
620
|
+
return marketplace.addMarketplace({ type: "github", repo: source });
|
|
621
|
+
} else {
|
|
622
|
+
return marketplace.addMarketplace({ type: "git", url: source });
|
|
623
|
+
}
|
|
624
|
+
},
|
|
625
|
+
marketplaceRemove: async (name) => {
|
|
626
|
+
const installedFromMarketplace = installer.getPluginsByMarketplace(name);
|
|
627
|
+
for (const record of installedFromMarketplace) {
|
|
628
|
+
await installer.uninstall(`${record.pluginName}@${record.marketplace}`);
|
|
629
|
+
}
|
|
630
|
+
marketplace.removeMarketplace(name);
|
|
631
|
+
},
|
|
632
|
+
marketplaceUpdate: async (name) => {
|
|
633
|
+
marketplace.updateMarketplace(name);
|
|
634
|
+
},
|
|
635
|
+
marketplaceList: async () => {
|
|
636
|
+
return marketplace.listMarketplaces().map((m) => ({
|
|
637
|
+
name: m.name,
|
|
638
|
+
type: m.source.type
|
|
639
|
+
}));
|
|
640
|
+
},
|
|
641
|
+
reloadPlugins: async () => {
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
}, [cwd]);
|
|
645
|
+
}
|
|
216
646
|
|
|
217
647
|
// src/ui/MessageList.tsx
|
|
218
|
-
var
|
|
648
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
649
|
+
var import_ink2 = require("ink");
|
|
650
|
+
var import_agent_core2 = require("@robota-sdk/agent-core");
|
|
219
651
|
|
|
220
652
|
// src/ui/render-markdown.ts
|
|
221
653
|
var import_marked = require("marked");
|
|
@@ -228,54 +660,201 @@ function renderMarkdown(md) {
|
|
|
228
660
|
return typeof result === "string" ? result.trimEnd() : md;
|
|
229
661
|
}
|
|
230
662
|
|
|
231
|
-
// src/ui/
|
|
663
|
+
// src/ui/DiffBlock.tsx
|
|
664
|
+
var import_ink = require("ink");
|
|
232
665
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
666
|
+
var MAX_DIFF_LINES = 12;
|
|
667
|
+
var TRUNCATED_SHOW = 10;
|
|
668
|
+
function DiffBlock({ file, lines }) {
|
|
669
|
+
const truncated = lines.length > MAX_DIFF_LINES;
|
|
670
|
+
const visible = truncated ? lines.slice(0, TRUNCATED_SHOW) : lines;
|
|
671
|
+
const remaining = lines.length - TRUNCATED_SHOW;
|
|
672
|
+
const maxLineNum = Math.max(...visible.map((l) => l.lineNumber), 0);
|
|
673
|
+
const numWidth = String(maxLineNum).length;
|
|
674
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Box, { flexDirection: "column", marginLeft: 4, children: [
|
|
675
|
+
file && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "white", dimColor: true, children: [
|
|
676
|
+
"\u2502 ",
|
|
677
|
+
file
|
|
678
|
+
] }),
|
|
679
|
+
visible.map((line, i) => {
|
|
680
|
+
const lineNum = String(line.lineNumber).padStart(numWidth, " ");
|
|
681
|
+
if (line.type === "context") {
|
|
682
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "white", dimColor: true, children: [
|
|
683
|
+
"\u2502 ",
|
|
684
|
+
lineNum,
|
|
685
|
+
" ",
|
|
686
|
+
line.text
|
|
687
|
+
] }, i);
|
|
688
|
+
}
|
|
689
|
+
const prefix = line.type === "remove" ? "-" : "+";
|
|
690
|
+
const bgColor = line.type === "remove" ? "#5c1a1a" : "#1a3d1a";
|
|
691
|
+
const fgColor = line.type === "remove" ? "#ff9999" : "#99ff99";
|
|
692
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: fgColor, backgroundColor: bgColor, children: [
|
|
693
|
+
"\u2502 ",
|
|
694
|
+
lineNum,
|
|
695
|
+
" ",
|
|
696
|
+
prefix,
|
|
697
|
+
" ",
|
|
698
|
+
line.text
|
|
699
|
+
] }, i);
|
|
700
|
+
}),
|
|
701
|
+
truncated && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink.Text, { color: "white", dimColor: true, children: [
|
|
702
|
+
"\u2502 ... and ",
|
|
703
|
+
remaining,
|
|
704
|
+
" more lines"
|
|
705
|
+
] })
|
|
706
|
+
] });
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// src/ui/MessageList.tsx
|
|
710
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
233
711
|
function RoleLabel({ role }) {
|
|
234
712
|
switch (role) {
|
|
235
713
|
case "user":
|
|
236
|
-
return /* @__PURE__ */ (0,
|
|
714
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "green", bold: true, children: [
|
|
237
715
|
"You:",
|
|
238
716
|
" "
|
|
239
717
|
] });
|
|
240
718
|
case "assistant":
|
|
241
|
-
return /* @__PURE__ */ (0,
|
|
719
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "cyan", bold: true, children: [
|
|
242
720
|
"Robota:",
|
|
243
721
|
" "
|
|
244
722
|
] });
|
|
245
723
|
case "system":
|
|
246
|
-
return /* @__PURE__ */ (0,
|
|
724
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "yellow", bold: true, children: [
|
|
247
725
|
"System:",
|
|
248
726
|
" "
|
|
249
727
|
] });
|
|
250
728
|
case "tool":
|
|
251
|
-
return /* @__PURE__ */ (0,
|
|
729
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", bold: true, children: [
|
|
252
730
|
"Tool:",
|
|
253
731
|
" "
|
|
254
732
|
] });
|
|
255
733
|
}
|
|
256
734
|
}
|
|
257
|
-
function
|
|
258
|
-
|
|
259
|
-
/* @__PURE__ */ (0,
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
735
|
+
function ToolMessage({ message }) {
|
|
736
|
+
if (!(0, import_agent_core2.isToolMessage)(message)) {
|
|
737
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
|
|
738
|
+
}
|
|
739
|
+
const toolName = message.name;
|
|
740
|
+
const content = message.content;
|
|
741
|
+
let summaries = null;
|
|
742
|
+
try {
|
|
743
|
+
const parsed = JSON.parse(content);
|
|
744
|
+
if (Array.isArray(parsed) && parsed.length > 0 && typeof parsed[0].line === "string") {
|
|
745
|
+
summaries = parsed;
|
|
746
|
+
}
|
|
747
|
+
} catch {
|
|
748
|
+
}
|
|
749
|
+
if (summaries) {
|
|
750
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
751
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { children: [
|
|
752
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", bold: true, children: [
|
|
753
|
+
"Tool:",
|
|
754
|
+
" "
|
|
755
|
+
] }),
|
|
756
|
+
toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
|
|
757
|
+
"[",
|
|
758
|
+
toolName,
|
|
759
|
+
"]"
|
|
760
|
+
] })
|
|
761
|
+
] }),
|
|
762
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
|
|
763
|
+
summaries.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", children: [
|
|
764
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "green", children: [
|
|
765
|
+
" ",
|
|
766
|
+
"\u2713",
|
|
767
|
+
" ",
|
|
768
|
+
s.line
|
|
769
|
+
] }),
|
|
770
|
+
s.diffLines && s.diffLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DiffBlock, { file: s.diffFile, lines: s.diffLines })
|
|
771
|
+
] }, i))
|
|
772
|
+
] });
|
|
773
|
+
}
|
|
774
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
775
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
776
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { children: [
|
|
777
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", bold: true, children: [
|
|
778
|
+
"Tool:",
|
|
265
779
|
" "
|
|
780
|
+
] }),
|
|
781
|
+
toolName && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
|
|
782
|
+
"[",
|
|
783
|
+
toolName,
|
|
784
|
+
"]"
|
|
266
785
|
] })
|
|
267
786
|
] }),
|
|
268
|
-
/* @__PURE__ */ (0,
|
|
269
|
-
|
|
787
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
|
|
788
|
+
lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "green", children: [
|
|
789
|
+
" ",
|
|
790
|
+
"\u2713",
|
|
791
|
+
" ",
|
|
792
|
+
line
|
|
793
|
+
] }, i))
|
|
794
|
+
] });
|
|
795
|
+
}
|
|
796
|
+
var MessageItem = import_react3.default.memo(function MessageItem2({
|
|
797
|
+
message
|
|
798
|
+
}) {
|
|
799
|
+
if ((0, import_agent_core2.isToolMessage)(message)) {
|
|
800
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolMessage, { message });
|
|
801
|
+
}
|
|
802
|
+
const content = message.content ?? "";
|
|
803
|
+
const isInterrupted = message.state === "interrupted";
|
|
804
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
805
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoleLabel, { role: message.role }) }),
|
|
806
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
|
|
807
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { wrap: "wrap", children: (0, import_agent_core2.isAssistantMessage)(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
|
|
808
|
+
] });
|
|
809
|
+
});
|
|
810
|
+
function ToolSummaryEntry({ entry }) {
|
|
811
|
+
const data = entry.data;
|
|
812
|
+
const lines = data?.summary?.split("\n") ?? [];
|
|
813
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
814
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "white", bold: true, children: [
|
|
815
|
+
"Tool:",
|
|
816
|
+
" "
|
|
817
|
+
] }) }),
|
|
818
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
|
|
819
|
+
lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "green", children: [
|
|
820
|
+
" ",
|
|
821
|
+
line
|
|
822
|
+
] }, i))
|
|
823
|
+
] });
|
|
824
|
+
}
|
|
825
|
+
function EventEntry({ entry }) {
|
|
826
|
+
const eventData = entry.data;
|
|
827
|
+
const eventMessage = typeof eventData?.message === "string" ? eventData.message : typeof eventData?.content === "string" ? eventData.content : entry.type;
|
|
828
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
829
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink2.Text, { color: "yellow", bold: true, children: [
|
|
830
|
+
"System:",
|
|
831
|
+
" "
|
|
832
|
+
] }) }),
|
|
833
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { children: " " }),
|
|
834
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Text, { wrap: "wrap", children: eventMessage }) })
|
|
270
835
|
] });
|
|
271
836
|
}
|
|
272
|
-
function
|
|
273
|
-
|
|
837
|
+
function EntryItem({ entry }) {
|
|
838
|
+
if (entry.category === "chat") {
|
|
839
|
+
const message = entry.data;
|
|
840
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MessageItem, { message });
|
|
841
|
+
}
|
|
842
|
+
if (entry.type === "tool-summary") {
|
|
843
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolSummaryEntry, { entry });
|
|
844
|
+
}
|
|
845
|
+
if (entry.type === "tool-start" || entry.type === "tool-end") {
|
|
846
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
|
|
847
|
+
}
|
|
848
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EventEntry, { entry });
|
|
849
|
+
}
|
|
850
|
+
function MessageList({ history }) {
|
|
851
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink2.Box, { flexDirection: "column", children: history.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EntryItem, { entry }, entry.id)) });
|
|
274
852
|
}
|
|
275
853
|
|
|
276
854
|
// src/ui/StatusBar.tsx
|
|
277
|
-
var
|
|
278
|
-
var
|
|
855
|
+
var import_ink3 = require("ink");
|
|
856
|
+
var import_agent_core3 = require("@robota-sdk/agent-core");
|
|
857
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
279
858
|
var CONTEXT_YELLOW_THRESHOLD = 70;
|
|
280
859
|
var CONTEXT_RED_THRESHOLD = 90;
|
|
281
860
|
function getContextColor(percentage) {
|
|
@@ -291,11 +870,12 @@ function StatusBar({
|
|
|
291
870
|
isThinking,
|
|
292
871
|
contextPercentage,
|
|
293
872
|
contextUsedTokens,
|
|
294
|
-
contextMaxTokens
|
|
873
|
+
contextMaxTokens,
|
|
874
|
+
sessionName
|
|
295
875
|
}) {
|
|
296
876
|
const contextColor = getContextColor(contextPercentage);
|
|
297
|
-
return /* @__PURE__ */ (0,
|
|
298
|
-
|
|
877
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
878
|
+
import_ink3.Box,
|
|
299
879
|
{
|
|
300
880
|
borderStyle: "single",
|
|
301
881
|
borderColor: "gray",
|
|
@@ -303,26 +883,30 @@ function StatusBar({
|
|
|
303
883
|
paddingRight: 1,
|
|
304
884
|
justifyContent: "space-between",
|
|
305
885
|
children: [
|
|
306
|
-
/* @__PURE__ */ (0,
|
|
307
|
-
/* @__PURE__ */ (0,
|
|
886
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink3.Text, { children: [
|
|
887
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { color: "cyan", bold: true, children: "Mode:" }),
|
|
308
888
|
" ",
|
|
309
|
-
/* @__PURE__ */ (0,
|
|
889
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { children: permissionMode }),
|
|
890
|
+
sessionName && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
891
|
+
" | ",
|
|
892
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { color: "magenta", children: sessionName })
|
|
893
|
+
] }),
|
|
310
894
|
" | ",
|
|
311
|
-
/* @__PURE__ */ (0,
|
|
895
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { dimColor: true, children: modelName }),
|
|
312
896
|
" | ",
|
|
313
|
-
/* @__PURE__ */ (0,
|
|
897
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink3.Text, { color: contextColor, children: [
|
|
314
898
|
"Context: ",
|
|
315
899
|
Math.round(contextPercentage),
|
|
316
900
|
"% (",
|
|
317
|
-
(
|
|
318
|
-
"
|
|
319
|
-
(
|
|
320
|
-
"
|
|
901
|
+
(0, import_agent_core3.formatTokenCount)(contextUsedTokens),
|
|
902
|
+
"/",
|
|
903
|
+
(0, import_agent_core3.formatTokenCount)(contextMaxTokens),
|
|
904
|
+
")"
|
|
321
905
|
] })
|
|
322
906
|
] }),
|
|
323
|
-
/* @__PURE__ */ (0,
|
|
324
|
-
isThinking && /* @__PURE__ */ (0,
|
|
325
|
-
/* @__PURE__ */ (0,
|
|
907
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink3.Text, { children: [
|
|
908
|
+
isThinking && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink3.Text, { color: "yellow", children: "Thinking... " }),
|
|
909
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink3.Text, { dimColor: true, children: [
|
|
326
910
|
"msgs: ",
|
|
327
911
|
messageCount
|
|
328
912
|
] })
|
|
@@ -333,146 +917,222 @@ function StatusBar({
|
|
|
333
917
|
}
|
|
334
918
|
|
|
335
919
|
// src/ui/InputArea.tsx
|
|
336
|
-
var
|
|
337
|
-
var
|
|
920
|
+
var import_react6 = __toESM(require("react"), 1);
|
|
921
|
+
var import_ink7 = require("ink");
|
|
338
922
|
|
|
339
923
|
// src/ui/CjkTextInput.tsx
|
|
340
|
-
var
|
|
341
|
-
var
|
|
924
|
+
var import_react4 = require("react");
|
|
925
|
+
var import_ink4 = require("ink");
|
|
926
|
+
var import_chalk = __toESM(require("chalk"), 1);
|
|
342
927
|
var import_string_width = __toESM(require("string-width"), 1);
|
|
343
|
-
var
|
|
344
|
-
var
|
|
928
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
929
|
+
var PASTE_START = "[200~";
|
|
930
|
+
var PASTE_END = "[201~";
|
|
931
|
+
function filterPrintable(input) {
|
|
932
|
+
if (!input || input.length === 0) return "";
|
|
933
|
+
return input.replace(/[\x00-\x1f\x7f]/g, "");
|
|
934
|
+
}
|
|
935
|
+
function insertAtCursor(value, cursor, input) {
|
|
936
|
+
const next = value.slice(0, cursor) + input + value.slice(cursor);
|
|
937
|
+
return { value: next, cursor: cursor + input.length };
|
|
938
|
+
}
|
|
939
|
+
function displayOffset(chars, charIndex, width) {
|
|
940
|
+
let offset = 0;
|
|
941
|
+
for (let i = 0; i < charIndex && i < chars.length; i++) {
|
|
942
|
+
const w = (0, import_string_width.default)(chars[i]);
|
|
943
|
+
const col = offset % width;
|
|
944
|
+
if (col + w > width) offset += width - col;
|
|
945
|
+
offset += w;
|
|
946
|
+
}
|
|
947
|
+
return offset;
|
|
948
|
+
}
|
|
949
|
+
function charIndexAtDisplayOffset(chars, target, width) {
|
|
950
|
+
let offset = 0;
|
|
951
|
+
for (let i = 0; i < chars.length; i++) {
|
|
952
|
+
if (offset >= target) return i;
|
|
953
|
+
const w = (0, import_string_width.default)(chars[i]);
|
|
954
|
+
const col = offset % width;
|
|
955
|
+
if (col + w > width) offset += width - col;
|
|
956
|
+
offset += w;
|
|
957
|
+
}
|
|
958
|
+
return chars.length;
|
|
959
|
+
}
|
|
345
960
|
function CjkTextInput({
|
|
346
961
|
value,
|
|
347
962
|
onChange,
|
|
348
963
|
onSubmit,
|
|
964
|
+
onPaste,
|
|
349
965
|
placeholder = "",
|
|
350
966
|
focus = true,
|
|
351
|
-
showCursor = true
|
|
967
|
+
showCursor = true,
|
|
968
|
+
availableWidth
|
|
352
969
|
}) {
|
|
353
|
-
const valueRef = (0,
|
|
354
|
-
const cursorRef = (0,
|
|
355
|
-
const [, forceRender] = (0,
|
|
356
|
-
const
|
|
970
|
+
const valueRef = (0, import_react4.useRef)(value);
|
|
971
|
+
const cursorRef = (0, import_react4.useRef)(value.length);
|
|
972
|
+
const [, forceRender] = (0, import_react4.useState)(0);
|
|
973
|
+
const isPastingRef = (0, import_react4.useRef)(false);
|
|
974
|
+
const pasteBufferRef = (0, import_react4.useRef)("");
|
|
357
975
|
if (value !== valueRef.current) {
|
|
358
976
|
valueRef.current = value;
|
|
359
|
-
|
|
360
|
-
cursorRef.current = value.length;
|
|
361
|
-
}
|
|
977
|
+
cursorRef.current = value.length;
|
|
362
978
|
}
|
|
363
|
-
(0,
|
|
979
|
+
(0, import_ink4.useInput)(
|
|
364
980
|
(input, key) => {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
if (cursorRef.current > 0) {
|
|
374
|
-
cursorRef.current -= 1;
|
|
375
|
-
forceRender((n) => n + 1);
|
|
981
|
+
try {
|
|
982
|
+
if (input === PASTE_START || input.startsWith(PASTE_START)) {
|
|
983
|
+
isPastingRef.current = true;
|
|
984
|
+
const afterMarker = input.slice(PASTE_START.length);
|
|
985
|
+
if (afterMarker.length > 0) {
|
|
986
|
+
pasteBufferRef.current += afterMarker;
|
|
987
|
+
}
|
|
988
|
+
return;
|
|
376
989
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
990
|
+
if (isPastingRef.current) {
|
|
991
|
+
if (input === PASTE_END || input.includes(PASTE_END)) {
|
|
992
|
+
const beforeMarker = input.split(PASTE_END)[0] ?? "";
|
|
993
|
+
pasteBufferRef.current += beforeMarker;
|
|
994
|
+
const text = pasteBufferRef.current.replace(/\r\n?/g, "\n");
|
|
995
|
+
pasteBufferRef.current = "";
|
|
996
|
+
isPastingRef.current = false;
|
|
997
|
+
if (text.length > 0) {
|
|
998
|
+
if (text.includes("\n") && onPaste) {
|
|
999
|
+
onPaste(text);
|
|
1000
|
+
} else {
|
|
1001
|
+
const printable2 = filterPrintable(text);
|
|
1002
|
+
if (printable2.length > 0) {
|
|
1003
|
+
const result2 = insertAtCursor(valueRef.current, cursorRef.current, printable2);
|
|
1004
|
+
cursorRef.current = result2.cursor;
|
|
1005
|
+
valueRef.current = result2.value;
|
|
1006
|
+
onChange(result2.value);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
} else {
|
|
1011
|
+
pasteBufferRef.current += input;
|
|
1012
|
+
}
|
|
1013
|
+
return;
|
|
383
1014
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
if (key.backspace || key.delete) {
|
|
387
|
-
if (cursorRef.current > 0) {
|
|
388
|
-
const v2 = valueRef.current;
|
|
389
|
-
const next2 = v2.slice(0, cursorRef.current - 1) + v2.slice(cursorRef.current);
|
|
390
|
-
cursorRef.current -= 1;
|
|
391
|
-
valueRef.current = next2;
|
|
392
|
-
onChange(next2);
|
|
1015
|
+
if (key.ctrl && input === "c" || key.tab || key.shift && key.tab) {
|
|
1016
|
+
return;
|
|
393
1017
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
1018
|
+
if (key.upArrow || key.downArrow) {
|
|
1019
|
+
if (availableWidth && availableWidth > 0) {
|
|
1020
|
+
const chars = [...valueRef.current];
|
|
1021
|
+
const offset = displayOffset(chars, cursorRef.current, availableWidth);
|
|
1022
|
+
const target = key.upArrow ? offset - availableWidth : offset + availableWidth;
|
|
1023
|
+
if (target >= 0) {
|
|
1024
|
+
const newCursor = charIndexAtDisplayOffset(chars, target, availableWidth);
|
|
1025
|
+
if (newCursor !== cursorRef.current) {
|
|
1026
|
+
cursorRef.current = newCursor;
|
|
1027
|
+
forceRender((n) => n + 1);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
if (key.return) {
|
|
1034
|
+
onSubmit?.(valueRef.current);
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
if (input.length > 1 && (input.includes("\n") || input.includes("\r")) && onPaste) {
|
|
1038
|
+
onPaste(input.replace(/\r\n?/g, "\n"));
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
if (key.leftArrow) {
|
|
1042
|
+
if (cursorRef.current > 0) {
|
|
1043
|
+
cursorRef.current -= 1;
|
|
1044
|
+
forceRender((n) => n + 1);
|
|
1045
|
+
}
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
if (key.rightArrow) {
|
|
1049
|
+
if (cursorRef.current < valueRef.current.length) {
|
|
1050
|
+
cursorRef.current += 1;
|
|
1051
|
+
forceRender((n) => n + 1);
|
|
1052
|
+
}
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
if (key.backspace || key.delete) {
|
|
1056
|
+
if (cursorRef.current > 0) {
|
|
1057
|
+
const v = valueRef.current;
|
|
1058
|
+
const next = v.slice(0, cursorRef.current - 1) + v.slice(cursorRef.current);
|
|
1059
|
+
cursorRef.current -= 1;
|
|
1060
|
+
valueRef.current = next;
|
|
1061
|
+
onChange(next);
|
|
1062
|
+
}
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
const printable = filterPrintable(input);
|
|
1066
|
+
if (printable.length === 0) return;
|
|
1067
|
+
const result = insertAtCursor(valueRef.current, cursorRef.current, printable);
|
|
1068
|
+
cursorRef.current = result.cursor;
|
|
1069
|
+
valueRef.current = result.value;
|
|
1070
|
+
onChange(result.value);
|
|
1071
|
+
} catch {
|
|
1072
|
+
}
|
|
1073
|
+
},
|
|
1074
|
+
{ isActive: focus }
|
|
1075
|
+
);
|
|
1076
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink4.Text, { children: renderWithCursor(valueRef.current, cursorRef.current, placeholder, showCursor && focus) });
|
|
1077
|
+
}
|
|
1078
|
+
function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
|
|
1079
|
+
if (!showCursor) {
|
|
1080
|
+
return value.length > 0 ? value : placeholder ? import_chalk.default.gray(placeholder) : "";
|
|
1081
|
+
}
|
|
1082
|
+
if (value.length === 0) {
|
|
417
1083
|
if (placeholder.length > 0) {
|
|
418
|
-
return
|
|
1084
|
+
return import_chalk.default.inverse(placeholder[0]) + import_chalk.default.gray(placeholder.slice(1));
|
|
419
1085
|
}
|
|
420
|
-
return
|
|
1086
|
+
return import_chalk.default.inverse(" ");
|
|
421
1087
|
}
|
|
422
1088
|
const chars = [...value];
|
|
423
1089
|
let rendered = "";
|
|
424
1090
|
for (let i = 0; i < chars.length; i++) {
|
|
425
1091
|
const char = chars[i] ?? "";
|
|
426
|
-
rendered += i === cursorOffset ?
|
|
1092
|
+
rendered += i === cursorOffset ? import_chalk.default.inverse(char) : char;
|
|
427
1093
|
}
|
|
428
1094
|
if (cursorOffset >= chars.length) {
|
|
429
|
-
rendered +=
|
|
1095
|
+
rendered += import_chalk.default.inverse(" ");
|
|
430
1096
|
}
|
|
431
1097
|
return rendered;
|
|
432
1098
|
}
|
|
433
1099
|
|
|
434
1100
|
// src/ui/WaveText.tsx
|
|
435
|
-
var
|
|
436
|
-
var
|
|
437
|
-
var
|
|
1101
|
+
var import_react5 = require("react");
|
|
1102
|
+
var import_ink5 = require("ink");
|
|
1103
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
438
1104
|
var WAVE_COLORS = ["#666666", "#888888", "#aaaaaa", "#888888"];
|
|
439
1105
|
var INTERVAL_MS = 400;
|
|
440
1106
|
var CHARS_PER_GROUP = 4;
|
|
441
1107
|
function WaveText({ text }) {
|
|
442
|
-
const [tick, setTick] = (0,
|
|
443
|
-
(0,
|
|
1108
|
+
const [tick, setTick] = (0, import_react5.useState)(0);
|
|
1109
|
+
(0, import_react5.useEffect)(() => {
|
|
444
1110
|
const timer = setInterval(() => {
|
|
445
1111
|
setTick((prev) => prev + 1);
|
|
446
1112
|
}, INTERVAL_MS);
|
|
447
1113
|
return () => clearInterval(timer);
|
|
448
1114
|
}, []);
|
|
449
1115
|
const chars = [...text];
|
|
450
|
-
return /* @__PURE__ */ (0,
|
|
1116
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Text, { children: chars.map((char, i) => {
|
|
451
1117
|
const group = Math.floor(i / CHARS_PER_GROUP);
|
|
452
1118
|
const colorIndex = (tick + group) % WAVE_COLORS.length;
|
|
453
|
-
return /* @__PURE__ */ (0,
|
|
1119
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Text, { color: WAVE_COLORS[colorIndex], children: char }, i);
|
|
454
1120
|
}) });
|
|
455
1121
|
}
|
|
456
1122
|
|
|
457
1123
|
// src/ui/SlashAutocomplete.tsx
|
|
458
|
-
var
|
|
459
|
-
var
|
|
1124
|
+
var import_ink6 = require("ink");
|
|
1125
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
460
1126
|
var MAX_VISIBLE = 8;
|
|
461
1127
|
function CommandRow(props) {
|
|
462
1128
|
const { cmd, isSelected, showSlash } = props;
|
|
463
|
-
const prefix = showSlash ? "/" : "";
|
|
464
1129
|
const indicator = isSelected ? "\u25B8 " : " ";
|
|
465
1130
|
const nameColor = isSelected ? "cyan" : void 0;
|
|
466
1131
|
const dimmed = !isSelected;
|
|
467
|
-
return /* @__PURE__ */ (0,
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
cmd.name
|
|
472
|
-
] }),
|
|
473
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Text, { dimColor: dimmed, children: " " }),
|
|
474
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink5.Text, { color: nameColor, dimColor: dimmed, children: cmd.description })
|
|
475
|
-
] });
|
|
1132
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ink6.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ink6.Text, { color: nameColor, dimColor: dimmed, children: [
|
|
1133
|
+
indicator,
|
|
1134
|
+
showSlash ? `/${cmd.name} ${cmd.description}` : cmd.description
|
|
1135
|
+
] }) });
|
|
476
1136
|
}
|
|
477
1137
|
function SlashAutocomplete({
|
|
478
1138
|
commands,
|
|
@@ -483,7 +1143,7 @@ function SlashAutocomplete({
|
|
|
483
1143
|
if (!visible || commands.length === 0) return null;
|
|
484
1144
|
const scrollOffset = computeScrollOffset(selectedIndex, commands.length);
|
|
485
1145
|
const visibleCommands = commands.slice(scrollOffset, scrollOffset + MAX_VISIBLE);
|
|
486
|
-
return /* @__PURE__ */ (0,
|
|
1146
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ink6.Box, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: visibleCommands.map((cmd, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
487
1147
|
CommandRow,
|
|
488
1148
|
{
|
|
489
1149
|
cmd,
|
|
@@ -500,8 +1160,14 @@ function computeScrollOffset(selectedIndex, total) {
|
|
|
500
1160
|
return Math.min(selectedIndex - MAX_VISIBLE + 1, maxOffset);
|
|
501
1161
|
}
|
|
502
1162
|
|
|
1163
|
+
// src/utils/paste-labels.ts
|
|
1164
|
+
var PASTE_LABEL_RE = /\[Pasted text #(\d+)(?: \+\d+ lines)?\]/g;
|
|
1165
|
+
function expandPasteLabels(text, store) {
|
|
1166
|
+
return text.replace(PASTE_LABEL_RE, (_, id) => store.get(Number(id)) ?? "");
|
|
1167
|
+
}
|
|
1168
|
+
|
|
503
1169
|
// src/ui/InputArea.tsx
|
|
504
|
-
var
|
|
1170
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
505
1171
|
function parseSlashInput(value) {
|
|
506
1172
|
if (!value.startsWith("/")) return { isSlash: false, parentCommand: "", filter: "" };
|
|
507
1173
|
const afterSlash = value.slice(1);
|
|
@@ -512,16 +1178,16 @@ function parseSlashInput(value) {
|
|
|
512
1178
|
return { isSlash: true, parentCommand: parent, filter: rest };
|
|
513
1179
|
}
|
|
514
1180
|
function useAutocomplete(value, registry) {
|
|
515
|
-
const [selectedIndex, setSelectedIndex] = (0,
|
|
516
|
-
const [dismissed, setDismissed] = (0,
|
|
517
|
-
const prevValueRef =
|
|
1181
|
+
const [selectedIndex, setSelectedIndex] = (0, import_react6.useState)(0);
|
|
1182
|
+
const [dismissed, setDismissed] = (0, import_react6.useState)(false);
|
|
1183
|
+
const prevValueRef = import_react6.default.useRef(value);
|
|
518
1184
|
if (prevValueRef.current !== value) {
|
|
519
1185
|
prevValueRef.current = value;
|
|
520
1186
|
if (dismissed) setDismissed(false);
|
|
521
1187
|
}
|
|
522
1188
|
const parsed = parseSlashInput(value);
|
|
523
1189
|
const isSubcommandMode = parsed.isSlash && parsed.parentCommand.length > 0;
|
|
524
|
-
const filteredCommands = (0,
|
|
1190
|
+
const filteredCommands = (0, import_react6.useMemo)(() => {
|
|
525
1191
|
if (!registry || !parsed.isSlash || dismissed) return [];
|
|
526
1192
|
if (isSubcommandMode) {
|
|
527
1193
|
const subs = registry.getSubcommands(parsed.parentCommand);
|
|
@@ -554,8 +1220,25 @@ function useAutocomplete(value, registry) {
|
|
|
554
1220
|
}
|
|
555
1221
|
};
|
|
556
1222
|
}
|
|
557
|
-
|
|
558
|
-
|
|
1223
|
+
var BORDER_HORIZONTAL = 2;
|
|
1224
|
+
var PADDING_LEFT = 1;
|
|
1225
|
+
var PROMPT_WIDTH = 2;
|
|
1226
|
+
var INPUT_AREA_OVERHEAD = BORDER_HORIZONTAL + PADDING_LEFT + PROMPT_WIDTH;
|
|
1227
|
+
function InputArea({
|
|
1228
|
+
onSubmit,
|
|
1229
|
+
onCancelQueue,
|
|
1230
|
+
isDisabled,
|
|
1231
|
+
isAborting,
|
|
1232
|
+
pendingPrompt,
|
|
1233
|
+
registry,
|
|
1234
|
+
sessionName
|
|
1235
|
+
}) {
|
|
1236
|
+
const [value, setValue] = (0, import_react6.useState)("");
|
|
1237
|
+
const pasteStore = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
|
|
1238
|
+
const { stdout } = (0, import_ink7.useStdout)();
|
|
1239
|
+
const terminalColumns = stdout?.columns ?? 80;
|
|
1240
|
+
const availableWidth = Math.max(1, terminalColumns - INPUT_AREA_OVERHEAD);
|
|
1241
|
+
const pasteIdRef = (0, import_react6.useRef)(0);
|
|
559
1242
|
const {
|
|
560
1243
|
showPopup,
|
|
561
1244
|
filteredCommands,
|
|
@@ -564,20 +1247,31 @@ function InputArea({ onSubmit, isDisabled, registry }) {
|
|
|
564
1247
|
isSubcommandMode,
|
|
565
1248
|
setShowPopup
|
|
566
1249
|
} = useAutocomplete(value, registry);
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
1250
|
+
const handlePaste = (0, import_react6.useCallback)((text) => {
|
|
1251
|
+
pasteIdRef.current += 1;
|
|
1252
|
+
const id = pasteIdRef.current;
|
|
1253
|
+
pasteStore.current.set(id, text);
|
|
1254
|
+
const lineCount = text.split("\n").length;
|
|
1255
|
+
const label = `[Pasted text #${id} +${lineCount} lines]`;
|
|
1256
|
+
setValue((prev) => prev ? `${prev} ${label}` : label);
|
|
1257
|
+
}, []);
|
|
1258
|
+
const tabCompleteCommand = (0, import_react6.useCallback)(
|
|
1259
|
+
(cmd) => {
|
|
1260
|
+
const parsed = parseSlashInput(value);
|
|
1261
|
+
if (parsed.parentCommand) {
|
|
1262
|
+
setValue(`/${parsed.parentCommand} ${cmd.name} `);
|
|
573
1263
|
return;
|
|
574
1264
|
}
|
|
575
|
-
|
|
576
|
-
|
|
1265
|
+
if (cmd.subcommands && cmd.subcommands.length > 0) {
|
|
1266
|
+
setValue(`/${cmd.name} `);
|
|
1267
|
+
setSelectedIndex(0);
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
setValue(`/${cmd.name} `);
|
|
577
1271
|
},
|
|
578
|
-
[
|
|
1272
|
+
[value, setSelectedIndex]
|
|
579
1273
|
);
|
|
580
|
-
const
|
|
1274
|
+
const enterSelectCommand = (0, import_react6.useCallback)(
|
|
581
1275
|
(cmd) => {
|
|
582
1276
|
const parsed = parseSlashInput(value);
|
|
583
1277
|
if (parsed.parentCommand) {
|
|
@@ -596,7 +1290,23 @@ function InputArea({ onSubmit, isDisabled, registry }) {
|
|
|
596
1290
|
},
|
|
597
1291
|
[value, onSubmit, setSelectedIndex]
|
|
598
1292
|
);
|
|
599
|
-
(0,
|
|
1293
|
+
const handleSubmit = (0, import_react6.useCallback)(
|
|
1294
|
+
(text) => {
|
|
1295
|
+
const trimmed = text.trim();
|
|
1296
|
+
if (trimmed.length === 0) return;
|
|
1297
|
+
if (showPopup && filteredCommands[selectedIndex]) {
|
|
1298
|
+
enterSelectCommand(filteredCommands[selectedIndex]);
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
const expanded = expandPasteLabels(trimmed, pasteStore.current);
|
|
1302
|
+
setValue("");
|
|
1303
|
+
pasteStore.current.clear();
|
|
1304
|
+
pasteIdRef.current = 0;
|
|
1305
|
+
onSubmit(expanded);
|
|
1306
|
+
},
|
|
1307
|
+
[showPopup, filteredCommands, selectedIndex, onSubmit, enterSelectCommand]
|
|
1308
|
+
);
|
|
1309
|
+
(0, import_ink7.useInput)(
|
|
600
1310
|
(_input, key) => {
|
|
601
1311
|
if (!showPopup) return;
|
|
602
1312
|
if (key.upArrow) {
|
|
@@ -607,13 +1317,32 @@ function InputArea({ onSubmit, isDisabled, registry }) {
|
|
|
607
1317
|
setShowPopup(false);
|
|
608
1318
|
} else if (key.tab) {
|
|
609
1319
|
const cmd = filteredCommands[selectedIndex];
|
|
610
|
-
if (cmd)
|
|
1320
|
+
if (cmd) tabCompleteCommand(cmd);
|
|
611
1321
|
}
|
|
612
1322
|
},
|
|
613
1323
|
{ isActive: showPopup && !isDisabled }
|
|
614
1324
|
);
|
|
615
|
-
|
|
616
|
-
|
|
1325
|
+
(0, import_ink7.useInput)(
|
|
1326
|
+
(_input, key) => {
|
|
1327
|
+
if ((key.backspace || key.delete) && pendingPrompt) {
|
|
1328
|
+
onCancelQueue?.();
|
|
1329
|
+
}
|
|
1330
|
+
},
|
|
1331
|
+
{ isActive: !!pendingPrompt }
|
|
1332
|
+
);
|
|
1333
|
+
const borderColor = isAborting ? "yellow" : pendingPrompt ? "cyan" : isDisabled ? "gray" : "green";
|
|
1334
|
+
const innerWidth = Math.max(1, terminalColumns - BORDER_HORIZONTAL);
|
|
1335
|
+
const topBorder = (() => {
|
|
1336
|
+
if (sessionName) {
|
|
1337
|
+
const label = ` "${sessionName}" `;
|
|
1338
|
+
const rightPad = 2;
|
|
1339
|
+
const leftLen = Math.max(0, innerWidth - label.length - rightPad);
|
|
1340
|
+
return { left: "\u250C" + "\u2500".repeat(leftLen), label, right: "\u2500".repeat(rightPad) + "\u2510" };
|
|
1341
|
+
}
|
|
1342
|
+
return { left: "\u250C" + "\u2500".repeat(innerWidth), label: "", right: "\u2510" };
|
|
1343
|
+
})();
|
|
1344
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { flexDirection: "column", children: [
|
|
1345
|
+
showPopup && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
617
1346
|
SlashAutocomplete,
|
|
618
1347
|
{
|
|
619
1348
|
commands: filteredCommands,
|
|
@@ -622,41 +1351,97 @@ function InputArea({ onSubmit, isDisabled, registry }) {
|
|
|
622
1351
|
isSubcommandMode
|
|
623
1352
|
}
|
|
624
1353
|
),
|
|
625
|
-
/* @__PURE__ */ (0,
|
|
626
|
-
|
|
627
|
-
/* @__PURE__ */ (0,
|
|
1354
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { color: borderColor, children: [
|
|
1355
|
+
topBorder.left,
|
|
1356
|
+
topBorder.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { backgroundColor: borderColor, color: "black", bold: true, children: topBorder.label }) : null,
|
|
1357
|
+
topBorder.right
|
|
1358
|
+
] }),
|
|
1359
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Box, { borderStyle: "single", borderTop: false, borderColor, paddingLeft: 1, children: isAborting ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "yellow", children: " Interrupting..." }) : pendingPrompt ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Text, { color: "cyan", children: [
|
|
1360
|
+
" ",
|
|
1361
|
+
"Queued: ",
|
|
1362
|
+
pendingPrompt.length > 50 ? pendingPrompt.slice(0, 47) + "..." : pendingPrompt,
|
|
1363
|
+
" ",
|
|
1364
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { dimColor: true, children: "(Backspace to cancel)" })
|
|
1365
|
+
] }) : isDisabled ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(WaveText, { text: " Waiting for response... (ESC to interrupt)" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ink7.Box, { children: [
|
|
1366
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { color: "green", bold: true, children: "> " }),
|
|
1367
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
628
1368
|
CjkTextInput,
|
|
629
1369
|
{
|
|
630
1370
|
value,
|
|
631
1371
|
onChange: setValue,
|
|
632
1372
|
onSubmit: handleSubmit,
|
|
633
|
-
|
|
1373
|
+
onPaste: handlePaste,
|
|
1374
|
+
placeholder: "Type a message or /help",
|
|
1375
|
+
availableWidth
|
|
634
1376
|
}
|
|
635
1377
|
)
|
|
636
1378
|
] }) })
|
|
637
1379
|
] });
|
|
638
1380
|
}
|
|
639
1381
|
|
|
1382
|
+
// src/ui/ConfirmPrompt.tsx
|
|
1383
|
+
var import_react7 = require("react");
|
|
1384
|
+
var import_ink8 = require("ink");
|
|
1385
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1386
|
+
function ConfirmPrompt({
|
|
1387
|
+
message,
|
|
1388
|
+
options = ["Yes", "No"],
|
|
1389
|
+
onSelect
|
|
1390
|
+
}) {
|
|
1391
|
+
const [selected, setSelected] = (0, import_react7.useState)(0);
|
|
1392
|
+
const resolvedRef = (0, import_react7.useRef)(false);
|
|
1393
|
+
const doSelect = (0, import_react7.useCallback)(
|
|
1394
|
+
(index) => {
|
|
1395
|
+
if (resolvedRef.current) return;
|
|
1396
|
+
resolvedRef.current = true;
|
|
1397
|
+
onSelect(index);
|
|
1398
|
+
},
|
|
1399
|
+
[onSelect]
|
|
1400
|
+
);
|
|
1401
|
+
(0, import_ink8.useInput)((input, key) => {
|
|
1402
|
+
if (resolvedRef.current) return;
|
|
1403
|
+
if (key.leftArrow || key.upArrow) {
|
|
1404
|
+
setSelected((prev) => prev > 0 ? prev - 1 : prev);
|
|
1405
|
+
} else if (key.rightArrow || key.downArrow) {
|
|
1406
|
+
setSelected((prev) => prev < options.length - 1 ? prev + 1 : prev);
|
|
1407
|
+
} else if (key.return) {
|
|
1408
|
+
doSelect(selected);
|
|
1409
|
+
} else if (input === "y" && options.length === 2) {
|
|
1410
|
+
doSelect(0);
|
|
1411
|
+
} else if (input === "n" && options.length === 2) {
|
|
1412
|
+
doSelect(1);
|
|
1413
|
+
}
|
|
1414
|
+
});
|
|
1415
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
1416
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: "yellow", children: message }),
|
|
1417
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Box, { marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink8.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
|
|
1418
|
+
i === selected ? "> " : " ",
|
|
1419
|
+
opt
|
|
1420
|
+
] }) }, opt)) }),
|
|
1421
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { dimColor: true, children: " arrow keys to select, Enter to confirm" })
|
|
1422
|
+
] });
|
|
1423
|
+
}
|
|
1424
|
+
|
|
640
1425
|
// src/ui/PermissionPrompt.tsx
|
|
641
|
-
var
|
|
642
|
-
var
|
|
643
|
-
var
|
|
1426
|
+
var import_react8 = __toESM(require("react"), 1);
|
|
1427
|
+
var import_ink9 = require("ink");
|
|
1428
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
644
1429
|
var OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
|
|
645
|
-
function
|
|
1430
|
+
function formatArgs(args) {
|
|
646
1431
|
const entries = Object.entries(args);
|
|
647
1432
|
if (entries.length === 0) return "(no arguments)";
|
|
648
1433
|
return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
649
1434
|
}
|
|
650
1435
|
function PermissionPrompt({ request }) {
|
|
651
|
-
const [selected, setSelected] =
|
|
652
|
-
const resolvedRef =
|
|
653
|
-
const prevRequestRef =
|
|
1436
|
+
const [selected, setSelected] = import_react8.default.useState(0);
|
|
1437
|
+
const resolvedRef = import_react8.default.useRef(false);
|
|
1438
|
+
const prevRequestRef = import_react8.default.useRef(request);
|
|
654
1439
|
if (prevRequestRef.current !== request) {
|
|
655
1440
|
prevRequestRef.current = request;
|
|
656
1441
|
resolvedRef.current = false;
|
|
657
1442
|
setSelected(0);
|
|
658
1443
|
}
|
|
659
|
-
const doResolve =
|
|
1444
|
+
const doResolve = import_react8.default.useCallback(
|
|
660
1445
|
(index) => {
|
|
661
1446
|
if (resolvedRef.current) return;
|
|
662
1447
|
resolvedRef.current = true;
|
|
@@ -666,7 +1451,7 @@ function PermissionPrompt({ request }) {
|
|
|
666
1451
|
},
|
|
667
1452
|
[request]
|
|
668
1453
|
);
|
|
669
|
-
(0,
|
|
1454
|
+
(0, import_ink9.useInput)((input, key) => {
|
|
670
1455
|
if (resolvedRef.current) return;
|
|
671
1456
|
if (key.upArrow || key.leftArrow) {
|
|
672
1457
|
setSelected((prev) => prev > 0 ? prev - 1 : prev);
|
|
@@ -682,364 +1467,857 @@ function PermissionPrompt({ request }) {
|
|
|
682
1467
|
doResolve(2);
|
|
683
1468
|
}
|
|
684
1469
|
});
|
|
685
|
-
return /* @__PURE__ */ (0,
|
|
686
|
-
/* @__PURE__ */ (0,
|
|
687
|
-
/* @__PURE__ */ (0,
|
|
1470
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
1471
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "yellow", bold: true, children: "[Permission Required]" }),
|
|
1472
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { children: [
|
|
688
1473
|
"Tool:",
|
|
689
1474
|
" ",
|
|
690
|
-
/* @__PURE__ */ (0,
|
|
1475
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "cyan", bold: true, children: request.toolName })
|
|
691
1476
|
] }),
|
|
692
|
-
/* @__PURE__ */ (0,
|
|
1477
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { dimColor: true, children: [
|
|
693
1478
|
" ",
|
|
694
|
-
|
|
1479
|
+
formatArgs(request.toolArgs)
|
|
695
1480
|
] }),
|
|
696
|
-
/* @__PURE__ */ (0,
|
|
1481
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { marginTop: 1, children: OPTIONS.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
|
|
697
1482
|
i === selected ? "> " : " ",
|
|
698
1483
|
opt
|
|
699
1484
|
] }) }, opt)) }),
|
|
700
|
-
/* @__PURE__ */ (0,
|
|
1485
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { dimColor: true, children: " left/right to select, Enter to confirm" })
|
|
701
1486
|
] });
|
|
702
1487
|
}
|
|
703
1488
|
|
|
704
|
-
// src/ui/
|
|
705
|
-
var
|
|
706
|
-
var
|
|
707
|
-
function
|
|
708
|
-
|
|
709
|
-
return
|
|
1489
|
+
// src/ui/StreamingIndicator.tsx
|
|
1490
|
+
var import_ink10 = require("ink");
|
|
1491
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1492
|
+
function getToolStyle(t) {
|
|
1493
|
+
if (t.isRunning) return { color: "yellow", icon: "\u27F3", strikethrough: false };
|
|
1494
|
+
if (t.result === "error") return { color: "red", icon: "\u2717", strikethrough: true };
|
|
1495
|
+
if (t.result === "denied") return { color: "yellowBright", icon: "\u2298", strikethrough: true };
|
|
1496
|
+
return { color: "green", icon: "\u2713", strikethrough: false };
|
|
710
1497
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
writeMarkdown: () => {
|
|
717
|
-
},
|
|
718
|
-
writeError: () => {
|
|
719
|
-
},
|
|
720
|
-
prompt: () => Promise.resolve(""),
|
|
721
|
-
select: () => Promise.resolve(0),
|
|
722
|
-
spinner: () => ({ stop: () => {
|
|
723
|
-
}, update: () => {
|
|
724
|
-
} })
|
|
725
|
-
};
|
|
726
|
-
function useSession(props) {
|
|
727
|
-
const [permissionRequest, setPermissionRequest] = (0, import_react5.useState)(null);
|
|
728
|
-
const [streamingText, setStreamingText] = (0, import_react5.useState)("");
|
|
729
|
-
const permissionQueueRef = (0, import_react5.useRef)([]);
|
|
730
|
-
const processingRef = (0, import_react5.useRef)(false);
|
|
731
|
-
const processNextPermission = (0, import_react5.useCallback)(() => {
|
|
732
|
-
if (processingRef.current) return;
|
|
733
|
-
const next = permissionQueueRef.current[0];
|
|
734
|
-
if (!next) {
|
|
735
|
-
setPermissionRequest(null);
|
|
736
|
-
return;
|
|
737
|
-
}
|
|
738
|
-
processingRef.current = true;
|
|
739
|
-
setPermissionRequest({
|
|
740
|
-
toolName: next.toolName,
|
|
741
|
-
toolArgs: next.toolArgs,
|
|
742
|
-
resolve: (result) => {
|
|
743
|
-
permissionQueueRef.current.shift();
|
|
744
|
-
processingRef.current = false;
|
|
745
|
-
setPermissionRequest(null);
|
|
746
|
-
next.resolve(result);
|
|
747
|
-
setTimeout(() => processNextPermission(), 0);
|
|
748
|
-
}
|
|
749
|
-
});
|
|
750
|
-
}, []);
|
|
751
|
-
const sessionRef = (0, import_react5.useRef)(null);
|
|
752
|
-
if (sessionRef.current === null) {
|
|
753
|
-
const permissionHandler = (toolName, toolArgs) => {
|
|
754
|
-
return new Promise((resolve) => {
|
|
755
|
-
permissionQueueRef.current.push({ toolName, toolArgs, resolve });
|
|
756
|
-
processNextPermission();
|
|
757
|
-
});
|
|
758
|
-
};
|
|
759
|
-
const onTextDelta = (delta) => {
|
|
760
|
-
setStreamingText((prev) => prev + delta);
|
|
761
|
-
};
|
|
762
|
-
const paths = (0, import_agent_sdk.projectPaths)(props.cwd ?? process.cwd());
|
|
763
|
-
sessionRef.current = (0, import_agent_sdk.createSession)({
|
|
764
|
-
config: props.config,
|
|
765
|
-
context: props.context,
|
|
766
|
-
terminal: NOOP_TERMINAL,
|
|
767
|
-
sessionLogger: new import_agent_sdk.FileSessionLogger(paths.logs),
|
|
768
|
-
projectInfo: props.projectInfo,
|
|
769
|
-
sessionStore: props.sessionStore,
|
|
770
|
-
permissionMode: props.permissionMode,
|
|
771
|
-
maxTurns: props.maxTurns,
|
|
772
|
-
permissionHandler,
|
|
773
|
-
onTextDelta
|
|
774
|
-
});
|
|
775
|
-
}
|
|
776
|
-
const clearStreamingText = (0, import_react5.useCallback)(() => setStreamingText(""), []);
|
|
777
|
-
return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText };
|
|
778
|
-
}
|
|
779
|
-
function useMessages() {
|
|
780
|
-
const [messages, setMessages] = (0, import_react5.useState)([]);
|
|
781
|
-
const addMessage = (0, import_react5.useCallback)((msg) => {
|
|
782
|
-
setMessages((prev) => [...prev, { ...msg, id: nextId(), timestamp: /* @__PURE__ */ new Date() }]);
|
|
783
|
-
}, []);
|
|
784
|
-
return { messages, setMessages, addMessage };
|
|
785
|
-
}
|
|
786
|
-
var HELP_TEXT = [
|
|
787
|
-
"Available commands:",
|
|
788
|
-
" /help \u2014 Show this help",
|
|
789
|
-
" /clear \u2014 Clear conversation",
|
|
790
|
-
" /compact [instr] \u2014 Compact context (optional focus instructions)",
|
|
791
|
-
" /mode [m] \u2014 Show/change permission mode",
|
|
792
|
-
" /cost \u2014 Show session info",
|
|
793
|
-
" /exit \u2014 Exit CLI"
|
|
794
|
-
].join("\n");
|
|
795
|
-
function handleModeCommand(arg, session, addMessage) {
|
|
796
|
-
const validModes = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
797
|
-
if (!arg) {
|
|
798
|
-
addMessage({ role: "system", content: `Current mode: ${session.getPermissionMode()}` });
|
|
799
|
-
} else if (validModes.includes(arg)) {
|
|
800
|
-
session.setPermissionMode(arg);
|
|
801
|
-
addMessage({ role: "system", content: `Permission mode set to: ${arg}` });
|
|
802
|
-
} else {
|
|
803
|
-
addMessage({ role: "system", content: `Invalid mode. Valid: ${validModes.join(" | ")}` });
|
|
1498
|
+
function StreamingIndicator({ text, activeTools }) {
|
|
1499
|
+
const hasTools = activeTools.length > 0;
|
|
1500
|
+
const hasText = text.length > 0;
|
|
1501
|
+
if (!hasTools && !hasText) {
|
|
1502
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, {});
|
|
804
1503
|
}
|
|
805
|
-
return
|
|
1504
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
|
|
1505
|
+
hasTools && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
1506
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "white", bold: true, children: "Tools:" }),
|
|
1507
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { children: " " }),
|
|
1508
|
+
activeTools.map((t, i) => {
|
|
1509
|
+
const { color, icon, strikethrough } = getToolStyle(t);
|
|
1510
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
|
|
1511
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Text, { color, strikethrough, children: [
|
|
1512
|
+
" ",
|
|
1513
|
+
icon,
|
|
1514
|
+
" ",
|
|
1515
|
+
t.toolName,
|
|
1516
|
+
"(",
|
|
1517
|
+
t.firstArg,
|
|
1518
|
+
")"
|
|
1519
|
+
] }),
|
|
1520
|
+
t.diffLines && t.diffLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DiffBlock, { file: t.diffFile, lines: t.diffLines })
|
|
1521
|
+
] }, `${t.toolName}-${i}`);
|
|
1522
|
+
})
|
|
1523
|
+
] }),
|
|
1524
|
+
hasText && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
1525
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "cyan", bold: true, children: "Robota:" }),
|
|
1526
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { children: " " }),
|
|
1527
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { wrap: "wrap", children: renderMarkdown(text) }) })
|
|
1528
|
+
] })
|
|
1529
|
+
] });
|
|
806
1530
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1531
|
+
|
|
1532
|
+
// src/ui/PluginTUI.tsx
|
|
1533
|
+
var import_react11 = require("react");
|
|
1534
|
+
|
|
1535
|
+
// src/ui/MenuSelect.tsx
|
|
1536
|
+
var import_react9 = require("react");
|
|
1537
|
+
var import_ink11 = require("ink");
|
|
1538
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1539
|
+
function MenuSelect({
|
|
1540
|
+
title,
|
|
1541
|
+
items,
|
|
1542
|
+
onSelect,
|
|
1543
|
+
onBack,
|
|
1544
|
+
loading,
|
|
1545
|
+
error
|
|
1546
|
+
}) {
|
|
1547
|
+
const [selected, setSelected] = (0, import_react9.useState)(0);
|
|
1548
|
+
const selectedRef = (0, import_react9.useRef)(0);
|
|
1549
|
+
const resolvedRef = (0, import_react9.useRef)(false);
|
|
1550
|
+
const doSelect = (0, import_react9.useCallback)(
|
|
1551
|
+
(index) => {
|
|
1552
|
+
if (resolvedRef.current || items.length === 0) return;
|
|
1553
|
+
resolvedRef.current = true;
|
|
1554
|
+
onSelect(items[index].value);
|
|
1555
|
+
},
|
|
1556
|
+
[items, onSelect]
|
|
1557
|
+
);
|
|
1558
|
+
(0, import_ink11.useInput)((input, key) => {
|
|
1559
|
+
if (resolvedRef.current) return;
|
|
1560
|
+
if (key.escape) {
|
|
1561
|
+
resolvedRef.current = true;
|
|
1562
|
+
onBack();
|
|
1563
|
+
return;
|
|
828
1564
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
1565
|
+
if (loading || error || items.length === 0) return;
|
|
1566
|
+
if (key.upArrow) {
|
|
1567
|
+
const next = selectedRef.current > 0 ? selectedRef.current - 1 : selectedRef.current;
|
|
1568
|
+
selectedRef.current = next;
|
|
1569
|
+
setSelected(next);
|
|
1570
|
+
} else if (key.downArrow) {
|
|
1571
|
+
const next = selectedRef.current < items.length - 1 ? selectedRef.current + 1 : selectedRef.current;
|
|
1572
|
+
selectedRef.current = next;
|
|
1573
|
+
setSelected(next);
|
|
1574
|
+
} else if (key.return) {
|
|
1575
|
+
doSelect(selectedRef.current);
|
|
1576
|
+
}
|
|
1577
|
+
});
|
|
1578
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
1579
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "yellow", bold: true, children: title }),
|
|
1580
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: "Loading..." }) }),
|
|
1581
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { marginTop: 1, flexDirection: "column", children: [
|
|
1582
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "red", children: error }),
|
|
1583
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: "Press Esc to go back" })
|
|
1584
|
+
] }),
|
|
1585
|
+
!loading && !error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { flexDirection: "column", marginTop: 1, children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { children: [
|
|
1586
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
|
|
1587
|
+
i === selected ? "> " : " ",
|
|
1588
|
+
item.label
|
|
1589
|
+
] }),
|
|
1590
|
+
item.hint && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { dimColor: true, children: [
|
|
1591
|
+
" ",
|
|
1592
|
+
item.hint
|
|
1593
|
+
] })
|
|
1594
|
+
] }, item.value)) }),
|
|
1595
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: loading || error ? "" : " \u2191\u2193 Navigate Enter Select Esc Back" })
|
|
1596
|
+
] });
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
// src/ui/TextPrompt.tsx
|
|
1600
|
+
var import_react10 = require("react");
|
|
1601
|
+
var import_ink12 = require("ink");
|
|
1602
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1603
|
+
function TextPrompt({
|
|
1604
|
+
title,
|
|
1605
|
+
placeholder,
|
|
1606
|
+
onSubmit,
|
|
1607
|
+
onCancel,
|
|
1608
|
+
validate
|
|
1609
|
+
}) {
|
|
1610
|
+
const [value, setValue] = (0, import_react10.useState)("");
|
|
1611
|
+
const [error, setError] = (0, import_react10.useState)();
|
|
1612
|
+
const resolvedRef = (0, import_react10.useRef)(false);
|
|
1613
|
+
const valueRef = (0, import_react10.useRef)("");
|
|
1614
|
+
const handleSubmit = (0, import_react10.useCallback)(() => {
|
|
1615
|
+
if (resolvedRef.current) return;
|
|
1616
|
+
const trimmed = valueRef.current.trim();
|
|
1617
|
+
if (!trimmed) return;
|
|
1618
|
+
if (validate) {
|
|
1619
|
+
const err = validate(trimmed);
|
|
1620
|
+
if (err) {
|
|
1621
|
+
setError(err);
|
|
1622
|
+
return;
|
|
846
1623
|
}
|
|
847
|
-
addMessage({ role: "system", content: lines.join("\n") });
|
|
848
|
-
return true;
|
|
849
1624
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
1625
|
+
resolvedRef.current = true;
|
|
1626
|
+
onSubmit(trimmed);
|
|
1627
|
+
}, [validate, onSubmit]);
|
|
1628
|
+
(0, import_ink12.useInput)((input, key) => {
|
|
1629
|
+
if (resolvedRef.current) return;
|
|
1630
|
+
if (key.escape) {
|
|
1631
|
+
resolvedRef.current = true;
|
|
1632
|
+
onCancel();
|
|
1633
|
+
return;
|
|
857
1634
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
return
|
|
861
|
-
default: {
|
|
862
|
-
const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
|
|
863
|
-
if (skillCmd) {
|
|
864
|
-
addMessage({ role: "system", content: `Invoking skill: ${cmd}` });
|
|
865
|
-
return false;
|
|
866
|
-
}
|
|
867
|
-
addMessage({ role: "system", content: `Unknown command "/${cmd}". Type /help for help.` });
|
|
868
|
-
return true;
|
|
1635
|
+
if (key.return) {
|
|
1636
|
+
handleSubmit();
|
|
1637
|
+
return;
|
|
869
1638
|
}
|
|
1639
|
+
if (key.backspace || key.delete) {
|
|
1640
|
+
valueRef.current = valueRef.current.slice(0, -1);
|
|
1641
|
+
setValue(valueRef.current);
|
|
1642
|
+
setError(void 0);
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
if (input && !key.ctrl && !key.meta) {
|
|
1646
|
+
valueRef.current = valueRef.current + input;
|
|
1647
|
+
setValue(valueRef.current);
|
|
1648
|
+
setError(void 0);
|
|
1649
|
+
}
|
|
1650
|
+
});
|
|
1651
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
1652
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "yellow", bold: true, children: title }),
|
|
1653
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { marginTop: 1, children: [
|
|
1654
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "cyan", children: "> " }),
|
|
1655
|
+
value ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { children: value }) : placeholder ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { dimColor: true, children: placeholder }) : null,
|
|
1656
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "cyan", children: "\u2588" })
|
|
1657
|
+
] }),
|
|
1658
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "red", children: error }),
|
|
1659
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { dimColor: true, children: " Enter Submit Esc Cancel" })
|
|
1660
|
+
] });
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
// src/ui/plugin-tui-handlers.ts
|
|
1664
|
+
function handleMainSelect(value, nav) {
|
|
1665
|
+
if (value === "marketplace") {
|
|
1666
|
+
nav.push({ screen: "marketplace-list" });
|
|
1667
|
+
} else if (value === "installed") {
|
|
1668
|
+
nav.push({ screen: "installed-list" });
|
|
870
1669
|
}
|
|
871
1670
|
}
|
|
872
|
-
function
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
},
|
|
879
|
-
[session, addMessage, setMessages, exit, registry]
|
|
880
|
-
);
|
|
1671
|
+
function handleMarketplaceListSelect(value, nav) {
|
|
1672
|
+
if (value === "__add__") {
|
|
1673
|
+
nav.push({ screen: "marketplace-add" });
|
|
1674
|
+
} else {
|
|
1675
|
+
nav.push({ screen: "marketplace-action", context: { marketplace: value } });
|
|
1676
|
+
}
|
|
881
1677
|
}
|
|
882
|
-
function
|
|
883
|
-
if (
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1678
|
+
function handleMarketplaceActionSelect(value, marketplace, callbacks, nav) {
|
|
1679
|
+
if (value === "browse") {
|
|
1680
|
+
nav.push({ screen: "marketplace-browse", context: { marketplace } });
|
|
1681
|
+
} else if (value === "update") {
|
|
1682
|
+
callbacks.marketplaceUpdate(marketplace).then(() => {
|
|
1683
|
+
nav.notify(`Updated marketplace "${marketplace}".`);
|
|
1684
|
+
nav.pop();
|
|
1685
|
+
}).catch((err) => {
|
|
1686
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1687
|
+
});
|
|
1688
|
+
} else if (value === "remove") {
|
|
1689
|
+
nav.setConfirm({
|
|
1690
|
+
message: `Remove marketplace "${marketplace}" and all its plugins?`,
|
|
1691
|
+
onConfirm: () => {
|
|
1692
|
+
nav.setConfirm(void 0);
|
|
1693
|
+
callbacks.marketplaceRemove(marketplace).then(() => {
|
|
1694
|
+
nav.notify(`Removed marketplace "${marketplace}".`);
|
|
1695
|
+
nav.popN(2);
|
|
1696
|
+
}).catch((err) => {
|
|
1697
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1698
|
+
});
|
|
1699
|
+
},
|
|
1700
|
+
onCancel: () => nav.setConfirm(void 0)
|
|
1701
|
+
});
|
|
892
1702
|
}
|
|
893
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: "yellow", children: "Thinking..." });
|
|
894
1703
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
setContextPercentage(session.getContextState().usedPercentage);
|
|
903
|
-
} catch (err) {
|
|
904
|
-
clearStreamingText();
|
|
905
|
-
if (err instanceof DOMException && err.name === "AbortError") {
|
|
906
|
-
addMessage({ role: "system", content: "Cancelled." });
|
|
907
|
-
} else {
|
|
908
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
909
|
-
addMessage({ role: "system", content: `Error: ${errMsg}` });
|
|
910
|
-
}
|
|
911
|
-
} finally {
|
|
912
|
-
setIsThinking(false);
|
|
1704
|
+
function handleMarketplaceBrowseSelect(value, marketplace, items, nav) {
|
|
1705
|
+
const fullId = `${value}@${marketplace}`;
|
|
1706
|
+
const item = items.find((i) => i.value === value);
|
|
1707
|
+
if (item?.hint === "installed") {
|
|
1708
|
+
nav.push({ screen: "installed-action", context: { pluginId: fullId } });
|
|
1709
|
+
} else {
|
|
1710
|
+
nav.push({ screen: "marketplace-install-scope", context: { marketplace, pluginId: fullId } });
|
|
913
1711
|
}
|
|
914
1712
|
}
|
|
915
|
-
function
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
1713
|
+
function handleInstallScopeSelect(value, pluginId, callbacks, nav) {
|
|
1714
|
+
const scope = value;
|
|
1715
|
+
callbacks.install(pluginId, scope).then(() => {
|
|
1716
|
+
nav.notify(`Installed plugin "${pluginId}" (${scope} scope).`);
|
|
1717
|
+
nav.popN(2);
|
|
1718
|
+
}).catch((err) => {
|
|
1719
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1720
|
+
});
|
|
922
1721
|
}
|
|
923
|
-
function
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1722
|
+
function handleInstalledListSelect(value, callbacks, nav) {
|
|
1723
|
+
nav.setConfirm({
|
|
1724
|
+
message: `Uninstall plugin "${value}"?`,
|
|
1725
|
+
onConfirm: () => {
|
|
1726
|
+
nav.setConfirm(void 0);
|
|
1727
|
+
callbacks.uninstall(value).then(() => {
|
|
1728
|
+
nav.notify(`Uninstalled plugin "${value}".`);
|
|
1729
|
+
nav.refresh();
|
|
1730
|
+
}).catch((err) => {
|
|
1731
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1732
|
+
});
|
|
1733
|
+
},
|
|
1734
|
+
onCancel: () => nav.setConfirm(void 0)
|
|
1735
|
+
});
|
|
1736
|
+
}
|
|
1737
|
+
function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
|
|
1738
|
+
if (value === "uninstall") {
|
|
1739
|
+
nav.setConfirm({
|
|
1740
|
+
message: `Uninstall plugin "${pluginId}"?`,
|
|
1741
|
+
onConfirm: () => {
|
|
1742
|
+
nav.setConfirm(void 0);
|
|
1743
|
+
callbacks.uninstall(pluginId).then(() => {
|
|
1744
|
+
nav.notify(`Uninstalled plugin "${pluginId}".`);
|
|
1745
|
+
nav.popN(2);
|
|
1746
|
+
}).catch((err) => {
|
|
1747
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1748
|
+
});
|
|
1749
|
+
},
|
|
1750
|
+
onCancel: () => nav.setConfirm(void 0)
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
// src/ui/PluginTUI.tsx
|
|
1756
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1757
|
+
function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
1758
|
+
const [stack, setStack] = (0, import_react11.useState)([{ screen: "main" }]);
|
|
1759
|
+
const [items, setItems] = (0, import_react11.useState)([]);
|
|
1760
|
+
const [loading, setLoading] = (0, import_react11.useState)(false);
|
|
1761
|
+
const [error, setError] = (0, import_react11.useState)();
|
|
1762
|
+
const [confirm, setConfirm] = (0, import_react11.useState)();
|
|
1763
|
+
const [refreshCounter, setRefreshCounter] = (0, import_react11.useState)(0);
|
|
1764
|
+
const current = stack[stack.length - 1] ?? { screen: "main" };
|
|
1765
|
+
const push = (0, import_react11.useCallback)((state) => {
|
|
1766
|
+
setStack((prev) => [...prev, state]);
|
|
1767
|
+
setItems([]);
|
|
1768
|
+
setError(void 0);
|
|
1769
|
+
}, []);
|
|
1770
|
+
const pop = (0, import_react11.useCallback)(() => {
|
|
1771
|
+
setStack((prev) => {
|
|
1772
|
+
if (prev.length <= 1) {
|
|
1773
|
+
onClose();
|
|
1774
|
+
return prev;
|
|
1775
|
+
}
|
|
1776
|
+
return prev.slice(0, -1);
|
|
1777
|
+
});
|
|
1778
|
+
setItems([]);
|
|
1779
|
+
setError(void 0);
|
|
1780
|
+
}, [onClose]);
|
|
1781
|
+
const popN = (0, import_react11.useCallback)(
|
|
1782
|
+
(n) => {
|
|
1783
|
+
setStack((prev) => {
|
|
1784
|
+
const next = prev.slice(0, Math.max(1, prev.length - n));
|
|
1785
|
+
if (next.length === 0) {
|
|
1786
|
+
onClose();
|
|
1787
|
+
return prev;
|
|
931
1788
|
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1789
|
+
return next;
|
|
1790
|
+
});
|
|
1791
|
+
setItems([]);
|
|
1792
|
+
setError(void 0);
|
|
1793
|
+
},
|
|
1794
|
+
[onClose]
|
|
1795
|
+
);
|
|
1796
|
+
const notify = (0, import_react11.useCallback)(
|
|
1797
|
+
(content) => {
|
|
1798
|
+
addMessage?.({ role: "system", content });
|
|
1799
|
+
},
|
|
1800
|
+
[addMessage]
|
|
1801
|
+
);
|
|
1802
|
+
const refresh = (0, import_react11.useCallback)(() => {
|
|
1803
|
+
setItems([]);
|
|
1804
|
+
setRefreshCounter((c) => c + 1);
|
|
1805
|
+
}, []);
|
|
1806
|
+
const nav = { push, pop, popN, notify, setConfirm, refresh };
|
|
1807
|
+
(0, import_react11.useEffect)(() => {
|
|
1808
|
+
const screen2 = current.screen;
|
|
1809
|
+
if (screen2 === "marketplace-list") {
|
|
1810
|
+
setLoading(true);
|
|
1811
|
+
callbacks.marketplaceList().then((sources) => {
|
|
1812
|
+
const baseItems = [{ label: "Add Marketplace", value: "__add__" }];
|
|
1813
|
+
const sourceItems = sources.map((s) => ({
|
|
1814
|
+
label: s.name,
|
|
1815
|
+
value: s.name,
|
|
1816
|
+
hint: s.type
|
|
1817
|
+
}));
|
|
1818
|
+
setItems([...baseItems, ...sourceItems]);
|
|
1819
|
+
setLoading(false);
|
|
1820
|
+
}).catch((err) => {
|
|
1821
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
1822
|
+
setLoading(false);
|
|
1823
|
+
});
|
|
1824
|
+
} else if (screen2 === "marketplace-browse") {
|
|
1825
|
+
const marketplace = current.context?.marketplace ?? "";
|
|
1826
|
+
setLoading(true);
|
|
1827
|
+
callbacks.listAvailablePlugins(marketplace).then((plugins) => {
|
|
1828
|
+
setItems(
|
|
1829
|
+
plugins.map((p) => ({
|
|
1830
|
+
label: p.name,
|
|
1831
|
+
value: p.name,
|
|
1832
|
+
hint: p.installed ? "installed" : p.description
|
|
1833
|
+
}))
|
|
941
1834
|
);
|
|
1835
|
+
setLoading(false);
|
|
1836
|
+
}).catch((err) => {
|
|
1837
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
1838
|
+
setLoading(false);
|
|
1839
|
+
});
|
|
1840
|
+
} else if (screen2 === "installed-list") {
|
|
1841
|
+
setLoading(true);
|
|
1842
|
+
callbacks.listInstalled().then((plugins) => {
|
|
1843
|
+
setItems(
|
|
1844
|
+
plugins.map((p) => ({
|
|
1845
|
+
label: p.name,
|
|
1846
|
+
value: p.name,
|
|
1847
|
+
hint: p.description
|
|
1848
|
+
}))
|
|
1849
|
+
);
|
|
1850
|
+
setLoading(false);
|
|
1851
|
+
}).catch((err) => {
|
|
1852
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
1853
|
+
setLoading(false);
|
|
1854
|
+
});
|
|
1855
|
+
}
|
|
1856
|
+
}, [stack.length, current.screen, current.context?.marketplace, callbacks, refreshCounter]);
|
|
1857
|
+
const handleSelect = (0, import_react11.useCallback)(
|
|
1858
|
+
(value) => {
|
|
1859
|
+
const screen2 = current.screen;
|
|
1860
|
+
const ctx = current.context;
|
|
1861
|
+
if (screen2 === "main") handleMainSelect(value, nav);
|
|
1862
|
+
else if (screen2 === "marketplace-list") handleMarketplaceListSelect(value, nav);
|
|
1863
|
+
else if (screen2 === "marketplace-action")
|
|
1864
|
+
handleMarketplaceActionSelect(value, ctx?.marketplace ?? "", callbacks, nav);
|
|
1865
|
+
else if (screen2 === "marketplace-browse")
|
|
1866
|
+
handleMarketplaceBrowseSelect(value, ctx?.marketplace ?? "", items, nav);
|
|
1867
|
+
else if (screen2 === "marketplace-install-scope")
|
|
1868
|
+
handleInstallScopeSelect(value, ctx?.pluginId ?? "", callbacks, nav);
|
|
1869
|
+
else if (screen2 === "installed-list") handleInstalledListSelect(value, callbacks, nav);
|
|
1870
|
+
else if (screen2 === "installed-action")
|
|
1871
|
+
handleInstalledActionSelect(value, ctx?.pluginId ?? "", callbacks, nav);
|
|
1872
|
+
},
|
|
1873
|
+
[current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
|
|
1874
|
+
);
|
|
1875
|
+
const handleTextSubmit = (0, import_react11.useCallback)(
|
|
1876
|
+
(value) => {
|
|
1877
|
+
if (current.screen === "marketplace-add") {
|
|
1878
|
+
callbacks.marketplaceAdd(value).then((name) => {
|
|
1879
|
+
notify(`Added marketplace "${name}" from ${value}.`);
|
|
1880
|
+
pop();
|
|
1881
|
+
}).catch((err) => {
|
|
1882
|
+
notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1883
|
+
pop();
|
|
1884
|
+
});
|
|
942
1885
|
}
|
|
943
|
-
addMessage({ role: "user", content: input });
|
|
944
|
-
return runSessionPrompt(
|
|
945
|
-
input,
|
|
946
|
-
session,
|
|
947
|
-
addMessage,
|
|
948
|
-
clearStreamingText,
|
|
949
|
-
setIsThinking,
|
|
950
|
-
setContextPercentage
|
|
951
|
-
);
|
|
952
1886
|
},
|
|
953
|
-
[
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1887
|
+
[current.screen, callbacks, notify, pop]
|
|
1888
|
+
);
|
|
1889
|
+
if (confirm) {
|
|
1890
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1891
|
+
ConfirmPrompt,
|
|
1892
|
+
{
|
|
1893
|
+
message: confirm.message,
|
|
1894
|
+
onSelect: (index) => {
|
|
1895
|
+
if (index === 0) confirm.onConfirm();
|
|
1896
|
+
else confirm.onCancel();
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
);
|
|
1900
|
+
}
|
|
1901
|
+
const screen = current.screen;
|
|
1902
|
+
if (screen === "marketplace-add") {
|
|
1903
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1904
|
+
TextPrompt,
|
|
1905
|
+
{
|
|
1906
|
+
title: "Add Marketplace Source",
|
|
1907
|
+
placeholder: "owner/repo or git URL",
|
|
1908
|
+
onSubmit: handleTextSubmit,
|
|
1909
|
+
onCancel: pop,
|
|
1910
|
+
validate: (v) => !v.includes("/") ? "Must be owner/repo or a git URL" : void 0
|
|
1911
|
+
}
|
|
1912
|
+
);
|
|
1913
|
+
}
|
|
1914
|
+
if (screen === "marketplace-action") {
|
|
1915
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1916
|
+
MenuSelect,
|
|
1917
|
+
{
|
|
1918
|
+
title: `Marketplace: ${current.context?.marketplace ?? ""}`,
|
|
1919
|
+
items: [
|
|
1920
|
+
{ label: "Browse plugins", value: "browse" },
|
|
1921
|
+
{ label: "Update", value: "update" },
|
|
1922
|
+
{ label: "Remove", value: "remove" }
|
|
1923
|
+
],
|
|
1924
|
+
onSelect: handleSelect,
|
|
1925
|
+
onBack: pop
|
|
1926
|
+
},
|
|
1927
|
+
stack.length
|
|
1928
|
+
);
|
|
1929
|
+
}
|
|
1930
|
+
if (screen === "marketplace-install-scope") {
|
|
1931
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1932
|
+
MenuSelect,
|
|
1933
|
+
{
|
|
1934
|
+
title: `Install scope for "${current.context?.pluginId ?? ""}"`,
|
|
1935
|
+
items: [
|
|
1936
|
+
{ label: "User scope", value: "user" },
|
|
1937
|
+
{ label: "Project scope", value: "project" }
|
|
1938
|
+
],
|
|
1939
|
+
onSelect: handleSelect,
|
|
1940
|
+
onBack: pop
|
|
1941
|
+
},
|
|
1942
|
+
stack.length
|
|
1943
|
+
);
|
|
1944
|
+
}
|
|
1945
|
+
if (screen === "installed-action") {
|
|
1946
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1947
|
+
MenuSelect,
|
|
1948
|
+
{
|
|
1949
|
+
title: `Plugin: ${current.context?.pluginId ?? ""}`,
|
|
1950
|
+
items: [{ label: "Uninstall", value: "uninstall" }],
|
|
1951
|
+
onSelect: handleSelect,
|
|
1952
|
+
onBack: pop
|
|
1953
|
+
},
|
|
1954
|
+
stack.length
|
|
1955
|
+
);
|
|
1956
|
+
}
|
|
1957
|
+
const titleMap = {
|
|
1958
|
+
main: "Plugin Management",
|
|
1959
|
+
"marketplace-list": "Marketplace",
|
|
1960
|
+
"marketplace-browse": `Browse: ${current.context?.marketplace ?? ""}`,
|
|
1961
|
+
"installed-list": "Installed Plugins"
|
|
1962
|
+
};
|
|
1963
|
+
const staticItemsMap = {
|
|
1964
|
+
main: [
|
|
1965
|
+
{ label: "Marketplace", value: "marketplace" },
|
|
1966
|
+
{ label: "Installed Plugins", value: "installed" }
|
|
961
1967
|
]
|
|
1968
|
+
};
|
|
1969
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1970
|
+
MenuSelect,
|
|
1971
|
+
{
|
|
1972
|
+
title: titleMap[screen] ?? "Plugin Management",
|
|
1973
|
+
items: staticItemsMap[screen] ?? items,
|
|
1974
|
+
onSelect: handleSelect,
|
|
1975
|
+
onBack: pop,
|
|
1976
|
+
loading,
|
|
1977
|
+
error
|
|
1978
|
+
},
|
|
1979
|
+
`${screen}-${stack.length}-${refreshCounter}`
|
|
962
1980
|
);
|
|
963
1981
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1982
|
+
|
|
1983
|
+
// src/ui/ListPicker.tsx
|
|
1984
|
+
var import_react12 = require("react");
|
|
1985
|
+
var import_ink13 = require("ink");
|
|
1986
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1987
|
+
var DEFAULT_MAX_VISIBLE = 3;
|
|
1988
|
+
function ListPicker({
|
|
1989
|
+
items,
|
|
1990
|
+
renderItem,
|
|
1991
|
+
onSelect,
|
|
1992
|
+
onCancel,
|
|
1993
|
+
maxVisible = DEFAULT_MAX_VISIBLE
|
|
1994
|
+
}) {
|
|
1995
|
+
const [selectedIndex, setSelectedIndex] = (0, import_react12.useState)(0);
|
|
1996
|
+
const [scrollOffset, setScrollOffset] = (0, import_react12.useState)(0);
|
|
1997
|
+
const selectedRef = (0, import_react12.useRef)(0);
|
|
1998
|
+
const resolvedRef = (0, import_react12.useRef)(false);
|
|
1999
|
+
(0, import_ink13.useInput)((_input, key) => {
|
|
2000
|
+
if (resolvedRef.current) return;
|
|
2001
|
+
if (key.escape) {
|
|
2002
|
+
resolvedRef.current = true;
|
|
2003
|
+
onCancel();
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
2006
|
+
if (items.length === 0) return;
|
|
2007
|
+
if (key.upArrow) {
|
|
2008
|
+
const next = selectedRef.current > 0 ? selectedRef.current - 1 : selectedRef.current;
|
|
2009
|
+
selectedRef.current = next;
|
|
2010
|
+
setSelectedIndex(next);
|
|
2011
|
+
if (next < scrollOffset) {
|
|
2012
|
+
setScrollOffset(next);
|
|
2013
|
+
}
|
|
2014
|
+
} else if (key.downArrow) {
|
|
2015
|
+
const next = selectedRef.current < items.length - 1 ? selectedRef.current + 1 : selectedRef.current;
|
|
2016
|
+
selectedRef.current = next;
|
|
2017
|
+
setSelectedIndex(next);
|
|
2018
|
+
if (next >= scrollOffset + maxVisible) {
|
|
2019
|
+
setScrollOffset(next - maxVisible + 1);
|
|
2020
|
+
}
|
|
2021
|
+
} else if (key.return) {
|
|
2022
|
+
const item = items[selectedRef.current];
|
|
2023
|
+
if (item !== void 0) {
|
|
2024
|
+
resolvedRef.current = true;
|
|
2025
|
+
onSelect(item);
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
});
|
|
2029
|
+
if (items.length === 0) {
|
|
2030
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Box, {});
|
|
971
2031
|
}
|
|
972
|
-
|
|
2032
|
+
const visibleItems = items.slice(scrollOffset, scrollOffset + maxVisible);
|
|
2033
|
+
const hasMore = scrollOffset + maxVisible < items.length;
|
|
2034
|
+
const hasLess = scrollOffset > 0;
|
|
2035
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Box, { flexDirection: "column", children: [
|
|
2036
|
+
hasLess && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Text, { dimColor: true, children: [
|
|
2037
|
+
" \u2191 ",
|
|
2038
|
+
scrollOffset,
|
|
2039
|
+
" more above"
|
|
2040
|
+
] }),
|
|
2041
|
+
visibleItems.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink13.Box, { marginBottom: 1, children: renderItem(item, scrollOffset + index === selectedIndex) }, scrollOffset + index)),
|
|
2042
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink13.Text, { dimColor: true, children: [
|
|
2043
|
+
" \u2193 ",
|
|
2044
|
+
items.length - scrollOffset - maxVisible,
|
|
2045
|
+
" more below"
|
|
2046
|
+
] })
|
|
2047
|
+
] });
|
|
973
2048
|
}
|
|
2049
|
+
|
|
2050
|
+
// src/ui/App.tsx
|
|
2051
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2052
|
+
var EXIT_DELAY_MS = 500;
|
|
2053
|
+
var SESSION_ID_DISPLAY_LENGTH = 8;
|
|
974
2054
|
function App(props) {
|
|
975
|
-
const
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
2055
|
+
const [activeSessionId, setActiveSessionId] = (0, import_react13.useState)(props.resumeSessionId);
|
|
2056
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2057
|
+
AppInner,
|
|
2058
|
+
{
|
|
2059
|
+
...props,
|
|
2060
|
+
resumeSessionId: activeSessionId,
|
|
2061
|
+
onSessionSwitch: (sessionId) => setActiveSessionId(sessionId)
|
|
2062
|
+
},
|
|
2063
|
+
activeSessionId ?? "__new__"
|
|
2064
|
+
);
|
|
2065
|
+
}
|
|
2066
|
+
function AppInner(props) {
|
|
2067
|
+
const { exit } = (0, import_ink14.useApp)();
|
|
2068
|
+
const cwd = props.cwd;
|
|
2069
|
+
const {
|
|
2070
|
+
interactiveSession,
|
|
2071
|
+
registry,
|
|
2072
|
+
history,
|
|
2073
|
+
addEntry,
|
|
2074
|
+
streamingText,
|
|
2075
|
+
activeTools,
|
|
2076
|
+
isThinking,
|
|
2077
|
+
isAborting,
|
|
2078
|
+
pendingPrompt,
|
|
2079
|
+
permissionRequest,
|
|
2080
|
+
contextState,
|
|
2081
|
+
handleSubmit: baseHandleSubmit,
|
|
2082
|
+
handleAbort,
|
|
2083
|
+
handleCancelQueue
|
|
2084
|
+
} = useInteractiveSession({
|
|
2085
|
+
cwd,
|
|
2086
|
+
provider: props.provider,
|
|
2087
|
+
permissionMode: props.permissionMode,
|
|
2088
|
+
maxTurns: props.maxTurns,
|
|
2089
|
+
sessionStore: props.sessionStore,
|
|
2090
|
+
resumeSessionId: props.resumeSessionId,
|
|
2091
|
+
forkSession: props.forkSession,
|
|
2092
|
+
sessionName: props.sessionName
|
|
2093
|
+
});
|
|
2094
|
+
const pluginCallbacks = usePluginCallbacks(cwd);
|
|
2095
|
+
const [pendingModelId, setPendingModelId] = (0, import_react13.useState)(null);
|
|
2096
|
+
const pendingModelChangeRef = (0, import_react13.useRef)(null);
|
|
2097
|
+
const [showPluginTUI, setShowPluginTUI] = (0, import_react13.useState)(false);
|
|
2098
|
+
const [showSessionPicker, setShowSessionPicker] = (0, import_react13.useState)(
|
|
2099
|
+
props.resumeSessionId === "__picker__"
|
|
990
2100
|
);
|
|
991
|
-
(0,
|
|
2101
|
+
const [sessionName, setSessionName] = (0, import_react13.useState)(props.sessionName);
|
|
2102
|
+
(0, import_react13.useEffect)(() => {
|
|
2103
|
+
const name = interactiveSession?.getName?.();
|
|
2104
|
+
if (name && !sessionName) setSessionName(name);
|
|
2105
|
+
}, [interactiveSession, sessionName]);
|
|
2106
|
+
(0, import_react13.useEffect)(() => {
|
|
2107
|
+
const title = sessionName ? `Robota \u2014 ${sessionName}` : "Robota";
|
|
2108
|
+
process.stdout.write(`\x1B]0;${title}\x07`);
|
|
2109
|
+
}, [sessionName]);
|
|
2110
|
+
const handleSubmit = async (input) => {
|
|
2111
|
+
await baseHandleSubmit(input);
|
|
2112
|
+
const sideEffects = interactiveSession;
|
|
2113
|
+
if (sideEffects._pendingModelId) {
|
|
2114
|
+
const modelId = sideEffects._pendingModelId;
|
|
2115
|
+
delete sideEffects._pendingModelId;
|
|
2116
|
+
pendingModelChangeRef.current = modelId;
|
|
2117
|
+
setPendingModelId(modelId);
|
|
2118
|
+
return;
|
|
2119
|
+
}
|
|
2120
|
+
if (sideEffects._pendingLanguage) {
|
|
2121
|
+
const lang = sideEffects._pendingLanguage;
|
|
2122
|
+
delete sideEffects._pendingLanguage;
|
|
2123
|
+
const settingsPath = getUserSettingsPath();
|
|
2124
|
+
const settings = readSettings(settingsPath);
|
|
2125
|
+
settings.language = lang;
|
|
2126
|
+
writeSettings(settingsPath, settings);
|
|
2127
|
+
addEntry(
|
|
2128
|
+
(0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)(`Language set to "${lang}". Restarting...`))
|
|
2129
|
+
);
|
|
2130
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2131
|
+
return;
|
|
2132
|
+
}
|
|
2133
|
+
if (sideEffects._resetRequested) {
|
|
2134
|
+
delete sideEffects._resetRequested;
|
|
2135
|
+
const settingsPath = getUserSettingsPath();
|
|
2136
|
+
if (deleteSettings(settingsPath)) {
|
|
2137
|
+
addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)(`Deleted ${settingsPath}. Exiting...`)));
|
|
2138
|
+
} else {
|
|
2139
|
+
addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("No user settings found.")));
|
|
2140
|
+
}
|
|
2141
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2142
|
+
return;
|
|
2143
|
+
}
|
|
2144
|
+
if (sideEffects._exitRequested) {
|
|
2145
|
+
delete sideEffects._exitRequested;
|
|
2146
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2147
|
+
return;
|
|
2148
|
+
}
|
|
2149
|
+
if (sideEffects._triggerPluginTUI) {
|
|
2150
|
+
delete sideEffects._triggerPluginTUI;
|
|
2151
|
+
setShowPluginTUI(true);
|
|
2152
|
+
return;
|
|
2153
|
+
}
|
|
2154
|
+
if (sideEffects._triggerResumePicker) {
|
|
2155
|
+
delete sideEffects._triggerResumePicker;
|
|
2156
|
+
setShowSessionPicker(true);
|
|
2157
|
+
return;
|
|
2158
|
+
}
|
|
2159
|
+
if (sideEffects._sessionName) {
|
|
2160
|
+
const name = sideEffects._sessionName;
|
|
2161
|
+
delete sideEffects._sessionName;
|
|
2162
|
+
interactiveSession.setName(name);
|
|
2163
|
+
setSessionName(name);
|
|
2164
|
+
return;
|
|
2165
|
+
}
|
|
2166
|
+
};
|
|
2167
|
+
(0, import_ink14.useInput)(
|
|
992
2168
|
(_input, key) => {
|
|
993
|
-
if (key.
|
|
994
|
-
|
|
2169
|
+
if (key.escape && isThinking) {
|
|
2170
|
+
handleAbort();
|
|
2171
|
+
}
|
|
995
2172
|
},
|
|
996
|
-
{ isActive: !permissionRequest }
|
|
2173
|
+
{ isActive: !permissionRequest && !showPluginTUI && !showSessionPicker }
|
|
997
2174
|
);
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
2175
|
+
let permissionMode = props.permissionMode ?? "default";
|
|
2176
|
+
let sessionId = "";
|
|
2177
|
+
try {
|
|
2178
|
+
const session = interactiveSession.getSession();
|
|
2179
|
+
permissionMode = session.getPermissionMode();
|
|
2180
|
+
sessionId = session.getSessionId();
|
|
2181
|
+
} catch {
|
|
2182
|
+
}
|
|
2183
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Box, { flexDirection: "column", children: [
|
|
2184
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
2185
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { color: "cyan", bold: true, children: `
|
|
1001
2186
|
____ ___ ____ ___ _____ _
|
|
1002
2187
|
| _ \\ / _ \\| __ ) / _ \\_ _|/ \\
|
|
1003
2188
|
| |_) | | | | _ \\| | | || | / _ \\
|
|
1004
2189
|
| _ <| |_| | |_) | |_| || |/ ___ \\
|
|
1005
2190
|
|_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
|
|
1006
2191
|
` }),
|
|
1007
|
-
/* @__PURE__ */ (0,
|
|
2192
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Text, { dimColor: true, children: [
|
|
1008
2193
|
" v",
|
|
1009
2194
|
props.version ?? "0.0.0"
|
|
1010
2195
|
] })
|
|
1011
2196
|
] }),
|
|
1012
|
-
/* @__PURE__ */ (0,
|
|
1013
|
-
/* @__PURE__ */ (0,
|
|
1014
|
-
isThinking && /* @__PURE__ */ (0,
|
|
2197
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
|
|
2198
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(MessageList, { history }),
|
|
2199
|
+
(isThinking || activeTools.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(StreamingIndicator, { text: streamingText, activeTools }) })
|
|
1015
2200
|
] }),
|
|
1016
|
-
permissionRequest && /* @__PURE__ */ (0,
|
|
1017
|
-
/* @__PURE__ */ (0,
|
|
2201
|
+
permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PermissionPrompt, { request: permissionRequest }),
|
|
2202
|
+
pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2203
|
+
ConfirmPrompt,
|
|
2204
|
+
{
|
|
2205
|
+
message: `Change model to ${(0, import_agent_core4.getModelName)(pendingModelId)}? This will restart the session.`,
|
|
2206
|
+
onSelect: (index) => {
|
|
2207
|
+
setPendingModelId(null);
|
|
2208
|
+
pendingModelChangeRef.current = null;
|
|
2209
|
+
if (index === 0) {
|
|
2210
|
+
try {
|
|
2211
|
+
const settingsPath = getUserSettingsPath();
|
|
2212
|
+
updateModelInSettings(settingsPath, pendingModelId);
|
|
2213
|
+
addEntry(
|
|
2214
|
+
(0, import_agent_core4.messageToHistoryEntry)(
|
|
2215
|
+
(0, import_agent_core4.createSystemMessage)(
|
|
2216
|
+
`Model changed to ${(0, import_agent_core4.getModelName)(pendingModelId)}. Restarting...`
|
|
2217
|
+
)
|
|
2218
|
+
)
|
|
2219
|
+
);
|
|
2220
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2221
|
+
} catch (err) {
|
|
2222
|
+
addEntry(
|
|
2223
|
+
(0, import_agent_core4.messageToHistoryEntry)(
|
|
2224
|
+
(0, import_agent_core4.createSystemMessage)(
|
|
2225
|
+
`Failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2226
|
+
)
|
|
2227
|
+
)
|
|
2228
|
+
);
|
|
2229
|
+
}
|
|
2230
|
+
} else {
|
|
2231
|
+
addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("Model change cancelled.")));
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
),
|
|
2236
|
+
showPluginTUI && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2237
|
+
PluginTUI,
|
|
2238
|
+
{
|
|
2239
|
+
callbacks: pluginCallbacks,
|
|
2240
|
+
onClose: () => setShowPluginTUI(false),
|
|
2241
|
+
addMessage: (msg) => addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)(msg.content)))
|
|
2242
|
+
}
|
|
2243
|
+
),
|
|
2244
|
+
showSessionPicker && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
2245
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
|
|
2246
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2247
|
+
ListPicker,
|
|
2248
|
+
{
|
|
2249
|
+
items: (props.sessionStore?.list() ?? []).filter((s) => s.cwd === props.cwd),
|
|
2250
|
+
renderItem: (session, isSelected) => {
|
|
2251
|
+
const lastMsg = session.messages.slice().reverse().find((m) => {
|
|
2252
|
+
const msg = m;
|
|
2253
|
+
return msg.role === "assistant" && msg.content;
|
|
2254
|
+
});
|
|
2255
|
+
const rawPreview = lastMsg?.content?.replace(/[\n\r]+/g, " ").trim() ?? "";
|
|
2256
|
+
const preview = rawPreview ? rawPreview.slice(0, 60) + (rawPreview.length > 60 ? "..." : "") : "";
|
|
2257
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Text, { children: [
|
|
2258
|
+
isSelected ? "> " : " ",
|
|
2259
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
|
|
2260
|
+
" ",
|
|
2261
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
|
|
2262
|
+
month: "short",
|
|
2263
|
+
day: "numeric",
|
|
2264
|
+
hour: "2-digit",
|
|
2265
|
+
minute: "2-digit"
|
|
2266
|
+
}) }),
|
|
2267
|
+
" ",
|
|
2268
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Text, { dimColor: true, children: [
|
|
2269
|
+
"msgs: ",
|
|
2270
|
+
session.messages.length
|
|
2271
|
+
] }),
|
|
2272
|
+
preview ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
2273
|
+
"\n ",
|
|
2274
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { color: "gray", children: preview })
|
|
2275
|
+
] }) : null
|
|
2276
|
+
] });
|
|
2277
|
+
},
|
|
2278
|
+
onSelect: (session) => {
|
|
2279
|
+
setShowSessionPicker(false);
|
|
2280
|
+
props.onSessionSwitch(session.id);
|
|
2281
|
+
},
|
|
2282
|
+
onCancel: () => {
|
|
2283
|
+
setShowSessionPicker(false);
|
|
2284
|
+
addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("Session resume cancelled.")));
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
)
|
|
2288
|
+
] }),
|
|
2289
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1018
2290
|
StatusBar,
|
|
1019
2291
|
{
|
|
1020
|
-
permissionMode
|
|
1021
|
-
modelName: props.
|
|
1022
|
-
sessionId
|
|
1023
|
-
messageCount:
|
|
2292
|
+
permissionMode,
|
|
2293
|
+
modelName: props.modelId ? (0, import_agent_core4.getModelName)(props.modelId) : "",
|
|
2294
|
+
sessionId,
|
|
2295
|
+
messageCount: history.length,
|
|
1024
2296
|
isThinking,
|
|
1025
|
-
contextPercentage,
|
|
1026
|
-
contextUsedTokens:
|
|
1027
|
-
contextMaxTokens:
|
|
2297
|
+
contextPercentage: contextState.percentage,
|
|
2298
|
+
contextUsedTokens: contextState.usedTokens,
|
|
2299
|
+
contextMaxTokens: contextState.maxTokens,
|
|
2300
|
+
sessionName
|
|
1028
2301
|
}
|
|
1029
2302
|
),
|
|
1030
|
-
/* @__PURE__ */ (0,
|
|
2303
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1031
2304
|
InputArea,
|
|
1032
2305
|
{
|
|
1033
2306
|
onSubmit: handleSubmit,
|
|
1034
|
-
|
|
1035
|
-
|
|
2307
|
+
onCancelQueue: handleCancelQueue,
|
|
2308
|
+
isDisabled: !!permissionRequest || showPluginTUI || showSessionPicker || isThinking && !!pendingPrompt,
|
|
2309
|
+
isAborting,
|
|
2310
|
+
pendingPrompt,
|
|
2311
|
+
registry,
|
|
2312
|
+
sessionName
|
|
1036
2313
|
}
|
|
1037
|
-
)
|
|
2314
|
+
),
|
|
2315
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Text, { children: " " })
|
|
1038
2316
|
] });
|
|
1039
2317
|
}
|
|
1040
2318
|
|
|
1041
2319
|
// src/ui/render.tsx
|
|
1042
|
-
var
|
|
2320
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1043
2321
|
function renderApp(options) {
|
|
1044
2322
|
process.on("unhandledRejection", (reason) => {
|
|
1045
2323
|
process.stderr.write(`
|
|
@@ -1050,29 +2328,50 @@ function renderApp(options) {
|
|
|
1050
2328
|
`);
|
|
1051
2329
|
}
|
|
1052
2330
|
});
|
|
1053
|
-
|
|
2331
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
2332
|
+
process.stdout.write("\x1B[?2004h");
|
|
2333
|
+
}
|
|
2334
|
+
const instance = (0, import_ink15.render)(/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(App, { ...options }), {
|
|
1054
2335
|
exitOnCtrlC: true
|
|
1055
2336
|
});
|
|
1056
|
-
instance.waitUntilExit().
|
|
2337
|
+
instance.waitUntilExit().then(() => {
|
|
2338
|
+
if (process.stdout.isTTY) {
|
|
2339
|
+
process.stdout.write("\x1B[?2004l");
|
|
2340
|
+
}
|
|
2341
|
+
process.exit(0);
|
|
2342
|
+
}).catch((err) => {
|
|
1057
2343
|
if (err) {
|
|
1058
2344
|
process.stderr.write(`
|
|
1059
2345
|
[EXIT ERROR] ${err}
|
|
1060
2346
|
`);
|
|
1061
2347
|
}
|
|
2348
|
+
process.exit(1);
|
|
1062
2349
|
});
|
|
1063
2350
|
}
|
|
1064
2351
|
|
|
1065
2352
|
// src/cli.ts
|
|
1066
2353
|
var import_meta = {};
|
|
1067
|
-
|
|
2354
|
+
function checkSettingsFile(filePath) {
|
|
2355
|
+
if (!(0, import_node_fs3.existsSync)(filePath)) return "missing";
|
|
2356
|
+
try {
|
|
2357
|
+
const raw = (0, import_node_fs3.readFileSync)(filePath, "utf8").trim();
|
|
2358
|
+
if (raw.length === 0) return "incomplete";
|
|
2359
|
+
const parsed = JSON.parse(raw);
|
|
2360
|
+
const provider = parsed.provider;
|
|
2361
|
+
if (!provider?.apiKey) return "incomplete";
|
|
2362
|
+
return "valid";
|
|
2363
|
+
} catch {
|
|
2364
|
+
return "corrupt";
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
1068
2367
|
function readVersion() {
|
|
1069
2368
|
try {
|
|
1070
2369
|
const thisFile = (0, import_node_url.fileURLToPath)(import_meta.url);
|
|
1071
|
-
const dir = (0,
|
|
1072
|
-
const candidates = [(0,
|
|
2370
|
+
const dir = (0, import_node_path5.dirname)(thisFile);
|
|
2371
|
+
const candidates = [(0, import_node_path5.join)(dir, "..", "..", "package.json"), (0, import_node_path5.join)(dir, "..", "package.json")];
|
|
1073
2372
|
for (const pkgPath of candidates) {
|
|
1074
2373
|
try {
|
|
1075
|
-
const raw = (0,
|
|
2374
|
+
const raw = (0, import_node_fs3.readFileSync)(pkgPath, "utf-8");
|
|
1076
2375
|
const pkg = JSON.parse(raw);
|
|
1077
2376
|
if (pkg.version !== void 0 && pkg.name !== void 0) {
|
|
1078
2377
|
return pkg.version;
|
|
@@ -1085,97 +2384,107 @@ function readVersion() {
|
|
|
1085
2384
|
return "0.0.0";
|
|
1086
2385
|
}
|
|
1087
2386
|
}
|
|
1088
|
-
function
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
2387
|
+
function promptInput(label, masked = false) {
|
|
2388
|
+
return new Promise((resolve) => {
|
|
2389
|
+
process.stdout.write(label);
|
|
2390
|
+
let input = "";
|
|
2391
|
+
const stdin = process.stdin;
|
|
2392
|
+
const wasRaw = stdin.isRaw;
|
|
2393
|
+
stdin.setRawMode(true);
|
|
2394
|
+
stdin.resume();
|
|
2395
|
+
stdin.setEncoding("utf8");
|
|
2396
|
+
const onData = (data) => {
|
|
2397
|
+
for (const ch of data) {
|
|
2398
|
+
if (ch === "\r" || ch === "\n") {
|
|
2399
|
+
stdin.removeListener("data", onData);
|
|
2400
|
+
stdin.setRawMode(wasRaw ?? false);
|
|
2401
|
+
stdin.pause();
|
|
2402
|
+
process.stdout.write("\n");
|
|
2403
|
+
resolve(input.trim());
|
|
2404
|
+
return;
|
|
2405
|
+
} else if (ch === "\x7F" || ch === "\b") {
|
|
2406
|
+
if (input.length > 0) {
|
|
2407
|
+
input = input.slice(0, -1);
|
|
2408
|
+
process.stdout.write("\b \b");
|
|
2409
|
+
}
|
|
2410
|
+
} else if (ch === "") {
|
|
2411
|
+
process.stdout.write("\n");
|
|
2412
|
+
process.exit(0);
|
|
2413
|
+
} else if (ch.charCodeAt(0) >= 32) {
|
|
2414
|
+
input += ch;
|
|
2415
|
+
process.stdout.write(masked ? "*" : ch);
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
};
|
|
2419
|
+
stdin.on("data", onData);
|
|
1119
2420
|
});
|
|
1120
|
-
return {
|
|
1121
|
-
positional: positionals,
|
|
1122
|
-
printMode: values["p"] ?? false,
|
|
1123
|
-
continueMode: values["c"] ?? false,
|
|
1124
|
-
resumeId: values["r"],
|
|
1125
|
-
model: values["model"],
|
|
1126
|
-
permissionMode: parsePermissionMode(values["permission-mode"]),
|
|
1127
|
-
maxTurns: parseMaxTurns(values["max-turns"]),
|
|
1128
|
-
version: values["version"] ?? false
|
|
1129
|
-
};
|
|
1130
2421
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
2422
|
+
async function ensureConfig(cwd) {
|
|
2423
|
+
const userPath = getUserSettingsPath();
|
|
2424
|
+
const projectPath = (0, import_node_path5.join)(cwd, ".robota", "settings.json");
|
|
2425
|
+
const localPath = (0, import_node_path5.join)(cwd, ".robota", "settings.local.json");
|
|
2426
|
+
const paths = [userPath, projectPath, localPath];
|
|
2427
|
+
const checks = paths.map((p) => ({ path: p, status: checkSettingsFile(p) }));
|
|
2428
|
+
if (checks.some((c) => c.status === "valid")) {
|
|
2429
|
+
return;
|
|
1134
2430
|
}
|
|
1135
|
-
|
|
1136
|
-
|
|
2431
|
+
const corrupt = checks.filter((c) => c.status === "corrupt");
|
|
2432
|
+
const incomplete = checks.filter((c) => c.status === "incomplete");
|
|
2433
|
+
process.stdout.write("\n");
|
|
2434
|
+
if (corrupt.length > 0) {
|
|
2435
|
+
for (const c of corrupt) {
|
|
2436
|
+
process.stderr.write(` ERROR: Settings file is corrupt (invalid JSON): ${c.path}
|
|
2437
|
+
`);
|
|
2438
|
+
}
|
|
2439
|
+
process.stdout.write("\n");
|
|
1137
2440
|
}
|
|
1138
|
-
|
|
1139
|
-
|
|
2441
|
+
if (incomplete.length > 0) {
|
|
2442
|
+
for (const c of incomplete) {
|
|
2443
|
+
process.stderr.write(` WARNING: Settings file is missing provider.apiKey: ${c.path}
|
|
2444
|
+
`);
|
|
2445
|
+
}
|
|
2446
|
+
process.stdout.write("\n");
|
|
1140
2447
|
}
|
|
1141
|
-
|
|
1142
|
-
process.
|
|
2448
|
+
if (corrupt.length === 0 && incomplete.length === 0) {
|
|
2449
|
+
process.stdout.write(" Welcome to Robota CLI!\n");
|
|
2450
|
+
process.stdout.write(" No configuration found. Let's set up.\n");
|
|
2451
|
+
} else {
|
|
2452
|
+
process.stdout.write(" Reconfiguring...\n");
|
|
1143
2453
|
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
terminal: false,
|
|
1150
|
-
historySize: 0
|
|
1151
|
-
});
|
|
1152
|
-
rl.question(question, (answer) => {
|
|
1153
|
-
rl.close();
|
|
1154
|
-
resolve(answer);
|
|
1155
|
-
});
|
|
1156
|
-
});
|
|
2454
|
+
process.stdout.write("\n");
|
|
2455
|
+
const apiKey = await promptInput(" Anthropic API key: ", true);
|
|
2456
|
+
if (!apiKey) {
|
|
2457
|
+
process.stderr.write("\n No API key provided. Exiting.\n");
|
|
2458
|
+
process.exit(1);
|
|
1157
2459
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
2460
|
+
const language = await promptInput(" Response language (ko/en/ja/zh, default: en): ");
|
|
2461
|
+
const settingsDir = (0, import_node_path5.dirname)(userPath);
|
|
2462
|
+
(0, import_node_fs3.mkdirSync)(settingsDir, { recursive: true });
|
|
2463
|
+
const settings = {
|
|
2464
|
+
provider: {
|
|
2465
|
+
name: "anthropic",
|
|
2466
|
+
model: "claude-sonnet-4-6",
|
|
2467
|
+
apiKey
|
|
1163
2468
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
const trimmed = answer.trim().toLowerCase();
|
|
1168
|
-
if (trimmed === "") return initialIndex;
|
|
1169
|
-
const num = parseInt(trimmed, 10);
|
|
1170
|
-
if (!isNaN(num) && num >= 1 && num <= options.length) return num - 1;
|
|
1171
|
-
return initialIndex;
|
|
2469
|
+
};
|
|
2470
|
+
if (language) {
|
|
2471
|
+
settings.language = language;
|
|
1172
2472
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
2473
|
+
(0, import_node_fs3.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
2474
|
+
process.stdout.write(`
|
|
2475
|
+
Config saved to ${userPath}
|
|
2476
|
+
|
|
2477
|
+
`);
|
|
2478
|
+
}
|
|
2479
|
+
function resetConfig() {
|
|
2480
|
+
const userPath = getUserSettingsPath();
|
|
2481
|
+
if (deleteSettings(userPath)) {
|
|
2482
|
+
process.stdout.write(`Deleted ${userPath}
|
|
2483
|
+
`);
|
|
2484
|
+
} else {
|
|
2485
|
+
process.stdout.write("No user settings found.\n");
|
|
1177
2486
|
}
|
|
1178
|
-
}
|
|
2487
|
+
}
|
|
1179
2488
|
async function startCli() {
|
|
1180
2489
|
const args = parseCliArgs();
|
|
1181
2490
|
if (args.version) {
|
|
@@ -1183,52 +2492,81 @@ async function startCli() {
|
|
|
1183
2492
|
`);
|
|
1184
2493
|
return;
|
|
1185
2494
|
}
|
|
2495
|
+
if (args.reset) {
|
|
2496
|
+
resetConfig();
|
|
2497
|
+
return;
|
|
2498
|
+
}
|
|
1186
2499
|
const cwd = process.cwd();
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
2500
|
+
await ensureConfig(cwd);
|
|
2501
|
+
const providerSettings = readProviderSettings(cwd);
|
|
2502
|
+
const modelId = args.model ?? providerSettings.model;
|
|
2503
|
+
const provider = createProviderFromSettings(cwd, args.model);
|
|
2504
|
+
const sessionStore = new import_agent_sessions.SessionStore();
|
|
2505
|
+
let resumeSessionId;
|
|
2506
|
+
if (args.continueMode) {
|
|
2507
|
+
const sessions = sessionStore.list().filter((s) => s.cwd === cwd);
|
|
2508
|
+
if (sessions.length > 0) {
|
|
2509
|
+
resumeSessionId = sessions[0].id;
|
|
2510
|
+
}
|
|
2511
|
+
} else if (args.resumeId !== void 0) {
|
|
2512
|
+
if (args.resumeId === "") {
|
|
2513
|
+
resumeSessionId = "__picker__";
|
|
2514
|
+
} else {
|
|
2515
|
+
const sessions = sessionStore.list();
|
|
2516
|
+
const match = sessions.find((s) => s.id === args.resumeId || s.name === args.resumeId);
|
|
2517
|
+
if (match) {
|
|
2518
|
+
resumeSessionId = match.id;
|
|
2519
|
+
} else {
|
|
2520
|
+
process.stderr.write(`Session not found: ${args.resumeId}
|
|
2521
|
+
`);
|
|
2522
|
+
process.exit(1);
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
1194
2525
|
}
|
|
1195
|
-
const sessionStore = new import_agent_sdk2.SessionStore();
|
|
1196
2526
|
if (args.printMode) {
|
|
1197
|
-
|
|
1198
|
-
if (prompt
|
|
2527
|
+
let prompt = args.positional.join(" ").trim();
|
|
2528
|
+
if (!prompt && !process.stdin.isTTY) {
|
|
2529
|
+
const chunks = [];
|
|
2530
|
+
for await (const chunk of process.stdin) {
|
|
2531
|
+
chunks.push(chunk);
|
|
2532
|
+
}
|
|
2533
|
+
prompt = Buffer.concat(chunks).toString("utf-8").trim();
|
|
2534
|
+
}
|
|
2535
|
+
if (!prompt) {
|
|
1199
2536
|
process.stderr.write("Print mode (-p) requires a prompt argument.\n");
|
|
1200
2537
|
process.exit(1);
|
|
1201
2538
|
}
|
|
1202
|
-
const
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
projectInfo,
|
|
1210
|
-
permissionMode: args.permissionMode,
|
|
1211
|
-
promptForApproval
|
|
2539
|
+
const session = new import_agent_sdk3.InteractiveSession({
|
|
2540
|
+
cwd,
|
|
2541
|
+
provider,
|
|
2542
|
+
permissionMode: args.permissionMode ?? "bypassPermissions",
|
|
2543
|
+
maxTurns: args.maxTurns,
|
|
2544
|
+
sessionStore,
|
|
2545
|
+
sessionName: args.sessionName
|
|
1212
2546
|
});
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1215
|
-
|
|
2547
|
+
const transport = (0, import_agent_transport_headless.createHeadlessTransport)({
|
|
2548
|
+
outputFormat: args.outputFormat ?? "text",
|
|
2549
|
+
prompt
|
|
2550
|
+
});
|
|
2551
|
+
session.attachTransport(transport);
|
|
2552
|
+
await transport.start();
|
|
2553
|
+
process.exit(transport.getExitCode());
|
|
1216
2554
|
}
|
|
1217
2555
|
renderApp({
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
2556
|
+
cwd,
|
|
2557
|
+
provider,
|
|
2558
|
+
modelId,
|
|
2559
|
+
language: args.language,
|
|
1222
2560
|
permissionMode: args.permissionMode,
|
|
1223
2561
|
maxTurns: args.maxTurns,
|
|
1224
|
-
version: readVersion()
|
|
2562
|
+
version: readVersion(),
|
|
2563
|
+
sessionStore,
|
|
2564
|
+
resumeSessionId,
|
|
2565
|
+
forkSession: args.forkSession,
|
|
2566
|
+
sessionName: args.sessionName
|
|
1225
2567
|
});
|
|
1226
2568
|
}
|
|
1227
2569
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1228
2570
|
0 && (module.exports = {
|
|
1229
|
-
Session,
|
|
1230
|
-
SessionStore,
|
|
1231
|
-
TRUST_TO_MODE,
|
|
1232
|
-
query,
|
|
1233
2571
|
startCli
|
|
1234
2572
|
});
|