@robota-sdk/agent-cli 3.0.0-beta.54 → 3.0.0-beta.55
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 +48 -17
- package/dist/node/bin.js +4 -2
- package/dist/node/chunk-2JAZDNYT.js +246 -0
- package/dist/node/chunk-H3NRW5FW.js +4372 -0
- package/dist/node/index.cjs +2572 -682
- package/dist/node/index.d.cts +55 -3
- package/dist/node/index.d.ts +55 -3
- package/dist/node/index.js +12 -1
- package/dist/node/subagents/child-process-subagent-worker.d.ts +2 -0
- package/dist/node/subagents/child-process-subagent-worker.js +123 -0
- package/package.json +17 -10
- package/dist/node/chunk-ASBCJDLC.js +0 -2648
package/dist/node/index.cjs
CHANGED
|
@@ -30,15 +30,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
ChildProcessSubagentRunner: () => ChildProcessSubagentRunner,
|
|
34
|
+
GitWorktreeIsolationAdapter: () => GitWorktreeIsolationAdapter,
|
|
35
|
+
createChildProcessSubagentRunnerFactory: () => createChildProcessSubagentRunnerFactory,
|
|
36
|
+
createGitWorktreeIsolationAdapter: () => createGitWorktreeIsolationAdapter,
|
|
37
|
+
createManagedShellProcessRunner: () => createManagedShellProcessRunner,
|
|
33
38
|
startCli: () => startCli
|
|
34
39
|
});
|
|
35
40
|
module.exports = __toCommonJS(index_exports);
|
|
36
41
|
|
|
37
42
|
// src/cli.ts
|
|
38
|
-
var
|
|
39
|
-
var
|
|
43
|
+
var import_node_fs6 = require("fs");
|
|
44
|
+
var import_node_path8 = require("path");
|
|
40
45
|
var import_node_url = require("url");
|
|
41
|
-
var
|
|
46
|
+
var import_agent_sdk8 = require("@robota-sdk/agent-sdk");
|
|
42
47
|
var import_agent_sessions = require("@robota-sdk/agent-sessions");
|
|
43
48
|
|
|
44
49
|
// src/utils/cli-args.ts
|
|
@@ -80,7 +85,20 @@ function parseCliArgs() {
|
|
|
80
85
|
"system-prompt": { type: "string" },
|
|
81
86
|
"append-system-prompt": { type: "string" },
|
|
82
87
|
version: { type: "boolean", default: false },
|
|
83
|
-
reset: { type: "boolean", default: false }
|
|
88
|
+
reset: { type: "boolean", default: false },
|
|
89
|
+
bare: { type: "boolean", default: false },
|
|
90
|
+
"allowed-tools": { type: "string" },
|
|
91
|
+
"no-session-persistence": { type: "boolean", default: false },
|
|
92
|
+
"json-schema": { type: "string" },
|
|
93
|
+
configure: { type: "boolean", default: false },
|
|
94
|
+
"configure-provider": { type: "string" },
|
|
95
|
+
provider: { type: "string" },
|
|
96
|
+
type: { type: "string" },
|
|
97
|
+
"base-url": { type: "string" },
|
|
98
|
+
"api-key": { type: "string" },
|
|
99
|
+
"api-key-env": { type: "string" },
|
|
100
|
+
"set-current": { type: "boolean", default: false },
|
|
101
|
+
"settings-scope": { type: "string" }
|
|
84
102
|
}
|
|
85
103
|
});
|
|
86
104
|
return {
|
|
@@ -98,7 +116,20 @@ function parseCliArgs() {
|
|
|
98
116
|
systemPrompt: values["system-prompt"],
|
|
99
117
|
appendSystemPrompt: values["append-system-prompt"],
|
|
100
118
|
version: values["version"] ?? false,
|
|
101
|
-
reset: values["reset"] ?? false
|
|
119
|
+
reset: values["reset"] ?? false,
|
|
120
|
+
bare: values["bare"] ?? false,
|
|
121
|
+
allowedTools: values["allowed-tools"],
|
|
122
|
+
noSessionPersistence: values["no-session-persistence"] ?? false,
|
|
123
|
+
jsonSchema: values["json-schema"],
|
|
124
|
+
configure: values["configure"] ?? false,
|
|
125
|
+
configureProvider: values["configure-provider"],
|
|
126
|
+
provider: values["provider"],
|
|
127
|
+
providerType: values["type"],
|
|
128
|
+
baseURL: values["base-url"],
|
|
129
|
+
apiKey: values["api-key"],
|
|
130
|
+
apiKeyEnv: values["api-key-env"],
|
|
131
|
+
setCurrent: values["set-current"] ?? false,
|
|
132
|
+
settingsScope: values["settings-scope"]
|
|
102
133
|
};
|
|
103
134
|
}
|
|
104
135
|
|
|
@@ -126,11 +157,26 @@ function writeSettings(path, settings) {
|
|
|
126
157
|
}
|
|
127
158
|
function updateModelInSettings(settingsPath, modelId) {
|
|
128
159
|
const settings = readSettings(settingsPath);
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
160
|
+
const currentProvider = settings.currentProvider;
|
|
161
|
+
const providers = settings.providers;
|
|
162
|
+
if (typeof currentProvider === "string" && isSettingsData(providers)) {
|
|
163
|
+
const providerMap = providers;
|
|
164
|
+
providerMap[currentProvider] = {
|
|
165
|
+
...isSettingsData(providerMap[currentProvider]) ? providerMap[currentProvider] : {},
|
|
166
|
+
model: modelId
|
|
167
|
+
};
|
|
168
|
+
settings.providers = providerMap;
|
|
169
|
+
} else {
|
|
170
|
+
settings.provider = {
|
|
171
|
+
...isSettingsData(settings.provider) ? settings.provider : {},
|
|
172
|
+
model: modelId
|
|
173
|
+
};
|
|
174
|
+
}
|
|
132
175
|
writeSettings(settingsPath, settings);
|
|
133
176
|
}
|
|
177
|
+
function isSettingsData(value) {
|
|
178
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
|
|
179
|
+
}
|
|
134
180
|
function deleteSettings(path) {
|
|
135
181
|
if ((0, import_node_fs.existsSync)(path)) {
|
|
136
182
|
(0, import_node_fs.unlinkSync)(path);
|
|
@@ -143,62 +189,556 @@ function deleteSettings(path) {
|
|
|
143
189
|
var import_node_fs2 = require("fs");
|
|
144
190
|
var import_node_path2 = require("path");
|
|
145
191
|
var import_node_os = require("os");
|
|
192
|
+
|
|
193
|
+
// src/utils/provider-default-definitions.ts
|
|
146
194
|
var import_agent_provider_anthropic = require("@robota-sdk/agent-provider-anthropic");
|
|
147
|
-
|
|
195
|
+
var import_agent_provider_gemma = require("@robota-sdk/agent-provider-gemma");
|
|
196
|
+
var import_agent_provider_openai = require("@robota-sdk/agent-provider-openai");
|
|
197
|
+
var DEFAULT_PROVIDER_DEFINITIONS = [
|
|
198
|
+
(0, import_agent_provider_anthropic.createAnthropicProviderDefinition)(),
|
|
199
|
+
(0, import_agent_provider_openai.createOpenAIProviderDefinition)(),
|
|
200
|
+
(0, import_agent_provider_gemma.createGemmaProviderDefinition)()
|
|
201
|
+
];
|
|
202
|
+
|
|
203
|
+
// src/utils/provider-definition.ts
|
|
204
|
+
var import_agent_core = require("@robota-sdk/agent-core");
|
|
205
|
+
|
|
206
|
+
// src/utils/provider-factory.ts
|
|
207
|
+
function readProviderSettings(cwd, options = {}) {
|
|
208
|
+
const merged = readMergedProviderSettings(cwd);
|
|
209
|
+
const providerConfig = resolveActiveProvider(
|
|
210
|
+
merged,
|
|
211
|
+
options.providerOverride,
|
|
212
|
+
getProviderDefinitions(options)
|
|
213
|
+
);
|
|
214
|
+
if (providerConfig !== void 0) {
|
|
215
|
+
return providerConfig;
|
|
216
|
+
}
|
|
217
|
+
throw new Error("No provider configuration found. Run `robota` to set up.");
|
|
218
|
+
}
|
|
219
|
+
function readMergedProviderSettings(cwd) {
|
|
148
220
|
const paths = [
|
|
149
|
-
(0, import_node_path2.join)(
|
|
221
|
+
(0, import_node_path2.join)((0, import_node_os.homedir)(), ".robota", "settings.json"),
|
|
222
|
+
(0, import_node_path2.join)((0, import_node_os.homedir)(), ".claude", "settings.json"),
|
|
150
223
|
(0, import_node_path2.join)(cwd, ".robota", "settings.json"),
|
|
151
|
-
(0, import_node_path2.join)(cwd, ".
|
|
224
|
+
(0, import_node_path2.join)(cwd, ".robota", "settings.local.json"),
|
|
152
225
|
(0, import_node_path2.join)(cwd, ".claude", "settings.json"),
|
|
153
|
-
(0, import_node_path2.join)(
|
|
154
|
-
(0, import_node_path2.join)((0, import_node_os.homedir)(), ".claude", "settings.json")
|
|
226
|
+
(0, import_node_path2.join)(cwd, ".claude", "settings.local.json")
|
|
155
227
|
];
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const parsed = JSON.parse(raw);
|
|
161
|
-
const provider = parsed.provider;
|
|
162
|
-
if (provider?.apiKey && provider?.name) {
|
|
163
|
-
return {
|
|
164
|
-
name: provider.name,
|
|
165
|
-
model: provider.model ?? "claude-sonnet-4-6",
|
|
166
|
-
apiKey: provider.apiKey
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
} catch {
|
|
170
|
-
continue;
|
|
228
|
+
return paths.reduce((settings, filePath) => {
|
|
229
|
+
const parsed = readSettingsFile(filePath);
|
|
230
|
+
if (parsed === void 0) {
|
|
231
|
+
return settings;
|
|
171
232
|
}
|
|
233
|
+
return mergeSettings(settings, parsed);
|
|
234
|
+
}, {});
|
|
235
|
+
}
|
|
236
|
+
function readSettingsFile(filePath) {
|
|
237
|
+
if (!(0, import_node_fs2.existsSync)(filePath)) {
|
|
238
|
+
return void 0;
|
|
172
239
|
}
|
|
173
|
-
|
|
240
|
+
try {
|
|
241
|
+
const raw = (0, import_node_fs2.readFileSync)(filePath, "utf8");
|
|
242
|
+
return JSON.parse(raw);
|
|
243
|
+
} catch {
|
|
244
|
+
return void 0;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function mergeSettings(base, override) {
|
|
248
|
+
return {
|
|
249
|
+
...base,
|
|
250
|
+
...override,
|
|
251
|
+
provider: base.provider !== void 0 || override.provider !== void 0 ? { ...base.provider, ...override.provider } : void 0,
|
|
252
|
+
providers: base.providers !== void 0 || override.providers !== void 0 ? mergeProviders(base.providers, override.providers) : void 0
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function mergeProviders(base, override) {
|
|
256
|
+
const result = { ...base ?? {} };
|
|
257
|
+
for (const [name, profile] of Object.entries(override ?? {})) {
|
|
258
|
+
result[name] = { ...result[name], ...profile };
|
|
259
|
+
}
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
262
|
+
function resolveActiveProvider(settings, providerOverride, providerDefinitions = DEFAULT_PROVIDER_DEFINITIONS) {
|
|
263
|
+
const activeProvider = providerOverride ?? settings.currentProvider;
|
|
264
|
+
if (activeProvider !== void 0) {
|
|
265
|
+
const profile = settings.providers?.[activeProvider];
|
|
266
|
+
if (profile === void 0) {
|
|
267
|
+
throw new Error(`Provider profile "${activeProvider}" was not found in providers`);
|
|
268
|
+
}
|
|
269
|
+
if (!profile.type) {
|
|
270
|
+
throw new Error(`Provider profile "${activeProvider}" is missing type`);
|
|
271
|
+
}
|
|
272
|
+
return normalizeProviderConfig(
|
|
273
|
+
{
|
|
274
|
+
name: profile.type,
|
|
275
|
+
model: profile.model,
|
|
276
|
+
apiKey: profile.apiKey,
|
|
277
|
+
baseURL: profile.baseURL,
|
|
278
|
+
timeout: profile.timeout
|
|
279
|
+
},
|
|
280
|
+
providerDefinitions
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
const provider = settings.provider;
|
|
284
|
+
if (provider?.name) {
|
|
285
|
+
return normalizeProviderConfig(
|
|
286
|
+
{
|
|
287
|
+
name: provider.name,
|
|
288
|
+
model: provider.model,
|
|
289
|
+
apiKey: provider.apiKey,
|
|
290
|
+
baseURL: provider.baseURL,
|
|
291
|
+
timeout: provider.timeout
|
|
292
|
+
},
|
|
293
|
+
providerDefinitions
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
return void 0;
|
|
174
297
|
}
|
|
175
|
-
function
|
|
176
|
-
const
|
|
298
|
+
function normalizeProviderConfig(settings, providerDefinitions) {
|
|
299
|
+
const defaults = (0, import_agent_core.findProviderDefinition)(providerDefinitions, settings.name)?.defaults ?? {};
|
|
300
|
+
const model = settings.model ?? defaults.model;
|
|
301
|
+
if (!model) {
|
|
302
|
+
throw new Error(`Provider ${settings.name} requires model`);
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
name: settings.name,
|
|
306
|
+
model,
|
|
307
|
+
apiKey: settings.apiKey !== void 0 ? resolveEnvRef(settings.apiKey) : defaults.apiKey,
|
|
308
|
+
baseURL: settings.baseURL ?? defaults.baseURL,
|
|
309
|
+
timeout: settings.timeout
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function resolveEnvRef(value) {
|
|
313
|
+
const envPrefix = "$ENV:";
|
|
314
|
+
if (!value.startsWith(envPrefix)) {
|
|
315
|
+
return value;
|
|
316
|
+
}
|
|
317
|
+
const envName = value.slice(envPrefix.length);
|
|
318
|
+
return process.env[envName] ?? value;
|
|
319
|
+
}
|
|
320
|
+
function createProviderFromConfig(settings, providerDefinitions) {
|
|
321
|
+
const definition = (0, import_agent_core.findProviderDefinition)(providerDefinitions, settings.name);
|
|
322
|
+
if (definition === void 0) {
|
|
323
|
+
throw new Error(
|
|
324
|
+
`Unknown provider: ${settings.name}. Currently supported: ${(0, import_agent_core.formatSupportedProviderTypes)(providerDefinitions)}`
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
if (definition.requiresApiKey === true && !settings.apiKey) {
|
|
328
|
+
throw new Error(`Provider ${settings.name} requires apiKey`);
|
|
329
|
+
}
|
|
330
|
+
return definition.createProvider(settings);
|
|
331
|
+
}
|
|
332
|
+
function createProviderFromSettings(cwd, modelOverride, options = {}) {
|
|
333
|
+
const providerDefinitions = getProviderDefinitions(options);
|
|
334
|
+
const settings = readProviderSettings(cwd, { ...options, providerDefinitions });
|
|
177
335
|
const model = modelOverride ?? settings.model;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
336
|
+
return createProviderFromConfig({ ...settings, model }, providerDefinitions);
|
|
337
|
+
}
|
|
338
|
+
function getProviderDefinitions(options) {
|
|
339
|
+
return options.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/utils/provider-setup.ts
|
|
343
|
+
var import_node_path3 = require("path");
|
|
344
|
+
var import_node_os2 = require("os");
|
|
345
|
+
|
|
346
|
+
// src/utils/settings-check.ts
|
|
347
|
+
var import_node_fs3 = require("fs");
|
|
348
|
+
function checkSettingsFile(filePath, providerDefinitions = []) {
|
|
349
|
+
if (!(0, import_node_fs3.existsSync)(filePath)) return "missing";
|
|
350
|
+
try {
|
|
351
|
+
const raw = (0, import_node_fs3.readFileSync)(filePath, "utf8").trim();
|
|
352
|
+
if (raw.length === 0) return "incomplete";
|
|
353
|
+
const parsed = JSON.parse(raw);
|
|
354
|
+
if (!hasUsableProviderConfig(parsed, providerDefinitions)) return "incomplete";
|
|
355
|
+
return "valid";
|
|
356
|
+
} catch {
|
|
357
|
+
return "corrupt";
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function hasUsableProviderConfig(settings, providerDefinitions) {
|
|
361
|
+
if (settings.provider && isUsableProviderProfile(settings.provider.name, settings.provider, providerDefinitions)) {
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
if (typeof settings.currentProvider !== "string") {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
const profile = settings.providers?.[settings.currentProvider];
|
|
368
|
+
return isUsableProviderProfile(profile?.type, profile, providerDefinitions);
|
|
369
|
+
}
|
|
370
|
+
function isUsableProviderProfile(type, profile, providerDefinitions) {
|
|
371
|
+
if (!profile) {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
if (profile.apiKey) {
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
if (!type) {
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
const definition = (0, import_agent_core.findProviderDefinition)(providerDefinitions, type);
|
|
381
|
+
if (definition === void 0) {
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
return definition.requiresApiKey !== true || definition.defaults?.apiKey !== void 0;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// src/utils/provider-settings.ts
|
|
388
|
+
function upsertProviderProfile(settings, profileName, profile) {
|
|
389
|
+
return {
|
|
390
|
+
...settings,
|
|
391
|
+
providers: {
|
|
392
|
+
...settings.providers ?? {},
|
|
393
|
+
[profileName]: profile
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function setCurrentProvider(settings, profileName) {
|
|
398
|
+
if (!settings.providers?.[profileName]) {
|
|
399
|
+
throw new Error(`Provider profile "${profileName}" was not found`);
|
|
400
|
+
}
|
|
401
|
+
return {
|
|
402
|
+
...settings,
|
|
403
|
+
currentProvider: profileName
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
function validateProviderProfile(profileName, profile, options = {}) {
|
|
407
|
+
if (!profile.type) {
|
|
408
|
+
throw new Error(`Provider profile "${profileName}" is missing type`);
|
|
409
|
+
}
|
|
410
|
+
if (!profile.model) {
|
|
411
|
+
throw new Error(`Provider profile "${profileName}" is missing model`);
|
|
412
|
+
}
|
|
413
|
+
const definition = (0, import_agent_core.findProviderDefinition)(options.providerDefinitions ?? [], profile.type);
|
|
414
|
+
if (definition?.requiresApiKey === true && !profile.apiKey && definition.defaults?.apiKey === void 0) {
|
|
415
|
+
throw new Error(`Provider profile "${profileName}" is missing apiKey`);
|
|
183
416
|
}
|
|
184
417
|
}
|
|
418
|
+
function buildProviderSetupPatch(input, options = {}) {
|
|
419
|
+
const profile = buildProviderProfile(input, options);
|
|
420
|
+
validateProviderProfile(input.profile, profile, options);
|
|
421
|
+
return {
|
|
422
|
+
...input.setCurrent && { currentProvider: input.profile },
|
|
423
|
+
providers: {
|
|
424
|
+
[input.profile]: profile
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
function buildProviderProfile(input, options = {}) {
|
|
429
|
+
const defaults = getProviderDefaults(input.type, options.providerDefinitions ?? []);
|
|
430
|
+
const apiKey = input.apiKeyEnv !== void 0 ? `$ENV:${input.apiKeyEnv}` : input.apiKey ?? defaults.apiKey;
|
|
431
|
+
const baseURL = input.baseURL ?? defaults.baseURL;
|
|
432
|
+
return {
|
|
433
|
+
type: input.type,
|
|
434
|
+
model: input.model ?? defaults.model,
|
|
435
|
+
...apiKey !== void 0 && { apiKey },
|
|
436
|
+
...baseURL !== void 0 && { baseURL },
|
|
437
|
+
...input.timeout !== void 0 && { timeout: input.timeout }
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
function getProviderDefaults(type, providerDefinitions) {
|
|
441
|
+
return (0, import_agent_core.findProviderDefinition)(providerDefinitions, type)?.defaults ?? {};
|
|
442
|
+
}
|
|
443
|
+
function mergeProviderPatch(settings, patch) {
|
|
444
|
+
const [profileName, profile] = Object.entries(patch.providers)[0] ?? [];
|
|
445
|
+
if (!profileName || !profile) {
|
|
446
|
+
return settings;
|
|
447
|
+
}
|
|
448
|
+
const withProfile = upsertProviderProfile(settings, profileName, profile);
|
|
449
|
+
return patch.currentProvider ? setCurrentProvider(withProfile, patch.currentProvider) : withProfile;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// src/utils/provider-configuration.ts
|
|
453
|
+
function readProviderDocument(settingsPath) {
|
|
454
|
+
return readSettings(settingsPath);
|
|
455
|
+
}
|
|
456
|
+
function applyProviderConfiguration(settingsPath, input, options = {}) {
|
|
457
|
+
const settings = readProviderDocument(settingsPath);
|
|
458
|
+
const patch = buildProviderSetupPatch(input, options);
|
|
459
|
+
const next = mergeProviderPatch(settings, patch);
|
|
460
|
+
writeSettings(settingsPath, next);
|
|
461
|
+
return next;
|
|
462
|
+
}
|
|
463
|
+
function applyProviderSwitch(settingsPath, profileName, options = {}) {
|
|
464
|
+
const settings = readProviderDocument(settingsPath);
|
|
465
|
+
const hasLocalProfile = settings.providers?.[profileName] !== void 0;
|
|
466
|
+
const hasKnownProfile = options.knownProviders?.[profileName] !== void 0;
|
|
467
|
+
const next = hasLocalProfile || hasKnownProfile ? { ...settings, currentProvider: profileName } : setCurrentProvider(settings, profileName);
|
|
468
|
+
writeSettings(settingsPath, next);
|
|
469
|
+
return next;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// src/utils/provider-setup-flow.ts
|
|
473
|
+
function createProviderSetupFlow(type, providerDefinitions) {
|
|
474
|
+
return {
|
|
475
|
+
type,
|
|
476
|
+
steps: getProviderSetupSteps(type, providerDefinitions),
|
|
477
|
+
stepIndex: 0,
|
|
478
|
+
values: {}
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
function getProviderSetupStep(state) {
|
|
482
|
+
const step = state.steps[state.stepIndex];
|
|
483
|
+
if (step === void 0) {
|
|
484
|
+
throw new Error(`Provider setup step ${state.stepIndex} is out of range`);
|
|
485
|
+
}
|
|
486
|
+
return step;
|
|
487
|
+
}
|
|
488
|
+
function submitProviderSetupValue(state, rawValue) {
|
|
489
|
+
const step = getProviderSetupStep(state);
|
|
490
|
+
const value = rawValue.trim() || step.defaultValue || "";
|
|
491
|
+
const validationMessage = validateProviderSetupValue(step, value);
|
|
492
|
+
if (validationMessage !== void 0) {
|
|
493
|
+
return { status: "error", state, message: validationMessage };
|
|
494
|
+
}
|
|
495
|
+
const nextState = {
|
|
496
|
+
...state,
|
|
497
|
+
stepIndex: state.stepIndex + 1,
|
|
498
|
+
values: { ...state.values, [step.key]: value }
|
|
499
|
+
};
|
|
500
|
+
if (nextState.stepIndex < state.steps.length) {
|
|
501
|
+
return { status: "next", state: nextState };
|
|
502
|
+
}
|
|
503
|
+
return { status: "complete", input: buildProviderSetupInput(nextState) };
|
|
504
|
+
}
|
|
505
|
+
async function runProviderSetupPromptFlow(type, promptInput2, providerDefinitions) {
|
|
506
|
+
let state = createProviderSetupFlow(type, providerDefinitions);
|
|
507
|
+
const stepCount = state.steps.length;
|
|
508
|
+
while (state.stepIndex < stepCount) {
|
|
509
|
+
const step = getProviderSetupStep(state);
|
|
510
|
+
const value = await promptInput2(formatProviderSetupPromptLabel(step), step.masked === true);
|
|
511
|
+
const result = submitProviderSetupValue(state, value);
|
|
512
|
+
if (result.status === "complete") {
|
|
513
|
+
return result.input;
|
|
514
|
+
}
|
|
515
|
+
if (result.status === "error") {
|
|
516
|
+
throw new Error(result.message);
|
|
517
|
+
}
|
|
518
|
+
state = result.state;
|
|
519
|
+
}
|
|
520
|
+
throw new Error("Provider setup flow ended without completion");
|
|
521
|
+
}
|
|
522
|
+
function formatProviderSetupPromptLabel(step) {
|
|
523
|
+
const suffix = step.defaultValue !== void 0 ? ` (default: ${step.defaultValue})` : "";
|
|
524
|
+
return ` ${step.title}${suffix}: `;
|
|
525
|
+
}
|
|
526
|
+
function validateProviderSetupValue(step, value) {
|
|
527
|
+
if (step.required === true && value.length === 0) {
|
|
528
|
+
return "Required";
|
|
529
|
+
}
|
|
530
|
+
return void 0;
|
|
531
|
+
}
|
|
532
|
+
function getProviderSetupSteps(type, providerDefinitions) {
|
|
533
|
+
const definition = (0, import_agent_core.findProviderDefinition)(providerDefinitions, type);
|
|
534
|
+
if (definition === void 0) {
|
|
535
|
+
throw new Error(
|
|
536
|
+
`Unknown provider: ${type}. Currently supported: ${(0, import_agent_core.formatSupportedProviderTypes)(providerDefinitions)}`
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
if (definition.setupSteps !== void 0) {
|
|
540
|
+
return [...definition.setupSteps];
|
|
541
|
+
}
|
|
542
|
+
const steps = [
|
|
543
|
+
{
|
|
544
|
+
key: "model",
|
|
545
|
+
title: `${definition.type} model`,
|
|
546
|
+
defaultValue: definition.defaults?.model,
|
|
547
|
+
required: definition.defaults?.model === void 0
|
|
548
|
+
}
|
|
549
|
+
];
|
|
550
|
+
if (definition.defaults?.baseURL !== void 0) {
|
|
551
|
+
steps.unshift({
|
|
552
|
+
key: "baseURL",
|
|
553
|
+
title: `${definition.type} base URL`,
|
|
554
|
+
defaultValue: definition.defaults.baseURL
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
if (definition.requiresApiKey === true) {
|
|
558
|
+
steps.push({
|
|
559
|
+
key: "apiKey",
|
|
560
|
+
title: `${definition.type} API key`,
|
|
561
|
+
defaultValue: definition.defaults?.apiKey,
|
|
562
|
+
required: definition.defaults?.apiKey === void 0,
|
|
563
|
+
masked: true
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
return steps;
|
|
567
|
+
}
|
|
568
|
+
function buildProviderSetupInput(state) {
|
|
569
|
+
return {
|
|
570
|
+
profile: state.type,
|
|
571
|
+
type: state.type,
|
|
572
|
+
model: state.values.model,
|
|
573
|
+
apiKey: state.values.apiKey,
|
|
574
|
+
...state.values.baseURL !== void 0 && { baseURL: state.values.baseURL },
|
|
575
|
+
setCurrent: true
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// src/utils/provider-setup.ts
|
|
580
|
+
function getSettingsPathForScope(cwd, scope) {
|
|
581
|
+
if (scope === void 0 || scope === "user") {
|
|
582
|
+
return getUserSettingsPath();
|
|
583
|
+
}
|
|
584
|
+
if (scope === "project-local") {
|
|
585
|
+
return (0, import_node_path3.join)(cwd, ".robota", "settings.local.json");
|
|
586
|
+
}
|
|
587
|
+
throw new Error(`Invalid --settings-scope "${scope}". Valid: user | project-local`);
|
|
588
|
+
}
|
|
589
|
+
function handleProviderConfigurationArgs(cwd, args, providerDefinitions = DEFAULT_PROVIDER_DEFINITIONS) {
|
|
590
|
+
const settingsPath = getSettingsPathForScope(cwd, args.settingsScope);
|
|
591
|
+
if (args.configureProvider) {
|
|
592
|
+
applyProviderConfiguration(settingsPath, buildSetupInputFromArgs(args), {
|
|
593
|
+
providerDefinitions
|
|
594
|
+
});
|
|
595
|
+
process.stdout.write(`Provider profile saved to ${settingsPath}
|
|
596
|
+
`);
|
|
597
|
+
return !args.printMode && args.positional.length === 0;
|
|
598
|
+
}
|
|
599
|
+
if (args.provider && args.setCurrent) {
|
|
600
|
+
applyProviderSwitch(settingsPath, args.provider, {
|
|
601
|
+
knownProviders: readMergedProviderSettings(cwd).providers
|
|
602
|
+
});
|
|
603
|
+
process.stdout.write(`Current provider set to ${args.provider}
|
|
604
|
+
`);
|
|
605
|
+
return !args.printMode && args.positional.length === 0;
|
|
606
|
+
}
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
609
|
+
async function ensureConfig(cwd, args, promptInput2, providerDefinitions = DEFAULT_PROVIDER_DEFINITIONS) {
|
|
610
|
+
const checks = getSettingsCheckPaths(cwd).map((path) => ({
|
|
611
|
+
path,
|
|
612
|
+
status: checkSettingsFile(path, providerDefinitions)
|
|
613
|
+
}));
|
|
614
|
+
if (checks.some((check) => check.status === "valid")) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
if (!isInteractiveTerminal()) {
|
|
618
|
+
throw new Error(formatMissingProviderConfigMessage());
|
|
619
|
+
}
|
|
620
|
+
await runInteractiveProviderSetup(cwd, args, promptInput2, providerDefinitions);
|
|
621
|
+
}
|
|
622
|
+
async function runInteractiveProviderSetup(cwd, args, promptInput2, providerDefinitions = DEFAULT_PROVIDER_DEFINITIONS) {
|
|
623
|
+
const defaultProviderType = providerDefinitions[0]?.type ?? "";
|
|
624
|
+
const supportedTypes = providerDefinitions.map((definition) => definition.type).join("/");
|
|
625
|
+
const providerChoice = await promptInput2(` Provider (${supportedTypes}, default: ${defaultProviderType}): `) || defaultProviderType;
|
|
626
|
+
const type = parseProviderSetupType(providerChoice);
|
|
627
|
+
const settingsPath = getSettingsPathForScope(cwd, args.settingsScope);
|
|
628
|
+
const input = await runProviderSetupPromptFlow(type, promptInput2, providerDefinitions);
|
|
629
|
+
applyProviderConfiguration(settingsPath, input, {
|
|
630
|
+
providerDefinitions
|
|
631
|
+
});
|
|
632
|
+
const language = await promptInput2(" Response language (ko/en/ja/zh, default: en): ");
|
|
633
|
+
if (language) {
|
|
634
|
+
const settings = readSettings(settingsPath);
|
|
635
|
+
settings.language = language;
|
|
636
|
+
writeSettings(settingsPath, settings);
|
|
637
|
+
}
|
|
638
|
+
process.stdout.write(`
|
|
639
|
+
Config saved to ${settingsPath}
|
|
640
|
+
|
|
641
|
+
`);
|
|
642
|
+
}
|
|
643
|
+
function parseProviderSetupType(value) {
|
|
644
|
+
return value.trim();
|
|
645
|
+
}
|
|
646
|
+
function buildSetupInputFromArgs(args) {
|
|
647
|
+
const type = args.providerType ?? args.configureProvider;
|
|
648
|
+
if (!args.configureProvider || !type) {
|
|
649
|
+
throw new Error("--configure-provider requires a provider profile and --type");
|
|
650
|
+
}
|
|
651
|
+
return {
|
|
652
|
+
profile: args.configureProvider,
|
|
653
|
+
type,
|
|
654
|
+
...args.model !== void 0 && { model: args.model },
|
|
655
|
+
...args.apiKey !== void 0 && { apiKey: args.apiKey },
|
|
656
|
+
...args.apiKeyEnv !== void 0 && { apiKeyEnv: args.apiKeyEnv },
|
|
657
|
+
...args.baseURL !== void 0 && { baseURL: args.baseURL },
|
|
658
|
+
setCurrent: args.setCurrent
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
function getSettingsCheckPaths(cwd) {
|
|
662
|
+
return [
|
|
663
|
+
getUserSettingsPath(),
|
|
664
|
+
(0, import_node_path3.join)((0, import_node_os2.homedir)(), ".claude", "settings.json"),
|
|
665
|
+
(0, import_node_path3.join)(cwd, ".robota", "settings.json"),
|
|
666
|
+
(0, import_node_path3.join)(cwd, ".robota", "settings.local.json"),
|
|
667
|
+
(0, import_node_path3.join)(cwd, ".claude", "settings.json"),
|
|
668
|
+
(0, import_node_path3.join)(cwd, ".claude", "settings.local.json")
|
|
669
|
+
];
|
|
670
|
+
}
|
|
671
|
+
function isInteractiveTerminal() {
|
|
672
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
673
|
+
}
|
|
674
|
+
function formatMissingProviderConfigMessage() {
|
|
675
|
+
return [
|
|
676
|
+
"No provider configuration found.",
|
|
677
|
+
"Run `robota --configure` in an interactive terminal, or configure a provider:",
|
|
678
|
+
" robota --configure-provider gemma --type gemma --base-url http://localhost:1234/v1 --model supergemma4-26b-uncensored-v2 --api-key lm-studio --set-current",
|
|
679
|
+
" robota --configure-provider openai --type openai --model <openai-compatible-model> --api-key-env OPENAI_API_KEY --set-current"
|
|
680
|
+
].join("\n");
|
|
681
|
+
}
|
|
185
682
|
|
|
186
683
|
// src/cli.ts
|
|
187
684
|
var import_agent_transport_headless = require("@robota-sdk/agent-transport-headless");
|
|
188
685
|
|
|
189
686
|
// src/ui/render.tsx
|
|
190
|
-
var
|
|
687
|
+
var import_ink18 = require("ink");
|
|
191
688
|
|
|
192
689
|
// src/ui/App.tsx
|
|
193
|
-
var
|
|
194
|
-
var
|
|
195
|
-
var
|
|
690
|
+
var import_react18 = require("react");
|
|
691
|
+
var import_ink17 = require("ink");
|
|
692
|
+
var import_agent_core7 = require("@robota-sdk/agent-core");
|
|
196
693
|
|
|
197
694
|
// src/ui/hooks/useInteractiveSession.ts
|
|
198
695
|
var import_react2 = require("react");
|
|
199
|
-
var
|
|
200
|
-
var
|
|
201
|
-
var
|
|
696
|
+
var import_node_os3 = require("os");
|
|
697
|
+
var import_node_path4 = require("path");
|
|
698
|
+
var import_agent_sdk = require("@robota-sdk/agent-sdk");
|
|
699
|
+
var import_agent_core3 = require("@robota-sdk/agent-core");
|
|
700
|
+
|
|
701
|
+
// src/ui/background-task-view-model.ts
|
|
702
|
+
var BACKGROUND_PREVIEW_LENGTH = 120;
|
|
703
|
+
var BACKGROUND_PREVIEW_WHITESPACE = /\s+/g;
|
|
704
|
+
var BACKGROUND_PREVIEW_SEPARATOR = " ";
|
|
705
|
+
var SUCCESS_EXIT_CODE = 0;
|
|
706
|
+
function toBackgroundTaskViewModel(state, partialText) {
|
|
707
|
+
return {
|
|
708
|
+
id: state.id,
|
|
709
|
+
kind: state.kind,
|
|
710
|
+
label: state.label,
|
|
711
|
+
status: state.status,
|
|
712
|
+
statusLabel: getBackgroundTaskStatusLabel(state),
|
|
713
|
+
mode: state.mode,
|
|
714
|
+
currentAction: state.currentAction,
|
|
715
|
+
unread: state.unread,
|
|
716
|
+
preview: trimBackgroundPreview(state.promptPreview ?? state.commandPreview) ?? "",
|
|
717
|
+
resultPreview: trimBackgroundPreview(state.result?.output ?? partialText),
|
|
718
|
+
errorPreview: trimBackgroundPreview(state.error?.message),
|
|
719
|
+
startedAt: state.startedAt,
|
|
720
|
+
lastActivityAt: state.lastActivityAt,
|
|
721
|
+
timeoutReason: state.timeoutReason
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
function getBackgroundTaskStatusLabel(state) {
|
|
725
|
+
if (state.status === "failed" && state.timeoutReason) {
|
|
726
|
+
if (state.timeoutReason === "idle" || state.timeoutReason === "max_runtime") {
|
|
727
|
+
return "timed out";
|
|
728
|
+
}
|
|
729
|
+
return state.timeoutReason.replace(/_/g, " ");
|
|
730
|
+
}
|
|
731
|
+
return state.status;
|
|
732
|
+
}
|
|
733
|
+
function shouldHideAtNextUserTurn(state) {
|
|
734
|
+
return state.status === "completed" && !state.error && (state.result?.exitCode === void 0 || state.result.exitCode === SUCCESS_EXIT_CODE) && !state.result?.signalCode && !state.worktreePath && !state.branchName;
|
|
735
|
+
}
|
|
736
|
+
function trimBackgroundPreview(value) {
|
|
737
|
+
if (!value) return void 0;
|
|
738
|
+
const preview = value.trim().replace(BACKGROUND_PREVIEW_WHITESPACE, BACKGROUND_PREVIEW_SEPARATOR);
|
|
739
|
+
if (!preview) return void 0;
|
|
740
|
+
return preview.length > BACKGROUND_PREVIEW_LENGTH ? `${preview.slice(0, BACKGROUND_PREVIEW_LENGTH)}...` : preview;
|
|
741
|
+
}
|
|
202
742
|
|
|
203
743
|
// src/ui/tui-state-manager.ts
|
|
204
744
|
var MAX_RENDERED_MESSAGES = 100;
|
|
@@ -231,10 +771,13 @@ var TuiStateManager = class {
|
|
|
231
771
|
isAborting = false;
|
|
232
772
|
pendingPrompt = null;
|
|
233
773
|
contextState = { percentage: 0, usedTokens: 0, maxTokens: 0 };
|
|
774
|
+
backgroundTasks = [];
|
|
234
775
|
/** Called after any state change. React hook sets this to trigger re-render. */
|
|
235
776
|
onChange = null;
|
|
236
777
|
// ── Internal ──────────────────────────────────────────────────
|
|
237
778
|
streamBuf = "";
|
|
779
|
+
backgroundTextBuffers = /* @__PURE__ */ new Map();
|
|
780
|
+
backgroundTasksHiddenOnNextTurn = /* @__PURE__ */ new Set();
|
|
238
781
|
debouncedStreamNotify = createDebouncedNotify(() => this.notify(), STREAMING_DEBOUNCE_MS);
|
|
239
782
|
notify() {
|
|
240
783
|
this.onChange?.();
|
|
@@ -296,6 +839,30 @@ var TuiStateManager = class {
|
|
|
296
839
|
this.activeTools = [];
|
|
297
840
|
this.notify();
|
|
298
841
|
};
|
|
842
|
+
onBackgroundTaskEvent = (event) => {
|
|
843
|
+
if ("task" in event) {
|
|
844
|
+
this.upsertBackgroundTask(event.task);
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
if (event.type === "background_task_closed") {
|
|
848
|
+
this.backgroundTextBuffers.delete(event.taskId);
|
|
849
|
+
this.backgroundTasksHiddenOnNextTurn.delete(event.taskId);
|
|
850
|
+
this.backgroundTasks = this.backgroundTasks.filter((task) => task.id !== event.taskId);
|
|
851
|
+
this.notify();
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
if (event.type === "background_task_text_delta") {
|
|
855
|
+
this.appendBackgroundTaskText(event.taskId, event.delta);
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
if (event.type === "background_task_tool_start") {
|
|
859
|
+
this.updateBackgroundTaskAction(event.taskId, event.firstArg ?? event.toolName);
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
if (event.type === "background_task_tool_end") {
|
|
863
|
+
this.updateBackgroundTaskAction(event.taskId, event.success ? void 0 : event.error);
|
|
864
|
+
}
|
|
865
|
+
};
|
|
299
866
|
// ── State updates from external sources ───────────────────────
|
|
300
867
|
/** Sync history from InteractiveSession */
|
|
301
868
|
syncHistory(entries) {
|
|
@@ -324,16 +891,176 @@ var TuiStateManager = class {
|
|
|
324
891
|
this.contextState = state;
|
|
325
892
|
this.notify();
|
|
326
893
|
}
|
|
894
|
+
onUserTurnAccepted() {
|
|
895
|
+
if (this.backgroundTasksHiddenOnNextTurn.size === 0) return;
|
|
896
|
+
const visible = this.backgroundTasks.filter(
|
|
897
|
+
(task) => !this.backgroundTasksHiddenOnNextTurn.has(task.id)
|
|
898
|
+
);
|
|
899
|
+
this.backgroundTasksHiddenOnNextTurn.clear();
|
|
900
|
+
if (visible.length === this.backgroundTasks.length) return;
|
|
901
|
+
this.backgroundTasks = visible;
|
|
902
|
+
this.notify();
|
|
903
|
+
}
|
|
904
|
+
upsertBackgroundTask(state) {
|
|
905
|
+
const partialText = state.result ? void 0 : this.backgroundTextBuffers.get(state.id);
|
|
906
|
+
const viewModel = toBackgroundTaskViewModel(state, partialText);
|
|
907
|
+
const index = this.backgroundTasks.findIndex((task) => task.id === state.id);
|
|
908
|
+
if (index === -1) {
|
|
909
|
+
this.backgroundTasks = [...this.backgroundTasks, viewModel];
|
|
910
|
+
} else {
|
|
911
|
+
const updated = [...this.backgroundTasks];
|
|
912
|
+
updated[index] = viewModel;
|
|
913
|
+
this.backgroundTasks = updated;
|
|
914
|
+
}
|
|
915
|
+
if (state.status === "completed" || state.status === "failed" || state.status === "cancelled") {
|
|
916
|
+
this.backgroundTextBuffers.delete(state.id);
|
|
917
|
+
}
|
|
918
|
+
if (shouldHideAtNextUserTurn(state)) {
|
|
919
|
+
this.backgroundTasksHiddenOnNextTurn.add(state.id);
|
|
920
|
+
} else {
|
|
921
|
+
this.backgroundTasksHiddenOnNextTurn.delete(state.id);
|
|
922
|
+
}
|
|
923
|
+
this.notify();
|
|
924
|
+
}
|
|
925
|
+
appendBackgroundTaskText(taskId, delta) {
|
|
926
|
+
const nextText = `${this.backgroundTextBuffers.get(taskId) ?? ""}${delta}`;
|
|
927
|
+
this.backgroundTextBuffers.set(taskId, nextText);
|
|
928
|
+
this.backgroundTasks = this.backgroundTasks.map(
|
|
929
|
+
(task) => task.id === taskId ? { ...task, resultPreview: trimBackgroundPreview(nextText) } : task
|
|
930
|
+
);
|
|
931
|
+
this.notify();
|
|
932
|
+
}
|
|
933
|
+
updateBackgroundTaskAction(taskId, currentAction) {
|
|
934
|
+
this.backgroundTasks = this.backgroundTasks.map(
|
|
935
|
+
(task) => task.id === taskId ? { ...task, currentAction } : task
|
|
936
|
+
);
|
|
937
|
+
this.notify();
|
|
938
|
+
}
|
|
327
939
|
};
|
|
328
940
|
|
|
329
941
|
// src/ui/hooks/useSlashRouting.ts
|
|
330
942
|
var import_react = require("react");
|
|
331
943
|
var import_node_crypto = require("crypto");
|
|
332
|
-
var
|
|
333
|
-
|
|
334
|
-
|
|
944
|
+
var import_agent_core2 = require("@robota-sdk/agent-core");
|
|
945
|
+
|
|
946
|
+
// src/utils/provider-command.ts
|
|
947
|
+
async function handleProviderCommand(cwd, args, deps = {}) {
|
|
948
|
+
const settings = readMergedProviderSettings(cwd);
|
|
949
|
+
const providerDefinitions = deps.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS;
|
|
950
|
+
const [subcommand = "current", profileArg] = args.trim().split(/\s+/);
|
|
951
|
+
if (subcommand === "list") {
|
|
952
|
+
return {
|
|
953
|
+
message: formatProviderList(settings.currentProvider, settings.providers),
|
|
954
|
+
success: true
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
if (subcommand === "current" || subcommand === "") {
|
|
958
|
+
return {
|
|
959
|
+
message: formatCurrentProvider(settings.currentProvider, settings.providers),
|
|
960
|
+
success: true
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
if (subcommand === "use") {
|
|
964
|
+
return buildProviderSwitch(settings.providers, profileArg);
|
|
965
|
+
}
|
|
966
|
+
if (subcommand === "test") {
|
|
967
|
+
return await testProvider(settings.currentProvider, settings.providers, profileArg, deps);
|
|
968
|
+
}
|
|
969
|
+
if (subcommand === "add") {
|
|
970
|
+
return buildProviderSetup(profileArg, providerDefinitions);
|
|
971
|
+
}
|
|
972
|
+
return {
|
|
973
|
+
message: "Usage: provider [current|list|use <profile>|add <type>|test [profile]]",
|
|
974
|
+
success: false
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
function formatProviderList(currentProvider, providers) {
|
|
978
|
+
const entries = Object.entries(providers ?? {});
|
|
979
|
+
if (entries.length === 0) {
|
|
980
|
+
return "No provider profiles configured.";
|
|
981
|
+
}
|
|
982
|
+
return entries.map(([name, profile]) => {
|
|
983
|
+
const marker = name === currentProvider ? "*" : "-";
|
|
984
|
+
return `${marker} ${name}: ${profile.type ?? "unknown"} ${profile.model ?? "(no model)"}`;
|
|
985
|
+
}).join("\n");
|
|
986
|
+
}
|
|
987
|
+
function formatCurrentProvider(currentProvider, providers) {
|
|
988
|
+
if (!currentProvider) {
|
|
989
|
+
return "No current provider configured.";
|
|
990
|
+
}
|
|
991
|
+
const profile = providers?.[currentProvider];
|
|
992
|
+
if (!profile) {
|
|
993
|
+
return `Current provider "${currentProvider}" was not found in providers.`;
|
|
994
|
+
}
|
|
995
|
+
return [
|
|
996
|
+
`Current provider: ${currentProvider}`,
|
|
997
|
+
`Type: ${profile.type ?? "unknown"}`,
|
|
998
|
+
`Model: ${profile.model ?? "(no model)"}`,
|
|
999
|
+
...profile.baseURL ? [`Base URL: ${profile.baseURL}`] : []
|
|
1000
|
+
].join("\n");
|
|
1001
|
+
}
|
|
1002
|
+
function buildProviderSwitch(providers, profileName) {
|
|
1003
|
+
if (!profileName) {
|
|
1004
|
+
return { message: "Usage: provider use <profile>", success: false };
|
|
1005
|
+
}
|
|
1006
|
+
if (!providers?.[profileName]) {
|
|
1007
|
+
return { message: `Provider profile "${profileName}" was not found.`, success: false };
|
|
1008
|
+
}
|
|
1009
|
+
return {
|
|
1010
|
+
message: `Provider change requested: ${profileName}`,
|
|
1011
|
+
success: true,
|
|
1012
|
+
data: { providerSwitch: { profile: profileName } }
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
function buildProviderSetup(type, providerDefinitions) {
|
|
1016
|
+
if (!type || (0, import_agent_core.findProviderDefinition)(providerDefinitions, type) === void 0) {
|
|
1017
|
+
return {
|
|
1018
|
+
message: `Usage: provider add <type>. Supported: ${(0, import_agent_core.formatSupportedProviderTypes)(providerDefinitions)}`,
|
|
1019
|
+
success: false
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
return {
|
|
1023
|
+
message: `Provider setup requested: ${type}`,
|
|
1024
|
+
success: true,
|
|
1025
|
+
data: { providerSetup: { type } }
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
async function testProvider(currentProvider, providers, profileArg, deps) {
|
|
1029
|
+
const profileName = profileArg ?? currentProvider;
|
|
1030
|
+
if (!profileName) {
|
|
1031
|
+
return { message: "No provider profile selected.", success: false };
|
|
1032
|
+
}
|
|
1033
|
+
const profile = providers?.[profileName];
|
|
1034
|
+
if (!profile) {
|
|
1035
|
+
return { message: `Provider profile "${profileName}" was not found.`, success: false };
|
|
1036
|
+
}
|
|
1037
|
+
try {
|
|
1038
|
+
validateProviderProfile(profileName, profile, {
|
|
1039
|
+
providerDefinitions: deps.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS
|
|
1040
|
+
});
|
|
1041
|
+
} catch (error) {
|
|
1042
|
+
return { message: error instanceof Error ? error.message : String(error), success: false };
|
|
1043
|
+
}
|
|
1044
|
+
const providerDefinitions = deps.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS;
|
|
1045
|
+
const definition = profile.type ? (0, import_agent_core.findProviderDefinition)(providerDefinitions, profile.type) : void 0;
|
|
1046
|
+
const probe = deps.probe ?? definition?.probeProfile ?? probeProviderProfile;
|
|
1047
|
+
const result = await probe(profile);
|
|
1048
|
+
return {
|
|
1049
|
+
message: result.ok ? `Provider "${profileName}" test passed: ${result.message}` : `Provider "${profileName}" test failed: ${result.message}; manual configuration can continue.`,
|
|
1050
|
+
success: true,
|
|
1051
|
+
data: { providerTest: { profile: profileName } }
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
async function probeProviderProfile(profile) {
|
|
1055
|
+
void profile;
|
|
1056
|
+
return { ok: true, message: "Profile fields are valid; no endpoint probe configured." };
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// src/ui/hooks/useSlashRouting.ts
|
|
1060
|
+
function useSlashRouting(cwd, interactiveSession, registry, manager, providerDefinitions) {
|
|
335
1061
|
return (0, import_react.useCallback)(
|
|
336
1062
|
async (input) => {
|
|
1063
|
+
manager.onUserTurnAccepted();
|
|
337
1064
|
if (!input.startsWith("/")) {
|
|
338
1065
|
await interactiveSession.submit(input);
|
|
339
1066
|
manager.setPendingPrompt(interactiveSession.getPendingPrompt());
|
|
@@ -342,81 +1069,114 @@ function useSlashRouting(interactiveSession, registry, manager) {
|
|
|
342
1069
|
const parts = input.slice(1).split(/\s+/);
|
|
343
1070
|
const cmd = parts[0]?.toLowerCase() ?? "";
|
|
344
1071
|
const args = parts.slice(1).join(" ");
|
|
1072
|
+
if (cmd === "provider") {
|
|
1073
|
+
await routeProviderCommand(cwd, args, interactiveSession, manager, providerDefinitions);
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
345
1076
|
const result = await interactiveSession.executeCommand(cmd, args);
|
|
346
1077
|
if (result) {
|
|
347
|
-
|
|
348
|
-
const effects = interactiveSession;
|
|
349
|
-
if (result.data?.modelId) {
|
|
350
|
-
effects._pendingModelId = result.data.modelId;
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
if (result.data?.language) {
|
|
354
|
-
effects._pendingLanguage = result.data.language;
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
if (result.data?.resetRequested) {
|
|
358
|
-
effects._resetRequested = true;
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
if (result.data?.triggerResumePicker) {
|
|
362
|
-
effects._triggerResumePicker = true;
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
if (result.data?.name) {
|
|
366
|
-
effects._sessionName = result.data.name;
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
const ctx = interactiveSession.getContextState();
|
|
370
|
-
manager.setContextState({
|
|
371
|
-
percentage: ctx.usedPercentage,
|
|
372
|
-
usedTokens: ctx.usedTokens,
|
|
373
|
-
maxTokens: ctx.maxTokens
|
|
374
|
-
});
|
|
1078
|
+
applySystemCommandResult(result, interactiveSession, manager);
|
|
375
1079
|
return;
|
|
376
1080
|
}
|
|
377
|
-
|
|
378
|
-
if (skillCmd) {
|
|
379
|
-
manager.addEntry({
|
|
380
|
-
id: (0, import_node_crypto.randomUUID)(),
|
|
381
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
382
|
-
category: "event",
|
|
383
|
-
type: "skill-invocation",
|
|
384
|
-
data: {
|
|
385
|
-
skillName: cmd,
|
|
386
|
-
source: skillCmd.source,
|
|
387
|
-
message: `Invoking ${skillCmd.source}: ${cmd}`
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
const prompt = await (0, import_agent_sdk.buildSkillPrompt)(input, registry);
|
|
391
|
-
if (prompt) {
|
|
392
|
-
const qualifiedName = registry.resolveQualifiedName(cmd);
|
|
393
|
-
const hookInput = qualifiedName ? `/${qualifiedName}${input.slice(1 + cmd.length)}` : input;
|
|
394
|
-
await interactiveSession.submit(prompt, input, hookInput);
|
|
395
|
-
manager.setPendingPrompt(interactiveSession.getPendingPrompt());
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
if (cmd === "exit") {
|
|
400
|
-
interactiveSession._exitRequested = true;
|
|
1081
|
+
if (await routeSkillCommand(input, cmd, registry, interactiveSession, manager)) {
|
|
401
1082
|
return;
|
|
402
1083
|
}
|
|
403
|
-
if (cmd
|
|
404
|
-
interactiveSession._triggerPluginTUI = true;
|
|
1084
|
+
if (routeTuiCommand(cmd, interactiveSession)) {
|
|
405
1085
|
return;
|
|
406
1086
|
}
|
|
407
1087
|
manager.addEntry(
|
|
408
|
-
(0,
|
|
409
|
-
(0,
|
|
1088
|
+
(0, import_agent_core2.messageToHistoryEntry)(
|
|
1089
|
+
(0, import_agent_core2.createSystemMessage)(`Unknown command "/${cmd}". Type /help for help.`)
|
|
410
1090
|
)
|
|
411
1091
|
);
|
|
412
1092
|
},
|
|
413
|
-
[interactiveSession, registry, manager]
|
|
1093
|
+
[cwd, interactiveSession, registry, manager, providerDefinitions]
|
|
414
1094
|
);
|
|
415
1095
|
}
|
|
1096
|
+
async function routeProviderCommand(cwd, args, interactiveSession, manager, providerDefinitions) {
|
|
1097
|
+
const result = await handleProviderCommand(cwd, args, { providerDefinitions });
|
|
1098
|
+
manager.addEntry((0, import_agent_core2.messageToHistoryEntry)((0, import_agent_core2.createSystemMessage)(result.message)));
|
|
1099
|
+
const providerSwitch = result.data?.providerSwitch;
|
|
1100
|
+
if (providerSwitch?.profile) {
|
|
1101
|
+
getEffects(interactiveSession)._pendingProviderProfile = providerSwitch.profile;
|
|
1102
|
+
}
|
|
1103
|
+
const providerSetup = result.data?.providerSetup;
|
|
1104
|
+
if (providerSetup?.type) {
|
|
1105
|
+
getEffects(interactiveSession)._pendingProviderSetupType = providerSetup.type;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
function applySystemCommandResult(result, interactiveSession, manager) {
|
|
1109
|
+
manager.addEntry((0, import_agent_core2.messageToHistoryEntry)((0, import_agent_core2.createSystemMessage)(result.message)));
|
|
1110
|
+
const data = result.data;
|
|
1111
|
+
const effects = getEffects(interactiveSession);
|
|
1112
|
+
if (typeof data?.modelId === "string") {
|
|
1113
|
+
effects._pendingModelId = data.modelId;
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
if (typeof data?.language === "string") {
|
|
1117
|
+
effects._pendingLanguage = data.language;
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
if (data?.resetRequested === true) {
|
|
1121
|
+
effects._resetRequested = true;
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
if (data?.triggerResumePicker === true) {
|
|
1125
|
+
effects._triggerResumePicker = true;
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
if (typeof data?.name === "string") {
|
|
1129
|
+
effects._sessionName = data.name;
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
const ctx = interactiveSession.getContextState();
|
|
1133
|
+
manager.setContextState({
|
|
1134
|
+
percentage: ctx.usedPercentage,
|
|
1135
|
+
usedTokens: ctx.usedTokens,
|
|
1136
|
+
maxTokens: ctx.maxTokens
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
async function routeSkillCommand(input, cmd, registry, interactiveSession, manager) {
|
|
1140
|
+
const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
|
|
1141
|
+
if (!skillCmd) {
|
|
1142
|
+
return false;
|
|
1143
|
+
}
|
|
1144
|
+
manager.addEntry({
|
|
1145
|
+
id: (0, import_node_crypto.randomUUID)(),
|
|
1146
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1147
|
+
category: "event",
|
|
1148
|
+
type: "skill-invocation",
|
|
1149
|
+
data: {
|
|
1150
|
+
skillName: cmd,
|
|
1151
|
+
source: skillCmd.source,
|
|
1152
|
+
message: `Invoking ${skillCmd.source}: ${cmd}`
|
|
1153
|
+
}
|
|
1154
|
+
});
|
|
1155
|
+
const args = input.slice(1 + cmd.length).trimStart();
|
|
1156
|
+
const qualifiedName = registry.resolveQualifiedName(cmd);
|
|
1157
|
+
const hookInput = qualifiedName ? `/${qualifiedName}${input.slice(1 + cmd.length)}` : input;
|
|
1158
|
+
await interactiveSession.executeSkillCommand(skillCmd, args, input, hookInput);
|
|
1159
|
+
manager.setPendingPrompt(interactiveSession.getPendingPrompt());
|
|
1160
|
+
return true;
|
|
1161
|
+
}
|
|
1162
|
+
function routeTuiCommand(cmd, interactiveSession) {
|
|
1163
|
+
if (cmd === "exit") {
|
|
1164
|
+
getEffects(interactiveSession)._exitRequested = true;
|
|
1165
|
+
return true;
|
|
1166
|
+
}
|
|
1167
|
+
if (cmd === "plugin") {
|
|
1168
|
+
getEffects(interactiveSession)._triggerPluginTUI = true;
|
|
1169
|
+
return true;
|
|
1170
|
+
}
|
|
1171
|
+
return false;
|
|
1172
|
+
}
|
|
1173
|
+
function getEffects(interactiveSession) {
|
|
1174
|
+
return interactiveSession;
|
|
1175
|
+
}
|
|
416
1176
|
|
|
417
1177
|
// src/ui/hooks/useInteractiveSession.ts
|
|
418
1178
|
function initializeSession(props, permissionHandler) {
|
|
419
|
-
const interactiveSession = new
|
|
1179
|
+
const interactiveSession = new import_agent_sdk.InteractiveSession({
|
|
420
1180
|
cwd: props.cwd,
|
|
421
1181
|
provider: props.provider,
|
|
422
1182
|
permissionMode: props.permissionMode,
|
|
@@ -425,17 +1185,23 @@ function initializeSession(props, permissionHandler) {
|
|
|
425
1185
|
sessionStore: props.sessionStore,
|
|
426
1186
|
resumeSessionId: props.resumeSessionId,
|
|
427
1187
|
forkSession: props.forkSession,
|
|
428
|
-
sessionName: props.sessionName
|
|
1188
|
+
sessionName: props.sessionName,
|
|
1189
|
+
backgroundTaskRunners: props.backgroundTaskRunners,
|
|
1190
|
+
subagentRunnerFactory: props.subagentRunnerFactory,
|
|
1191
|
+
commandModules: props.commandModules
|
|
429
1192
|
});
|
|
430
|
-
const registry = new
|
|
431
|
-
registry.addSource(new
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
1193
|
+
const registry = new import_agent_sdk.CommandRegistry();
|
|
1194
|
+
registry.addSource(new import_agent_sdk.BuiltinCommandSource());
|
|
1195
|
+
for (const module2 of props.commandModules ?? []) {
|
|
1196
|
+
registry.addModule(module2);
|
|
1197
|
+
}
|
|
1198
|
+
registry.addSource(new import_agent_sdk.SkillCommandSource(props.cwd));
|
|
1199
|
+
const pluginsDir = (0, import_node_path4.join)((0, import_node_os3.homedir)(), ".robota", "plugins");
|
|
1200
|
+
const loader = new import_agent_sdk.BundlePluginLoader(pluginsDir);
|
|
435
1201
|
try {
|
|
436
1202
|
const plugins = loader.loadPluginsSync();
|
|
437
1203
|
if (plugins.length > 0) {
|
|
438
|
-
registry.addSource(new
|
|
1204
|
+
registry.addSource(new import_agent_sdk.PluginCommandSource(plugins));
|
|
439
1205
|
}
|
|
440
1206
|
} catch {
|
|
441
1207
|
}
|
|
@@ -445,6 +1211,7 @@ function initializeSession(props, permissionHandler) {
|
|
|
445
1211
|
function useInteractiveSession(props) {
|
|
446
1212
|
const [, forceRender] = (0, import_react2.useState)(0);
|
|
447
1213
|
const [permissionRequest, setPermissionRequest] = (0, import_react2.useState)(null);
|
|
1214
|
+
const [isShuttingDown, setIsShuttingDown] = (0, import_react2.useState)(false);
|
|
448
1215
|
const permissionQueueRef = (0, import_react2.useRef)([]);
|
|
449
1216
|
const processingRef = (0, import_react2.useRef)(false);
|
|
450
1217
|
const processNextPermission = (0, import_react2.useCallback)(() => {
|
|
@@ -494,6 +1261,7 @@ function useInteractiveSession(props) {
|
|
|
494
1261
|
interactiveSession.on("complete", manager.onComplete);
|
|
495
1262
|
interactiveSession.on("interrupted", manager.onInterrupted);
|
|
496
1263
|
interactiveSession.on("error", manager.onError);
|
|
1264
|
+
interactiveSession.on("background_task_event", manager.onBackgroundTaskEvent);
|
|
497
1265
|
const initCheck = setInterval(() => {
|
|
498
1266
|
try {
|
|
499
1267
|
const ctx = interactiveSession.getContextState();
|
|
@@ -519,6 +1287,7 @@ function useInteractiveSession(props) {
|
|
|
519
1287
|
interactiveSession.off("complete", manager.onComplete);
|
|
520
1288
|
interactiveSession.off("interrupted", manager.onInterrupted);
|
|
521
1289
|
interactiveSession.off("error", manager.onError);
|
|
1290
|
+
interactiveSession.off("background_task_event", manager.onBackgroundTaskEvent);
|
|
522
1291
|
};
|
|
523
1292
|
}, [interactiveSession, manager]);
|
|
524
1293
|
(0, import_react2.useEffect)(() => {
|
|
@@ -527,7 +1296,13 @@ function useInteractiveSession(props) {
|
|
|
527
1296
|
manager.setPendingPrompt(interactiveSession.getPendingPrompt());
|
|
528
1297
|
}
|
|
529
1298
|
}, [manager.isThinking, interactiveSession, manager]);
|
|
530
|
-
const handleSubmit = useSlashRouting(
|
|
1299
|
+
const handleSubmit = useSlashRouting(
|
|
1300
|
+
props.cwd,
|
|
1301
|
+
interactiveSession,
|
|
1302
|
+
registry,
|
|
1303
|
+
manager,
|
|
1304
|
+
props.providerDefinitions ?? []
|
|
1305
|
+
);
|
|
531
1306
|
const handleAbort = (0, import_react2.useCallback)(() => {
|
|
532
1307
|
manager.setAborting(true);
|
|
533
1308
|
interactiveSession.abort();
|
|
@@ -536,6 +1311,15 @@ function useInteractiveSession(props) {
|
|
|
536
1311
|
interactiveSession.cancelQueue();
|
|
537
1312
|
manager.setPendingPrompt(null);
|
|
538
1313
|
}, [interactiveSession, manager]);
|
|
1314
|
+
const handleShutdown = (0, import_react2.useCallback)(
|
|
1315
|
+
async (reason = "prompt_input_exit") => {
|
|
1316
|
+
if (isShuttingDown) return;
|
|
1317
|
+
setIsShuttingDown(true);
|
|
1318
|
+
manager.addEntry((0, import_agent_core3.messageToHistoryEntry)((0, import_agent_core3.createSystemMessage)("Shutting down...")));
|
|
1319
|
+
await interactiveSession.shutdown({ reason, message: "CLI shutdown" });
|
|
1320
|
+
},
|
|
1321
|
+
[interactiveSession, manager, isShuttingDown]
|
|
1322
|
+
);
|
|
539
1323
|
return {
|
|
540
1324
|
interactiveSession,
|
|
541
1325
|
registry,
|
|
@@ -545,33 +1329,36 @@ function useInteractiveSession(props) {
|
|
|
545
1329
|
activeTools: manager.activeTools,
|
|
546
1330
|
isThinking: manager.isThinking,
|
|
547
1331
|
isAborting: manager.isAborting,
|
|
1332
|
+
isShuttingDown,
|
|
548
1333
|
pendingPrompt: manager.pendingPrompt,
|
|
1334
|
+
backgroundTasks: manager.backgroundTasks,
|
|
549
1335
|
permissionRequest,
|
|
550
1336
|
contextState: manager.contextState,
|
|
551
1337
|
handleSubmit,
|
|
552
1338
|
handleAbort,
|
|
553
|
-
handleCancelQueue
|
|
1339
|
+
handleCancelQueue,
|
|
1340
|
+
handleShutdown
|
|
554
1341
|
};
|
|
555
1342
|
}
|
|
556
1343
|
|
|
557
1344
|
// src/ui/hooks/usePluginCallbacks.ts
|
|
558
1345
|
var import_react3 = require("react");
|
|
559
|
-
var
|
|
560
|
-
var
|
|
561
|
-
var
|
|
1346
|
+
var import_node_os4 = require("os");
|
|
1347
|
+
var import_node_path5 = require("path");
|
|
1348
|
+
var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
|
|
562
1349
|
function usePluginCallbacks(cwd) {
|
|
563
1350
|
return (0, import_react3.useMemo)(() => {
|
|
564
|
-
const home = (0,
|
|
565
|
-
const pluginsDir = (0,
|
|
566
|
-
const userSettingsPath = (0,
|
|
567
|
-
const settingsStore = new
|
|
568
|
-
const marketplace = new
|
|
569
|
-
const installer = new
|
|
1351
|
+
const home = (0, import_node_os4.homedir)();
|
|
1352
|
+
const pluginsDir = (0, import_node_path5.join)(home, ".robota", "plugins");
|
|
1353
|
+
const userSettingsPath = (0, import_node_path5.join)(home, ".robota", "settings.json");
|
|
1354
|
+
const settingsStore = new import_agent_sdk2.PluginSettingsStore(userSettingsPath);
|
|
1355
|
+
const marketplace = new import_agent_sdk2.MarketplaceClient({ pluginsDir });
|
|
1356
|
+
const installer = new import_agent_sdk2.BundlePluginInstaller({
|
|
570
1357
|
pluginsDir,
|
|
571
1358
|
settingsStore,
|
|
572
1359
|
marketplaceClient: marketplace
|
|
573
1360
|
});
|
|
574
|
-
const loader = new
|
|
1361
|
+
const loader = new import_agent_sdk2.BundlePluginLoader(pluginsDir);
|
|
575
1362
|
return {
|
|
576
1363
|
listInstalled: async () => {
|
|
577
1364
|
const plugins = await loader.loadAll();
|
|
@@ -609,8 +1396,8 @@ function usePluginCallbacks(cwd) {
|
|
|
609
1396
|
throw new Error("Plugin ID must be in format: name@marketplace");
|
|
610
1397
|
}
|
|
611
1398
|
if (scope === "project") {
|
|
612
|
-
const projectPluginsDir = (0,
|
|
613
|
-
const projectInstaller = new
|
|
1399
|
+
const projectPluginsDir = (0, import_node_path5.join)(cwd, ".robota", "plugins");
|
|
1400
|
+
const projectInstaller = new import_agent_sdk2.BundlePluginInstaller({
|
|
614
1401
|
pluginsDir: projectPluginsDir,
|
|
615
1402
|
settingsStore,
|
|
616
1403
|
marketplaceClient: marketplace
|
|
@@ -661,19 +1448,33 @@ function usePluginCallbacks(cwd) {
|
|
|
661
1448
|
// src/ui/hooks/useSideEffects.ts
|
|
662
1449
|
var import_react4 = require("react");
|
|
663
1450
|
var import_ink = require("ink");
|
|
664
|
-
var
|
|
1451
|
+
var import_agent_core4 = require("@robota-sdk/agent-core");
|
|
665
1452
|
var EXIT_DELAY_MS = 500;
|
|
666
1453
|
function useSideEffects({
|
|
1454
|
+
cwd,
|
|
667
1455
|
interactiveSession,
|
|
668
1456
|
addEntry,
|
|
669
1457
|
baseHandleSubmit,
|
|
670
|
-
setSessionName
|
|
1458
|
+
setSessionName,
|
|
1459
|
+
providerDefinitions
|
|
671
1460
|
}) {
|
|
672
1461
|
const { exit } = (0, import_ink.useApp)();
|
|
673
1462
|
const [pendingModelId, setPendingModelId] = (0, import_react4.useState)(null);
|
|
674
1463
|
const pendingModelChangeRef = (0, import_react4.useRef)(null);
|
|
1464
|
+
const [pendingProviderProfile, setPendingProviderProfile] = (0, import_react4.useState)(null);
|
|
1465
|
+
const pendingProviderProfileRef = (0, import_react4.useRef)(null);
|
|
1466
|
+
const [pendingProviderSetupType, setPendingProviderSetupType] = (0, import_react4.useState)(null);
|
|
675
1467
|
const [showPluginTUI, setShowPluginTUI] = (0, import_react4.useState)(false);
|
|
676
1468
|
const [showSessionPicker, setShowSessionPicker] = (0, import_react4.useState)(false);
|
|
1469
|
+
const requestShutdown = (0, import_react4.useCallback)(
|
|
1470
|
+
(reason, message) => {
|
|
1471
|
+
addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("Shutting down...")));
|
|
1472
|
+
setTimeout(() => {
|
|
1473
|
+
void interactiveSession.shutdown({ reason, message }).finally(() => exit());
|
|
1474
|
+
}, EXIT_DELAY_MS);
|
|
1475
|
+
},
|
|
1476
|
+
[interactiveSession, addEntry, exit]
|
|
1477
|
+
);
|
|
677
1478
|
const handleSubmit = (0, import_react4.useCallback)(
|
|
678
1479
|
async (input) => {
|
|
679
1480
|
await baseHandleSubmit(input);
|
|
@@ -693,9 +1494,22 @@ function useSideEffects({
|
|
|
693
1494
|
settings.language = lang;
|
|
694
1495
|
writeSettings(settingsPath, settings);
|
|
695
1496
|
addEntry(
|
|
696
|
-
(0,
|
|
1497
|
+
(0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)(`Language set to "${lang}". Restarting...`))
|
|
697
1498
|
);
|
|
698
|
-
|
|
1499
|
+
requestShutdown("other", "Language change restart");
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
if (sideEffects._pendingProviderProfile) {
|
|
1503
|
+
const profile = sideEffects._pendingProviderProfile;
|
|
1504
|
+
delete sideEffects._pendingProviderProfile;
|
|
1505
|
+
pendingProviderProfileRef.current = profile;
|
|
1506
|
+
setPendingProviderProfile(profile);
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
if (sideEffects._pendingProviderSetupType) {
|
|
1510
|
+
const type = sideEffects._pendingProviderSetupType;
|
|
1511
|
+
delete sideEffects._pendingProviderSetupType;
|
|
1512
|
+
setPendingProviderSetupType(type);
|
|
699
1513
|
return;
|
|
700
1514
|
}
|
|
701
1515
|
if (sideEffects._resetRequested) {
|
|
@@ -703,17 +1517,17 @@ function useSideEffects({
|
|
|
703
1517
|
const settingsPath = getUserSettingsPath();
|
|
704
1518
|
if (deleteSettings(settingsPath)) {
|
|
705
1519
|
addEntry(
|
|
706
|
-
(0,
|
|
1520
|
+
(0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)(`Deleted ${settingsPath}. Exiting...`))
|
|
707
1521
|
);
|
|
708
1522
|
} else {
|
|
709
|
-
addEntry((0,
|
|
1523
|
+
addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("No user settings found.")));
|
|
710
1524
|
}
|
|
711
|
-
|
|
1525
|
+
requestShutdown("other", "Reset settings restart");
|
|
712
1526
|
return;
|
|
713
1527
|
}
|
|
714
1528
|
if (sideEffects._exitRequested) {
|
|
715
1529
|
delete sideEffects._exitRequested;
|
|
716
|
-
|
|
1530
|
+
requestShutdown("prompt_input_exit", "User requested exit");
|
|
717
1531
|
return;
|
|
718
1532
|
}
|
|
719
1533
|
if (sideEffects._triggerPluginTUI) {
|
|
@@ -734,7 +1548,7 @@ function useSideEffects({
|
|
|
734
1548
|
return;
|
|
735
1549
|
}
|
|
736
1550
|
},
|
|
737
|
-
[interactiveSession, baseHandleSubmit, addEntry,
|
|
1551
|
+
[interactiveSession, baseHandleSubmit, addEntry, requestShutdown, setSessionName]
|
|
738
1552
|
);
|
|
739
1553
|
const handleModelConfirm = (0, import_react4.useCallback)(
|
|
740
1554
|
(index) => {
|
|
@@ -746,40 +1560,101 @@ function useSideEffects({
|
|
|
746
1560
|
const settingsPath = getUserSettingsPath();
|
|
747
1561
|
updateModelInSettings(settingsPath, modelId);
|
|
748
1562
|
addEntry(
|
|
749
|
-
(0,
|
|
750
|
-
(0,
|
|
1563
|
+
(0, import_agent_core4.messageToHistoryEntry)(
|
|
1564
|
+
(0, import_agent_core4.createSystemMessage)(`Model changed to ${(0, import_agent_core4.getModelName)(modelId)}. Restarting...`)
|
|
1565
|
+
)
|
|
1566
|
+
);
|
|
1567
|
+
requestShutdown("other", "Model change restart");
|
|
1568
|
+
} catch (err) {
|
|
1569
|
+
addEntry(
|
|
1570
|
+
(0, import_agent_core4.messageToHistoryEntry)(
|
|
1571
|
+
(0, import_agent_core4.createSystemMessage)(`Failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
1572
|
+
)
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
} else {
|
|
1576
|
+
addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("Model change cancelled.")));
|
|
1577
|
+
}
|
|
1578
|
+
},
|
|
1579
|
+
[addEntry, requestShutdown]
|
|
1580
|
+
);
|
|
1581
|
+
const handleProviderConfirm = (0, import_react4.useCallback)(
|
|
1582
|
+
(index) => {
|
|
1583
|
+
const profile = pendingProviderProfileRef.current;
|
|
1584
|
+
setPendingProviderProfile(null);
|
|
1585
|
+
pendingProviderProfileRef.current = null;
|
|
1586
|
+
if (index === 0 && profile) {
|
|
1587
|
+
try {
|
|
1588
|
+
const settingsPath = getUserSettingsPath();
|
|
1589
|
+
applyProviderSwitch(settingsPath, profile, {
|
|
1590
|
+
knownProviders: readMergedProviderSettings(cwd).providers
|
|
1591
|
+
});
|
|
1592
|
+
addEntry(
|
|
1593
|
+
(0, import_agent_core4.messageToHistoryEntry)(
|
|
1594
|
+
(0, import_agent_core4.createSystemMessage)(`Provider changed to ${profile}. Restarting...`)
|
|
751
1595
|
)
|
|
752
1596
|
);
|
|
753
|
-
|
|
1597
|
+
requestShutdown("other", "Provider change restart");
|
|
754
1598
|
} catch (err) {
|
|
755
1599
|
addEntry(
|
|
756
|
-
(0,
|
|
757
|
-
(0,
|
|
1600
|
+
(0, import_agent_core4.messageToHistoryEntry)(
|
|
1601
|
+
(0, import_agent_core4.createSystemMessage)(`Failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
758
1602
|
)
|
|
759
1603
|
);
|
|
760
1604
|
}
|
|
761
1605
|
} else {
|
|
762
|
-
addEntry((0,
|
|
1606
|
+
addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("Provider change cancelled.")));
|
|
1607
|
+
}
|
|
1608
|
+
},
|
|
1609
|
+
[cwd, addEntry, requestShutdown]
|
|
1610
|
+
);
|
|
1611
|
+
const handleProviderSetupSubmit = (0, import_react4.useCallback)(
|
|
1612
|
+
(input) => {
|
|
1613
|
+
setPendingProviderSetupType(null);
|
|
1614
|
+
try {
|
|
1615
|
+
const settingsPath = getUserSettingsPath();
|
|
1616
|
+
applyProviderConfiguration(settingsPath, input, { providerDefinitions });
|
|
1617
|
+
addEntry(
|
|
1618
|
+
(0, import_agent_core4.messageToHistoryEntry)(
|
|
1619
|
+
(0, import_agent_core4.createSystemMessage)(`Provider ${input.profile} configured. Restarting...`)
|
|
1620
|
+
)
|
|
1621
|
+
);
|
|
1622
|
+
requestShutdown("other", "Provider setup restart");
|
|
1623
|
+
} catch (err) {
|
|
1624
|
+
addEntry(
|
|
1625
|
+
(0, import_agent_core4.messageToHistoryEntry)(
|
|
1626
|
+
(0, import_agent_core4.createSystemMessage)(`Failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
1627
|
+
)
|
|
1628
|
+
);
|
|
763
1629
|
}
|
|
764
1630
|
},
|
|
765
|
-
[addEntry,
|
|
1631
|
+
[addEntry, requestShutdown, providerDefinitions]
|
|
766
1632
|
);
|
|
1633
|
+
const handleProviderSetupCancel = (0, import_react4.useCallback)(() => {
|
|
1634
|
+
setPendingProviderSetupType(null);
|
|
1635
|
+
addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("Provider setup cancelled.")));
|
|
1636
|
+
}, [addEntry]);
|
|
767
1637
|
return {
|
|
768
1638
|
handleSubmit,
|
|
769
1639
|
pendingModelId,
|
|
1640
|
+
pendingProviderProfile,
|
|
1641
|
+
pendingProviderSetupType,
|
|
770
1642
|
showPluginTUI,
|
|
771
1643
|
showSessionPicker,
|
|
772
1644
|
setPendingModelId,
|
|
773
1645
|
setShowPluginTUI,
|
|
774
1646
|
setShowSessionPicker,
|
|
775
|
-
handleModelConfirm
|
|
1647
|
+
handleModelConfirm,
|
|
1648
|
+
handleProviderConfirm,
|
|
1649
|
+
handleProviderSetupSubmit,
|
|
1650
|
+
handleProviderSetupCancel
|
|
776
1651
|
};
|
|
777
1652
|
}
|
|
778
1653
|
|
|
779
1654
|
// src/ui/MessageList.tsx
|
|
780
1655
|
var import_react5 = __toESM(require("react"), 1);
|
|
781
1656
|
var import_ink3 = require("ink");
|
|
782
|
-
var
|
|
1657
|
+
var import_agent_core5 = require("@robota-sdk/agent-core");
|
|
783
1658
|
|
|
784
1659
|
// src/ui/render-markdown.ts
|
|
785
1660
|
var import_marked = require("marked");
|
|
@@ -865,7 +1740,7 @@ function RoleLabel({ role }) {
|
|
|
865
1740
|
}
|
|
866
1741
|
}
|
|
867
1742
|
function ToolMessage({ message }) {
|
|
868
|
-
if (!(0,
|
|
1743
|
+
if (!(0, import_agent_core5.isToolMessage)(message)) {
|
|
869
1744
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
|
|
870
1745
|
}
|
|
871
1746
|
const toolName = message.name;
|
|
@@ -928,7 +1803,7 @@ function ToolMessage({ message }) {
|
|
|
928
1803
|
var MessageItem = import_react5.default.memo(function MessageItem2({
|
|
929
1804
|
message
|
|
930
1805
|
}) {
|
|
931
|
-
if ((0,
|
|
1806
|
+
if ((0, import_agent_core5.isToolMessage)(message)) {
|
|
932
1807
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolMessage, { message });
|
|
933
1808
|
}
|
|
934
1809
|
const content = message.content ?? "";
|
|
@@ -936,7 +1811,7 @@ var MessageItem = import_react5.default.memo(function MessageItem2({
|
|
|
936
1811
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
937
1812
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoleLabel, { role: message.role }) }),
|
|
938
1813
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Text, { children: " " }),
|
|
939
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Text, { wrap: "wrap", children: (0,
|
|
1814
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Text, { wrap: "wrap", children: (0, import_agent_core5.isAssistantMessage)(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
|
|
940
1815
|
] });
|
|
941
1816
|
});
|
|
942
1817
|
function ToolSummaryEntry({ entry }) {
|
|
@@ -985,7 +1860,7 @@ function MessageList({ history }) {
|
|
|
985
1860
|
|
|
986
1861
|
// src/ui/StatusBar.tsx
|
|
987
1862
|
var import_ink4 = require("ink");
|
|
988
|
-
var
|
|
1863
|
+
var import_agent_core6 = require("@robota-sdk/agent-core");
|
|
989
1864
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
990
1865
|
var CONTEXT_YELLOW_THRESHOLD = 70;
|
|
991
1866
|
var CONTEXT_RED_THRESHOLD = 90;
|
|
@@ -1030,9 +1905,9 @@ function StatusBar({
|
|
|
1030
1905
|
"Context: ",
|
|
1031
1906
|
Math.round(contextPercentage),
|
|
1032
1907
|
"% (",
|
|
1033
|
-
(0,
|
|
1908
|
+
(0, import_agent_core6.formatTokenCount)(contextUsedTokens),
|
|
1034
1909
|
"/",
|
|
1035
|
-
(0,
|
|
1910
|
+
(0, import_agent_core6.formatTokenCount)(contextMaxTokens),
|
|
1036
1911
|
")"
|
|
1037
1912
|
] })
|
|
1038
1913
|
] }),
|
|
@@ -1056,39 +1931,183 @@ var import_ink8 = require("ink");
|
|
|
1056
1931
|
var import_react6 = require("react");
|
|
1057
1932
|
var import_ink5 = require("ink");
|
|
1058
1933
|
var import_chalk = __toESM(require("chalk"), 1);
|
|
1934
|
+
|
|
1935
|
+
// src/ui/flows/cjk-text-input-flow.ts
|
|
1059
1936
|
var import_string_width = __toESM(require("string-width"), 1);
|
|
1060
|
-
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1061
1937
|
var PASTE_START = "[200~";
|
|
1062
1938
|
var PASTE_END = "[201~";
|
|
1939
|
+
var LAST_ASCII_CONTROL_CODE = 31;
|
|
1940
|
+
var DELETE_CONTROL_CODE = 127;
|
|
1941
|
+
function createCjkTextInputFlowState(value) {
|
|
1942
|
+
return { value, cursor: value.length, isPasting: false, pasteBuffer: "" };
|
|
1943
|
+
}
|
|
1944
|
+
function syncCjkTextInputFlowState(state, value, cursorHint) {
|
|
1945
|
+
if (value === state.value) {
|
|
1946
|
+
return state;
|
|
1947
|
+
}
|
|
1948
|
+
return {
|
|
1949
|
+
...state,
|
|
1950
|
+
value,
|
|
1951
|
+
cursor: cursorHint != null ? Math.min(cursorHint, value.length) : value.length
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
function applyCjkTextInput(state, input, key, options) {
|
|
1955
|
+
const pasteResult = applyPasteBoundaryInput(state, input, options);
|
|
1956
|
+
if (pasteResult !== void 0) return pasteResult;
|
|
1957
|
+
const controlResult = applyControlInput(state, input, key, options);
|
|
1958
|
+
if (controlResult !== void 0) return controlResult;
|
|
1959
|
+
const cursorResult = applyCursorInput(state, key, options.availableWidth);
|
|
1960
|
+
if (cursorResult !== void 0) return cursorResult;
|
|
1961
|
+
return insertPrintableInput(state, input);
|
|
1962
|
+
}
|
|
1963
|
+
function applyPasteBoundaryInput(state, input, options) {
|
|
1964
|
+
if (input === PASTE_START || input.startsWith(PASTE_START)) {
|
|
1965
|
+
return startBracketedPaste(state, input);
|
|
1966
|
+
}
|
|
1967
|
+
if (state.isPasting) {
|
|
1968
|
+
return continueBracketedPaste(state, input, options);
|
|
1969
|
+
}
|
|
1970
|
+
return void 0;
|
|
1971
|
+
}
|
|
1972
|
+
function applyControlInput(state, input, key, options) {
|
|
1973
|
+
if (key.ctrl === true && input === "c" || key.tab === true) {
|
|
1974
|
+
return { state, effect: { type: "none" } };
|
|
1975
|
+
}
|
|
1976
|
+
if (key.return === true) {
|
|
1977
|
+
return { state, effect: { type: "submit", value: state.value } };
|
|
1978
|
+
}
|
|
1979
|
+
if (input.length > 1 && (input.includes("\n") || input.includes("\r")) && options.canPaste) {
|
|
1980
|
+
return {
|
|
1981
|
+
state,
|
|
1982
|
+
effect: { type: "paste", text: input.replace(/\r\n?/g, "\n"), cursor: state.cursor }
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
return void 0;
|
|
1986
|
+
}
|
|
1987
|
+
function applyCursorInput(state, key, availableWidth) {
|
|
1988
|
+
if (key.upArrow === true || key.downArrow === true) {
|
|
1989
|
+
return moveCursorVertically(state, key.upArrow === true ? "up" : "down", availableWidth);
|
|
1990
|
+
}
|
|
1991
|
+
if (key.leftArrow === true) {
|
|
1992
|
+
return moveCursorHorizontally(state, "left");
|
|
1993
|
+
}
|
|
1994
|
+
if (key.rightArrow === true) {
|
|
1995
|
+
return moveCursorHorizontally(state, "right");
|
|
1996
|
+
}
|
|
1997
|
+
if (key.backspace === true || key.delete === true) {
|
|
1998
|
+
return deleteBeforeCursor(state);
|
|
1999
|
+
}
|
|
2000
|
+
return void 0;
|
|
2001
|
+
}
|
|
1063
2002
|
function filterPrintable(input) {
|
|
1064
2003
|
if (!input || input.length === 0) return "";
|
|
1065
|
-
|
|
2004
|
+
let output = "";
|
|
2005
|
+
for (const char of input) {
|
|
2006
|
+
const code = char.charCodeAt(0);
|
|
2007
|
+
if (code > LAST_ASCII_CONTROL_CODE && code !== DELETE_CONTROL_CODE) {
|
|
2008
|
+
output += char;
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
return output;
|
|
1066
2012
|
}
|
|
1067
2013
|
function insertAtCursor(value, cursor, input) {
|
|
1068
2014
|
const next = value.slice(0, cursor) + input + value.slice(cursor);
|
|
1069
2015
|
return { value: next, cursor: cursor + input.length };
|
|
1070
2016
|
}
|
|
1071
|
-
function displayOffset(chars, charIndex, width) {
|
|
1072
|
-
let offset = 0;
|
|
1073
|
-
for (let i = 0; i < charIndex && i < chars.length; i++) {
|
|
1074
|
-
const w = (0, import_string_width.default)(chars[i]);
|
|
1075
|
-
const col = offset % width;
|
|
1076
|
-
if (col + w > width) offset += width - col;
|
|
1077
|
-
offset += w;
|
|
2017
|
+
function displayOffset(chars, charIndex, width) {
|
|
2018
|
+
let offset = 0;
|
|
2019
|
+
for (let i = 0; i < charIndex && i < chars.length; i++) {
|
|
2020
|
+
const w = (0, import_string_width.default)(chars[i]);
|
|
2021
|
+
const col = offset % width;
|
|
2022
|
+
if (col + w > width) offset += width - col;
|
|
2023
|
+
offset += w;
|
|
2024
|
+
}
|
|
2025
|
+
return offset;
|
|
2026
|
+
}
|
|
2027
|
+
function charIndexAtDisplayOffset(chars, target, width) {
|
|
2028
|
+
let offset = 0;
|
|
2029
|
+
for (let i = 0; i < chars.length; i++) {
|
|
2030
|
+
if (offset >= target) return i;
|
|
2031
|
+
const w = (0, import_string_width.default)(chars[i]);
|
|
2032
|
+
const col = offset % width;
|
|
2033
|
+
if (col + w > width) offset += width - col;
|
|
2034
|
+
offset += w;
|
|
2035
|
+
}
|
|
2036
|
+
return chars.length;
|
|
2037
|
+
}
|
|
2038
|
+
function startBracketedPaste(state, input) {
|
|
2039
|
+
return {
|
|
2040
|
+
state: { ...state, isPasting: true, pasteBuffer: input.slice(PASTE_START.length) },
|
|
2041
|
+
effect: { type: "none" }
|
|
2042
|
+
};
|
|
2043
|
+
}
|
|
2044
|
+
function continueBracketedPaste(state, input, options) {
|
|
2045
|
+
if (input !== PASTE_END && !input.includes(PASTE_END)) {
|
|
2046
|
+
return {
|
|
2047
|
+
state: { ...state, pasteBuffer: state.pasteBuffer + input },
|
|
2048
|
+
effect: { type: "none" }
|
|
2049
|
+
};
|
|
2050
|
+
}
|
|
2051
|
+
const beforeMarker = input.split(PASTE_END)[0] ?? "";
|
|
2052
|
+
const text = (state.pasteBuffer + beforeMarker).replace(/\r\n?/g, "\n");
|
|
2053
|
+
const nextState = { ...state, isPasting: false, pasteBuffer: "" };
|
|
2054
|
+
if (text.length === 0) {
|
|
2055
|
+
return { state: nextState, effect: { type: "none" } };
|
|
2056
|
+
}
|
|
2057
|
+
if (text.includes("\n") && options.canPaste) {
|
|
2058
|
+
return { state: nextState, effect: { type: "paste", text, cursor: state.cursor } };
|
|
2059
|
+
}
|
|
2060
|
+
return insertPrintableInput(nextState, text);
|
|
2061
|
+
}
|
|
2062
|
+
function moveCursorVertically(state, direction, availableWidth) {
|
|
2063
|
+
if (!availableWidth || availableWidth <= 0) {
|
|
2064
|
+
return { state, effect: { type: "none" } };
|
|
2065
|
+
}
|
|
2066
|
+
const chars = [...state.value];
|
|
2067
|
+
const offset = displayOffset(chars, state.cursor, availableWidth);
|
|
2068
|
+
const target = direction === "up" ? offset - availableWidth : offset + availableWidth;
|
|
2069
|
+
if (target < 0) {
|
|
2070
|
+
return { state, effect: { type: "none" } };
|
|
2071
|
+
}
|
|
2072
|
+
const cursor = charIndexAtDisplayOffset(chars, target, availableWidth);
|
|
2073
|
+
if (cursor === state.cursor) {
|
|
2074
|
+
return { state, effect: { type: "none" } };
|
|
2075
|
+
}
|
|
2076
|
+
return { state: { ...state, cursor }, effect: { type: "render" } };
|
|
2077
|
+
}
|
|
2078
|
+
function moveCursorHorizontally(state, direction) {
|
|
2079
|
+
if (direction === "left" && state.cursor > 0) {
|
|
2080
|
+
return { state: { ...state, cursor: state.cursor - 1 }, effect: { type: "render" } };
|
|
2081
|
+
}
|
|
2082
|
+
if (direction === "right" && state.cursor < state.value.length) {
|
|
2083
|
+
return { state: { ...state, cursor: state.cursor + 1 }, effect: { type: "render" } };
|
|
2084
|
+
}
|
|
2085
|
+
return { state, effect: { type: "none" } };
|
|
2086
|
+
}
|
|
2087
|
+
function deleteBeforeCursor(state) {
|
|
2088
|
+
if (state.cursor === 0) {
|
|
2089
|
+
return { state, effect: { type: "none" } };
|
|
1078
2090
|
}
|
|
1079
|
-
|
|
2091
|
+
const value = state.value.slice(0, state.cursor - 1) + state.value.slice(state.cursor);
|
|
2092
|
+
return {
|
|
2093
|
+
state: { ...state, value, cursor: state.cursor - 1 },
|
|
2094
|
+
effect: { type: "change", value }
|
|
2095
|
+
};
|
|
1080
2096
|
}
|
|
1081
|
-
function
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
const w = (0, import_string_width.default)(chars[i]);
|
|
1086
|
-
const col = offset % width;
|
|
1087
|
-
if (col + w > width) offset += width - col;
|
|
1088
|
-
offset += w;
|
|
2097
|
+
function insertPrintableInput(state, input) {
|
|
2098
|
+
const printable = filterPrintable(input);
|
|
2099
|
+
if (printable.length === 0) {
|
|
2100
|
+
return { state, effect: { type: "none" } };
|
|
1089
2101
|
}
|
|
1090
|
-
|
|
2102
|
+
const result = insertAtCursor(state.value, state.cursor, printable);
|
|
2103
|
+
return {
|
|
2104
|
+
state: { ...state, value: result.value, cursor: result.cursor },
|
|
2105
|
+
effect: { type: "change", value: result.value }
|
|
2106
|
+
};
|
|
1091
2107
|
}
|
|
2108
|
+
|
|
2109
|
+
// src/ui/CjkTextInput.tsx
|
|
2110
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1092
2111
|
function CjkTextInput({
|
|
1093
2112
|
value,
|
|
1094
2113
|
onChange,
|
|
@@ -1100,113 +2119,40 @@ function CjkTextInput({
|
|
|
1100
2119
|
availableWidth,
|
|
1101
2120
|
cursorHint = null
|
|
1102
2121
|
}) {
|
|
1103
|
-
const
|
|
1104
|
-
const cursorRef = (0, import_react6.useRef)(value.length);
|
|
2122
|
+
const stateRef = (0, import_react6.useRef)(createCjkTextInputFlowState(value));
|
|
1105
2123
|
const [, forceRender] = (0, import_react6.useState)(0);
|
|
1106
|
-
|
|
1107
|
-
const pasteBufferRef = (0, import_react6.useRef)("");
|
|
1108
|
-
if (value !== valueRef.current) {
|
|
1109
|
-
valueRef.current = value;
|
|
1110
|
-
cursorRef.current = cursorHint != null ? Math.min(cursorHint, value.length) : value.length;
|
|
1111
|
-
}
|
|
2124
|
+
stateRef.current = syncCjkTextInputFlowState(stateRef.current, value, cursorHint);
|
|
1112
2125
|
(0, import_ink5.useInput)(
|
|
1113
2126
|
(input, key) => {
|
|
1114
2127
|
try {
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
return;
|
|
1122
|
-
}
|
|
1123
|
-
if (isPastingRef.current) {
|
|
1124
|
-
if (input === PASTE_END || input.includes(PASTE_END)) {
|
|
1125
|
-
const beforeMarker = input.split(PASTE_END)[0] ?? "";
|
|
1126
|
-
pasteBufferRef.current += beforeMarker;
|
|
1127
|
-
const text = pasteBufferRef.current.replace(/\r\n?/g, "\n");
|
|
1128
|
-
pasteBufferRef.current = "";
|
|
1129
|
-
isPastingRef.current = false;
|
|
1130
|
-
if (text.length > 0) {
|
|
1131
|
-
if (text.includes("\n") && onPaste) {
|
|
1132
|
-
onPaste(text, cursorRef.current);
|
|
1133
|
-
} else {
|
|
1134
|
-
const printable2 = filterPrintable(text);
|
|
1135
|
-
if (printable2.length > 0) {
|
|
1136
|
-
const result2 = insertAtCursor(valueRef.current, cursorRef.current, printable2);
|
|
1137
|
-
cursorRef.current = result2.cursor;
|
|
1138
|
-
valueRef.current = result2.value;
|
|
1139
|
-
onChange(result2.value);
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
} else {
|
|
1144
|
-
pasteBufferRef.current += input;
|
|
1145
|
-
}
|
|
1146
|
-
return;
|
|
1147
|
-
}
|
|
1148
|
-
if (key.ctrl && input === "c" || key.tab || key.shift && key.tab) {
|
|
1149
|
-
return;
|
|
1150
|
-
}
|
|
1151
|
-
if (key.upArrow || key.downArrow) {
|
|
1152
|
-
if (availableWidth && availableWidth > 0) {
|
|
1153
|
-
const chars = [...valueRef.current];
|
|
1154
|
-
const offset = displayOffset(chars, cursorRef.current, availableWidth);
|
|
1155
|
-
const target = key.upArrow ? offset - availableWidth : offset + availableWidth;
|
|
1156
|
-
if (target >= 0) {
|
|
1157
|
-
const newCursor = charIndexAtDisplayOffset(chars, target, availableWidth);
|
|
1158
|
-
if (newCursor !== cursorRef.current) {
|
|
1159
|
-
cursorRef.current = newCursor;
|
|
1160
|
-
forceRender((n) => n + 1);
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
return;
|
|
1165
|
-
}
|
|
1166
|
-
if (key.return) {
|
|
1167
|
-
onSubmit?.(valueRef.current);
|
|
1168
|
-
return;
|
|
1169
|
-
}
|
|
1170
|
-
if (input.length > 1 && (input.includes("\n") || input.includes("\r")) && onPaste) {
|
|
1171
|
-
onPaste(input.replace(/\r\n?/g, "\n"), cursorRef.current);
|
|
1172
|
-
return;
|
|
1173
|
-
}
|
|
1174
|
-
if (key.leftArrow) {
|
|
1175
|
-
if (cursorRef.current > 0) {
|
|
1176
|
-
cursorRef.current -= 1;
|
|
1177
|
-
forceRender((n) => n + 1);
|
|
1178
|
-
}
|
|
1179
|
-
return;
|
|
1180
|
-
}
|
|
1181
|
-
if (key.rightArrow) {
|
|
1182
|
-
if (cursorRef.current < valueRef.current.length) {
|
|
1183
|
-
cursorRef.current += 1;
|
|
1184
|
-
forceRender((n) => n + 1);
|
|
1185
|
-
}
|
|
1186
|
-
return;
|
|
1187
|
-
}
|
|
1188
|
-
if (key.backspace || key.delete) {
|
|
1189
|
-
if (cursorRef.current > 0) {
|
|
1190
|
-
const v = valueRef.current;
|
|
1191
|
-
const next = v.slice(0, cursorRef.current - 1) + v.slice(cursorRef.current);
|
|
1192
|
-
cursorRef.current -= 1;
|
|
1193
|
-
valueRef.current = next;
|
|
1194
|
-
onChange(next);
|
|
1195
|
-
}
|
|
1196
|
-
return;
|
|
1197
|
-
}
|
|
1198
|
-
const printable = filterPrintable(input);
|
|
1199
|
-
if (printable.length === 0) return;
|
|
1200
|
-
const result = insertAtCursor(valueRef.current, cursorRef.current, printable);
|
|
1201
|
-
cursorRef.current = result.cursor;
|
|
1202
|
-
valueRef.current = result.value;
|
|
1203
|
-
onChange(result.value);
|
|
2128
|
+
const result = applyCjkTextInput(stateRef.current, input, key, {
|
|
2129
|
+
availableWidth,
|
|
2130
|
+
canPaste: onPaste !== void 0
|
|
2131
|
+
});
|
|
2132
|
+
stateRef.current = result.state;
|
|
2133
|
+
applyCjkTextInputEffect(result.effect, onChange, onSubmit, onPaste, forceRender);
|
|
1204
2134
|
} catch {
|
|
1205
2135
|
}
|
|
1206
2136
|
},
|
|
1207
2137
|
{ isActive: focus }
|
|
1208
2138
|
);
|
|
1209
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: renderWithCursor(
|
|
2139
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: renderWithCursor(
|
|
2140
|
+
stateRef.current.value,
|
|
2141
|
+
stateRef.current.cursor,
|
|
2142
|
+
placeholder,
|
|
2143
|
+
showCursor && focus
|
|
2144
|
+
) });
|
|
2145
|
+
}
|
|
2146
|
+
function applyCjkTextInputEffect(effect, onChange, onSubmit, onPaste, forceRender) {
|
|
2147
|
+
if (effect.type === "change") {
|
|
2148
|
+
onChange(effect.value);
|
|
2149
|
+
} else if (effect.type === "submit") {
|
|
2150
|
+
onSubmit?.(effect.value);
|
|
2151
|
+
} else if (effect.type === "paste") {
|
|
2152
|
+
onPaste?.(effect.text, effect.cursor);
|
|
2153
|
+
} else if (effect.type === "render") {
|
|
2154
|
+
forceRender((n) => n + 1);
|
|
2155
|
+
}
|
|
1210
2156
|
}
|
|
1211
2157
|
function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
|
|
1212
2158
|
if (!showCursor) {
|
|
@@ -1354,6 +2300,61 @@ function useAutocomplete(value, registry) {
|
|
|
1354
2300
|
};
|
|
1355
2301
|
}
|
|
1356
2302
|
|
|
2303
|
+
// src/ui/flows/input-area-flow.ts
|
|
2304
|
+
function getAutocompletePopupAction(key) {
|
|
2305
|
+
if (key.upArrow === true) return "previous";
|
|
2306
|
+
if (key.downArrow === true) return "next";
|
|
2307
|
+
if (key.escape === true) return "close";
|
|
2308
|
+
if (key.tab === true) return "complete";
|
|
2309
|
+
return void 0;
|
|
2310
|
+
}
|
|
2311
|
+
function getPendingPromptInputAction(key) {
|
|
2312
|
+
if (key.backspace === true || key.delete === true) {
|
|
2313
|
+
return "cancelQueue";
|
|
2314
|
+
}
|
|
2315
|
+
return void 0;
|
|
2316
|
+
}
|
|
2317
|
+
function moveAutocompleteSelection(selectedIndex, commandCount, direction) {
|
|
2318
|
+
if (commandCount === 0) return 0;
|
|
2319
|
+
if (direction === "previous") {
|
|
2320
|
+
return selectedIndex > 0 ? selectedIndex - 1 : commandCount - 1;
|
|
2321
|
+
}
|
|
2322
|
+
return selectedIndex < commandCount - 1 ? selectedIndex + 1 : 0;
|
|
2323
|
+
}
|
|
2324
|
+
function resolveTabCompletion(value, command) {
|
|
2325
|
+
const parsed = parseSlashInput(value);
|
|
2326
|
+
if (parsed.parentCommand) {
|
|
2327
|
+
return { type: "insert", value: `/${parsed.parentCommand} ${command.name} ` };
|
|
2328
|
+
}
|
|
2329
|
+
if (command.subcommands && command.subcommands.length > 0) {
|
|
2330
|
+
return { type: "insert", value: `/${command.name} `, selectedIndex: 0 };
|
|
2331
|
+
}
|
|
2332
|
+
return { type: "insert", value: `/${command.name} ` };
|
|
2333
|
+
}
|
|
2334
|
+
function resolveEnterCommandSelection(value, command) {
|
|
2335
|
+
const parsed = parseSlashInput(value);
|
|
2336
|
+
if (parsed.parentCommand) {
|
|
2337
|
+
return { type: "submit", value: `/${parsed.parentCommand} ${command.name}` };
|
|
2338
|
+
}
|
|
2339
|
+
if (command.subcommands && command.subcommands.length > 0) {
|
|
2340
|
+
return { type: "insert", value: `/${command.name} `, selectedIndex: 0 };
|
|
2341
|
+
}
|
|
2342
|
+
return { type: "submit", value: `/${command.name}` };
|
|
2343
|
+
}
|
|
2344
|
+
function createPasteLabelChange(value, cursorPosition, pasteId, text) {
|
|
2345
|
+
const lineCount = text.split("\n").length;
|
|
2346
|
+
const label = `[Pasted text #${pasteId} +${lineCount} lines]`;
|
|
2347
|
+
return {
|
|
2348
|
+
value: value.slice(0, cursorPosition) + label + value.slice(cursorPosition),
|
|
2349
|
+
cursorHint: cursorPosition + label.length,
|
|
2350
|
+
label,
|
|
2351
|
+
lineCount
|
|
2352
|
+
};
|
|
2353
|
+
}
|
|
2354
|
+
function shouldSubmitInput(text) {
|
|
2355
|
+
return text.trim().length > 0;
|
|
2356
|
+
}
|
|
2357
|
+
|
|
1357
2358
|
// src/ui/InputArea.tsx
|
|
1358
2359
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1359
2360
|
var BORDER_HORIZONTAL = 2;
|
|
@@ -1388,56 +2389,47 @@ function InputArea({
|
|
|
1388
2389
|
pasteIdRef.current += 1;
|
|
1389
2390
|
const id = pasteIdRef.current;
|
|
1390
2391
|
pasteStore.current.set(id, text);
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
2392
|
+
setValue((prev) => {
|
|
2393
|
+
const change = createPasteLabelChange(prev, cursorPosition, id, text);
|
|
2394
|
+
setCursorHint(change.cursorHint);
|
|
2395
|
+
return change.value;
|
|
2396
|
+
});
|
|
1396
2397
|
}, []);
|
|
1397
2398
|
const tabCompleteCommand = (0, import_react9.useCallback)(
|
|
1398
2399
|
(cmd) => {
|
|
1399
|
-
const
|
|
1400
|
-
if (
|
|
1401
|
-
setValue(
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
setValue(`/${cmd.name} `);
|
|
1406
|
-
setSelectedIndex(0);
|
|
1407
|
-
return;
|
|
2400
|
+
const result = resolveTabCompletion(value, cmd);
|
|
2401
|
+
if (result.type === "insert") {
|
|
2402
|
+
setValue(result.value);
|
|
2403
|
+
if (result.selectedIndex !== void 0) {
|
|
2404
|
+
setSelectedIndex(result.selectedIndex);
|
|
2405
|
+
}
|
|
1408
2406
|
}
|
|
1409
|
-
setValue(`/${cmd.name} `);
|
|
1410
2407
|
},
|
|
1411
2408
|
[value, setSelectedIndex]
|
|
1412
2409
|
);
|
|
1413
2410
|
const enterSelectCommand = (0, import_react9.useCallback)(
|
|
1414
2411
|
(cmd) => {
|
|
1415
|
-
const
|
|
1416
|
-
if (
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
}
|
|
1422
|
-
if (cmd.subcommands && cmd.subcommands.length > 0) {
|
|
1423
|
-
setValue(`/${cmd.name} `);
|
|
1424
|
-
setSelectedIndex(0);
|
|
2412
|
+
const result = resolveEnterCommandSelection(value, cmd);
|
|
2413
|
+
if (result.type === "insert") {
|
|
2414
|
+
setValue(result.value);
|
|
2415
|
+
if (result.selectedIndex !== void 0) {
|
|
2416
|
+
setSelectedIndex(result.selectedIndex);
|
|
2417
|
+
}
|
|
1425
2418
|
return;
|
|
1426
2419
|
}
|
|
1427
2420
|
setValue("");
|
|
1428
|
-
onSubmit(
|
|
2421
|
+
onSubmit(result.value);
|
|
1429
2422
|
},
|
|
1430
2423
|
[value, onSubmit, setSelectedIndex]
|
|
1431
2424
|
);
|
|
1432
2425
|
const handleSubmit = (0, import_react9.useCallback)(
|
|
1433
2426
|
(text) => {
|
|
1434
|
-
|
|
1435
|
-
if (trimmed.length === 0) return;
|
|
2427
|
+
if (!shouldSubmitInput(text)) return;
|
|
1436
2428
|
if (showPopup && filteredCommands[selectedIndex]) {
|
|
1437
2429
|
enterSelectCommand(filteredCommands[selectedIndex]);
|
|
1438
2430
|
return;
|
|
1439
2431
|
}
|
|
1440
|
-
const expanded = expandPasteLabels(
|
|
2432
|
+
const expanded = expandPasteLabels(text.trim(), pasteStore.current);
|
|
1441
2433
|
setValue("");
|
|
1442
2434
|
pasteStore.current.clear();
|
|
1443
2435
|
pasteIdRef.current = 0;
|
|
@@ -1448,13 +2440,14 @@ function InputArea({
|
|
|
1448
2440
|
(0, import_ink8.useInput)(
|
|
1449
2441
|
(_input, key) => {
|
|
1450
2442
|
if (!showPopup) return;
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
2443
|
+
const action = getAutocompletePopupAction(key);
|
|
2444
|
+
if (action === "previous" || action === "next") {
|
|
2445
|
+
setSelectedIndex(
|
|
2446
|
+
(prev) => moveAutocompleteSelection(prev, filteredCommands.length, action)
|
|
2447
|
+
);
|
|
2448
|
+
} else if (action === "close") {
|
|
1456
2449
|
setShowPopup(false);
|
|
1457
|
-
} else if (
|
|
2450
|
+
} else if (action === "complete") {
|
|
1458
2451
|
const cmd = filteredCommands[selectedIndex];
|
|
1459
2452
|
if (cmd) tabCompleteCommand(cmd);
|
|
1460
2453
|
}
|
|
@@ -1463,7 +2456,7 @@ function InputArea({
|
|
|
1463
2456
|
);
|
|
1464
2457
|
(0, import_ink8.useInput)(
|
|
1465
2458
|
(_input, key) => {
|
|
1466
|
-
if ((key
|
|
2459
|
+
if (getPendingPromptInputAction(key) === "cancelQueue" && pendingPrompt) {
|
|
1467
2460
|
onCancelQueue?.();
|
|
1468
2461
|
}
|
|
1469
2462
|
},
|
|
@@ -1525,113 +2518,409 @@ function InputArea({
|
|
|
1525
2518
|
// src/ui/ConfirmPrompt.tsx
|
|
1526
2519
|
var import_react10 = require("react");
|
|
1527
2520
|
var import_ink9 = require("ink");
|
|
2521
|
+
|
|
2522
|
+
// src/ui/flows/selection-flow.ts
|
|
2523
|
+
function createSelectionFlowState() {
|
|
2524
|
+
return { selectedIndex: 0, scrollOffset: 0, resolved: false };
|
|
2525
|
+
}
|
|
2526
|
+
function getVerticalSelectionInputAction(key) {
|
|
2527
|
+
if (key.escape === true) return "cancel";
|
|
2528
|
+
if (key.upArrow === true) return "previous";
|
|
2529
|
+
if (key.downArrow === true) return "next";
|
|
2530
|
+
if (key.return === true) return "select";
|
|
2531
|
+
return void 0;
|
|
2532
|
+
}
|
|
2533
|
+
function getDirectionalSelectionInputAction(key) {
|
|
2534
|
+
if (key.escape === true) return "cancel";
|
|
2535
|
+
if (key.leftArrow === true || key.upArrow === true) return "previous";
|
|
2536
|
+
if (key.rightArrow === true || key.downArrow === true) return "next";
|
|
2537
|
+
if (key.return === true) return "select";
|
|
2538
|
+
return void 0;
|
|
2539
|
+
}
|
|
2540
|
+
function applySelectionInput(state, action, options) {
|
|
2541
|
+
if (state.resolved) {
|
|
2542
|
+
return { state, effect: { type: "none" } };
|
|
2543
|
+
}
|
|
2544
|
+
if (action === "cancel") {
|
|
2545
|
+
return { state: { ...state, resolved: true }, effect: { type: "cancel" } };
|
|
2546
|
+
}
|
|
2547
|
+
if (options.enabled === false || options.itemCount === 0) {
|
|
2548
|
+
return { state, effect: { type: "none" } };
|
|
2549
|
+
}
|
|
2550
|
+
if (action === "select") {
|
|
2551
|
+
const index = clampIndex(state.selectedIndex, options.itemCount);
|
|
2552
|
+
return {
|
|
2553
|
+
state: { ...state, selectedIndex: index, resolved: true },
|
|
2554
|
+
effect: { type: "select", index }
|
|
2555
|
+
};
|
|
2556
|
+
}
|
|
2557
|
+
const selectedIndex = moveSelection(state.selectedIndex, action, options);
|
|
2558
|
+
const scrollOffset = resolveScrollOffset(selectedIndex, state.scrollOffset, options);
|
|
2559
|
+
return { state: { ...state, selectedIndex, scrollOffset }, effect: { type: "none" } };
|
|
2560
|
+
}
|
|
2561
|
+
function normalizeSelectionState(state, options) {
|
|
2562
|
+
if (options.itemCount === 0) {
|
|
2563
|
+
return { ...state, selectedIndex: 0, scrollOffset: 0 };
|
|
2564
|
+
}
|
|
2565
|
+
const selectedIndex = clampIndex(state.selectedIndex, options.itemCount);
|
|
2566
|
+
const scrollOffset = resolveScrollOffset(selectedIndex, state.scrollOffset, options);
|
|
2567
|
+
if (selectedIndex === state.selectedIndex && scrollOffset === state.scrollOffset) {
|
|
2568
|
+
return state;
|
|
2569
|
+
}
|
|
2570
|
+
return {
|
|
2571
|
+
...state,
|
|
2572
|
+
selectedIndex,
|
|
2573
|
+
scrollOffset
|
|
2574
|
+
};
|
|
2575
|
+
}
|
|
2576
|
+
function moveSelection(selectedIndex, action, options) {
|
|
2577
|
+
if (action === "previous") {
|
|
2578
|
+
if (options.wrap === true && selectedIndex === 0) return options.itemCount - 1;
|
|
2579
|
+
return Math.max(0, selectedIndex - 1);
|
|
2580
|
+
}
|
|
2581
|
+
if (options.wrap === true && selectedIndex === options.itemCount - 1) return 0;
|
|
2582
|
+
return Math.min(options.itemCount - 1, selectedIndex + 1);
|
|
2583
|
+
}
|
|
2584
|
+
function resolveScrollOffset(selectedIndex, scrollOffset, options) {
|
|
2585
|
+
const maxVisible = options.maxVisible ?? options.itemCount;
|
|
2586
|
+
if (maxVisible <= 0) return 0;
|
|
2587
|
+
if (selectedIndex < scrollOffset) return selectedIndex;
|
|
2588
|
+
if (selectedIndex >= scrollOffset + maxVisible) return selectedIndex - maxVisible + 1;
|
|
2589
|
+
return Math.max(0, scrollOffset);
|
|
2590
|
+
}
|
|
2591
|
+
function clampIndex(index, itemCount) {
|
|
2592
|
+
return Math.min(Math.max(index, 0), itemCount - 1);
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
// src/ui/flows/confirm-prompt-flow.ts
|
|
2596
|
+
function getConfirmPromptInputAction(input, key, optionCount) {
|
|
2597
|
+
const action = getDirectionalSelectionInputAction({ ...key, escape: false });
|
|
2598
|
+
if (action !== void 0) {
|
|
2599
|
+
return action;
|
|
2600
|
+
}
|
|
2601
|
+
if (optionCount === 2 && input === "y") {
|
|
2602
|
+
return { type: "shortcut", index: 0 };
|
|
2603
|
+
}
|
|
2604
|
+
if (optionCount === 2 && input === "n") {
|
|
2605
|
+
return { type: "shortcut", index: 1 };
|
|
2606
|
+
}
|
|
2607
|
+
return void 0;
|
|
2608
|
+
}
|
|
2609
|
+
function applyConfirmPromptInput(state, action, optionCount) {
|
|
2610
|
+
if (state.resolved) {
|
|
2611
|
+
return { state, effect: { type: "none" } };
|
|
2612
|
+
}
|
|
2613
|
+
if (typeof action !== "string") {
|
|
2614
|
+
return {
|
|
2615
|
+
state: { ...state, selectedIndex: action.index, resolved: true },
|
|
2616
|
+
effect: { type: "select", index: action.index }
|
|
2617
|
+
};
|
|
2618
|
+
}
|
|
2619
|
+
return applySelectionInput(state, action, { itemCount: optionCount });
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2622
|
+
// src/ui/ConfirmPrompt.tsx
|
|
1528
2623
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1529
2624
|
function ConfirmPrompt({
|
|
1530
2625
|
message,
|
|
1531
2626
|
options = ["Yes", "No"],
|
|
1532
2627
|
onSelect
|
|
1533
2628
|
}) {
|
|
1534
|
-
const [
|
|
1535
|
-
const
|
|
1536
|
-
const
|
|
1537
|
-
(
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
2629
|
+
const [state, setState] = (0, import_react10.useState)(() => createSelectionFlowState());
|
|
2630
|
+
const stateRef = (0, import_react10.useRef)(state);
|
|
2631
|
+
const applyAction = (0, import_react10.useCallback)(
|
|
2632
|
+
(action) => {
|
|
2633
|
+
const result = applyConfirmPromptInput(stateRef.current, action, options.length);
|
|
2634
|
+
stateRef.current = result.state;
|
|
2635
|
+
setState(result.state);
|
|
2636
|
+
if (result.effect.type === "select") {
|
|
2637
|
+
onSelect(result.effect.index);
|
|
2638
|
+
}
|
|
1541
2639
|
},
|
|
1542
|
-
[onSelect]
|
|
2640
|
+
[onSelect, options.length]
|
|
1543
2641
|
);
|
|
1544
2642
|
(0, import_ink9.useInput)((input, key) => {
|
|
1545
|
-
|
|
1546
|
-
if (
|
|
1547
|
-
|
|
1548
|
-
} else if (key.rightArrow || key.downArrow) {
|
|
1549
|
-
setSelected((prev) => prev < options.length - 1 ? prev + 1 : prev);
|
|
1550
|
-
} else if (key.return) {
|
|
1551
|
-
doSelect(selected);
|
|
1552
|
-
} else if (input === "y" && options.length === 2) {
|
|
1553
|
-
doSelect(0);
|
|
1554
|
-
} else if (input === "n" && options.length === 2) {
|
|
1555
|
-
doSelect(1);
|
|
2643
|
+
const action = getConfirmPromptInputAction(input, key, options.length);
|
|
2644
|
+
if (action !== void 0) {
|
|
2645
|
+
applyAction(action);
|
|
1556
2646
|
}
|
|
1557
2647
|
});
|
|
1558
2648
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_ink9.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
1559
2649
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink9.Text, { color: "yellow", children: message }),
|
|
1560
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink9.Box, { marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink9.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
2650
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink9.Box, { marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink9.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2651
|
+
import_ink9.Text,
|
|
2652
|
+
{
|
|
2653
|
+
color: i === state.selectedIndex ? "cyan" : void 0,
|
|
2654
|
+
bold: i === state.selectedIndex,
|
|
2655
|
+
children: [
|
|
2656
|
+
i === state.selectedIndex ? "> " : " ",
|
|
2657
|
+
opt
|
|
2658
|
+
]
|
|
2659
|
+
}
|
|
2660
|
+
) }, opt)) }),
|
|
1564
2661
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink9.Text, { dimColor: true, children: " arrow keys to select, Enter to confirm" })
|
|
1565
2662
|
] });
|
|
1566
2663
|
}
|
|
1567
2664
|
|
|
1568
|
-
// src/ui/
|
|
1569
|
-
var
|
|
2665
|
+
// src/ui/ProviderSetupPrompt.tsx
|
|
2666
|
+
var import_react12 = require("react");
|
|
2667
|
+
|
|
2668
|
+
// src/ui/TextPrompt.tsx
|
|
2669
|
+
var import_react11 = require("react");
|
|
1570
2670
|
var import_ink10 = require("ink");
|
|
2671
|
+
|
|
2672
|
+
// src/ui/flows/text-prompt-flow.ts
|
|
2673
|
+
function createTextPromptFlowState() {
|
|
2674
|
+
return { value: "", resolved: false };
|
|
2675
|
+
}
|
|
2676
|
+
function getTextPromptInputAction(input, key) {
|
|
2677
|
+
if (key.escape === true) {
|
|
2678
|
+
return { type: "cancel" };
|
|
2679
|
+
}
|
|
2680
|
+
if (key.return === true) {
|
|
2681
|
+
return { type: "submit" };
|
|
2682
|
+
}
|
|
2683
|
+
if (key.backspace === true || key.delete === true) {
|
|
2684
|
+
return { type: "delete" };
|
|
2685
|
+
}
|
|
2686
|
+
if (input && key.ctrl !== true && key.meta !== true) {
|
|
2687
|
+
return { type: "insert", value: input };
|
|
2688
|
+
}
|
|
2689
|
+
return void 0;
|
|
2690
|
+
}
|
|
2691
|
+
function applyTextPromptInput(state, action, options) {
|
|
2692
|
+
if (state.resolved) {
|
|
2693
|
+
return { state, effect: { type: "none" } };
|
|
2694
|
+
}
|
|
2695
|
+
if (action.type === "cancel") {
|
|
2696
|
+
return { state: { ...state, resolved: true }, effect: { type: "cancel" } };
|
|
2697
|
+
}
|
|
2698
|
+
if (action.type === "delete") {
|
|
2699
|
+
return {
|
|
2700
|
+
state: { ...state, value: state.value.slice(0, -1), error: void 0 },
|
|
2701
|
+
effect: { type: "none" }
|
|
2702
|
+
};
|
|
2703
|
+
}
|
|
2704
|
+
if (action.type === "insert") {
|
|
2705
|
+
return {
|
|
2706
|
+
state: { ...state, value: state.value + action.value, error: void 0 },
|
|
2707
|
+
effect: { type: "none" }
|
|
2708
|
+
};
|
|
2709
|
+
}
|
|
2710
|
+
return submitTextPromptValue(state, options);
|
|
2711
|
+
}
|
|
2712
|
+
function submitTextPromptValue(state, options) {
|
|
2713
|
+
const trimmed = state.value.trim();
|
|
2714
|
+
if (!trimmed && !options.allowEmpty) {
|
|
2715
|
+
const emptyError = options.validate?.(trimmed);
|
|
2716
|
+
return {
|
|
2717
|
+
state: emptyError ? { ...state, error: emptyError } : state,
|
|
2718
|
+
effect: { type: "none" }
|
|
2719
|
+
};
|
|
2720
|
+
}
|
|
2721
|
+
const error = options.validate?.(trimmed);
|
|
2722
|
+
if (error !== void 0) {
|
|
2723
|
+
return { state: { ...state, error }, effect: { type: "none" } };
|
|
2724
|
+
}
|
|
2725
|
+
return { state: { ...state, resolved: true }, effect: { type: "submit", value: trimmed } };
|
|
2726
|
+
}
|
|
2727
|
+
|
|
2728
|
+
// src/ui/TextPrompt.tsx
|
|
1571
2729
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1572
|
-
|
|
2730
|
+
function TextPrompt({
|
|
2731
|
+
title,
|
|
2732
|
+
placeholder,
|
|
2733
|
+
onSubmit,
|
|
2734
|
+
onCancel,
|
|
2735
|
+
validate,
|
|
2736
|
+
allowEmpty = false,
|
|
2737
|
+
masked = false
|
|
2738
|
+
}) {
|
|
2739
|
+
const [state, setState] = (0, import_react11.useState)(() => createTextPromptFlowState());
|
|
2740
|
+
const stateRef = (0, import_react11.useRef)(state);
|
|
2741
|
+
const applyAction = (0, import_react11.useCallback)(
|
|
2742
|
+
(action) => {
|
|
2743
|
+
const result = applyTextPromptInput(stateRef.current, action, { allowEmpty, validate });
|
|
2744
|
+
stateRef.current = result.state;
|
|
2745
|
+
setState(result.state);
|
|
2746
|
+
if (result.effect.type === "cancel") {
|
|
2747
|
+
onCancel();
|
|
2748
|
+
} else if (result.effect.type === "submit") {
|
|
2749
|
+
onSubmit(result.effect.value);
|
|
2750
|
+
}
|
|
2751
|
+
},
|
|
2752
|
+
[allowEmpty, validate, onCancel, onSubmit]
|
|
2753
|
+
);
|
|
2754
|
+
(0, import_ink10.useInput)((input, key) => {
|
|
2755
|
+
const action = getTextPromptInputAction(input, key);
|
|
2756
|
+
if (action !== void 0) {
|
|
2757
|
+
applyAction(action);
|
|
2758
|
+
}
|
|
2759
|
+
});
|
|
2760
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink10.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
2761
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink10.Text, { color: "yellow", bold: true, children: title }),
|
|
2762
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ink10.Box, { marginTop: 1, children: [
|
|
2763
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink10.Text, { color: "cyan", children: "> " }),
|
|
2764
|
+
state.value ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink10.Text, { children: masked ? "*".repeat(state.value.length) : state.value }) : placeholder ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink10.Text, { dimColor: true, children: placeholder }) : null,
|
|
2765
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink10.Text, { color: "cyan", children: "\u2588" })
|
|
2766
|
+
] }),
|
|
2767
|
+
state.error && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink10.Text, { color: "red", children: state.error }),
|
|
2768
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink10.Text, { dimColor: true, children: " Enter Submit Esc Cancel" })
|
|
2769
|
+
] });
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2772
|
+
// src/ui/ProviderSetupPrompt.tsx
|
|
2773
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
2774
|
+
function ProviderSetupPrompt({
|
|
2775
|
+
type,
|
|
2776
|
+
providerDefinitions,
|
|
2777
|
+
onSubmit,
|
|
2778
|
+
onCancel
|
|
2779
|
+
}) {
|
|
2780
|
+
const [state, setState] = (0, import_react12.useState)(
|
|
2781
|
+
() => createProviderSetupFlow(type, providerDefinitions)
|
|
2782
|
+
);
|
|
2783
|
+
const step = getProviderSetupStep(state);
|
|
2784
|
+
const handleStepSubmit = (rawValue) => {
|
|
2785
|
+
const result = submitProviderSetupValue(state, rawValue);
|
|
2786
|
+
if (result.status === "next") {
|
|
2787
|
+
setState(result.state);
|
|
2788
|
+
return;
|
|
2789
|
+
}
|
|
2790
|
+
if (result.status === "complete") {
|
|
2791
|
+
onSubmit(result.input);
|
|
2792
|
+
}
|
|
2793
|
+
};
|
|
2794
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2795
|
+
TextPrompt,
|
|
2796
|
+
{
|
|
2797
|
+
title: step.title,
|
|
2798
|
+
placeholder: step.defaultValue,
|
|
2799
|
+
allowEmpty: step.defaultValue !== void 0,
|
|
2800
|
+
masked: step.masked,
|
|
2801
|
+
validate: (value) => validateProviderSetupValue(step, value),
|
|
2802
|
+
onSubmit: handleStepSubmit,
|
|
2803
|
+
onCancel
|
|
2804
|
+
},
|
|
2805
|
+
`${type}-${step.key}`
|
|
2806
|
+
);
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
// src/ui/PermissionPrompt.tsx
|
|
2810
|
+
var import_react13 = __toESM(require("react"), 1);
|
|
2811
|
+
var import_ink11 = require("ink");
|
|
2812
|
+
|
|
2813
|
+
// src/ui/flows/permission-prompt-flow.ts
|
|
2814
|
+
var PERMISSION_PROMPT_OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
|
|
2815
|
+
function getPermissionPromptInputAction(input, key) {
|
|
2816
|
+
const action = getDirectionalSelectionInputAction({ ...key, escape: false });
|
|
2817
|
+
if (action !== void 0) {
|
|
2818
|
+
return action;
|
|
2819
|
+
}
|
|
2820
|
+
if (input === "y" || input === "1") {
|
|
2821
|
+
return { type: "shortcut", index: 0 };
|
|
2822
|
+
}
|
|
2823
|
+
if (input === "a" || input === "2") {
|
|
2824
|
+
return { type: "shortcut", index: 1 };
|
|
2825
|
+
}
|
|
2826
|
+
if (input === "n" || input === "d" || input === "3") {
|
|
2827
|
+
return { type: "shortcut", index: 2 };
|
|
2828
|
+
}
|
|
2829
|
+
return void 0;
|
|
2830
|
+
}
|
|
2831
|
+
function applyPermissionPromptInput(state, action) {
|
|
2832
|
+
if (state.resolved) {
|
|
2833
|
+
return { state, effect: { type: "none" } };
|
|
2834
|
+
}
|
|
2835
|
+
if (typeof action !== "string") {
|
|
2836
|
+
return resolvePermissionIndex(state, action.index);
|
|
2837
|
+
}
|
|
2838
|
+
const result = applySelectionInput(state, action, {
|
|
2839
|
+
itemCount: PERMISSION_PROMPT_OPTIONS.length
|
|
2840
|
+
});
|
|
2841
|
+
if (result.effect.type !== "select") {
|
|
2842
|
+
return { state: result.state, effect: { type: "none" } };
|
|
2843
|
+
}
|
|
2844
|
+
return {
|
|
2845
|
+
state: result.state,
|
|
2846
|
+
effect: { type: "resolve", decision: getPermissionDecision(result.effect.index) }
|
|
2847
|
+
};
|
|
2848
|
+
}
|
|
2849
|
+
function getPermissionDecision(index) {
|
|
2850
|
+
if (index === 0) return true;
|
|
2851
|
+
if (index === 1) return "allow-session";
|
|
2852
|
+
return false;
|
|
2853
|
+
}
|
|
2854
|
+
function resolvePermissionIndex(state, index) {
|
|
2855
|
+
return {
|
|
2856
|
+
state: { ...state, selectedIndex: index, resolved: true },
|
|
2857
|
+
effect: { type: "resolve", decision: getPermissionDecision(index) }
|
|
2858
|
+
};
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2861
|
+
// src/ui/PermissionPrompt.tsx
|
|
2862
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1573
2863
|
function formatArgs(args) {
|
|
1574
2864
|
const entries = Object.entries(args);
|
|
1575
2865
|
if (entries.length === 0) return "(no arguments)";
|
|
1576
2866
|
return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
1577
2867
|
}
|
|
1578
2868
|
function PermissionPrompt({ request }) {
|
|
1579
|
-
const [
|
|
1580
|
-
const
|
|
1581
|
-
const prevRequestRef =
|
|
2869
|
+
const [state, setState] = import_react13.default.useState(() => createSelectionFlowState());
|
|
2870
|
+
const stateRef = import_react13.default.useRef(state);
|
|
2871
|
+
const prevRequestRef = import_react13.default.useRef(request);
|
|
1582
2872
|
if (prevRequestRef.current !== request) {
|
|
1583
2873
|
prevRequestRef.current = request;
|
|
1584
|
-
|
|
1585
|
-
|
|
2874
|
+
const nextState = createSelectionFlowState();
|
|
2875
|
+
stateRef.current = nextState;
|
|
2876
|
+
setState(nextState);
|
|
1586
2877
|
}
|
|
1587
|
-
const
|
|
1588
|
-
(
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
2878
|
+
const applyAction = import_react13.default.useCallback(
|
|
2879
|
+
(action) => {
|
|
2880
|
+
const result = applyPermissionPromptInput(stateRef.current, action);
|
|
2881
|
+
stateRef.current = result.state;
|
|
2882
|
+
setState(result.state);
|
|
2883
|
+
if (result.effect.type === "resolve") {
|
|
2884
|
+
request.resolve(result.effect.decision);
|
|
2885
|
+
}
|
|
1594
2886
|
},
|
|
1595
2887
|
[request]
|
|
1596
2888
|
);
|
|
1597
|
-
(0,
|
|
1598
|
-
|
|
1599
|
-
if (
|
|
1600
|
-
|
|
1601
|
-
} else if (key.downArrow || key.rightArrow) {
|
|
1602
|
-
setSelected((prev) => prev < OPTIONS.length - 1 ? prev + 1 : prev);
|
|
1603
|
-
} else if (key.return) {
|
|
1604
|
-
doResolve(selected);
|
|
1605
|
-
} else if (input === "y" || input === "1") {
|
|
1606
|
-
doResolve(0);
|
|
1607
|
-
} else if (input === "a" || input === "2") {
|
|
1608
|
-
doResolve(1);
|
|
1609
|
-
} else if (input === "n" || input === "d" || input === "3") {
|
|
1610
|
-
doResolve(2);
|
|
2889
|
+
(0, import_ink11.useInput)((input, key) => {
|
|
2890
|
+
const action = getPermissionPromptInputAction(input, key);
|
|
2891
|
+
if (action !== void 0) {
|
|
2892
|
+
applyAction(action);
|
|
1611
2893
|
}
|
|
1612
2894
|
});
|
|
1613
|
-
return /* @__PURE__ */ (0,
|
|
1614
|
-
/* @__PURE__ */ (0,
|
|
1615
|
-
/* @__PURE__ */ (0,
|
|
2895
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
2896
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "yellow", bold: true, children: "[Permission Required]" }),
|
|
2897
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { children: [
|
|
1616
2898
|
"Tool:",
|
|
1617
2899
|
" ",
|
|
1618
|
-
/* @__PURE__ */ (0,
|
|
2900
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "cyan", bold: true, children: request.toolName })
|
|
1619
2901
|
] }),
|
|
1620
|
-
/* @__PURE__ */ (0,
|
|
2902
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Text, { dimColor: true, children: [
|
|
1621
2903
|
" ",
|
|
1622
2904
|
formatArgs(request.toolArgs)
|
|
1623
2905
|
] }),
|
|
1624
|
-
/* @__PURE__ */ (0,
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
2906
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { marginTop: 1, children: PERMISSION_PROMPT_OPTIONS.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2907
|
+
import_ink11.Text,
|
|
2908
|
+
{
|
|
2909
|
+
color: i === state.selectedIndex ? "cyan" : void 0,
|
|
2910
|
+
bold: i === state.selectedIndex,
|
|
2911
|
+
children: [
|
|
2912
|
+
i === state.selectedIndex ? "> " : " ",
|
|
2913
|
+
opt
|
|
2914
|
+
]
|
|
2915
|
+
}
|
|
2916
|
+
) }, opt)) }),
|
|
2917
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: " left/right to select, Enter to confirm" })
|
|
1629
2918
|
] });
|
|
1630
2919
|
}
|
|
1631
2920
|
|
|
1632
2921
|
// src/ui/StreamingIndicator.tsx
|
|
1633
|
-
var
|
|
1634
|
-
var
|
|
2922
|
+
var import_ink12 = require("ink");
|
|
2923
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1635
2924
|
function getToolStyle(t) {
|
|
1636
2925
|
if (t.isRunning) return { color: "yellow", icon: "\u27F3", strikethrough: false };
|
|
1637
2926
|
if (t.result === "error") return { color: "red", icon: "\u2717", strikethrough: true };
|
|
@@ -1642,16 +2931,16 @@ function StreamingIndicator({ text, activeTools }) {
|
|
|
1642
2931
|
const hasTools = activeTools.length > 0;
|
|
1643
2932
|
const hasText = text.length > 0;
|
|
1644
2933
|
if (!hasTools && !hasText) {
|
|
1645
|
-
return /* @__PURE__ */ (0,
|
|
2934
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_jsx_runtime12.Fragment, {});
|
|
1646
2935
|
}
|
|
1647
|
-
return /* @__PURE__ */ (0,
|
|
1648
|
-
hasTools && /* @__PURE__ */ (0,
|
|
1649
|
-
/* @__PURE__ */ (0,
|
|
1650
|
-
/* @__PURE__ */ (0,
|
|
2936
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", children: [
|
|
2937
|
+
hasTools && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
2938
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "white", bold: true, children: "Tools:" }),
|
|
2939
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { children: " " }),
|
|
1651
2940
|
activeTools.map((t, i) => {
|
|
1652
2941
|
const { color, icon, strikethrough } = getToolStyle(t);
|
|
1653
|
-
return /* @__PURE__ */ (0,
|
|
1654
|
-
/* @__PURE__ */ (0,
|
|
2942
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", children: [
|
|
2943
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Text, { color, strikethrough, children: [
|
|
1655
2944
|
" ",
|
|
1656
2945
|
icon,
|
|
1657
2946
|
" ",
|
|
@@ -1660,25 +2949,25 @@ function StreamingIndicator({ text, activeTools }) {
|
|
|
1660
2949
|
t.firstArg,
|
|
1661
2950
|
")"
|
|
1662
2951
|
] }),
|
|
1663
|
-
t.diffLines && t.diffLines.length > 0 && /* @__PURE__ */ (0,
|
|
2952
|
+
t.diffLines && t.diffLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(DiffBlock, { file: t.diffFile, lines: t.diffLines })
|
|
1664
2953
|
] }, `${t.toolName}-${i}`);
|
|
1665
2954
|
})
|
|
1666
2955
|
] }),
|
|
1667
|
-
hasText && /* @__PURE__ */ (0,
|
|
1668
|
-
/* @__PURE__ */ (0,
|
|
1669
|
-
/* @__PURE__ */ (0,
|
|
1670
|
-
/* @__PURE__ */ (0,
|
|
2956
|
+
hasText && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
2957
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { color: "cyan", bold: true, children: "Robota:" }),
|
|
2958
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { children: " " }),
|
|
2959
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Text, { wrap: "wrap", children: renderMarkdown(text) }) })
|
|
1671
2960
|
] })
|
|
1672
2961
|
] });
|
|
1673
2962
|
}
|
|
1674
2963
|
|
|
1675
2964
|
// src/ui/PluginTUI.tsx
|
|
1676
|
-
var
|
|
2965
|
+
var import_react16 = require("react");
|
|
1677
2966
|
|
|
1678
2967
|
// src/ui/MenuSelect.tsx
|
|
1679
|
-
var
|
|
1680
|
-
var
|
|
1681
|
-
var
|
|
2968
|
+
var import_react14 = require("react");
|
|
2969
|
+
var import_ink13 = require("ink");
|
|
2970
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1682
2971
|
function MenuSelect({
|
|
1683
2972
|
title,
|
|
1684
2973
|
items,
|
|
@@ -1687,119 +2976,57 @@ function MenuSelect({
|
|
|
1687
2976
|
loading,
|
|
1688
2977
|
error
|
|
1689
2978
|
}) {
|
|
1690
|
-
const [
|
|
1691
|
-
const
|
|
1692
|
-
const
|
|
1693
|
-
const
|
|
1694
|
-
(
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
if (loading || error || items.length === 0) return;
|
|
1709
|
-
if (key.upArrow) {
|
|
1710
|
-
const next = selectedRef.current > 0 ? selectedRef.current - 1 : selectedRef.current;
|
|
1711
|
-
selectedRef.current = next;
|
|
1712
|
-
setSelected(next);
|
|
1713
|
-
} else if (key.downArrow) {
|
|
1714
|
-
const next = selectedRef.current < items.length - 1 ? selectedRef.current + 1 : selectedRef.current;
|
|
1715
|
-
selectedRef.current = next;
|
|
1716
|
-
setSelected(next);
|
|
1717
|
-
} else if (key.return) {
|
|
1718
|
-
doSelect(selectedRef.current);
|
|
1719
|
-
}
|
|
1720
|
-
});
|
|
1721
|
-
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink12.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
1722
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink12.Text, { color: "yellow", bold: true, children: title }),
|
|
1723
|
-
loading && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink12.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink12.Text, { dimColor: true, children: "Loading..." }) }),
|
|
1724
|
-
error && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink12.Box, { marginTop: 1, flexDirection: "column", children: [
|
|
1725
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink12.Text, { color: "red", children: error }),
|
|
1726
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink12.Text, { dimColor: true, children: "Press Esc to go back" })
|
|
1727
|
-
] }),
|
|
1728
|
-
!loading && !error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink12.Box, { flexDirection: "column", marginTop: 1, children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink12.Box, { children: [
|
|
1729
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink12.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
|
|
1730
|
-
i === selected ? "> " : " ",
|
|
1731
|
-
item.label
|
|
1732
|
-
] }),
|
|
1733
|
-
item.hint && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink12.Text, { dimColor: true, children: [
|
|
1734
|
-
" ",
|
|
1735
|
-
item.hint
|
|
1736
|
-
] })
|
|
1737
|
-
] }, item.value)) }),
|
|
1738
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink12.Text, { dimColor: true, children: loading || error ? "" : " \u2191\u2193 Navigate Enter Select Esc Back" })
|
|
1739
|
-
] });
|
|
1740
|
-
}
|
|
1741
|
-
|
|
1742
|
-
// src/ui/TextPrompt.tsx
|
|
1743
|
-
var import_react13 = require("react");
|
|
1744
|
-
var import_ink13 = require("ink");
|
|
1745
|
-
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1746
|
-
function TextPrompt({
|
|
1747
|
-
title,
|
|
1748
|
-
placeholder,
|
|
1749
|
-
onSubmit,
|
|
1750
|
-
onCancel,
|
|
1751
|
-
validate
|
|
1752
|
-
}) {
|
|
1753
|
-
const [value, setValue] = (0, import_react13.useState)("");
|
|
1754
|
-
const [error, setError] = (0, import_react13.useState)();
|
|
1755
|
-
const resolvedRef = (0, import_react13.useRef)(false);
|
|
1756
|
-
const valueRef = (0, import_react13.useRef)("");
|
|
1757
|
-
const handleSubmit = (0, import_react13.useCallback)(() => {
|
|
1758
|
-
if (resolvedRef.current) return;
|
|
1759
|
-
const trimmed = valueRef.current.trim();
|
|
1760
|
-
if (!trimmed) return;
|
|
1761
|
-
if (validate) {
|
|
1762
|
-
const err = validate(trimmed);
|
|
1763
|
-
if (err) {
|
|
1764
|
-
setError(err);
|
|
1765
|
-
return;
|
|
2979
|
+
const [state, setState] = (0, import_react14.useState)(() => createSelectionFlowState());
|
|
2980
|
+
const stateRef = (0, import_react14.useRef)(state);
|
|
2981
|
+
const isEnabled = !loading && !error;
|
|
2982
|
+
const applyAction = (0, import_react14.useCallback)(
|
|
2983
|
+
(action) => {
|
|
2984
|
+
const result = applySelectionInput(stateRef.current, action, {
|
|
2985
|
+
itemCount: items.length,
|
|
2986
|
+
enabled: isEnabled
|
|
2987
|
+
});
|
|
2988
|
+
stateRef.current = result.state;
|
|
2989
|
+
setState(result.state);
|
|
2990
|
+
if (result.effect.type === "cancel") {
|
|
2991
|
+
onBack();
|
|
2992
|
+
} else if (result.effect.type === "select") {
|
|
2993
|
+
const item = items[result.effect.index];
|
|
2994
|
+
if (item !== void 0) {
|
|
2995
|
+
onSelect(item.value);
|
|
2996
|
+
}
|
|
1766
2997
|
}
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
if (
|
|
1773
|
-
|
|
1774
|
-
resolvedRef.current = true;
|
|
1775
|
-
onCancel();
|
|
1776
|
-
return;
|
|
1777
|
-
}
|
|
1778
|
-
if (key.return) {
|
|
1779
|
-
handleSubmit();
|
|
1780
|
-
return;
|
|
1781
|
-
}
|
|
1782
|
-
if (key.backspace || key.delete) {
|
|
1783
|
-
valueRef.current = valueRef.current.slice(0, -1);
|
|
1784
|
-
setValue(valueRef.current);
|
|
1785
|
-
setError(void 0);
|
|
1786
|
-
return;
|
|
1787
|
-
}
|
|
1788
|
-
if (input && !key.ctrl && !key.meta) {
|
|
1789
|
-
valueRef.current = valueRef.current + input;
|
|
1790
|
-
setValue(valueRef.current);
|
|
1791
|
-
setError(void 0);
|
|
2998
|
+
},
|
|
2999
|
+
[isEnabled, items, onBack, onSelect]
|
|
3000
|
+
);
|
|
3001
|
+
(0, import_ink13.useInput)((input, key) => {
|
|
3002
|
+
const action = getVerticalSelectionInputAction(key);
|
|
3003
|
+
if (action !== void 0) {
|
|
3004
|
+
applyAction(action);
|
|
1792
3005
|
}
|
|
1793
3006
|
});
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
3007
|
+
const normalizedState = normalizeSelectionState(state, { itemCount: items.length });
|
|
3008
|
+
if (normalizedState !== state) {
|
|
3009
|
+
stateRef.current = normalizedState;
|
|
3010
|
+
}
|
|
3011
|
+
const selected = normalizedState.selectedIndex;
|
|
3012
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
|
|
3013
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { color: "yellow", bold: true, children: title }),
|
|
3014
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { dimColor: true, children: "Loading..." }) }),
|
|
3015
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Box, { marginTop: 1, flexDirection: "column", children: [
|
|
3016
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { color: "red", children: error }),
|
|
3017
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { dimColor: true, children: "Press Esc to go back" })
|
|
1800
3018
|
] }),
|
|
1801
|
-
error && /* @__PURE__ */ (0,
|
|
1802
|
-
|
|
3019
|
+
!loading && !error && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Box, { flexDirection: "column", marginTop: 1, children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Box, { children: [
|
|
3020
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
|
|
3021
|
+
i === selected ? "> " : " ",
|
|
3022
|
+
item.label
|
|
3023
|
+
] }),
|
|
3024
|
+
item.hint && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Text, { dimColor: true, children: [
|
|
3025
|
+
" ",
|
|
3026
|
+
item.hint
|
|
3027
|
+
] })
|
|
3028
|
+
] }, item.value)) }),
|
|
3029
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { dimColor: true, children: loading || error ? "" : " \u2191\u2193 Navigate Enter Select Esc Back" })
|
|
1803
3030
|
] });
|
|
1804
3031
|
}
|
|
1805
3032
|
|
|
@@ -1896,12 +3123,12 @@ function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
|
|
|
1896
3123
|
}
|
|
1897
3124
|
|
|
1898
3125
|
// src/ui/hooks/usePluginScreenData.ts
|
|
1899
|
-
var
|
|
3126
|
+
var import_react15 = require("react");
|
|
1900
3127
|
function usePluginScreenData(screen, marketplace, callbacks, refreshCounter, stackLength) {
|
|
1901
|
-
const [items, setItems] = (0,
|
|
1902
|
-
const [loading, setLoading] = (0,
|
|
1903
|
-
const [error, setError] = (0,
|
|
1904
|
-
(0,
|
|
3128
|
+
const [items, setItems] = (0, import_react15.useState)([]);
|
|
3129
|
+
const [loading, setLoading] = (0, import_react15.useState)(false);
|
|
3130
|
+
const [error, setError] = (0, import_react15.useState)();
|
|
3131
|
+
(0, import_react15.useEffect)(() => {
|
|
1905
3132
|
setItems([]);
|
|
1906
3133
|
setError(void 0);
|
|
1907
3134
|
if (screen === "marketplace-list") {
|
|
@@ -1956,16 +3183,16 @@ function usePluginScreenData(screen, marketplace, callbacks, refreshCounter, sta
|
|
|
1956
3183
|
}
|
|
1957
3184
|
|
|
1958
3185
|
// src/ui/PluginTUI.tsx
|
|
1959
|
-
var
|
|
3186
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1960
3187
|
function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
1961
|
-
const [stack, setStack] = (0,
|
|
1962
|
-
const [confirm, setConfirm] = (0,
|
|
1963
|
-
const [refreshCounter, setRefreshCounter] = (0,
|
|
3188
|
+
const [stack, setStack] = (0, import_react16.useState)([{ screen: "main" }]);
|
|
3189
|
+
const [confirm, setConfirm] = (0, import_react16.useState)();
|
|
3190
|
+
const [refreshCounter, setRefreshCounter] = (0, import_react16.useState)(0);
|
|
1964
3191
|
const current = stack[stack.length - 1] ?? { screen: "main" };
|
|
1965
|
-
const push = (0,
|
|
3192
|
+
const push = (0, import_react16.useCallback)((state) => {
|
|
1966
3193
|
setStack((prev) => [...prev, state]);
|
|
1967
3194
|
}, []);
|
|
1968
|
-
const pop = (0,
|
|
3195
|
+
const pop = (0, import_react16.useCallback)(() => {
|
|
1969
3196
|
setStack((prev) => {
|
|
1970
3197
|
if (prev.length <= 1) {
|
|
1971
3198
|
onClose();
|
|
@@ -1974,7 +3201,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
1974
3201
|
return prev.slice(0, -1);
|
|
1975
3202
|
});
|
|
1976
3203
|
}, [onClose]);
|
|
1977
|
-
const popN = (0,
|
|
3204
|
+
const popN = (0, import_react16.useCallback)(
|
|
1978
3205
|
(n) => {
|
|
1979
3206
|
setStack((prev) => {
|
|
1980
3207
|
const next = prev.slice(0, Math.max(1, prev.length - n));
|
|
@@ -1987,20 +3214,20 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
1987
3214
|
},
|
|
1988
3215
|
[onClose]
|
|
1989
3216
|
);
|
|
1990
|
-
const notify = (0,
|
|
3217
|
+
const notify = (0, import_react16.useCallback)(
|
|
1991
3218
|
(content) => {
|
|
1992
3219
|
addMessage?.({ role: "system", content });
|
|
1993
3220
|
},
|
|
1994
3221
|
[addMessage]
|
|
1995
3222
|
);
|
|
1996
|
-
const refresh = (0,
|
|
3223
|
+
const refresh = (0, import_react16.useCallback)(() => {
|
|
1997
3224
|
setRefreshCounter((c) => c + 1);
|
|
1998
3225
|
}, []);
|
|
1999
|
-
const setConfirmNav = (0,
|
|
3226
|
+
const setConfirmNav = (0, import_react16.useCallback)(
|
|
2000
3227
|
(state) => setConfirm(state),
|
|
2001
3228
|
[setConfirm]
|
|
2002
3229
|
);
|
|
2003
|
-
const pushNav = (0,
|
|
3230
|
+
const pushNav = (0, import_react16.useCallback)(
|
|
2004
3231
|
(state) => push({ screen: state.screen, context: state.context }),
|
|
2005
3232
|
[push]
|
|
2006
3233
|
);
|
|
@@ -2012,7 +3239,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2012
3239
|
refreshCounter,
|
|
2013
3240
|
stack.length
|
|
2014
3241
|
);
|
|
2015
|
-
const handleSelect = (0,
|
|
3242
|
+
const handleSelect = (0, import_react16.useCallback)(
|
|
2016
3243
|
(value) => {
|
|
2017
3244
|
const screen2 = current.screen;
|
|
2018
3245
|
const ctx = current.context;
|
|
@@ -2030,7 +3257,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2030
3257
|
},
|
|
2031
3258
|
[current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
|
|
2032
3259
|
);
|
|
2033
|
-
const handleTextSubmit = (0,
|
|
3260
|
+
const handleTextSubmit = (0, import_react16.useCallback)(
|
|
2034
3261
|
(value) => {
|
|
2035
3262
|
if (current.screen === "marketplace-add") {
|
|
2036
3263
|
callbacks.marketplaceAdd(value).then((name) => {
|
|
@@ -2045,7 +3272,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2045
3272
|
[current.screen, callbacks, notify, pop]
|
|
2046
3273
|
);
|
|
2047
3274
|
if (confirm) {
|
|
2048
|
-
return /* @__PURE__ */ (0,
|
|
3275
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2049
3276
|
ConfirmPrompt,
|
|
2050
3277
|
{
|
|
2051
3278
|
message: confirm.message,
|
|
@@ -2058,7 +3285,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2058
3285
|
}
|
|
2059
3286
|
const screen = current.screen;
|
|
2060
3287
|
if (screen === "marketplace-add") {
|
|
2061
|
-
return /* @__PURE__ */ (0,
|
|
3288
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2062
3289
|
TextPrompt,
|
|
2063
3290
|
{
|
|
2064
3291
|
title: "Add Marketplace Source",
|
|
@@ -2070,7 +3297,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2070
3297
|
);
|
|
2071
3298
|
}
|
|
2072
3299
|
if (screen === "marketplace-action") {
|
|
2073
|
-
return /* @__PURE__ */ (0,
|
|
3300
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2074
3301
|
MenuSelect,
|
|
2075
3302
|
{
|
|
2076
3303
|
title: `Marketplace: ${current.context?.marketplace ?? ""}`,
|
|
@@ -2086,7 +3313,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2086
3313
|
);
|
|
2087
3314
|
}
|
|
2088
3315
|
if (screen === "marketplace-install-scope") {
|
|
2089
|
-
return /* @__PURE__ */ (0,
|
|
3316
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2090
3317
|
MenuSelect,
|
|
2091
3318
|
{
|
|
2092
3319
|
title: `Install scope for "${current.context?.pluginId ?? ""}"`,
|
|
@@ -2101,7 +3328,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2101
3328
|
);
|
|
2102
3329
|
}
|
|
2103
3330
|
if (screen === "installed-action") {
|
|
2104
|
-
return /* @__PURE__ */ (0,
|
|
3331
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2105
3332
|
MenuSelect,
|
|
2106
3333
|
{
|
|
2107
3334
|
title: `Plugin: ${current.context?.pluginId ?? ""}`,
|
|
@@ -2124,7 +3351,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2124
3351
|
{ label: "Installed Plugins", value: "installed" }
|
|
2125
3352
|
]
|
|
2126
3353
|
};
|
|
2127
|
-
return /* @__PURE__ */ (0,
|
|
3354
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2128
3355
|
MenuSelect,
|
|
2129
3356
|
{
|
|
2130
3357
|
title: titleMap[screen] ?? "Plugin Management",
|
|
@@ -2142,9 +3369,9 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
2142
3369
|
var import_ink15 = require("ink");
|
|
2143
3370
|
|
|
2144
3371
|
// src/ui/ListPicker.tsx
|
|
2145
|
-
var
|
|
3372
|
+
var import_react17 = require("react");
|
|
2146
3373
|
var import_ink14 = require("ink");
|
|
2147
|
-
var
|
|
3374
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2148
3375
|
var DEFAULT_MAX_VISIBLE = 3;
|
|
2149
3376
|
function ListPicker({
|
|
2150
3377
|
items,
|
|
@@ -2153,54 +3380,52 @@ function ListPicker({
|
|
|
2153
3380
|
onCancel,
|
|
2154
3381
|
maxVisible = DEFAULT_MAX_VISIBLE
|
|
2155
3382
|
}) {
|
|
2156
|
-
const [
|
|
2157
|
-
const
|
|
2158
|
-
const
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
setScrollOffset(next);
|
|
2174
|
-
}
|
|
2175
|
-
} else if (key.downArrow) {
|
|
2176
|
-
const next = selectedRef.current < items.length - 1 ? selectedRef.current + 1 : selectedRef.current;
|
|
2177
|
-
selectedRef.current = next;
|
|
2178
|
-
setSelectedIndex(next);
|
|
2179
|
-
if (next >= scrollOffset + maxVisible) {
|
|
2180
|
-
setScrollOffset(next - maxVisible + 1);
|
|
2181
|
-
}
|
|
2182
|
-
} else if (key.return) {
|
|
2183
|
-
const item = items[selectedRef.current];
|
|
2184
|
-
if (item !== void 0) {
|
|
2185
|
-
resolvedRef.current = true;
|
|
2186
|
-
onSelect(item);
|
|
3383
|
+
const [state, setState] = (0, import_react17.useState)(() => createSelectionFlowState());
|
|
3384
|
+
const stateRef = (0, import_react17.useRef)(state);
|
|
3385
|
+
const applyAction = (0, import_react17.useCallback)(
|
|
3386
|
+
(action) => {
|
|
3387
|
+
const result = applySelectionInput(stateRef.current, action, {
|
|
3388
|
+
itemCount: items.length,
|
|
3389
|
+
maxVisible
|
|
3390
|
+
});
|
|
3391
|
+
stateRef.current = result.state;
|
|
3392
|
+
setState(result.state);
|
|
3393
|
+
if (result.effect.type === "cancel") {
|
|
3394
|
+
onCancel();
|
|
3395
|
+
} else if (result.effect.type === "select") {
|
|
3396
|
+
const item = items[result.effect.index];
|
|
3397
|
+
if (item !== void 0) {
|
|
3398
|
+
onSelect(item);
|
|
3399
|
+
}
|
|
2187
3400
|
}
|
|
3401
|
+
},
|
|
3402
|
+
[items, maxVisible, onCancel, onSelect]
|
|
3403
|
+
);
|
|
3404
|
+
(0, import_ink14.useInput)((_input, key) => {
|
|
3405
|
+
const action = getVerticalSelectionInputAction(key);
|
|
3406
|
+
if (action !== void 0) {
|
|
3407
|
+
applyAction(action);
|
|
2188
3408
|
}
|
|
2189
3409
|
});
|
|
2190
3410
|
if (items.length === 0) {
|
|
2191
|
-
return /* @__PURE__ */ (0,
|
|
3411
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Box, {});
|
|
3412
|
+
}
|
|
3413
|
+
const normalizedState = normalizeSelectionState(state, { itemCount: items.length, maxVisible });
|
|
3414
|
+
if (normalizedState !== state) {
|
|
3415
|
+
stateRef.current = normalizedState;
|
|
2192
3416
|
}
|
|
3417
|
+
const { selectedIndex, scrollOffset } = normalizedState;
|
|
2193
3418
|
const visibleItems = items.slice(scrollOffset, scrollOffset + maxVisible);
|
|
2194
3419
|
const hasMore = scrollOffset + maxVisible < items.length;
|
|
2195
3420
|
const hasLess = scrollOffset > 0;
|
|
2196
|
-
return /* @__PURE__ */ (0,
|
|
2197
|
-
hasLess && /* @__PURE__ */ (0,
|
|
3421
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Box, { flexDirection: "column", children: [
|
|
3422
|
+
hasLess && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Text, { dimColor: true, children: [
|
|
2198
3423
|
" \u2191 ",
|
|
2199
3424
|
scrollOffset,
|
|
2200
3425
|
" more above"
|
|
2201
3426
|
] }),
|
|
2202
|
-
visibleItems.map((item, index) => /* @__PURE__ */ (0,
|
|
2203
|
-
hasMore && /* @__PURE__ */ (0,
|
|
3427
|
+
visibleItems.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink14.Box, { marginBottom: 1, children: renderItem(item, scrollOffset + index === selectedIndex) }, scrollOffset + index)),
|
|
3428
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink14.Text, { dimColor: true, children: [
|
|
2204
3429
|
" \u2193 ",
|
|
2205
3430
|
items.length - scrollOffset - maxVisible,
|
|
2206
3431
|
" more below"
|
|
@@ -2209,7 +3434,7 @@ function ListPicker({
|
|
|
2209
3434
|
}
|
|
2210
3435
|
|
|
2211
3436
|
// src/ui/SessionPicker.tsx
|
|
2212
|
-
var
|
|
3437
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
2213
3438
|
var SESSION_ID_DISPLAY_LENGTH = 8;
|
|
2214
3439
|
function SessionPicker({
|
|
2215
3440
|
sessionStore,
|
|
@@ -2218,9 +3443,9 @@ function SessionPicker({
|
|
|
2218
3443
|
onCancel
|
|
2219
3444
|
}) {
|
|
2220
3445
|
const sessions = (sessionStore?.list() ?? []).filter((s) => s.cwd === cwd);
|
|
2221
|
-
return /* @__PURE__ */ (0,
|
|
2222
|
-
/* @__PURE__ */ (0,
|
|
2223
|
-
/* @__PURE__ */ (0,
|
|
3446
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink15.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
3447
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink15.Text, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
|
|
3448
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2224
3449
|
ListPicker,
|
|
2225
3450
|
{
|
|
2226
3451
|
items: sessions,
|
|
@@ -2231,24 +3456,24 @@ function SessionPicker({
|
|
|
2231
3456
|
});
|
|
2232
3457
|
const rawPreview = lastMsg?.content?.replace(/[\n\r]+/g, " ").trim() ?? "";
|
|
2233
3458
|
const preview = rawPreview ? rawPreview.slice(0, 60) + (rawPreview.length > 60 ? "..." : "") : "";
|
|
2234
|
-
return /* @__PURE__ */ (0,
|
|
3459
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink15.Text, { children: [
|
|
2235
3460
|
isSelected ? "> " : " ",
|
|
2236
|
-
/* @__PURE__ */ (0,
|
|
3461
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink15.Text, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
|
|
2237
3462
|
" ",
|
|
2238
|
-
/* @__PURE__ */ (0,
|
|
3463
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink15.Text, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
|
|
2239
3464
|
month: "short",
|
|
2240
3465
|
day: "numeric",
|
|
2241
3466
|
hour: "2-digit",
|
|
2242
3467
|
minute: "2-digit"
|
|
2243
3468
|
}) }),
|
|
2244
3469
|
" ",
|
|
2245
|
-
/* @__PURE__ */ (0,
|
|
3470
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink15.Text, { dimColor: true, children: [
|
|
2246
3471
|
"msgs: ",
|
|
2247
3472
|
session.messages.length
|
|
2248
3473
|
] }),
|
|
2249
|
-
preview ? /* @__PURE__ */ (0,
|
|
3474
|
+
preview ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
2250
3475
|
"\n ",
|
|
2251
|
-
/* @__PURE__ */ (0,
|
|
3476
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink15.Text, { color: "gray", children: preview })
|
|
2252
3477
|
] }) : null
|
|
2253
3478
|
] });
|
|
2254
3479
|
},
|
|
@@ -2259,11 +3484,55 @@ function SessionPicker({
|
|
|
2259
3484
|
] });
|
|
2260
3485
|
}
|
|
2261
3486
|
|
|
3487
|
+
// src/ui/BackgroundTaskPanel.tsx
|
|
3488
|
+
var import_ink16 = require("ink");
|
|
3489
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
3490
|
+
var MS_PER_SECOND = 1e3;
|
|
3491
|
+
var SECONDS_PER_MINUTE = 60;
|
|
3492
|
+
var MINUTES_PER_HOUR = 60;
|
|
3493
|
+
function getStatusColor(status) {
|
|
3494
|
+
if (status === "completed") return "green";
|
|
3495
|
+
if (status === "failed") return "red";
|
|
3496
|
+
if (status === "cancelled") return "yellow";
|
|
3497
|
+
return "cyan";
|
|
3498
|
+
}
|
|
3499
|
+
function getStatusMarker(status) {
|
|
3500
|
+
if (status === "queued" || status === "running") return "\u25A1";
|
|
3501
|
+
return "\u25A0";
|
|
3502
|
+
}
|
|
3503
|
+
function getTaskPreview(task) {
|
|
3504
|
+
return task.errorPreview ?? task.resultPreview ?? task.currentAction ?? task.preview;
|
|
3505
|
+
}
|
|
3506
|
+
function formatAge(iso) {
|
|
3507
|
+
if (!iso) return void 0;
|
|
3508
|
+
const timestamp = Date.parse(iso);
|
|
3509
|
+
if (Number.isNaN(timestamp)) return void 0;
|
|
3510
|
+
const seconds = Math.max(0, Math.floor((Date.now() - timestamp) / MS_PER_SECOND));
|
|
3511
|
+
if (seconds < SECONDS_PER_MINUTE) return `${seconds}s`;
|
|
3512
|
+
const minutes = Math.floor(seconds / SECONDS_PER_MINUTE);
|
|
3513
|
+
if (minutes < MINUTES_PER_HOUR) return `${minutes}m`;
|
|
3514
|
+
return `${Math.floor(minutes / MINUTES_PER_HOUR)}h`;
|
|
3515
|
+
}
|
|
3516
|
+
function BackgroundTaskPanel({ tasks }) {
|
|
3517
|
+
if (tasks.length === 0) return null;
|
|
3518
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_ink16.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
3519
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink16.Text, { color: "cyan", bold: true, children: "Background" }),
|
|
3520
|
+
tasks.map((task) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_ink16.Text, { children: [
|
|
3521
|
+
"- ",
|
|
3522
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink16.Text, { color: getStatusColor(task.status), children: getStatusMarker(task.status) }),
|
|
3523
|
+
` ${task.kind}:${task.label} ${task.id}`,
|
|
3524
|
+
task.status === "running" && formatAge(task.lastActivityAt) ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink16.Text, { dimColor: true, children: ` idle ${formatAge(task.lastActivityAt)}` }) : null,
|
|
3525
|
+
task.timeoutReason ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink16.Text, { dimColor: true, children: ` (${task.timeoutReason})` }) : null,
|
|
3526
|
+
getTaskPreview(task) ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink16.Text, { dimColor: true, children: ` - ${getTaskPreview(task)}` }) : null
|
|
3527
|
+
] }, task.id))
|
|
3528
|
+
] });
|
|
3529
|
+
}
|
|
3530
|
+
|
|
2262
3531
|
// src/ui/App.tsx
|
|
2263
|
-
var
|
|
3532
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
2264
3533
|
function App(props) {
|
|
2265
|
-
const [activeSessionId, setActiveSessionId] = (0,
|
|
2266
|
-
return /* @__PURE__ */ (0,
|
|
3534
|
+
const [activeSessionId, setActiveSessionId] = (0, import_react18.useState)(props.resumeSessionId);
|
|
3535
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2267
3536
|
AppInner,
|
|
2268
3537
|
{
|
|
2269
3538
|
...props,
|
|
@@ -2275,6 +3544,7 @@ function App(props) {
|
|
|
2275
3544
|
}
|
|
2276
3545
|
function AppInner(props) {
|
|
2277
3546
|
const cwd = props.cwd;
|
|
3547
|
+
const providerDefinitions = props.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS;
|
|
2278
3548
|
const {
|
|
2279
3549
|
interactiveSession,
|
|
2280
3550
|
registry,
|
|
@@ -2284,12 +3554,15 @@ function AppInner(props) {
|
|
|
2284
3554
|
activeTools,
|
|
2285
3555
|
isThinking,
|
|
2286
3556
|
isAborting,
|
|
3557
|
+
isShuttingDown,
|
|
2287
3558
|
pendingPrompt,
|
|
3559
|
+
backgroundTasks,
|
|
2288
3560
|
permissionRequest,
|
|
2289
3561
|
contextState,
|
|
2290
3562
|
handleSubmit: baseHandleSubmit,
|
|
2291
3563
|
handleAbort,
|
|
2292
|
-
handleCancelQueue
|
|
3564
|
+
handleCancelQueue,
|
|
3565
|
+
handleShutdown
|
|
2293
3566
|
} = useInteractiveSession({
|
|
2294
3567
|
cwd,
|
|
2295
3568
|
provider: props.provider,
|
|
@@ -2298,38 +3571,65 @@ function AppInner(props) {
|
|
|
2298
3571
|
sessionStore: props.sessionStore,
|
|
2299
3572
|
resumeSessionId: props.resumeSessionId,
|
|
2300
3573
|
forkSession: props.forkSession,
|
|
2301
|
-
sessionName: props.sessionName
|
|
3574
|
+
sessionName: props.sessionName,
|
|
3575
|
+
backgroundTaskRunners: props.backgroundTaskRunners,
|
|
3576
|
+
subagentRunnerFactory: props.subagentRunnerFactory,
|
|
3577
|
+
commandModules: props.commandModules,
|
|
3578
|
+
providerDefinitions
|
|
2302
3579
|
});
|
|
2303
3580
|
const pluginCallbacks = usePluginCallbacks(cwd);
|
|
2304
|
-
const
|
|
3581
|
+
const { exit } = (0, import_ink17.useApp)();
|
|
3582
|
+
const [sessionName, setSessionName] = (0, import_react18.useState)(props.sessionName);
|
|
2305
3583
|
const {
|
|
2306
3584
|
handleSubmit,
|
|
2307
3585
|
pendingModelId,
|
|
3586
|
+
pendingProviderProfile,
|
|
3587
|
+
pendingProviderSetupType,
|
|
2308
3588
|
showPluginTUI,
|
|
2309
3589
|
showSessionPicker,
|
|
2310
3590
|
setShowPluginTUI,
|
|
2311
3591
|
setShowSessionPicker,
|
|
2312
|
-
handleModelConfirm
|
|
3592
|
+
handleModelConfirm,
|
|
3593
|
+
handleProviderConfirm,
|
|
3594
|
+
handleProviderSetupSubmit,
|
|
3595
|
+
handleProviderSetupCancel
|
|
2313
3596
|
} = useSideEffects({
|
|
3597
|
+
cwd,
|
|
2314
3598
|
interactiveSession,
|
|
2315
3599
|
addEntry,
|
|
2316
3600
|
baseHandleSubmit,
|
|
2317
|
-
setSessionName
|
|
3601
|
+
setSessionName,
|
|
3602
|
+
providerDefinitions
|
|
2318
3603
|
});
|
|
2319
|
-
(0,
|
|
3604
|
+
(0, import_react18.useEffect)(() => {
|
|
2320
3605
|
const name = interactiveSession?.getName?.();
|
|
2321
3606
|
if (name && !sessionName) setSessionName(name);
|
|
2322
3607
|
}, [interactiveSession, sessionName]);
|
|
2323
|
-
(0,
|
|
3608
|
+
(0, import_react18.useEffect)(() => {
|
|
2324
3609
|
const title = sessionName ? `Robota \u2014 ${sessionName}` : "Robota";
|
|
2325
3610
|
process.stdout.write(`\x1B]0;${title}\x07`);
|
|
2326
3611
|
}, [sessionName]);
|
|
2327
|
-
(0,
|
|
2328
|
-
(
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
)
|
|
3612
|
+
(0, import_ink17.useInput)((_input, key) => {
|
|
3613
|
+
if (!key.escape || !isThinking) return;
|
|
3614
|
+
if (permissionRequest || showPluginTUI || showSessionPicker) return;
|
|
3615
|
+
handleAbort();
|
|
3616
|
+
});
|
|
3617
|
+
(0, import_ink17.useInput)((input, key) => {
|
|
3618
|
+
if (!key.ctrl || input !== "c" || isShuttingDown) return;
|
|
3619
|
+
void handleShutdown("prompt_input_exit").finally(() => exit());
|
|
3620
|
+
});
|
|
3621
|
+
(0, import_react18.useEffect)(() => {
|
|
3622
|
+
const onSigterm = () => {
|
|
3623
|
+
if (isShuttingDown) return;
|
|
3624
|
+
void handleShutdown("other").finally(() => exit());
|
|
3625
|
+
};
|
|
3626
|
+
process.once("SIGINT", onSigterm);
|
|
3627
|
+
process.once("SIGTERM", onSigterm);
|
|
3628
|
+
return () => {
|
|
3629
|
+
process.off("SIGINT", onSigterm);
|
|
3630
|
+
process.off("SIGTERM", onSigterm);
|
|
3631
|
+
};
|
|
3632
|
+
}, [handleShutdown, exit, isShuttingDown]);
|
|
2333
3633
|
let permissionMode = props.permissionMode ?? "default";
|
|
2334
3634
|
let sessionId = "";
|
|
2335
3635
|
try {
|
|
@@ -2338,41 +3638,59 @@ function AppInner(props) {
|
|
|
2338
3638
|
sessionId = session.getSessionId();
|
|
2339
3639
|
} catch {
|
|
2340
3640
|
}
|
|
2341
|
-
return /* @__PURE__ */ (0,
|
|
2342
|
-
/* @__PURE__ */ (0,
|
|
2343
|
-
/* @__PURE__ */ (0,
|
|
3641
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_ink17.Box, { flexDirection: "column", children: [
|
|
3642
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_ink17.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
3643
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink17.Text, { color: "cyan", bold: true, children: `
|
|
2344
3644
|
____ ___ ____ ___ _____ _
|
|
2345
3645
|
| _ \\ / _ \\| __ ) / _ \\_ _|/ \\
|
|
2346
3646
|
| |_) | | | | _ \\| | | || | / _ \\
|
|
2347
3647
|
| _ <| |_| | |_) | |_| || |/ ___ \\
|
|
2348
3648
|
|_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
|
|
2349
3649
|
` }),
|
|
2350
|
-
/* @__PURE__ */ (0,
|
|
3650
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_ink17.Text, { dimColor: true, children: [
|
|
2351
3651
|
" v",
|
|
2352
3652
|
props.version ?? "0.0.0"
|
|
2353
3653
|
] })
|
|
2354
3654
|
] }),
|
|
2355
|
-
/* @__PURE__ */ (0,
|
|
2356
|
-
/* @__PURE__ */ (0,
|
|
2357
|
-
|
|
3655
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_ink17.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
|
|
3656
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(MessageList, { history }),
|
|
3657
|
+
isShuttingDown && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink17.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink17.Text, { color: "yellow", children: "Shutting down..." }) }),
|
|
3658
|
+
(isThinking || activeTools.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink17.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StreamingIndicator, { text: streamingText, activeTools }) }),
|
|
3659
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(BackgroundTaskPanel, { tasks: backgroundTasks })
|
|
2358
3660
|
] }),
|
|
2359
|
-
permissionRequest && /* @__PURE__ */ (0,
|
|
2360
|
-
pendingModelId && /* @__PURE__ */ (0,
|
|
3661
|
+
permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PermissionPrompt, { request: permissionRequest }),
|
|
3662
|
+
pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2361
3663
|
ConfirmPrompt,
|
|
2362
3664
|
{
|
|
2363
|
-
message: `Change model to ${(0,
|
|
3665
|
+
message: `Change model to ${(0, import_agent_core7.getModelName)(pendingModelId)}? This will restart the session.`,
|
|
2364
3666
|
onSelect: handleModelConfirm
|
|
2365
3667
|
}
|
|
2366
3668
|
),
|
|
2367
|
-
|
|
3669
|
+
pendingProviderProfile && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3670
|
+
ConfirmPrompt,
|
|
3671
|
+
{
|
|
3672
|
+
message: `Change provider to ${pendingProviderProfile}? This will restart the session.`,
|
|
3673
|
+
onSelect: handleProviderConfirm
|
|
3674
|
+
}
|
|
3675
|
+
),
|
|
3676
|
+
pendingProviderSetupType && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
3677
|
+
ProviderSetupPrompt,
|
|
3678
|
+
{
|
|
3679
|
+
type: pendingProviderSetupType,
|
|
3680
|
+
providerDefinitions,
|
|
3681
|
+
onSubmit: handleProviderSetupSubmit,
|
|
3682
|
+
onCancel: handleProviderSetupCancel
|
|
3683
|
+
}
|
|
3684
|
+
),
|
|
3685
|
+
showPluginTUI && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2368
3686
|
PluginTUI,
|
|
2369
3687
|
{
|
|
2370
3688
|
callbacks: pluginCallbacks,
|
|
2371
3689
|
onClose: () => setShowPluginTUI(false),
|
|
2372
|
-
addMessage: (msg) => addEntry((0,
|
|
3690
|
+
addMessage: (msg) => addEntry((0, import_agent_core7.messageToHistoryEntry)((0, import_agent_core7.createSystemMessage)(msg.content)))
|
|
2373
3691
|
}
|
|
2374
3692
|
),
|
|
2375
|
-
showSessionPicker && /* @__PURE__ */ (0,
|
|
3693
|
+
showSessionPicker && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2376
3694
|
SessionPicker,
|
|
2377
3695
|
{
|
|
2378
3696
|
sessionStore: props.sessionStore,
|
|
@@ -2383,15 +3701,15 @@ function AppInner(props) {
|
|
|
2383
3701
|
},
|
|
2384
3702
|
onCancel: () => {
|
|
2385
3703
|
setShowSessionPicker(false);
|
|
2386
|
-
addEntry((0,
|
|
3704
|
+
addEntry((0, import_agent_core7.messageToHistoryEntry)((0, import_agent_core7.createSystemMessage)("Session resume cancelled.")));
|
|
2387
3705
|
}
|
|
2388
3706
|
}
|
|
2389
3707
|
),
|
|
2390
|
-
/* @__PURE__ */ (0,
|
|
3708
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2391
3709
|
StatusBar,
|
|
2392
3710
|
{
|
|
2393
3711
|
permissionMode,
|
|
2394
|
-
modelName: props.modelId ? (0,
|
|
3712
|
+
modelName: props.modelId ? (0, import_agent_core7.getModelName)(props.modelId) : "",
|
|
2395
3713
|
sessionId,
|
|
2396
3714
|
messageCount: history.length,
|
|
2397
3715
|
isThinking,
|
|
@@ -2401,24 +3719,24 @@ function AppInner(props) {
|
|
|
2401
3719
|
sessionName
|
|
2402
3720
|
}
|
|
2403
3721
|
),
|
|
2404
|
-
/* @__PURE__ */ (0,
|
|
3722
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2405
3723
|
InputArea,
|
|
2406
3724
|
{
|
|
2407
3725
|
onSubmit: handleSubmit,
|
|
2408
3726
|
onCancelQueue: handleCancelQueue,
|
|
2409
|
-
isDisabled: !!permissionRequest || showPluginTUI || showSessionPicker || isThinking && !!pendingPrompt,
|
|
3727
|
+
isDisabled: !!permissionRequest || showPluginTUI || showSessionPicker || isShuttingDown || !!pendingProviderSetupType || isThinking && !!pendingPrompt,
|
|
2410
3728
|
isAborting,
|
|
2411
3729
|
pendingPrompt,
|
|
2412
3730
|
registry,
|
|
2413
3731
|
sessionName
|
|
2414
3732
|
}
|
|
2415
3733
|
),
|
|
2416
|
-
/* @__PURE__ */ (0,
|
|
3734
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ink17.Text, { children: " " })
|
|
2417
3735
|
] });
|
|
2418
3736
|
}
|
|
2419
3737
|
|
|
2420
3738
|
// src/ui/render.tsx
|
|
2421
|
-
var
|
|
3739
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
2422
3740
|
function renderApp(options) {
|
|
2423
3741
|
process.on("unhandledRejection", (reason) => {
|
|
2424
3742
|
process.stderr.write(`
|
|
@@ -2432,8 +3750,8 @@ function renderApp(options) {
|
|
|
2432
3750
|
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
2433
3751
|
process.stdout.write("\x1B[?2004h");
|
|
2434
3752
|
}
|
|
2435
|
-
const instance = (0,
|
|
2436
|
-
exitOnCtrlC:
|
|
3753
|
+
const instance = (0, import_ink18.render)(/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(App, { ...options }), {
|
|
3754
|
+
exitOnCtrlC: false
|
|
2437
3755
|
});
|
|
2438
3756
|
instance.waitUntilExit().then(() => {
|
|
2439
3757
|
if (process.stdout.isTTY) {
|
|
@@ -2450,29 +3768,613 @@ function renderApp(options) {
|
|
|
2450
3768
|
});
|
|
2451
3769
|
}
|
|
2452
3770
|
|
|
2453
|
-
// src/
|
|
2454
|
-
var
|
|
2455
|
-
|
|
2456
|
-
|
|
3771
|
+
// src/background/managed-shell-process-runner.ts
|
|
3772
|
+
var import_node_child_process = require("child_process");
|
|
3773
|
+
var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
|
|
3774
|
+
var DEFAULT_OUTPUT_LIMIT_BYTES = 3e4;
|
|
3775
|
+
var DEFAULT_KILL_GRACE_MS = 2e3;
|
|
3776
|
+
var LOG_PAGE_SIZE = 200;
|
|
3777
|
+
function createCapture(limitBytes) {
|
|
3778
|
+
const chunks = [];
|
|
3779
|
+
let capturedBytes = 0;
|
|
3780
|
+
let truncated = false;
|
|
3781
|
+
return {
|
|
3782
|
+
appendOutput(text) {
|
|
3783
|
+
if (truncated) return;
|
|
3784
|
+
const remaining = limitBytes - capturedBytes;
|
|
3785
|
+
const buffer = Buffer.from(text, "utf8");
|
|
3786
|
+
if (buffer.byteLength <= remaining) {
|
|
3787
|
+
chunks.push(text);
|
|
3788
|
+
capturedBytes += buffer.byteLength;
|
|
3789
|
+
return;
|
|
3790
|
+
}
|
|
3791
|
+
chunks.push(buffer.subarray(0, Math.max(remaining, 0)).toString("utf8"));
|
|
3792
|
+
chunks.push("\n[output truncated]\n");
|
|
3793
|
+
truncated = true;
|
|
3794
|
+
},
|
|
3795
|
+
getOutput() {
|
|
3796
|
+
return chunks.join("");
|
|
3797
|
+
}
|
|
3798
|
+
};
|
|
3799
|
+
}
|
|
3800
|
+
function appendLog(lines, source, text) {
|
|
3801
|
+
for (const line of text.split(/\r?\n/)) {
|
|
3802
|
+
if (line.length > 0) lines.push(`[${source}] ${line}`);
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
function resolveShell(request) {
|
|
3806
|
+
return { command: request.shell ?? "sh", args: ["-c", request.command] };
|
|
3807
|
+
}
|
|
3808
|
+
function sendInput(child, input) {
|
|
3809
|
+
return new Promise((resolve, reject) => {
|
|
3810
|
+
const onError = (error) => {
|
|
3811
|
+
child.stdin.off("error", onError);
|
|
3812
|
+
reject(error);
|
|
3813
|
+
};
|
|
3814
|
+
child.stdin.once("error", onError);
|
|
3815
|
+
child.stdin.end(input, () => {
|
|
3816
|
+
child.stdin.off("error", onError);
|
|
3817
|
+
resolve();
|
|
3818
|
+
});
|
|
3819
|
+
});
|
|
3820
|
+
}
|
|
3821
|
+
function createManagedShellProcessRunner(options = {}) {
|
|
3822
|
+
const killGraceMs = options.killGraceMs ?? DEFAULT_KILL_GRACE_MS;
|
|
3823
|
+
return {
|
|
3824
|
+
kind: "process",
|
|
3825
|
+
start(task) {
|
|
3826
|
+
if (task.request.kind !== "process") {
|
|
3827
|
+
throw new import_agent_sdk3.BackgroundTaskError("runner", `Invalid process task kind: ${task.request.kind}`);
|
|
3828
|
+
}
|
|
3829
|
+
return startProcessTask(task.taskId, task.request, killGraceMs);
|
|
3830
|
+
}
|
|
3831
|
+
};
|
|
3832
|
+
}
|
|
3833
|
+
function startProcessTask(taskId, request, killGraceMs) {
|
|
3834
|
+
const shell = resolveShell(request);
|
|
3835
|
+
const runtime = {
|
|
3836
|
+
taskId,
|
|
3837
|
+
request,
|
|
3838
|
+
child: (0, import_node_child_process.spawn)(shell.command, shell.args, {
|
|
3839
|
+
cwd: request.cwd,
|
|
3840
|
+
env: { ...process.env, ...request.env ?? {} },
|
|
3841
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3842
|
+
}),
|
|
3843
|
+
logs: [],
|
|
3844
|
+
capture: createCapture(request.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES),
|
|
3845
|
+
killGraceMs
|
|
3846
|
+
};
|
|
3847
|
+
const result = createProcessResult(runtime);
|
|
3848
|
+
return createProcessHandle(runtime, result);
|
|
3849
|
+
}
|
|
3850
|
+
function createProcessResult(runtime) {
|
|
3851
|
+
let settled = false;
|
|
3852
|
+
return new Promise((resolve, reject) => {
|
|
3853
|
+
const timeoutTimer = runtime.request.timeoutMs ? setTimeout(() => {
|
|
3854
|
+
appendLog(runtime.logs, "system", `timed out after ${runtime.request.timeoutMs}ms`);
|
|
3855
|
+
runtime.child.kill("SIGTERM");
|
|
3856
|
+
rejectOnceLocal(new import_agent_sdk3.BackgroundTaskError("timeout", "Background process timed out"));
|
|
3857
|
+
}, runtime.request.timeoutMs) : void 0;
|
|
3858
|
+
function clearTimers() {
|
|
3859
|
+
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
3860
|
+
if (runtime.killTimer) clearTimeout(runtime.killTimer);
|
|
3861
|
+
}
|
|
3862
|
+
function resolveOnce(exitCode, signalCode) {
|
|
3863
|
+
if (settled) return;
|
|
3864
|
+
settled = true;
|
|
3865
|
+
clearTimers();
|
|
3866
|
+
resolve({
|
|
3867
|
+
taskId: runtime.taskId,
|
|
3868
|
+
kind: "process",
|
|
3869
|
+
output: runtime.capture.getOutput(),
|
|
3870
|
+
exitCode,
|
|
3871
|
+
signalCode
|
|
3872
|
+
});
|
|
3873
|
+
}
|
|
3874
|
+
function rejectOnceLocal(error) {
|
|
3875
|
+
if (settled) return;
|
|
3876
|
+
settled = true;
|
|
3877
|
+
clearTimers();
|
|
3878
|
+
reject(error);
|
|
3879
|
+
}
|
|
3880
|
+
attachOutputListeners(runtime);
|
|
3881
|
+
runtime.child.on("error", (error) => {
|
|
3882
|
+
appendLog(runtime.logs, "system", error.message);
|
|
3883
|
+
rejectOnceLocal(new import_agent_sdk3.BackgroundTaskError("process", error.message));
|
|
3884
|
+
});
|
|
3885
|
+
runtime.child.on("close", (code, signal) => {
|
|
3886
|
+
resolveOnce(code ?? void 0, signal ?? void 0);
|
|
3887
|
+
});
|
|
3888
|
+
if (runtime.request.stdin) {
|
|
3889
|
+
runtime.child.stdin.write(runtime.request.stdin);
|
|
3890
|
+
runtime.child.stdin.end();
|
|
3891
|
+
}
|
|
3892
|
+
});
|
|
3893
|
+
}
|
|
3894
|
+
function createProcessHandle(runtime, result) {
|
|
3895
|
+
return {
|
|
3896
|
+
taskId: runtime.taskId,
|
|
3897
|
+
...runtime.child.pid !== void 0 ? { pid: runtime.child.pid } : {},
|
|
3898
|
+
result,
|
|
3899
|
+
cancel: async (reason) => {
|
|
3900
|
+
cancelProcess(runtime, reason);
|
|
3901
|
+
},
|
|
3902
|
+
send: async (input) => {
|
|
3903
|
+
if (!input.stdin) return;
|
|
3904
|
+
await sendInput(runtime.child, input.stdin);
|
|
3905
|
+
},
|
|
3906
|
+
readLog: (cursor) => Promise.resolve(readProcessLog(runtime, cursor))
|
|
3907
|
+
};
|
|
3908
|
+
}
|
|
3909
|
+
function attachOutputListeners(runtime) {
|
|
3910
|
+
runtime.child.stdout.on("data", (chunk) => {
|
|
3911
|
+
const text = chunk.toString("utf8");
|
|
3912
|
+
runtime.capture.appendOutput(text);
|
|
3913
|
+
appendLog(runtime.logs, "stdout", text);
|
|
3914
|
+
});
|
|
3915
|
+
runtime.child.stderr.on("data", (chunk) => {
|
|
3916
|
+
const text = chunk.toString("utf8");
|
|
3917
|
+
runtime.capture.appendOutput(text);
|
|
3918
|
+
appendLog(runtime.logs, "stderr", text);
|
|
3919
|
+
});
|
|
3920
|
+
}
|
|
3921
|
+
function cancelProcess(runtime, reason) {
|
|
3922
|
+
appendLog(runtime.logs, "system", reason ? `cancel requested: ${reason}` : "cancel requested");
|
|
3923
|
+
if (!runtime.child.killed) runtime.child.kill("SIGTERM");
|
|
3924
|
+
runtime.killTimer = setTimeout(() => {
|
|
3925
|
+
if (!runtime.child.killed) runtime.child.kill("SIGKILL");
|
|
3926
|
+
}, runtime.killGraceMs);
|
|
3927
|
+
}
|
|
3928
|
+
function readProcessLog(runtime, cursor) {
|
|
3929
|
+
const offset = cursor?.offset ?? 0;
|
|
3930
|
+
const nextOffset = Math.min(offset + LOG_PAGE_SIZE, runtime.logs.length);
|
|
3931
|
+
return {
|
|
3932
|
+
taskId: runtime.taskId,
|
|
3933
|
+
cursor,
|
|
3934
|
+
nextCursor: nextOffset < runtime.logs.length ? { offset: nextOffset } : void 0,
|
|
3935
|
+
lines: runtime.logs.slice(offset, nextOffset)
|
|
3936
|
+
};
|
|
3937
|
+
}
|
|
3938
|
+
|
|
3939
|
+
// src/subagents/child-process-subagent-runner.ts
|
|
3940
|
+
var import_node_child_process3 = require("child_process");
|
|
3941
|
+
var import_node_fs5 = require("fs");
|
|
3942
|
+
var import_node_path7 = require("path");
|
|
3943
|
+
var import_agent_sdk7 = require("@robota-sdk/agent-sdk");
|
|
3944
|
+
|
|
3945
|
+
// src/subagents/child-process-subagent-runner-result.ts
|
|
3946
|
+
var import_agent_sdk5 = require("@robota-sdk/agent-sdk");
|
|
3947
|
+
|
|
3948
|
+
// src/subagents/child-process-subagent-ipc.ts
|
|
3949
|
+
function isRecord(value) {
|
|
3950
|
+
return typeof value === "object" && value !== null;
|
|
3951
|
+
}
|
|
3952
|
+
function hasString(value, key) {
|
|
3953
|
+
return typeof value[key] === "string";
|
|
3954
|
+
}
|
|
3955
|
+
function isSubagentWorkerChildMessage(value) {
|
|
3956
|
+
if (!isRecord(value) || !hasString(value, "type")) return false;
|
|
3957
|
+
switch (value.type) {
|
|
3958
|
+
case "ready":
|
|
3959
|
+
return true;
|
|
3960
|
+
case "text_delta":
|
|
3961
|
+
return hasString(value, "delta");
|
|
3962
|
+
case "tool_start":
|
|
3963
|
+
return hasString(value, "toolName");
|
|
3964
|
+
case "tool_end":
|
|
3965
|
+
return hasString(value, "toolName") && typeof value.success === "boolean";
|
|
3966
|
+
case "result":
|
|
3967
|
+
return hasString(value, "output");
|
|
3968
|
+
case "error":
|
|
3969
|
+
return hasString(value, "message");
|
|
3970
|
+
case "cancelled":
|
|
3971
|
+
return value.reason === void 0 || typeof value.reason === "string";
|
|
3972
|
+
default:
|
|
3973
|
+
return false;
|
|
3974
|
+
}
|
|
3975
|
+
}
|
|
3976
|
+
|
|
3977
|
+
// src/subagents/child-process-subagent-transport.ts
|
|
3978
|
+
var import_agent_sdk4 = require("@robota-sdk/agent-sdk");
|
|
3979
|
+
function handleWorkerMessage(message, startWorker, resolveOnce, rejectOnce, emit) {
|
|
3980
|
+
switch (message.type) {
|
|
3981
|
+
case "ready":
|
|
3982
|
+
startWorker();
|
|
3983
|
+
break;
|
|
3984
|
+
case "result":
|
|
3985
|
+
resolveOnce(message.output);
|
|
3986
|
+
break;
|
|
3987
|
+
case "error":
|
|
3988
|
+
rejectOnce(new import_agent_sdk4.BackgroundTaskError("runner", message.message));
|
|
3989
|
+
break;
|
|
3990
|
+
case "cancelled":
|
|
3991
|
+
rejectOnce(new import_agent_sdk4.BackgroundTaskError("runner", message.reason ?? "Subagent worker cancelled"));
|
|
3992
|
+
break;
|
|
3993
|
+
case "text_delta":
|
|
3994
|
+
emit?.({ type: "background_task_text_delta", delta: message.delta });
|
|
3995
|
+
break;
|
|
3996
|
+
case "tool_start":
|
|
3997
|
+
emit?.({
|
|
3998
|
+
type: "background_task_tool_start",
|
|
3999
|
+
toolName: message.toolName,
|
|
4000
|
+
firstArg: extractFirstArg(message.toolArgs)
|
|
4001
|
+
});
|
|
4002
|
+
break;
|
|
4003
|
+
case "tool_end":
|
|
4004
|
+
emit?.({
|
|
4005
|
+
type: "background_task_tool_end",
|
|
4006
|
+
toolName: message.toolName,
|
|
4007
|
+
success: message.success
|
|
4008
|
+
});
|
|
4009
|
+
break;
|
|
4010
|
+
default:
|
|
4011
|
+
rejectOnce(new import_agent_sdk4.BackgroundTaskError("runner", "Unhandled subagent worker message"));
|
|
4012
|
+
}
|
|
4013
|
+
}
|
|
4014
|
+
function extractFirstArg(toolArgs) {
|
|
4015
|
+
if (!toolArgs) return void 0;
|
|
4016
|
+
const firstValue = Object.values(toolArgs)[0];
|
|
4017
|
+
if (firstValue === void 0) return void 0;
|
|
4018
|
+
return typeof firstValue === "object" ? JSON.stringify(firstValue) : String(firstValue);
|
|
4019
|
+
}
|
|
4020
|
+
function sendWorkerMessage(child, message) {
|
|
4021
|
+
return new Promise((resolve, reject) => {
|
|
4022
|
+
if (!child.connected) {
|
|
4023
|
+
reject(new import_agent_sdk4.BackgroundTaskError("crash", "Subagent worker IPC channel is closed"));
|
|
4024
|
+
return;
|
|
4025
|
+
}
|
|
4026
|
+
child.send(message, (error) => {
|
|
4027
|
+
if (error) {
|
|
4028
|
+
reject(error);
|
|
4029
|
+
return;
|
|
4030
|
+
}
|
|
4031
|
+
resolve();
|
|
4032
|
+
});
|
|
4033
|
+
});
|
|
4034
|
+
}
|
|
4035
|
+
async function cancelChildProcess(runtime, reason) {
|
|
4036
|
+
if (runtime.child.connected) {
|
|
4037
|
+
await sendWorkerMessage(runtime.child, { type: "cancel", reason }).catch(() => void 0);
|
|
4038
|
+
}
|
|
4039
|
+
runtime.killTimer = setTimeout(() => {
|
|
4040
|
+
if (!runtime.child.killed) {
|
|
4041
|
+
runtime.child.kill("SIGTERM");
|
|
4042
|
+
}
|
|
4043
|
+
}, runtime.killGraceMs);
|
|
4044
|
+
}
|
|
4045
|
+
|
|
4046
|
+
// src/subagents/child-process-subagent-runner-result.ts
|
|
4047
|
+
function createChildProcessSubagentResult(options) {
|
|
4048
|
+
return new Promise((resolve, reject) => {
|
|
4049
|
+
new ChildProcessSubagentResultController(options, resolve, reject).start();
|
|
4050
|
+
});
|
|
4051
|
+
}
|
|
4052
|
+
var ChildProcessSubagentResultController = class {
|
|
4053
|
+
constructor(options, resolve, reject) {
|
|
4054
|
+
this.options = options;
|
|
4055
|
+
this.resolve = resolve;
|
|
4056
|
+
this.reject = reject;
|
|
4057
|
+
this.timeoutTimer = createTimeoutTimer(this.options.runtime, (error) => this.rejectOnce(error));
|
|
4058
|
+
}
|
|
4059
|
+
settled = false;
|
|
4060
|
+
started = false;
|
|
4061
|
+
timeoutTimer;
|
|
4062
|
+
start() {
|
|
4063
|
+
const { child } = this.options.runtime;
|
|
4064
|
+
child.on("message", this.onMessage);
|
|
4065
|
+
child.on("error", this.onError);
|
|
4066
|
+
child.on("exit", this.onExit);
|
|
4067
|
+
child.once("spawn", () => {
|
|
4068
|
+
setImmediate(this.startWorker);
|
|
4069
|
+
});
|
|
4070
|
+
}
|
|
4071
|
+
startWorker = () => {
|
|
4072
|
+
if (this.started) return;
|
|
4073
|
+
this.started = true;
|
|
4074
|
+
const { child } = this.options.runtime;
|
|
4075
|
+
void sendWorkerMessage(child, { type: "start", payload: this.options.payload }).catch(
|
|
4076
|
+
(error) => {
|
|
4077
|
+
this.rejectOnce(error instanceof Error ? error : new Error(String(error)));
|
|
4078
|
+
}
|
|
4079
|
+
);
|
|
4080
|
+
};
|
|
4081
|
+
onMessage = (message) => {
|
|
4082
|
+
if (!isSubagentWorkerChildMessage(message)) {
|
|
4083
|
+
this.rejectOnce(
|
|
4084
|
+
new import_agent_sdk5.BackgroundTaskError("runner", "Received malformed subagent worker message")
|
|
4085
|
+
);
|
|
4086
|
+
return;
|
|
4087
|
+
}
|
|
4088
|
+
const { job } = this.options.runtime;
|
|
4089
|
+
handleWorkerMessage(message, this.startWorker, this.resolveOnce, this.rejectOnce, job.emit);
|
|
4090
|
+
};
|
|
4091
|
+
onError = (error) => {
|
|
4092
|
+
this.rejectOnce(new import_agent_sdk5.BackgroundTaskError("crash", error.message));
|
|
4093
|
+
};
|
|
4094
|
+
onExit = (code, signal) => {
|
|
4095
|
+
if (this.settled) return;
|
|
4096
|
+
this.rejectOnce(new import_agent_sdk5.BackgroundTaskError("crash", formatEarlyExitMessage(code, signal)));
|
|
4097
|
+
};
|
|
4098
|
+
resolveOnce = (output) => {
|
|
4099
|
+
if (this.settled) return;
|
|
4100
|
+
this.settled = true;
|
|
4101
|
+
this.clearTimers();
|
|
4102
|
+
this.cleanup();
|
|
4103
|
+
const { runtime, resolveTranscriptPath } = this.options;
|
|
4104
|
+
this.resolve(toSubagentResult(runtime.job, output, resolveTranscriptPath));
|
|
4105
|
+
};
|
|
4106
|
+
rejectOnce = (error) => {
|
|
4107
|
+
if (this.settled) return;
|
|
4108
|
+
this.settled = true;
|
|
4109
|
+
this.clearTimers();
|
|
4110
|
+
this.cleanup();
|
|
4111
|
+
this.reject(error);
|
|
4112
|
+
};
|
|
4113
|
+
clearTimers() {
|
|
4114
|
+
if (this.timeoutTimer) clearTimeout(this.timeoutTimer);
|
|
4115
|
+
if (this.options.runtime.killTimer) clearTimeout(this.options.runtime.killTimer);
|
|
4116
|
+
}
|
|
4117
|
+
cleanup() {
|
|
4118
|
+
const { child } = this.options.runtime;
|
|
4119
|
+
child.off("message", this.onMessage);
|
|
4120
|
+
child.off("error", this.onError);
|
|
4121
|
+
child.off("exit", this.onExit);
|
|
4122
|
+
}
|
|
4123
|
+
};
|
|
4124
|
+
function createCancellationResult(jobId) {
|
|
4125
|
+
let settled = false;
|
|
4126
|
+
let rejectFn = () => {
|
|
4127
|
+
};
|
|
4128
|
+
const promise = new Promise((_resolve, reject) => {
|
|
4129
|
+
rejectFn = reject;
|
|
4130
|
+
});
|
|
4131
|
+
return {
|
|
4132
|
+
promise,
|
|
4133
|
+
reject(reason) {
|
|
4134
|
+
if (settled) return;
|
|
4135
|
+
settled = true;
|
|
4136
|
+
rejectFn(new import_agent_sdk5.BackgroundTaskError("runner", reason ?? `Subagent job cancelled: ${jobId}`));
|
|
4137
|
+
}
|
|
4138
|
+
};
|
|
4139
|
+
}
|
|
4140
|
+
function createTimeoutTimer(runtime, rejectOnce) {
|
|
4141
|
+
if (!runtime.job.request.timeoutMs) return void 0;
|
|
4142
|
+
return setTimeout(() => {
|
|
4143
|
+
void cancelChildProcess(runtime, "Subagent worker timed out");
|
|
4144
|
+
rejectOnce(new import_agent_sdk5.BackgroundTaskError("timeout", "Subagent worker timed out"));
|
|
4145
|
+
}, runtime.job.request.timeoutMs);
|
|
4146
|
+
}
|
|
4147
|
+
function toSubagentResult(job, output, resolveTranscriptPath) {
|
|
4148
|
+
const transcriptPath = resolveTranscriptPath(job);
|
|
4149
|
+
return {
|
|
4150
|
+
jobId: job.jobId,
|
|
4151
|
+
output,
|
|
4152
|
+
...transcriptPath ? { metadata: { transcriptPath, logPath: transcriptPath } } : {}
|
|
4153
|
+
};
|
|
4154
|
+
}
|
|
4155
|
+
function formatEarlyExitMessage(code, signal) {
|
|
4156
|
+
const detail = signal !== null ? `signal ${signal}` : `exit code ${code === null ? "unknown" : code}`;
|
|
4157
|
+
return `Subagent worker exited before result: ${detail}`;
|
|
4158
|
+
}
|
|
4159
|
+
|
|
4160
|
+
// src/subagents/git-worktree-isolation-adapter.ts
|
|
4161
|
+
var import_node_child_process2 = require("child_process");
|
|
4162
|
+
var import_node_crypto2 = require("crypto");
|
|
4163
|
+
var import_node_fs4 = require("fs");
|
|
4164
|
+
var import_node_path6 = require("path");
|
|
4165
|
+
var import_agent_sdk6 = require("@robota-sdk/agent-sdk");
|
|
4166
|
+
var WORKTREE_DIR = ".robota/worktrees";
|
|
4167
|
+
var BRANCH_PREFIX = "robota";
|
|
4168
|
+
var GIT_ENCODING = "utf8";
|
|
4169
|
+
var SHORT_ID_LENGTH = 8;
|
|
4170
|
+
function createGitWorktreeIsolationAdapter(options) {
|
|
4171
|
+
return new GitWorktreeIsolationAdapter(options);
|
|
4172
|
+
}
|
|
4173
|
+
var GitWorktreeIsolationAdapter = class {
|
|
4174
|
+
worktreeDir;
|
|
4175
|
+
branchPrefix;
|
|
4176
|
+
constructor(options = {}) {
|
|
4177
|
+
this.worktreeDir = options.worktreeDir ?? WORKTREE_DIR;
|
|
4178
|
+
this.branchPrefix = options.branchPrefix ?? BRANCH_PREFIX;
|
|
4179
|
+
}
|
|
4180
|
+
prepare(request) {
|
|
4181
|
+
const repoRoot = runGit(request.cwd, ["rev-parse", "--show-toplevel"]).trim();
|
|
4182
|
+
const shortId = (0, import_node_crypto2.randomUUID)().slice(0, SHORT_ID_LENGTH);
|
|
4183
|
+
const branchName = `${this.branchPrefix}/${request.jobId}-${shortId}`;
|
|
4184
|
+
const worktreeRoot = (0, import_node_path6.join)(repoRoot, this.worktreeDir);
|
|
4185
|
+
const worktreePath = (0, import_node_path6.join)(worktreeRoot, `${request.jobId}-${shortId}`);
|
|
4186
|
+
(0, import_node_fs4.mkdirSync)(worktreeRoot, { recursive: true });
|
|
4187
|
+
runGit(repoRoot, ["worktree", "add", "-b", branchName, worktreePath, "HEAD"]);
|
|
4188
|
+
return { repoRoot, worktreePath, branchName };
|
|
4189
|
+
}
|
|
4190
|
+
isClean(worktree) {
|
|
4191
|
+
return runGit(worktree.worktreePath, ["status", "--porcelain"]).trim().length === 0;
|
|
4192
|
+
}
|
|
4193
|
+
remove(worktree) {
|
|
4194
|
+
runGit(worktree.repoRoot, ["worktree", "remove", "--force", worktree.worktreePath]);
|
|
4195
|
+
runGit(worktree.repoRoot, ["branch", "-D", worktree.branchName]);
|
|
4196
|
+
}
|
|
4197
|
+
};
|
|
4198
|
+
function runGit(cwd, args) {
|
|
2457
4199
|
try {
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
4200
|
+
return (0, import_node_child_process2.execFileSync)("git", args, {
|
|
4201
|
+
cwd,
|
|
4202
|
+
encoding: GIT_ENCODING,
|
|
4203
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
4204
|
+
});
|
|
4205
|
+
} catch (error) {
|
|
4206
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4207
|
+
throw new import_agent_sdk6.BackgroundTaskError("runner", `git ${args.join(" ")} failed: ${message}`);
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
|
|
4211
|
+
// src/subagents/child-process-subagent-runner.ts
|
|
4212
|
+
var DEFAULT_KILL_GRACE_MS2 = 2e3;
|
|
4213
|
+
var LOG_PAGE_SIZE2 = 200;
|
|
4214
|
+
function createChildProcessSubagentRunnerFactory(options = {}) {
|
|
4215
|
+
return (deps) => {
|
|
4216
|
+
const runner = new ChildProcessSubagentRunner(deps, options);
|
|
4217
|
+
if (options.worktreeIsolation === false) return runner;
|
|
4218
|
+
return (0, import_agent_sdk7.createWorktreeSubagentRunner)({
|
|
4219
|
+
runner,
|
|
4220
|
+
worktreeAdapter: options.worktreeAdapter ?? createGitWorktreeIsolationAdapter(),
|
|
4221
|
+
hooks: deps.config.hooks,
|
|
4222
|
+
hookTypeExecutors: deps.hookTypeExecutors
|
|
4223
|
+
});
|
|
4224
|
+
};
|
|
4225
|
+
}
|
|
4226
|
+
var ChildProcessSubagentRunner = class {
|
|
4227
|
+
constructor(deps, options = {}) {
|
|
4228
|
+
this.deps = deps;
|
|
4229
|
+
this.workerPath = options.workerPath ?? resolveDefaultWorkerPath();
|
|
4230
|
+
this.execArgv = options.execArgv;
|
|
4231
|
+
this.killGraceMs = options.killGraceMs ?? DEFAULT_KILL_GRACE_MS2;
|
|
4232
|
+
this.providerConfig = options.providerConfig;
|
|
4233
|
+
this.env = options.env;
|
|
4234
|
+
this.logsDir = options.logsDir;
|
|
4235
|
+
}
|
|
4236
|
+
workerPath;
|
|
4237
|
+
execArgv;
|
|
4238
|
+
killGraceMs;
|
|
4239
|
+
providerConfig;
|
|
4240
|
+
env;
|
|
4241
|
+
logsDir;
|
|
4242
|
+
start(job) {
|
|
4243
|
+
const child = (0, import_node_child_process3.fork)(this.workerPath, [], {
|
|
4244
|
+
cwd: job.request.cwd,
|
|
4245
|
+
env: { ...process.env, ...this.env ?? {} },
|
|
4246
|
+
execArgv: this.execArgv ?? resolveDefaultExecArgv(this.workerPath),
|
|
4247
|
+
stdio: ["ignore", "ignore", "ignore", "ipc"]
|
|
4248
|
+
});
|
|
4249
|
+
const runtime = {
|
|
4250
|
+
job,
|
|
4251
|
+
child,
|
|
4252
|
+
killGraceMs: this.killGraceMs
|
|
4253
|
+
};
|
|
4254
|
+
const payload = this.createStartPayload(job);
|
|
4255
|
+
const workerResult = createChildProcessSubagentResult({
|
|
4256
|
+
runtime,
|
|
4257
|
+
payload,
|
|
4258
|
+
resolveTranscriptPath: (request) => this.resolveTranscriptPath(request)
|
|
4259
|
+
});
|
|
4260
|
+
const cancellation = createCancellationResult(job.jobId);
|
|
4261
|
+
void workerResult.catch(() => void 0);
|
|
4262
|
+
const result = Promise.race([workerResult, cancellation.promise]);
|
|
4263
|
+
const transcriptPath = this.resolveTranscriptPath(job);
|
|
4264
|
+
return {
|
|
4265
|
+
jobId: job.jobId,
|
|
4266
|
+
...child.pid !== void 0 && { pid: child.pid },
|
|
4267
|
+
...transcriptPath !== void 0 && { transcriptPath, logPath: transcriptPath },
|
|
4268
|
+
result,
|
|
4269
|
+
cancel: async (reason) => {
|
|
4270
|
+
cancellation.reject(reason);
|
|
4271
|
+
await cancelChildProcess(runtime, reason);
|
|
4272
|
+
},
|
|
4273
|
+
send: async (prompt) => {
|
|
4274
|
+
await sendWorkerMessage(child, { type: "send", prompt });
|
|
4275
|
+
},
|
|
4276
|
+
...transcriptPath !== void 0 && {
|
|
4277
|
+
readLog: async (cursor) => readTranscriptLog(job.jobId, transcriptPath, cursor)
|
|
4278
|
+
}
|
|
4279
|
+
};
|
|
4280
|
+
}
|
|
4281
|
+
createStartPayload(job) {
|
|
4282
|
+
const definition = resolveAgentDefinition(job.request.type, this.deps.customAgentRegistry);
|
|
4283
|
+
return {
|
|
4284
|
+
jobId: job.jobId,
|
|
4285
|
+
request: job.request,
|
|
4286
|
+
agentDefinition: applyRequestOverrides(definition, job),
|
|
4287
|
+
parentConfig: this.deps.config,
|
|
4288
|
+
parentContext: this.deps.context,
|
|
4289
|
+
providerProfile: createProviderProfile(this.providerConfig, this.deps, job),
|
|
4290
|
+
permissionMode: this.deps.permissionMode,
|
|
4291
|
+
...this.logsDir ? { logsDir: this.logsDir } : {}
|
|
4292
|
+
};
|
|
4293
|
+
}
|
|
4294
|
+
resolveTranscriptPath(job) {
|
|
4295
|
+
if (!this.logsDir) return void 0;
|
|
4296
|
+
return (0, import_node_path7.join)(this.logsDir, job.request.parentSessionId, "subagents", `${job.jobId}.jsonl`);
|
|
4297
|
+
}
|
|
4298
|
+
};
|
|
4299
|
+
function resolveAgentDefinition(agentType, customRegistry) {
|
|
4300
|
+
const definition = customRegistry?.(agentType) ?? (0, import_agent_sdk7.getBuiltInAgent)(agentType);
|
|
4301
|
+
if (!definition) {
|
|
4302
|
+
throw new import_agent_sdk7.BackgroundTaskError("validation", `Unknown agent type: ${agentType}`);
|
|
4303
|
+
}
|
|
4304
|
+
return definition;
|
|
4305
|
+
}
|
|
4306
|
+
function applyRequestOverrides(definition, job) {
|
|
4307
|
+
return {
|
|
4308
|
+
...definition,
|
|
4309
|
+
...job.request.model ? { model: job.request.model } : {},
|
|
4310
|
+
...job.request.allowedTools ? { tools: job.request.allowedTools } : {},
|
|
4311
|
+
...job.request.disallowedTools ? { disallowedTools: job.request.disallowedTools } : {}
|
|
4312
|
+
};
|
|
4313
|
+
}
|
|
4314
|
+
function createProviderProfile(providerConfig, deps, job) {
|
|
4315
|
+
const provider = providerConfig ?? deps.config.provider;
|
|
4316
|
+
return {
|
|
4317
|
+
profileName: deps.config.currentProvider,
|
|
4318
|
+
type: provider.name,
|
|
4319
|
+
model: job.request.model ?? provider.model,
|
|
4320
|
+
apiKey: provider.apiKey,
|
|
4321
|
+
baseURL: provider.baseURL,
|
|
4322
|
+
timeout: provider.timeout
|
|
4323
|
+
};
|
|
4324
|
+
}
|
|
4325
|
+
function resolveDefaultWorkerPath() {
|
|
4326
|
+
const entryPoint = process.argv[1] ?? "";
|
|
4327
|
+
const entryDir = entryPoint ? (0, import_node_path7.dirname)(entryPoint) : process.cwd();
|
|
4328
|
+
const extension = entryPoint.endsWith(".ts") || entryPoint.endsWith(".tsx") ? ".ts" : ".js";
|
|
4329
|
+
const candidates = [
|
|
4330
|
+
(0, import_node_path7.join)(entryDir, "subagents", `child-process-subagent-worker${extension}`),
|
|
4331
|
+
(0, import_node_path7.join)(entryDir, `child-process-subagent-worker${extension}`)
|
|
4332
|
+
];
|
|
4333
|
+
for (const candidate of candidates) {
|
|
4334
|
+
if ((0, import_node_fs5.existsSync)(candidate)) {
|
|
4335
|
+
return candidate;
|
|
4336
|
+
}
|
|
4337
|
+
}
|
|
4338
|
+
return candidates[0];
|
|
4339
|
+
}
|
|
4340
|
+
function resolveDefaultExecArgv(workerPath) {
|
|
4341
|
+
if (!workerPath.endsWith(".ts")) {
|
|
4342
|
+
return process.execArgv;
|
|
4343
|
+
}
|
|
4344
|
+
if (process.execArgv.some((arg) => arg.includes("tsx"))) {
|
|
4345
|
+
return process.execArgv;
|
|
4346
|
+
}
|
|
4347
|
+
return [...process.execArgv, "--import", "tsx"];
|
|
4348
|
+
}
|
|
4349
|
+
function readTranscriptLog(jobId, transcriptPath, cursor) {
|
|
4350
|
+
const offset = cursor?.offset ?? 0;
|
|
4351
|
+
if (!(0, import_node_fs5.existsSync)(transcriptPath)) {
|
|
4352
|
+
return {
|
|
4353
|
+
taskId: jobId,
|
|
4354
|
+
cursor,
|
|
4355
|
+
lines: []
|
|
4356
|
+
};
|
|
2466
4357
|
}
|
|
4358
|
+
const lines = (0, import_node_fs5.readFileSync)(transcriptPath, "utf8").split(/\r?\n/).filter(Boolean);
|
|
4359
|
+
const nextOffset = Math.min(offset + LOG_PAGE_SIZE2, lines.length);
|
|
4360
|
+
return {
|
|
4361
|
+
taskId: jobId,
|
|
4362
|
+
cursor,
|
|
4363
|
+
nextCursor: nextOffset < lines.length ? { offset: nextOffset } : void 0,
|
|
4364
|
+
lines: lines.slice(offset, nextOffset)
|
|
4365
|
+
};
|
|
2467
4366
|
}
|
|
4367
|
+
|
|
4368
|
+
// src/cli.ts
|
|
4369
|
+
var import_meta = {};
|
|
2468
4370
|
function readVersion() {
|
|
2469
4371
|
try {
|
|
2470
4372
|
const thisFile = (0, import_node_url.fileURLToPath)(import_meta.url);
|
|
2471
|
-
const dir = (0,
|
|
2472
|
-
const candidates = [(0,
|
|
4373
|
+
const dir = (0, import_node_path8.dirname)(thisFile);
|
|
4374
|
+
const candidates = [(0, import_node_path8.join)(dir, "..", "..", "package.json"), (0, import_node_path8.join)(dir, "..", "package.json")];
|
|
2473
4375
|
for (const pkgPath of candidates) {
|
|
2474
4376
|
try {
|
|
2475
|
-
const raw = (0,
|
|
4377
|
+
const raw = (0, import_node_fs6.readFileSync)(pkgPath, "utf-8");
|
|
2476
4378
|
const pkg = JSON.parse(raw);
|
|
2477
4379
|
if (pkg.version !== void 0 && pkg.name !== void 0) {
|
|
2478
4380
|
return pkg.version;
|
|
@@ -2520,63 +4422,6 @@ function promptInput(label, masked = false) {
|
|
|
2520
4422
|
stdin.on("data", onData);
|
|
2521
4423
|
});
|
|
2522
4424
|
}
|
|
2523
|
-
async function ensureConfig(cwd) {
|
|
2524
|
-
const userPath = getUserSettingsPath();
|
|
2525
|
-
const projectPath = (0, import_node_path5.join)(cwd, ".robota", "settings.json");
|
|
2526
|
-
const localPath = (0, import_node_path5.join)(cwd, ".robota", "settings.local.json");
|
|
2527
|
-
const paths = [userPath, projectPath, localPath];
|
|
2528
|
-
const checks = paths.map((p) => ({ path: p, status: checkSettingsFile(p) }));
|
|
2529
|
-
if (checks.some((c) => c.status === "valid")) {
|
|
2530
|
-
return;
|
|
2531
|
-
}
|
|
2532
|
-
const corrupt = checks.filter((c) => c.status === "corrupt");
|
|
2533
|
-
const incomplete = checks.filter((c) => c.status === "incomplete");
|
|
2534
|
-
process.stdout.write("\n");
|
|
2535
|
-
if (corrupt.length > 0) {
|
|
2536
|
-
for (const c of corrupt) {
|
|
2537
|
-
process.stderr.write(` ERROR: Settings file is corrupt (invalid JSON): ${c.path}
|
|
2538
|
-
`);
|
|
2539
|
-
}
|
|
2540
|
-
process.stdout.write("\n");
|
|
2541
|
-
}
|
|
2542
|
-
if (incomplete.length > 0) {
|
|
2543
|
-
for (const c of incomplete) {
|
|
2544
|
-
process.stderr.write(` WARNING: Settings file is missing provider.apiKey: ${c.path}
|
|
2545
|
-
`);
|
|
2546
|
-
}
|
|
2547
|
-
process.stdout.write("\n");
|
|
2548
|
-
}
|
|
2549
|
-
if (corrupt.length === 0 && incomplete.length === 0) {
|
|
2550
|
-
process.stdout.write(" Welcome to Robota CLI!\n");
|
|
2551
|
-
process.stdout.write(" No configuration found. Let's set up.\n");
|
|
2552
|
-
} else {
|
|
2553
|
-
process.stdout.write(" Reconfiguring...\n");
|
|
2554
|
-
}
|
|
2555
|
-
process.stdout.write("\n");
|
|
2556
|
-
const apiKey = await promptInput(" Anthropic API key: ", true);
|
|
2557
|
-
if (!apiKey) {
|
|
2558
|
-
process.stderr.write("\n No API key provided. Exiting.\n");
|
|
2559
|
-
process.exit(1);
|
|
2560
|
-
}
|
|
2561
|
-
const language = await promptInput(" Response language (ko/en/ja/zh, default: en): ");
|
|
2562
|
-
const settingsDir = (0, import_node_path5.dirname)(userPath);
|
|
2563
|
-
(0, import_node_fs3.mkdirSync)(settingsDir, { recursive: true });
|
|
2564
|
-
const settings = {
|
|
2565
|
-
provider: {
|
|
2566
|
-
name: "anthropic",
|
|
2567
|
-
model: "claude-sonnet-4-6",
|
|
2568
|
-
apiKey
|
|
2569
|
-
}
|
|
2570
|
-
};
|
|
2571
|
-
if (language) {
|
|
2572
|
-
settings.language = language;
|
|
2573
|
-
}
|
|
2574
|
-
(0, import_node_fs3.writeFileSync)(userPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
2575
|
-
process.stdout.write(`
|
|
2576
|
-
Config saved to ${userPath}
|
|
2577
|
-
|
|
2578
|
-
`);
|
|
2579
|
-
}
|
|
2580
4425
|
function resetConfig() {
|
|
2581
4426
|
const userPath = getUserSettingsPath();
|
|
2582
4427
|
if (deleteSettings(userPath)) {
|
|
@@ -2586,7 +4431,7 @@ function resetConfig() {
|
|
|
2586
4431
|
process.stdout.write("No user settings found.\n");
|
|
2587
4432
|
}
|
|
2588
4433
|
}
|
|
2589
|
-
async function startCli() {
|
|
4434
|
+
async function startCli(options = {}) {
|
|
2590
4435
|
const args = parseCliArgs();
|
|
2591
4436
|
if (args.version) {
|
|
2592
4437
|
process.stdout.write(`robota ${readVersion()}
|
|
@@ -2598,11 +4443,32 @@ async function startCli() {
|
|
|
2598
4443
|
return;
|
|
2599
4444
|
}
|
|
2600
4445
|
const cwd = process.cwd();
|
|
2601
|
-
|
|
2602
|
-
|
|
4446
|
+
const providerDefinitions = options.providerDefinitions ?? DEFAULT_PROVIDER_DEFINITIONS;
|
|
4447
|
+
if (args.configure) {
|
|
4448
|
+
await runInteractiveProviderSetup(cwd, args, promptInput, providerDefinitions);
|
|
4449
|
+
return;
|
|
4450
|
+
}
|
|
4451
|
+
if (handleProviderConfigurationArgs(cwd, args, providerDefinitions)) {
|
|
4452
|
+
return;
|
|
4453
|
+
}
|
|
4454
|
+
try {
|
|
4455
|
+
await ensureConfig(cwd, args, promptInput, providerDefinitions);
|
|
4456
|
+
} catch (error) {
|
|
4457
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
4458
|
+
`);
|
|
4459
|
+
process.exit(1);
|
|
4460
|
+
}
|
|
4461
|
+
const providerOptions = args.provider ? { providerOverride: args.provider, providerDefinitions } : { providerDefinitions };
|
|
4462
|
+
const providerSettings = readProviderSettings(cwd, providerOptions);
|
|
2603
4463
|
const modelId = args.model ?? providerSettings.model;
|
|
2604
|
-
const provider = createProviderFromSettings(cwd, args.model);
|
|
2605
|
-
const
|
|
4464
|
+
const provider = createProviderFromSettings(cwd, args.model, providerOptions);
|
|
4465
|
+
const backgroundTaskRunners = [createManagedShellProcessRunner()];
|
|
4466
|
+
const paths = (0, import_agent_sdk8.projectPaths)(cwd);
|
|
4467
|
+
const subagentRunnerFactory = createChildProcessSubagentRunnerFactory({
|
|
4468
|
+
providerConfig: { ...providerSettings, model: modelId },
|
|
4469
|
+
logsDir: paths.logs
|
|
4470
|
+
});
|
|
4471
|
+
const sessionStore = new import_agent_sessions.SessionStore(paths.sessions);
|
|
2606
4472
|
let resumeSessionId;
|
|
2607
4473
|
if (args.continueMode) {
|
|
2608
4474
|
const sessions = sessionStore.list().filter((s) => s.cwd === cwd);
|
|
@@ -2637,13 +4503,27 @@ async function startCli() {
|
|
|
2637
4503
|
process.stderr.write("Print mode (-p) requires a prompt argument.\n");
|
|
2638
4504
|
process.exit(1);
|
|
2639
4505
|
}
|
|
2640
|
-
const
|
|
4506
|
+
const appendParts = [];
|
|
4507
|
+
if (args.appendSystemPrompt) appendParts.push(args.appendSystemPrompt);
|
|
4508
|
+
if (args.jsonSchema)
|
|
4509
|
+
appendParts.push(
|
|
4510
|
+
`Respond with valid JSON only, matching this JSON schema:
|
|
4511
|
+
${args.jsonSchema}`
|
|
4512
|
+
);
|
|
4513
|
+
const appendSystemPrompt = appendParts.length > 0 ? appendParts.join("\n\n") : void 0;
|
|
4514
|
+
const session = new import_agent_sdk8.InteractiveSession({
|
|
2641
4515
|
cwd,
|
|
2642
4516
|
provider,
|
|
2643
4517
|
permissionMode: args.permissionMode ?? "bypassPermissions",
|
|
2644
4518
|
maxTurns: args.maxTurns,
|
|
2645
|
-
sessionStore,
|
|
2646
|
-
sessionName: args.sessionName
|
|
4519
|
+
sessionStore: args.noSessionPersistence ? void 0 : sessionStore,
|
|
4520
|
+
sessionName: args.sessionName,
|
|
4521
|
+
bare: args.bare || void 0,
|
|
4522
|
+
allowedTools: args.allowedTools ? args.allowedTools.split(",").map((t) => t.trim()).filter((t) => t.length > 0) : void 0,
|
|
4523
|
+
appendSystemPrompt,
|
|
4524
|
+
backgroundTaskRunners,
|
|
4525
|
+
subagentRunnerFactory,
|
|
4526
|
+
commandModules: options.commandModules
|
|
2647
4527
|
});
|
|
2648
4528
|
const transport = (0, import_agent_transport_headless.createHeadlessTransport)({
|
|
2649
4529
|
outputFormat: args.outputFormat ?? "text",
|
|
@@ -2651,6 +4531,7 @@ async function startCli() {
|
|
|
2651
4531
|
});
|
|
2652
4532
|
session.attachTransport(transport);
|
|
2653
4533
|
await transport.start();
|
|
4534
|
+
await session.shutdown({ reason: "prompt_input_exit", message: "Headless transport complete" });
|
|
2654
4535
|
process.exit(transport.getExitCode());
|
|
2655
4536
|
}
|
|
2656
4537
|
renderApp({
|
|
@@ -2664,10 +4545,19 @@ async function startCli() {
|
|
|
2664
4545
|
sessionStore,
|
|
2665
4546
|
resumeSessionId,
|
|
2666
4547
|
forkSession: args.forkSession,
|
|
2667
|
-
sessionName: args.sessionName
|
|
4548
|
+
sessionName: args.sessionName,
|
|
4549
|
+
backgroundTaskRunners,
|
|
4550
|
+
subagentRunnerFactory,
|
|
4551
|
+
commandModules: options.commandModules,
|
|
4552
|
+
providerDefinitions
|
|
2668
4553
|
});
|
|
2669
4554
|
}
|
|
2670
4555
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2671
4556
|
0 && (module.exports = {
|
|
4557
|
+
ChildProcessSubagentRunner,
|
|
4558
|
+
GitWorktreeIsolationAdapter,
|
|
4559
|
+
createChildProcessSubagentRunnerFactory,
|
|
4560
|
+
createGitWorktreeIsolationAdapter,
|
|
4561
|
+
createManagedShellProcessRunner,
|
|
2672
4562
|
startCli
|
|
2673
4563
|
});
|