@robota-sdk/agent-cli 3.0.0-beta.45 → 3.0.0-beta.47
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 +67 -13
- package/dist/node/bin.js +1 -1
- package/dist/node/{chunk-Y6VSMUKG.js → chunk-74WEPZG2.js} +346 -98
- package/dist/node/index.cjs +345 -97
- package/dist/node/index.js +1 -1
- package/package.json +6 -4
|
@@ -3,6 +3,7 @@ import { readFileSync as readFileSync3, existsSync as existsSync3, mkdirSync as
|
|
|
3
3
|
import { join as join5, dirname as dirname2 } from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import { InteractiveSession as InteractiveSession2 } from "@robota-sdk/agent-sdk";
|
|
6
|
+
import { SessionStore } from "@robota-sdk/agent-sessions";
|
|
6
7
|
|
|
7
8
|
// src/utils/cli-args.ts
|
|
8
9
|
import { parseArgs } from "util";
|
|
@@ -31,12 +32,17 @@ function parseCliArgs() {
|
|
|
31
32
|
allowPositionals: true,
|
|
32
33
|
options: {
|
|
33
34
|
p: { type: "boolean", short: "p", default: false },
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
continue: { type: "boolean", short: "c", default: false },
|
|
36
|
+
resume: { type: "string", short: "r" },
|
|
36
37
|
model: { type: "string" },
|
|
37
38
|
language: { type: "string" },
|
|
38
39
|
"permission-mode": { type: "string" },
|
|
39
40
|
"max-turns": { type: "string" },
|
|
41
|
+
"fork-session": { type: "boolean", default: false },
|
|
42
|
+
name: { type: "string", short: "n" },
|
|
43
|
+
"output-format": { type: "string" },
|
|
44
|
+
"system-prompt": { type: "string" },
|
|
45
|
+
"append-system-prompt": { type: "string" },
|
|
40
46
|
version: { type: "boolean", default: false },
|
|
41
47
|
reset: { type: "boolean", default: false }
|
|
42
48
|
}
|
|
@@ -44,12 +50,17 @@ function parseCliArgs() {
|
|
|
44
50
|
return {
|
|
45
51
|
positional: positionals,
|
|
46
52
|
printMode: values["p"] ?? false,
|
|
47
|
-
continueMode: values["
|
|
48
|
-
resumeId: values["
|
|
53
|
+
continueMode: values["continue"] ?? false,
|
|
54
|
+
resumeId: values["resume"],
|
|
49
55
|
model: values["model"],
|
|
50
56
|
language: values["language"],
|
|
51
57
|
permissionMode: parsePermissionMode(values["permission-mode"]),
|
|
52
58
|
maxTurns: parseMaxTurns(values["max-turns"]),
|
|
59
|
+
forkSession: values["fork-session"] ?? false,
|
|
60
|
+
sessionName: values["name"],
|
|
61
|
+
outputFormat: values["output-format"],
|
|
62
|
+
systemPrompt: values["system-prompt"],
|
|
63
|
+
appendSystemPrompt: values["append-system-prompt"],
|
|
53
64
|
version: values["version"] ?? false,
|
|
54
65
|
reset: values["reset"] ?? false
|
|
55
66
|
};
|
|
@@ -129,12 +140,15 @@ function createProviderFromSettings(cwd, modelOverride) {
|
|
|
129
140
|
}
|
|
130
141
|
}
|
|
131
142
|
|
|
143
|
+
// src/cli.ts
|
|
144
|
+
import { createHeadlessTransport } from "@robota-sdk/agent-transport-headless";
|
|
145
|
+
|
|
132
146
|
// src/ui/render.tsx
|
|
133
147
|
import { render } from "ink";
|
|
134
148
|
|
|
135
149
|
// src/ui/App.tsx
|
|
136
|
-
import { useState as
|
|
137
|
-
import { Box as
|
|
150
|
+
import { useState as useState10, useRef as useRef8, useEffect as useEffect4 } from "react";
|
|
151
|
+
import { Box as Box12, Text as Text14, useApp, useInput as useInput8 } from "ink";
|
|
138
152
|
import { getModelName, createSystemMessage as createSystemMessage2, messageToHistoryEntry as messageToHistoryEntry2 } from "@robota-sdk/agent-core";
|
|
139
153
|
|
|
140
154
|
// src/ui/hooks/useInteractiveSession.ts
|
|
@@ -258,7 +272,11 @@ function initializeSession(props, permissionHandler) {
|
|
|
258
272
|
provider: props.provider,
|
|
259
273
|
permissionMode: props.permissionMode,
|
|
260
274
|
maxTurns: props.maxTurns,
|
|
261
|
-
permissionHandler
|
|
275
|
+
permissionHandler,
|
|
276
|
+
sessionStore: props.sessionStore,
|
|
277
|
+
resumeSessionId: props.resumeSessionId,
|
|
278
|
+
forkSession: props.forkSession,
|
|
279
|
+
sessionName: props.sessionName
|
|
262
280
|
});
|
|
263
281
|
const registry = new CommandRegistry();
|
|
264
282
|
registry.addSource(new BuiltinCommandSource());
|
|
@@ -313,6 +331,12 @@ function useInteractiveSession(props) {
|
|
|
313
331
|
}
|
|
314
332
|
const { interactiveSession, registry, manager } = stateRef.current;
|
|
315
333
|
manager.onChange = () => forceRender((n) => n + 1);
|
|
334
|
+
if (manager.history.length === 0) {
|
|
335
|
+
const restored = interactiveSession.getFullHistory();
|
|
336
|
+
if (restored.length > 0) {
|
|
337
|
+
manager.syncHistory(restored);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
316
340
|
useEffect(() => {
|
|
317
341
|
interactiveSession.on("text_delta", manager.onTextDelta);
|
|
318
342
|
interactiveSession.on("tool_start", manager.onToolStart);
|
|
@@ -329,6 +353,10 @@ function useInteractiveSession(props) {
|
|
|
329
353
|
usedTokens: ctx.usedTokens,
|
|
330
354
|
maxTokens: ctx.maxTokens
|
|
331
355
|
});
|
|
356
|
+
const restored = interactiveSession.getFullHistory();
|
|
357
|
+
if (restored.length > 0) {
|
|
358
|
+
manager.syncHistory(restored);
|
|
359
|
+
}
|
|
332
360
|
clearInterval(initCheck);
|
|
333
361
|
} catch {
|
|
334
362
|
}
|
|
@@ -372,6 +400,14 @@ function useInteractiveSession(props) {
|
|
|
372
400
|
effects._resetRequested = true;
|
|
373
401
|
return;
|
|
374
402
|
}
|
|
403
|
+
if (result.data?.triggerResumePicker) {
|
|
404
|
+
effects._triggerResumePicker = true;
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
if (result.data?.name) {
|
|
408
|
+
effects._sessionName = result.data.name;
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
375
411
|
const ctx = interactiveSession.getContextState();
|
|
376
412
|
manager.setContextState({
|
|
377
413
|
percentage: ctx.usedPercentage,
|
|
@@ -764,7 +800,7 @@ function MessageList({ history }) {
|
|
|
764
800
|
// src/ui/StatusBar.tsx
|
|
765
801
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
766
802
|
import { formatTokenCount } from "@robota-sdk/agent-core";
|
|
767
|
-
import { jsx as jsx2, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
803
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
768
804
|
var CONTEXT_YELLOW_THRESHOLD = 70;
|
|
769
805
|
var CONTEXT_RED_THRESHOLD = 90;
|
|
770
806
|
function getContextColor(percentage) {
|
|
@@ -780,7 +816,8 @@ function StatusBar({
|
|
|
780
816
|
isThinking,
|
|
781
817
|
contextPercentage,
|
|
782
818
|
contextUsedTokens,
|
|
783
|
-
contextMaxTokens
|
|
819
|
+
contextMaxTokens,
|
|
820
|
+
sessionName
|
|
784
821
|
}) {
|
|
785
822
|
const contextColor = getContextColor(contextPercentage);
|
|
786
823
|
return /* @__PURE__ */ jsxs3(
|
|
@@ -796,6 +833,10 @@ function StatusBar({
|
|
|
796
833
|
/* @__PURE__ */ jsx2(Text3, { color: "cyan", bold: true, children: "Mode:" }),
|
|
797
834
|
" ",
|
|
798
835
|
/* @__PURE__ */ jsx2(Text3, { children: permissionMode }),
|
|
836
|
+
sessionName && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
837
|
+
" | ",
|
|
838
|
+
/* @__PURE__ */ jsx2(Text3, { color: "magenta", children: sessionName })
|
|
839
|
+
] }),
|
|
799
840
|
" | ",
|
|
800
841
|
/* @__PURE__ */ jsx2(Text3, { dimColor: true, children: modelName }),
|
|
801
842
|
" | ",
|
|
@@ -879,9 +920,7 @@ function CjkTextInput({
|
|
|
879
920
|
const pasteBufferRef = useRef2("");
|
|
880
921
|
if (value !== valueRef.current) {
|
|
881
922
|
valueRef.current = value;
|
|
882
|
-
|
|
883
|
-
cursorRef.current = value.length;
|
|
884
|
-
}
|
|
923
|
+
cursorRef.current = value.length;
|
|
885
924
|
}
|
|
886
925
|
useInput(
|
|
887
926
|
(input, key) => {
|
|
@@ -1137,7 +1176,8 @@ function InputArea({
|
|
|
1137
1176
|
isDisabled,
|
|
1138
1177
|
isAborting,
|
|
1139
1178
|
pendingPrompt,
|
|
1140
|
-
registry
|
|
1179
|
+
registry,
|
|
1180
|
+
sessionName
|
|
1141
1181
|
}) {
|
|
1142
1182
|
const [value, setValue] = useState4("");
|
|
1143
1183
|
const pasteStore = useRef3(/* @__PURE__ */ new Map());
|
|
@@ -1161,23 +1201,23 @@ function InputArea({
|
|
|
1161
1201
|
const label = `[Pasted text #${id} +${lineCount} lines]`;
|
|
1162
1202
|
setValue((prev) => prev ? `${prev} ${label}` : label);
|
|
1163
1203
|
}, []);
|
|
1164
|
-
const
|
|
1165
|
-
(
|
|
1166
|
-
const
|
|
1167
|
-
if (
|
|
1168
|
-
|
|
1169
|
-
selectCommand(filteredCommands[selectedIndex]);
|
|
1204
|
+
const tabCompleteCommand = useCallback2(
|
|
1205
|
+
(cmd) => {
|
|
1206
|
+
const parsed = parseSlashInput(value);
|
|
1207
|
+
if (parsed.parentCommand) {
|
|
1208
|
+
setValue(`/${parsed.parentCommand} ${cmd.name} `);
|
|
1170
1209
|
return;
|
|
1171
1210
|
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1211
|
+
if (cmd.subcommands && cmd.subcommands.length > 0) {
|
|
1212
|
+
setValue(`/${cmd.name} `);
|
|
1213
|
+
setSelectedIndex(0);
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
setValue(`/${cmd.name} `);
|
|
1177
1217
|
},
|
|
1178
|
-
[
|
|
1218
|
+
[value, setSelectedIndex]
|
|
1179
1219
|
);
|
|
1180
|
-
const
|
|
1220
|
+
const enterSelectCommand = useCallback2(
|
|
1181
1221
|
(cmd) => {
|
|
1182
1222
|
const parsed = parseSlashInput(value);
|
|
1183
1223
|
if (parsed.parentCommand) {
|
|
@@ -1196,6 +1236,22 @@ function InputArea({
|
|
|
1196
1236
|
},
|
|
1197
1237
|
[value, onSubmit, setSelectedIndex]
|
|
1198
1238
|
);
|
|
1239
|
+
const handleSubmit = useCallback2(
|
|
1240
|
+
(text) => {
|
|
1241
|
+
const trimmed = text.trim();
|
|
1242
|
+
if (trimmed.length === 0) return;
|
|
1243
|
+
if (showPopup && filteredCommands[selectedIndex]) {
|
|
1244
|
+
enterSelectCommand(filteredCommands[selectedIndex]);
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
const expanded = expandPasteLabels(trimmed, pasteStore.current);
|
|
1248
|
+
setValue("");
|
|
1249
|
+
pasteStore.current.clear();
|
|
1250
|
+
pasteIdRef.current = 0;
|
|
1251
|
+
onSubmit(expanded);
|
|
1252
|
+
},
|
|
1253
|
+
[showPopup, filteredCommands, selectedIndex, onSubmit, enterSelectCommand]
|
|
1254
|
+
);
|
|
1199
1255
|
useInput2(
|
|
1200
1256
|
(_input, key) => {
|
|
1201
1257
|
if (!showPopup) return;
|
|
@@ -1207,7 +1263,7 @@ function InputArea({
|
|
|
1207
1263
|
setShowPopup(false);
|
|
1208
1264
|
} else if (key.tab) {
|
|
1209
1265
|
const cmd = filteredCommands[selectedIndex];
|
|
1210
|
-
if (cmd)
|
|
1266
|
+
if (cmd) tabCompleteCommand(cmd);
|
|
1211
1267
|
}
|
|
1212
1268
|
},
|
|
1213
1269
|
{ isActive: showPopup && !isDisabled }
|
|
@@ -1220,6 +1276,17 @@ function InputArea({
|
|
|
1220
1276
|
},
|
|
1221
1277
|
{ isActive: !!pendingPrompt }
|
|
1222
1278
|
);
|
|
1279
|
+
const borderColor = isAborting ? "yellow" : pendingPrompt ? "cyan" : isDisabled ? "gray" : "green";
|
|
1280
|
+
const innerWidth = Math.max(1, terminalColumns - BORDER_HORIZONTAL);
|
|
1281
|
+
const topBorder = (() => {
|
|
1282
|
+
if (sessionName) {
|
|
1283
|
+
const label = ` "${sessionName}" `;
|
|
1284
|
+
const rightPad = 2;
|
|
1285
|
+
const leftLen = Math.max(0, innerWidth - label.length - rightPad);
|
|
1286
|
+
return { left: "\u250C" + "\u2500".repeat(leftLen), label, right: "\u2500".repeat(rightPad) + "\u2510" };
|
|
1287
|
+
}
|
|
1288
|
+
return { left: "\u250C" + "\u2500".repeat(innerWidth), label: "", right: "\u2510" };
|
|
1289
|
+
})();
|
|
1223
1290
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
1224
1291
|
showPopup && /* @__PURE__ */ jsx6(
|
|
1225
1292
|
SlashAutocomplete,
|
|
@@ -1230,34 +1297,31 @@ function InputArea({
|
|
|
1230
1297
|
isSubcommandMode
|
|
1231
1298
|
}
|
|
1232
1299
|
),
|
|
1233
|
-
/* @__PURE__ */
|
|
1234
|
-
|
|
1235
|
-
{
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
] })
|
|
1259
|
-
}
|
|
1260
|
-
)
|
|
1300
|
+
/* @__PURE__ */ jsxs5(Text7, { color: borderColor, children: [
|
|
1301
|
+
topBorder.left,
|
|
1302
|
+
topBorder.label ? /* @__PURE__ */ jsx6(Text7, { backgroundColor: borderColor, color: "black", bold: true, children: topBorder.label }) : null,
|
|
1303
|
+
topBorder.right
|
|
1304
|
+
] }),
|
|
1305
|
+
/* @__PURE__ */ jsx6(Box5, { borderStyle: "single", borderTop: false, borderColor, paddingLeft: 1, children: isAborting ? /* @__PURE__ */ jsx6(Text7, { color: "yellow", children: " Interrupting..." }) : pendingPrompt ? /* @__PURE__ */ jsxs5(Text7, { color: "cyan", children: [
|
|
1306
|
+
" ",
|
|
1307
|
+
"Queued: ",
|
|
1308
|
+
pendingPrompt.length > 50 ? pendingPrompt.slice(0, 47) + "..." : pendingPrompt,
|
|
1309
|
+
" ",
|
|
1310
|
+
/* @__PURE__ */ jsx6(Text7, { dimColor: true, children: "(Backspace to cancel)" })
|
|
1311
|
+
] }) : isDisabled ? /* @__PURE__ */ jsx6(WaveText, { text: " Waiting for response... (ESC to interrupt)" }) : /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
1312
|
+
/* @__PURE__ */ jsx6(Text7, { color: "green", bold: true, children: "> " }),
|
|
1313
|
+
/* @__PURE__ */ jsx6(
|
|
1314
|
+
CjkTextInput,
|
|
1315
|
+
{
|
|
1316
|
+
value,
|
|
1317
|
+
onChange: setValue,
|
|
1318
|
+
onSubmit: handleSubmit,
|
|
1319
|
+
onPaste: handlePaste,
|
|
1320
|
+
placeholder: "Type a message or /help",
|
|
1321
|
+
availableWidth
|
|
1322
|
+
}
|
|
1323
|
+
)
|
|
1324
|
+
] }) })
|
|
1261
1325
|
] });
|
|
1262
1326
|
}
|
|
1263
1327
|
|
|
@@ -1370,7 +1434,7 @@ function PermissionPrompt({ request }) {
|
|
|
1370
1434
|
|
|
1371
1435
|
// src/ui/StreamingIndicator.tsx
|
|
1372
1436
|
import { Box as Box8, Text as Text10 } from "ink";
|
|
1373
|
-
import { Fragment as
|
|
1437
|
+
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1374
1438
|
function getToolStyle(t) {
|
|
1375
1439
|
if (t.isRunning) return { color: "yellow", icon: "\u27F3", strikethrough: false };
|
|
1376
1440
|
if (t.result === "error") return { color: "red", icon: "\u2717", strikethrough: true };
|
|
@@ -1381,7 +1445,7 @@ function StreamingIndicator({ text, activeTools }) {
|
|
|
1381
1445
|
const hasTools = activeTools.length > 0;
|
|
1382
1446
|
const hasText = text.length > 0;
|
|
1383
1447
|
if (!hasTools && !hasText) {
|
|
1384
|
-
return /* @__PURE__ */ jsx9(
|
|
1448
|
+
return /* @__PURE__ */ jsx9(Fragment3, {});
|
|
1385
1449
|
}
|
|
1386
1450
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
1387
1451
|
hasTools && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
|
|
@@ -1862,10 +1926,90 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
1862
1926
|
);
|
|
1863
1927
|
}
|
|
1864
1928
|
|
|
1865
|
-
// src/ui/
|
|
1929
|
+
// src/ui/ListPicker.tsx
|
|
1930
|
+
import { useState as useState9, useRef as useRef7 } from "react";
|
|
1931
|
+
import { Box as Box11, Text as Text13, useInput as useInput7 } from "ink";
|
|
1866
1932
|
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1933
|
+
var DEFAULT_MAX_VISIBLE = 3;
|
|
1934
|
+
function ListPicker({
|
|
1935
|
+
items,
|
|
1936
|
+
renderItem,
|
|
1937
|
+
onSelect,
|
|
1938
|
+
onCancel,
|
|
1939
|
+
maxVisible = DEFAULT_MAX_VISIBLE
|
|
1940
|
+
}) {
|
|
1941
|
+
const [selectedIndex, setSelectedIndex] = useState9(0);
|
|
1942
|
+
const [scrollOffset, setScrollOffset] = useState9(0);
|
|
1943
|
+
const selectedRef = useRef7(0);
|
|
1944
|
+
const resolvedRef = useRef7(false);
|
|
1945
|
+
useInput7((_input, key) => {
|
|
1946
|
+
if (resolvedRef.current) return;
|
|
1947
|
+
if (key.escape) {
|
|
1948
|
+
resolvedRef.current = true;
|
|
1949
|
+
onCancel();
|
|
1950
|
+
return;
|
|
1951
|
+
}
|
|
1952
|
+
if (items.length === 0) return;
|
|
1953
|
+
if (key.upArrow) {
|
|
1954
|
+
const next = selectedRef.current > 0 ? selectedRef.current - 1 : selectedRef.current;
|
|
1955
|
+
selectedRef.current = next;
|
|
1956
|
+
setSelectedIndex(next);
|
|
1957
|
+
if (next < scrollOffset) {
|
|
1958
|
+
setScrollOffset(next);
|
|
1959
|
+
}
|
|
1960
|
+
} else if (key.downArrow) {
|
|
1961
|
+
const next = selectedRef.current < items.length - 1 ? selectedRef.current + 1 : selectedRef.current;
|
|
1962
|
+
selectedRef.current = next;
|
|
1963
|
+
setSelectedIndex(next);
|
|
1964
|
+
if (next >= scrollOffset + maxVisible) {
|
|
1965
|
+
setScrollOffset(next - maxVisible + 1);
|
|
1966
|
+
}
|
|
1967
|
+
} else if (key.return) {
|
|
1968
|
+
const item = items[selectedRef.current];
|
|
1969
|
+
if (item !== void 0) {
|
|
1970
|
+
resolvedRef.current = true;
|
|
1971
|
+
onSelect(item);
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
});
|
|
1975
|
+
if (items.length === 0) {
|
|
1976
|
+
return /* @__PURE__ */ jsx13(Box11, {});
|
|
1977
|
+
}
|
|
1978
|
+
const visibleItems = items.slice(scrollOffset, scrollOffset + maxVisible);
|
|
1979
|
+
const hasMore = scrollOffset + maxVisible < items.length;
|
|
1980
|
+
const hasLess = scrollOffset > 0;
|
|
1981
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
1982
|
+
hasLess && /* @__PURE__ */ jsxs11(Text13, { dimColor: true, children: [
|
|
1983
|
+
" \u2191 ",
|
|
1984
|
+
scrollOffset,
|
|
1985
|
+
" more above"
|
|
1986
|
+
] }),
|
|
1987
|
+
visibleItems.map((item, index) => /* @__PURE__ */ jsx13(Box11, { marginBottom: 1, children: renderItem(item, scrollOffset + index === selectedIndex) }, scrollOffset + index)),
|
|
1988
|
+
hasMore && /* @__PURE__ */ jsxs11(Text13, { dimColor: true, children: [
|
|
1989
|
+
" \u2193 ",
|
|
1990
|
+
items.length - scrollOffset - maxVisible,
|
|
1991
|
+
" more below"
|
|
1992
|
+
] })
|
|
1993
|
+
] });
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
// src/ui/App.tsx
|
|
1997
|
+
import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1867
1998
|
var EXIT_DELAY_MS = 500;
|
|
1999
|
+
var SESSION_ID_DISPLAY_LENGTH = 8;
|
|
1868
2000
|
function App(props) {
|
|
2001
|
+
const [activeSessionId, setActiveSessionId] = useState10(props.resumeSessionId);
|
|
2002
|
+
return /* @__PURE__ */ jsx14(
|
|
2003
|
+
AppInner,
|
|
2004
|
+
{
|
|
2005
|
+
...props,
|
|
2006
|
+
resumeSessionId: activeSessionId,
|
|
2007
|
+
onSessionSwitch: (sessionId) => setActiveSessionId(sessionId)
|
|
2008
|
+
},
|
|
2009
|
+
activeSessionId ?? "__new__"
|
|
2010
|
+
);
|
|
2011
|
+
}
|
|
2012
|
+
function AppInner(props) {
|
|
1869
2013
|
const { exit } = useApp();
|
|
1870
2014
|
const cwd = props.cwd;
|
|
1871
2015
|
const {
|
|
@@ -1887,12 +2031,28 @@ function App(props) {
|
|
|
1887
2031
|
cwd,
|
|
1888
2032
|
provider: props.provider,
|
|
1889
2033
|
permissionMode: props.permissionMode,
|
|
1890
|
-
maxTurns: props.maxTurns
|
|
2034
|
+
maxTurns: props.maxTurns,
|
|
2035
|
+
sessionStore: props.sessionStore,
|
|
2036
|
+
resumeSessionId: props.resumeSessionId,
|
|
2037
|
+
forkSession: props.forkSession,
|
|
2038
|
+
sessionName: props.sessionName
|
|
1891
2039
|
});
|
|
1892
2040
|
const pluginCallbacks = usePluginCallbacks(cwd);
|
|
1893
|
-
const [pendingModelId, setPendingModelId] =
|
|
1894
|
-
const pendingModelChangeRef =
|
|
1895
|
-
const [showPluginTUI, setShowPluginTUI] =
|
|
2041
|
+
const [pendingModelId, setPendingModelId] = useState10(null);
|
|
2042
|
+
const pendingModelChangeRef = useRef8(null);
|
|
2043
|
+
const [showPluginTUI, setShowPluginTUI] = useState10(false);
|
|
2044
|
+
const [showSessionPicker, setShowSessionPicker] = useState10(
|
|
2045
|
+
props.resumeSessionId === "__picker__"
|
|
2046
|
+
);
|
|
2047
|
+
const [sessionName, setSessionName] = useState10(props.sessionName);
|
|
2048
|
+
useEffect4(() => {
|
|
2049
|
+
const name = interactiveSession?.getName?.();
|
|
2050
|
+
if (name && !sessionName) setSessionName(name);
|
|
2051
|
+
}, [interactiveSession, sessionName]);
|
|
2052
|
+
useEffect4(() => {
|
|
2053
|
+
const title = sessionName ? `Robota \u2014 ${sessionName}` : "Robota";
|
|
2054
|
+
process.stdout.write(`\x1B]0;${title}\x07`);
|
|
2055
|
+
}, [sessionName]);
|
|
1896
2056
|
const handleSubmit = async (input) => {
|
|
1897
2057
|
await baseHandleSubmit(input);
|
|
1898
2058
|
const sideEffects = interactiveSession;
|
|
@@ -1937,14 +2097,26 @@ function App(props) {
|
|
|
1937
2097
|
setShowPluginTUI(true);
|
|
1938
2098
|
return;
|
|
1939
2099
|
}
|
|
2100
|
+
if (sideEffects._triggerResumePicker) {
|
|
2101
|
+
delete sideEffects._triggerResumePicker;
|
|
2102
|
+
setShowSessionPicker(true);
|
|
2103
|
+
return;
|
|
2104
|
+
}
|
|
2105
|
+
if (sideEffects._sessionName) {
|
|
2106
|
+
const name = sideEffects._sessionName;
|
|
2107
|
+
delete sideEffects._sessionName;
|
|
2108
|
+
interactiveSession.setName(name);
|
|
2109
|
+
setSessionName(name);
|
|
2110
|
+
return;
|
|
2111
|
+
}
|
|
1940
2112
|
};
|
|
1941
|
-
|
|
2113
|
+
useInput8(
|
|
1942
2114
|
(_input, key) => {
|
|
1943
2115
|
if (key.escape && isThinking) {
|
|
1944
2116
|
handleAbort();
|
|
1945
2117
|
}
|
|
1946
2118
|
},
|
|
1947
|
-
{ isActive: !permissionRequest && !showPluginTUI }
|
|
2119
|
+
{ isActive: !permissionRequest && !showPluginTUI && !showSessionPicker }
|
|
1948
2120
|
);
|
|
1949
2121
|
let permissionMode = props.permissionMode ?? "default";
|
|
1950
2122
|
let sessionId = "";
|
|
@@ -1954,26 +2126,26 @@ function App(props) {
|
|
|
1954
2126
|
sessionId = session.getSessionId();
|
|
1955
2127
|
} catch {
|
|
1956
2128
|
}
|
|
1957
|
-
return /* @__PURE__ */
|
|
1958
|
-
/* @__PURE__ */
|
|
1959
|
-
/* @__PURE__ */
|
|
2129
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
2130
|
+
/* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
2131
|
+
/* @__PURE__ */ jsx14(Text14, { color: "cyan", bold: true, children: `
|
|
1960
2132
|
____ ___ ____ ___ _____ _
|
|
1961
2133
|
| _ \\ / _ \\| __ ) / _ \\_ _|/ \\
|
|
1962
2134
|
| |_) | | | | _ \\| | | || | / _ \\
|
|
1963
2135
|
| _ <| |_| | |_) | |_| || |/ ___ \\
|
|
1964
2136
|
|_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
|
|
1965
2137
|
` }),
|
|
1966
|
-
/* @__PURE__ */
|
|
2138
|
+
/* @__PURE__ */ jsxs12(Text14, { dimColor: true, children: [
|
|
1967
2139
|
" v",
|
|
1968
2140
|
props.version ?? "0.0.0"
|
|
1969
2141
|
] })
|
|
1970
2142
|
] }),
|
|
1971
|
-
/* @__PURE__ */
|
|
1972
|
-
/* @__PURE__ */
|
|
1973
|
-
(isThinking || activeTools.length > 0) && /* @__PURE__ */
|
|
2143
|
+
/* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
|
|
2144
|
+
/* @__PURE__ */ jsx14(MessageList, { history }),
|
|
2145
|
+
(isThinking || activeTools.length > 0) && /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx14(StreamingIndicator, { text: streamingText, activeTools }) })
|
|
1974
2146
|
] }),
|
|
1975
|
-
permissionRequest && /* @__PURE__ */
|
|
1976
|
-
pendingModelId && /* @__PURE__ */
|
|
2147
|
+
permissionRequest && /* @__PURE__ */ jsx14(PermissionPrompt, { request: permissionRequest }),
|
|
2148
|
+
pendingModelId && /* @__PURE__ */ jsx14(
|
|
1977
2149
|
ConfirmPrompt,
|
|
1978
2150
|
{
|
|
1979
2151
|
message: `Change model to ${getModelName(pendingModelId)}? This will restart the session.`,
|
|
@@ -2007,7 +2179,7 @@ function App(props) {
|
|
|
2007
2179
|
}
|
|
2008
2180
|
}
|
|
2009
2181
|
),
|
|
2010
|
-
showPluginTUI && /* @__PURE__ */
|
|
2182
|
+
showPluginTUI && /* @__PURE__ */ jsx14(
|
|
2011
2183
|
PluginTUI,
|
|
2012
2184
|
{
|
|
2013
2185
|
callbacks: pluginCallbacks,
|
|
@@ -2015,7 +2187,52 @@ function App(props) {
|
|
|
2015
2187
|
addMessage: (msg) => addEntry(messageToHistoryEntry2(createSystemMessage2(msg.content)))
|
|
2016
2188
|
}
|
|
2017
2189
|
),
|
|
2018
|
-
/* @__PURE__ */
|
|
2190
|
+
showSessionPicker && /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
2191
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
|
|
2192
|
+
/* @__PURE__ */ jsx14(
|
|
2193
|
+
ListPicker,
|
|
2194
|
+
{
|
|
2195
|
+
items: (props.sessionStore?.list() ?? []).filter((s) => s.cwd === props.cwd),
|
|
2196
|
+
renderItem: (session, isSelected) => {
|
|
2197
|
+
const lastMsg = session.messages.slice().reverse().find((m) => {
|
|
2198
|
+
const msg = m;
|
|
2199
|
+
return msg.role === "assistant" && msg.content;
|
|
2200
|
+
});
|
|
2201
|
+
const rawPreview = lastMsg?.content?.replace(/[\n\r]+/g, " ").trim() ?? "";
|
|
2202
|
+
const preview = rawPreview ? rawPreview.slice(0, 60) + (rawPreview.length > 60 ? "..." : "") : "";
|
|
2203
|
+
return /* @__PURE__ */ jsxs12(Text14, { children: [
|
|
2204
|
+
isSelected ? "> " : " ",
|
|
2205
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
|
|
2206
|
+
" ",
|
|
2207
|
+
/* @__PURE__ */ jsx14(Text14, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
|
|
2208
|
+
month: "short",
|
|
2209
|
+
day: "numeric",
|
|
2210
|
+
hour: "2-digit",
|
|
2211
|
+
minute: "2-digit"
|
|
2212
|
+
}) }),
|
|
2213
|
+
" ",
|
|
2214
|
+
/* @__PURE__ */ jsxs12(Text14, { dimColor: true, children: [
|
|
2215
|
+
"msgs: ",
|
|
2216
|
+
session.messages.length
|
|
2217
|
+
] }),
|
|
2218
|
+
preview ? /* @__PURE__ */ jsxs12(Fragment4, { children: [
|
|
2219
|
+
"\n ",
|
|
2220
|
+
/* @__PURE__ */ jsx14(Text14, { color: "gray", children: preview })
|
|
2221
|
+
] }) : null
|
|
2222
|
+
] });
|
|
2223
|
+
},
|
|
2224
|
+
onSelect: (session) => {
|
|
2225
|
+
setShowSessionPicker(false);
|
|
2226
|
+
props.onSessionSwitch(session.id);
|
|
2227
|
+
},
|
|
2228
|
+
onCancel: () => {
|
|
2229
|
+
setShowSessionPicker(false);
|
|
2230
|
+
addEntry(messageToHistoryEntry2(createSystemMessage2("Session resume cancelled.")));
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
)
|
|
2234
|
+
] }),
|
|
2235
|
+
/* @__PURE__ */ jsx14(
|
|
2019
2236
|
StatusBar,
|
|
2020
2237
|
{
|
|
2021
2238
|
permissionMode,
|
|
@@ -2025,26 +2242,28 @@ function App(props) {
|
|
|
2025
2242
|
isThinking,
|
|
2026
2243
|
contextPercentage: contextState.percentage,
|
|
2027
2244
|
contextUsedTokens: contextState.usedTokens,
|
|
2028
|
-
contextMaxTokens: contextState.maxTokens
|
|
2245
|
+
contextMaxTokens: contextState.maxTokens,
|
|
2246
|
+
sessionName
|
|
2029
2247
|
}
|
|
2030
2248
|
),
|
|
2031
|
-
/* @__PURE__ */
|
|
2249
|
+
/* @__PURE__ */ jsx14(
|
|
2032
2250
|
InputArea,
|
|
2033
2251
|
{
|
|
2034
2252
|
onSubmit: handleSubmit,
|
|
2035
2253
|
onCancelQueue: handleCancelQueue,
|
|
2036
|
-
isDisabled: !!permissionRequest || showPluginTUI || isThinking && !!pendingPrompt,
|
|
2254
|
+
isDisabled: !!permissionRequest || showPluginTUI || showSessionPicker || isThinking && !!pendingPrompt,
|
|
2037
2255
|
isAborting,
|
|
2038
2256
|
pendingPrompt,
|
|
2039
|
-
registry
|
|
2257
|
+
registry,
|
|
2258
|
+
sessionName
|
|
2040
2259
|
}
|
|
2041
2260
|
),
|
|
2042
|
-
/* @__PURE__ */
|
|
2261
|
+
/* @__PURE__ */ jsx14(Text14, { children: " " })
|
|
2043
2262
|
] });
|
|
2044
2263
|
}
|
|
2045
2264
|
|
|
2046
2265
|
// src/ui/render.tsx
|
|
2047
|
-
import { jsx as
|
|
2266
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
2048
2267
|
function renderApp(options) {
|
|
2049
2268
|
process.on("unhandledRejection", (reason) => {
|
|
2050
2269
|
process.stderr.write(`
|
|
@@ -2058,7 +2277,7 @@ function renderApp(options) {
|
|
|
2058
2277
|
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
2059
2278
|
process.stdout.write("\x1B[?2004h");
|
|
2060
2279
|
}
|
|
2061
|
-
const instance = render(/* @__PURE__ */
|
|
2280
|
+
const instance = render(/* @__PURE__ */ jsx15(App, { ...options }), {
|
|
2062
2281
|
exitOnCtrlC: true
|
|
2063
2282
|
});
|
|
2064
2283
|
instance.waitUntilExit().then(() => {
|
|
@@ -2227,9 +2446,38 @@ async function startCli() {
|
|
|
2227
2446
|
const providerSettings = readProviderSettings(cwd);
|
|
2228
2447
|
const modelId = args.model ?? providerSettings.model;
|
|
2229
2448
|
const provider = createProviderFromSettings(cwd, args.model);
|
|
2449
|
+
const sessionStore = new SessionStore();
|
|
2450
|
+
let resumeSessionId;
|
|
2451
|
+
if (args.continueMode) {
|
|
2452
|
+
const sessions = sessionStore.list().filter((s) => s.cwd === cwd);
|
|
2453
|
+
if (sessions.length > 0) {
|
|
2454
|
+
resumeSessionId = sessions[0].id;
|
|
2455
|
+
}
|
|
2456
|
+
} else if (args.resumeId !== void 0) {
|
|
2457
|
+
if (args.resumeId === "") {
|
|
2458
|
+
resumeSessionId = "__picker__";
|
|
2459
|
+
} else {
|
|
2460
|
+
const sessions = sessionStore.list();
|
|
2461
|
+
const match = sessions.find((s) => s.id === args.resumeId || s.name === args.resumeId);
|
|
2462
|
+
if (match) {
|
|
2463
|
+
resumeSessionId = match.id;
|
|
2464
|
+
} else {
|
|
2465
|
+
process.stderr.write(`Session not found: ${args.resumeId}
|
|
2466
|
+
`);
|
|
2467
|
+
process.exit(1);
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2230
2471
|
if (args.printMode) {
|
|
2231
|
-
|
|
2232
|
-
if (prompt
|
|
2472
|
+
let prompt = args.positional.join(" ").trim();
|
|
2473
|
+
if (!prompt && !process.stdin.isTTY) {
|
|
2474
|
+
const chunks = [];
|
|
2475
|
+
for await (const chunk of process.stdin) {
|
|
2476
|
+
chunks.push(chunk);
|
|
2477
|
+
}
|
|
2478
|
+
prompt = Buffer.concat(chunks).toString("utf-8").trim();
|
|
2479
|
+
}
|
|
2480
|
+
if (!prompt) {
|
|
2233
2481
|
process.stderr.write("Print mode (-p) requires a prompt argument.\n");
|
|
2234
2482
|
process.exit(1);
|
|
2235
2483
|
}
|
|
@@ -2237,21 +2485,17 @@ async function startCli() {
|
|
|
2237
2485
|
cwd,
|
|
2238
2486
|
provider,
|
|
2239
2487
|
permissionMode: args.permissionMode ?? "bypassPermissions",
|
|
2240
|
-
maxTurns: args.maxTurns
|
|
2488
|
+
maxTurns: args.maxTurns,
|
|
2489
|
+
sessionStore,
|
|
2490
|
+
sessionName: args.sessionName
|
|
2241
2491
|
});
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
resolve();
|
|
2246
|
-
});
|
|
2247
|
-
session.on("interrupted", (result) => {
|
|
2248
|
-
if (result.response) process.stdout.write(result.response + "\n");
|
|
2249
|
-
resolve();
|
|
2250
|
-
});
|
|
2251
|
-
session.on("error", (err) => reject(err));
|
|
2252
|
-
session.submit(prompt).catch(reject);
|
|
2492
|
+
const transport = createHeadlessTransport({
|
|
2493
|
+
outputFormat: args.outputFormat ?? "text",
|
|
2494
|
+
prompt
|
|
2253
2495
|
});
|
|
2254
|
-
|
|
2496
|
+
session.attachTransport(transport);
|
|
2497
|
+
await transport.start();
|
|
2498
|
+
process.exit(transport.getExitCode());
|
|
2255
2499
|
}
|
|
2256
2500
|
renderApp({
|
|
2257
2501
|
cwd,
|
|
@@ -2260,7 +2504,11 @@ async function startCli() {
|
|
|
2260
2504
|
language: args.language,
|
|
2261
2505
|
permissionMode: args.permissionMode,
|
|
2262
2506
|
maxTurns: args.maxTurns,
|
|
2263
|
-
version: readVersion()
|
|
2507
|
+
version: readVersion(),
|
|
2508
|
+
sessionStore,
|
|
2509
|
+
resumeSessionId,
|
|
2510
|
+
forkSession: args.forkSession,
|
|
2511
|
+
sessionName: args.sessionName
|
|
2264
2512
|
});
|
|
2265
2513
|
}
|
|
2266
2514
|
|