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