@robota-sdk/agent-cli 3.0.0-beta.56 → 3.0.0-beta.58
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 +46 -13
- package/dist/node/bin.js +2 -2
- package/dist/node/{chunk-2JAZDNYT.js → chunk-4ZX5RLIX.js} +45 -15
- package/dist/node/{chunk-H3NRW5FW.js → chunk-B522YHTK.js} +1839 -526
- package/dist/node/index.cjs +1922 -585
- package/dist/node/index.js +2 -2
- package/dist/node/subagents/child-process-subagent-worker.js +1 -1
- package/package.json +14 -11
|
@@ -3,10 +3,11 @@ import {
|
|
|
3
3
|
createProviderFromSettings,
|
|
4
4
|
findProviderDefinition,
|
|
5
5
|
formatSupportedProviderTypes,
|
|
6
|
+
hasUsableSecretReference,
|
|
6
7
|
isSubagentWorkerChildMessage,
|
|
7
8
|
readMergedProviderSettings,
|
|
8
9
|
readProviderSettings
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-4ZX5RLIX.js";
|
|
10
11
|
|
|
11
12
|
// src/background/managed-shell-process-runner.ts
|
|
12
13
|
import { spawn } from "child_process";
|
|
@@ -48,7 +49,7 @@ function resolveShell(request) {
|
|
|
48
49
|
return { command: request.shell ?? "sh", args: ["-c", request.command] };
|
|
49
50
|
}
|
|
50
51
|
function sendInput(child, input) {
|
|
51
|
-
return new Promise((
|
|
52
|
+
return new Promise((resolve2, reject) => {
|
|
52
53
|
const onError = (error) => {
|
|
53
54
|
child.stdin.off("error", onError);
|
|
54
55
|
reject(error);
|
|
@@ -56,7 +57,7 @@ function sendInput(child, input) {
|
|
|
56
57
|
child.stdin.once("error", onError);
|
|
57
58
|
child.stdin.end(input, () => {
|
|
58
59
|
child.stdin.off("error", onError);
|
|
59
|
-
|
|
60
|
+
resolve2();
|
|
60
61
|
});
|
|
61
62
|
});
|
|
62
63
|
}
|
|
@@ -91,7 +92,7 @@ function startProcessTask(taskId, request, killGraceMs) {
|
|
|
91
92
|
}
|
|
92
93
|
function createProcessResult(runtime) {
|
|
93
94
|
let settled = false;
|
|
94
|
-
return new Promise((
|
|
95
|
+
return new Promise((resolve2, reject) => {
|
|
95
96
|
const timeoutTimer = runtime.request.timeoutMs ? setTimeout(() => {
|
|
96
97
|
appendLog(runtime.logs, "system", `timed out after ${runtime.request.timeoutMs}ms`);
|
|
97
98
|
runtime.child.kill("SIGTERM");
|
|
@@ -105,7 +106,7 @@ function createProcessResult(runtime) {
|
|
|
105
106
|
if (settled) return;
|
|
106
107
|
settled = true;
|
|
107
108
|
clearTimers();
|
|
108
|
-
|
|
109
|
+
resolve2({
|
|
109
110
|
taskId: runtime.taskId,
|
|
110
111
|
kind: "process",
|
|
111
112
|
output: runtime.capture.getOutput(),
|
|
@@ -292,7 +293,7 @@ function extractFirstArg(toolArgs) {
|
|
|
292
293
|
return typeof firstValue === "object" ? JSON.stringify(firstValue) : String(firstValue);
|
|
293
294
|
}
|
|
294
295
|
function sendWorkerMessage(child, message) {
|
|
295
|
-
return new Promise((
|
|
296
|
+
return new Promise((resolve2, reject) => {
|
|
296
297
|
if (!child.connected) {
|
|
297
298
|
reject(new BackgroundTaskError3("crash", "Subagent worker IPC channel is closed"));
|
|
298
299
|
return;
|
|
@@ -302,7 +303,7 @@ function sendWorkerMessage(child, message) {
|
|
|
302
303
|
reject(error);
|
|
303
304
|
return;
|
|
304
305
|
}
|
|
305
|
-
|
|
306
|
+
resolve2();
|
|
306
307
|
});
|
|
307
308
|
});
|
|
308
309
|
}
|
|
@@ -319,14 +320,14 @@ async function cancelChildProcess(runtime, reason) {
|
|
|
319
320
|
|
|
320
321
|
// src/subagents/child-process-subagent-runner-result.ts
|
|
321
322
|
function createChildProcessSubagentResult(options) {
|
|
322
|
-
return new Promise((
|
|
323
|
-
new ChildProcessSubagentResultController(options,
|
|
323
|
+
return new Promise((resolve2, reject) => {
|
|
324
|
+
new ChildProcessSubagentResultController(options, resolve2, reject).start();
|
|
324
325
|
});
|
|
325
326
|
}
|
|
326
327
|
var ChildProcessSubagentResultController = class {
|
|
327
|
-
constructor(options,
|
|
328
|
+
constructor(options, resolve2, reject) {
|
|
328
329
|
this.options = options;
|
|
329
|
-
this.resolve =
|
|
330
|
+
this.resolve = resolve2;
|
|
330
331
|
this.reject = reject;
|
|
331
332
|
this.timeoutTimer = createTimeoutTimer(this.options.runtime, (error) => this.rejectOnce(error));
|
|
332
333
|
}
|
|
@@ -542,7 +543,8 @@ function createProviderProfile(providerConfig, deps, job) {
|
|
|
542
543
|
model: job.request.model ?? provider.model,
|
|
543
544
|
apiKey: provider.apiKey,
|
|
544
545
|
baseURL: provider.baseURL,
|
|
545
|
-
timeout: provider.timeout
|
|
546
|
+
timeout: provider.timeout,
|
|
547
|
+
options: provider.options
|
|
546
548
|
};
|
|
547
549
|
}
|
|
548
550
|
function resolveDefaultWorkerPath() {
|
|
@@ -589,8 +591,8 @@ function readTranscriptLog(jobId, transcriptPath, cursor) {
|
|
|
589
591
|
}
|
|
590
592
|
|
|
591
593
|
// src/cli.ts
|
|
592
|
-
import { readFileSync as
|
|
593
|
-
import { join as
|
|
594
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
595
|
+
import { join as join9, dirname as dirname5 } from "path";
|
|
594
596
|
import { fileURLToPath } from "url";
|
|
595
597
|
import { InteractiveSession as InteractiveSession2, projectPaths } from "@robota-sdk/agent-sdk";
|
|
596
598
|
import { SessionStore } from "@robota-sdk/agent-sessions";
|
|
@@ -647,7 +649,9 @@ function parseCliArgs() {
|
|
|
647
649
|
"api-key": { type: "string" },
|
|
648
650
|
"api-key-env": { type: "string" },
|
|
649
651
|
"set-current": { type: "boolean", default: false },
|
|
650
|
-
"settings-scope": { type: "string" }
|
|
652
|
+
"settings-scope": { type: "string" },
|
|
653
|
+
"check-update": { type: "boolean", default: false },
|
|
654
|
+
"disable-update-check": { type: "boolean", default: false }
|
|
651
655
|
}
|
|
652
656
|
});
|
|
653
657
|
return {
|
|
@@ -678,7 +682,9 @@ function parseCliArgs() {
|
|
|
678
682
|
apiKey: values["api-key"],
|
|
679
683
|
apiKeyEnv: values["api-key-env"],
|
|
680
684
|
setCurrent: values["set-current"] ?? false,
|
|
681
|
-
settingsScope: values["settings-scope"]
|
|
685
|
+
settingsScope: values["settings-scope"],
|
|
686
|
+
checkUpdate: values["check-update"] ?? false,
|
|
687
|
+
disableUpdateCheck: values["disable-update-check"] ?? false
|
|
682
688
|
};
|
|
683
689
|
}
|
|
684
690
|
|
|
@@ -766,7 +772,7 @@ function isUsableProviderProfile(type, profile, providerDefinitions) {
|
|
|
766
772
|
if (!profile) {
|
|
767
773
|
return false;
|
|
768
774
|
}
|
|
769
|
-
if (profile.apiKey) {
|
|
775
|
+
if (hasUsableSecretReference(profile.apiKey)) {
|
|
770
776
|
return true;
|
|
771
777
|
}
|
|
772
778
|
if (!type) {
|
|
@@ -776,7 +782,7 @@ function isUsableProviderProfile(type, profile, providerDefinitions) {
|
|
|
776
782
|
if (definition === void 0) {
|
|
777
783
|
return false;
|
|
778
784
|
}
|
|
779
|
-
return definition.requiresApiKey !== true || definition.defaults?.apiKey
|
|
785
|
+
return definition.requiresApiKey !== true || hasUsableSecretReference(definition.defaults?.apiKey);
|
|
780
786
|
}
|
|
781
787
|
|
|
782
788
|
// src/utils/provider-settings.ts
|
|
@@ -806,7 +812,7 @@ function validateProviderProfile(profileName, profile, options = {}) {
|
|
|
806
812
|
throw new Error(`Provider profile "${profileName}" is missing model`);
|
|
807
813
|
}
|
|
808
814
|
const definition = findProviderDefinition(options.providerDefinitions ?? [], profile.type);
|
|
809
|
-
if (definition?.requiresApiKey === true && !profile.apiKey
|
|
815
|
+
if (definition?.requiresApiKey === true && !hasUsableSecretReference(profile.apiKey ?? definition.defaults?.apiKey)) {
|
|
810
816
|
throw new Error(`Provider profile "${profileName}" is missing apiKey`);
|
|
811
817
|
}
|
|
812
818
|
}
|
|
@@ -873,6 +879,40 @@ function createProviderSetupFlow(type, providerDefinitions) {
|
|
|
873
879
|
values: {}
|
|
874
880
|
};
|
|
875
881
|
}
|
|
882
|
+
function formatProviderSetupSelectionPrompt(providerDefinitions) {
|
|
883
|
+
if (providerDefinitions.length === 0) {
|
|
884
|
+
return " No providers are available.";
|
|
885
|
+
}
|
|
886
|
+
const lines = [
|
|
887
|
+
" Select provider:",
|
|
888
|
+
...providerDefinitions.map(
|
|
889
|
+
(definition, index) => ` ${index + 1}. ${formatProviderSetupChoiceLabel(definition)}`
|
|
890
|
+
),
|
|
891
|
+
` Provider [1-${providerDefinitions.length}] (default: 1): `
|
|
892
|
+
];
|
|
893
|
+
return lines.join("\n");
|
|
894
|
+
}
|
|
895
|
+
function resolveProviderSetupSelection(rawValue, providerDefinitions) {
|
|
896
|
+
const value = rawValue.trim();
|
|
897
|
+
const selectedValue = value.length > 0 ? value : "1";
|
|
898
|
+
const index = parseProviderSelectionIndex(selectedValue);
|
|
899
|
+
if (index !== void 0) {
|
|
900
|
+
const definition2 = providerDefinitions[index];
|
|
901
|
+
if (definition2 !== void 0) {
|
|
902
|
+
return definition2.type;
|
|
903
|
+
}
|
|
904
|
+
throw new Error(
|
|
905
|
+
`Provider selection ${selectedValue} is out of range. Currently supported: ${formatSupportedProviderTypes(providerDefinitions)}`
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
const definition = findProviderDefinition(providerDefinitions, selectedValue);
|
|
909
|
+
if (definition === void 0) {
|
|
910
|
+
throw new Error(
|
|
911
|
+
`Unknown provider: ${selectedValue}. Currently supported: ${formatSupportedProviderTypes(providerDefinitions)}`
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
return definition.type;
|
|
915
|
+
}
|
|
876
916
|
function getProviderSetupStep(state) {
|
|
877
917
|
const step = state.steps[state.stepIndex];
|
|
878
918
|
if (step === void 0) {
|
|
@@ -918,6 +958,16 @@ function formatProviderSetupPromptLabel(step) {
|
|
|
918
958
|
const suffix = step.defaultValue !== void 0 ? ` (default: ${step.defaultValue})` : "";
|
|
919
959
|
return ` ${step.title}${suffix}: `;
|
|
920
960
|
}
|
|
961
|
+
function formatProviderSetupChoiceLabel(definition) {
|
|
962
|
+
const label = definition.displayName !== void 0 ? `${definition.displayName} (${definition.type})` : definition.type;
|
|
963
|
+
return definition.description !== void 0 ? `${label} - ${definition.description}` : label;
|
|
964
|
+
}
|
|
965
|
+
function parseProviderSelectionIndex(value) {
|
|
966
|
+
if (!/^\d+$/.test(value)) {
|
|
967
|
+
return void 0;
|
|
968
|
+
}
|
|
969
|
+
return Number(value) - 1;
|
|
970
|
+
}
|
|
921
971
|
function validateProviderSetupValue(step, value) {
|
|
922
972
|
if (step.required === true && value.length === 0) {
|
|
923
973
|
return "Required";
|
|
@@ -1010,15 +1060,13 @@ async function ensureConfig(cwd, args, promptInput2, providerDefinitions = DEFAU
|
|
|
1010
1060
|
return;
|
|
1011
1061
|
}
|
|
1012
1062
|
if (!isInteractiveTerminal()) {
|
|
1013
|
-
throw new Error(formatMissingProviderConfigMessage());
|
|
1063
|
+
throw new Error(formatMissingProviderConfigMessage(providerDefinitions));
|
|
1014
1064
|
}
|
|
1015
1065
|
await runInteractiveProviderSetup(cwd, args, promptInput2, providerDefinitions);
|
|
1016
1066
|
}
|
|
1017
1067
|
async function runInteractiveProviderSetup(cwd, args, promptInput2, providerDefinitions = DEFAULT_PROVIDER_DEFINITIONS) {
|
|
1018
|
-
const
|
|
1019
|
-
const
|
|
1020
|
-
const providerChoice = await promptInput2(` Provider (${supportedTypes}, default: ${defaultProviderType}): `) || defaultProviderType;
|
|
1021
|
-
const type = parseProviderSetupType(providerChoice);
|
|
1068
|
+
const providerChoice = await promptInput2(formatProviderSetupSelectionPrompt(providerDefinitions));
|
|
1069
|
+
const type = resolveProviderSetupSelection(providerChoice, providerDefinitions);
|
|
1022
1070
|
const settingsPath = getSettingsPathForScope(cwd, args.settingsScope);
|
|
1023
1071
|
const input = await runProviderSetupPromptFlow(type, promptInput2, providerDefinitions);
|
|
1024
1072
|
applyProviderConfiguration(settingsPath, input, {
|
|
@@ -1035,9 +1083,6 @@ async function runInteractiveProviderSetup(cwd, args, promptInput2, providerDefi
|
|
|
1035
1083
|
|
|
1036
1084
|
`);
|
|
1037
1085
|
}
|
|
1038
|
-
function parseProviderSetupType(value) {
|
|
1039
|
-
return value.trim();
|
|
1040
|
-
}
|
|
1041
1086
|
function buildSetupInputFromArgs(args) {
|
|
1042
1087
|
const type = args.providerType ?? args.configureProvider;
|
|
1043
1088
|
if (!args.configureProvider || !type) {
|
|
@@ -1066,14 +1111,25 @@ function getSettingsCheckPaths(cwd) {
|
|
|
1066
1111
|
function isInteractiveTerminal() {
|
|
1067
1112
|
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
1068
1113
|
}
|
|
1069
|
-
function formatMissingProviderConfigMessage() {
|
|
1114
|
+
function formatMissingProviderConfigMessage(providerDefinitions = DEFAULT_PROVIDER_DEFINITIONS) {
|
|
1070
1115
|
return [
|
|
1071
1116
|
"No provider configuration found.",
|
|
1072
1117
|
"Run `robota --configure` in an interactive terminal, or configure a provider:",
|
|
1073
|
-
|
|
1074
|
-
|
|
1118
|
+
`Supported providers: ${formatSupportedProviderTypes(providerDefinitions)}`,
|
|
1119
|
+
...providerDefinitions.map(formatConfigureProviderExample)
|
|
1075
1120
|
].join("\n");
|
|
1076
1121
|
}
|
|
1122
|
+
function formatConfigureProviderExample(definition) {
|
|
1123
|
+
const flags = [
|
|
1124
|
+
`robota --configure-provider ${definition.type}`,
|
|
1125
|
+
`--type ${definition.type}`,
|
|
1126
|
+
...definition.defaults?.baseURL !== void 0 ? ["--base-url <url>"] : [],
|
|
1127
|
+
"--model <model>",
|
|
1128
|
+
...definition.requiresApiKey === true ? ["--api-key-env <ENV_NAME>"] : [],
|
|
1129
|
+
"--set-current"
|
|
1130
|
+
];
|
|
1131
|
+
return ` ${flags.join(" ")}`;
|
|
1132
|
+
}
|
|
1077
1133
|
|
|
1078
1134
|
// src/cli.ts
|
|
1079
1135
|
import { createHeadlessTransport } from "@robota-sdk/agent-transport-headless";
|
|
@@ -1083,8 +1139,8 @@ import { render } from "ink";
|
|
|
1083
1139
|
|
|
1084
1140
|
// src/ui/App.tsx
|
|
1085
1141
|
import { useState as useState14, useEffect as useEffect4 } from "react";
|
|
1086
|
-
import { Box as
|
|
1087
|
-
import { getModelName as
|
|
1142
|
+
import { Box as Box18, Text as Text20, useApp as useApp2, useInput as useInput8 } from "ink";
|
|
1143
|
+
import { getModelName as getModelName3, createSystemMessage as createSystemMessage4, messageToHistoryEntry as messageToHistoryEntry4 } from "@robota-sdk/agent-core";
|
|
1088
1144
|
|
|
1089
1145
|
// src/ui/hooks/useInteractiveSession.ts
|
|
1090
1146
|
import { useState, useRef, useCallback as useCallback2, useEffect } from "react";
|
|
@@ -1120,7 +1176,9 @@ function toBackgroundTaskViewModel(state, partialText) {
|
|
|
1120
1176
|
errorPreview: trimBackgroundPreview(state.error?.message),
|
|
1121
1177
|
startedAt: state.startedAt,
|
|
1122
1178
|
lastActivityAt: state.lastActivityAt,
|
|
1123
|
-
timeoutReason: state.timeoutReason
|
|
1179
|
+
timeoutReason: state.timeoutReason,
|
|
1180
|
+
exitCode: state.result?.exitCode,
|
|
1181
|
+
signalCode: state.result?.signalCode
|
|
1124
1182
|
};
|
|
1125
1183
|
}
|
|
1126
1184
|
function getBackgroundTaskStatusLabel(state) {
|
|
@@ -1241,6 +1299,13 @@ var TuiStateManager = class {
|
|
|
1241
1299
|
this.activeTools = [];
|
|
1242
1300
|
this.notify();
|
|
1243
1301
|
};
|
|
1302
|
+
onContextUpdate = (state) => {
|
|
1303
|
+
this.setContextState({
|
|
1304
|
+
percentage: state.usedPercentage,
|
|
1305
|
+
usedTokens: state.usedTokens,
|
|
1306
|
+
maxTokens: state.maxTokens
|
|
1307
|
+
});
|
|
1308
|
+
};
|
|
1244
1309
|
onBackgroundTaskEvent = (event) => {
|
|
1245
1310
|
if ("task" in event) {
|
|
1246
1311
|
this.upsertBackgroundTask(event.task);
|
|
@@ -1343,7 +1408,10 @@ var TuiStateManager = class {
|
|
|
1343
1408
|
// src/ui/hooks/useSlashRouting.ts
|
|
1344
1409
|
import { useCallback } from "react";
|
|
1345
1410
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1346
|
-
import {
|
|
1411
|
+
import {
|
|
1412
|
+
createSystemMessage,
|
|
1413
|
+
messageToHistoryEntry
|
|
1414
|
+
} from "@robota-sdk/agent-core";
|
|
1347
1415
|
|
|
1348
1416
|
// src/utils/provider-command.ts
|
|
1349
1417
|
async function handleProviderCommand(cwd, args, deps = {}) {
|
|
@@ -1415,7 +1483,14 @@ function buildProviderSwitch(providers, profileName) {
|
|
|
1415
1483
|
};
|
|
1416
1484
|
}
|
|
1417
1485
|
function buildProviderSetup(type, providerDefinitions) {
|
|
1418
|
-
if (!type
|
|
1486
|
+
if (!type) {
|
|
1487
|
+
return {
|
|
1488
|
+
message: "Provider setup requested. Select a provider to continue.",
|
|
1489
|
+
success: true,
|
|
1490
|
+
data: { providerSetup: {} }
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
if (findProviderDefinition(providerDefinitions, type) === void 0) {
|
|
1419
1494
|
return {
|
|
1420
1495
|
message: `Usage: provider add <type>. Supported: ${formatSupportedProviderTypes(providerDefinitions)}`,
|
|
1421
1496
|
success: false
|
|
@@ -1503,8 +1578,8 @@ async function routeProviderCommand(cwd, args, interactiveSession, manager, prov
|
|
|
1503
1578
|
getEffects(interactiveSession)._pendingProviderProfile = providerSwitch.profile;
|
|
1504
1579
|
}
|
|
1505
1580
|
const providerSetup = result.data?.providerSetup;
|
|
1506
|
-
if (providerSetup
|
|
1507
|
-
getEffects(interactiveSession).
|
|
1581
|
+
if (providerSetup !== void 0) {
|
|
1582
|
+
getEffects(interactiveSession)._pendingProviderSetup = providerSetup;
|
|
1508
1583
|
}
|
|
1509
1584
|
}
|
|
1510
1585
|
function applySystemCommandResult(result, interactiveSession, manager) {
|
|
@@ -1531,6 +1606,11 @@ function applySystemCommandResult(result, interactiveSession, manager) {
|
|
|
1531
1606
|
effects._sessionName = data.name;
|
|
1532
1607
|
return;
|
|
1533
1608
|
}
|
|
1609
|
+
const statusLinePatch = data?.statuslinePatch;
|
|
1610
|
+
if (isStatusLineSettingsPatch(statusLinePatch)) {
|
|
1611
|
+
effects._statusLinePatch = statusLinePatch;
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1534
1614
|
const ctx = interactiveSession.getContextState();
|
|
1535
1615
|
manager.setContextState({
|
|
1536
1616
|
percentage: ctx.usedPercentage,
|
|
@@ -1575,6 +1655,13 @@ function routeTuiCommand(cmd, interactiveSession) {
|
|
|
1575
1655
|
function getEffects(interactiveSession) {
|
|
1576
1656
|
return interactiveSession;
|
|
1577
1657
|
}
|
|
1658
|
+
function isStatusLineSettingsPatch(value) {
|
|
1659
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date) {
|
|
1660
|
+
return false;
|
|
1661
|
+
}
|
|
1662
|
+
const candidate = value;
|
|
1663
|
+
return (candidate.enabled === void 0 || typeof candidate.enabled === "boolean") && (candidate.gitBranch === void 0 || typeof candidate.gitBranch === "boolean");
|
|
1664
|
+
}
|
|
1578
1665
|
|
|
1579
1666
|
// src/ui/hooks/useInteractiveSession.ts
|
|
1580
1667
|
function initializeSession(props, permissionHandler) {
|
|
@@ -1637,8 +1724,8 @@ function useInteractiveSession(props) {
|
|
|
1637
1724
|
});
|
|
1638
1725
|
}, []);
|
|
1639
1726
|
const permissionHandler = useCallback2(
|
|
1640
|
-
(toolName, toolArgs) => new Promise((
|
|
1641
|
-
permissionQueueRef.current.push({ toolName, toolArgs, resolve });
|
|
1727
|
+
(toolName, toolArgs) => new Promise((resolve2) => {
|
|
1728
|
+
permissionQueueRef.current.push({ toolName, toolArgs, resolve: resolve2 });
|
|
1642
1729
|
processNextPermission();
|
|
1643
1730
|
}),
|
|
1644
1731
|
[processNextPermission]
|
|
@@ -1663,6 +1750,7 @@ function useInteractiveSession(props) {
|
|
|
1663
1750
|
interactiveSession.on("complete", manager.onComplete);
|
|
1664
1751
|
interactiveSession.on("interrupted", manager.onInterrupted);
|
|
1665
1752
|
interactiveSession.on("error", manager.onError);
|
|
1753
|
+
interactiveSession.on("context_update", manager.onContextUpdate);
|
|
1666
1754
|
interactiveSession.on("background_task_event", manager.onBackgroundTaskEvent);
|
|
1667
1755
|
const initCheck = setInterval(() => {
|
|
1668
1756
|
try {
|
|
@@ -1689,6 +1777,7 @@ function useInteractiveSession(props) {
|
|
|
1689
1777
|
interactiveSession.off("complete", manager.onComplete);
|
|
1690
1778
|
interactiveSession.off("interrupted", manager.onInterrupted);
|
|
1691
1779
|
interactiveSession.off("error", manager.onError);
|
|
1780
|
+
interactiveSession.off("context_update", manager.onContextUpdate);
|
|
1692
1781
|
interactiveSession.off("background_task_event", manager.onBackgroundTaskEvent);
|
|
1693
1782
|
};
|
|
1694
1783
|
}, [interactiveSession, manager]);
|
|
@@ -1856,6 +1945,108 @@ function usePluginCallbacks(cwd) {
|
|
|
1856
1945
|
import { useState as useState2, useRef as useRef2, useCallback as useCallback3 } from "react";
|
|
1857
1946
|
import { useApp } from "ink";
|
|
1858
1947
|
import { createSystemMessage as createSystemMessage3, messageToHistoryEntry as messageToHistoryEntry3, getModelName } from "@robota-sdk/agent-core";
|
|
1948
|
+
|
|
1949
|
+
// src/utils/provider-setup-interaction.ts
|
|
1950
|
+
function startProviderSetupInteraction(providerDefinitions, type) {
|
|
1951
|
+
if (type === void 0) {
|
|
1952
|
+
const state2 = {
|
|
1953
|
+
mode: "select-provider",
|
|
1954
|
+
providerDefinitions
|
|
1955
|
+
};
|
|
1956
|
+
return { status: "prompt", state: state2, prompt: toProviderSelectionPrompt(providerDefinitions) };
|
|
1957
|
+
}
|
|
1958
|
+
const state = {
|
|
1959
|
+
mode: "setup-fields",
|
|
1960
|
+
providerDefinitions,
|
|
1961
|
+
flow: createProviderSetupFlow(type, providerDefinitions)
|
|
1962
|
+
};
|
|
1963
|
+
return { status: "prompt", state, prompt: toProviderSetupStepPrompt(state.flow) };
|
|
1964
|
+
}
|
|
1965
|
+
function submitProviderSetupInteractionValue(state, value) {
|
|
1966
|
+
if (state.mode === "select-provider") {
|
|
1967
|
+
const nextState2 = {
|
|
1968
|
+
mode: "setup-fields",
|
|
1969
|
+
providerDefinitions: state.providerDefinitions,
|
|
1970
|
+
flow: createProviderSetupFlow(value, state.providerDefinitions)
|
|
1971
|
+
};
|
|
1972
|
+
return {
|
|
1973
|
+
status: "prompt",
|
|
1974
|
+
state: nextState2,
|
|
1975
|
+
prompt: toProviderSetupStepPrompt(nextState2.flow)
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
const result = submitProviderSetupValue(state.flow, value);
|
|
1979
|
+
if (result.status === "complete") {
|
|
1980
|
+
return { status: "complete", input: result.input };
|
|
1981
|
+
}
|
|
1982
|
+
const nextState = {
|
|
1983
|
+
...state,
|
|
1984
|
+
flow: result.state
|
|
1985
|
+
};
|
|
1986
|
+
return { status: "prompt", state: nextState, prompt: toProviderSetupStepPrompt(result.state) };
|
|
1987
|
+
}
|
|
1988
|
+
function toProviderSelectionPrompt(providerDefinitions) {
|
|
1989
|
+
return {
|
|
1990
|
+
kind: "choice",
|
|
1991
|
+
title: "Select provider",
|
|
1992
|
+
options: providerDefinitions.map((definition) => ({
|
|
1993
|
+
value: definition.type,
|
|
1994
|
+
label: formatProviderSetupChoiceLabel(definition)
|
|
1995
|
+
})),
|
|
1996
|
+
maxVisible: 6
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1999
|
+
function toProviderSetupStepPrompt(flow) {
|
|
2000
|
+
const step = getProviderSetupStep(flow);
|
|
2001
|
+
return {
|
|
2002
|
+
kind: "text",
|
|
2003
|
+
title: step.title,
|
|
2004
|
+
...step.defaultValue !== void 0 ? { placeholder: step.defaultValue } : {},
|
|
2005
|
+
...step.defaultValue !== void 0 ? { allowEmpty: true } : {},
|
|
2006
|
+
...step.masked !== void 0 ? { masked: step.masked } : {},
|
|
2007
|
+
validate: (value) => validateProviderSetupValue(step, value)
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
// src/utils/statusline-settings.ts
|
|
2012
|
+
var DEFAULT_STATUS_LINE_SETTINGS = {
|
|
2013
|
+
enabled: true,
|
|
2014
|
+
gitBranch: true
|
|
2015
|
+
};
|
|
2016
|
+
function readStatusLineSettings(settings) {
|
|
2017
|
+
const raw = settings.statusline;
|
|
2018
|
+
if (!isRecord(raw)) {
|
|
2019
|
+
return { ...DEFAULT_STATUS_LINE_SETTINGS };
|
|
2020
|
+
}
|
|
2021
|
+
return {
|
|
2022
|
+
enabled: typeof raw.enabled === "boolean" ? raw.enabled : DEFAULT_STATUS_LINE_SETTINGS.enabled,
|
|
2023
|
+
gitBranch: typeof raw.gitBranch === "boolean" ? raw.gitBranch : DEFAULT_STATUS_LINE_SETTINGS.gitBranch
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
function applyStatusLineSettings(settingsPath, patch) {
|
|
2027
|
+
const settings = readSettings(settingsPath);
|
|
2028
|
+
const next = {
|
|
2029
|
+
...readStatusLineSettings(settings),
|
|
2030
|
+
...patch
|
|
2031
|
+
};
|
|
2032
|
+
settings.statusline = next;
|
|
2033
|
+
writeSettings(settingsPath, settings);
|
|
2034
|
+
return next;
|
|
2035
|
+
}
|
|
2036
|
+
function isRecord(value) {
|
|
2037
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
// src/ui/hooks/statusline-side-effect.ts
|
|
2041
|
+
function applyPendingStatusLinePatch(sideEffects, setStatusLineSettings) {
|
|
2042
|
+
if (!sideEffects._statusLinePatch) return false;
|
|
2043
|
+
const patch = sideEffects._statusLinePatch;
|
|
2044
|
+
delete sideEffects._statusLinePatch;
|
|
2045
|
+
setStatusLineSettings(applyStatusLineSettings(getUserSettingsPath(), patch));
|
|
2046
|
+
return true;
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
// src/ui/hooks/useSideEffects.ts
|
|
1859
2050
|
var EXIT_DELAY_MS = 500;
|
|
1860
2051
|
function useSideEffects({
|
|
1861
2052
|
cwd,
|
|
@@ -1863,6 +2054,7 @@ function useSideEffects({
|
|
|
1863
2054
|
addEntry,
|
|
1864
2055
|
baseHandleSubmit,
|
|
1865
2056
|
setSessionName,
|
|
2057
|
+
setStatusLineSettings,
|
|
1866
2058
|
providerDefinitions
|
|
1867
2059
|
}) {
|
|
1868
2060
|
const { exit } = useApp();
|
|
@@ -1870,7 +2062,8 @@ function useSideEffects({
|
|
|
1870
2062
|
const pendingModelChangeRef = useRef2(null);
|
|
1871
2063
|
const [pendingProviderProfile, setPendingProviderProfile] = useState2(null);
|
|
1872
2064
|
const pendingProviderProfileRef = useRef2(null);
|
|
1873
|
-
const [
|
|
2065
|
+
const [pendingInteractionPrompt, setPendingInteractionPrompt] = useState2(null);
|
|
2066
|
+
const providerSetupInteractionRef = useRef2(null);
|
|
1874
2067
|
const [showPluginTUI, setShowPluginTUI] = useState2(false);
|
|
1875
2068
|
const [showSessionPicker, setShowSessionPicker] = useState2(false);
|
|
1876
2069
|
const requestShutdown = useCallback3(
|
|
@@ -1913,10 +2106,14 @@ function useSideEffects({
|
|
|
1913
2106
|
setPendingProviderProfile(profile);
|
|
1914
2107
|
return;
|
|
1915
2108
|
}
|
|
1916
|
-
if (sideEffects.
|
|
1917
|
-
const
|
|
1918
|
-
delete sideEffects.
|
|
1919
|
-
|
|
2109
|
+
if (sideEffects._pendingProviderSetup !== void 0) {
|
|
2110
|
+
const setup = sideEffects._pendingProviderSetup;
|
|
2111
|
+
delete sideEffects._pendingProviderSetup;
|
|
2112
|
+
const result = startProviderSetupInteraction(providerDefinitions, setup.type);
|
|
2113
|
+
if (result.status === "prompt") {
|
|
2114
|
+
providerSetupInteractionRef.current = result.state;
|
|
2115
|
+
setPendingInteractionPrompt(result.prompt);
|
|
2116
|
+
}
|
|
1920
2117
|
return;
|
|
1921
2118
|
}
|
|
1922
2119
|
if (sideEffects._resetRequested) {
|
|
@@ -1954,8 +2151,17 @@ function useSideEffects({
|
|
|
1954
2151
|
setSessionName(name);
|
|
1955
2152
|
return;
|
|
1956
2153
|
}
|
|
2154
|
+
if (applyPendingStatusLinePatch(sideEffects, setStatusLineSettings)) return;
|
|
1957
2155
|
},
|
|
1958
|
-
[
|
|
2156
|
+
[
|
|
2157
|
+
interactiveSession,
|
|
2158
|
+
baseHandleSubmit,
|
|
2159
|
+
addEntry,
|
|
2160
|
+
requestShutdown,
|
|
2161
|
+
setSessionName,
|
|
2162
|
+
setStatusLineSettings,
|
|
2163
|
+
providerDefinitions
|
|
2164
|
+
]
|
|
1959
2165
|
);
|
|
1960
2166
|
const handleModelConfirm = useCallback3(
|
|
1961
2167
|
(index) => {
|
|
@@ -2015,9 +2221,10 @@ function useSideEffects({
|
|
|
2015
2221
|
},
|
|
2016
2222
|
[cwd, addEntry, requestShutdown]
|
|
2017
2223
|
);
|
|
2018
|
-
const
|
|
2224
|
+
const completeProviderSetup = useCallback3(
|
|
2019
2225
|
(input) => {
|
|
2020
|
-
|
|
2226
|
+
providerSetupInteractionRef.current = null;
|
|
2227
|
+
setPendingInteractionPrompt(null);
|
|
2021
2228
|
try {
|
|
2022
2229
|
const settingsPath = getUserSettingsPath();
|
|
2023
2230
|
applyProviderConfiguration(settingsPath, input, { providerDefinitions });
|
|
@@ -2037,15 +2244,43 @@ function useSideEffects({
|
|
|
2037
2244
|
},
|
|
2038
2245
|
[addEntry, requestShutdown, providerDefinitions]
|
|
2039
2246
|
);
|
|
2040
|
-
const
|
|
2041
|
-
|
|
2247
|
+
const handleInteractionSubmit = useCallback3(
|
|
2248
|
+
(value) => {
|
|
2249
|
+
const state = providerSetupInteractionRef.current;
|
|
2250
|
+
if (state === null) {
|
|
2251
|
+
setPendingInteractionPrompt(null);
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
try {
|
|
2255
|
+
const result = submitProviderSetupInteractionValue(state, value);
|
|
2256
|
+
if (result.status === "complete") {
|
|
2257
|
+
completeProviderSetup(result.input);
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2260
|
+
providerSetupInteractionRef.current = result.state;
|
|
2261
|
+
setPendingInteractionPrompt(result.prompt);
|
|
2262
|
+
} catch (err) {
|
|
2263
|
+
providerSetupInteractionRef.current = null;
|
|
2264
|
+
setPendingInteractionPrompt(null);
|
|
2265
|
+
addEntry(
|
|
2266
|
+
messageToHistoryEntry3(
|
|
2267
|
+
createSystemMessage3(`Failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
2268
|
+
)
|
|
2269
|
+
);
|
|
2270
|
+
}
|
|
2271
|
+
},
|
|
2272
|
+
[addEntry, completeProviderSetup]
|
|
2273
|
+
);
|
|
2274
|
+
const handleInteractionCancel = useCallback3(() => {
|
|
2275
|
+
providerSetupInteractionRef.current = null;
|
|
2276
|
+
setPendingInteractionPrompt(null);
|
|
2042
2277
|
addEntry(messageToHistoryEntry3(createSystemMessage3("Provider setup cancelled.")));
|
|
2043
2278
|
}, [addEntry]);
|
|
2044
2279
|
return {
|
|
2045
2280
|
handleSubmit,
|
|
2046
2281
|
pendingModelId,
|
|
2047
2282
|
pendingProviderProfile,
|
|
2048
|
-
|
|
2283
|
+
pendingInteractionPrompt,
|
|
2049
2284
|
showPluginTUI,
|
|
2050
2285
|
showSessionPicker,
|
|
2051
2286
|
setPendingModelId,
|
|
@@ -2053,94 +2288,336 @@ function useSideEffects({
|
|
|
2053
2288
|
setShowSessionPicker,
|
|
2054
2289
|
handleModelConfirm,
|
|
2055
2290
|
handleProviderConfirm,
|
|
2056
|
-
|
|
2057
|
-
|
|
2291
|
+
handleInteractionSubmit,
|
|
2292
|
+
handleInteractionCancel
|
|
2058
2293
|
};
|
|
2059
2294
|
}
|
|
2060
2295
|
|
|
2296
|
+
// src/ui/hooks/useStatusLineSettings.ts
|
|
2297
|
+
import { useState as useState3 } from "react";
|
|
2298
|
+
function useStatusLineSettings() {
|
|
2299
|
+
return useState3(
|
|
2300
|
+
() => readStatusLineSettings(readSettings(getUserSettingsPath()))
|
|
2301
|
+
);
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2061
2304
|
// src/ui/MessageList.tsx
|
|
2062
2305
|
import React from "react";
|
|
2063
|
-
import { Box as
|
|
2306
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
2064
2307
|
import { isToolMessage, isAssistantMessage } from "@robota-sdk/agent-core";
|
|
2065
2308
|
|
|
2066
2309
|
// src/ui/render-markdown.ts
|
|
2067
2310
|
import { marked } from "marked";
|
|
2068
2311
|
import TerminalRenderer from "marked-terminal";
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2312
|
+
var ANSI_RED = "\x1B[31m";
|
|
2313
|
+
var ANSI_GREEN = "\x1B[32m";
|
|
2314
|
+
var ANSI_CYAN = "\x1B[36m";
|
|
2315
|
+
var ANSI_DIM = "\x1B[2m";
|
|
2316
|
+
var ANSI_RESET = "\x1B[0m";
|
|
2317
|
+
var CODE_BLOCK_INDENT = " ";
|
|
2318
|
+
var ZERO_COLOR = "0";
|
|
2319
|
+
var TerminalRendererConstructor = TerminalRenderer;
|
|
2320
|
+
function shouldUseColor(option) {
|
|
2321
|
+
if (option !== void 0) {
|
|
2322
|
+
return option;
|
|
2323
|
+
}
|
|
2324
|
+
if (process.env.NO_COLOR || process.env.FORCE_COLOR === ZERO_COLOR) {
|
|
2325
|
+
return false;
|
|
2326
|
+
}
|
|
2327
|
+
if (process.env.FORCE_COLOR) {
|
|
2328
|
+
return true;
|
|
2329
|
+
}
|
|
2330
|
+
return Boolean(process.stdout.isTTY);
|
|
2331
|
+
}
|
|
2332
|
+
function isDiffLanguage(language) {
|
|
2333
|
+
return language?.trim().toLowerCase() === "diff";
|
|
2334
|
+
}
|
|
2335
|
+
function colorizeDiffLine(line, color) {
|
|
2336
|
+
if (!color) {
|
|
2337
|
+
return line;
|
|
2338
|
+
}
|
|
2339
|
+
if (line.startsWith("+")) {
|
|
2340
|
+
return `${ANSI_GREEN}${line}${ANSI_RESET}`;
|
|
2341
|
+
}
|
|
2342
|
+
if (line.startsWith("-")) {
|
|
2343
|
+
return `${ANSI_RED}${line}${ANSI_RESET}`;
|
|
2344
|
+
}
|
|
2345
|
+
if (line.startsWith("@@")) {
|
|
2346
|
+
return `${ANSI_CYAN}${line}${ANSI_RESET}`;
|
|
2347
|
+
}
|
|
2348
|
+
if (line.startsWith("diff ") || line.startsWith("index ")) {
|
|
2349
|
+
return `${ANSI_DIM}${line}${ANSI_RESET}`;
|
|
2350
|
+
}
|
|
2351
|
+
return line;
|
|
2352
|
+
}
|
|
2353
|
+
function renderDiffCodeBlock(code, color) {
|
|
2354
|
+
const body = code.split("\n").map((line) => `${CODE_BLOCK_INDENT}${colorizeDiffLine(line, color)}`).join("\n");
|
|
2355
|
+
return `${body}
|
|
2356
|
+
|
|
2357
|
+
`;
|
|
2358
|
+
}
|
|
2359
|
+
function createTerminalRenderer(color) {
|
|
2360
|
+
const renderer = new TerminalRendererConstructor(void 0, { ignoreIllegals: true });
|
|
2361
|
+
const renderCode = renderer.code.bind(renderer);
|
|
2362
|
+
renderer.code = (code, language, escaped) => {
|
|
2363
|
+
if (isDiffLanguage(language)) {
|
|
2364
|
+
return renderDiffCodeBlock(code, color);
|
|
2365
|
+
}
|
|
2366
|
+
return renderCode(code, language, escaped);
|
|
2367
|
+
};
|
|
2368
|
+
return renderer;
|
|
2369
|
+
}
|
|
2370
|
+
function renderMarkdown(md, options = {}) {
|
|
2371
|
+
const result = marked.parse(md, {
|
|
2372
|
+
renderer: createTerminalRenderer(shouldUseColor(options.color))
|
|
2373
|
+
});
|
|
2074
2374
|
return typeof result === "string" ? result.trimEnd() : md;
|
|
2075
2375
|
}
|
|
2076
2376
|
|
|
2077
|
-
// src/ui/
|
|
2377
|
+
// src/ui/ToolDiffBlock.tsx
|
|
2078
2378
|
import { Box, Text } from "ink";
|
|
2079
|
-
|
|
2379
|
+
|
|
2380
|
+
// src/utils/tool-diff-summary.ts
|
|
2080
2381
|
var MAX_DIFF_LINES = 12;
|
|
2081
2382
|
var TRUNCATED_SHOW = 10;
|
|
2082
|
-
function
|
|
2083
|
-
const
|
|
2084
|
-
const
|
|
2085
|
-
const
|
|
2086
|
-
const
|
|
2087
|
-
|
|
2383
|
+
function buildToolDiffSummary(input) {
|
|
2384
|
+
const visibleLines = input.lines.length > MAX_DIFF_LINES ? selectVisibleDiffLines(input.lines) : input.lines;
|
|
2385
|
+
const lineNumberWidth = Math.max(...visibleLines.map((line) => line.lineNumber), 0).toString().length;
|
|
2386
|
+
const body = visibleLines.map((line) => formatDiffLine(line, lineNumberWidth));
|
|
2387
|
+
const truncated = input.lines.length > MAX_DIFF_LINES;
|
|
2388
|
+
return {
|
|
2389
|
+
file: input.file,
|
|
2390
|
+
markdown: ["```diff", ...body, "```"].join("\n"),
|
|
2391
|
+
truncated,
|
|
2392
|
+
remainingLineCount: truncated ? input.lines.length - visibleLines.length : 0
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2395
|
+
function formatDiffLine(line, lineNumberWidth) {
|
|
2396
|
+
if (line.type === "hunk") return line.text;
|
|
2397
|
+
const lineNumber = line.lineNumber.toString().padStart(lineNumberWidth, " ");
|
|
2398
|
+
if (line.type === "remove") return `- ${lineNumber} | ${line.text}`;
|
|
2399
|
+
if (line.type === "add") return `+ ${lineNumber} | ${line.text}`;
|
|
2400
|
+
return ` ${lineNumber} | ${line.text}`;
|
|
2401
|
+
}
|
|
2402
|
+
function selectVisibleDiffLines(lines) {
|
|
2403
|
+
const groups = groupByHunk(lines);
|
|
2404
|
+
const visible = [];
|
|
2405
|
+
for (const group of groups) {
|
|
2406
|
+
if (visible.length === 0 && group.length > TRUNCATED_SHOW) {
|
|
2407
|
+
return group.slice(0, TRUNCATED_SHOW);
|
|
2408
|
+
}
|
|
2409
|
+
if (visible.length + group.length > TRUNCATED_SHOW) break;
|
|
2410
|
+
visible.push(...group);
|
|
2411
|
+
}
|
|
2412
|
+
return visible.length > 0 ? visible : lines.slice(0, TRUNCATED_SHOW);
|
|
2413
|
+
}
|
|
2414
|
+
function groupByHunk(lines) {
|
|
2415
|
+
const groups = [];
|
|
2416
|
+
let current = [];
|
|
2417
|
+
for (const line of lines) {
|
|
2418
|
+
if (line.type === "hunk" && current.length > 0) {
|
|
2419
|
+
groups.push(current);
|
|
2420
|
+
current = [line];
|
|
2421
|
+
continue;
|
|
2422
|
+
}
|
|
2423
|
+
current.push(line);
|
|
2424
|
+
}
|
|
2425
|
+
if (current.length > 0) {
|
|
2426
|
+
groups.push(current);
|
|
2427
|
+
}
|
|
2428
|
+
return groups;
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
// src/ui/ToolDiffBlock.tsx
|
|
2432
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2433
|
+
function ToolDiffBlock({ file, lines }) {
|
|
2434
|
+
const summary = buildToolDiffSummary({ file, lines });
|
|
2088
2435
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 4, children: [
|
|
2089
|
-
file && /* @__PURE__ */ jsxs(Text, { color: "white", dimColor: true, children: [
|
|
2436
|
+
summary.file && /* @__PURE__ */ jsxs(Text, { color: "white", dimColor: true, children: [
|
|
2090
2437
|
"\u2502 ",
|
|
2091
|
-
file
|
|
2438
|
+
summary.file
|
|
2092
2439
|
] }),
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
if (line.type === "context") {
|
|
2096
|
-
return /* @__PURE__ */ jsxs(Text, { color: "white", dimColor: true, children: [
|
|
2097
|
-
"\u2502 ",
|
|
2098
|
-
lineNum,
|
|
2099
|
-
" ",
|
|
2100
|
-
line.text
|
|
2101
|
-
] }, i);
|
|
2102
|
-
}
|
|
2103
|
-
const prefix = line.type === "remove" ? "-" : "+";
|
|
2104
|
-
const bgColor = line.type === "remove" ? "#5c1a1a" : "#1a3d1a";
|
|
2105
|
-
const fgColor = line.type === "remove" ? "#ff9999" : "#99ff99";
|
|
2106
|
-
return /* @__PURE__ */ jsxs(Text, { color: fgColor, backgroundColor: bgColor, children: [
|
|
2107
|
-
"\u2502 ",
|
|
2108
|
-
lineNum,
|
|
2109
|
-
" ",
|
|
2110
|
-
prefix,
|
|
2111
|
-
" ",
|
|
2112
|
-
line.text
|
|
2113
|
-
] }, i);
|
|
2114
|
-
}),
|
|
2115
|
-
truncated && /* @__PURE__ */ jsxs(Text, { color: "white", dimColor: true, children: [
|
|
2440
|
+
/* @__PURE__ */ jsx(Text, { children: renderMarkdown(summary.markdown) }),
|
|
2441
|
+
summary.truncated && /* @__PURE__ */ jsxs(Text, { color: "white", dimColor: true, children: [
|
|
2116
2442
|
"\u2502 ... and ",
|
|
2117
|
-
|
|
2443
|
+
summary.remainingLineCount,
|
|
2118
2444
|
" more lines"
|
|
2119
2445
|
] })
|
|
2120
2446
|
] });
|
|
2121
2447
|
}
|
|
2122
2448
|
|
|
2449
|
+
// src/ui/UsageSummaryEntry.tsx
|
|
2450
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
2451
|
+
import { formatTokenCount } from "@robota-sdk/agent-core";
|
|
2452
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2453
|
+
var TOKEN_COMPACT_THRESHOLD = 1e3;
|
|
2454
|
+
function UsageSummaryEntry({ entry }) {
|
|
2455
|
+
const usage = entry.data;
|
|
2456
|
+
if (!usage) return /* @__PURE__ */ jsx2(Fragment, {});
|
|
2457
|
+
const prompt = usage.promptTokens !== void 0 ? formatUsageTokenCount(usage.promptTokens) : "?";
|
|
2458
|
+
const completion = usage.completionTokens !== void 0 ? formatUsageTokenCount(usage.completionTokens) : "?";
|
|
2459
|
+
const total = formatUsageTokenCount(usage.totalTokens);
|
|
2460
|
+
const context = `${Math.round(usage.contextUsedPercentage)}% (${formatTokenCount(
|
|
2461
|
+
usage.contextUsedTokens
|
|
2462
|
+
)}/${formatTokenCount(usage.contextMaxTokens)})`;
|
|
2463
|
+
const costLabel = usage.costStatus === "unknown" ? "cost unknown" : `cost ${usage.costStatus}`;
|
|
2464
|
+
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
2465
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "white", bold: true, children: [
|
|
2466
|
+
"Usage:",
|
|
2467
|
+
" "
|
|
2468
|
+
] }),
|
|
2469
|
+
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
2470
|
+
usage.kind,
|
|
2471
|
+
" ",
|
|
2472
|
+
total,
|
|
2473
|
+
" tokens (in ",
|
|
2474
|
+
prompt,
|
|
2475
|
+
" / out ",
|
|
2476
|
+
completion,
|
|
2477
|
+
") \xB7 Context ",
|
|
2478
|
+
context,
|
|
2479
|
+
" \xB7",
|
|
2480
|
+
" ",
|
|
2481
|
+
costLabel
|
|
2482
|
+
] })
|
|
2483
|
+
] }) });
|
|
2484
|
+
}
|
|
2485
|
+
function formatUsageTokenCount(tokens) {
|
|
2486
|
+
return tokens < TOKEN_COMPACT_THRESHOLD ? tokens.toLocaleString() : formatTokenCount(tokens);
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
// src/ui/command-output-summary.ts
|
|
2490
|
+
var MAX_PREVIEW_LINES = 4;
|
|
2491
|
+
var SUCCESS_EXIT_CODE2 = 0;
|
|
2492
|
+
var COMMAND_TOOL_NAMES = /* @__PURE__ */ new Set(["Bash", "BackgroundProcess"]);
|
|
2493
|
+
function formatCommandOutputSummary(tool) {
|
|
2494
|
+
if (!COMMAND_TOOL_NAMES.has(tool.toolName) || !tool.toolResultData) return void 0;
|
|
2495
|
+
const parsed = parseToolResultData(tool.toolResultData);
|
|
2496
|
+
const exitCode = getNumberValue(parsed, "exitCode");
|
|
2497
|
+
const successValue = getBooleanValue(parsed, "success");
|
|
2498
|
+
const output = buildOutputText(tool.toolResultData, parsed);
|
|
2499
|
+
const lines = trimTrailingBlankLines(splitOutputLines(output));
|
|
2500
|
+
const previewLines = lines.slice(0, MAX_PREVIEW_LINES);
|
|
2501
|
+
const omittedLineCount = Math.max(0, lines.length - previewLines.length);
|
|
2502
|
+
const isFailed = tool.result === "error" || successValue === false || exitCode !== void 0 && exitCode !== SUCCESS_EXIT_CODE2;
|
|
2503
|
+
return {
|
|
2504
|
+
status: isFailed ? "error" : "success",
|
|
2505
|
+
statusLabel: formatStatusLabel(isFailed, exitCode),
|
|
2506
|
+
previewLines,
|
|
2507
|
+
omittedLineCount,
|
|
2508
|
+
transcriptHint: omittedLineCount > 0 ? `... +${omittedLineCount} lines (full output in session transcript)` : void 0
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2511
|
+
function parseToolResultData(value) {
|
|
2512
|
+
try {
|
|
2513
|
+
return JSON.parse(value);
|
|
2514
|
+
} catch {
|
|
2515
|
+
return value;
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
function buildOutputText(raw, parsed) {
|
|
2519
|
+
if (!isUniversalObject(parsed)) return raw;
|
|
2520
|
+
const output = getStringValue(parsed, "output");
|
|
2521
|
+
if (output !== void 0) return output;
|
|
2522
|
+
const stdout = getStringValue(parsed, "stdout");
|
|
2523
|
+
const stderr = getStringValue(parsed, "stderr");
|
|
2524
|
+
const error = getStringValue(parsed, "error");
|
|
2525
|
+
const lines = [];
|
|
2526
|
+
if (stdout) lines.push(stdout);
|
|
2527
|
+
if (stderr) lines.push(prefixLines(stderr, "[stderr] "));
|
|
2528
|
+
if (!stdout && !stderr && error) lines.push(error);
|
|
2529
|
+
return lines.join("\n");
|
|
2530
|
+
}
|
|
2531
|
+
function formatStatusLabel(isFailed, exitCode) {
|
|
2532
|
+
if (exitCode !== void 0 && exitCode !== SUCCESS_EXIT_CODE2) return `exit ${exitCode}`;
|
|
2533
|
+
return isFailed ? "error" : "ok";
|
|
2534
|
+
}
|
|
2535
|
+
function splitOutputLines(output) {
|
|
2536
|
+
if (!output) return [];
|
|
2537
|
+
return output.replace(/\r\n/g, "\n").split("\n");
|
|
2538
|
+
}
|
|
2539
|
+
function trimTrailingBlankLines(lines) {
|
|
2540
|
+
let end = lines.length;
|
|
2541
|
+
while (end > 0 && lines[end - 1].trim().length === 0) {
|
|
2542
|
+
end -= 1;
|
|
2543
|
+
}
|
|
2544
|
+
return lines.slice(0, end);
|
|
2545
|
+
}
|
|
2546
|
+
function prefixLines(value, prefix) {
|
|
2547
|
+
return splitOutputLines(value).map((line) => `${prefix}${line}`).join("\n");
|
|
2548
|
+
}
|
|
2549
|
+
function isUniversalObject(value) {
|
|
2550
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
2551
|
+
}
|
|
2552
|
+
function getStringValue(source, key) {
|
|
2553
|
+
if (!isUniversalObject(source)) return void 0;
|
|
2554
|
+
const value = source[key];
|
|
2555
|
+
return typeof value === "string" ? value : void 0;
|
|
2556
|
+
}
|
|
2557
|
+
function getNumberValue(source, key) {
|
|
2558
|
+
if (!isUniversalObject(source)) return void 0;
|
|
2559
|
+
const value = source[key];
|
|
2560
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2561
|
+
}
|
|
2562
|
+
function getBooleanValue(source, key) {
|
|
2563
|
+
if (!isUniversalObject(source)) return void 0;
|
|
2564
|
+
const value = source[key];
|
|
2565
|
+
return typeof value === "boolean" ? value : void 0;
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
// src/ui/ToolCommandOutput.tsx
|
|
2569
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
2570
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2571
|
+
function ToolCommandOutput({ tool }) {
|
|
2572
|
+
const summary = formatCommandOutputSummary(tool);
|
|
2573
|
+
if (!summary) return null;
|
|
2574
|
+
if (summary.statusLabel === "ok" && summary.previewLines.length === 0 && !summary.transcriptHint) {
|
|
2575
|
+
return null;
|
|
2576
|
+
}
|
|
2577
|
+
const color = summary.status === "error" ? "red" : "white";
|
|
2578
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginLeft: 4, children: [
|
|
2579
|
+
summary.statusLabel !== "ok" && /* @__PURE__ */ jsx3(Text3, { color, dimColor: true, children: summary.statusLabel }),
|
|
2580
|
+
summary.previewLines.map((line, index) => /* @__PURE__ */ jsx3(Text3, { color, dimColor: true, children: line }, `${line}-${index}`)),
|
|
2581
|
+
summary.transcriptHint && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: summary.transcriptHint })
|
|
2582
|
+
] });
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2123
2585
|
// src/ui/MessageList.tsx
|
|
2124
|
-
import { Fragment, jsx, jsxs as
|
|
2586
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2587
|
+
function getToolSummaryStatus(tool) {
|
|
2588
|
+
if (formatCommandOutputSummary(tool)?.status === "error") return "\u2717";
|
|
2589
|
+
if (tool.isRunning) return "\u27F3";
|
|
2590
|
+
if (tool.result === "error") return "\u2717";
|
|
2591
|
+
if (tool.result === "denied") return "\u2298";
|
|
2592
|
+
return "\u2713";
|
|
2593
|
+
}
|
|
2594
|
+
function getToolSummaryColor(tool) {
|
|
2595
|
+
if (formatCommandOutputSummary(tool)?.status === "error" || tool.result === "error") return "red";
|
|
2596
|
+
if (tool.isRunning || tool.result === "denied") return "yellow";
|
|
2597
|
+
return "green";
|
|
2598
|
+
}
|
|
2599
|
+
function getToolSummaryLabel(tool) {
|
|
2600
|
+
return `${getToolSummaryStatus(tool)} ${tool.toolName}${tool.firstArg ? `(${tool.firstArg})` : ""}`;
|
|
2601
|
+
}
|
|
2125
2602
|
function RoleLabel({ role }) {
|
|
2126
2603
|
switch (role) {
|
|
2127
2604
|
case "user":
|
|
2128
|
-
return /* @__PURE__ */
|
|
2605
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: "green", bold: true, children: [
|
|
2129
2606
|
"You:",
|
|
2130
2607
|
" "
|
|
2131
2608
|
] });
|
|
2132
2609
|
case "assistant":
|
|
2133
|
-
return /* @__PURE__ */
|
|
2610
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: "cyan", bold: true, children: [
|
|
2134
2611
|
"Robota:",
|
|
2135
2612
|
" "
|
|
2136
2613
|
] });
|
|
2137
2614
|
case "system":
|
|
2138
|
-
return /* @__PURE__ */
|
|
2615
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: "yellow", bold: true, children: [
|
|
2139
2616
|
"System:",
|
|
2140
2617
|
" "
|
|
2141
2618
|
] });
|
|
2142
2619
|
case "tool":
|
|
2143
|
-
return /* @__PURE__ */
|
|
2620
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: "white", bold: true, children: [
|
|
2144
2621
|
"Tool:",
|
|
2145
2622
|
" "
|
|
2146
2623
|
] });
|
|
@@ -2148,7 +2625,7 @@ function RoleLabel({ role }) {
|
|
|
2148
2625
|
}
|
|
2149
2626
|
function ToolMessage({ message }) {
|
|
2150
2627
|
if (!isToolMessage(message)) {
|
|
2151
|
-
return /* @__PURE__ */
|
|
2628
|
+
return /* @__PURE__ */ jsx4(Fragment2, {});
|
|
2152
2629
|
}
|
|
2153
2630
|
const toolName = message.name;
|
|
2154
2631
|
const content = message.content;
|
|
@@ -2161,45 +2638,45 @@ function ToolMessage({ message }) {
|
|
|
2161
2638
|
} catch {
|
|
2162
2639
|
}
|
|
2163
2640
|
if (summaries) {
|
|
2164
|
-
return /* @__PURE__ */
|
|
2165
|
-
/* @__PURE__ */
|
|
2166
|
-
/* @__PURE__ */
|
|
2641
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
|
|
2642
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
2643
|
+
/* @__PURE__ */ jsxs4(Text4, { color: "white", bold: true, children: [
|
|
2167
2644
|
"Tool:",
|
|
2168
2645
|
" "
|
|
2169
2646
|
] }),
|
|
2170
|
-
toolName && /* @__PURE__ */
|
|
2647
|
+
toolName && /* @__PURE__ */ jsxs4(Text4, { color: "white", dimColor: true, children: [
|
|
2171
2648
|
"[",
|
|
2172
2649
|
toolName,
|
|
2173
2650
|
"]"
|
|
2174
2651
|
] })
|
|
2175
2652
|
] }),
|
|
2176
|
-
/* @__PURE__ */
|
|
2177
|
-
summaries.map((s, i) => /* @__PURE__ */
|
|
2178
|
-
/* @__PURE__ */
|
|
2653
|
+
/* @__PURE__ */ jsx4(Text4, { children: " " }),
|
|
2654
|
+
summaries.map((s, i) => /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
2655
|
+
/* @__PURE__ */ jsxs4(Text4, { color: "green", children: [
|
|
2179
2656
|
" ",
|
|
2180
2657
|
"\u2713",
|
|
2181
2658
|
" ",
|
|
2182
2659
|
s.line
|
|
2183
2660
|
] }),
|
|
2184
|
-
s.diffLines && s.diffLines.length > 0 && /* @__PURE__ */
|
|
2661
|
+
s.diffLines && s.diffLines.length > 0 && /* @__PURE__ */ jsx4(ToolDiffBlock, { file: s.diffFile, lines: s.diffLines })
|
|
2185
2662
|
] }, i))
|
|
2186
2663
|
] });
|
|
2187
2664
|
}
|
|
2188
2665
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
2189
|
-
return /* @__PURE__ */
|
|
2190
|
-
/* @__PURE__ */
|
|
2191
|
-
/* @__PURE__ */
|
|
2666
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
|
|
2667
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
2668
|
+
/* @__PURE__ */ jsxs4(Text4, { color: "white", bold: true, children: [
|
|
2192
2669
|
"Tool:",
|
|
2193
2670
|
" "
|
|
2194
2671
|
] }),
|
|
2195
|
-
toolName && /* @__PURE__ */
|
|
2672
|
+
toolName && /* @__PURE__ */ jsxs4(Text4, { color: "white", dimColor: true, children: [
|
|
2196
2673
|
"[",
|
|
2197
2674
|
toolName,
|
|
2198
2675
|
"]"
|
|
2199
2676
|
] })
|
|
2200
2677
|
] }),
|
|
2201
|
-
/* @__PURE__ */
|
|
2202
|
-
lines.map((line, i) => /* @__PURE__ */
|
|
2678
|
+
/* @__PURE__ */ jsx4(Text4, { children: " " }),
|
|
2679
|
+
lines.map((line, i) => /* @__PURE__ */ jsxs4(Text4, { color: "green", children: [
|
|
2203
2680
|
" ",
|
|
2204
2681
|
"\u2713",
|
|
2205
2682
|
" ",
|
|
@@ -2211,26 +2688,44 @@ var MessageItem = React.memo(function MessageItem2({
|
|
|
2211
2688
|
message
|
|
2212
2689
|
}) {
|
|
2213
2690
|
if (isToolMessage(message)) {
|
|
2214
|
-
return /* @__PURE__ */
|
|
2691
|
+
return /* @__PURE__ */ jsx4(ToolMessage, { message });
|
|
2215
2692
|
}
|
|
2216
2693
|
const content = message.content ?? "";
|
|
2217
2694
|
const isInterrupted = message.state === "interrupted";
|
|
2218
|
-
return /* @__PURE__ */
|
|
2219
|
-
/* @__PURE__ */
|
|
2220
|
-
/* @__PURE__ */
|
|
2221
|
-
/* @__PURE__ */
|
|
2695
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
|
|
2696
|
+
/* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsx4(RoleLabel, { role: message.role }) }),
|
|
2697
|
+
/* @__PURE__ */ jsx4(Text4, { children: " " }),
|
|
2698
|
+
/* @__PURE__ */ jsx4(Box4, { marginLeft: 2, children: /* @__PURE__ */ jsx4(Text4, { wrap: "wrap", children: isAssistantMessage(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
|
|
2222
2699
|
] });
|
|
2223
2700
|
});
|
|
2224
2701
|
function ToolSummaryEntry({ entry }) {
|
|
2225
2702
|
const data = entry.data;
|
|
2703
|
+
const tools = data?.tools;
|
|
2226
2704
|
const lines = data?.summary?.split("\n") ?? [];
|
|
2227
|
-
|
|
2228
|
-
|
|
2705
|
+
if (tools && tools.length > 0) {
|
|
2706
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
|
|
2707
|
+
/* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { color: "white", bold: true, children: [
|
|
2708
|
+
"Tool:",
|
|
2709
|
+
" "
|
|
2710
|
+
] }) }),
|
|
2711
|
+
/* @__PURE__ */ jsx4(Text4, { children: " " }),
|
|
2712
|
+
tools.map((tool, i) => /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
2713
|
+
/* @__PURE__ */ jsxs4(Text4, { color: getToolSummaryColor(tool), children: [
|
|
2714
|
+
" ",
|
|
2715
|
+
getToolSummaryLabel(tool)
|
|
2716
|
+
] }),
|
|
2717
|
+
/* @__PURE__ */ jsx4(ToolCommandOutput, { tool }),
|
|
2718
|
+
tool.diffLines && tool.diffLines.length > 0 && /* @__PURE__ */ jsx4(ToolDiffBlock, { file: tool.diffFile, lines: tool.diffLines })
|
|
2719
|
+
] }, i))
|
|
2720
|
+
] });
|
|
2721
|
+
}
|
|
2722
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
|
|
2723
|
+
/* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { color: "white", bold: true, children: [
|
|
2229
2724
|
"Tool:",
|
|
2230
2725
|
" "
|
|
2231
2726
|
] }) }),
|
|
2232
|
-
/* @__PURE__ */
|
|
2233
|
-
lines.map((line, i) => /* @__PURE__ */
|
|
2727
|
+
/* @__PURE__ */ jsx4(Text4, { children: " " }),
|
|
2728
|
+
lines.map((line, i) => /* @__PURE__ */ jsxs4(Text4, { color: "green", children: [
|
|
2234
2729
|
" ",
|
|
2235
2730
|
line
|
|
2236
2731
|
] }, i))
|
|
@@ -2239,36 +2734,134 @@ function ToolSummaryEntry({ entry }) {
|
|
|
2239
2734
|
function EventEntry({ entry }) {
|
|
2240
2735
|
const eventData = entry.data;
|
|
2241
2736
|
const eventMessage = typeof eventData?.message === "string" ? eventData.message : typeof eventData?.content === "string" ? eventData.content : entry.type;
|
|
2242
|
-
return /* @__PURE__ */
|
|
2243
|
-
/* @__PURE__ */
|
|
2737
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
|
|
2738
|
+
/* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { color: "yellow", bold: true, children: [
|
|
2244
2739
|
"System:",
|
|
2245
2740
|
" "
|
|
2246
2741
|
] }) }),
|
|
2247
|
-
/* @__PURE__ */
|
|
2248
|
-
/* @__PURE__ */
|
|
2742
|
+
/* @__PURE__ */ jsx4(Text4, { children: " " }),
|
|
2743
|
+
/* @__PURE__ */ jsx4(Box4, { marginLeft: 2, children: /* @__PURE__ */ jsx4(Text4, { wrap: "wrap", children: eventMessage }) })
|
|
2249
2744
|
] });
|
|
2250
2745
|
}
|
|
2251
2746
|
function EntryItem({ entry }) {
|
|
2252
2747
|
if (entry.category === "chat") {
|
|
2253
2748
|
const message = entry.data;
|
|
2254
|
-
return /* @__PURE__ */
|
|
2749
|
+
return /* @__PURE__ */ jsx4(MessageItem, { message });
|
|
2255
2750
|
}
|
|
2256
2751
|
if (entry.type === "tool-summary") {
|
|
2257
|
-
return /* @__PURE__ */
|
|
2752
|
+
return /* @__PURE__ */ jsx4(ToolSummaryEntry, { entry });
|
|
2753
|
+
}
|
|
2754
|
+
if (entry.type === "usage-summary") {
|
|
2755
|
+
return /* @__PURE__ */ jsx4(UsageSummaryEntry, { entry });
|
|
2258
2756
|
}
|
|
2259
2757
|
if (entry.type === "tool-start" || entry.type === "tool-end") {
|
|
2260
|
-
return /* @__PURE__ */
|
|
2758
|
+
return /* @__PURE__ */ jsx4(Fragment2, {});
|
|
2261
2759
|
}
|
|
2262
|
-
return /* @__PURE__ */
|
|
2760
|
+
return /* @__PURE__ */ jsx4(EventEntry, { entry });
|
|
2263
2761
|
}
|
|
2264
2762
|
function MessageList({ history }) {
|
|
2265
|
-
return /* @__PURE__ */
|
|
2763
|
+
return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", children: history.map((entry) => /* @__PURE__ */ jsx4(EntryItem, { entry }, entry.id)) });
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
// src/ui/SessionStatusBar.tsx
|
|
2767
|
+
import { useMemo as useMemo2 } from "react";
|
|
2768
|
+
import { getModelName as getModelName2 } from "@robota-sdk/agent-core";
|
|
2769
|
+
|
|
2770
|
+
// src/utils/git-branch.ts
|
|
2771
|
+
import { existsSync as existsSync4, lstatSync, readFileSync as readFileSync4 } from "fs";
|
|
2772
|
+
import { dirname as dirname3, isAbsolute, join as join7, resolve } from "path";
|
|
2773
|
+
var DETACHED_HEAD_LENGTH = 7;
|
|
2774
|
+
function resolveGitBranch(cwd) {
|
|
2775
|
+
try {
|
|
2776
|
+
const gitDir = findGitDir(cwd);
|
|
2777
|
+
if (!gitDir) return void 0;
|
|
2778
|
+
const head = readFileSync4(join7(gitDir, "HEAD"), "utf8").trim();
|
|
2779
|
+
if (!head) return void 0;
|
|
2780
|
+
if (head.startsWith("ref: ")) {
|
|
2781
|
+
const ref = head.slice("ref: ".length).trim();
|
|
2782
|
+
const branchPrefix = "refs/heads/";
|
|
2783
|
+
return ref.startsWith(branchPrefix) ? ref.slice(branchPrefix.length) : ref;
|
|
2784
|
+
}
|
|
2785
|
+
return head.slice(0, DETACHED_HEAD_LENGTH);
|
|
2786
|
+
} catch {
|
|
2787
|
+
return void 0;
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
function findGitDir(start) {
|
|
2791
|
+
let current = resolve(start);
|
|
2792
|
+
let parent = dirname3(current);
|
|
2793
|
+
while (parent !== current) {
|
|
2794
|
+
const candidate = join7(current, ".git");
|
|
2795
|
+
const resolved = resolveGitMetadata(candidate, current);
|
|
2796
|
+
if (resolved) return resolved;
|
|
2797
|
+
current = parent;
|
|
2798
|
+
parent = dirname3(current);
|
|
2799
|
+
}
|
|
2800
|
+
const rootCandidate = join7(current, ".git");
|
|
2801
|
+
return resolveGitMetadata(rootCandidate, current);
|
|
2802
|
+
}
|
|
2803
|
+
function resolveGitMetadata(candidate, repoDir) {
|
|
2804
|
+
if (!existsSync4(candidate)) return void 0;
|
|
2805
|
+
const stat = lstatSync(candidate);
|
|
2806
|
+
if (stat.isDirectory()) return candidate;
|
|
2807
|
+
if (!stat.isFile()) return void 0;
|
|
2808
|
+
const content = readFileSync4(candidate, "utf8").trim();
|
|
2809
|
+
const prefix = "gitdir:";
|
|
2810
|
+
if (!content.startsWith(prefix)) return void 0;
|
|
2811
|
+
const rawPath = content.slice(prefix.length).trim();
|
|
2812
|
+
return isAbsolute(rawPath) ? rawPath : resolve(repoDir, rawPath);
|
|
2266
2813
|
}
|
|
2267
2814
|
|
|
2268
2815
|
// src/ui/StatusBar.tsx
|
|
2269
|
-
import { Box as
|
|
2270
|
-
import { formatTokenCount } from "@robota-sdk/agent-core";
|
|
2271
|
-
|
|
2816
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
2817
|
+
import { formatTokenCount as formatTokenCount2 } from "@robota-sdk/agent-core";
|
|
2818
|
+
|
|
2819
|
+
// src/ui/status-activity.ts
|
|
2820
|
+
var NO_ACTIVE_ITEMS = 0;
|
|
2821
|
+
function formatStatusActivity(input) {
|
|
2822
|
+
const base = getPrimaryActivity(input);
|
|
2823
|
+
const segments = input.hasPendingPrompt && base.kind !== "queued" ? ["queued"] : [];
|
|
2824
|
+
const text = [base.label, ...segments].join(" \xB7 ");
|
|
2825
|
+
return { ...base, segments, text };
|
|
2826
|
+
}
|
|
2827
|
+
function getPrimaryActivity(input) {
|
|
2828
|
+
if (input.activeToolCount > NO_ACTIVE_ITEMS) {
|
|
2829
|
+
return {
|
|
2830
|
+
kind: "tools",
|
|
2831
|
+
label: `Tools x${input.activeToolCount}`,
|
|
2832
|
+
color: "cyan"
|
|
2833
|
+
};
|
|
2834
|
+
}
|
|
2835
|
+
if (input.isThinking) {
|
|
2836
|
+
return {
|
|
2837
|
+
kind: "thinking",
|
|
2838
|
+
label: "Thinking",
|
|
2839
|
+
color: "yellow"
|
|
2840
|
+
};
|
|
2841
|
+
}
|
|
2842
|
+
if (input.activeBackgroundTaskCount > NO_ACTIVE_ITEMS) {
|
|
2843
|
+
return {
|
|
2844
|
+
kind: "background",
|
|
2845
|
+
label: `Background x${input.activeBackgroundTaskCount}`,
|
|
2846
|
+
color: "cyan"
|
|
2847
|
+
};
|
|
2848
|
+
}
|
|
2849
|
+
if (input.hasPendingPrompt) {
|
|
2850
|
+
return {
|
|
2851
|
+
kind: "queued",
|
|
2852
|
+
label: "Queued",
|
|
2853
|
+
color: "yellow"
|
|
2854
|
+
};
|
|
2855
|
+
}
|
|
2856
|
+
return {
|
|
2857
|
+
kind: "idle",
|
|
2858
|
+
label: "Idle",
|
|
2859
|
+
color: "gray"
|
|
2860
|
+
};
|
|
2861
|
+
}
|
|
2862
|
+
|
|
2863
|
+
// src/ui/StatusBar.tsx
|
|
2864
|
+
import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2272
2865
|
var CONTEXT_YELLOW_THRESHOLD = 70;
|
|
2273
2866
|
var CONTEXT_RED_THRESHOLD = 90;
|
|
2274
2867
|
function getContextColor(percentage) {
|
|
@@ -2276,20 +2869,115 @@ function getContextColor(percentage) {
|
|
|
2276
2869
|
if (percentage >= CONTEXT_YELLOW_THRESHOLD) return "yellow";
|
|
2277
2870
|
return "green";
|
|
2278
2871
|
}
|
|
2872
|
+
function StatusActivityText({
|
|
2873
|
+
isThinking,
|
|
2874
|
+
activeToolCount,
|
|
2875
|
+
activeBackgroundTaskCount,
|
|
2876
|
+
hasPendingPrompt
|
|
2877
|
+
}) {
|
|
2878
|
+
const activity = formatStatusActivity({
|
|
2879
|
+
isThinking,
|
|
2880
|
+
activeToolCount,
|
|
2881
|
+
activeBackgroundTaskCount,
|
|
2882
|
+
hasPendingPrompt
|
|
2883
|
+
});
|
|
2884
|
+
return /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
2885
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", bold: true, children: "Activity:" }),
|
|
2886
|
+
" ",
|
|
2887
|
+
/* @__PURE__ */ jsx5(Text5, { color: activity.color, bold: activity.kind !== "idle", children: activity.text })
|
|
2888
|
+
] });
|
|
2889
|
+
}
|
|
2890
|
+
function ContextText({
|
|
2891
|
+
percentage,
|
|
2892
|
+
usedTokens,
|
|
2893
|
+
maxTokens
|
|
2894
|
+
}) {
|
|
2895
|
+
return /* @__PURE__ */ jsxs5(Text5, { color: getContextColor(percentage), children: [
|
|
2896
|
+
"Context: ",
|
|
2897
|
+
Math.round(percentage),
|
|
2898
|
+
"% (",
|
|
2899
|
+
formatTokenCount2(usedTokens),
|
|
2900
|
+
"/",
|
|
2901
|
+
formatTokenCount2(maxTokens),
|
|
2902
|
+
")"
|
|
2903
|
+
] });
|
|
2904
|
+
}
|
|
2905
|
+
function ModeText({ permissionMode }) {
|
|
2906
|
+
return /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
2907
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", bold: true, children: "Mode:" }),
|
|
2908
|
+
" ",
|
|
2909
|
+
/* @__PURE__ */ jsx5(Text5, { children: permissionMode })
|
|
2910
|
+
] });
|
|
2911
|
+
}
|
|
2912
|
+
function StatusLeft({
|
|
2913
|
+
permissionMode,
|
|
2914
|
+
modelName,
|
|
2915
|
+
isThinking,
|
|
2916
|
+
activeToolCount,
|
|
2917
|
+
activeBackgroundTaskCount,
|
|
2918
|
+
hasPendingPrompt,
|
|
2919
|
+
contextPercentage,
|
|
2920
|
+
contextUsedTokens,
|
|
2921
|
+
contextMaxTokens,
|
|
2922
|
+
sessionName,
|
|
2923
|
+
gitBranch,
|
|
2924
|
+
showGitBranch
|
|
2925
|
+
}) {
|
|
2926
|
+
const shouldShowGitBranch = showGitBranch && gitBranch !== void 0 && gitBranch.length > 0;
|
|
2927
|
+
return /* @__PURE__ */ jsxs5(Text5, { children: [
|
|
2928
|
+
/* @__PURE__ */ jsx5(
|
|
2929
|
+
StatusActivityText,
|
|
2930
|
+
{
|
|
2931
|
+
isThinking,
|
|
2932
|
+
activeToolCount,
|
|
2933
|
+
activeBackgroundTaskCount,
|
|
2934
|
+
hasPendingPrompt
|
|
2935
|
+
}
|
|
2936
|
+
),
|
|
2937
|
+
" | ",
|
|
2938
|
+
/* @__PURE__ */ jsx5(ModeText, { permissionMode }),
|
|
2939
|
+
sessionName && /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
2940
|
+
" | ",
|
|
2941
|
+
/* @__PURE__ */ jsx5(Text5, { color: "magenta", children: sessionName })
|
|
2942
|
+
] }),
|
|
2943
|
+
shouldShowGitBranch && /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
2944
|
+
" | ",
|
|
2945
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
2946
|
+
"git: ",
|
|
2947
|
+
gitBranch
|
|
2948
|
+
] })
|
|
2949
|
+
] }),
|
|
2950
|
+
" | ",
|
|
2951
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: modelName }),
|
|
2952
|
+
" | ",
|
|
2953
|
+
/* @__PURE__ */ jsx5(
|
|
2954
|
+
ContextText,
|
|
2955
|
+
{
|
|
2956
|
+
percentage: contextPercentage,
|
|
2957
|
+
usedTokens: contextUsedTokens,
|
|
2958
|
+
maxTokens: contextMaxTokens
|
|
2959
|
+
}
|
|
2960
|
+
)
|
|
2961
|
+
] });
|
|
2962
|
+
}
|
|
2279
2963
|
function StatusBar({
|
|
2280
2964
|
permissionMode,
|
|
2281
2965
|
modelName,
|
|
2282
2966
|
sessionId: _sessionId,
|
|
2283
2967
|
messageCount,
|
|
2284
2968
|
isThinking,
|
|
2969
|
+
activeToolCount = 0,
|
|
2970
|
+
activeBackgroundTaskCount = 0,
|
|
2971
|
+
hasPendingPrompt = false,
|
|
2285
2972
|
contextPercentage,
|
|
2286
2973
|
contextUsedTokens,
|
|
2287
2974
|
contextMaxTokens,
|
|
2288
|
-
sessionName
|
|
2975
|
+
sessionName,
|
|
2976
|
+
gitBranch,
|
|
2977
|
+
showGitBranch = true
|
|
2289
2978
|
}) {
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
Box3,
|
|
2979
|
+
return /* @__PURE__ */ jsxs5(
|
|
2980
|
+
Box5,
|
|
2293
2981
|
{
|
|
2294
2982
|
borderStyle: "single",
|
|
2295
2983
|
borderColor: "gray",
|
|
@@ -2297,46 +2985,78 @@ function StatusBar({
|
|
|
2297
2985
|
paddingRight: 1,
|
|
2298
2986
|
justifyContent: "space-between",
|
|
2299
2987
|
children: [
|
|
2300
|
-
/* @__PURE__ */
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
] })
|
|
2321
|
-
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
2322
|
-
isThinking && /* @__PURE__ */ jsx2(Text3, { color: "yellow", children: "Thinking... " }),
|
|
2323
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
2324
|
-
"msgs: ",
|
|
2325
|
-
messageCount
|
|
2326
|
-
] })
|
|
2327
|
-
] })
|
|
2988
|
+
/* @__PURE__ */ jsx5(
|
|
2989
|
+
StatusLeft,
|
|
2990
|
+
{
|
|
2991
|
+
permissionMode,
|
|
2992
|
+
modelName,
|
|
2993
|
+
isThinking,
|
|
2994
|
+
activeToolCount,
|
|
2995
|
+
activeBackgroundTaskCount,
|
|
2996
|
+
hasPendingPrompt,
|
|
2997
|
+
contextPercentage,
|
|
2998
|
+
contextUsedTokens,
|
|
2999
|
+
contextMaxTokens,
|
|
3000
|
+
sessionName,
|
|
3001
|
+
gitBranch,
|
|
3002
|
+
showGitBranch
|
|
3003
|
+
}
|
|
3004
|
+
),
|
|
3005
|
+
/* @__PURE__ */ jsx5(Text5, { children: /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
3006
|
+
"msgs: ",
|
|
3007
|
+
messageCount
|
|
3008
|
+
] }) })
|
|
2328
3009
|
]
|
|
2329
3010
|
}
|
|
2330
3011
|
);
|
|
2331
3012
|
}
|
|
2332
3013
|
|
|
3014
|
+
// src/ui/SessionStatusBar.tsx
|
|
3015
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
3016
|
+
function SessionStatusBar({
|
|
3017
|
+
cwd,
|
|
3018
|
+
permissionMode,
|
|
3019
|
+
modelId,
|
|
3020
|
+
sessionId,
|
|
3021
|
+
messageCount,
|
|
3022
|
+
isThinking,
|
|
3023
|
+
activeToolCount,
|
|
3024
|
+
activeBackgroundTaskCount,
|
|
3025
|
+
hasPendingPrompt,
|
|
3026
|
+
contextState,
|
|
3027
|
+
sessionName,
|
|
3028
|
+
settings
|
|
3029
|
+
}) {
|
|
3030
|
+
const gitBranch = useMemo2(() => resolveGitBranch(cwd), [cwd]);
|
|
3031
|
+
if (!settings.enabled) return null;
|
|
3032
|
+
return /* @__PURE__ */ jsx6(
|
|
3033
|
+
StatusBar,
|
|
3034
|
+
{
|
|
3035
|
+
permissionMode,
|
|
3036
|
+
modelName: modelId ? getModelName2(modelId) : "",
|
|
3037
|
+
sessionId,
|
|
3038
|
+
messageCount,
|
|
3039
|
+
isThinking,
|
|
3040
|
+
activeToolCount,
|
|
3041
|
+
activeBackgroundTaskCount,
|
|
3042
|
+
hasPendingPrompt,
|
|
3043
|
+
contextPercentage: contextState.percentage,
|
|
3044
|
+
contextUsedTokens: contextState.usedTokens,
|
|
3045
|
+
contextMaxTokens: contextState.maxTokens,
|
|
3046
|
+
sessionName,
|
|
3047
|
+
gitBranch,
|
|
3048
|
+
showGitBranch: settings.gitBranch
|
|
3049
|
+
}
|
|
3050
|
+
);
|
|
3051
|
+
}
|
|
3052
|
+
|
|
2333
3053
|
// src/ui/InputArea.tsx
|
|
2334
|
-
import { useState as
|
|
2335
|
-
import { Box as
|
|
3054
|
+
import { useState as useState7, useCallback as useCallback4, useRef as useRef4, useMemo as useMemo4 } from "react";
|
|
3055
|
+
import { Box as Box7, Text as Text9, useInput as useInput2, useWindowSize } from "ink";
|
|
2336
3056
|
|
|
2337
3057
|
// src/ui/CjkTextInput.tsx
|
|
2338
|
-
import { useRef as useRef3, useState as
|
|
2339
|
-
import { Text as
|
|
3058
|
+
import { useRef as useRef3, useState as useState4 } from "react";
|
|
3059
|
+
import { Text as Text6, useInput, usePaste } from "ink";
|
|
2340
3060
|
import chalk from "chalk";
|
|
2341
3061
|
|
|
2342
3062
|
// src/ui/flows/cjk-text-input-flow.ts
|
|
@@ -2363,10 +3083,20 @@ function applyCjkTextInput(state, input, key, options) {
|
|
|
2363
3083
|
if (pasteResult !== void 0) return pasteResult;
|
|
2364
3084
|
const controlResult = applyControlInput(state, input, key, options);
|
|
2365
3085
|
if (controlResult !== void 0) return controlResult;
|
|
2366
|
-
const cursorResult = applyCursorInput(state, key, options
|
|
3086
|
+
const cursorResult = applyCursorInput(state, key, options);
|
|
2367
3087
|
if (cursorResult !== void 0) return cursorResult;
|
|
2368
3088
|
return insertPrintableInput(state, input);
|
|
2369
3089
|
}
|
|
3090
|
+
function applyCjkTextPaste(state, text, options) {
|
|
3091
|
+
const normalizedText = text.replace(/\r\n?/g, "\n");
|
|
3092
|
+
if (normalizedText.length === 0) {
|
|
3093
|
+
return { state, effect: { type: "none" } };
|
|
3094
|
+
}
|
|
3095
|
+
if (normalizedText.includes("\n") && options.canPaste) {
|
|
3096
|
+
return { state, effect: { type: "paste", text: normalizedText, cursor: state.cursor } };
|
|
3097
|
+
}
|
|
3098
|
+
return insertPrintableInput(state, normalizedText);
|
|
3099
|
+
}
|
|
2370
3100
|
function applyPasteBoundaryInput(state, input, options) {
|
|
2371
3101
|
if (input === PASTE_START || input.startsWith(PASTE_START)) {
|
|
2372
3102
|
return startBracketedPaste(state, input);
|
|
@@ -2391,9 +3121,16 @@ function applyControlInput(state, input, key, options) {
|
|
|
2391
3121
|
}
|
|
2392
3122
|
return void 0;
|
|
2393
3123
|
}
|
|
2394
|
-
function applyCursorInput(state, key,
|
|
3124
|
+
function applyCursorInput(state, key, options) {
|
|
2395
3125
|
if (key.upArrow === true || key.downArrow === true) {
|
|
2396
|
-
|
|
3126
|
+
if (options.enableVerticalNavigation === false) {
|
|
3127
|
+
return { state, effect: { type: "none" } };
|
|
3128
|
+
}
|
|
3129
|
+
return moveCursorVertically(
|
|
3130
|
+
state,
|
|
3131
|
+
key.upArrow === true ? "up" : "down",
|
|
3132
|
+
options.availableWidth
|
|
3133
|
+
);
|
|
2397
3134
|
}
|
|
2398
3135
|
if (key.leftArrow === true) {
|
|
2399
3136
|
return moveCursorHorizontally(state, "left");
|
|
@@ -2456,15 +3193,8 @@ function continueBracketedPaste(state, input, options) {
|
|
|
2456
3193
|
};
|
|
2457
3194
|
}
|
|
2458
3195
|
const beforeMarker = input.split(PASTE_END)[0] ?? "";
|
|
2459
|
-
const text = (state.pasteBuffer + beforeMarker).replace(/\r\n?/g, "\n");
|
|
2460
3196
|
const nextState = { ...state, isPasting: false, pasteBuffer: "" };
|
|
2461
|
-
|
|
2462
|
-
return { state: nextState, effect: { type: "none" } };
|
|
2463
|
-
}
|
|
2464
|
-
if (text.includes("\n") && options.canPaste) {
|
|
2465
|
-
return { state: nextState, effect: { type: "paste", text, cursor: state.cursor } };
|
|
2466
|
-
}
|
|
2467
|
-
return insertPrintableInput(nextState, text);
|
|
3197
|
+
return applyCjkTextPaste(nextState, state.pasteBuffer + beforeMarker, options);
|
|
2468
3198
|
}
|
|
2469
3199
|
function moveCursorVertically(state, direction, availableWidth) {
|
|
2470
3200
|
if (!availableWidth || availableWidth <= 0) {
|
|
@@ -2514,7 +3244,7 @@ function insertPrintableInput(state, input) {
|
|
|
2514
3244
|
}
|
|
2515
3245
|
|
|
2516
3246
|
// src/ui/CjkTextInput.tsx
|
|
2517
|
-
import { jsx as
|
|
3247
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
2518
3248
|
function CjkTextInput({
|
|
2519
3249
|
value,
|
|
2520
3250
|
onChange,
|
|
@@ -2524,41 +3254,79 @@ function CjkTextInput({
|
|
|
2524
3254
|
focus = true,
|
|
2525
3255
|
showCursor = true,
|
|
2526
3256
|
availableWidth,
|
|
2527
|
-
cursorHint = null
|
|
3257
|
+
cursorHint = null,
|
|
3258
|
+
enableVerticalNavigation = true
|
|
2528
3259
|
}) {
|
|
2529
3260
|
const stateRef = useRef3(createCjkTextInputFlowState(value));
|
|
2530
|
-
const [, forceRender] =
|
|
3261
|
+
const [, forceRender] = useState4(0);
|
|
2531
3262
|
stateRef.current = syncCjkTextInputFlowState(stateRef.current, value, cursorHint);
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
},
|
|
2544
|
-
{ isActive: focus }
|
|
2545
|
-
);
|
|
2546
|
-
return /* @__PURE__ */ jsx3(Text4, { children: renderWithCursor(
|
|
3263
|
+
useCjkTextInputHandlers({
|
|
3264
|
+
stateRef,
|
|
3265
|
+
onChange,
|
|
3266
|
+
onSubmit,
|
|
3267
|
+
onPaste,
|
|
3268
|
+
availableWidth,
|
|
3269
|
+
focus,
|
|
3270
|
+
enableVerticalNavigation,
|
|
3271
|
+
forceRender
|
|
3272
|
+
});
|
|
3273
|
+
return /* @__PURE__ */ jsx7(Text6, { children: renderWithCursor(
|
|
2547
3274
|
stateRef.current.value,
|
|
2548
3275
|
stateRef.current.cursor,
|
|
2549
3276
|
placeholder,
|
|
2550
3277
|
showCursor && focus
|
|
2551
3278
|
) });
|
|
2552
3279
|
}
|
|
2553
|
-
function
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
3280
|
+
function useCjkTextInputHandlers(options) {
|
|
3281
|
+
usePaste(
|
|
3282
|
+
(text) => {
|
|
3283
|
+
applyCjkFlowSafely(
|
|
3284
|
+
options,
|
|
3285
|
+
() => applyCjkTextPaste(options.stateRef.current, text, createFlowOptions(options))
|
|
3286
|
+
);
|
|
3287
|
+
},
|
|
3288
|
+
{ isActive: options.focus }
|
|
3289
|
+
);
|
|
3290
|
+
useInput(
|
|
3291
|
+
(input, key) => {
|
|
3292
|
+
applyCjkFlowSafely(
|
|
3293
|
+
options,
|
|
3294
|
+
() => applyCjkTextInput(options.stateRef.current, input, key, createFlowOptions(options))
|
|
3295
|
+
);
|
|
3296
|
+
},
|
|
3297
|
+
{ isActive: options.focus }
|
|
3298
|
+
);
|
|
3299
|
+
}
|
|
3300
|
+
function createFlowOptions(options) {
|
|
3301
|
+
return {
|
|
3302
|
+
availableWidth: options.availableWidth,
|
|
3303
|
+
canPaste: options.onPaste !== void 0,
|
|
3304
|
+
enableVerticalNavigation: options.enableVerticalNavigation
|
|
3305
|
+
};
|
|
3306
|
+
}
|
|
3307
|
+
function applyCjkFlowSafely(options, run) {
|
|
3308
|
+
try {
|
|
3309
|
+
const result = run();
|
|
3310
|
+
options.stateRef.current = result.state;
|
|
3311
|
+
applyCjkTextInputEffect(
|
|
3312
|
+
result.effect,
|
|
3313
|
+
options.onChange,
|
|
3314
|
+
options.onSubmit,
|
|
3315
|
+
options.onPaste,
|
|
3316
|
+
options.forceRender
|
|
3317
|
+
);
|
|
3318
|
+
} catch {
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
function applyCjkTextInputEffect(effect, onChange, onSubmit, onPaste, forceRender) {
|
|
3322
|
+
if (effect.type === "change") {
|
|
3323
|
+
onChange(effect.value);
|
|
3324
|
+
} else if (effect.type === "submit") {
|
|
3325
|
+
onSubmit?.(effect.value);
|
|
3326
|
+
} else if (effect.type === "paste") {
|
|
3327
|
+
onPaste?.(effect.text, effect.cursor);
|
|
3328
|
+
} else if (effect.type === "render") {
|
|
3329
|
+
forceRender((n) => n + 1);
|
|
2562
3330
|
}
|
|
2563
3331
|
}
|
|
2564
3332
|
function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
|
|
@@ -2584,14 +3352,14 @@ function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
|
|
|
2584
3352
|
}
|
|
2585
3353
|
|
|
2586
3354
|
// src/ui/WaveText.tsx
|
|
2587
|
-
import { useState as
|
|
2588
|
-
import { Text as
|
|
2589
|
-
import { jsx as
|
|
3355
|
+
import { useState as useState5, useEffect as useEffect2 } from "react";
|
|
3356
|
+
import { Text as Text7 } from "ink";
|
|
3357
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
2590
3358
|
var WAVE_COLORS = ["#666666", "#888888", "#aaaaaa", "#888888"];
|
|
2591
3359
|
var INTERVAL_MS = 400;
|
|
2592
3360
|
var CHARS_PER_GROUP = 4;
|
|
2593
3361
|
function WaveText({ text }) {
|
|
2594
|
-
const [tick, setTick] =
|
|
3362
|
+
const [tick, setTick] = useState5(0);
|
|
2595
3363
|
useEffect2(() => {
|
|
2596
3364
|
const timer = setInterval(() => {
|
|
2597
3365
|
setTick((prev) => prev + 1);
|
|
@@ -2599,23 +3367,23 @@ function WaveText({ text }) {
|
|
|
2599
3367
|
return () => clearInterval(timer);
|
|
2600
3368
|
}, []);
|
|
2601
3369
|
const chars = [...text];
|
|
2602
|
-
return /* @__PURE__ */
|
|
3370
|
+
return /* @__PURE__ */ jsx8(Text7, { children: chars.map((char, i) => {
|
|
2603
3371
|
const group = Math.floor(i / CHARS_PER_GROUP);
|
|
2604
3372
|
const colorIndex = (tick + group) % WAVE_COLORS.length;
|
|
2605
|
-
return /* @__PURE__ */
|
|
3373
|
+
return /* @__PURE__ */ jsx8(Text7, { color: WAVE_COLORS[colorIndex], children: char }, i);
|
|
2606
3374
|
}) });
|
|
2607
3375
|
}
|
|
2608
3376
|
|
|
2609
3377
|
// src/ui/SlashAutocomplete.tsx
|
|
2610
|
-
import { Box as
|
|
2611
|
-
import { jsx as
|
|
3378
|
+
import { Box as Box6, Text as Text8 } from "ink";
|
|
3379
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2612
3380
|
var MAX_VISIBLE = 8;
|
|
2613
3381
|
function CommandRow(props) {
|
|
2614
3382
|
const { cmd, isSelected, showSlash } = props;
|
|
2615
3383
|
const indicator = isSelected ? "\u25B8 " : " ";
|
|
2616
3384
|
const nameColor = isSelected ? "cyan" : void 0;
|
|
2617
3385
|
const dimmed = !isSelected;
|
|
2618
|
-
return /* @__PURE__ */
|
|
3386
|
+
return /* @__PURE__ */ jsx9(Box6, { children: /* @__PURE__ */ jsxs6(Text8, { color: nameColor, dimColor: dimmed, children: [
|
|
2619
3387
|
indicator,
|
|
2620
3388
|
showSlash ? `/${cmd.name} ${cmd.description}` : cmd.description
|
|
2621
3389
|
] }) });
|
|
@@ -2629,7 +3397,7 @@ function SlashAutocomplete({
|
|
|
2629
3397
|
if (!visible || commands.length === 0) return null;
|
|
2630
3398
|
const scrollOffset = computeScrollOffset(selectedIndex, commands.length);
|
|
2631
3399
|
const visibleCommands = commands.slice(scrollOffset, scrollOffset + MAX_VISIBLE);
|
|
2632
|
-
return /* @__PURE__ */
|
|
3400
|
+
return /* @__PURE__ */ jsx9(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: visibleCommands.map((cmd, i) => /* @__PURE__ */ jsx9(
|
|
2633
3401
|
CommandRow,
|
|
2634
3402
|
{
|
|
2635
3403
|
cmd,
|
|
@@ -2653,7 +3421,7 @@ function expandPasteLabels(text, store) {
|
|
|
2653
3421
|
}
|
|
2654
3422
|
|
|
2655
3423
|
// src/ui/hooks/useAutocomplete.ts
|
|
2656
|
-
import
|
|
3424
|
+
import React5, { useState as useState6, useMemo as useMemo3 } from "react";
|
|
2657
3425
|
function parseSlashInput(value) {
|
|
2658
3426
|
if (!value.startsWith("/")) return { isSlash: false, parentCommand: "", filter: "" };
|
|
2659
3427
|
const afterSlash = value.slice(1);
|
|
@@ -2664,16 +3432,16 @@ function parseSlashInput(value) {
|
|
|
2664
3432
|
return { isSlash: true, parentCommand: parent, filter: rest };
|
|
2665
3433
|
}
|
|
2666
3434
|
function useAutocomplete(value, registry) {
|
|
2667
|
-
const [selectedIndex, setSelectedIndex] =
|
|
2668
|
-
const [dismissed, setDismissed] =
|
|
2669
|
-
const prevValueRef =
|
|
3435
|
+
const [selectedIndex, setSelectedIndex] = useState6(0);
|
|
3436
|
+
const [dismissed, setDismissed] = useState6(false);
|
|
3437
|
+
const prevValueRef = React5.useRef(value);
|
|
2670
3438
|
if (prevValueRef.current !== value) {
|
|
2671
3439
|
prevValueRef.current = value;
|
|
2672
3440
|
if (dismissed) setDismissed(false);
|
|
2673
3441
|
}
|
|
2674
3442
|
const parsed = parseSlashInput(value);
|
|
2675
3443
|
const isSubcommandMode = parsed.isSlash && parsed.parentCommand.length > 0;
|
|
2676
|
-
const filteredCommands =
|
|
3444
|
+
const filteredCommands = useMemo3(() => {
|
|
2677
3445
|
if (!registry || !parsed.isSlash || dismissed) return [];
|
|
2678
3446
|
if (isSubcommandMode) {
|
|
2679
3447
|
const subs = registry.getSubcommands(parsed.parentCommand);
|
|
@@ -2721,6 +3489,61 @@ function getPendingPromptInputAction(key) {
|
|
|
2721
3489
|
}
|
|
2722
3490
|
return void 0;
|
|
2723
3491
|
}
|
|
3492
|
+
function getPromptHistoryInputAction(key) {
|
|
3493
|
+
if (key.upArrow === true) return "previous";
|
|
3494
|
+
if (key.downArrow === true) return "next";
|
|
3495
|
+
return void 0;
|
|
3496
|
+
}
|
|
3497
|
+
function createPromptHistoryNavigationState() {
|
|
3498
|
+
return { selectedIndex: null, draft: "" };
|
|
3499
|
+
}
|
|
3500
|
+
function navigatePromptHistory(value, history, state, action) {
|
|
3501
|
+
if (history.length === 0) {
|
|
3502
|
+
return { value, cursorHint: value.length, state };
|
|
3503
|
+
}
|
|
3504
|
+
if (action === "previous") {
|
|
3505
|
+
const selectedIndex = state.selectedIndex === null ? history.length - 1 : Math.max(0, state.selectedIndex - 1);
|
|
3506
|
+
const nextValue = history[selectedIndex] ?? value;
|
|
3507
|
+
return {
|
|
3508
|
+
value: nextValue,
|
|
3509
|
+
cursorHint: nextValue.length,
|
|
3510
|
+
state: { selectedIndex, draft: state.selectedIndex === null ? value : state.draft }
|
|
3511
|
+
};
|
|
3512
|
+
}
|
|
3513
|
+
if (state.selectedIndex === null) {
|
|
3514
|
+
return { value, cursorHint: value.length, state };
|
|
3515
|
+
}
|
|
3516
|
+
if (state.selectedIndex < history.length - 1) {
|
|
3517
|
+
const selectedIndex = state.selectedIndex + 1;
|
|
3518
|
+
const nextValue = history[selectedIndex] ?? value;
|
|
3519
|
+
return {
|
|
3520
|
+
value: nextValue,
|
|
3521
|
+
cursorHint: nextValue.length,
|
|
3522
|
+
state: { ...state, selectedIndex }
|
|
3523
|
+
};
|
|
3524
|
+
}
|
|
3525
|
+
return {
|
|
3526
|
+
value: state.draft,
|
|
3527
|
+
cursorHint: state.draft.length,
|
|
3528
|
+
state: createPromptHistoryNavigationState()
|
|
3529
|
+
};
|
|
3530
|
+
}
|
|
3531
|
+
function appendPromptHistory(history, value) {
|
|
3532
|
+
const prompt = value.trim();
|
|
3533
|
+
if (prompt.length === 0) return [...history];
|
|
3534
|
+
if (history[history.length - 1] === prompt) return [...history];
|
|
3535
|
+
return [...history, prompt];
|
|
3536
|
+
}
|
|
3537
|
+
function extractPromptHistory(entries) {
|
|
3538
|
+
let prompts = [];
|
|
3539
|
+
for (const entry of entries) {
|
|
3540
|
+
if (entry.category !== "chat" || entry.type !== "user") continue;
|
|
3541
|
+
const data = entry.data;
|
|
3542
|
+
if (typeof data?.content !== "string") continue;
|
|
3543
|
+
prompts = appendPromptHistory(prompts, data.content);
|
|
3544
|
+
}
|
|
3545
|
+
return prompts;
|
|
3546
|
+
}
|
|
2724
3547
|
function moveAutocompleteSelection(selectedIndex, commandCount, direction) {
|
|
2725
3548
|
if (commandCount === 0) return 0;
|
|
2726
3549
|
if (direction === "previous") {
|
|
@@ -2763,11 +3586,12 @@ function shouldSubmitInput(text) {
|
|
|
2763
3586
|
}
|
|
2764
3587
|
|
|
2765
3588
|
// src/ui/InputArea.tsx
|
|
2766
|
-
import { jsx as
|
|
3589
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2767
3590
|
var BORDER_HORIZONTAL = 2;
|
|
2768
3591
|
var PADDING_LEFT = 1;
|
|
2769
3592
|
var PROMPT_WIDTH = 2;
|
|
2770
3593
|
var INPUT_AREA_OVERHEAD = BORDER_HORIZONTAL + PADDING_LEFT + PROMPT_WIDTH;
|
|
3594
|
+
var DEFAULT_TERMINAL_COLUMNS = 80;
|
|
2771
3595
|
function InputArea({
|
|
2772
3596
|
onSubmit,
|
|
2773
3597
|
onCancelQueue,
|
|
@@ -2775,13 +3599,24 @@ function InputArea({
|
|
|
2775
3599
|
isAborting,
|
|
2776
3600
|
pendingPrompt,
|
|
2777
3601
|
registry,
|
|
2778
|
-
sessionName
|
|
3602
|
+
sessionName,
|
|
3603
|
+
history
|
|
2779
3604
|
}) {
|
|
2780
|
-
const [value, setValue] =
|
|
2781
|
-
const [cursorHint, setCursorHint] =
|
|
3605
|
+
const [value, setValue] = useState7("");
|
|
3606
|
+
const [cursorHint, setCursorHint] = useState7(null);
|
|
3607
|
+
const [historyState, setHistoryState] = useState7(createPromptHistoryNavigationState);
|
|
3608
|
+
const [localPromptHistory, setLocalPromptHistory] = useState7([]);
|
|
3609
|
+
const restoredPromptHistory = useMemo4(() => extractPromptHistory(history ?? []), [history]);
|
|
3610
|
+
const promptHistory = useMemo4(
|
|
3611
|
+
() => localPromptHistory.reduce(
|
|
3612
|
+
(prompts, prompt) => appendPromptHistory(prompts, prompt),
|
|
3613
|
+
restoredPromptHistory
|
|
3614
|
+
),
|
|
3615
|
+
[restoredPromptHistory, localPromptHistory]
|
|
3616
|
+
);
|
|
2782
3617
|
const pasteStore = useRef4(/* @__PURE__ */ new Map());
|
|
2783
|
-
const {
|
|
2784
|
-
const terminalColumns =
|
|
3618
|
+
const { columns } = useWindowSize();
|
|
3619
|
+
const terminalColumns = columns > 0 ? columns : DEFAULT_TERMINAL_COLUMNS;
|
|
2785
3620
|
const availableWidth = Math.max(1, terminalColumns - INPUT_AREA_OVERHEAD);
|
|
2786
3621
|
const pasteIdRef = useRef4(0);
|
|
2787
3622
|
const {
|
|
@@ -2802,6 +3637,20 @@ function InputArea({
|
|
|
2802
3637
|
return change.value;
|
|
2803
3638
|
});
|
|
2804
3639
|
}, []);
|
|
3640
|
+
const resetHistoryNavigation = useCallback4(() => {
|
|
3641
|
+
setHistoryState(createPromptHistoryNavigationState());
|
|
3642
|
+
}, []);
|
|
3643
|
+
const recordPromptHistory = useCallback4((prompt) => {
|
|
3644
|
+
setLocalPromptHistory((prev) => appendPromptHistory(prev, prompt));
|
|
3645
|
+
}, []);
|
|
3646
|
+
const submitPrompt = useCallback4(
|
|
3647
|
+
(prompt) => {
|
|
3648
|
+
recordPromptHistory(prompt);
|
|
3649
|
+
resetHistoryNavigation();
|
|
3650
|
+
onSubmit(prompt);
|
|
3651
|
+
},
|
|
3652
|
+
[onSubmit, recordPromptHistory, resetHistoryNavigation]
|
|
3653
|
+
);
|
|
2805
3654
|
const tabCompleteCommand = useCallback4(
|
|
2806
3655
|
(cmd) => {
|
|
2807
3656
|
const result = resolveTabCompletion(value, cmd);
|
|
@@ -2825,9 +3674,9 @@ function InputArea({
|
|
|
2825
3674
|
return;
|
|
2826
3675
|
}
|
|
2827
3676
|
setValue("");
|
|
2828
|
-
|
|
3677
|
+
submitPrompt(result.value);
|
|
2829
3678
|
},
|
|
2830
|
-
[value,
|
|
3679
|
+
[value, submitPrompt, setSelectedIndex]
|
|
2831
3680
|
);
|
|
2832
3681
|
const handleSubmit = useCallback4(
|
|
2833
3682
|
(text) => {
|
|
@@ -2840,9 +3689,9 @@ function InputArea({
|
|
|
2840
3689
|
setValue("");
|
|
2841
3690
|
pasteStore.current.clear();
|
|
2842
3691
|
pasteIdRef.current = 0;
|
|
2843
|
-
|
|
3692
|
+
submitPrompt(expanded);
|
|
2844
3693
|
},
|
|
2845
|
-
[showPopup, filteredCommands, selectedIndex,
|
|
3694
|
+
[showPopup, filteredCommands, selectedIndex, enterSelectCommand, submitPrompt]
|
|
2846
3695
|
);
|
|
2847
3696
|
useInput2(
|
|
2848
3697
|
(_input, key) => {
|
|
@@ -2861,6 +3710,17 @@ function InputArea({
|
|
|
2861
3710
|
},
|
|
2862
3711
|
{ isActive: showPopup && !isDisabled }
|
|
2863
3712
|
);
|
|
3713
|
+
useInput2(
|
|
3714
|
+
(_input, key) => {
|
|
3715
|
+
const action = getPromptHistoryInputAction(key);
|
|
3716
|
+
if (!action) return;
|
|
3717
|
+
const result = navigatePromptHistory(value, promptHistory, historyState, action);
|
|
3718
|
+
setValue(result.value);
|
|
3719
|
+
setCursorHint(result.cursorHint);
|
|
3720
|
+
setHistoryState(result.state);
|
|
3721
|
+
},
|
|
3722
|
+
{ isActive: !showPopup && !isDisabled && !pendingPrompt }
|
|
3723
|
+
);
|
|
2864
3724
|
useInput2(
|
|
2865
3725
|
(_input, key) => {
|
|
2866
3726
|
if (getPendingPromptInputAction(key) === "cancelQueue" && pendingPrompt) {
|
|
@@ -2880,8 +3740,8 @@ function InputArea({
|
|
|
2880
3740
|
}
|
|
2881
3741
|
return { left: "\u250C" + "\u2500".repeat(innerWidth), label: "", right: "\u2510" };
|
|
2882
3742
|
})();
|
|
2883
|
-
return /* @__PURE__ */
|
|
2884
|
-
showPopup && /* @__PURE__ */
|
|
3743
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
3744
|
+
showPopup && /* @__PURE__ */ jsx10(
|
|
2885
3745
|
SlashAutocomplete,
|
|
2886
3746
|
{
|
|
2887
3747
|
commands: filteredCommands,
|
|
@@ -2890,32 +3750,34 @@ function InputArea({
|
|
|
2890
3750
|
isSubcommandMode
|
|
2891
3751
|
}
|
|
2892
3752
|
),
|
|
2893
|
-
/* @__PURE__ */
|
|
3753
|
+
/* @__PURE__ */ jsxs7(Text9, { color: borderColor, children: [
|
|
2894
3754
|
topBorder.left,
|
|
2895
|
-
topBorder.label ? /* @__PURE__ */
|
|
3755
|
+
topBorder.label ? /* @__PURE__ */ jsx10(Text9, { backgroundColor: borderColor, color: "black", bold: true, children: topBorder.label }) : null,
|
|
2896
3756
|
topBorder.right
|
|
2897
3757
|
] }),
|
|
2898
|
-
/* @__PURE__ */
|
|
3758
|
+
/* @__PURE__ */ jsx10(Box7, { borderStyle: "single", borderTop: false, borderColor, paddingLeft: 1, children: isAborting ? /* @__PURE__ */ jsx10(Text9, { color: "yellow", children: " Interrupting..." }) : pendingPrompt ? /* @__PURE__ */ jsxs7(Text9, { color: "cyan", children: [
|
|
2899
3759
|
" ",
|
|
2900
3760
|
"Queued: ",
|
|
2901
3761
|
pendingPrompt.length > 50 ? pendingPrompt.slice(0, 47) + "..." : pendingPrompt,
|
|
2902
3762
|
" ",
|
|
2903
|
-
/* @__PURE__ */
|
|
2904
|
-
] }) : isDisabled ? /* @__PURE__ */
|
|
2905
|
-
/* @__PURE__ */
|
|
2906
|
-
/* @__PURE__ */
|
|
3763
|
+
/* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "(Backspace to cancel)" })
|
|
3764
|
+
] }) : isDisabled ? /* @__PURE__ */ jsx10(WaveText, { text: " Waiting for response... (ESC to interrupt)" }) : /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
3765
|
+
/* @__PURE__ */ jsx10(Text9, { color: "green", bold: true, children: "> " }),
|
|
3766
|
+
/* @__PURE__ */ jsx10(
|
|
2907
3767
|
CjkTextInput,
|
|
2908
3768
|
{
|
|
2909
3769
|
value,
|
|
2910
3770
|
onChange: (v) => {
|
|
2911
3771
|
setValue(v);
|
|
3772
|
+
resetHistoryNavigation();
|
|
2912
3773
|
setCursorHint(null);
|
|
2913
3774
|
},
|
|
2914
3775
|
onSubmit: handleSubmit,
|
|
2915
3776
|
onPaste: handlePaste,
|
|
2916
3777
|
placeholder: "Type a message or /help",
|
|
2917
3778
|
availableWidth,
|
|
2918
|
-
cursorHint
|
|
3779
|
+
cursorHint,
|
|
3780
|
+
enableVerticalNavigation: false
|
|
2919
3781
|
}
|
|
2920
3782
|
)
|
|
2921
3783
|
] }) })
|
|
@@ -2923,8 +3785,8 @@ function InputArea({
|
|
|
2923
3785
|
}
|
|
2924
3786
|
|
|
2925
3787
|
// src/ui/ConfirmPrompt.tsx
|
|
2926
|
-
import { useState as
|
|
2927
|
-
import { Box as
|
|
3788
|
+
import { useState as useState8, useRef as useRef5, useCallback as useCallback5 } from "react";
|
|
3789
|
+
import { Box as Box8, Text as Text10, useInput as useInput3 } from "ink";
|
|
2928
3790
|
|
|
2929
3791
|
// src/ui/flows/selection-flow.ts
|
|
2930
3792
|
function createSelectionFlowState() {
|
|
@@ -3027,13 +3889,13 @@ function applyConfirmPromptInput(state, action, optionCount) {
|
|
|
3027
3889
|
}
|
|
3028
3890
|
|
|
3029
3891
|
// src/ui/ConfirmPrompt.tsx
|
|
3030
|
-
import { jsx as
|
|
3892
|
+
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3031
3893
|
function ConfirmPrompt({
|
|
3032
3894
|
message,
|
|
3033
3895
|
options = ["Yes", "No"],
|
|
3034
3896
|
onSelect
|
|
3035
3897
|
}) {
|
|
3036
|
-
const [state, setState] =
|
|
3898
|
+
const [state, setState] = useState8(() => createSelectionFlowState());
|
|
3037
3899
|
const stateRef = useRef5(state);
|
|
3038
3900
|
const applyAction = useCallback5(
|
|
3039
3901
|
(action) => {
|
|
@@ -3052,10 +3914,10 @@ function ConfirmPrompt({
|
|
|
3052
3914
|
applyAction(action);
|
|
3053
3915
|
}
|
|
3054
3916
|
});
|
|
3055
|
-
return /* @__PURE__ */
|
|
3056
|
-
/* @__PURE__ */
|
|
3057
|
-
/* @__PURE__ */
|
|
3058
|
-
|
|
3917
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
3918
|
+
/* @__PURE__ */ jsx11(Text10, { color: "yellow", children: message }),
|
|
3919
|
+
/* @__PURE__ */ jsx11(Box8, { marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ jsx11(Box8, { marginRight: 2, children: /* @__PURE__ */ jsxs8(
|
|
3920
|
+
Text10,
|
|
3059
3921
|
{
|
|
3060
3922
|
color: i === state.selectedIndex ? "cyan" : void 0,
|
|
3061
3923
|
bold: i === state.selectedIndex,
|
|
@@ -3065,16 +3927,81 @@ function ConfirmPrompt({
|
|
|
3065
3927
|
]
|
|
3066
3928
|
}
|
|
3067
3929
|
) }, opt)) }),
|
|
3068
|
-
/* @__PURE__ */
|
|
3930
|
+
/* @__PURE__ */ jsx11(Text10, { dimColor: true, children: " arrow keys to select, Enter to confirm" })
|
|
3069
3931
|
] });
|
|
3070
3932
|
}
|
|
3071
3933
|
|
|
3072
|
-
// src/ui/
|
|
3073
|
-
import {
|
|
3934
|
+
// src/ui/InteractivePrompt.tsx
|
|
3935
|
+
import { Box as Box11, Text as Text13 } from "ink";
|
|
3936
|
+
|
|
3937
|
+
// src/ui/ListPicker.tsx
|
|
3938
|
+
import { useState as useState9, useRef as useRef6, useCallback as useCallback6 } from "react";
|
|
3939
|
+
import { Box as Box9, Text as Text11, useInput as useInput4 } from "ink";
|
|
3940
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
3941
|
+
var DEFAULT_MAX_VISIBLE = 3;
|
|
3942
|
+
function ListPicker({
|
|
3943
|
+
items,
|
|
3944
|
+
renderItem,
|
|
3945
|
+
onSelect,
|
|
3946
|
+
onCancel,
|
|
3947
|
+
maxVisible = DEFAULT_MAX_VISIBLE
|
|
3948
|
+
}) {
|
|
3949
|
+
const [state, setState] = useState9(() => createSelectionFlowState());
|
|
3950
|
+
const stateRef = useRef6(state);
|
|
3951
|
+
const applyAction = useCallback6(
|
|
3952
|
+
(action) => {
|
|
3953
|
+
const result = applySelectionInput(stateRef.current, action, {
|
|
3954
|
+
itemCount: items.length,
|
|
3955
|
+
maxVisible
|
|
3956
|
+
});
|
|
3957
|
+
stateRef.current = result.state;
|
|
3958
|
+
setState(result.state);
|
|
3959
|
+
if (result.effect.type === "cancel") {
|
|
3960
|
+
onCancel();
|
|
3961
|
+
} else if (result.effect.type === "select") {
|
|
3962
|
+
const item = items[result.effect.index];
|
|
3963
|
+
if (item !== void 0) {
|
|
3964
|
+
onSelect(item);
|
|
3965
|
+
}
|
|
3966
|
+
}
|
|
3967
|
+
},
|
|
3968
|
+
[items, maxVisible, onCancel, onSelect]
|
|
3969
|
+
);
|
|
3970
|
+
useInput4((_input, key) => {
|
|
3971
|
+
const action = getVerticalSelectionInputAction(key);
|
|
3972
|
+
if (action !== void 0) {
|
|
3973
|
+
applyAction(action);
|
|
3974
|
+
}
|
|
3975
|
+
});
|
|
3976
|
+
if (items.length === 0) {
|
|
3977
|
+
return /* @__PURE__ */ jsx12(Box9, {});
|
|
3978
|
+
}
|
|
3979
|
+
const normalizedState = normalizeSelectionState(state, { itemCount: items.length, maxVisible });
|
|
3980
|
+
if (normalizedState !== state) {
|
|
3981
|
+
stateRef.current = normalizedState;
|
|
3982
|
+
}
|
|
3983
|
+
const { selectedIndex, scrollOffset } = normalizedState;
|
|
3984
|
+
const visibleItems = items.slice(scrollOffset, scrollOffset + maxVisible);
|
|
3985
|
+
const hasMore = scrollOffset + maxVisible < items.length;
|
|
3986
|
+
const hasLess = scrollOffset > 0;
|
|
3987
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
3988
|
+
hasLess && /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
|
|
3989
|
+
" \u2191 ",
|
|
3990
|
+
scrollOffset,
|
|
3991
|
+
" more above"
|
|
3992
|
+
] }),
|
|
3993
|
+
visibleItems.map((item, index) => /* @__PURE__ */ jsx12(Box9, { marginBottom: 1, children: renderItem(item, scrollOffset + index === selectedIndex) }, scrollOffset + index)),
|
|
3994
|
+
hasMore && /* @__PURE__ */ jsxs9(Text11, { dimColor: true, children: [
|
|
3995
|
+
" \u2193 ",
|
|
3996
|
+
items.length - scrollOffset - maxVisible,
|
|
3997
|
+
" more below"
|
|
3998
|
+
] })
|
|
3999
|
+
] });
|
|
4000
|
+
}
|
|
3074
4001
|
|
|
3075
4002
|
// src/ui/TextPrompt.tsx
|
|
3076
|
-
import { useState as
|
|
3077
|
-
import { Box as
|
|
4003
|
+
import { useState as useState10, useRef as useRef7, useCallback as useCallback7 } from "react";
|
|
4004
|
+
import { Box as Box10, Text as Text12, useInput as useInput5 } from "ink";
|
|
3078
4005
|
|
|
3079
4006
|
// src/ui/flows/text-prompt-flow.ts
|
|
3080
4007
|
function createTextPromptFlowState() {
|
|
@@ -3133,7 +4060,7 @@ function submitTextPromptValue(state, options) {
|
|
|
3133
4060
|
}
|
|
3134
4061
|
|
|
3135
4062
|
// src/ui/TextPrompt.tsx
|
|
3136
|
-
import { jsx as
|
|
4063
|
+
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3137
4064
|
function TextPrompt({
|
|
3138
4065
|
title,
|
|
3139
4066
|
placeholder,
|
|
@@ -3143,9 +4070,9 @@ function TextPrompt({
|
|
|
3143
4070
|
allowEmpty = false,
|
|
3144
4071
|
masked = false
|
|
3145
4072
|
}) {
|
|
3146
|
-
const [state, setState] =
|
|
3147
|
-
const stateRef =
|
|
3148
|
-
const applyAction =
|
|
4073
|
+
const [state, setState] = useState10(() => createTextPromptFlowState());
|
|
4074
|
+
const stateRef = useRef7(state);
|
|
4075
|
+
const applyAction = useCallback7(
|
|
3149
4076
|
(action) => {
|
|
3150
4077
|
const result = applyTextPromptInput(stateRef.current, action, { allowEmpty, validate });
|
|
3151
4078
|
stateRef.current = result.state;
|
|
@@ -3158,64 +4085,67 @@ function TextPrompt({
|
|
|
3158
4085
|
},
|
|
3159
4086
|
[allowEmpty, validate, onCancel, onSubmit]
|
|
3160
4087
|
);
|
|
3161
|
-
|
|
4088
|
+
useInput5((input, key) => {
|
|
3162
4089
|
const action = getTextPromptInputAction(input, key);
|
|
3163
4090
|
if (action !== void 0) {
|
|
3164
4091
|
applyAction(action);
|
|
3165
4092
|
}
|
|
3166
4093
|
});
|
|
3167
|
-
return /* @__PURE__ */
|
|
3168
|
-
/* @__PURE__ */
|
|
3169
|
-
/* @__PURE__ */
|
|
3170
|
-
/* @__PURE__ */
|
|
3171
|
-
state.value ? /* @__PURE__ */
|
|
3172
|
-
/* @__PURE__ */
|
|
4094
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
4095
|
+
/* @__PURE__ */ jsx13(Text12, { color: "yellow", bold: true, children: title }),
|
|
4096
|
+
/* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
|
|
4097
|
+
/* @__PURE__ */ jsx13(Text12, { color: "cyan", children: "> " }),
|
|
4098
|
+
state.value ? /* @__PURE__ */ jsx13(Text12, { children: masked ? "*".repeat(state.value.length) : state.value }) : placeholder ? /* @__PURE__ */ jsx13(Text12, { dimColor: true, children: placeholder }) : null,
|
|
4099
|
+
/* @__PURE__ */ jsx13(Text12, { color: "cyan", children: "\u2588" })
|
|
3173
4100
|
] }),
|
|
3174
|
-
state.error && /* @__PURE__ */
|
|
3175
|
-
/* @__PURE__ */
|
|
4101
|
+
state.error && /* @__PURE__ */ jsx13(Text12, { color: "red", children: state.error }),
|
|
4102
|
+
/* @__PURE__ */ jsx13(Text12, { dimColor: true, children: " Enter Submit Esc Cancel" })
|
|
3176
4103
|
] });
|
|
3177
4104
|
}
|
|
3178
4105
|
|
|
3179
|
-
// src/ui/
|
|
3180
|
-
import { jsx as
|
|
3181
|
-
function
|
|
3182
|
-
|
|
3183
|
-
providerDefinitions,
|
|
4106
|
+
// src/ui/InteractivePrompt.tsx
|
|
4107
|
+
import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
4108
|
+
function InteractivePrompt({
|
|
4109
|
+
prompt,
|
|
3184
4110
|
onSubmit,
|
|
3185
4111
|
onCancel
|
|
3186
4112
|
}) {
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
{
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
4113
|
+
if (prompt.kind === "text") {
|
|
4114
|
+
return /* @__PURE__ */ jsx14(
|
|
4115
|
+
TextPrompt,
|
|
4116
|
+
{
|
|
4117
|
+
title: prompt.title,
|
|
4118
|
+
placeholder: prompt.placeholder,
|
|
4119
|
+
allowEmpty: prompt.allowEmpty,
|
|
4120
|
+
masked: prompt.masked,
|
|
4121
|
+
validate: prompt.validate,
|
|
4122
|
+
onSubmit,
|
|
4123
|
+
onCancel
|
|
4124
|
+
},
|
|
4125
|
+
`text:${prompt.title}`
|
|
4126
|
+
);
|
|
4127
|
+
}
|
|
4128
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
4129
|
+
/* @__PURE__ */ jsx14(Text13, { bold: true, children: prompt.title }),
|
|
4130
|
+
/* @__PURE__ */ jsx14(
|
|
4131
|
+
ListPicker,
|
|
4132
|
+
{
|
|
4133
|
+
items: [...prompt.options],
|
|
4134
|
+
maxVisible: prompt.maxVisible,
|
|
4135
|
+
renderItem: (option, isSelected) => /* @__PURE__ */ jsxs11(Text13, { color: isSelected ? "cyan" : void 0, children: [
|
|
4136
|
+
isSelected ? "> " : " ",
|
|
4137
|
+
option.label
|
|
4138
|
+
] }),
|
|
4139
|
+
onSelect: (option) => onSubmit(option.value),
|
|
4140
|
+
onCancel
|
|
4141
|
+
}
|
|
4142
|
+
)
|
|
4143
|
+
] });
|
|
3214
4144
|
}
|
|
3215
4145
|
|
|
3216
4146
|
// src/ui/PermissionPrompt.tsx
|
|
3217
|
-
import
|
|
3218
|
-
import { Box as
|
|
4147
|
+
import React10 from "react";
|
|
4148
|
+
import { Box as Box12, Text as Text14, useInput as useInput6 } from "ink";
|
|
3219
4149
|
|
|
3220
4150
|
// src/ui/flows/permission-prompt-flow.ts
|
|
3221
4151
|
var PERMISSION_PROMPT_OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
|
|
@@ -3266,23 +4196,23 @@ function resolvePermissionIndex(state, index) {
|
|
|
3266
4196
|
}
|
|
3267
4197
|
|
|
3268
4198
|
// src/ui/PermissionPrompt.tsx
|
|
3269
|
-
import { jsx as
|
|
4199
|
+
import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3270
4200
|
function formatArgs(args) {
|
|
3271
4201
|
const entries = Object.entries(args);
|
|
3272
4202
|
if (entries.length === 0) return "(no arguments)";
|
|
3273
4203
|
return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
3274
4204
|
}
|
|
3275
4205
|
function PermissionPrompt({ request }) {
|
|
3276
|
-
const [state, setState] =
|
|
3277
|
-
const stateRef =
|
|
3278
|
-
const prevRequestRef =
|
|
4206
|
+
const [state, setState] = React10.useState(() => createSelectionFlowState());
|
|
4207
|
+
const stateRef = React10.useRef(state);
|
|
4208
|
+
const prevRequestRef = React10.useRef(request);
|
|
3279
4209
|
if (prevRequestRef.current !== request) {
|
|
3280
4210
|
prevRequestRef.current = request;
|
|
3281
4211
|
const nextState = createSelectionFlowState();
|
|
3282
4212
|
stateRef.current = nextState;
|
|
3283
4213
|
setState(nextState);
|
|
3284
4214
|
}
|
|
3285
|
-
const applyAction =
|
|
4215
|
+
const applyAction = React10.useCallback(
|
|
3286
4216
|
(action) => {
|
|
3287
4217
|
const result = applyPermissionPromptInput(stateRef.current, action);
|
|
3288
4218
|
stateRef.current = result.state;
|
|
@@ -3293,25 +4223,25 @@ function PermissionPrompt({ request }) {
|
|
|
3293
4223
|
},
|
|
3294
4224
|
[request]
|
|
3295
4225
|
);
|
|
3296
|
-
|
|
4226
|
+
useInput6((input, key) => {
|
|
3297
4227
|
const action = getPermissionPromptInputAction(input, key);
|
|
3298
4228
|
if (action !== void 0) {
|
|
3299
4229
|
applyAction(action);
|
|
3300
4230
|
}
|
|
3301
4231
|
});
|
|
3302
|
-
return /* @__PURE__ */
|
|
3303
|
-
/* @__PURE__ */
|
|
3304
|
-
/* @__PURE__ */
|
|
4232
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
4233
|
+
/* @__PURE__ */ jsx15(Text14, { color: "yellow", bold: true, children: "[Permission Required]" }),
|
|
4234
|
+
/* @__PURE__ */ jsxs12(Text14, { children: [
|
|
3305
4235
|
"Tool:",
|
|
3306
4236
|
" ",
|
|
3307
|
-
/* @__PURE__ */
|
|
4237
|
+
/* @__PURE__ */ jsx15(Text14, { color: "cyan", bold: true, children: request.toolName })
|
|
3308
4238
|
] }),
|
|
3309
|
-
/* @__PURE__ */
|
|
4239
|
+
/* @__PURE__ */ jsxs12(Text14, { dimColor: true, children: [
|
|
3310
4240
|
" ",
|
|
3311
4241
|
formatArgs(request.toolArgs)
|
|
3312
4242
|
] }),
|
|
3313
|
-
/* @__PURE__ */
|
|
3314
|
-
|
|
4243
|
+
/* @__PURE__ */ jsx15(Box12, { marginTop: 1, children: PERMISSION_PROMPT_OPTIONS.map((opt, i) => /* @__PURE__ */ jsx15(Box12, { marginRight: 2, children: /* @__PURE__ */ jsxs12(
|
|
4244
|
+
Text14,
|
|
3315
4245
|
{
|
|
3316
4246
|
color: i === state.selectedIndex ? "cyan" : void 0,
|
|
3317
4247
|
bold: i === state.selectedIndex,
|
|
@@ -3321,13 +4251,13 @@ function PermissionPrompt({ request }) {
|
|
|
3321
4251
|
]
|
|
3322
4252
|
}
|
|
3323
4253
|
) }, opt)) }),
|
|
3324
|
-
/* @__PURE__ */
|
|
4254
|
+
/* @__PURE__ */ jsx15(Text14, { dimColor: true, children: " left/right to select, Enter to confirm" })
|
|
3325
4255
|
] });
|
|
3326
4256
|
}
|
|
3327
4257
|
|
|
3328
4258
|
// src/ui/StreamingIndicator.tsx
|
|
3329
|
-
import { Box as
|
|
3330
|
-
import { Fragment as
|
|
4259
|
+
import { Box as Box13, Text as Text15 } from "ink";
|
|
4260
|
+
import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3331
4261
|
function getToolStyle(t) {
|
|
3332
4262
|
if (t.isRunning) return { color: "yellow", icon: "\u27F3", strikethrough: false };
|
|
3333
4263
|
if (t.result === "error") return { color: "red", icon: "\u2717", strikethrough: true };
|
|
@@ -3338,16 +4268,16 @@ function StreamingIndicator({ text, activeTools }) {
|
|
|
3338
4268
|
const hasTools = activeTools.length > 0;
|
|
3339
4269
|
const hasText = text.length > 0;
|
|
3340
4270
|
if (!hasTools && !hasText) {
|
|
3341
|
-
return /* @__PURE__ */
|
|
4271
|
+
return /* @__PURE__ */ jsx16(Fragment4, {});
|
|
3342
4272
|
}
|
|
3343
|
-
return /* @__PURE__ */
|
|
3344
|
-
hasTools && /* @__PURE__ */
|
|
3345
|
-
/* @__PURE__ */
|
|
3346
|
-
/* @__PURE__ */
|
|
4273
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
4274
|
+
hasTools && /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginBottom: 1, children: [
|
|
4275
|
+
/* @__PURE__ */ jsx16(Text15, { color: "white", bold: true, children: "Tools:" }),
|
|
4276
|
+
/* @__PURE__ */ jsx16(Text15, { children: " " }),
|
|
3347
4277
|
activeTools.map((t, i) => {
|
|
3348
4278
|
const { color, icon, strikethrough } = getToolStyle(t);
|
|
3349
|
-
return /* @__PURE__ */
|
|
3350
|
-
/* @__PURE__ */
|
|
4279
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
4280
|
+
/* @__PURE__ */ jsxs13(Text15, { color, strikethrough, children: [
|
|
3351
4281
|
" ",
|
|
3352
4282
|
icon,
|
|
3353
4283
|
" ",
|
|
@@ -3356,25 +4286,25 @@ function StreamingIndicator({ text, activeTools }) {
|
|
|
3356
4286
|
t.firstArg,
|
|
3357
4287
|
")"
|
|
3358
4288
|
] }),
|
|
3359
|
-
t.diffLines && t.diffLines.length > 0 && /* @__PURE__ */
|
|
4289
|
+
t.diffLines && t.diffLines.length > 0 && /* @__PURE__ */ jsx16(ToolDiffBlock, { file: t.diffFile, lines: t.diffLines })
|
|
3360
4290
|
] }, `${t.toolName}-${i}`);
|
|
3361
4291
|
})
|
|
3362
4292
|
] }),
|
|
3363
|
-
hasText && /* @__PURE__ */
|
|
3364
|
-
/* @__PURE__ */
|
|
3365
|
-
/* @__PURE__ */
|
|
3366
|
-
/* @__PURE__ */
|
|
4293
|
+
hasText && /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginBottom: 1, children: [
|
|
4294
|
+
/* @__PURE__ */ jsx16(Text15, { color: "cyan", bold: true, children: "Robota:" }),
|
|
4295
|
+
/* @__PURE__ */ jsx16(Text15, { children: " " }),
|
|
4296
|
+
/* @__PURE__ */ jsx16(Box13, { marginLeft: 2, children: /* @__PURE__ */ jsx16(Text15, { wrap: "wrap", children: renderMarkdown(text) }) })
|
|
3367
4297
|
] })
|
|
3368
4298
|
] });
|
|
3369
4299
|
}
|
|
3370
4300
|
|
|
3371
4301
|
// src/ui/PluginTUI.tsx
|
|
3372
|
-
import { useState as
|
|
4302
|
+
import { useState as useState13, useCallback as useCallback9 } from "react";
|
|
3373
4303
|
|
|
3374
4304
|
// src/ui/MenuSelect.tsx
|
|
3375
|
-
import { useState as
|
|
3376
|
-
import { Box as
|
|
3377
|
-
import { jsx as
|
|
4305
|
+
import { useState as useState11, useCallback as useCallback8, useRef as useRef8 } from "react";
|
|
4306
|
+
import { Box as Box14, Text as Text16, useInput as useInput7 } from "ink";
|
|
4307
|
+
import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3378
4308
|
function MenuSelect({
|
|
3379
4309
|
title,
|
|
3380
4310
|
items,
|
|
@@ -3383,10 +4313,10 @@ function MenuSelect({
|
|
|
3383
4313
|
loading,
|
|
3384
4314
|
error
|
|
3385
4315
|
}) {
|
|
3386
|
-
const [state, setState] =
|
|
3387
|
-
const stateRef =
|
|
4316
|
+
const [state, setState] = useState11(() => createSelectionFlowState());
|
|
4317
|
+
const stateRef = useRef8(state);
|
|
3388
4318
|
const isEnabled = !loading && !error;
|
|
3389
|
-
const applyAction =
|
|
4319
|
+
const applyAction = useCallback8(
|
|
3390
4320
|
(action) => {
|
|
3391
4321
|
const result = applySelectionInput(stateRef.current, action, {
|
|
3392
4322
|
itemCount: items.length,
|
|
@@ -3405,7 +4335,7 @@ function MenuSelect({
|
|
|
3405
4335
|
},
|
|
3406
4336
|
[isEnabled, items, onBack, onSelect]
|
|
3407
4337
|
);
|
|
3408
|
-
|
|
4338
|
+
useInput7((input, key) => {
|
|
3409
4339
|
const action = getVerticalSelectionInputAction(key);
|
|
3410
4340
|
if (action !== void 0) {
|
|
3411
4341
|
applyAction(action);
|
|
@@ -3416,24 +4346,24 @@ function MenuSelect({
|
|
|
3416
4346
|
stateRef.current = normalizedState;
|
|
3417
4347
|
}
|
|
3418
4348
|
const selected = normalizedState.selectedIndex;
|
|
3419
|
-
return /* @__PURE__ */
|
|
3420
|
-
/* @__PURE__ */
|
|
3421
|
-
loading && /* @__PURE__ */
|
|
3422
|
-
error && /* @__PURE__ */
|
|
3423
|
-
/* @__PURE__ */
|
|
3424
|
-
/* @__PURE__ */
|
|
4349
|
+
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
4350
|
+
/* @__PURE__ */ jsx17(Text16, { color: "yellow", bold: true, children: title }),
|
|
4351
|
+
loading && /* @__PURE__ */ jsx17(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text16, { dimColor: true, children: "Loading..." }) }),
|
|
4352
|
+
error && /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, flexDirection: "column", children: [
|
|
4353
|
+
/* @__PURE__ */ jsx17(Text16, { color: "red", children: error }),
|
|
4354
|
+
/* @__PURE__ */ jsx17(Text16, { dimColor: true, children: "Press Esc to go back" })
|
|
3425
4355
|
] }),
|
|
3426
|
-
!loading && !error && /* @__PURE__ */
|
|
3427
|
-
/* @__PURE__ */
|
|
4356
|
+
!loading && !error && /* @__PURE__ */ jsx17(Box14, { flexDirection: "column", marginTop: 1, children: items.map((item, i) => /* @__PURE__ */ jsxs14(Box14, { children: [
|
|
4357
|
+
/* @__PURE__ */ jsxs14(Text16, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
|
|
3428
4358
|
i === selected ? "> " : " ",
|
|
3429
4359
|
item.label
|
|
3430
4360
|
] }),
|
|
3431
|
-
item.hint && /* @__PURE__ */
|
|
4361
|
+
item.hint && /* @__PURE__ */ jsxs14(Text16, { dimColor: true, children: [
|
|
3432
4362
|
" ",
|
|
3433
4363
|
item.hint
|
|
3434
4364
|
] })
|
|
3435
4365
|
] }, item.value)) }),
|
|
3436
|
-
/* @__PURE__ */
|
|
4366
|
+
/* @__PURE__ */ jsx17(Text16, { dimColor: true, children: loading || error ? "" : " \u2191\u2193 Navigate Enter Select Esc Back" })
|
|
3437
4367
|
] });
|
|
3438
4368
|
}
|
|
3439
4369
|
|
|
@@ -3530,11 +4460,11 @@ function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
|
|
|
3530
4460
|
}
|
|
3531
4461
|
|
|
3532
4462
|
// src/ui/hooks/usePluginScreenData.ts
|
|
3533
|
-
import { useState as
|
|
4463
|
+
import { useState as useState12, useEffect as useEffect3 } from "react";
|
|
3534
4464
|
function usePluginScreenData(screen, marketplace, callbacks, refreshCounter, stackLength) {
|
|
3535
|
-
const [items, setItems] =
|
|
3536
|
-
const [loading, setLoading] =
|
|
3537
|
-
const [error, setError] =
|
|
4465
|
+
const [items, setItems] = useState12([]);
|
|
4466
|
+
const [loading, setLoading] = useState12(false);
|
|
4467
|
+
const [error, setError] = useState12();
|
|
3538
4468
|
useEffect3(() => {
|
|
3539
4469
|
setItems([]);
|
|
3540
4470
|
setError(void 0);
|
|
@@ -3590,16 +4520,16 @@ function usePluginScreenData(screen, marketplace, callbacks, refreshCounter, sta
|
|
|
3590
4520
|
}
|
|
3591
4521
|
|
|
3592
4522
|
// src/ui/PluginTUI.tsx
|
|
3593
|
-
import { jsx as
|
|
4523
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
3594
4524
|
function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
3595
|
-
const [stack, setStack] =
|
|
3596
|
-
const [confirm, setConfirm] =
|
|
3597
|
-
const [refreshCounter, setRefreshCounter] =
|
|
4525
|
+
const [stack, setStack] = useState13([{ screen: "main" }]);
|
|
4526
|
+
const [confirm, setConfirm] = useState13();
|
|
4527
|
+
const [refreshCounter, setRefreshCounter] = useState13(0);
|
|
3598
4528
|
const current = stack[stack.length - 1] ?? { screen: "main" };
|
|
3599
|
-
const push =
|
|
4529
|
+
const push = useCallback9((state) => {
|
|
3600
4530
|
setStack((prev) => [...prev, state]);
|
|
3601
4531
|
}, []);
|
|
3602
|
-
const pop =
|
|
4532
|
+
const pop = useCallback9(() => {
|
|
3603
4533
|
setStack((prev) => {
|
|
3604
4534
|
if (prev.length <= 1) {
|
|
3605
4535
|
onClose();
|
|
@@ -3608,7 +4538,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
3608
4538
|
return prev.slice(0, -1);
|
|
3609
4539
|
});
|
|
3610
4540
|
}, [onClose]);
|
|
3611
|
-
const popN =
|
|
4541
|
+
const popN = useCallback9(
|
|
3612
4542
|
(n) => {
|
|
3613
4543
|
setStack((prev) => {
|
|
3614
4544
|
const next = prev.slice(0, Math.max(1, prev.length - n));
|
|
@@ -3621,20 +4551,20 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
3621
4551
|
},
|
|
3622
4552
|
[onClose]
|
|
3623
4553
|
);
|
|
3624
|
-
const notify =
|
|
4554
|
+
const notify = useCallback9(
|
|
3625
4555
|
(content) => {
|
|
3626
4556
|
addMessage?.({ role: "system", content });
|
|
3627
4557
|
},
|
|
3628
4558
|
[addMessage]
|
|
3629
4559
|
);
|
|
3630
|
-
const refresh =
|
|
4560
|
+
const refresh = useCallback9(() => {
|
|
3631
4561
|
setRefreshCounter((c) => c + 1);
|
|
3632
4562
|
}, []);
|
|
3633
|
-
const setConfirmNav =
|
|
4563
|
+
const setConfirmNav = useCallback9(
|
|
3634
4564
|
(state) => setConfirm(state),
|
|
3635
4565
|
[setConfirm]
|
|
3636
4566
|
);
|
|
3637
|
-
const pushNav =
|
|
4567
|
+
const pushNav = useCallback9(
|
|
3638
4568
|
(state) => push({ screen: state.screen, context: state.context }),
|
|
3639
4569
|
[push]
|
|
3640
4570
|
);
|
|
@@ -3646,7 +4576,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
3646
4576
|
refreshCounter,
|
|
3647
4577
|
stack.length
|
|
3648
4578
|
);
|
|
3649
|
-
const handleSelect =
|
|
4579
|
+
const handleSelect = useCallback9(
|
|
3650
4580
|
(value) => {
|
|
3651
4581
|
const screen2 = current.screen;
|
|
3652
4582
|
const ctx = current.context;
|
|
@@ -3664,7 +4594,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
3664
4594
|
},
|
|
3665
4595
|
[current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
|
|
3666
4596
|
);
|
|
3667
|
-
const handleTextSubmit =
|
|
4597
|
+
const handleTextSubmit = useCallback9(
|
|
3668
4598
|
(value) => {
|
|
3669
4599
|
if (current.screen === "marketplace-add") {
|
|
3670
4600
|
callbacks.marketplaceAdd(value).then((name) => {
|
|
@@ -3679,7 +4609,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
3679
4609
|
[current.screen, callbacks, notify, pop]
|
|
3680
4610
|
);
|
|
3681
4611
|
if (confirm) {
|
|
3682
|
-
return /* @__PURE__ */
|
|
4612
|
+
return /* @__PURE__ */ jsx18(
|
|
3683
4613
|
ConfirmPrompt,
|
|
3684
4614
|
{
|
|
3685
4615
|
message: confirm.message,
|
|
@@ -3692,7 +4622,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
3692
4622
|
}
|
|
3693
4623
|
const screen = current.screen;
|
|
3694
4624
|
if (screen === "marketplace-add") {
|
|
3695
|
-
return /* @__PURE__ */
|
|
4625
|
+
return /* @__PURE__ */ jsx18(
|
|
3696
4626
|
TextPrompt,
|
|
3697
4627
|
{
|
|
3698
4628
|
title: "Add Marketplace Source",
|
|
@@ -3704,7 +4634,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
3704
4634
|
);
|
|
3705
4635
|
}
|
|
3706
4636
|
if (screen === "marketplace-action") {
|
|
3707
|
-
return /* @__PURE__ */
|
|
4637
|
+
return /* @__PURE__ */ jsx18(
|
|
3708
4638
|
MenuSelect,
|
|
3709
4639
|
{
|
|
3710
4640
|
title: `Marketplace: ${current.context?.marketplace ?? ""}`,
|
|
@@ -3720,7 +4650,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
3720
4650
|
);
|
|
3721
4651
|
}
|
|
3722
4652
|
if (screen === "marketplace-install-scope") {
|
|
3723
|
-
return /* @__PURE__ */
|
|
4653
|
+
return /* @__PURE__ */ jsx18(
|
|
3724
4654
|
MenuSelect,
|
|
3725
4655
|
{
|
|
3726
4656
|
title: `Install scope for "${current.context?.pluginId ?? ""}"`,
|
|
@@ -3735,7 +4665,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
3735
4665
|
);
|
|
3736
4666
|
}
|
|
3737
4667
|
if (screen === "installed-action") {
|
|
3738
|
-
return /* @__PURE__ */
|
|
4668
|
+
return /* @__PURE__ */ jsx18(
|
|
3739
4669
|
MenuSelect,
|
|
3740
4670
|
{
|
|
3741
4671
|
title: `Plugin: ${current.context?.pluginId ?? ""}`,
|
|
@@ -3758,7 +4688,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
3758
4688
|
{ label: "Installed Plugins", value: "installed" }
|
|
3759
4689
|
]
|
|
3760
4690
|
};
|
|
3761
|
-
return /* @__PURE__ */
|
|
4691
|
+
return /* @__PURE__ */ jsx18(
|
|
3762
4692
|
MenuSelect,
|
|
3763
4693
|
{
|
|
3764
4694
|
title: titleMap[screen] ?? "Plugin Management",
|
|
@@ -3773,75 +4703,8 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
3773
4703
|
}
|
|
3774
4704
|
|
|
3775
4705
|
// src/ui/SessionPicker.tsx
|
|
3776
|
-
import { Box as
|
|
3777
|
-
|
|
3778
|
-
// src/ui/ListPicker.tsx
|
|
3779
|
-
import { useState as useState13, useRef as useRef8, useCallback as useCallback9 } from "react";
|
|
3780
|
-
import { Box as Box11, Text as Text13, useInput as useInput7 } from "ink";
|
|
3781
|
-
import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3782
|
-
var DEFAULT_MAX_VISIBLE = 3;
|
|
3783
|
-
function ListPicker({
|
|
3784
|
-
items,
|
|
3785
|
-
renderItem,
|
|
3786
|
-
onSelect,
|
|
3787
|
-
onCancel,
|
|
3788
|
-
maxVisible = DEFAULT_MAX_VISIBLE
|
|
3789
|
-
}) {
|
|
3790
|
-
const [state, setState] = useState13(() => createSelectionFlowState());
|
|
3791
|
-
const stateRef = useRef8(state);
|
|
3792
|
-
const applyAction = useCallback9(
|
|
3793
|
-
(action) => {
|
|
3794
|
-
const result = applySelectionInput(stateRef.current, action, {
|
|
3795
|
-
itemCount: items.length,
|
|
3796
|
-
maxVisible
|
|
3797
|
-
});
|
|
3798
|
-
stateRef.current = result.state;
|
|
3799
|
-
setState(result.state);
|
|
3800
|
-
if (result.effect.type === "cancel") {
|
|
3801
|
-
onCancel();
|
|
3802
|
-
} else if (result.effect.type === "select") {
|
|
3803
|
-
const item = items[result.effect.index];
|
|
3804
|
-
if (item !== void 0) {
|
|
3805
|
-
onSelect(item);
|
|
3806
|
-
}
|
|
3807
|
-
}
|
|
3808
|
-
},
|
|
3809
|
-
[items, maxVisible, onCancel, onSelect]
|
|
3810
|
-
);
|
|
3811
|
-
useInput7((_input, key) => {
|
|
3812
|
-
const action = getVerticalSelectionInputAction(key);
|
|
3813
|
-
if (action !== void 0) {
|
|
3814
|
-
applyAction(action);
|
|
3815
|
-
}
|
|
3816
|
-
});
|
|
3817
|
-
if (items.length === 0) {
|
|
3818
|
-
return /* @__PURE__ */ jsx14(Box11, {});
|
|
3819
|
-
}
|
|
3820
|
-
const normalizedState = normalizeSelectionState(state, { itemCount: items.length, maxVisible });
|
|
3821
|
-
if (normalizedState !== state) {
|
|
3822
|
-
stateRef.current = normalizedState;
|
|
3823
|
-
}
|
|
3824
|
-
const { selectedIndex, scrollOffset } = normalizedState;
|
|
3825
|
-
const visibleItems = items.slice(scrollOffset, scrollOffset + maxVisible);
|
|
3826
|
-
const hasMore = scrollOffset + maxVisible < items.length;
|
|
3827
|
-
const hasLess = scrollOffset > 0;
|
|
3828
|
-
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
3829
|
-
hasLess && /* @__PURE__ */ jsxs11(Text13, { dimColor: true, children: [
|
|
3830
|
-
" \u2191 ",
|
|
3831
|
-
scrollOffset,
|
|
3832
|
-
" more above"
|
|
3833
|
-
] }),
|
|
3834
|
-
visibleItems.map((item, index) => /* @__PURE__ */ jsx14(Box11, { marginBottom: 1, children: renderItem(item, scrollOffset + index === selectedIndex) }, scrollOffset + index)),
|
|
3835
|
-
hasMore && /* @__PURE__ */ jsxs11(Text13, { dimColor: true, children: [
|
|
3836
|
-
" \u2193 ",
|
|
3837
|
-
items.length - scrollOffset - maxVisible,
|
|
3838
|
-
" more below"
|
|
3839
|
-
] })
|
|
3840
|
-
] });
|
|
3841
|
-
}
|
|
3842
|
-
|
|
3843
|
-
// src/ui/SessionPicker.tsx
|
|
3844
|
-
import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4706
|
+
import { Box as Box15, Text as Text17 } from "ink";
|
|
4707
|
+
import { Fragment as Fragment5, jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3845
4708
|
var SESSION_ID_DISPLAY_LENGTH = 8;
|
|
3846
4709
|
function SessionPicker({
|
|
3847
4710
|
sessionStore,
|
|
@@ -3850,9 +4713,9 @@ function SessionPicker({
|
|
|
3850
4713
|
onCancel
|
|
3851
4714
|
}) {
|
|
3852
4715
|
const sessions = (sessionStore?.list() ?? []).filter((s) => s.cwd === cwd);
|
|
3853
|
-
return /* @__PURE__ */
|
|
3854
|
-
/* @__PURE__ */
|
|
3855
|
-
/* @__PURE__ */
|
|
4716
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
4717
|
+
/* @__PURE__ */ jsx19(Text17, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
|
|
4718
|
+
/* @__PURE__ */ jsx19(
|
|
3856
4719
|
ListPicker,
|
|
3857
4720
|
{
|
|
3858
4721
|
items: sessions,
|
|
@@ -3863,24 +4726,24 @@ function SessionPicker({
|
|
|
3863
4726
|
});
|
|
3864
4727
|
const rawPreview = lastMsg?.content?.replace(/[\n\r]+/g, " ").trim() ?? "";
|
|
3865
4728
|
const preview = rawPreview ? rawPreview.slice(0, 60) + (rawPreview.length > 60 ? "..." : "") : "";
|
|
3866
|
-
return /* @__PURE__ */
|
|
4729
|
+
return /* @__PURE__ */ jsxs15(Text17, { children: [
|
|
3867
4730
|
isSelected ? "> " : " ",
|
|
3868
|
-
/* @__PURE__ */
|
|
4731
|
+
/* @__PURE__ */ jsx19(Text17, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
|
|
3869
4732
|
" ",
|
|
3870
|
-
/* @__PURE__ */
|
|
4733
|
+
/* @__PURE__ */ jsx19(Text17, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
|
|
3871
4734
|
month: "short",
|
|
3872
4735
|
day: "numeric",
|
|
3873
4736
|
hour: "2-digit",
|
|
3874
4737
|
minute: "2-digit"
|
|
3875
4738
|
}) }),
|
|
3876
4739
|
" ",
|
|
3877
|
-
/* @__PURE__ */
|
|
4740
|
+
/* @__PURE__ */ jsxs15(Text17, { dimColor: true, children: [
|
|
3878
4741
|
"msgs: ",
|
|
3879
4742
|
session.messages.length
|
|
3880
4743
|
] }),
|
|
3881
|
-
preview ? /* @__PURE__ */
|
|
4744
|
+
preview ? /* @__PURE__ */ jsxs15(Fragment5, { children: [
|
|
3882
4745
|
"\n ",
|
|
3883
|
-
/* @__PURE__ */
|
|
4746
|
+
/* @__PURE__ */ jsx19(Text17, { color: "gray", children: preview })
|
|
3884
4747
|
] }) : null
|
|
3885
4748
|
] });
|
|
3886
4749
|
},
|
|
@@ -3892,54 +4755,382 @@ function SessionPicker({
|
|
|
3892
4755
|
}
|
|
3893
4756
|
|
|
3894
4757
|
// src/ui/BackgroundTaskPanel.tsx
|
|
3895
|
-
import { Box as
|
|
3896
|
-
|
|
4758
|
+
import { Box as Box16, Text as Text18 } from "ink";
|
|
4759
|
+
|
|
4760
|
+
// src/ui/background-task-row-format.ts
|
|
3897
4761
|
var MS_PER_SECOND = 1e3;
|
|
3898
4762
|
var SECONDS_PER_MINUTE = 60;
|
|
3899
4763
|
var MINUTES_PER_HOUR = 60;
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
4764
|
+
var SUCCESS_EXIT_CODE3 = 0;
|
|
4765
|
+
function formatBackgroundTaskRow(task, options = {}) {
|
|
4766
|
+
const row = {
|
|
4767
|
+
connector: options.isLast === false ? "\u251C" : "\u2514",
|
|
4768
|
+
marker: getStatusMarker(task),
|
|
4769
|
+
color: getStatusColor(task),
|
|
4770
|
+
label: getTaskLabel(task),
|
|
4771
|
+
segments: getTaskSegments(task, options.now ?? Date.now()),
|
|
4772
|
+
preview: getTaskPreview(task)
|
|
4773
|
+
};
|
|
4774
|
+
return {
|
|
4775
|
+
...row,
|
|
4776
|
+
accessibleText: formatAccessibleText(row)
|
|
4777
|
+
};
|
|
4778
|
+
}
|
|
4779
|
+
function getStatusColor(task) {
|
|
4780
|
+
if (isFailedTask(task)) return "red";
|
|
4781
|
+
if (task.status === "completed") return "green";
|
|
4782
|
+
if (task.status === "cancelled") return "yellow";
|
|
3904
4783
|
return "cyan";
|
|
3905
4784
|
}
|
|
3906
|
-
function getStatusMarker(
|
|
3907
|
-
if (status === "queued" || status === "running") return "\u25A1";
|
|
4785
|
+
function getStatusMarker(task) {
|
|
4786
|
+
if (task.status === "queued" || task.status === "running") return "\u25A1";
|
|
3908
4787
|
return "\u25A0";
|
|
3909
4788
|
}
|
|
4789
|
+
function getTaskLabel(task) {
|
|
4790
|
+
if (task.kind === "agent") return `${task.label} agent`;
|
|
4791
|
+
if (task.kind === "process") return task.label || "Process";
|
|
4792
|
+
return task.label;
|
|
4793
|
+
}
|
|
4794
|
+
function getTaskSegments(task, now) {
|
|
4795
|
+
const segments = [];
|
|
4796
|
+
if (task.status === "running") {
|
|
4797
|
+
const idle = formatAge(task.lastActivityAt, now);
|
|
4798
|
+
if (idle) segments.push(`idle ${idle}`);
|
|
4799
|
+
}
|
|
4800
|
+
if (task.status === "failed") {
|
|
4801
|
+
segments.push(task.statusLabel === "timed out" ? "timed out" : "failed");
|
|
4802
|
+
}
|
|
4803
|
+
if (task.status === "cancelled") {
|
|
4804
|
+
segments.push("cancelled");
|
|
4805
|
+
}
|
|
4806
|
+
if (task.timeoutReason) {
|
|
4807
|
+
segments.push(task.timeoutReason);
|
|
4808
|
+
}
|
|
4809
|
+
if (task.status === "completed" && task.exitCode !== void 0 && task.exitCode !== SUCCESS_EXIT_CODE3) {
|
|
4810
|
+
segments.push(`exit ${task.exitCode}`);
|
|
4811
|
+
}
|
|
4812
|
+
if (task.signalCode) {
|
|
4813
|
+
segments.push(`signal ${task.signalCode}`);
|
|
4814
|
+
}
|
|
4815
|
+
return segments;
|
|
4816
|
+
}
|
|
3910
4817
|
function getTaskPreview(task) {
|
|
3911
|
-
|
|
4818
|
+
const preview = task.errorPreview ?? task.resultPreview ?? task.currentAction ?? task.preview;
|
|
4819
|
+
return preview || void 0;
|
|
3912
4820
|
}
|
|
3913
|
-
function formatAge(iso) {
|
|
4821
|
+
function formatAge(iso, now) {
|
|
3914
4822
|
if (!iso) return void 0;
|
|
3915
4823
|
const timestamp = Date.parse(iso);
|
|
3916
4824
|
if (Number.isNaN(timestamp)) return void 0;
|
|
3917
|
-
const seconds = Math.max(0, Math.floor((
|
|
4825
|
+
const seconds = Math.max(0, Math.floor((now - timestamp) / MS_PER_SECOND));
|
|
3918
4826
|
if (seconds < SECONDS_PER_MINUTE) return `${seconds}s`;
|
|
3919
4827
|
const minutes = Math.floor(seconds / SECONDS_PER_MINUTE);
|
|
3920
4828
|
if (minutes < MINUTES_PER_HOUR) return `${minutes}m`;
|
|
3921
4829
|
return `${Math.floor(minutes / MINUTES_PER_HOUR)}h`;
|
|
3922
4830
|
}
|
|
4831
|
+
function isFailedTask(task) {
|
|
4832
|
+
return task.status === "failed" || task.status === "completed" && (task.exitCode !== void 0 && task.exitCode !== SUCCESS_EXIT_CODE3 || !!task.signalCode);
|
|
4833
|
+
}
|
|
4834
|
+
function formatAccessibleText(row) {
|
|
4835
|
+
const parts = [`${row.connector} ${row.marker} ${row.label}`, ...row.segments];
|
|
4836
|
+
if (row.preview) parts.push(row.preview);
|
|
4837
|
+
return parts.join(" \xB7 ");
|
|
4838
|
+
}
|
|
4839
|
+
|
|
4840
|
+
// src/ui/BackgroundTaskPanel.tsx
|
|
4841
|
+
import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3923
4842
|
function BackgroundTaskPanel({ tasks }) {
|
|
3924
4843
|
if (tasks.length === 0) return null;
|
|
3925
|
-
return /* @__PURE__ */
|
|
3926
|
-
/* @__PURE__ */
|
|
3927
|
-
tasks.map((task) =>
|
|
3928
|
-
|
|
3929
|
-
/* @__PURE__ */
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
4844
|
+
return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", marginBottom: 1, children: [
|
|
4845
|
+
/* @__PURE__ */ jsx20(Text18, { color: "cyan", bold: true, children: "Background work" }),
|
|
4846
|
+
tasks.map((task, index) => {
|
|
4847
|
+
const row = formatBackgroundTaskRow(task, { isLast: index === tasks.length - 1 });
|
|
4848
|
+
return /* @__PURE__ */ jsxs16(Text18, { children: [
|
|
4849
|
+
`${row.connector} `,
|
|
4850
|
+
/* @__PURE__ */ jsx20(Text18, { color: row.color, children: row.marker }),
|
|
4851
|
+
` ${row.label}`,
|
|
4852
|
+
row.segments.map((segment, segmentIndex) => /* @__PURE__ */ jsx20(Text18, { dimColor: true, children: ` \xB7 ${segment}` }, `${segment}-${segmentIndex}`)),
|
|
4853
|
+
row.preview ? /* @__PURE__ */ jsx20(Text18, { dimColor: true, children: ` \xB7 ${row.preview}` }) : null
|
|
4854
|
+
] }, task.id);
|
|
4855
|
+
})
|
|
3935
4856
|
] });
|
|
3936
4857
|
}
|
|
3937
4858
|
|
|
4859
|
+
// src/ui/UpdateNotice.tsx
|
|
4860
|
+
import { Box as Box17, Text as Text19 } from "ink";
|
|
4861
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
4862
|
+
function UpdateNotice({ message }) {
|
|
4863
|
+
return /* @__PURE__ */ jsx21(Box17, { paddingX: 1, marginBottom: 1, children: /* @__PURE__ */ jsx21(Text19, { color: "yellow", children: message }) });
|
|
4864
|
+
}
|
|
4865
|
+
|
|
4866
|
+
// src/utils/update-check.ts
|
|
4867
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
4868
|
+
import { dirname as dirname4, join as join8 } from "path";
|
|
4869
|
+
|
|
4870
|
+
// src/utils/semver-compare.ts
|
|
4871
|
+
function compareSemverVersions(left, right) {
|
|
4872
|
+
const parsedLeft = parseSemver(left);
|
|
4873
|
+
const parsedRight = parseSemver(right);
|
|
4874
|
+
if (parsedLeft === void 0 || parsedRight === void 0) {
|
|
4875
|
+
return Math.sign(left.localeCompare(right));
|
|
4876
|
+
}
|
|
4877
|
+
const coreCompare = compareNumber(parsedLeft.major, parsedRight.major) || compareNumber(parsedLeft.minor, parsedRight.minor) || compareNumber(parsedLeft.patch, parsedRight.patch);
|
|
4878
|
+
if (coreCompare !== 0) {
|
|
4879
|
+
return coreCompare;
|
|
4880
|
+
}
|
|
4881
|
+
return comparePrerelease(parsedLeft.prerelease, parsedRight.prerelease);
|
|
4882
|
+
}
|
|
4883
|
+
function isNewerSemverVersion(candidate, current) {
|
|
4884
|
+
return compareSemverVersions(candidate, current) > 0;
|
|
4885
|
+
}
|
|
4886
|
+
function parseSemver(value) {
|
|
4887
|
+
const normalized = value.trim().replace(/^v/, "").split("+")[0] ?? "";
|
|
4888
|
+
const [core, prereleaseText] = normalized.split("-", 2);
|
|
4889
|
+
const [majorText, minorText, patchText] = core.split(".");
|
|
4890
|
+
const major = parseNumericIdentifier(majorText);
|
|
4891
|
+
const minor = parseNumericIdentifier(minorText);
|
|
4892
|
+
const patch = parseNumericIdentifier(patchText);
|
|
4893
|
+
if (major === void 0 || minor === void 0 || patch === void 0) {
|
|
4894
|
+
return void 0;
|
|
4895
|
+
}
|
|
4896
|
+
return {
|
|
4897
|
+
major,
|
|
4898
|
+
minor,
|
|
4899
|
+
patch,
|
|
4900
|
+
prerelease: prereleaseText ? prereleaseText.split(".") : []
|
|
4901
|
+
};
|
|
4902
|
+
}
|
|
4903
|
+
function parseNumericIdentifier(value) {
|
|
4904
|
+
if (value === void 0 || !/^\d+$/.test(value)) {
|
|
4905
|
+
return void 0;
|
|
4906
|
+
}
|
|
4907
|
+
return Number(value);
|
|
4908
|
+
}
|
|
4909
|
+
function compareNumber(left, right) {
|
|
4910
|
+
return Math.sign(left - right);
|
|
4911
|
+
}
|
|
4912
|
+
function comparePrerelease(left, right) {
|
|
4913
|
+
if (left.length === 0 && right.length === 0) {
|
|
4914
|
+
return 0;
|
|
4915
|
+
}
|
|
4916
|
+
if (left.length === 0) {
|
|
4917
|
+
return 1;
|
|
4918
|
+
}
|
|
4919
|
+
if (right.length === 0) {
|
|
4920
|
+
return -1;
|
|
4921
|
+
}
|
|
4922
|
+
const max = Math.max(left.length, right.length);
|
|
4923
|
+
for (let index = 0; index < max; index += 1) {
|
|
4924
|
+
const leftPart = left[index];
|
|
4925
|
+
const rightPart = right[index];
|
|
4926
|
+
if (leftPart === void 0) {
|
|
4927
|
+
return -1;
|
|
4928
|
+
}
|
|
4929
|
+
if (rightPart === void 0) {
|
|
4930
|
+
return 1;
|
|
4931
|
+
}
|
|
4932
|
+
const partCompare = comparePrereleaseIdentifier(leftPart, rightPart);
|
|
4933
|
+
if (partCompare !== 0) {
|
|
4934
|
+
return partCompare;
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
return 0;
|
|
4938
|
+
}
|
|
4939
|
+
function comparePrereleaseIdentifier(left, right) {
|
|
4940
|
+
const leftNumber = parseNumericIdentifier(left);
|
|
4941
|
+
const rightNumber = parseNumericIdentifier(right);
|
|
4942
|
+
if (leftNumber !== void 0 && rightNumber !== void 0) {
|
|
4943
|
+
return compareNumber(leftNumber, rightNumber);
|
|
4944
|
+
}
|
|
4945
|
+
if (leftNumber !== void 0) {
|
|
4946
|
+
return -1;
|
|
4947
|
+
}
|
|
4948
|
+
if (rightNumber !== void 0) {
|
|
4949
|
+
return 1;
|
|
4950
|
+
}
|
|
4951
|
+
return Math.sign(left.localeCompare(right));
|
|
4952
|
+
}
|
|
4953
|
+
|
|
4954
|
+
// src/utils/update-check.ts
|
|
4955
|
+
var CLI_UPDATE_PACKAGE_NAME = "@robota-sdk/agent-cli";
|
|
4956
|
+
var CLI_UPDATE_REGISTRY_URL = "https://registry.npmjs.org";
|
|
4957
|
+
var HOURS_PER_DAY = 24;
|
|
4958
|
+
var MINUTES_PER_HOUR2 = 60;
|
|
4959
|
+
var SECONDS_PER_MINUTE2 = 60;
|
|
4960
|
+
var MS_PER_SECOND2 = 1e3;
|
|
4961
|
+
var CLI_UPDATE_CACHE_TTL_MS = HOURS_PER_DAY * MINUTES_PER_HOUR2 * SECONDS_PER_MINUTE2 * MS_PER_SECOND2;
|
|
4962
|
+
var CLI_UPDATE_TIMEOUT_MS = 1500;
|
|
4963
|
+
var DEFAULT_INSTALL_COMMAND = "npm install -g '@robota-sdk/agent-cli@latest'";
|
|
4964
|
+
function getUserUpdateCheckCachePath(home = process.env.HOME ?? process.env.USERPROFILE ?? "/") {
|
|
4965
|
+
return join8(home, ".robota", "update-check.json");
|
|
4966
|
+
}
|
|
4967
|
+
function readUpdateCheckCache(path) {
|
|
4968
|
+
if (!existsSync5(path)) {
|
|
4969
|
+
return void 0;
|
|
4970
|
+
}
|
|
4971
|
+
try {
|
|
4972
|
+
const parsed = JSON.parse(readFileSync5(path, "utf8"));
|
|
4973
|
+
return parseUpdateCheckCache(parsed);
|
|
4974
|
+
} catch {
|
|
4975
|
+
return void 0;
|
|
4976
|
+
}
|
|
4977
|
+
}
|
|
4978
|
+
function writeUpdateCheckCache(path, cache) {
|
|
4979
|
+
mkdirSync3(dirname4(path), { recursive: true });
|
|
4980
|
+
writeFileSync2(path, JSON.stringify(cache, null, 2) + "\n", "utf8");
|
|
4981
|
+
}
|
|
4982
|
+
async function checkForCliUpdate(options) {
|
|
4983
|
+
if (options.disabled === true) {
|
|
4984
|
+
return { status: "skipped", reason: "disabled" };
|
|
4985
|
+
}
|
|
4986
|
+
const packageName = options.packageName ?? CLI_UPDATE_PACKAGE_NAME;
|
|
4987
|
+
const cachePath = options.cachePath ?? getUserUpdateCheckCachePath();
|
|
4988
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
4989
|
+
const ttlMs = options.ttlMs ?? CLI_UPDATE_CACHE_TTL_MS;
|
|
4990
|
+
if (options.force !== true) {
|
|
4991
|
+
const cached = readUpdateCheckCache(cachePath);
|
|
4992
|
+
if (cached !== void 0 && isFreshCache(cached, now, ttlMs, packageName)) {
|
|
4993
|
+
return resultFromCache(cached, options.currentVersion);
|
|
4994
|
+
}
|
|
4995
|
+
}
|
|
4996
|
+
try {
|
|
4997
|
+
const latestVersion = await fetchLatestVersion({
|
|
4998
|
+
fetchImpl: options.fetchImpl ?? fetch,
|
|
4999
|
+
packageName,
|
|
5000
|
+
registryUrl: options.registryUrl ?? CLI_UPDATE_REGISTRY_URL,
|
|
5001
|
+
timeoutMs: options.timeoutMs ?? CLI_UPDATE_TIMEOUT_MS
|
|
5002
|
+
});
|
|
5003
|
+
tryWriteUpdateCheckCache(cachePath, {
|
|
5004
|
+
packageName,
|
|
5005
|
+
checkedAt: now.toISOString(),
|
|
5006
|
+
currentVersion: options.currentVersion,
|
|
5007
|
+
latestVersion
|
|
5008
|
+
});
|
|
5009
|
+
return resultFromLatestVersion(options.currentVersion, latestVersion);
|
|
5010
|
+
} catch (error) {
|
|
5011
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5012
|
+
tryWriteUpdateCheckCache(cachePath, {
|
|
5013
|
+
packageName,
|
|
5014
|
+
checkedAt: now.toISOString(),
|
|
5015
|
+
currentVersion: options.currentVersion,
|
|
5016
|
+
errorMessage
|
|
5017
|
+
});
|
|
5018
|
+
return { status: "error", errorMessage };
|
|
5019
|
+
}
|
|
5020
|
+
}
|
|
5021
|
+
function tryWriteUpdateCheckCache(path, cache) {
|
|
5022
|
+
try {
|
|
5023
|
+
writeUpdateCheckCache(path, cache);
|
|
5024
|
+
} catch {
|
|
5025
|
+
}
|
|
5026
|
+
}
|
|
5027
|
+
async function getStartupCliUpdateNotice(options) {
|
|
5028
|
+
const result = await checkForCliUpdate(options);
|
|
5029
|
+
return result.status === "update_available" ? result.notice : void 0;
|
|
5030
|
+
}
|
|
5031
|
+
function shouldRunStartupCliUpdateCheck(input) {
|
|
5032
|
+
return input.printMode === false && input.disableUpdateCheck === false;
|
|
5033
|
+
}
|
|
5034
|
+
function formatCliUpdateNotice(notice) {
|
|
5035
|
+
return [
|
|
5036
|
+
`Robota update available: ${notice.currentVersion} -> ${notice.latestVersion}.`,
|
|
5037
|
+
`Run ${notice.installCommand}`
|
|
5038
|
+
].join(" ");
|
|
5039
|
+
}
|
|
5040
|
+
function formatCliUpdateCheckMessage(result) {
|
|
5041
|
+
if (result.status === "update_available") {
|
|
5042
|
+
return formatCliUpdateNotice(result.notice);
|
|
5043
|
+
}
|
|
5044
|
+
if (result.status === "current") {
|
|
5045
|
+
return `Robota is up to date (${result.currentVersion}).`;
|
|
5046
|
+
}
|
|
5047
|
+
if (result.status === "skipped") {
|
|
5048
|
+
return "Robota update check skipped.";
|
|
5049
|
+
}
|
|
5050
|
+
return `Robota update check failed: ${result.errorMessage}`;
|
|
5051
|
+
}
|
|
5052
|
+
function resultFromCache(cache, currentVersion) {
|
|
5053
|
+
if (cache.errorMessage !== void 0) {
|
|
5054
|
+
return { status: "error", errorMessage: cache.errorMessage };
|
|
5055
|
+
}
|
|
5056
|
+
if (cache.latestVersion === void 0) {
|
|
5057
|
+
return { status: "error", errorMessage: "Cached update check has no latest version" };
|
|
5058
|
+
}
|
|
5059
|
+
return resultFromLatestVersion(currentVersion, cache.latestVersion);
|
|
5060
|
+
}
|
|
5061
|
+
function resultFromLatestVersion(currentVersion, latestVersion) {
|
|
5062
|
+
if (isNewerSemverVersion(latestVersion, currentVersion)) {
|
|
5063
|
+
return {
|
|
5064
|
+
status: "update_available",
|
|
5065
|
+
notice: {
|
|
5066
|
+
currentVersion,
|
|
5067
|
+
latestVersion,
|
|
5068
|
+
installCommand: DEFAULT_INSTALL_COMMAND
|
|
5069
|
+
}
|
|
5070
|
+
};
|
|
5071
|
+
}
|
|
5072
|
+
return { status: "current", currentVersion, latestVersion };
|
|
5073
|
+
}
|
|
5074
|
+
function isFreshCache(cache, now, ttlMs, packageName) {
|
|
5075
|
+
if (cache.packageName !== packageName) {
|
|
5076
|
+
return false;
|
|
5077
|
+
}
|
|
5078
|
+
const checkedAt = Date.parse(cache.checkedAt);
|
|
5079
|
+
if (!Number.isFinite(checkedAt)) {
|
|
5080
|
+
return false;
|
|
5081
|
+
}
|
|
5082
|
+
return now.getTime() - checkedAt < ttlMs;
|
|
5083
|
+
}
|
|
5084
|
+
async function fetchLatestVersion(options) {
|
|
5085
|
+
const controller = new AbortController();
|
|
5086
|
+
const timeout = setTimeout(() => controller.abort(), options.timeoutMs);
|
|
5087
|
+
try {
|
|
5088
|
+
const packageUrl = buildPackageMetadataUrl(options.registryUrl, options.packageName);
|
|
5089
|
+
const response = await options.fetchImpl(packageUrl, {
|
|
5090
|
+
headers: { accept: "application/json" },
|
|
5091
|
+
signal: controller.signal
|
|
5092
|
+
});
|
|
5093
|
+
if (!response.ok) {
|
|
5094
|
+
throw new Error(`registry responded with HTTP ${response.status}`);
|
|
5095
|
+
}
|
|
5096
|
+
const metadata = await response.json();
|
|
5097
|
+
const latest = metadata["dist-tags"]?.latest;
|
|
5098
|
+
if (typeof latest !== "string" || latest.trim().length === 0) {
|
|
5099
|
+
throw new Error("registry metadata is missing dist-tags.latest");
|
|
5100
|
+
}
|
|
5101
|
+
return latest;
|
|
5102
|
+
} finally {
|
|
5103
|
+
clearTimeout(timeout);
|
|
5104
|
+
}
|
|
5105
|
+
}
|
|
5106
|
+
function buildPackageMetadataUrl(registryUrl, packageName) {
|
|
5107
|
+
return `${registryUrl.replace(/\/+$/, "")}/${encodeURIComponent(packageName)}`;
|
|
5108
|
+
}
|
|
5109
|
+
function parseUpdateCheckCache(value) {
|
|
5110
|
+
if (!isJsonObject(value)) {
|
|
5111
|
+
return void 0;
|
|
5112
|
+
}
|
|
5113
|
+
const candidate = value;
|
|
5114
|
+
if (typeof candidate.packageName === "string" && typeof candidate.checkedAt === "string" && typeof candidate.currentVersion === "string" && (candidate.latestVersion === void 0 || typeof candidate.latestVersion === "string") && (candidate.errorMessage === void 0 || typeof candidate.errorMessage === "string")) {
|
|
5115
|
+
return {
|
|
5116
|
+
packageName: candidate.packageName,
|
|
5117
|
+
checkedAt: candidate.checkedAt,
|
|
5118
|
+
currentVersion: candidate.currentVersion,
|
|
5119
|
+
...candidate.latestVersion !== void 0 && { latestVersion: candidate.latestVersion },
|
|
5120
|
+
...candidate.errorMessage !== void 0 && { errorMessage: candidate.errorMessage }
|
|
5121
|
+
};
|
|
5122
|
+
}
|
|
5123
|
+
return void 0;
|
|
5124
|
+
}
|
|
5125
|
+
function isJsonObject(value) {
|
|
5126
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5127
|
+
}
|
|
5128
|
+
|
|
3938
5129
|
// src/ui/App.tsx
|
|
3939
|
-
import { jsx as
|
|
5130
|
+
import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
3940
5131
|
function App(props) {
|
|
3941
5132
|
const [activeSessionId, setActiveSessionId] = useState14(props.resumeSessionId);
|
|
3942
|
-
return /* @__PURE__ */
|
|
5133
|
+
return /* @__PURE__ */ jsx22(
|
|
3943
5134
|
AppInner,
|
|
3944
5135
|
{
|
|
3945
5136
|
...props,
|
|
@@ -3951,7 +5142,7 @@ function App(props) {
|
|
|
3951
5142
|
}
|
|
3952
5143
|
function AppInner(props) {
|
|
3953
5144
|
const cwd = props.cwd;
|
|
3954
|
-
const providerDefinitions = props.providerDefinitions ??
|
|
5145
|
+
const providerDefinitions = props.providerDefinitions ?? [];
|
|
3955
5146
|
const {
|
|
3956
5147
|
interactiveSession,
|
|
3957
5148
|
registry,
|
|
@@ -3987,31 +5178,49 @@ function AppInner(props) {
|
|
|
3987
5178
|
const pluginCallbacks = usePluginCallbacks(cwd);
|
|
3988
5179
|
const { exit } = useApp2();
|
|
3989
5180
|
const [sessionName, setSessionName] = useState14(props.sessionName);
|
|
5181
|
+
const [updateNotice, setUpdateNotice] = useState14();
|
|
5182
|
+
const [statusLineSettings, setStatusLineSettings] = useStatusLineSettings();
|
|
5183
|
+
const activeBackgroundTaskCount = backgroundTasks.filter(
|
|
5184
|
+
(task) => task.status === "queued" || task.status === "running"
|
|
5185
|
+
).length;
|
|
3990
5186
|
const {
|
|
3991
5187
|
handleSubmit,
|
|
3992
5188
|
pendingModelId,
|
|
3993
5189
|
pendingProviderProfile,
|
|
3994
|
-
|
|
5190
|
+
pendingInteractionPrompt,
|
|
3995
5191
|
showPluginTUI,
|
|
3996
5192
|
showSessionPicker,
|
|
3997
5193
|
setShowPluginTUI,
|
|
3998
5194
|
setShowSessionPicker,
|
|
3999
5195
|
handleModelConfirm,
|
|
4000
5196
|
handleProviderConfirm,
|
|
4001
|
-
|
|
4002
|
-
|
|
5197
|
+
handleInteractionSubmit,
|
|
5198
|
+
handleInteractionCancel
|
|
4003
5199
|
} = useSideEffects({
|
|
4004
5200
|
cwd,
|
|
4005
5201
|
interactiveSession,
|
|
4006
5202
|
addEntry,
|
|
4007
5203
|
baseHandleSubmit,
|
|
4008
5204
|
setSessionName,
|
|
5205
|
+
setStatusLineSettings,
|
|
4009
5206
|
providerDefinitions
|
|
4010
5207
|
});
|
|
4011
5208
|
useEffect4(() => {
|
|
4012
5209
|
const name = interactiveSession?.getName?.();
|
|
4013
5210
|
if (name && !sessionName) setSessionName(name);
|
|
4014
5211
|
}, [interactiveSession, sessionName]);
|
|
5212
|
+
useEffect4(() => {
|
|
5213
|
+
let isMounted = true;
|
|
5214
|
+
props.startupUpdateNoticePromise?.then((notice) => {
|
|
5215
|
+
if (isMounted && notice !== void 0) {
|
|
5216
|
+
setUpdateNotice(notice);
|
|
5217
|
+
}
|
|
5218
|
+
}).catch(() => {
|
|
5219
|
+
});
|
|
5220
|
+
return () => {
|
|
5221
|
+
isMounted = false;
|
|
5222
|
+
};
|
|
5223
|
+
}, [props.startupUpdateNoticePromise]);
|
|
4015
5224
|
useEffect4(() => {
|
|
4016
5225
|
const title = sessionName ? `Robota \u2014 ${sessionName}` : "Robota";
|
|
4017
5226
|
process.stdout.write(`\x1B]0;${title}\x07`);
|
|
@@ -4045,51 +5254,51 @@ function AppInner(props) {
|
|
|
4045
5254
|
sessionId = session.getSessionId();
|
|
4046
5255
|
} catch {
|
|
4047
5256
|
}
|
|
4048
|
-
return /* @__PURE__ */
|
|
4049
|
-
/* @__PURE__ */
|
|
4050
|
-
/* @__PURE__ */
|
|
5257
|
+
return /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", children: [
|
|
5258
|
+
/* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
5259
|
+
/* @__PURE__ */ jsx22(Text20, { color: "cyan", bold: true, children: `
|
|
4051
5260
|
____ ___ ____ ___ _____ _
|
|
4052
5261
|
| _ \\ / _ \\| __ ) / _ \\_ _|/ \\
|
|
4053
5262
|
| |_) | | | | _ \\| | | || | / _ \\
|
|
4054
5263
|
| _ <| |_| | |_) | |_| || |/ ___ \\
|
|
4055
5264
|
|_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
|
|
4056
5265
|
` }),
|
|
4057
|
-
/* @__PURE__ */
|
|
5266
|
+
/* @__PURE__ */ jsxs17(Text20, { dimColor: true, children: [
|
|
4058
5267
|
" v",
|
|
4059
5268
|
props.version ?? "0.0.0"
|
|
4060
5269
|
] })
|
|
4061
5270
|
] }),
|
|
4062
|
-
/* @__PURE__ */
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
/* @__PURE__ */
|
|
5271
|
+
updateNotice && /* @__PURE__ */ jsx22(UpdateNotice, { message: formatCliUpdateNotice(updateNotice) }),
|
|
5272
|
+
/* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
|
|
5273
|
+
/* @__PURE__ */ jsx22(MessageList, { history }),
|
|
5274
|
+
isShuttingDown && /* @__PURE__ */ jsx22(Box18, { marginBottom: 1, children: /* @__PURE__ */ jsx22(Text20, { color: "yellow", children: "Shutting down..." }) }),
|
|
5275
|
+
(isThinking || activeTools.length > 0) && /* @__PURE__ */ jsx22(Box18, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx22(StreamingIndicator, { text: streamingText, activeTools }) }),
|
|
5276
|
+
/* @__PURE__ */ jsx22(BackgroundTaskPanel, { tasks: backgroundTasks })
|
|
4067
5277
|
] }),
|
|
4068
|
-
permissionRequest && /* @__PURE__ */
|
|
4069
|
-
pendingModelId && /* @__PURE__ */
|
|
5278
|
+
permissionRequest && /* @__PURE__ */ jsx22(PermissionPrompt, { request: permissionRequest }),
|
|
5279
|
+
pendingModelId && /* @__PURE__ */ jsx22(
|
|
4070
5280
|
ConfirmPrompt,
|
|
4071
5281
|
{
|
|
4072
|
-
message: `Change model to ${
|
|
5282
|
+
message: `Change model to ${getModelName3(pendingModelId)}? This will restart the session.`,
|
|
4073
5283
|
onSelect: handleModelConfirm
|
|
4074
5284
|
}
|
|
4075
5285
|
),
|
|
4076
|
-
pendingProviderProfile && /* @__PURE__ */
|
|
5286
|
+
pendingProviderProfile && /* @__PURE__ */ jsx22(
|
|
4077
5287
|
ConfirmPrompt,
|
|
4078
5288
|
{
|
|
4079
5289
|
message: `Change provider to ${pendingProviderProfile}? This will restart the session.`,
|
|
4080
5290
|
onSelect: handleProviderConfirm
|
|
4081
5291
|
}
|
|
4082
5292
|
),
|
|
4083
|
-
|
|
4084
|
-
|
|
5293
|
+
pendingInteractionPrompt && /* @__PURE__ */ jsx22(
|
|
5294
|
+
InteractivePrompt,
|
|
4085
5295
|
{
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
onCancel: handleProviderSetupCancel
|
|
5296
|
+
prompt: pendingInteractionPrompt,
|
|
5297
|
+
onSubmit: handleInteractionSubmit,
|
|
5298
|
+
onCancel: handleInteractionCancel
|
|
4090
5299
|
}
|
|
4091
5300
|
),
|
|
4092
|
-
showPluginTUI && /* @__PURE__ */
|
|
5301
|
+
showPluginTUI && /* @__PURE__ */ jsx22(
|
|
4093
5302
|
PluginTUI,
|
|
4094
5303
|
{
|
|
4095
5304
|
callbacks: pluginCallbacks,
|
|
@@ -4097,7 +5306,7 @@ function AppInner(props) {
|
|
|
4097
5306
|
addMessage: (msg) => addEntry(messageToHistoryEntry4(createSystemMessage4(msg.content)))
|
|
4098
5307
|
}
|
|
4099
5308
|
),
|
|
4100
|
-
showSessionPicker && /* @__PURE__ */
|
|
5309
|
+
showSessionPicker && /* @__PURE__ */ jsx22(
|
|
4101
5310
|
SessionPicker,
|
|
4102
5311
|
{
|
|
4103
5312
|
sessionStore: props.sessionStore,
|
|
@@ -4112,38 +5321,42 @@ function AppInner(props) {
|
|
|
4112
5321
|
}
|
|
4113
5322
|
}
|
|
4114
5323
|
),
|
|
4115
|
-
/* @__PURE__ */
|
|
4116
|
-
|
|
5324
|
+
/* @__PURE__ */ jsx22(
|
|
5325
|
+
SessionStatusBar,
|
|
4117
5326
|
{
|
|
5327
|
+
cwd,
|
|
4118
5328
|
permissionMode,
|
|
4119
|
-
|
|
5329
|
+
modelId: props.modelId,
|
|
4120
5330
|
sessionId,
|
|
4121
5331
|
messageCount: history.length,
|
|
4122
5332
|
isThinking,
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
5333
|
+
activeToolCount: activeTools.length,
|
|
5334
|
+
activeBackgroundTaskCount,
|
|
5335
|
+
hasPendingPrompt: pendingPrompt !== null,
|
|
5336
|
+
contextState,
|
|
5337
|
+
sessionName,
|
|
5338
|
+
settings: statusLineSettings
|
|
4127
5339
|
}
|
|
4128
5340
|
),
|
|
4129
|
-
/* @__PURE__ */
|
|
5341
|
+
/* @__PURE__ */ jsx22(
|
|
4130
5342
|
InputArea,
|
|
4131
5343
|
{
|
|
4132
5344
|
onSubmit: handleSubmit,
|
|
4133
5345
|
onCancelQueue: handleCancelQueue,
|
|
4134
|
-
isDisabled: !!permissionRequest || showPluginTUI || showSessionPicker || isShuttingDown ||
|
|
5346
|
+
isDisabled: !!permissionRequest || showPluginTUI || showSessionPicker || isShuttingDown || pendingInteractionPrompt !== null || isThinking && !!pendingPrompt,
|
|
4135
5347
|
isAborting,
|
|
4136
5348
|
pendingPrompt,
|
|
4137
5349
|
registry,
|
|
4138
|
-
sessionName
|
|
5350
|
+
sessionName,
|
|
5351
|
+
history
|
|
4139
5352
|
}
|
|
4140
5353
|
),
|
|
4141
|
-
/* @__PURE__ */
|
|
5354
|
+
/* @__PURE__ */ jsx22(Text20, { children: " " })
|
|
4142
5355
|
] });
|
|
4143
5356
|
}
|
|
4144
5357
|
|
|
4145
5358
|
// src/ui/render.tsx
|
|
4146
|
-
import { jsx as
|
|
5359
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
4147
5360
|
function renderApp(options) {
|
|
4148
5361
|
process.on("unhandledRejection", (reason) => {
|
|
4149
5362
|
process.stderr.write(`
|
|
@@ -4154,16 +5367,10 @@ function renderApp(options) {
|
|
|
4154
5367
|
`);
|
|
4155
5368
|
}
|
|
4156
5369
|
});
|
|
4157
|
-
|
|
4158
|
-
process.stdout.write("\x1B[?2004h");
|
|
4159
|
-
}
|
|
4160
|
-
const instance = render(/* @__PURE__ */ jsx18(App, { ...options }), {
|
|
5370
|
+
const instance = render(/* @__PURE__ */ jsx23(App, { ...options }), {
|
|
4161
5371
|
exitOnCtrlC: false
|
|
4162
5372
|
});
|
|
4163
5373
|
instance.waitUntilExit().then(() => {
|
|
4164
|
-
if (process.stdout.isTTY) {
|
|
4165
|
-
process.stdout.write("\x1B[?2004l");
|
|
4166
|
-
}
|
|
4167
5374
|
process.exit(0);
|
|
4168
5375
|
}).catch((err) => {
|
|
4169
5376
|
if (err) {
|
|
@@ -4175,15 +5382,102 @@ function renderApp(options) {
|
|
|
4175
5382
|
});
|
|
4176
5383
|
}
|
|
4177
5384
|
|
|
5385
|
+
// src/commands/statusline-command-module.ts
|
|
5386
|
+
var USAGE = [
|
|
5387
|
+
"Usage: /statusline on | off | reset | git on | git off",
|
|
5388
|
+
"Fields: model, context, permission mode, message count, session name, thinking state, git branch."
|
|
5389
|
+
].join("\n");
|
|
5390
|
+
function createStatusLineEntry() {
|
|
5391
|
+
return {
|
|
5392
|
+
name: "statusline",
|
|
5393
|
+
description: "Configure TUI status-line visibility and fields such as model, context, tokens, session, and git branch.",
|
|
5394
|
+
source: "cli",
|
|
5395
|
+
modelInvocable: false,
|
|
5396
|
+
argumentHint: "on | off | reset | git on | git off",
|
|
5397
|
+
subcommands: [
|
|
5398
|
+
{ name: "on", description: "Show the status line", source: "cli" },
|
|
5399
|
+
{ name: "off", description: "Hide the status line", source: "cli" },
|
|
5400
|
+
{ name: "reset", description: "Restore default status-line fields", source: "cli" },
|
|
5401
|
+
{ name: "git", description: "Show or hide git branch field", source: "cli" }
|
|
5402
|
+
]
|
|
5403
|
+
};
|
|
5404
|
+
}
|
|
5405
|
+
function parseStatusLineArgs(args) {
|
|
5406
|
+
const parts = args.trim().toLowerCase().split(/\s+/).filter((part) => part.length > 0);
|
|
5407
|
+
const [first, second] = parts;
|
|
5408
|
+
if (first === "on" && second === void 0) {
|
|
5409
|
+
return { success: true, message: "Status line enabled.", patch: { enabled: true } };
|
|
5410
|
+
}
|
|
5411
|
+
if (first === "off" && second === void 0) {
|
|
5412
|
+
return { success: true, message: "Status line disabled.", patch: { enabled: false } };
|
|
5413
|
+
}
|
|
5414
|
+
if (first === "reset" && second === void 0) {
|
|
5415
|
+
return {
|
|
5416
|
+
success: true,
|
|
5417
|
+
message: "Status line settings reset.",
|
|
5418
|
+
patch: { enabled: true, gitBranch: true }
|
|
5419
|
+
};
|
|
5420
|
+
}
|
|
5421
|
+
if (first === "git" && second === "on" && parts.length === 2) {
|
|
5422
|
+
return {
|
|
5423
|
+
success: true,
|
|
5424
|
+
message: "Status line git branch shown.",
|
|
5425
|
+
patch: { gitBranch: true }
|
|
5426
|
+
};
|
|
5427
|
+
}
|
|
5428
|
+
if (first === "git" && second === "off" && parts.length === 2) {
|
|
5429
|
+
return {
|
|
5430
|
+
success: true,
|
|
5431
|
+
message: "Status line git branch hidden.",
|
|
5432
|
+
patch: { gitBranch: false }
|
|
5433
|
+
};
|
|
5434
|
+
}
|
|
5435
|
+
return { success: false, message: USAGE };
|
|
5436
|
+
}
|
|
5437
|
+
function createStatusLineSystemCommand() {
|
|
5438
|
+
const entry = createStatusLineEntry();
|
|
5439
|
+
return {
|
|
5440
|
+
name: entry.name,
|
|
5441
|
+
description: entry.description,
|
|
5442
|
+
modelInvocable: false,
|
|
5443
|
+
userInvocable: true,
|
|
5444
|
+
argumentHint: entry.argumentHint,
|
|
5445
|
+
execute: (_session, args) => {
|
|
5446
|
+
const action = parseStatusLineArgs(args);
|
|
5447
|
+
if (!action.success) {
|
|
5448
|
+
return { success: false, message: action.message };
|
|
5449
|
+
}
|
|
5450
|
+
return {
|
|
5451
|
+
success: true,
|
|
5452
|
+
message: action.message,
|
|
5453
|
+
data: { statuslinePatch: action.patch }
|
|
5454
|
+
};
|
|
5455
|
+
}
|
|
5456
|
+
};
|
|
5457
|
+
}
|
|
5458
|
+
var StatusLineCommandSource = class {
|
|
5459
|
+
name = "cli-statusline";
|
|
5460
|
+
getCommands() {
|
|
5461
|
+
return [createStatusLineEntry()];
|
|
5462
|
+
}
|
|
5463
|
+
};
|
|
5464
|
+
function createStatusLineCommandModule() {
|
|
5465
|
+
return {
|
|
5466
|
+
name: "cli-statusline",
|
|
5467
|
+
commandSources: [new StatusLineCommandSource()],
|
|
5468
|
+
systemCommands: [createStatusLineSystemCommand()]
|
|
5469
|
+
};
|
|
5470
|
+
}
|
|
5471
|
+
|
|
4178
5472
|
// src/cli.ts
|
|
4179
5473
|
function readVersion() {
|
|
4180
5474
|
try {
|
|
4181
5475
|
const thisFile = fileURLToPath(import.meta.url);
|
|
4182
|
-
const dir =
|
|
4183
|
-
const candidates = [
|
|
5476
|
+
const dir = dirname5(thisFile);
|
|
5477
|
+
const candidates = [join9(dir, "..", "..", "package.json"), join9(dir, "..", "package.json")];
|
|
4184
5478
|
for (const pkgPath of candidates) {
|
|
4185
5479
|
try {
|
|
4186
|
-
const raw =
|
|
5480
|
+
const raw = readFileSync6(pkgPath, "utf-8");
|
|
4187
5481
|
const pkg = JSON.parse(raw);
|
|
4188
5482
|
if (pkg.version !== void 0 && pkg.name !== void 0) {
|
|
4189
5483
|
return pkg.version;
|
|
@@ -4197,7 +5491,7 @@ function readVersion() {
|
|
|
4197
5491
|
}
|
|
4198
5492
|
}
|
|
4199
5493
|
function promptInput(label, masked = false) {
|
|
4200
|
-
return new Promise((
|
|
5494
|
+
return new Promise((resolve2) => {
|
|
4201
5495
|
process.stdout.write(label);
|
|
4202
5496
|
let input = "";
|
|
4203
5497
|
const stdin = process.stdin;
|
|
@@ -4212,7 +5506,7 @@ function promptInput(label, masked = false) {
|
|
|
4212
5506
|
stdin.setRawMode(wasRaw ?? false);
|
|
4213
5507
|
stdin.pause();
|
|
4214
5508
|
process.stdout.write("\n");
|
|
4215
|
-
|
|
5509
|
+
resolve2(input.trim());
|
|
4216
5510
|
return;
|
|
4217
5511
|
} else if (ch === "\x7F" || ch === "\b") {
|
|
4218
5512
|
if (input.length > 0) {
|
|
@@ -4242,8 +5536,21 @@ function resetConfig() {
|
|
|
4242
5536
|
}
|
|
4243
5537
|
async function startCli(options = {}) {
|
|
4244
5538
|
const args = parseCliArgs();
|
|
5539
|
+
const version = readVersion();
|
|
4245
5540
|
if (args.version) {
|
|
4246
|
-
process.stdout.write(`robota ${
|
|
5541
|
+
process.stdout.write(`robota ${version}
|
|
5542
|
+
`);
|
|
5543
|
+
return;
|
|
5544
|
+
}
|
|
5545
|
+
if (args.checkUpdate) {
|
|
5546
|
+
const result = await checkForCliUpdate({ currentVersion: version, force: true });
|
|
5547
|
+
const message = formatCliUpdateCheckMessage(result);
|
|
5548
|
+
if (result.status === "error") {
|
|
5549
|
+
process.stderr.write(`${message}
|
|
5550
|
+
`);
|
|
5551
|
+
process.exit(1);
|
|
5552
|
+
}
|
|
5553
|
+
process.stdout.write(`${message}
|
|
4247
5554
|
`);
|
|
4248
5555
|
return;
|
|
4249
5556
|
}
|
|
@@ -4253,6 +5560,11 @@ async function startCli(options = {}) {
|
|
|
4253
5560
|
}
|
|
4254
5561
|
const cwd = process.cwd();
|
|
4255
5562
|
const providerDefinitions = options.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS;
|
|
5563
|
+
const commandModules = [
|
|
5564
|
+
createStatusLineCommandModule(),
|
|
5565
|
+
...options.commandModules ?? []
|
|
5566
|
+
];
|
|
5567
|
+
const startupUpdateNoticePromise = shouldRunStartupCliUpdateCheck(args) ? getStartupCliUpdateNotice({ currentVersion: version }) : void 0;
|
|
4256
5568
|
if (args.configure) {
|
|
4257
5569
|
await runInteractiveProviderSetup(cwd, args, promptInput, providerDefinitions);
|
|
4258
5570
|
return;
|
|
@@ -4332,7 +5644,7 @@ ${args.jsonSchema}`
|
|
|
4332
5644
|
appendSystemPrompt,
|
|
4333
5645
|
backgroundTaskRunners,
|
|
4334
5646
|
subagentRunnerFactory,
|
|
4335
|
-
commandModules
|
|
5647
|
+
commandModules
|
|
4336
5648
|
});
|
|
4337
5649
|
const transport = createHeadlessTransport({
|
|
4338
5650
|
outputFormat: args.outputFormat ?? "text",
|
|
@@ -4350,15 +5662,16 @@ ${args.jsonSchema}`
|
|
|
4350
5662
|
language: args.language,
|
|
4351
5663
|
permissionMode: args.permissionMode,
|
|
4352
5664
|
maxTurns: args.maxTurns,
|
|
4353
|
-
version
|
|
5665
|
+
version,
|
|
4354
5666
|
sessionStore,
|
|
4355
5667
|
resumeSessionId,
|
|
4356
5668
|
forkSession: args.forkSession,
|
|
4357
5669
|
sessionName: args.sessionName,
|
|
4358
5670
|
backgroundTaskRunners,
|
|
4359
5671
|
subagentRunnerFactory,
|
|
4360
|
-
commandModules
|
|
4361
|
-
providerDefinitions
|
|
5672
|
+
commandModules,
|
|
5673
|
+
providerDefinitions,
|
|
5674
|
+
startupUpdateNoticePromise
|
|
4362
5675
|
});
|
|
4363
5676
|
}
|
|
4364
5677
|
|