@robota-sdk/agent-cli 3.0.0-beta.16 → 3.0.0-beta.18
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/dist/node/bin.cjs +416 -300
- package/dist/node/bin.js +1 -1
- package/dist/node/{chunk-OZHWJAII.js → chunk-M723M4M4.js} +416 -300
- package/dist/node/index.cjs +416 -300
- package/dist/node/index.js +1 -1
- package/package.json +3 -3
package/dist/node/bin.cjs
CHANGED
|
@@ -24,22 +24,273 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/cli.ts
|
|
27
|
-
var import_node_util = require("util");
|
|
28
27
|
var import_node_fs3 = require("fs");
|
|
29
28
|
var import_node_path3 = require("path");
|
|
30
29
|
var import_node_url = require("url");
|
|
31
|
-
var readline = __toESM(require("readline"), 1);
|
|
32
30
|
var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
|
|
33
31
|
var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
|
|
34
32
|
|
|
33
|
+
// src/utils/cli-args.ts
|
|
34
|
+
var import_node_util = require("util");
|
|
35
|
+
var VALID_MODES = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
36
|
+
function parsePermissionMode(raw) {
|
|
37
|
+
if (raw === void 0) return void 0;
|
|
38
|
+
if (!VALID_MODES.includes(raw)) {
|
|
39
|
+
process.stderr.write(`Invalid --permission-mode "${raw}". Valid: ${VALID_MODES.join(" | ")}
|
|
40
|
+
`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
return raw;
|
|
44
|
+
}
|
|
45
|
+
function parseMaxTurns(raw) {
|
|
46
|
+
if (raw === void 0) return void 0;
|
|
47
|
+
const n = parseInt(raw, 10);
|
|
48
|
+
if (isNaN(n) || n <= 0) {
|
|
49
|
+
process.stderr.write(`Invalid --max-turns "${raw}". Must be a positive integer.
|
|
50
|
+
`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
return n;
|
|
54
|
+
}
|
|
55
|
+
function parseCliArgs() {
|
|
56
|
+
const { values, positionals } = (0, import_node_util.parseArgs)({
|
|
57
|
+
allowPositionals: true,
|
|
58
|
+
options: {
|
|
59
|
+
p: { type: "boolean", short: "p", default: false },
|
|
60
|
+
c: { type: "boolean", short: "c", default: false },
|
|
61
|
+
r: { type: "string", short: "r" },
|
|
62
|
+
model: { type: "string" },
|
|
63
|
+
"permission-mode": { type: "string" },
|
|
64
|
+
"max-turns": { type: "string" },
|
|
65
|
+
version: { type: "boolean", default: false },
|
|
66
|
+
reset: { type: "boolean", default: false }
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
positional: positionals,
|
|
71
|
+
printMode: values["p"] ?? false,
|
|
72
|
+
continueMode: values["c"] ?? false,
|
|
73
|
+
resumeId: values["r"],
|
|
74
|
+
model: values["model"],
|
|
75
|
+
permissionMode: parsePermissionMode(values["permission-mode"]),
|
|
76
|
+
maxTurns: parseMaxTurns(values["max-turns"]),
|
|
77
|
+
version: values["version"] ?? false,
|
|
78
|
+
reset: values["reset"] ?? false
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/utils/settings-io.ts
|
|
83
|
+
var import_node_fs = require("fs");
|
|
84
|
+
var import_node_path = require("path");
|
|
85
|
+
function getUserSettingsPath() {
|
|
86
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
|
|
87
|
+
return (0, import_node_path.join)(home, ".robota", "settings.json");
|
|
88
|
+
}
|
|
89
|
+
function readSettings(path) {
|
|
90
|
+
if (!(0, import_node_fs.existsSync)(path)) return {};
|
|
91
|
+
return JSON.parse((0, import_node_fs.readFileSync)(path, "utf8"));
|
|
92
|
+
}
|
|
93
|
+
function writeSettings(path, settings) {
|
|
94
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path), { recursive: true });
|
|
95
|
+
(0, import_node_fs.writeFileSync)(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
96
|
+
}
|
|
97
|
+
function updateModelInSettings(settingsPath, modelId) {
|
|
98
|
+
const settings = readSettings(settingsPath);
|
|
99
|
+
const provider = settings.provider ?? {};
|
|
100
|
+
provider.model = modelId;
|
|
101
|
+
settings.provider = provider;
|
|
102
|
+
writeSettings(settingsPath, settings);
|
|
103
|
+
}
|
|
104
|
+
function deleteSettings(path) {
|
|
105
|
+
if ((0, import_node_fs.existsSync)(path)) {
|
|
106
|
+
(0, import_node_fs.unlinkSync)(path);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/print-terminal.ts
|
|
113
|
+
var readline = __toESM(require("readline"), 1);
|
|
114
|
+
var PrintTerminal = class {
|
|
115
|
+
write(text) {
|
|
116
|
+
process.stdout.write(text);
|
|
117
|
+
}
|
|
118
|
+
writeLine(text) {
|
|
119
|
+
process.stdout.write(text + "\n");
|
|
120
|
+
}
|
|
121
|
+
writeMarkdown(md) {
|
|
122
|
+
process.stdout.write(md);
|
|
123
|
+
}
|
|
124
|
+
writeError(text) {
|
|
125
|
+
process.stderr.write(text + "\n");
|
|
126
|
+
}
|
|
127
|
+
prompt(question) {
|
|
128
|
+
return new Promise((resolve) => {
|
|
129
|
+
const rl = readline.createInterface({
|
|
130
|
+
input: process.stdin,
|
|
131
|
+
output: process.stdout,
|
|
132
|
+
terminal: false,
|
|
133
|
+
historySize: 0
|
|
134
|
+
});
|
|
135
|
+
rl.question(question, (answer) => {
|
|
136
|
+
rl.close();
|
|
137
|
+
resolve(answer);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async select(options, initialIndex = 0) {
|
|
142
|
+
for (let i = 0; i < options.length; i++) {
|
|
143
|
+
const marker = i === initialIndex ? ">" : " ";
|
|
144
|
+
process.stdout.write(` ${marker} ${i + 1}) ${options[i]}
|
|
145
|
+
`);
|
|
146
|
+
}
|
|
147
|
+
const answer = await this.prompt(
|
|
148
|
+
` Choose [1-${options.length}] (default: ${options[initialIndex]}): `
|
|
149
|
+
);
|
|
150
|
+
const trimmed = answer.trim().toLowerCase();
|
|
151
|
+
if (trimmed === "") return initialIndex;
|
|
152
|
+
const num = parseInt(trimmed, 10);
|
|
153
|
+
if (!isNaN(num) && num >= 1 && num <= options.length) return num - 1;
|
|
154
|
+
return initialIndex;
|
|
155
|
+
}
|
|
156
|
+
spinner(_message) {
|
|
157
|
+
return { stop() {
|
|
158
|
+
}, update() {
|
|
159
|
+
} };
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
35
163
|
// src/ui/render.tsx
|
|
36
|
-
var
|
|
164
|
+
var import_ink11 = require("ink");
|
|
37
165
|
|
|
38
166
|
// src/ui/App.tsx
|
|
39
167
|
var import_react6 = require("react");
|
|
40
|
-
var
|
|
41
|
-
|
|
42
|
-
|
|
168
|
+
var import_ink10 = require("ink");
|
|
169
|
+
|
|
170
|
+
// src/commands/slash-executor.ts
|
|
171
|
+
var VALID_MODES2 = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
172
|
+
var HELP_TEXT = [
|
|
173
|
+
"Available commands:",
|
|
174
|
+
" /help \u2014 Show this help",
|
|
175
|
+
" /clear \u2014 Clear conversation",
|
|
176
|
+
" /compact [instr] \u2014 Compact context (optional focus instructions)",
|
|
177
|
+
" /mode [m] \u2014 Show/change permission mode",
|
|
178
|
+
" /cost \u2014 Show session info",
|
|
179
|
+
" /reset \u2014 Delete settings and exit",
|
|
180
|
+
" /exit \u2014 Exit CLI"
|
|
181
|
+
].join("\n");
|
|
182
|
+
function handleHelp(addMessage) {
|
|
183
|
+
addMessage({ role: "system", content: HELP_TEXT });
|
|
184
|
+
return { handled: true };
|
|
185
|
+
}
|
|
186
|
+
function handleClear(addMessage, clearMessages, session) {
|
|
187
|
+
clearMessages();
|
|
188
|
+
session.clearHistory();
|
|
189
|
+
addMessage({ role: "system", content: "Conversation cleared." });
|
|
190
|
+
return { handled: true };
|
|
191
|
+
}
|
|
192
|
+
async function handleCompact(args, session, addMessage) {
|
|
193
|
+
const instructions = args.trim() || void 0;
|
|
194
|
+
const before = session.getContextState().usedPercentage;
|
|
195
|
+
addMessage({ role: "system", content: "Compacting context..." });
|
|
196
|
+
await session.compact(instructions);
|
|
197
|
+
const after = session.getContextState().usedPercentage;
|
|
198
|
+
addMessage({
|
|
199
|
+
role: "system",
|
|
200
|
+
content: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`
|
|
201
|
+
});
|
|
202
|
+
return { handled: true };
|
|
203
|
+
}
|
|
204
|
+
function handleMode(arg, session, addMessage) {
|
|
205
|
+
if (!arg) {
|
|
206
|
+
addMessage({ role: "system", content: `Current mode: ${session.getPermissionMode()}` });
|
|
207
|
+
} else if (VALID_MODES2.includes(arg)) {
|
|
208
|
+
session.setPermissionMode(arg);
|
|
209
|
+
addMessage({ role: "system", content: `Permission mode set to: ${arg}` });
|
|
210
|
+
} else {
|
|
211
|
+
addMessage({ role: "system", content: `Invalid mode. Valid: ${VALID_MODES2.join(" | ")}` });
|
|
212
|
+
}
|
|
213
|
+
return { handled: true };
|
|
214
|
+
}
|
|
215
|
+
function handleModel(modelId, addMessage) {
|
|
216
|
+
if (!modelId) {
|
|
217
|
+
addMessage({ role: "system", content: "Select a model from the /model submenu." });
|
|
218
|
+
return { handled: true };
|
|
219
|
+
}
|
|
220
|
+
return { handled: true, pendingModelId: modelId };
|
|
221
|
+
}
|
|
222
|
+
function handleCost(session, addMessage) {
|
|
223
|
+
addMessage({
|
|
224
|
+
role: "system",
|
|
225
|
+
content: `Session: ${session.getSessionId()}
|
|
226
|
+
Messages: ${session.getMessageCount()}`
|
|
227
|
+
});
|
|
228
|
+
return { handled: true };
|
|
229
|
+
}
|
|
230
|
+
function handlePermissions(session, addMessage) {
|
|
231
|
+
const mode = session.getPermissionMode();
|
|
232
|
+
const sessionAllowed = session.getSessionAllowedTools();
|
|
233
|
+
const lines = [`Permission mode: ${mode}`];
|
|
234
|
+
if (sessionAllowed.length > 0) {
|
|
235
|
+
lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
|
|
236
|
+
} else {
|
|
237
|
+
lines.push("No session-approved tools.");
|
|
238
|
+
}
|
|
239
|
+
addMessage({ role: "system", content: lines.join("\n") });
|
|
240
|
+
return { handled: true };
|
|
241
|
+
}
|
|
242
|
+
function handleContext(session, addMessage) {
|
|
243
|
+
const ctx = session.getContextState();
|
|
244
|
+
addMessage({
|
|
245
|
+
role: "system",
|
|
246
|
+
content: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`
|
|
247
|
+
});
|
|
248
|
+
return { handled: true };
|
|
249
|
+
}
|
|
250
|
+
function handleReset(addMessage) {
|
|
251
|
+
const settingsPath = getUserSettingsPath();
|
|
252
|
+
if (deleteSettings(settingsPath)) {
|
|
253
|
+
addMessage({ role: "system", content: `Deleted ${settingsPath}. Exiting...` });
|
|
254
|
+
} else {
|
|
255
|
+
addMessage({ role: "system", content: "No user settings found." });
|
|
256
|
+
}
|
|
257
|
+
return { handled: true, exitRequested: true };
|
|
258
|
+
}
|
|
259
|
+
async function executeSlashCommand(cmd, args, session, addMessage, clearMessages, registry) {
|
|
260
|
+
switch (cmd) {
|
|
261
|
+
case "help":
|
|
262
|
+
return handleHelp(addMessage);
|
|
263
|
+
case "clear":
|
|
264
|
+
return handleClear(addMessage, clearMessages, session);
|
|
265
|
+
case "compact":
|
|
266
|
+
return handleCompact(args, session, addMessage);
|
|
267
|
+
case "mode":
|
|
268
|
+
return handleMode(args.split(/\s+/)[0] || void 0, session, addMessage);
|
|
269
|
+
case "model":
|
|
270
|
+
return handleModel(args.split(/\s+/)[0] || void 0, addMessage);
|
|
271
|
+
case "cost":
|
|
272
|
+
return handleCost(session, addMessage);
|
|
273
|
+
case "permissions":
|
|
274
|
+
return handlePermissions(session, addMessage);
|
|
275
|
+
case "context":
|
|
276
|
+
return handleContext(session, addMessage);
|
|
277
|
+
case "reset":
|
|
278
|
+
return handleReset(addMessage);
|
|
279
|
+
case "exit":
|
|
280
|
+
return { handled: true, exitRequested: true };
|
|
281
|
+
default: {
|
|
282
|
+
const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
|
|
283
|
+
if (skillCmd) {
|
|
284
|
+
addMessage({ role: "system", content: `Invoking skill: ${cmd}` });
|
|
285
|
+
return { handled: false };
|
|
286
|
+
}
|
|
287
|
+
addMessage({ role: "system", content: `Unknown command "/${cmd}". Type /help for help.` });
|
|
288
|
+
return { handled: true };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/ui/App.tsx
|
|
43
294
|
var import_agent_sdk = require("@robota-sdk/agent-sdk");
|
|
44
295
|
var import_agent_core3 = require("@robota-sdk/agent-core");
|
|
45
296
|
|
|
@@ -130,8 +381,8 @@ var BuiltinCommandSource = class {
|
|
|
130
381
|
};
|
|
131
382
|
|
|
132
383
|
// src/commands/skill-source.ts
|
|
133
|
-
var
|
|
134
|
-
var
|
|
384
|
+
var import_node_fs2 = require("fs");
|
|
385
|
+
var import_node_path2 = require("path");
|
|
135
386
|
var import_node_os = require("os");
|
|
136
387
|
function parseFrontmatter(content) {
|
|
137
388
|
const lines = content.split("\n");
|
|
@@ -154,14 +405,14 @@ function parseFrontmatter(content) {
|
|
|
154
405
|
return name ? { name, description } : null;
|
|
155
406
|
}
|
|
156
407
|
function scanSkillsDir(skillsDir) {
|
|
157
|
-
if (!(0,
|
|
408
|
+
if (!(0, import_node_fs2.existsSync)(skillsDir)) return [];
|
|
158
409
|
const commands = [];
|
|
159
|
-
const entries = (0,
|
|
410
|
+
const entries = (0, import_node_fs2.readdirSync)(skillsDir, { withFileTypes: true });
|
|
160
411
|
for (const entry of entries) {
|
|
161
412
|
if (!entry.isDirectory()) continue;
|
|
162
|
-
const skillFile = (0,
|
|
163
|
-
if (!(0,
|
|
164
|
-
const content = (0,
|
|
413
|
+
const skillFile = (0, import_node_path2.join)(skillsDir, entry.name, "SKILL.md");
|
|
414
|
+
if (!(0, import_node_fs2.existsSync)(skillFile)) continue;
|
|
415
|
+
const content = (0, import_node_fs2.readFileSync)(skillFile, "utf-8");
|
|
165
416
|
const frontmatter = parseFrontmatter(content);
|
|
166
417
|
commands.push({
|
|
167
418
|
name: frontmatter?.name ?? entry.name,
|
|
@@ -181,8 +432,8 @@ var SkillCommandSource = class {
|
|
|
181
432
|
}
|
|
182
433
|
getCommands() {
|
|
183
434
|
if (this.cachedCommands) return this.cachedCommands;
|
|
184
|
-
const projectSkills = scanSkillsDir((0,
|
|
185
|
-
const userSkills = scanSkillsDir((0,
|
|
435
|
+
const projectSkills = scanSkillsDir((0, import_node_path2.join)(this.cwd, ".agents", "skills"));
|
|
436
|
+
const userSkills = scanSkillsDir((0, import_node_path2.join)((0, import_node_os.homedir)(), ".claude", "skills"));
|
|
186
437
|
const seen = new Set(projectSkills.map((cmd) => cmd.name));
|
|
187
438
|
const merged = [...projectSkills];
|
|
188
439
|
for (const cmd of userSkills) {
|
|
@@ -724,8 +975,66 @@ function PermissionPrompt({ request }) {
|
|
|
724
975
|
] });
|
|
725
976
|
}
|
|
726
977
|
|
|
727
|
-
// src/
|
|
978
|
+
// src/utils/tool-call-extractor.ts
|
|
979
|
+
var TOOL_ARG_MAX_LENGTH = 80;
|
|
980
|
+
var TOOL_ARG_TRUNCATE_LENGTH = 77;
|
|
981
|
+
function extractToolCalls(history, startIndex) {
|
|
982
|
+
const lines = [];
|
|
983
|
+
for (let i = startIndex; i < history.length; i++) {
|
|
984
|
+
const msg = history[i];
|
|
985
|
+
if (msg.role === "assistant" && msg.toolCalls) {
|
|
986
|
+
for (const tc of msg.toolCalls) {
|
|
987
|
+
const value = parseFirstArgValue(tc.function.arguments);
|
|
988
|
+
const truncated = value.length > TOOL_ARG_MAX_LENGTH ? value.slice(0, TOOL_ARG_TRUNCATE_LENGTH) + "..." : value;
|
|
989
|
+
lines.push(`${tc.function.name}(${truncated})`);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return lines;
|
|
994
|
+
}
|
|
995
|
+
function parseFirstArgValue(argsJson) {
|
|
996
|
+
try {
|
|
997
|
+
const parsed = JSON.parse(argsJson);
|
|
998
|
+
const firstVal = Object.values(parsed)[0];
|
|
999
|
+
return typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
|
|
1000
|
+
} catch {
|
|
1001
|
+
return argsJson;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// src/ui/StreamingIndicator.tsx
|
|
1006
|
+
var import_ink9 = require("ink");
|
|
728
1007
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1008
|
+
function StreamingIndicator({ text, activeTools }) {
|
|
1009
|
+
const hasTools = activeTools.length > 0;
|
|
1010
|
+
const hasText = text.length > 0;
|
|
1011
|
+
if (!hasTools && !hasText) {
|
|
1012
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "yellow", children: "Thinking..." });
|
|
1013
|
+
}
|
|
1014
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", children: [
|
|
1015
|
+
hasTools && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
1016
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "gray", bold: true, children: "Tools:" }),
|
|
1017
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " }),
|
|
1018
|
+
activeTools.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { color: t.isRunning ? "yellow" : "green", children: [
|
|
1019
|
+
" ",
|
|
1020
|
+
t.isRunning ? "\u27F3" : "\u2713",
|
|
1021
|
+
" ",
|
|
1022
|
+
t.toolName,
|
|
1023
|
+
"(",
|
|
1024
|
+
t.firstArg,
|
|
1025
|
+
")"
|
|
1026
|
+
] }, `${t.toolName}-${i}`))
|
|
1027
|
+
] }),
|
|
1028
|
+
hasText && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
1029
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "cyan", bold: true, children: "Robota:" }),
|
|
1030
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " }),
|
|
1031
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { wrap: "wrap", children: renderMarkdown(text) }) })
|
|
1032
|
+
] })
|
|
1033
|
+
] });
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// src/ui/App.tsx
|
|
1037
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
729
1038
|
var msgIdCounter = 0;
|
|
730
1039
|
function nextId() {
|
|
731
1040
|
msgIdCounter += 1;
|
|
@@ -749,6 +1058,7 @@ var NOOP_TERMINAL = {
|
|
|
749
1058
|
function useSession(props) {
|
|
750
1059
|
const [permissionRequest, setPermissionRequest] = (0, import_react6.useState)(null);
|
|
751
1060
|
const [streamingText, setStreamingText] = (0, import_react6.useState)("");
|
|
1061
|
+
const [activeTools, setActiveTools] = (0, import_react6.useState)([]);
|
|
752
1062
|
const permissionQueueRef = (0, import_react6.useRef)([]);
|
|
753
1063
|
const processingRef = (0, import_react6.useRef)(false);
|
|
754
1064
|
const processNextPermission = (0, import_react6.useCallback)(() => {
|
|
@@ -782,6 +1092,25 @@ function useSession(props) {
|
|
|
782
1092
|
const onTextDelta = (delta) => {
|
|
783
1093
|
setStreamingText((prev) => prev + delta);
|
|
784
1094
|
};
|
|
1095
|
+
const TOOL_ARG_DISPLAY_MAX = 80;
|
|
1096
|
+
const TOOL_ARG_TRUNCATE_AT = 77;
|
|
1097
|
+
const onToolExecution = (event) => {
|
|
1098
|
+
if (event.type === "start") {
|
|
1099
|
+
let firstArg = "";
|
|
1100
|
+
if (event.toolArgs) {
|
|
1101
|
+
const firstVal = Object.values(event.toolArgs)[0];
|
|
1102
|
+
const raw = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal ?? "");
|
|
1103
|
+
firstArg = raw.length > TOOL_ARG_DISPLAY_MAX ? raw.slice(0, TOOL_ARG_TRUNCATE_AT) + "..." : raw;
|
|
1104
|
+
}
|
|
1105
|
+
setActiveTools((prev) => [...prev, { toolName: event.toolName, firstArg, isRunning: true }]);
|
|
1106
|
+
} else {
|
|
1107
|
+
setActiveTools(
|
|
1108
|
+
(prev) => prev.map(
|
|
1109
|
+
(t) => t.toolName === event.toolName && t.isRunning ? { ...t, isRunning: false } : t
|
|
1110
|
+
)
|
|
1111
|
+
);
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
785
1114
|
const paths = (0, import_agent_sdk.projectPaths)(props.cwd ?? process.cwd());
|
|
786
1115
|
sessionRef.current = (0, import_agent_sdk.createSession)({
|
|
787
1116
|
config: props.config,
|
|
@@ -793,11 +1122,15 @@ function useSession(props) {
|
|
|
793
1122
|
permissionMode: props.permissionMode,
|
|
794
1123
|
maxTurns: props.maxTurns,
|
|
795
1124
|
permissionHandler,
|
|
796
|
-
onTextDelta
|
|
1125
|
+
onTextDelta,
|
|
1126
|
+
onToolExecution
|
|
797
1127
|
});
|
|
798
1128
|
}
|
|
799
|
-
const clearStreamingText = (0, import_react6.useCallback)(() =>
|
|
800
|
-
|
|
1129
|
+
const clearStreamingText = (0, import_react6.useCallback)(() => {
|
|
1130
|
+
setStreamingText("");
|
|
1131
|
+
setActiveTools([]);
|
|
1132
|
+
}, []);
|
|
1133
|
+
return { session: sessionRef.current, permissionRequest, streamingText, clearStreamingText, activeTools };
|
|
801
1134
|
}
|
|
802
1135
|
function useMessages() {
|
|
803
1136
|
const [messages, setMessages] = (0, import_react6.useState)([]);
|
|
@@ -806,139 +1139,32 @@ function useMessages() {
|
|
|
806
1139
|
}, []);
|
|
807
1140
|
return { messages, setMessages, addMessage };
|
|
808
1141
|
}
|
|
809
|
-
var
|
|
810
|
-
"Available commands:",
|
|
811
|
-
" /help \u2014 Show this help",
|
|
812
|
-
" /clear \u2014 Clear conversation",
|
|
813
|
-
" /compact [instr] \u2014 Compact context (optional focus instructions)",
|
|
814
|
-
" /mode [m] \u2014 Show/change permission mode",
|
|
815
|
-
" /cost \u2014 Show session info",
|
|
816
|
-
" /reset \u2014 Delete settings and exit",
|
|
817
|
-
" /exit \u2014 Exit CLI"
|
|
818
|
-
].join("\n");
|
|
819
|
-
function handleModeCommand(arg, session, addMessage) {
|
|
820
|
-
const validModes = ["plan", "default", "acceptEdits", "bypassPermissions"];
|
|
821
|
-
if (!arg) {
|
|
822
|
-
addMessage({ role: "system", content: `Current mode: ${session.getPermissionMode()}` });
|
|
823
|
-
} else if (validModes.includes(arg)) {
|
|
824
|
-
session.setPermissionMode(arg);
|
|
825
|
-
addMessage({ role: "system", content: `Permission mode set to: ${arg}` });
|
|
826
|
-
} else {
|
|
827
|
-
addMessage({ role: "system", content: `Invalid mode. Valid: ${validModes.join(" | ")}` });
|
|
828
|
-
}
|
|
829
|
-
return true;
|
|
830
|
-
}
|
|
831
|
-
async function executeSlashCommand(cmd, parts, session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId) {
|
|
832
|
-
switch (cmd) {
|
|
833
|
-
case "help":
|
|
834
|
-
addMessage({ role: "system", content: HELP_TEXT });
|
|
835
|
-
return true;
|
|
836
|
-
case "clear":
|
|
837
|
-
setMessages([]);
|
|
838
|
-
session.clearHistory();
|
|
839
|
-
addMessage({ role: "system", content: "Conversation cleared." });
|
|
840
|
-
return true;
|
|
841
|
-
case "compact": {
|
|
842
|
-
const instructions = parts.slice(1).join(" ").trim() || void 0;
|
|
843
|
-
const before = session.getContextState().usedPercentage;
|
|
844
|
-
addMessage({ role: "system", content: "Compacting context..." });
|
|
845
|
-
await session.compact(instructions);
|
|
846
|
-
const after = session.getContextState().usedPercentage;
|
|
847
|
-
addMessage({
|
|
848
|
-
role: "system",
|
|
849
|
-
content: `Context compacted: ${Math.round(before)}% -> ${Math.round(after)}%`
|
|
850
|
-
});
|
|
851
|
-
return true;
|
|
852
|
-
}
|
|
853
|
-
case "mode":
|
|
854
|
-
return handleModeCommand(parts[1], session, addMessage);
|
|
855
|
-
case "model": {
|
|
856
|
-
const modelId = parts[1];
|
|
857
|
-
if (!modelId) {
|
|
858
|
-
addMessage({ role: "system", content: "Select a model from the /model submenu." });
|
|
859
|
-
return true;
|
|
860
|
-
}
|
|
861
|
-
pendingModelChangeRef.current = modelId;
|
|
862
|
-
setPendingModelId(modelId);
|
|
863
|
-
return true;
|
|
864
|
-
}
|
|
865
|
-
case "cost":
|
|
866
|
-
addMessage({
|
|
867
|
-
role: "system",
|
|
868
|
-
content: `Session: ${session.getSessionId()}
|
|
869
|
-
Messages: ${session.getMessageCount()}`
|
|
870
|
-
});
|
|
871
|
-
return true;
|
|
872
|
-
case "permissions": {
|
|
873
|
-
const mode = session.getPermissionMode();
|
|
874
|
-
const sessionAllowed = session.getSessionAllowedTools();
|
|
875
|
-
const lines = [`Permission mode: ${mode}`];
|
|
876
|
-
if (sessionAllowed.length > 0) {
|
|
877
|
-
lines.push(`Session-approved tools: ${sessionAllowed.join(", ")}`);
|
|
878
|
-
} else {
|
|
879
|
-
lines.push("No session-approved tools.");
|
|
880
|
-
}
|
|
881
|
-
addMessage({ role: "system", content: lines.join("\n") });
|
|
882
|
-
return true;
|
|
883
|
-
}
|
|
884
|
-
case "context": {
|
|
885
|
-
const ctx = session.getContextState();
|
|
886
|
-
addMessage({
|
|
887
|
-
role: "system",
|
|
888
|
-
content: `Context: ${ctx.usedTokens.toLocaleString()} / ${ctx.maxTokens.toLocaleString()} tokens (${Math.round(ctx.usedPercentage)}%)`
|
|
889
|
-
});
|
|
890
|
-
return true;
|
|
891
|
-
}
|
|
892
|
-
case "reset": {
|
|
893
|
-
const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
|
|
894
|
-
const settingsPath = `${home}/.robota/settings.json`;
|
|
895
|
-
if ((0, import_node_fs2.existsSync)(settingsPath)) {
|
|
896
|
-
(0, import_node_fs2.unlinkSync)(settingsPath);
|
|
897
|
-
addMessage({ role: "system", content: `Deleted ${settingsPath}. Exiting...` });
|
|
898
|
-
} else {
|
|
899
|
-
addMessage({ role: "system", content: "No user settings found." });
|
|
900
|
-
}
|
|
901
|
-
setTimeout(() => exit(), 500);
|
|
902
|
-
return true;
|
|
903
|
-
}
|
|
904
|
-
case "exit":
|
|
905
|
-
exit();
|
|
906
|
-
return true;
|
|
907
|
-
default: {
|
|
908
|
-
const skillCmd = registry.getCommands().find((c) => c.name === cmd && c.source === "skill");
|
|
909
|
-
if (skillCmd) {
|
|
910
|
-
addMessage({ role: "system", content: `Invoking skill: ${cmd}` });
|
|
911
|
-
return false;
|
|
912
|
-
}
|
|
913
|
-
addMessage({ role: "system", content: `Unknown command "/${cmd}". Type /help for help.` });
|
|
914
|
-
return true;
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
}
|
|
1142
|
+
var EXIT_DELAY_MS = 500;
|
|
918
1143
|
function useSlashCommands(session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId) {
|
|
919
1144
|
return (0, import_react6.useCallback)(
|
|
920
1145
|
async (input) => {
|
|
921
1146
|
const parts = input.slice(1).split(/\s+/);
|
|
922
1147
|
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
923
|
-
|
|
1148
|
+
const args = parts.slice(1).join(" ");
|
|
1149
|
+
const clearMessages = () => setMessages([]);
|
|
1150
|
+
const result = await executeSlashCommand(cmd, args, session, addMessage, clearMessages, registry);
|
|
1151
|
+
if (result.pendingModelId) {
|
|
1152
|
+
pendingModelChangeRef.current = result.pendingModelId;
|
|
1153
|
+
setPendingModelId(result.pendingModelId);
|
|
1154
|
+
}
|
|
1155
|
+
if (result.exitRequested) {
|
|
1156
|
+
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
1157
|
+
}
|
|
1158
|
+
return result.handled;
|
|
924
1159
|
},
|
|
925
1160
|
[session, addMessage, setMessages, exit, registry, pendingModelChangeRef, setPendingModelId]
|
|
926
1161
|
);
|
|
927
1162
|
}
|
|
928
|
-
function
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink9.Text, { color: "cyan", bold: true, children: [
|
|
932
|
-
"Robota:",
|
|
933
|
-
" "
|
|
934
|
-
] }),
|
|
935
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { children: " " }),
|
|
936
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { wrap: "wrap", children: renderMarkdown(text) }) })
|
|
937
|
-
] });
|
|
938
|
-
}
|
|
939
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: "yellow", children: "Thinking..." });
|
|
1163
|
+
function syncContextState(session, setter) {
|
|
1164
|
+
const ctx = session.getContextState();
|
|
1165
|
+
setter({ percentage: ctx.usedPercentage, usedTokens: ctx.usedTokens, maxTokens: ctx.maxTokens });
|
|
940
1166
|
}
|
|
941
|
-
async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking,
|
|
1167
|
+
async function runSessionPrompt(prompt, session, addMessage, clearStreamingText, setIsThinking, setContextState) {
|
|
942
1168
|
setIsThinking(true);
|
|
943
1169
|
clearStreamingText();
|
|
944
1170
|
const historyBefore = session.getHistory().length;
|
|
@@ -946,29 +1172,15 @@ async function runSessionPrompt(prompt, session, addMessage, clearStreamingText,
|
|
|
946
1172
|
const response = await session.run(prompt);
|
|
947
1173
|
clearStreamingText();
|
|
948
1174
|
const history = session.getHistory();
|
|
949
|
-
const toolLines =
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
for (const tc of msg.toolCalls) {
|
|
954
|
-
let value = "";
|
|
955
|
-
try {
|
|
956
|
-
const parsed = JSON.parse(tc.function.arguments);
|
|
957
|
-
const firstVal = Object.values(parsed)[0];
|
|
958
|
-
value = typeof firstVal === "string" ? firstVal : JSON.stringify(firstVal);
|
|
959
|
-
} catch {
|
|
960
|
-
value = tc.function.arguments;
|
|
961
|
-
}
|
|
962
|
-
const truncated = value.length > 80 ? value.slice(0, 77) + "..." : value;
|
|
963
|
-
toolLines.push(`${tc.function.name}(${truncated})`);
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
}
|
|
1175
|
+
const toolLines = extractToolCalls(
|
|
1176
|
+
history,
|
|
1177
|
+
historyBefore
|
|
1178
|
+
);
|
|
967
1179
|
if (toolLines.length > 0) {
|
|
968
1180
|
addMessage({ role: "tool", content: toolLines.join("\n"), toolName: `${toolLines.length} tools` });
|
|
969
1181
|
}
|
|
970
1182
|
addMessage({ role: "assistant", content: response || "(empty response)" });
|
|
971
|
-
|
|
1183
|
+
syncContextState(session, setContextState);
|
|
972
1184
|
} catch (err) {
|
|
973
1185
|
clearStreamingText();
|
|
974
1186
|
if (err instanceof DOMException && err.name === "AbortError") {
|
|
@@ -997,13 +1209,13 @@ Execute the "${cmd}" skill: ${userInstruction}`;
|
|
|
997
1209
|
}
|
|
998
1210
|
return `Use the "${cmd}" skill: ${userInstruction}`;
|
|
999
1211
|
}
|
|
1000
|
-
function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking,
|
|
1212
|
+
function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamingText, setIsThinking, setContextState, registry) {
|
|
1001
1213
|
return (0, import_react6.useCallback)(
|
|
1002
1214
|
async (input) => {
|
|
1003
1215
|
if (input.startsWith("/")) {
|
|
1004
1216
|
const handled = await handleSlashCommand(input);
|
|
1005
1217
|
if (handled) {
|
|
1006
|
-
|
|
1218
|
+
syncContextState(session, setContextState);
|
|
1007
1219
|
return;
|
|
1008
1220
|
}
|
|
1009
1221
|
const prompt = buildSkillPrompt(input, registry);
|
|
@@ -1014,7 +1226,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
|
|
|
1014
1226
|
addMessage,
|
|
1015
1227
|
clearStreamingText,
|
|
1016
1228
|
setIsThinking,
|
|
1017
|
-
|
|
1229
|
+
setContextState
|
|
1018
1230
|
);
|
|
1019
1231
|
}
|
|
1020
1232
|
addMessage({ role: "user", content: input });
|
|
@@ -1024,7 +1236,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
|
|
|
1024
1236
|
addMessage,
|
|
1025
1237
|
clearStreamingText,
|
|
1026
1238
|
setIsThinking,
|
|
1027
|
-
|
|
1239
|
+
setContextState
|
|
1028
1240
|
);
|
|
1029
1241
|
},
|
|
1030
1242
|
[
|
|
@@ -1033,7 +1245,7 @@ function useSubmitHandler(session, addMessage, handleSlashCommand, clearStreamin
|
|
|
1033
1245
|
handleSlashCommand,
|
|
1034
1246
|
clearStreamingText,
|
|
1035
1247
|
setIsThinking,
|
|
1036
|
-
|
|
1248
|
+
setContextState,
|
|
1037
1249
|
registry
|
|
1038
1250
|
]
|
|
1039
1251
|
);
|
|
@@ -1049,11 +1261,11 @@ function useCommandRegistry(cwd) {
|
|
|
1049
1261
|
return registryRef.current;
|
|
1050
1262
|
}
|
|
1051
1263
|
function App(props) {
|
|
1052
|
-
const { exit } = (0,
|
|
1053
|
-
const { session, permissionRequest, streamingText, clearStreamingText } = useSession(props);
|
|
1264
|
+
const { exit } = (0, import_ink10.useApp)();
|
|
1265
|
+
const { session, permissionRequest, streamingText, clearStreamingText, activeTools } = useSession(props);
|
|
1054
1266
|
const { messages, setMessages, addMessage } = useMessages();
|
|
1055
1267
|
const [isThinking, setIsThinking] = (0, import_react6.useState)(false);
|
|
1056
|
-
const [
|
|
1268
|
+
const [contextState, setContextState] = (0, import_react6.useState)({ percentage: 0, usedTokens: 0, maxTokens: 0 });
|
|
1057
1269
|
const registry = useCommandRegistry(props.cwd ?? process.cwd());
|
|
1058
1270
|
const pendingModelChangeRef = (0, import_react6.useRef)(null);
|
|
1059
1271
|
const [pendingModelId, setPendingModelId] = (0, import_react6.useState)(null);
|
|
@@ -1064,36 +1276,36 @@ function App(props) {
|
|
|
1064
1276
|
handleSlashCommand,
|
|
1065
1277
|
clearStreamingText,
|
|
1066
1278
|
setIsThinking,
|
|
1067
|
-
|
|
1279
|
+
setContextState,
|
|
1068
1280
|
registry
|
|
1069
1281
|
);
|
|
1070
|
-
(0,
|
|
1282
|
+
(0, import_ink10.useInput)(
|
|
1071
1283
|
(_input, key) => {
|
|
1072
1284
|
if (key.ctrl && _input === "c") exit();
|
|
1073
1285
|
if (key.escape && isThinking) session.abort();
|
|
1074
1286
|
},
|
|
1075
1287
|
{ isActive: !permissionRequest }
|
|
1076
1288
|
);
|
|
1077
|
-
return /* @__PURE__ */ (0,
|
|
1078
|
-
/* @__PURE__ */ (0,
|
|
1079
|
-
/* @__PURE__ */ (0,
|
|
1289
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
|
|
1290
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
1291
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "cyan", bold: true, children: `
|
|
1080
1292
|
____ ___ ____ ___ _____ _
|
|
1081
1293
|
| _ \\ / _ \\| __ ) / _ \\_ _|/ \\
|
|
1082
1294
|
| |_) | | | | _ \\| | | || | / _ \\
|
|
1083
1295
|
| _ <| |_| | |_) | |_| || |/ ___ \\
|
|
1084
1296
|
|_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
|
|
1085
1297
|
` }),
|
|
1086
|
-
/* @__PURE__ */ (0,
|
|
1298
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Text, { dimColor: true, children: [
|
|
1087
1299
|
" v",
|
|
1088
1300
|
props.version ?? "0.0.0"
|
|
1089
1301
|
] })
|
|
1090
1302
|
] }),
|
|
1091
|
-
/* @__PURE__ */ (0,
|
|
1092
|
-
/* @__PURE__ */ (0,
|
|
1093
|
-
isThinking && /* @__PURE__ */ (0,
|
|
1303
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
|
|
1304
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MessageList, { messages }),
|
|
1305
|
+
isThinking && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StreamingIndicator, { text: streamingText, activeTools }) })
|
|
1094
1306
|
] }),
|
|
1095
|
-
permissionRequest && /* @__PURE__ */ (0,
|
|
1096
|
-
pendingModelId && /* @__PURE__ */ (0,
|
|
1307
|
+
permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PermissionPrompt, { request: permissionRequest }),
|
|
1308
|
+
pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1097
1309
|
ConfirmPrompt,
|
|
1098
1310
|
{
|
|
1099
1311
|
message: `Change model to ${(0, import_agent_core3.getModelName)(pendingModelId)}? This will restart the session.`,
|
|
@@ -1102,17 +1314,8 @@ function App(props) {
|
|
|
1102
1314
|
pendingModelChangeRef.current = null;
|
|
1103
1315
|
if (index === 0) {
|
|
1104
1316
|
try {
|
|
1105
|
-
const
|
|
1106
|
-
|
|
1107
|
-
let settings = {};
|
|
1108
|
-
if ((0, import_node_fs2.existsSync)(settingsPath)) {
|
|
1109
|
-
settings = JSON.parse((0, import_node_fs2.readFileSync)(settingsPath, "utf8"));
|
|
1110
|
-
}
|
|
1111
|
-
const provider = settings.provider ?? {};
|
|
1112
|
-
provider.model = pendingModelId;
|
|
1113
|
-
settings.provider = provider;
|
|
1114
|
-
(0, import_node_fs2.mkdirSync)((0, import_node_path2.join)(home, ".robota"), { recursive: true });
|
|
1115
|
-
(0, import_node_fs2.writeFileSync)(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
1317
|
+
const settingsPath = getUserSettingsPath();
|
|
1318
|
+
updateModelInSettings(settingsPath, pendingModelId);
|
|
1116
1319
|
addMessage({ role: "system", content: `Model changed to ${(0, import_agent_core3.getModelName)(pendingModelId)}. Restarting...` });
|
|
1117
1320
|
setTimeout(() => exit(), 500);
|
|
1118
1321
|
} catch (err) {
|
|
@@ -1124,7 +1327,7 @@ function App(props) {
|
|
|
1124
1327
|
}
|
|
1125
1328
|
}
|
|
1126
1329
|
),
|
|
1127
|
-
/* @__PURE__ */ (0,
|
|
1330
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1128
1331
|
StatusBar,
|
|
1129
1332
|
{
|
|
1130
1333
|
permissionMode: session.getPermissionMode(),
|
|
@@ -1132,12 +1335,12 @@ function App(props) {
|
|
|
1132
1335
|
sessionId: session.getSessionId(),
|
|
1133
1336
|
messageCount: messages.length,
|
|
1134
1337
|
isThinking,
|
|
1135
|
-
contextPercentage,
|
|
1136
|
-
contextUsedTokens:
|
|
1137
|
-
contextMaxTokens:
|
|
1338
|
+
contextPercentage: contextState.percentage,
|
|
1339
|
+
contextUsedTokens: contextState.usedTokens,
|
|
1340
|
+
contextMaxTokens: contextState.maxTokens
|
|
1138
1341
|
}
|
|
1139
1342
|
),
|
|
1140
|
-
/* @__PURE__ */ (0,
|
|
1343
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1141
1344
|
InputArea,
|
|
1142
1345
|
{
|
|
1143
1346
|
onSubmit: handleSubmit,
|
|
@@ -1145,12 +1348,12 @@ function App(props) {
|
|
|
1145
1348
|
registry
|
|
1146
1349
|
}
|
|
1147
1350
|
),
|
|
1148
|
-
/* @__PURE__ */ (0,
|
|
1351
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { children: " " })
|
|
1149
1352
|
] });
|
|
1150
1353
|
}
|
|
1151
1354
|
|
|
1152
1355
|
// src/ui/render.tsx
|
|
1153
|
-
var
|
|
1356
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1154
1357
|
function renderApp(options) {
|
|
1155
1358
|
process.on("unhandledRejection", (reason) => {
|
|
1156
1359
|
process.stderr.write(`
|
|
@@ -1161,7 +1364,7 @@ function renderApp(options) {
|
|
|
1161
1364
|
`);
|
|
1162
1365
|
}
|
|
1163
1366
|
});
|
|
1164
|
-
const instance = (0,
|
|
1367
|
+
const instance = (0, import_ink11.render)(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(App, { ...options }), {
|
|
1165
1368
|
exitOnCtrlC: true
|
|
1166
1369
|
});
|
|
1167
1370
|
instance.waitUntilExit().catch((err) => {
|
|
@@ -1175,7 +1378,18 @@ function renderApp(options) {
|
|
|
1175
1378
|
|
|
1176
1379
|
// src/cli.ts
|
|
1177
1380
|
var import_meta = {};
|
|
1178
|
-
|
|
1381
|
+
function hasValidSettingsFile(filePath) {
|
|
1382
|
+
if (!(0, import_node_fs3.existsSync)(filePath)) return false;
|
|
1383
|
+
try {
|
|
1384
|
+
const raw = (0, import_node_fs3.readFileSync)(filePath, "utf8").trim();
|
|
1385
|
+
if (raw.length === 0) return false;
|
|
1386
|
+
const parsed = JSON.parse(raw);
|
|
1387
|
+
const provider = parsed.provider;
|
|
1388
|
+
return !!provider?.apiKey;
|
|
1389
|
+
} catch {
|
|
1390
|
+
return false;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1179
1393
|
function readVersion() {
|
|
1180
1394
|
try {
|
|
1181
1395
|
const thisFile = (0, import_node_url.fileURLToPath)(import_meta.url);
|
|
@@ -1196,108 +1410,11 @@ function readVersion() {
|
|
|
1196
1410
|
return "0.0.0";
|
|
1197
1411
|
}
|
|
1198
1412
|
}
|
|
1199
|
-
function parsePermissionMode(raw) {
|
|
1200
|
-
if (raw === void 0) return void 0;
|
|
1201
|
-
if (!VALID_MODES.includes(raw)) {
|
|
1202
|
-
process.stderr.write(`Invalid --permission-mode "${raw}". Valid: ${VALID_MODES.join(" | ")}
|
|
1203
|
-
`);
|
|
1204
|
-
process.exit(1);
|
|
1205
|
-
}
|
|
1206
|
-
return raw;
|
|
1207
|
-
}
|
|
1208
|
-
function parseMaxTurns(raw) {
|
|
1209
|
-
if (raw === void 0) return void 0;
|
|
1210
|
-
const n = parseInt(raw, 10);
|
|
1211
|
-
if (isNaN(n) || n <= 0) {
|
|
1212
|
-
process.stderr.write(`Invalid --max-turns "${raw}". Must be a positive integer.
|
|
1213
|
-
`);
|
|
1214
|
-
process.exit(1);
|
|
1215
|
-
}
|
|
1216
|
-
return n;
|
|
1217
|
-
}
|
|
1218
|
-
function parseCliArgs() {
|
|
1219
|
-
const { values, positionals } = (0, import_node_util.parseArgs)({
|
|
1220
|
-
allowPositionals: true,
|
|
1221
|
-
options: {
|
|
1222
|
-
p: { type: "boolean", short: "p", default: false },
|
|
1223
|
-
c: { type: "boolean", short: "c", default: false },
|
|
1224
|
-
r: { type: "string", short: "r" },
|
|
1225
|
-
model: { type: "string" },
|
|
1226
|
-
"permission-mode": { type: "string" },
|
|
1227
|
-
"max-turns": { type: "string" },
|
|
1228
|
-
version: { type: "boolean", default: false },
|
|
1229
|
-
reset: { type: "boolean", default: false }
|
|
1230
|
-
}
|
|
1231
|
-
});
|
|
1232
|
-
return {
|
|
1233
|
-
positional: positionals,
|
|
1234
|
-
printMode: values["p"] ?? false,
|
|
1235
|
-
continueMode: values["c"] ?? false,
|
|
1236
|
-
resumeId: values["r"],
|
|
1237
|
-
model: values["model"],
|
|
1238
|
-
permissionMode: parsePermissionMode(values["permission-mode"]),
|
|
1239
|
-
maxTurns: parseMaxTurns(values["max-turns"]),
|
|
1240
|
-
version: values["version"] ?? false,
|
|
1241
|
-
reset: values["reset"] ?? false
|
|
1242
|
-
};
|
|
1243
|
-
}
|
|
1244
|
-
var PrintTerminal = class {
|
|
1245
|
-
write(text) {
|
|
1246
|
-
process.stdout.write(text);
|
|
1247
|
-
}
|
|
1248
|
-
writeLine(text) {
|
|
1249
|
-
process.stdout.write(text + "\n");
|
|
1250
|
-
}
|
|
1251
|
-
writeMarkdown(md) {
|
|
1252
|
-
process.stdout.write(md);
|
|
1253
|
-
}
|
|
1254
|
-
writeError(text) {
|
|
1255
|
-
process.stderr.write(text + "\n");
|
|
1256
|
-
}
|
|
1257
|
-
prompt(question) {
|
|
1258
|
-
return new Promise((resolve) => {
|
|
1259
|
-
const rl = readline.createInterface({
|
|
1260
|
-
input: process.stdin,
|
|
1261
|
-
output: process.stdout,
|
|
1262
|
-
terminal: false,
|
|
1263
|
-
historySize: 0
|
|
1264
|
-
});
|
|
1265
|
-
rl.question(question, (answer) => {
|
|
1266
|
-
rl.close();
|
|
1267
|
-
resolve(answer);
|
|
1268
|
-
});
|
|
1269
|
-
});
|
|
1270
|
-
}
|
|
1271
|
-
async select(options, initialIndex = 0) {
|
|
1272
|
-
for (let i = 0; i < options.length; i++) {
|
|
1273
|
-
const marker = i === initialIndex ? ">" : " ";
|
|
1274
|
-
process.stdout.write(` ${marker} ${i + 1}) ${options[i]}
|
|
1275
|
-
`);
|
|
1276
|
-
}
|
|
1277
|
-
const answer = await this.prompt(
|
|
1278
|
-
` Choose [1-${options.length}] (default: ${options[initialIndex]}): `
|
|
1279
|
-
);
|
|
1280
|
-
const trimmed = answer.trim().toLowerCase();
|
|
1281
|
-
if (trimmed === "") return initialIndex;
|
|
1282
|
-
const num = parseInt(trimmed, 10);
|
|
1283
|
-
if (!isNaN(num) && num >= 1 && num <= options.length) return num - 1;
|
|
1284
|
-
return initialIndex;
|
|
1285
|
-
}
|
|
1286
|
-
spinner(_message) {
|
|
1287
|
-
return { stop() {
|
|
1288
|
-
}, update() {
|
|
1289
|
-
} };
|
|
1290
|
-
}
|
|
1291
|
-
};
|
|
1292
|
-
function getUserSettingsPath() {
|
|
1293
|
-
const home = process.env.HOME ?? process.env.USERPROFILE ?? "/";
|
|
1294
|
-
return (0, import_node_path3.join)(home, ".robota", "settings.json");
|
|
1295
|
-
}
|
|
1296
1413
|
async function ensureConfig(cwd) {
|
|
1297
1414
|
const userPath = getUserSettingsPath();
|
|
1298
1415
|
const projectPath = (0, import_node_path3.join)(cwd, ".robota", "settings.json");
|
|
1299
1416
|
const localPath = (0, import_node_path3.join)(cwd, ".robota", "settings.local.json");
|
|
1300
|
-
if ((
|
|
1417
|
+
if (hasValidSettingsFile(userPath) || hasValidSettingsFile(projectPath) || hasValidSettingsFile(localPath)) {
|
|
1301
1418
|
return;
|
|
1302
1419
|
}
|
|
1303
1420
|
process.stdout.write("\n");
|
|
@@ -1358,8 +1475,7 @@ async function ensureConfig(cwd) {
|
|
|
1358
1475
|
}
|
|
1359
1476
|
function resetConfig() {
|
|
1360
1477
|
const userPath = getUserSettingsPath();
|
|
1361
|
-
if ((
|
|
1362
|
-
(0, import_node_fs3.unlinkSync)(userPath);
|
|
1478
|
+
if (deleteSettings(userPath)) {
|
|
1363
1479
|
process.stdout.write(`Deleted ${userPath}
|
|
1364
1480
|
`);
|
|
1365
1481
|
} else {
|