@standardagents/code 0.11.6 → 0.11.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +421 -234
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -135,6 +135,36 @@ function parseSharedMessagingSnapshot(value) {
|
|
|
135
135
|
}
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
|
+
function parseComposerDraft(value) {
|
|
139
|
+
const draft = record(value);
|
|
140
|
+
if (!draft || draft.version !== 1 || typeof draft.content !== "string") {
|
|
141
|
+
throw new TypeError("unsupported or malformed shared draft");
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
version: 1,
|
|
145
|
+
revision: finiteNumber(draft.revision, "draft revision"),
|
|
146
|
+
content: draft.content,
|
|
147
|
+
attachments: attachments(draft.attachments),
|
|
148
|
+
updatedAt: finiteNumber(draft.updatedAt, "draft updatedAt"),
|
|
149
|
+
...origin(draft)
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function parseMessagingChangedEvent(value) {
|
|
153
|
+
const event = record(value);
|
|
154
|
+
if (!event || event.version !== 1) return null;
|
|
155
|
+
try {
|
|
156
|
+
return {
|
|
157
|
+
version: 1,
|
|
158
|
+
pendingRevision: finiteNumber(event.pendingRevision, "event pending revision"),
|
|
159
|
+
draftRevision: finiteNumber(event.draftRevision, "event draft revision"),
|
|
160
|
+
...event.draft !== void 0 ? { draft: parseComposerDraft(event.draft) } : {},
|
|
161
|
+
...event.draftOmitted === true ? { draftOmitted: true } : {},
|
|
162
|
+
...origin(event)
|
|
163
|
+
};
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
138
168
|
|
|
139
169
|
// src/api.ts
|
|
140
170
|
var ApiHttpError = class extends Error {
|
|
@@ -2690,7 +2720,7 @@ var McpManager = class {
|
|
|
2690
2720
|
}
|
|
2691
2721
|
}
|
|
2692
2722
|
closeAll() {
|
|
2693
|
-
for (const [,
|
|
2723
|
+
for (const [, c6] of this.clients) c6.close();
|
|
2694
2724
|
this.clients.clear();
|
|
2695
2725
|
}
|
|
2696
2726
|
get(name) {
|
|
@@ -2701,13 +2731,13 @@ var McpManager = class {
|
|
|
2701
2731
|
}
|
|
2702
2732
|
toolCount() {
|
|
2703
2733
|
let n = 0;
|
|
2704
|
-
for (const [,
|
|
2734
|
+
for (const [, c6] of this.clients) n += c6.tools.length;
|
|
2705
2735
|
return n;
|
|
2706
2736
|
}
|
|
2707
2737
|
/** A JSON-serializable catalog of every connected server for the KV/context. */
|
|
2708
2738
|
catalog() {
|
|
2709
2739
|
return {
|
|
2710
|
-
servers: Array.from(this.clients.values()).map((
|
|
2740
|
+
servers: Array.from(this.clients.values()).map((c6) => c6.catalogEntry()),
|
|
2711
2741
|
generatedAt: Date.now()
|
|
2712
2742
|
};
|
|
2713
2743
|
}
|
|
@@ -2797,9 +2827,9 @@ function flattenContent(content, structured) {
|
|
|
2797
2827
|
}
|
|
2798
2828
|
function flattenResourceContents(contents) {
|
|
2799
2829
|
const parts = [];
|
|
2800
|
-
for (const
|
|
2801
|
-
if (typeof
|
|
2802
|
-
else if (typeof
|
|
2830
|
+
for (const c6 of contents || []) {
|
|
2831
|
+
if (typeof c6.text === "string") parts.push(c6.text);
|
|
2832
|
+
else if (typeof c6.blob === "string") parts.push(`[binary resource ${String(c6.uri ?? "")} (${c6.blob.length} b64 chars)]`);
|
|
2803
2833
|
}
|
|
2804
2834
|
return parts.join("\n").trim();
|
|
2805
2835
|
}
|
|
@@ -3849,17 +3879,17 @@ function wrapBlock(out, cols2, leadFirst, leadRest, leadWidth, text) {
|
|
|
3849
3879
|
function renderTable(rows) {
|
|
3850
3880
|
const cols2 = Math.max(...rows.map((r) => r.length));
|
|
3851
3881
|
const widths = [];
|
|
3852
|
-
for (let
|
|
3853
|
-
widths[
|
|
3882
|
+
for (let c6 = 0; c6 < cols2; c6++) {
|
|
3883
|
+
widths[c6] = Math.max(...rows.map((r) => visibleWidth(inline(r[c6] ?? ""))));
|
|
3854
3884
|
}
|
|
3855
3885
|
const sep = `${GRAY} \u2502 ${R}`;
|
|
3856
3886
|
const out = [];
|
|
3857
3887
|
rows.forEach((r, ri) => {
|
|
3858
3888
|
const cells = [];
|
|
3859
|
-
for (let
|
|
3860
|
-
const raw = r[
|
|
3889
|
+
for (let c6 = 0; c6 < cols2; c6++) {
|
|
3890
|
+
const raw = r[c6] ?? "";
|
|
3861
3891
|
const styled = ri === 0 ? `${BOLD}${inline(raw)}${R}` : inline(raw);
|
|
3862
|
-
cells.push(padEndVisible(styled, widths[
|
|
3892
|
+
cells.push(padEndVisible(styled, widths[c6]));
|
|
3863
3893
|
}
|
|
3864
3894
|
out.push((" " + cells.join(sep)).replace(/\s+$/, ""));
|
|
3865
3895
|
if (ri === 0) {
|
|
@@ -4331,7 +4361,7 @@ function buildInputBoxRows(opts) {
|
|
|
4331
4361
|
let di = 0;
|
|
4332
4362
|
for (let ci = 0; ci < plainChars.length; ci++) {
|
|
4333
4363
|
const ch = plainChars[ci];
|
|
4334
|
-
const
|
|
4364
|
+
const c6 = colorAt(perimeterIndex("top", xi++, W, bodyH));
|
|
4335
4365
|
if (ci >= agentSpanStart && ci < agentSpanEnd) {
|
|
4336
4366
|
top += `\x1B[2m${themeGray}${ch}\x1B[0m`;
|
|
4337
4367
|
continue;
|
|
@@ -4340,9 +4370,9 @@ function buildInputBoxRows(opts) {
|
|
|
4340
4370
|
const levelN = Math.max(1, Math.min(5, level));
|
|
4341
4371
|
const filled = di < levelN;
|
|
4342
4372
|
di++;
|
|
4343
|
-
top += (filled ? levelColor ||
|
|
4373
|
+
top += (filled ? levelColor || c6 : "\x1B[38;5;240m") + (filled ? "\u25CF" : "\u25CB") + reset;
|
|
4344
4374
|
} else {
|
|
4345
|
-
top +=
|
|
4375
|
+
top += c6 + ch + reset;
|
|
4346
4376
|
}
|
|
4347
4377
|
}
|
|
4348
4378
|
}
|
|
@@ -4363,10 +4393,10 @@ function buildInputBoxRows(opts) {
|
|
|
4363
4393
|
let bottom = "";
|
|
4364
4394
|
for (let x = 0; x < W; x++) {
|
|
4365
4395
|
const idx = perimeterIndex("bottom", W - 1 - x, W, bodyH);
|
|
4366
|
-
const
|
|
4367
|
-
if (x === 0) bottom +=
|
|
4368
|
-
else if (x === W - 1) bottom +=
|
|
4369
|
-
else bottom +=
|
|
4396
|
+
const c6 = colorAt(idx);
|
|
4397
|
+
if (x === 0) bottom += c6 + "\u2570" + reset;
|
|
4398
|
+
else if (x === W - 1) bottom += c6 + "\u256F" + reset;
|
|
4399
|
+
else bottom += c6 + "\u2500" + reset;
|
|
4370
4400
|
}
|
|
4371
4401
|
return [pad + top, ...bodyRows, pad + bottom];
|
|
4372
4402
|
}
|
|
@@ -5101,7 +5131,7 @@ var Tui = class _Tui {
|
|
|
5101
5131
|
const q = this.inputBuffer.slice(1).trim().toLowerCase();
|
|
5102
5132
|
if (q === "") return this.commands;
|
|
5103
5133
|
return this.commands.filter(
|
|
5104
|
-
(
|
|
5134
|
+
(c6) => c6.name.startsWith(q) || c6.name.includes(q) || c6.label.toLowerCase().includes(q)
|
|
5105
5135
|
);
|
|
5106
5136
|
}
|
|
5107
5137
|
runCommand(cmd) {
|
|
@@ -6460,7 +6490,7 @@ function readVersion() {
|
|
|
6460
6490
|
if (typeof pkg.version === "string" && pkg.version) return pkg.version;
|
|
6461
6491
|
} catch {
|
|
6462
6492
|
}
|
|
6463
|
-
return "0.11.
|
|
6493
|
+
return "0.11.7" ;
|
|
6464
6494
|
}
|
|
6465
6495
|
function isLocalHost(host) {
|
|
6466
6496
|
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host.endsWith(".local") || host.endsWith(".localhost") || /^10\./.test(host) || /^192\.168\./.test(host) || /^172\.(1[6-9]|2\d|3[01])\./.test(host);
|
|
@@ -6819,7 +6849,7 @@ async function clearDaemon(api, identity) {
|
|
|
6819
6849
|
function parseCommands(value) {
|
|
6820
6850
|
if (!Array.isArray(value)) return [];
|
|
6821
6851
|
return value.filter(
|
|
6822
|
-
(
|
|
6852
|
+
(c6) => !!c6 && typeof c6 === "object" && typeof c6.id === "string" && typeof c6.kind === "string"
|
|
6823
6853
|
);
|
|
6824
6854
|
}
|
|
6825
6855
|
async function enqueueMachineCommand(api, machineId, kind, args) {
|
|
@@ -6842,7 +6872,7 @@ async function clearMachineCommands(api, machineId, appliedIds) {
|
|
|
6842
6872
|
if (appliedIds.length === 0) return;
|
|
6843
6873
|
const key = commandKey(machineId);
|
|
6844
6874
|
const remaining = parseCommands(await api.userKvGet(key)).filter(
|
|
6845
|
-
(
|
|
6875
|
+
(c6) => !appliedIds.includes(c6.id)
|
|
6846
6876
|
);
|
|
6847
6877
|
await api.userKvSet(key, remaining.length ? remaining : null);
|
|
6848
6878
|
}
|
|
@@ -7228,7 +7258,7 @@ var HubSocket = class {
|
|
|
7228
7258
|
}
|
|
7229
7259
|
openSocket() {
|
|
7230
7260
|
if (this.closed) return;
|
|
7231
|
-
let url = `${this.api.wsEndpoint}/api/users/me/
|
|
7261
|
+
let url = `${this.api.wsEndpoint}/api/users/me/stream?token=${encodeURIComponent(this.api.bearer)}&client_id=${encodeURIComponent(this.clientId)}&client_kind=daemon`;
|
|
7232
7262
|
if (this.clientName) url += `&client_name=${encodeURIComponent(this.clientName)}`;
|
|
7233
7263
|
let ws;
|
|
7234
7264
|
try {
|
|
@@ -7257,8 +7287,15 @@ var HubSocket = class {
|
|
|
7257
7287
|
} catch {
|
|
7258
7288
|
return;
|
|
7259
7289
|
}
|
|
7260
|
-
if (msg
|
|
7261
|
-
|
|
7290
|
+
if (!msg || typeof msg !== "object") return;
|
|
7291
|
+
const frame = msg;
|
|
7292
|
+
if (frame.event === "standardagents.wake") {
|
|
7293
|
+
const threadId = frame.data?.thread_id;
|
|
7294
|
+
if (typeof threadId === "string") this.hooks.onWake(threadId);
|
|
7295
|
+
return;
|
|
7296
|
+
}
|
|
7297
|
+
if (frame.type === "wake" && typeof frame.threadId === "string") {
|
|
7298
|
+
this.hooks.onWake(frame.threadId);
|
|
7262
7299
|
}
|
|
7263
7300
|
}
|
|
7264
7301
|
handleDrop(ws) {
|
|
@@ -8303,8 +8340,127 @@ async function runDaemonCommand(argv) {
|
|
|
8303
8340
|
}
|
|
8304
8341
|
}
|
|
8305
8342
|
|
|
8306
|
-
// src/
|
|
8343
|
+
// src/auth-cli.ts
|
|
8307
8344
|
var c4 = {
|
|
8345
|
+
reset: "\x1B[0m",
|
|
8346
|
+
dim: "\x1B[2m",
|
|
8347
|
+
bold: "\x1B[1m",
|
|
8348
|
+
green: "\x1B[32m",
|
|
8349
|
+
red: "\x1B[31m",
|
|
8350
|
+
teal: "\x1B[38;5;43m"
|
|
8351
|
+
};
|
|
8352
|
+
function parseAuthArgs(argv) {
|
|
8353
|
+
let endpoint = null;
|
|
8354
|
+
let token = null;
|
|
8355
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
8356
|
+
const arg = argv[i];
|
|
8357
|
+
if (arg === "--endpoint" || arg === "-e") {
|
|
8358
|
+
endpoint = argv[++i] ?? "";
|
|
8359
|
+
if (!endpoint) throw new Error("--endpoint requires a URL");
|
|
8360
|
+
} else if (arg.startsWith("--endpoint=")) {
|
|
8361
|
+
endpoint = arg.slice("--endpoint=".length);
|
|
8362
|
+
} else if (arg === "--token") {
|
|
8363
|
+
token = argv[++i] ?? "";
|
|
8364
|
+
if (!token) throw new Error("--token requires a value");
|
|
8365
|
+
} else if (arg.startsWith("--token=")) {
|
|
8366
|
+
token = arg.slice("--token=".length);
|
|
8367
|
+
} else {
|
|
8368
|
+
throw new Error(`Unknown option for login/logout: ${arg}`);
|
|
8369
|
+
}
|
|
8370
|
+
}
|
|
8371
|
+
const endpointExplicit = endpoint !== null;
|
|
8372
|
+
const resolved = normalizeEndpoint(endpoint ?? defaultEndpoint() ?? PRODUCTION_ENDPOINT);
|
|
8373
|
+
return { endpoint: resolved, endpointExplicit, token };
|
|
8374
|
+
}
|
|
8375
|
+
function hostLabel(endpoint) {
|
|
8376
|
+
return endpoint.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
8377
|
+
}
|
|
8378
|
+
async function signedInLabel(endpoint, token) {
|
|
8379
|
+
try {
|
|
8380
|
+
const res = await fetch(`${endpoint}/api/auth/me`, {
|
|
8381
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
8382
|
+
signal: AbortSignal.timeout(1e4)
|
|
8383
|
+
});
|
|
8384
|
+
if (!res.ok) return null;
|
|
8385
|
+
const body = await res.json();
|
|
8386
|
+
return body.user?.email || body.user?.username || null;
|
|
8387
|
+
} catch {
|
|
8388
|
+
return null;
|
|
8389
|
+
}
|
|
8390
|
+
}
|
|
8391
|
+
async function runAuthCommand(command, argv) {
|
|
8392
|
+
let args;
|
|
8393
|
+
try {
|
|
8394
|
+
args = parseAuthArgs(argv);
|
|
8395
|
+
} catch (error) {
|
|
8396
|
+
process.stdout.write(`${c4.red}error:${c4.reset} ${error instanceof Error ? error.message : String(error)}
|
|
8397
|
+
`);
|
|
8398
|
+
process.stdout.write(`${c4.dim}usage: standardcode ${command} [--endpoint <url>]${command === "login" ? " [--token <key>]" : ""}${c4.reset}
|
|
8399
|
+
`);
|
|
8400
|
+
process.exitCode = 1;
|
|
8401
|
+
return;
|
|
8402
|
+
}
|
|
8403
|
+
const host = hostLabel(args.endpoint);
|
|
8404
|
+
relaxTlsForLocalEndpoint(args.endpoint);
|
|
8405
|
+
const existing = getCredential(args.endpoint);
|
|
8406
|
+
if (command === "logout") {
|
|
8407
|
+
if (!existing) {
|
|
8408
|
+
process.stdout.write(`You're not signed in to ${c4.teal}${host}${c4.reset} \u2014 nothing to do.
|
|
8409
|
+
`);
|
|
8410
|
+
return;
|
|
8411
|
+
}
|
|
8412
|
+
deleteCredential(args.endpoint);
|
|
8413
|
+
process.stdout.write(`${c4.green}\u2713${c4.reset} Signed out of ${c4.teal}${host}${c4.reset}.
|
|
8414
|
+
`);
|
|
8415
|
+
process.stdout.write(`${c4.dim}Run \`standardcode login\` to sign in again \u2014 with any account.${c4.reset}
|
|
8416
|
+
`);
|
|
8417
|
+
return;
|
|
8418
|
+
}
|
|
8419
|
+
if (existing) {
|
|
8420
|
+
const who2 = await signedInLabel(args.endpoint, existing.access_token);
|
|
8421
|
+
process.stdout.write(
|
|
8422
|
+
`${c4.dim}Currently signed in to ${host}${who2 ? ` as ${who2}` : ""} \u2014 replacing that sign-in.${c4.reset}
|
|
8423
|
+
`
|
|
8424
|
+
);
|
|
8425
|
+
}
|
|
8426
|
+
let token = args.token;
|
|
8427
|
+
if (!token) {
|
|
8428
|
+
process.stdout.write(`${c4.bold}Sign in to Standard Code${c4.reset}
|
|
8429
|
+
`);
|
|
8430
|
+
if (`https://${host}` !== PRODUCTION_ENDPOINT && `http://${host}` !== PRODUCTION_ENDPOINT) {
|
|
8431
|
+
process.stdout.write(`${c4.dim}Connecting to${c4.reset} ${c4.teal}${host}${c4.reset}
|
|
8432
|
+
`);
|
|
8433
|
+
}
|
|
8434
|
+
try {
|
|
8435
|
+
token = await deviceLogin(args.endpoint);
|
|
8436
|
+
} catch (error) {
|
|
8437
|
+
process.stdout.write(`${c4.red}\u2717${c4.reset} ${error instanceof Error ? error.message : String(error)}
|
|
8438
|
+
`);
|
|
8439
|
+
process.exitCode = 1;
|
|
8440
|
+
return;
|
|
8441
|
+
}
|
|
8442
|
+
}
|
|
8443
|
+
const api = new ApiClient(args.endpoint, token);
|
|
8444
|
+
const check = await api.verifyDetailed();
|
|
8445
|
+
if (!check.ok) {
|
|
8446
|
+
process.stdout.write(`${c4.red}\u2717${c4.reset} Sign-in didn't verify: ${check.reason}
|
|
8447
|
+
`);
|
|
8448
|
+
if (check.hint) process.stdout.write(` ${c4.dim}${check.hint}${c4.reset}
|
|
8449
|
+
`);
|
|
8450
|
+
process.exitCode = 1;
|
|
8451
|
+
return;
|
|
8452
|
+
}
|
|
8453
|
+
saveCredential(
|
|
8454
|
+
{ endpoint: args.endpoint, access_token: token, token_type: "Bearer", saved_at: Date.now() },
|
|
8455
|
+
{ updateDefault: !args.endpointExplicit }
|
|
8456
|
+
);
|
|
8457
|
+
const who = await signedInLabel(args.endpoint, token);
|
|
8458
|
+
process.stdout.write(`${c4.green}\u2713${c4.reset} Signed in to ${c4.teal}${host}${c4.reset}${who ? ` as ${c4.bold}${who}${c4.reset}` : ""}.
|
|
8459
|
+
`);
|
|
8460
|
+
}
|
|
8461
|
+
|
|
8462
|
+
// src/index.ts
|
|
8463
|
+
var c5 = {
|
|
8308
8464
|
reset: "\x1B[0m",
|
|
8309
8465
|
dim: "\x1B[2m",
|
|
8310
8466
|
bold: "\x1B[1m",
|
|
@@ -8333,10 +8489,16 @@ function printUsage() {
|
|
|
8333
8489
|
stdout.write(
|
|
8334
8490
|
[
|
|
8335
8491
|
"",
|
|
8336
|
-
`${
|
|
8492
|
+
`${c5.bold}Usage${c5.reset}`,
|
|
8337
8493
|
" standardcode [options] [dir]",
|
|
8494
|
+
" standardcode login [--endpoint <url>] [--token <key>]",
|
|
8495
|
+
" Sign in (or switch accounts \u2014 always re-runs the flow).",
|
|
8496
|
+
" standardcode logout [--endpoint <url>]",
|
|
8497
|
+
" Sign out of the saved endpoint.",
|
|
8498
|
+
" standardcode daemon <install|run|status|uninstall>",
|
|
8499
|
+
" Manage this machine's headless execution daemon.",
|
|
8338
8500
|
"",
|
|
8339
|
-
`${
|
|
8501
|
+
`${c5.bold}Options${c5.reset}`,
|
|
8340
8502
|
" -v, --version Print the Standard Code version and exit.",
|
|
8341
8503
|
" -e, --endpoint [url] Use a different Standard Agents instance for this run",
|
|
8342
8504
|
" (default: https://api.standardcode.ai).",
|
|
@@ -8405,15 +8567,15 @@ function parseArgs2(args) {
|
|
|
8405
8567
|
}
|
|
8406
8568
|
function printCommandBlock(tui, command, output4, ok, where) {
|
|
8407
8569
|
tui.print("");
|
|
8408
|
-
const note = where ? ` ${
|
|
8409
|
-
tui.print(`${
|
|
8570
|
+
const note = where ? ` ${c5.dim}(ran on ${where})${c5.reset}` : "";
|
|
8571
|
+
tui.print(`${c5.magenta}!${c5.reset} ${c5.bold}${command}${c5.reset}${note}`);
|
|
8410
8572
|
const body = (output4 ?? "").replace(/\s+$/, "");
|
|
8411
8573
|
if (body) {
|
|
8412
8574
|
for (const line of body.split("\n")) {
|
|
8413
|
-
tui.print(` ${ok ?
|
|
8575
|
+
tui.print(` ${ok ? c5.dim : c5.red}${line}${c5.reset}`);
|
|
8414
8576
|
}
|
|
8415
8577
|
} else {
|
|
8416
|
-
tui.print(` ${
|
|
8578
|
+
tui.print(` ${c5.dim}(no output)${c5.reset}`);
|
|
8417
8579
|
}
|
|
8418
8580
|
tui.print("");
|
|
8419
8581
|
}
|
|
@@ -8424,7 +8586,7 @@ function printAssistant(tui, text) {
|
|
|
8424
8586
|
let dotted = false;
|
|
8425
8587
|
for (const line of renderStreamingMarkdown(text, cols2)) {
|
|
8426
8588
|
if (!dotted && line.trim()) {
|
|
8427
|
-
tui.print(`${
|
|
8589
|
+
tui.print(`${c5.gray}\u2022${c5.reset} ${line}`);
|
|
8428
8590
|
dotted = true;
|
|
8429
8591
|
} else {
|
|
8430
8592
|
tui.print(` ${line}`);
|
|
@@ -8439,7 +8601,7 @@ function startLoader(label) {
|
|
|
8439
8601
|
const draw = () => {
|
|
8440
8602
|
const now = Date.now();
|
|
8441
8603
|
const f = frames[Math.floor(now / 70) % frames.length];
|
|
8442
|
-
stdout.write(`\r\x1B[K${pad}${brandCycleColor(now)}${f}${
|
|
8604
|
+
stdout.write(`\r\x1B[K${pad}${brandCycleColor(now)}${f}${c5.reset} ${c5.dim}${label}\u2026${c5.reset}`);
|
|
8443
8605
|
};
|
|
8444
8606
|
draw();
|
|
8445
8607
|
const timer = setInterval(draw, 70);
|
|
@@ -8455,12 +8617,12 @@ function farewell(stoppedProcs = 0) {
|
|
|
8455
8617
|
if (stoppedProcs > 0) {
|
|
8456
8618
|
stdout.write(
|
|
8457
8619
|
`
|
|
8458
|
-
${
|
|
8620
|
+
${c5.cyan}\u2699${c5.reset} Stopped ${stoppedProcs} background process${stoppedProcs === 1 ? "" : "es"}.
|
|
8459
8621
|
`
|
|
8460
8622
|
);
|
|
8461
8623
|
}
|
|
8462
8624
|
stdout.write(`
|
|
8463
|
-
${
|
|
8625
|
+
${c5.teal}\u25C7${c5.reset} ${c5.dim}Standard Code \u2014 see you soon.${c5.reset}
|
|
8464
8626
|
`);
|
|
8465
8627
|
}
|
|
8466
8628
|
function printWelcome(endpoint, projectDir) {
|
|
@@ -8474,10 +8636,10 @@ function printWelcome(endpoint, projectDir) {
|
|
|
8474
8636
|
const metaWidth = Math.max(1, terminalColumns - pad.length - markWidth - 3 - 1);
|
|
8475
8637
|
const displayDir = truncateMiddle(dir2, metaWidth);
|
|
8476
8638
|
const meta = [
|
|
8477
|
-
`${
|
|
8478
|
-
`${
|
|
8479
|
-
...endpoint === PRODUCTION_ENDPOINT ? [] : [`${
|
|
8480
|
-
`${
|
|
8639
|
+
`${c5.bold}${gradientText("Standard Code")}${c5.reset}${version ? ` ${c5.dim}v${version}${c5.reset}` : ""}`,
|
|
8640
|
+
`${c5.dim}terminal coding agent${c5.reset}`,
|
|
8641
|
+
...endpoint === PRODUCTION_ENDPOINT ? [] : [`${c5.teal}${host}${c5.reset}`],
|
|
8642
|
+
`${c5.dim}${displayDir}${c5.reset}`
|
|
8481
8643
|
];
|
|
8482
8644
|
const metaTop = Math.floor((LOGO_MARK.length - meta.length) / 2);
|
|
8483
8645
|
stdout.write("\n");
|
|
@@ -8491,11 +8653,11 @@ function printWelcome(endpoint, projectDir) {
|
|
|
8491
8653
|
}
|
|
8492
8654
|
function colorActivity(line) {
|
|
8493
8655
|
const m = line.match(/^(\s*)([✓✗⛔])\s?([\s\S]*)$/);
|
|
8494
|
-
if (!m) return `${
|
|
8656
|
+
if (!m) return `${c5.dim}${line}${c5.reset}`;
|
|
8495
8657
|
const [, indent, glyph, rest] = m;
|
|
8496
8658
|
if (glyph === "\u2713") {
|
|
8497
|
-
const body = rest.replace(/\s(\([^()]*\))\s*$/, ` ${
|
|
8498
|
-
return `${indent}${
|
|
8659
|
+
const body = rest.replace(/\s(\([^()]*\))\s*$/, ` ${c5.dim}$1${c5.reset}`);
|
|
8660
|
+
return `${indent}${c5.green}\u2713${c5.reset} ${body}`;
|
|
8499
8661
|
}
|
|
8500
8662
|
if (glyph === "\u2717") {
|
|
8501
8663
|
const ERR_MAX_LINES = 7;
|
|
@@ -8503,15 +8665,15 @@ function colorActivity(line) {
|
|
|
8503
8665
|
const shown = lines.slice(0, ERR_MAX_LINES);
|
|
8504
8666
|
const hidden = lines.length - shown.length;
|
|
8505
8667
|
const body = shown.map(
|
|
8506
|
-
(l, i) => i === 0 ? `${indent}${
|
|
8668
|
+
(l, i) => i === 0 ? `${indent}${c5.red}\u2717 ${l}${c5.reset}` : `${indent}${c5.red}${c5.dim}${l}${c5.reset}`
|
|
8507
8669
|
).join("\n");
|
|
8508
8670
|
if (hidden > 0) {
|
|
8509
8671
|
return `${body}
|
|
8510
|
-
${indent}${
|
|
8672
|
+
${indent}${c5.dim}\u2026 +${hidden} more line${hidden === 1 ? "" : "s"}${c5.reset}`;
|
|
8511
8673
|
}
|
|
8512
8674
|
return body;
|
|
8513
8675
|
}
|
|
8514
|
-
return `${indent}${
|
|
8676
|
+
return `${indent}${c5.yellow}\u26D4 ${rest}${c5.reset}`;
|
|
8515
8677
|
}
|
|
8516
8678
|
async function main() {
|
|
8517
8679
|
if (process.argv.slice(2).some((arg) => arg === "-v" || arg === "--version")) {
|
|
@@ -8523,11 +8685,15 @@ async function main() {
|
|
|
8523
8685
|
await runDaemonCommand(process.argv.slice(3));
|
|
8524
8686
|
return;
|
|
8525
8687
|
}
|
|
8688
|
+
if (process.argv[2] === "login" || process.argv[2] === "logout") {
|
|
8689
|
+
await runAuthCommand(process.argv[2], process.argv.slice(3));
|
|
8690
|
+
return;
|
|
8691
|
+
}
|
|
8526
8692
|
let cliArgs;
|
|
8527
8693
|
try {
|
|
8528
8694
|
cliArgs = parseArgs2(process.argv.slice(2));
|
|
8529
8695
|
} catch (error) {
|
|
8530
|
-
stdout.write(`${
|
|
8696
|
+
stdout.write(`${c5.red}error:${c5.reset} ${error instanceof Error ? error.message : String(error)}
|
|
8531
8697
|
`);
|
|
8532
8698
|
printUsage();
|
|
8533
8699
|
process.exit(1);
|
|
@@ -8554,7 +8720,7 @@ async function main() {
|
|
|
8554
8720
|
}
|
|
8555
8721
|
preflightArmed = true;
|
|
8556
8722
|
stdout.write(`
|
|
8557
|
-
${
|
|
8723
|
+
${c5.dim}Press Control-C again to exit${c5.reset}
|
|
8558
8724
|
`);
|
|
8559
8725
|
preflightTimer = setTimeout(() => {
|
|
8560
8726
|
preflightArmed = false;
|
|
@@ -8576,10 +8742,10 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
|
|
|
8576
8742
|
const askEndpoint = async () => {
|
|
8577
8743
|
for (; ; ) {
|
|
8578
8744
|
const answer = (await ask(
|
|
8579
|
-
`${
|
|
8745
|
+
`${c5.cyan}Standard Agents instance URL${c5.reset} (e.g. http://localhost:5178): `
|
|
8580
8746
|
)).trim();
|
|
8581
8747
|
if (answer) return answer;
|
|
8582
|
-
stdout.write(`${
|
|
8748
|
+
stdout.write(`${c5.dim}An endpoint URL is required.${c5.reset}
|
|
8583
8749
|
`);
|
|
8584
8750
|
}
|
|
8585
8751
|
};
|
|
@@ -8594,7 +8760,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
|
|
|
8594
8760
|
const tlsRelaxed = relaxTlsForLocalEndpoint(endpoint);
|
|
8595
8761
|
printWelcome(endpoint, projectDir);
|
|
8596
8762
|
if (tlsRelaxed) {
|
|
8597
|
-
stdout.write(`${
|
|
8763
|
+
stdout.write(`${c5.dim} TLS verification relaxed for local endpoint.${c5.reset}
|
|
8598
8764
|
|
|
8599
8765
|
`);
|
|
8600
8766
|
}
|
|
@@ -8606,7 +8772,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
|
|
|
8606
8772
|
loading.stop();
|
|
8607
8773
|
const applied = consumeAppliedUpdate(version);
|
|
8608
8774
|
if (applied) {
|
|
8609
|
-
stdout.write(` ${
|
|
8775
|
+
stdout.write(` ${c5.green}\u2713${c5.reset} ${c5.dim}Standard Code updated to v${version}.${c5.reset}
|
|
8610
8776
|
|
|
8611
8777
|
`);
|
|
8612
8778
|
}
|
|
@@ -8615,21 +8781,21 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
|
|
|
8615
8781
|
const decision = decideAutoUpdate(updateAvailable, { state: readAutoUpdateState(), pm });
|
|
8616
8782
|
if (decision === "start" && pm && startBackgroundUpdate(updateAvailable.latest, pm)) {
|
|
8617
8783
|
stdout.write(
|
|
8618
|
-
` ${
|
|
8784
|
+
` ${c5.teal}\u27F3${c5.reset} ${c5.dim}Standard Code ${c5.reset}${c5.bold}v${updateAvailable.latest}${c5.reset}${c5.dim} is installing in the background \u2014 it applies on your next launch.${c5.reset}
|
|
8619
8785
|
|
|
8620
8786
|
`
|
|
8621
8787
|
);
|
|
8622
8788
|
} else if (decision === "in_flight") {
|
|
8623
8789
|
stdout.write(
|
|
8624
|
-
` ${
|
|
8790
|
+
` ${c5.teal}\u27F3${c5.reset} ${c5.dim}Standard Code v${updateAvailable.latest} is still installing in the background.${c5.reset}
|
|
8625
8791
|
|
|
8626
8792
|
`
|
|
8627
8793
|
);
|
|
8628
8794
|
} else {
|
|
8629
8795
|
const display = updateCommand(pm ?? "npm").display;
|
|
8630
8796
|
stdout.write(
|
|
8631
|
-
` ${
|
|
8632
|
-
${
|
|
8797
|
+
` ${c5.teal}\u25C7${c5.reset} ${c5.dim}Update available:${c5.reset} ${c5.dim}v${updateAvailable.current}${c5.reset} \u2192 ${c5.bold}v${updateAvailable.latest}${c5.reset}
|
|
8798
|
+
${c5.dim}Run ${c5.reset}${c5.bold}${display}${c5.reset}${c5.dim} to update${c5.reset}
|
|
8633
8799
|
|
|
8634
8800
|
`
|
|
8635
8801
|
);
|
|
@@ -8647,39 +8813,39 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
|
|
|
8647
8813
|
if (!api || !storedCheck?.ok) {
|
|
8648
8814
|
const host = endpoint.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
8649
8815
|
if (storedCheck && !storedCheck.ok) {
|
|
8650
|
-
stdout.write(`${
|
|
8816
|
+
stdout.write(`${c5.red}\u2717${c5.reset} ${c5.dim}Saved sign-in for this endpoint failed:${c5.reset} ${storedCheck.reason}
|
|
8651
8817
|
`);
|
|
8652
|
-
if (storedCheck.hint) stdout.write(` ${
|
|
8818
|
+
if (storedCheck.hint) stdout.write(` ${c5.dim}${storedCheck.hint}${c5.reset}
|
|
8653
8819
|
`);
|
|
8654
8820
|
stdout.write("\n");
|
|
8655
8821
|
}
|
|
8656
8822
|
const explainFailure = (result, prefix) => {
|
|
8657
|
-
stdout.write(`${
|
|
8823
|
+
stdout.write(`${c5.red}\u2717${c5.reset} ${prefix}${result.reason}
|
|
8658
8824
|
`);
|
|
8659
|
-
if (result.hint) stdout.write(` ${
|
|
8825
|
+
if (result.hint) stdout.write(` ${c5.dim}${result.hint}${c5.reset}
|
|
8660
8826
|
`);
|
|
8661
8827
|
};
|
|
8662
|
-
stdout.write(`${
|
|
8828
|
+
stdout.write(`${c5.bold}Sign in to ${gradientText("Standard Code")}${c5.reset}
|
|
8663
8829
|
`);
|
|
8664
8830
|
if (`https://${host}` !== PRODUCTION_ENDPOINT) {
|
|
8665
|
-
stdout.write(`${
|
|
8831
|
+
stdout.write(`${c5.dim}Connecting to${c5.reset} ${c5.teal}${host}${c5.reset}
|
|
8666
8832
|
`);
|
|
8667
8833
|
}
|
|
8668
8834
|
stdout.write(
|
|
8669
|
-
`${
|
|
8835
|
+
`${c5.dim}You'll only need to do this once on this machine.${c5.reset}
|
|
8670
8836
|
|
|
8671
8837
|
`
|
|
8672
8838
|
);
|
|
8673
8839
|
stdout.write(
|
|
8674
|
-
`${
|
|
8840
|
+
`${c5.white}Press ${c5.bold}Enter${c5.reset}${c5.white} to open your browser and sign in.${c5.reset} ${c5.dim}(or paste an API token)${c5.reset}
|
|
8675
8841
|
|
|
8676
8842
|
`
|
|
8677
8843
|
);
|
|
8678
8844
|
for (; ; ) {
|
|
8679
|
-
const token = (await ask(`${
|
|
8845
|
+
const token = (await ask(`${c5.teal}\u276F${c5.reset} `)).trim();
|
|
8680
8846
|
if (!token) {
|
|
8681
8847
|
const got = await deviceLogin(endpoint).catch((e) => {
|
|
8682
|
-
stdout.write(`${
|
|
8848
|
+
stdout.write(`${c5.red}\u2717${c5.reset} ${c5.dim}${e instanceof Error ? e.message : String(e)}${c5.reset}
|
|
8683
8849
|
`);
|
|
8684
8850
|
return null;
|
|
8685
8851
|
});
|
|
@@ -8693,7 +8859,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
|
|
|
8693
8859
|
{ endpoint, access_token: got, token_type: "Bearer", saved_at: Date.now() },
|
|
8694
8860
|
{ updateDefault: !endpointOverride }
|
|
8695
8861
|
);
|
|
8696
|
-
stdout.write(`${
|
|
8862
|
+
stdout.write(`${c5.green}\u2713${c5.reset} Connected to ${c5.teal}${host}${c5.reset}
|
|
8697
8863
|
`);
|
|
8698
8864
|
break;
|
|
8699
8865
|
}
|
|
@@ -8709,7 +8875,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
|
|
|
8709
8875
|
{ endpoint, access_token: token, token_type: "Bearer", saved_at: Date.now() },
|
|
8710
8876
|
{ updateDefault: !endpointOverride }
|
|
8711
8877
|
);
|
|
8712
|
-
stdout.write(`${
|
|
8878
|
+
stdout.write(`${c5.green}\u2713${c5.reset} Connected to ${c5.teal}${host}${c5.reset}
|
|
8713
8879
|
`);
|
|
8714
8880
|
break;
|
|
8715
8881
|
}
|
|
@@ -8733,7 +8899,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
|
|
|
8733
8899
|
} else if (["unlimited", "standard", "unlimited_one", AGENT_ID].includes(wanted)) {
|
|
8734
8900
|
agentOverride = AGENT_ID;
|
|
8735
8901
|
} else {
|
|
8736
|
-
stdout.write(`${
|
|
8902
|
+
stdout.write(`${c5.red}error:${c5.reset} Unknown agent "${cliArgs.agent}". Use unlimited or sama.
|
|
8737
8903
|
`);
|
|
8738
8904
|
process.exit(1);
|
|
8739
8905
|
}
|
|
@@ -8771,7 +8937,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
|
|
|
8771
8937
|
flow: for (; ; ) {
|
|
8772
8938
|
if (step === "machine") {
|
|
8773
8939
|
const picked = await tui.select(
|
|
8774
|
-
`${
|
|
8940
|
+
`${c5.bold}Where should this session run?${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
|
|
8775
8941
|
[
|
|
8776
8942
|
{
|
|
8777
8943
|
label: `This machine \u2014 ${self ? machineDisplayName(self) : machine}`,
|
|
@@ -8855,7 +9021,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
|
|
|
8855
9021
|
items.push({ label: "\uFF0B Start a new session", value: null });
|
|
8856
9022
|
const whereLabel = session.mode === "remote" && session.runner ? `${session.runner.name}:${shortenPath(session.remotePath ?? "")}` : shortDir;
|
|
8857
9023
|
const picked = await tui.select(
|
|
8858
|
-
`${
|
|
9024
|
+
`${c5.bold}Resume a session${c5.reset} ${c5.gray}${whereLabel}${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
|
|
8859
9025
|
items
|
|
8860
9026
|
);
|
|
8861
9027
|
if (picked === void 0) {
|
|
@@ -8881,7 +9047,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
|
|
|
8881
9047
|
} else {
|
|
8882
9048
|
if (!agentOverride) {
|
|
8883
9049
|
const picked = await tui.select(
|
|
8884
|
-
`${
|
|
9050
|
+
`${c5.bold}Which agent?${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
|
|
8885
9051
|
AGENT_CHOICES.map((choice) => ({
|
|
8886
9052
|
label: choice.title,
|
|
8887
9053
|
hint: choice.description,
|
|
@@ -8931,7 +9097,7 @@ async function ensureSamaAuth(tui, api) {
|
|
|
8931
9097
|
checking.stop();
|
|
8932
9098
|
if (already) return true;
|
|
8933
9099
|
const picked = await tui.select(
|
|
8934
|
-
`${
|
|
9100
|
+
`${c5.bold}Authenticate with ChatGPT${c5.reset} ${c5.dim}Sama One runs on OpenAI using your own ChatGPT Pro account${c5.reset}`,
|
|
8935
9101
|
[
|
|
8936
9102
|
{
|
|
8937
9103
|
label: "Continue with ChatGPT",
|
|
@@ -8944,7 +9110,7 @@ async function ensureSamaAuth(tui, api) {
|
|
|
8944
9110
|
if (picked !== "continue") return false;
|
|
8945
9111
|
openUrl("https://standardcode.ai/app?connect=sama");
|
|
8946
9112
|
tui.print(
|
|
8947
|
-
`${
|
|
9113
|
+
`${c5.dim}Finish connecting ChatGPT in the browser \u2014 waiting here for the authorization to land on your account\u2026${c5.reset}`
|
|
8948
9114
|
);
|
|
8949
9115
|
const waiting = startLoader("Waiting for your ChatGPT authorization");
|
|
8950
9116
|
const deadline = Date.now() + 5 * 60 * 1e3;
|
|
@@ -8953,20 +9119,20 @@ async function ensureSamaAuth(tui, api) {
|
|
|
8953
9119
|
if (await api.openSamaStatus().catch(() => false)) {
|
|
8954
9120
|
waiting.stop();
|
|
8955
9121
|
tui.print(
|
|
8956
|
-
`${
|
|
9122
|
+
`${c5.green}\u2713${c5.reset} ChatGPT connected \u2014 Sama One is now unlocked on your account (terminal, web, and macOS app).`
|
|
8957
9123
|
);
|
|
8958
9124
|
return true;
|
|
8959
9125
|
}
|
|
8960
9126
|
}
|
|
8961
9127
|
waiting.stop();
|
|
8962
9128
|
tui.print(
|
|
8963
|
-
`${
|
|
9129
|
+
`${c5.yellow}Still not connected.${c5.reset} Finish the flow at ${c5.teal}standardcode.ai/app${c5.reset} and pick Sama One again.`
|
|
8964
9130
|
);
|
|
8965
9131
|
return false;
|
|
8966
9132
|
}
|
|
8967
9133
|
async function runAgentSwitchMenu(tui, api, threadId) {
|
|
8968
9134
|
const picked = await tui.select(
|
|
8969
|
-
`${
|
|
9135
|
+
`${c5.bold}Switch agent${c5.reset} ${c5.dim}takes effect on the next message${c5.reset}`,
|
|
8970
9136
|
AGENT_CHOICES.map((choice) => ({
|
|
8971
9137
|
label: choice.title,
|
|
8972
9138
|
hint: choice.description,
|
|
@@ -8983,10 +9149,10 @@ async function runAgentSwitchMenu(tui, api, threadId) {
|
|
|
8983
9149
|
try {
|
|
8984
9150
|
await api.setThreadAgent(threadId, picked);
|
|
8985
9151
|
tui.setAgentLabel(title);
|
|
8986
|
-
tui.print(`${
|
|
9152
|
+
tui.print(`${c5.green}\u2713${c5.reset} Session handed to ${c5.bold}${title}${c5.reset} \u2014 applies from your next message.`);
|
|
8987
9153
|
} catch (e) {
|
|
8988
9154
|
tui.print(
|
|
8989
|
-
`${
|
|
9155
|
+
`${c5.red}\u2717 couldn't switch agent:${c5.reset} ${c5.gray}${e instanceof Error ? e.message : String(e)}${c5.reset}`
|
|
8990
9156
|
);
|
|
8991
9157
|
}
|
|
8992
9158
|
}
|
|
@@ -9011,7 +9177,7 @@ async function pickLocalProject(tui, api, self, cwd, machineName) {
|
|
|
9011
9177
|
{ label: "\uFF0B New project", hint: "browse or create a directory", value: NEW }
|
|
9012
9178
|
];
|
|
9013
9179
|
const picked = await tui.select(
|
|
9014
|
-
`${
|
|
9180
|
+
`${c5.bold}Project on ${label}${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
|
|
9015
9181
|
items,
|
|
9016
9182
|
{ spaced: true }
|
|
9017
9183
|
);
|
|
@@ -9035,7 +9201,7 @@ async function pickRemoteProject(tui, api, runner) {
|
|
|
9035
9201
|
);
|
|
9036
9202
|
items.push({ label: "\uFF0B New project", hint: "browse or create a directory", value: ENTER_PATH });
|
|
9037
9203
|
const picked = await tui.select(
|
|
9038
|
-
`${
|
|
9204
|
+
`${c5.bold}Project on ${runner.name}${c5.reset} ${c5.dim}\u2191\u2193 \xB7 enter \xB7 esc${c5.reset}`,
|
|
9039
9205
|
items,
|
|
9040
9206
|
{ spaced: true }
|
|
9041
9207
|
);
|
|
@@ -9068,7 +9234,7 @@ async function summarizeThreads(api, threads) {
|
|
|
9068
9234
|
}
|
|
9069
9235
|
function subagentLabel(s, titles) {
|
|
9070
9236
|
const agentName = (s.agent_name || "").trim();
|
|
9071
|
-
const title = (s.title || "").trim() || titles.get(agentName) || (agentName ? agentName.replace(/[_-]+/g, " ").replace(/\b\w/g, (
|
|
9237
|
+
const title = (s.title || "").trim() || titles.get(agentName) || (agentName ? agentName.replace(/[_-]+/g, " ").replace(/\b\w/g, (c6) => c6.toUpperCase()) : "Subagent");
|
|
9072
9238
|
const tagged = (s.threadName || "").trim();
|
|
9073
9239
|
return tagged ? `${title} \xB7 ${tagged}` : title;
|
|
9074
9240
|
}
|
|
@@ -9091,8 +9257,8 @@ async function printHistory(api, threadId, tui) {
|
|
|
9091
9257
|
).sort((a, b) => (a.created_at ?? 0) - (b.created_at ?? 0));
|
|
9092
9258
|
if (!convo.length) return;
|
|
9093
9259
|
const shown = convo.slice(-24);
|
|
9094
|
-
tui.print(`${
|
|
9095
|
-
if (shown.length < convo.length) tui.print(`${
|
|
9260
|
+
tui.print(`${c5.dim}\u2500\u2500 resuming session \xB7 ${convo.length} message${convo.length === 1 ? "" : "s"} \u2500\u2500${c5.reset}`);
|
|
9261
|
+
if (shown.length < convo.length) tui.print(`${c5.dim} \u2026 earlier messages omitted${c5.reset}`);
|
|
9096
9262
|
for (const m of shown) {
|
|
9097
9263
|
if (m.metadata?.user_command) {
|
|
9098
9264
|
printCommandBlock(
|
|
@@ -9153,6 +9319,7 @@ async function runInteractive(tui, api, threadId, projectDir, machine, resumed,
|
|
|
9153
9319
|
let editingPendingId = null;
|
|
9154
9320
|
let reconcileSharedMessaging = async () => {
|
|
9155
9321
|
};
|
|
9322
|
+
let applySharedMessagingEvent = () => false;
|
|
9156
9323
|
let refreshSessionProjection = async () => {
|
|
9157
9324
|
};
|
|
9158
9325
|
const shownIds = /* @__PURE__ */ new Set();
|
|
@@ -9210,21 +9377,21 @@ async function runInteractive(tui, api, threadId, projectDir, machine, resumed,
|
|
|
9210
9377
|
if (isOwner) {
|
|
9211
9378
|
bridge?.setClaim("takeover");
|
|
9212
9379
|
exec?.writeSessionInfo();
|
|
9213
|
-
if (wasKnown && moved) tui.print(`${
|
|
9380
|
+
if (wasKnown && moved) tui.print(`${c5.dim}Tool execution moved to this terminal.${c5.reset}`);
|
|
9214
9381
|
} else if (wasKnown && moved) {
|
|
9215
|
-
tui.print(`${
|
|
9382
|
+
tui.print(`${c5.dim}Tool execution moved to ${describeExecOwner(owner2)}.${c5.reset}`);
|
|
9216
9383
|
}
|
|
9217
9384
|
},
|
|
9218
9385
|
onClaimRefused: (reason) => {
|
|
9219
9386
|
if (reason === "in_flight") {
|
|
9220
9387
|
tui.print(
|
|
9221
|
-
`${
|
|
9388
|
+
`${c5.dim}The session's current client is mid-operation \u2014 execution stays there until it finishes.${c5.reset}`
|
|
9222
9389
|
);
|
|
9223
9390
|
}
|
|
9224
9391
|
},
|
|
9225
9392
|
onSuperseded: () => {
|
|
9226
9393
|
tui.print(
|
|
9227
|
-
`${
|
|
9394
|
+
`${c5.yellow}\u26A0 Another Standard Code process on this machine took over this session \u2014 this terminal is watching.${c5.reset}`
|
|
9228
9395
|
);
|
|
9229
9396
|
},
|
|
9230
9397
|
onStatus: (id, summary) => {
|
|
@@ -9286,7 +9453,7 @@ why: ${req.requestPermission}` : ""}`,
|
|
|
9286
9453
|
} else if (eventType === "goal_updated" && data) {
|
|
9287
9454
|
tui.setGoal(data);
|
|
9288
9455
|
} else if (eventType === SHARED_MESSAGING_EVENT) {
|
|
9289
|
-
void reconcileSharedMessaging(true);
|
|
9456
|
+
if (!applySharedMessagingEvent(data)) void reconcileSharedMessaging(true);
|
|
9290
9457
|
}
|
|
9291
9458
|
},
|
|
9292
9459
|
// A failed turn whose message is the lease service's at-limit denial → offer
|
|
@@ -9382,7 +9549,7 @@ why: ${req.requestPermission}` : ""}`,
|
|
|
9382
9549
|
const logout = async () => {
|
|
9383
9550
|
deleteCredential(api.origin);
|
|
9384
9551
|
const instanceHost = api.origin.replace(/^https?:\/\//, "");
|
|
9385
|
-
tui.print(`${
|
|
9552
|
+
tui.print(`${c5.gray}Signed out \u2014 removed the saved token for ${c5.teal}${instanceHost}${c5.reset}${c5.gray}. Run standardcode to sign in again.${c5.reset}`);
|
|
9386
9553
|
await quit();
|
|
9387
9554
|
};
|
|
9388
9555
|
const bgMgr = {
|
|
@@ -9390,7 +9557,7 @@ why: ${req.requestPermission}` : ""}`,
|
|
|
9390
9557
|
stop: async (id) => {
|
|
9391
9558
|
if (!host) {
|
|
9392
9559
|
tui.print(
|
|
9393
|
-
`${
|
|
9560
|
+
`${c5.gray}That process runs on ${runnerName} \u2014 ask the agent to stop it (it manages processes there).${c5.reset}`
|
|
9394
9561
|
);
|
|
9395
9562
|
return;
|
|
9396
9563
|
}
|
|
@@ -9471,17 +9638,25 @@ why: ${req.requestPermission}` : ""}`,
|
|
|
9471
9638
|
} catch (error) {
|
|
9472
9639
|
if (!sharedMessagingReady && !sharedMessagingUnavailableShown) {
|
|
9473
9640
|
sharedMessagingUnavailableShown = true;
|
|
9474
|
-
tui.print(`${
|
|
9641
|
+
tui.print(`${c5.dim}shared messaging unavailable: ${error instanceof Error ? error.message : String(error)}${c5.reset}`);
|
|
9475
9642
|
}
|
|
9476
9643
|
}
|
|
9477
9644
|
};
|
|
9645
|
+
applySharedMessagingEvent = (data) => {
|
|
9646
|
+
if (!sharedMessagingReady) return false;
|
|
9647
|
+
const event = parseMessagingChangedEvent(data);
|
|
9648
|
+
if (!event || !event.draft || event.draftOmitted) return false;
|
|
9649
|
+
if (event.pendingRevision > sharedMessaging.pending.revision) return false;
|
|
9650
|
+
applySharedMessaging({ version: 1, pending: sharedMessaging.pending, draft: event.draft }, true);
|
|
9651
|
+
return true;
|
|
9652
|
+
};
|
|
9478
9653
|
const onTerminalResume = () => void reconcileSharedMessaging(true);
|
|
9479
9654
|
process.on("SIGCONT", onTerminalResume);
|
|
9480
9655
|
const applySharedMutation = (promise) => promise.then((snapshot) => {
|
|
9481
9656
|
applySharedMessaging(snapshot, false);
|
|
9482
9657
|
return true;
|
|
9483
9658
|
}).catch((error) => {
|
|
9484
|
-
tui.print(`${
|
|
9659
|
+
tui.print(`${c5.dim}shared messaging failed: ${error instanceof Error ? error.message : String(error)}${c5.reset}`);
|
|
9485
9660
|
return false;
|
|
9486
9661
|
});
|
|
9487
9662
|
const appendSharedPending = (text, images, refs = []) => applySharedMutation(api.appendPendingInput(threadId, {
|
|
@@ -9530,7 +9705,7 @@ why: ${req.requestPermission}` : ""}`,
|
|
|
9530
9705
|
busy = false;
|
|
9531
9706
|
optimisticBusyUntil = 0;
|
|
9532
9707
|
tui.setWorking(false);
|
|
9533
|
-
tui.print(`${
|
|
9708
|
+
tui.print(`${c5.dim}failed to send: ${e instanceof Error ? e.message : String(e)}${c5.reset}`);
|
|
9534
9709
|
return false;
|
|
9535
9710
|
}
|
|
9536
9711
|
return true;
|
|
@@ -9539,11 +9714,11 @@ why: ${req.requestPermission}` : ""}`,
|
|
|
9539
9714
|
let bangRunning = false;
|
|
9540
9715
|
const runBangCommand = async (command) => {
|
|
9541
9716
|
if (bangRunning) {
|
|
9542
|
-
tui.print(`${
|
|
9717
|
+
tui.print(`${c5.dim}a command is already running \u2014 one at a time.${c5.reset}`);
|
|
9543
9718
|
return;
|
|
9544
9719
|
}
|
|
9545
9720
|
bangRunning = true;
|
|
9546
|
-
tui.print(`${
|
|
9721
|
+
tui.print(`${c5.magenta}!${c5.reset} ${c5.dim}running on ${whereLabel}\u2026${c5.reset}`);
|
|
9547
9722
|
try {
|
|
9548
9723
|
const res = await api.runCommand(threadId, command);
|
|
9549
9724
|
if (res.messageId) shownIds.add(res.messageId);
|
|
@@ -9557,7 +9732,7 @@ why: ${req.requestPermission}` : ""}`,
|
|
|
9557
9732
|
const openPendingMenu = async () => {
|
|
9558
9733
|
const items = sharedMessaging.pending.items;
|
|
9559
9734
|
if (!items.length) {
|
|
9560
|
-
tui.print(`${
|
|
9735
|
+
tui.print(`${c5.dim}No pending messages.${c5.reset}`);
|
|
9561
9736
|
return;
|
|
9562
9737
|
}
|
|
9563
9738
|
const picked = await tui.select("Pending messages", items.map((item, index) => ({
|
|
@@ -9587,16 +9762,16 @@ why: ${req.requestPermission}` : ""}`,
|
|
|
9587
9762
|
try {
|
|
9588
9763
|
await api.compact(threadId);
|
|
9589
9764
|
} catch (err) {
|
|
9590
|
-
tui.print(`${
|
|
9765
|
+
tui.print(`${c5.red}\u2717${c5.reset} couldn't start compaction: ${err.message}`);
|
|
9591
9766
|
}
|
|
9592
9767
|
};
|
|
9593
9768
|
const runAccountCommand = async () => {
|
|
9594
|
-
tui.print(`${
|
|
9769
|
+
tui.print(`${c5.gray}Opening your account\u2026${c5.reset}`);
|
|
9595
9770
|
const link = await api.accountLink(threadId).catch(() => null);
|
|
9596
9771
|
const target = link?.url ?? "https://standardcode.ai/account";
|
|
9597
9772
|
openUrl(target);
|
|
9598
9773
|
tui.print(
|
|
9599
|
-
link?.preauthed ? `${
|
|
9774
|
+
link?.preauthed ? `${c5.gray}\u2192 account dashboard opened in your browser (signed in)${c5.reset}` : `${c5.gray}\u2192 opened ${target} \u2014 sign in with your account email${c5.reset}`
|
|
9600
9775
|
);
|
|
9601
9776
|
};
|
|
9602
9777
|
const ordinal = (n) => {
|
|
@@ -9607,24 +9782,24 @@ why: ${req.requestPermission}` : ""}`,
|
|
|
9607
9782
|
const renderUpgradePanel = (q) => {
|
|
9608
9783
|
const dots = [];
|
|
9609
9784
|
for (let i = 0; i < q.max; i++) {
|
|
9610
|
-
if (i < q.current) dots.push(`${
|
|
9611
|
-
else if (i === q.current) dots.push(`${
|
|
9612
|
-
else dots.push(`${
|
|
9785
|
+
if (i < q.current) dots.push(`${c5.teal}\u25CF${c5.reset}`);
|
|
9786
|
+
else if (i === q.current) dots.push(`${c5.bold}${gradientText("\uFF0B")}${c5.reset}`);
|
|
9787
|
+
else dots.push(`${c5.dim}\xB7${c5.reset}`);
|
|
9613
9788
|
}
|
|
9614
9789
|
const cost = fmtCost(q);
|
|
9615
9790
|
const lines = [
|
|
9616
9791
|
"",
|
|
9617
|
-
`${
|
|
9792
|
+
`${c5.bold}\u2726 Add a parallel session${c5.reset}`,
|
|
9618
9793
|
"",
|
|
9619
|
-
`${dots.join(" ")} ${
|
|
9794
|
+
`${dots.join(" ")} ${c5.dim}${q.current} of ${q.current} session${q.current === 1 ? "" : "s"} in use${c5.reset}`
|
|
9620
9795
|
];
|
|
9621
9796
|
if (q.ends_trial) {
|
|
9622
9797
|
lines.push(
|
|
9623
|
-
`${
|
|
9624
|
-
`${
|
|
9798
|
+
`${c5.yellow}Your $5 trial covers 1 session. Adding a ${ordinal(q.sessions)} ends the trial${c5.reset}`,
|
|
9799
|
+
`${c5.yellow}and starts your $49/mo plan now${cost ? ` \u2014 ${c5.bold}${cost} charged today${c5.reset}${c5.yellow}` : ""}.${c5.reset}`
|
|
9625
9800
|
);
|
|
9626
9801
|
} else if (cost) {
|
|
9627
|
-
lines.push(`Add a ${ordinal(q.sessions)} session at $49/mo \u2014 ${
|
|
9802
|
+
lines.push(`Add a ${ordinal(q.sessions)} session at $49/mo \u2014 ${c5.bold}${cost} charged now${c5.reset}.`);
|
|
9628
9803
|
} else {
|
|
9629
9804
|
lines.push(`Add a ${ordinal(q.sessions)} session at $49/mo \u2014 prorated on your next invoice.`);
|
|
9630
9805
|
}
|
|
@@ -9637,7 +9812,7 @@ why: ${req.requestPermission}` : ""}`,
|
|
|
9637
9812
|
try {
|
|
9638
9813
|
if (opts.auto) {
|
|
9639
9814
|
tui.print(
|
|
9640
|
-
`${
|
|
9815
|
+
`${c5.yellow}You're out of simultaneous sessions \u2014 another Standard Code session is using your slot.${c5.reset}`
|
|
9641
9816
|
);
|
|
9642
9817
|
}
|
|
9643
9818
|
const quote = await api.sessionsQuote(threadId);
|
|
@@ -9645,16 +9820,16 @@ why: ${req.requestPermission}` : ""}`,
|
|
|
9645
9820
|
const link = await api.accountLink(threadId).catch(() => null);
|
|
9646
9821
|
const target = link?.url ?? "https://standardcode.ai/account";
|
|
9647
9822
|
tui.print(
|
|
9648
|
-
`${
|
|
9823
|
+
`${c5.gray}Close the other session (its slot frees within ~90s) \u2014 or add another simultaneous session to your plan, then resend your message.${c5.reset}`
|
|
9649
9824
|
);
|
|
9650
9825
|
openUrl(target);
|
|
9651
|
-
tui.print(`${
|
|
9826
|
+
tui.print(`${c5.gray}\u2192 opened ${target} to manage your plan${c5.reset}`);
|
|
9652
9827
|
return;
|
|
9653
9828
|
}
|
|
9654
9829
|
if (quote.current >= quote.max) {
|
|
9655
9830
|
tui.print(
|
|
9656
|
-
`${
|
|
9657
|
-
${
|
|
9831
|
+
`${c5.yellow}You're at the maximum of ${quote.max} parallel session${quote.max === 1 ? "" : "s"}.${c5.reset}
|
|
9832
|
+
${c5.gray}Close another session (its slot frees within ~90s), then resend your message.${c5.reset}`
|
|
9658
9833
|
);
|
|
9659
9834
|
return;
|
|
9660
9835
|
}
|
|
@@ -9666,25 +9841,25 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
|
|
|
9666
9841
|
{ label: "Not now", value: "no" }
|
|
9667
9842
|
]);
|
|
9668
9843
|
if (choice !== "go") {
|
|
9669
|
-
tui.print(`${
|
|
9844
|
+
tui.print(`${c5.gray}No change made \u2014 you can upgrade anytime with /upgrade.${c5.reset}`);
|
|
9670
9845
|
return;
|
|
9671
9846
|
}
|
|
9672
|
-
tui.print(`${
|
|
9847
|
+
tui.print(`${c5.gray}Applying\u2026${c5.reset}`);
|
|
9673
9848
|
let applied;
|
|
9674
9849
|
try {
|
|
9675
9850
|
applied = await api.sessionsUpgrade(threadId, quote.sessions);
|
|
9676
9851
|
} catch (e) {
|
|
9677
|
-
tui.print(`${
|
|
9852
|
+
tui.print(`${c5.red}\u2717${c5.reset} Upgrade failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
9678
9853
|
return;
|
|
9679
9854
|
}
|
|
9680
9855
|
if (!applied?.ok) {
|
|
9681
|
-
tui.print(`${
|
|
9856
|
+
tui.print(`${c5.red}\u2717${c5.reset} Upgrade failed: ${applied?.error ?? "unknown error"}`);
|
|
9682
9857
|
return;
|
|
9683
9858
|
}
|
|
9684
9859
|
const n = applied.sessions ?? quote.sessions;
|
|
9685
|
-
tui.print(`${
|
|
9860
|
+
tui.print(`${c5.green}\u2713${c5.reset} ${c5.bold}${gradientText(`You now have ${n} parallel session${n === 1 ? "" : "s"}.`)}${c5.reset}`);
|
|
9686
9861
|
if (opts.auto && lastSent) {
|
|
9687
|
-
tui.print(`${
|
|
9862
|
+
tui.print(`${c5.gray}Continuing\u2026${c5.reset}`);
|
|
9688
9863
|
await sendNow(lastSent.text, lastSent.images, lastSent.refs);
|
|
9689
9864
|
}
|
|
9690
9865
|
} finally {
|
|
@@ -9795,38 +9970,50 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
|
|
|
9795
9970
|
const history = await loadHistory(api, threadId, historySeedThreadId);
|
|
9796
9971
|
tui.setHistory(history);
|
|
9797
9972
|
await reconcileSharedMessaging(true);
|
|
9798
|
-
let
|
|
9799
|
-
|
|
9800
|
-
|
|
9801
|
-
|
|
9802
|
-
|
|
9803
|
-
|
|
9804
|
-
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
if (sharedMessagingReady) {
|
|
9811
|
-
const hasDraft = !!textVal.trim() || images.length > 0 || mirroredDraftRefs.length > 0;
|
|
9973
|
+
let latestDraftPayload = null;
|
|
9974
|
+
let draftDirty = false;
|
|
9975
|
+
let draftInFlight = false;
|
|
9976
|
+
const pumpComposerDraft = async () => {
|
|
9977
|
+
if (draftInFlight) return;
|
|
9978
|
+
draftInFlight = true;
|
|
9979
|
+
try {
|
|
9980
|
+
while (draftDirty) {
|
|
9981
|
+
draftDirty = false;
|
|
9982
|
+
const payload = latestDraftPayload;
|
|
9983
|
+
if (!payload || !sharedMessagingReady) continue;
|
|
9984
|
+
const hasDraft = !!payload.text.trim() || payload.images.length > 0 || mirroredDraftRefs.length > 0;
|
|
9812
9985
|
const mutation = {
|
|
9813
|
-
content:
|
|
9986
|
+
content: payload.text,
|
|
9814
9987
|
attachments: [
|
|
9815
9988
|
...hasDraft ? mirroredDraftRefs : [],
|
|
9816
|
-
...toSharedAttachments(images)
|
|
9989
|
+
...toSharedAttachments(payload.images)
|
|
9817
9990
|
],
|
|
9818
9991
|
...messagingOrigin
|
|
9819
9992
|
};
|
|
9820
|
-
|
|
9993
|
+
await applySharedMutation(
|
|
9821
9994
|
hasDraft ? api.putSharedDraft(threadId, mutation) : api.clearSharedDraft(threadId, messagingOrigin)
|
|
9822
9995
|
);
|
|
9823
9996
|
}
|
|
9824
|
-
}
|
|
9997
|
+
} finally {
|
|
9998
|
+
draftInFlight = false;
|
|
9999
|
+
}
|
|
10000
|
+
};
|
|
10001
|
+
const clearComposerDraft = () => {
|
|
10002
|
+
mirroredDraftRefs = [];
|
|
10003
|
+
tui.setExternalAttachmentNames([]);
|
|
10004
|
+
latestDraftPayload = { text: "", images: [] };
|
|
10005
|
+
draftDirty = true;
|
|
10006
|
+
void pumpComposerDraft();
|
|
10007
|
+
};
|
|
10008
|
+
tui.onDraftChange = (textVal, images) => {
|
|
10009
|
+
latestDraftPayload = { text: textVal, images };
|
|
10010
|
+
draftDirty = true;
|
|
10011
|
+
void pumpComposerDraft();
|
|
9825
10012
|
};
|
|
9826
10013
|
const submitComposer = async (text, images, steer) => {
|
|
9827
10014
|
const draftRefs = mirroredDraftRefs;
|
|
9828
10015
|
if (!sharedMessagingReady && (busy || steer || editingPendingId !== null)) {
|
|
9829
|
-
tui.print(`${
|
|
10016
|
+
tui.print(`${c5.dim}Restoring shared message state \u2014 try again in a moment.${c5.reset}`);
|
|
9830
10017
|
tui.setExternalAttachmentNames(draftRefs.map((attachment) => attachment.name));
|
|
9831
10018
|
tui.setInput(text, images);
|
|
9832
10019
|
return;
|
|
@@ -9849,7 +10036,7 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
|
|
|
9849
10036
|
const item = sharedMessaging.pending.items.find((candidate) => candidate.id === pendingId);
|
|
9850
10037
|
editingPendingId = null;
|
|
9851
10038
|
if (!item) {
|
|
9852
|
-
tui.print(`${
|
|
10039
|
+
tui.print(`${c5.dim}That pending message was already dispatched or dismissed.${c5.reset}`);
|
|
9853
10040
|
return;
|
|
9854
10041
|
}
|
|
9855
10042
|
const updated = await editSharedPending(item, text, images, draftRefs);
|
|
@@ -9862,7 +10049,7 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
|
|
|
9862
10049
|
return;
|
|
9863
10050
|
}
|
|
9864
10051
|
if (steer) {
|
|
9865
|
-
tui.print(`${
|
|
10052
|
+
tui.print(`${c5.yellow}\u21AA steering now \u2014 stopping the current step${c5.reset}`);
|
|
9866
10053
|
await Promise.all([
|
|
9867
10054
|
api.stopThread(threadId).catch(() => {
|
|
9868
10055
|
}),
|
|
@@ -9899,18 +10086,18 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
|
|
|
9899
10086
|
tui.onInterrupt = () => {
|
|
9900
10087
|
const firstPending = sharedMessaging.pending.items[0];
|
|
9901
10088
|
if (!busy && firstPending) {
|
|
9902
|
-
tui.print(`${
|
|
10089
|
+
tui.print(`${c5.yellow}\u21AA steering the first pending message\u2026${c5.reset}`);
|
|
9903
10090
|
void promoteSharedPending(firstPending);
|
|
9904
10091
|
return;
|
|
9905
10092
|
}
|
|
9906
10093
|
if (busy) {
|
|
9907
10094
|
const head = sharedMessaging.pending.items[0];
|
|
9908
10095
|
if (head) {
|
|
9909
|
-
tui.print(`${
|
|
10096
|
+
tui.print(`${c5.yellow}\u21AA steering \u2014 stopping the current step to run the queued message${c5.reset}`);
|
|
9910
10097
|
void promoteSharedPending(head);
|
|
9911
10098
|
return;
|
|
9912
10099
|
}
|
|
9913
|
-
tui.print(`${
|
|
10100
|
+
tui.print(`${c5.yellow}\u25A0 stopping now${c5.reset}`);
|
|
9914
10101
|
const stops = [api.stopThread(threadId).catch(() => {
|
|
9915
10102
|
})];
|
|
9916
10103
|
for (const childId of activeSubagents.keys()) {
|
|
@@ -9940,7 +10127,7 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
|
|
|
9940
10127
|
void api.getGoal(threadId).then((g) => tui.setGoal(g)).catch(() => {
|
|
9941
10128
|
});
|
|
9942
10129
|
attaching.stop();
|
|
9943
|
-
const header = `${
|
|
10130
|
+
const header = `${c5.bold}${c5.magenta}Standard Code${c5.reset} ${c5.dim}\u2014 ${agentTitle}${c5.reset}`;
|
|
9944
10131
|
const remoteDaemonV = session.runner?.daemon?.version;
|
|
9945
10132
|
const owner = currentOwner;
|
|
9946
10133
|
let localOwnerDesc = ownsExecution ? "this terminal" : describeExecOwner(owner);
|
|
@@ -9948,24 +10135,24 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
|
|
|
9948
10135
|
const selfRec = await loadMachine(api, session.identity.machine_id).catch(() => null);
|
|
9949
10136
|
if (selfRec?.daemon?.version) localOwnerDesc = `the daemon on this machine (v${selfRec.daemon.version})`;
|
|
9950
10137
|
}
|
|
9951
|
-
const execLine = remote ? `${
|
|
10138
|
+
const execLine = remote ? `${c5.gray}tool execution:${c5.reset} daemon${remoteDaemonV ? ` v${remoteDaemonV}` : ""} on ${runnerName}` : `${c5.gray}tool execution:${c5.reset} ${localOwnerDesc}`;
|
|
9952
10139
|
tui.setAgentLabel(agentTitle);
|
|
9953
10140
|
tui.banner(
|
|
9954
10141
|
remote ? [
|
|
9955
10142
|
header,
|
|
9956
|
-
`${
|
|
9957
|
-
`${
|
|
10143
|
+
`${c5.gray}project:${c5.reset} ${session.remotePath ?? "?"} ${c5.teal}on ${runnerName}${c5.reset}`,
|
|
10144
|
+
`${c5.gray}machine:${c5.reset} ${runnerName} ${c5.gray}thread:${c5.reset} ${threadId.slice(0, 8)}`,
|
|
9958
10145
|
execLine
|
|
9959
10146
|
] : [
|
|
9960
10147
|
header,
|
|
9961
|
-
`${
|
|
9962
|
-
`${
|
|
10148
|
+
`${c5.gray}project:${c5.reset} ${projectDir}`,
|
|
10149
|
+
`${c5.gray}machine:${c5.reset} ${machine} ${c5.gray}thread:${c5.reset} ${threadId.slice(0, 8)}`,
|
|
9963
10150
|
execLine
|
|
9964
10151
|
]
|
|
9965
10152
|
);
|
|
9966
10153
|
if (!remote && session.suggestDaemonInstall && process.platform !== "win32" && !serviceStatus().installed) {
|
|
9967
10154
|
tui.print(
|
|
9968
|
-
`${
|
|
10155
|
+
`${c5.dim}Tip: install the always-on daemon (${c5.reset}standardcode daemon install${c5.dim}) to start sessions on this machine from anywhere.${c5.reset}`
|
|
9969
10156
|
);
|
|
9970
10157
|
}
|
|
9971
10158
|
if (resumed) await printHistory(api, threadId, tui);
|
|
@@ -9976,17 +10163,17 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
|
|
|
9976
10163
|
const runningProcs = (await registry.list()).filter((p) => p.status === "running");
|
|
9977
10164
|
if (runningProcs.length) {
|
|
9978
10165
|
tui.print(
|
|
9979
|
-
`${
|
|
10166
|
+
`${c5.cyan}\u2699 ${runningProcs.length} background process${runningProcs.length === 1 ? "" : "es"} running:${c5.reset}`
|
|
9980
10167
|
);
|
|
9981
|
-
for (const p of runningProcs) tui.print(`${
|
|
10168
|
+
for (const p of runningProcs) tui.print(`${c5.gray} ${p.id} ${p.description || p.command}${c5.reset}`);
|
|
9982
10169
|
}
|
|
9983
10170
|
refreshBgCount();
|
|
9984
10171
|
if (exec && ownsExecution) {
|
|
9985
10172
|
for (const res of await exec.connectEnabledMcpServers()) {
|
|
9986
10173
|
if (res.ok) {
|
|
9987
|
-
tui.print(`${
|
|
10174
|
+
tui.print(`${c5.cyan}\u26A1 MCP "${res.name}" connected${c5.reset} ${c5.gray}(${res.tools} tool${res.tools === 1 ? "" : "s"})${c5.reset}`);
|
|
9988
10175
|
} else {
|
|
9989
|
-
tui.print(`${
|
|
10176
|
+
tui.print(`${c5.red}\u26A0 MCP "${res.name}" failed:${c5.reset} ${c5.gray}${res.error}${c5.reset}`);
|
|
9990
10177
|
}
|
|
9991
10178
|
}
|
|
9992
10179
|
}
|
|
@@ -10002,8 +10189,8 @@ ${c4.gray}Close another session (its slot frees within ~90s), then resend your m
|
|
|
10002
10189
|
try {
|
|
10003
10190
|
const { choice, reason } = await tui.approval(
|
|
10004
10191
|
`${request.summary}${request.permission ? `
|
|
10005
|
-
${
|
|
10006
|
-
${
|
|
10192
|
+
${c5.bold}why: ${request.permission}${c5.reset}` : ""}
|
|
10193
|
+
${c5.dim}runs on ${request.machine || runnerName}${c5.reset}`,
|
|
10007
10194
|
request.risk
|
|
10008
10195
|
);
|
|
10009
10196
|
answeredApprovals.add(request.tool_call_id);
|
|
@@ -10030,7 +10217,7 @@ ${c4.dim}runs on ${request.machine || runnerName}${c4.reset}`,
|
|
|
10030
10217
|
if (snapshot.stalled && !stalledNoticeShown) {
|
|
10031
10218
|
stalledNoticeShown = true;
|
|
10032
10219
|
tui.print(
|
|
10033
|
-
`${
|
|
10220
|
+
`${c5.yellow}\u26A0 session interrupted${c5.reset} ${c5.dim}\u2014 the last operation never finished (the runtime is attempting recovery). Send a message to continue.${c5.reset}`
|
|
10034
10221
|
);
|
|
10035
10222
|
} else if (!snapshot.stalled && stalledNoticeShown && snapshot.busy) {
|
|
10036
10223
|
stalledNoticeShown = false;
|
|
@@ -10063,7 +10250,7 @@ ${c4.dim}runs on ${request.machine || runnerName}${c4.reset}`,
|
|
|
10063
10250
|
continue;
|
|
10064
10251
|
}
|
|
10065
10252
|
if (m.role === "assistant" && text) printAssistant(tui, text);
|
|
10066
|
-
else if (m.role === "system" && text) tui.print(`${
|
|
10253
|
+
else if (m.role === "system" && text) tui.print(`${c5.dim}${text}${c5.reset}`);
|
|
10067
10254
|
else if (m.role === "user" && text) {
|
|
10068
10255
|
const pending = pendingSent.get(text) ?? 0;
|
|
10069
10256
|
if (pending > 0) {
|
|
@@ -10157,16 +10344,16 @@ ${c4.dim}runs on ${request.machine || runnerName}${c4.reset}`,
|
|
|
10157
10344
|
tui.setStep(null, 0);
|
|
10158
10345
|
tui.setBackgroundCount(0);
|
|
10159
10346
|
if (killed > 0) {
|
|
10160
|
-
tui.print(`${
|
|
10347
|
+
tui.print(`${c5.cyan}\u2699${c5.reset} Stopped ${killed} background process${killed === 1 ? "" : "es"}.`);
|
|
10161
10348
|
}
|
|
10162
|
-
tui.print(`${
|
|
10349
|
+
tui.print(`${c5.dim}\u2500\u2500 conversation cleared \u2014 starting a fresh session \u2500\u2500${c5.reset}`);
|
|
10163
10350
|
}
|
|
10164
10351
|
async function runSkillsMenu(tui, skills) {
|
|
10165
10352
|
let list;
|
|
10166
10353
|
try {
|
|
10167
10354
|
list = await skills.list();
|
|
10168
10355
|
} catch (e) {
|
|
10169
|
-
tui.print(`${
|
|
10356
|
+
tui.print(`${c5.red}\u2717 couldn't load skills:${c5.reset} ${c5.gray}${e instanceof Error ? e.message : String(e)}${c5.reset}`);
|
|
10170
10357
|
return;
|
|
10171
10358
|
}
|
|
10172
10359
|
const INSTALL = "__install__";
|
|
@@ -10177,7 +10364,7 @@ async function runSkillsMenu(tui, skills) {
|
|
|
10177
10364
|
}));
|
|
10178
10365
|
items.push({ label: "\uFF0B Install a skill\u2026", hint: "find & install", value: INSTALL });
|
|
10179
10366
|
const picked = await tui.select(
|
|
10180
|
-
`${
|
|
10367
|
+
`${c5.bold}Agent skills${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter \xB7 esc to close)${c5.reset}`,
|
|
10181
10368
|
items
|
|
10182
10369
|
);
|
|
10183
10370
|
if (!picked) return;
|
|
@@ -10190,8 +10377,8 @@ async function runSkillsMenu(tui, skills) {
|
|
|
10190
10377
|
return;
|
|
10191
10378
|
}
|
|
10192
10379
|
const skill = list.find((s) => s.name === picked);
|
|
10193
|
-
tui.print(`${
|
|
10194
|
-
const action = await tui.select(`${
|
|
10380
|
+
tui.print(`${c5.cyan}${skill.name}${c5.reset}${skill.version ? ` ${c5.dim}v${skill.version}${c5.reset}` : ""} ${c5.gray}\u2014 ${skill.description}${c5.reset}`);
|
|
10381
|
+
const action = await tui.select(`${c5.bold}${picked}${c5.reset}`, [
|
|
10195
10382
|
skill.enabled ? { label: "Disable (hide from the agent)", value: "disable" } : { label: "Enable", value: "enable" },
|
|
10196
10383
|
{ label: "View files", value: "files" },
|
|
10197
10384
|
{ label: "Remove this skill", value: "remove" },
|
|
@@ -10200,20 +10387,20 @@ async function runSkillsMenu(tui, skills) {
|
|
|
10200
10387
|
try {
|
|
10201
10388
|
if (action === "enable" || action === "disable") {
|
|
10202
10389
|
await skills.setEnabled(picked, action === "enable");
|
|
10203
|
-
tui.print(`${
|
|
10390
|
+
tui.print(`${c5.gray}${action}d ${picked}${c5.reset}`);
|
|
10204
10391
|
} else if (action === "files") {
|
|
10205
|
-
for (const f of skill.files) tui.print(` ${
|
|
10392
|
+
for (const f of skill.files) tui.print(` ${c5.gray}${f}${c5.reset}`);
|
|
10206
10393
|
} else if (action === "remove") {
|
|
10207
10394
|
await skills.remove(picked);
|
|
10208
|
-
tui.print(`${
|
|
10395
|
+
tui.print(`${c5.gray}removed ${picked}${c5.reset}`);
|
|
10209
10396
|
}
|
|
10210
10397
|
} catch (e) {
|
|
10211
|
-
tui.print(`${
|
|
10398
|
+
tui.print(`${c5.red}\u2717 ${e instanceof Error ? e.message : String(e)}${c5.reset}`);
|
|
10212
10399
|
}
|
|
10213
10400
|
}
|
|
10214
10401
|
async function runLevelMenu(tui, perm) {
|
|
10215
10402
|
const picked = await tui.select(
|
|
10216
|
-
`${
|
|
10403
|
+
`${c5.bold}Auto-accept level${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter \xB7 shift-tab cycles)${c5.reset}`,
|
|
10217
10404
|
LEVELS.map((l) => ({
|
|
10218
10405
|
label: levelLabel(l),
|
|
10219
10406
|
hint: l === tui.level ? "current" : "",
|
|
@@ -10230,16 +10417,16 @@ async function runMachinesMenu(tui, api, self) {
|
|
|
10230
10417
|
try {
|
|
10231
10418
|
machines = await loadMachines(api);
|
|
10232
10419
|
} catch (e) {
|
|
10233
|
-
tui.print(`${
|
|
10420
|
+
tui.print(`${c5.red}\u2717 couldn't load machines:${c5.reset} ${c5.gray}${e instanceof Error ? e.message : String(e)}${c5.reset}`);
|
|
10234
10421
|
return;
|
|
10235
10422
|
}
|
|
10236
10423
|
if (!machines.length) {
|
|
10237
|
-
tui.print(`${
|
|
10424
|
+
tui.print(`${c5.gray}No machines registered yet. Run standardcode on a machine (or install its daemon) to register it.${c5.reset}`);
|
|
10238
10425
|
return;
|
|
10239
10426
|
}
|
|
10240
10427
|
machines.sort((a, b) => (b.updated_at ?? 0) - (a.updated_at ?? 0));
|
|
10241
10428
|
const picked = await tui.select(
|
|
10242
|
-
`${
|
|
10429
|
+
`${c5.bold}Your machines${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter \xB7 esc)${c5.reset}`,
|
|
10243
10430
|
machines.map((m) => {
|
|
10244
10431
|
const isSelf = m.id === self.machine_id;
|
|
10245
10432
|
const online = daemonOnline(m);
|
|
@@ -10273,26 +10460,26 @@ async function manageMachine(tui, api, self, machine) {
|
|
|
10273
10460
|
options.push({ label: "Back", value: "back" });
|
|
10274
10461
|
if (!isSelf && !machine.daemon) {
|
|
10275
10462
|
tui.print(
|
|
10276
|
-
`${
|
|
10463
|
+
`${c5.dim}${machine.name} has no daemon \u2014 you can rename it here; update and project changes need its daemon installed.${c5.reset}`
|
|
10277
10464
|
);
|
|
10278
10465
|
} else if (!isSelf && machine.daemon && !online) {
|
|
10279
10466
|
tui.print(
|
|
10280
|
-
`${
|
|
10467
|
+
`${c5.dim}${machine.name}'s daemon is offline \u2014 queued changes apply when it next comes online.${c5.reset}`
|
|
10281
10468
|
);
|
|
10282
10469
|
}
|
|
10283
|
-
const action = await tui.select(`${
|
|
10470
|
+
const action = await tui.select(`${c5.bold}${machineIcon(machine)} ${machine.name}${c5.reset}`, options);
|
|
10284
10471
|
if (!action || action === "back") return;
|
|
10285
10472
|
if (action === "icon") {
|
|
10286
10473
|
const current = machine.icon ?? "";
|
|
10287
10474
|
const emoji = await tui.prompt(
|
|
10288
|
-
`${
|
|
10475
|
+
`${c5.bold}Icon for ${machine.name}${c5.reset} ${c5.dim}(paste an emoji, blank to reset)${c5.reset}`,
|
|
10289
10476
|
current
|
|
10290
10477
|
);
|
|
10291
10478
|
if (emoji !== null) {
|
|
10292
10479
|
const trimmed = emoji.trim();
|
|
10293
10480
|
await setMachineIcon(api, machine.id, trimmed);
|
|
10294
10481
|
machine.icon = trimmed || void 0;
|
|
10295
|
-
tui.print(`${
|
|
10482
|
+
tui.print(`${c5.green}\u2713${c5.reset} icon ${trimmed ? `set to ${trimmed}` : "reset"} for ${machine.name}`);
|
|
10296
10483
|
}
|
|
10297
10484
|
return manageMachine(tui, api, self, machine);
|
|
10298
10485
|
}
|
|
@@ -10308,7 +10495,7 @@ async function manageMachine(tui, api, self, machine) {
|
|
|
10308
10495
|
const name = await tui.prompt(`New name for ${machine.name}`, machine.name);
|
|
10309
10496
|
if (name === null || !name.trim()) return;
|
|
10310
10497
|
await setMachineName(api, machine.id, name.trim());
|
|
10311
|
-
tui.print(`${
|
|
10498
|
+
tui.print(`${c5.green}\u2713${c5.reset} Renamed ${c5.bold}${machine.name}${c5.reset} \u2192 ${c5.bold}${name.trim()}${c5.reset}.`);
|
|
10312
10499
|
} else if (action === "update") {
|
|
10313
10500
|
if (isSelf) {
|
|
10314
10501
|
await runUpdateCommand(tui);
|
|
@@ -10319,7 +10506,7 @@ async function manageMachine(tui, api, self, machine) {
|
|
|
10319
10506
|
]);
|
|
10320
10507
|
if (go !== "yes") return;
|
|
10321
10508
|
await dispatch("update");
|
|
10322
|
-
tui.print(`${
|
|
10509
|
+
tui.print(`${c5.green}\u2713${c5.reset} Update ${c5.gray}${applyNote} (its daemon updates and restarts on the new version).${c5.reset}`);
|
|
10323
10510
|
}
|
|
10324
10511
|
} else if (action === "projects") {
|
|
10325
10512
|
await manageMachineProjects(tui, api, self, machine, dispatch, applyNote);
|
|
@@ -10330,7 +10517,7 @@ async function manageMachineProjects(tui, api, self, machine, dispatch, applyNot
|
|
|
10330
10517
|
const paths = Object.keys(machine.projects).sort();
|
|
10331
10518
|
const projLabels = projectDisplayLabels(machine.projects);
|
|
10332
10519
|
const picked = await tui.select(
|
|
10333
|
-
`${
|
|
10520
|
+
`${c5.bold}Projects on ${machine.name}${c5.reset} ${c5.dim}(\u2191\u2193 \xB7 enter \xB7 esc)${c5.reset}`,
|
|
10334
10521
|
[
|
|
10335
10522
|
...paths.map((p) => ({
|
|
10336
10523
|
label: projLabels.get(p) ?? projectDisplayName(p, machine.projects[p]),
|
|
@@ -10351,12 +10538,12 @@ async function manageMachineProjects(tui, api, self, machine, dispatch, applyNot
|
|
|
10351
10538
|
});
|
|
10352
10539
|
if (!chosen) return;
|
|
10353
10540
|
await dispatch("add_project", { path: chosen });
|
|
10354
|
-
tui.print(`${
|
|
10541
|
+
tui.print(`${c5.green}\u2713${c5.reset} Add ${chosen} ${c5.gray}${applyNote}.${c5.reset}`);
|
|
10355
10542
|
return;
|
|
10356
10543
|
}
|
|
10357
10544
|
const project = machine.projects[picked];
|
|
10358
10545
|
const displayName = projectDisplayName(picked, project);
|
|
10359
|
-
const action = await tui.select(`${
|
|
10546
|
+
const action = await tui.select(`${c5.bold}${displayName}${c5.reset} ${c5.dim}${shortenPath(picked, 48)}${c5.reset}`, [
|
|
10360
10547
|
{ label: "Rename", hint: "display name only \u2014 the directory is untouched", value: "rename" },
|
|
10361
10548
|
{ label: "Remove from this machine's projects", hint: "doesn't delete the directory", value: "remove" },
|
|
10362
10549
|
{ label: "Back", value: "back" }
|
|
@@ -10371,60 +10558,60 @@ async function manageMachineProjects(tui, api, self, machine, dispatch, applyNot
|
|
|
10371
10558
|
await setProjectName(api, machine.id, picked, name);
|
|
10372
10559
|
const now = name.trim() || projectDisplayName(picked, null);
|
|
10373
10560
|
if (project) project.name = now;
|
|
10374
|
-
tui.print(`${
|
|
10561
|
+
tui.print(`${c5.green}\u2713${c5.reset} Renamed ${c5.bold}${displayName}${c5.reset} \u2192 ${c5.bold}${now}${c5.reset}.`);
|
|
10375
10562
|
} else {
|
|
10376
10563
|
await dispatch("remove_project", { path: picked });
|
|
10377
|
-
tui.print(`${
|
|
10564
|
+
tui.print(`${c5.green}\u2713${c5.reset} Remove ${picked} ${c5.gray}${applyNote}.${c5.reset}`);
|
|
10378
10565
|
}
|
|
10379
10566
|
}
|
|
10380
10567
|
function showDaemonInfo(tui, session) {
|
|
10381
10568
|
if (session.mode === "remote" && session.runner) {
|
|
10382
10569
|
tui.print(
|
|
10383
|
-
`${
|
|
10570
|
+
`${c5.gray}This session runs on${c5.reset} ${c5.bold}${session.runner.name}${c5.reset} ${c5.gray}(${session.runner.hostname}) \u2014 its daemon executes the tools.${c5.reset}`
|
|
10384
10571
|
);
|
|
10385
10572
|
} else {
|
|
10386
|
-
tui.print(`${
|
|
10573
|
+
tui.print(`${c5.gray}This session runs on this machine.${c5.reset}`);
|
|
10387
10574
|
}
|
|
10388
10575
|
const status = serviceStatus();
|
|
10389
10576
|
tui.print(
|
|
10390
|
-
`${
|
|
10577
|
+
`${c5.gray}Daemon on this machine:${c5.reset} ${status.installed ? status.detail : "not installed"}`
|
|
10391
10578
|
);
|
|
10392
10579
|
if (!status.installed) {
|
|
10393
10580
|
tui.print(
|
|
10394
|
-
`${
|
|
10581
|
+
`${c5.gray}Install it to start sessions on this machine from anywhere:${c5.reset} ${c5.bold}standardcode daemon install${c5.reset}`
|
|
10395
10582
|
);
|
|
10396
10583
|
tui.print(
|
|
10397
|
-
`${
|
|
10584
|
+
`${c5.dim}The daemon keeps running after you close the terminal \u2014 it self-restarts, self-updates, and executes sessions you start from other machines.${c5.reset}`
|
|
10398
10585
|
);
|
|
10399
10586
|
} else {
|
|
10400
|
-
tui.print(`${
|
|
10587
|
+
tui.print(`${c5.dim}Manage it with standardcode daemon status | uninstall | add-project <path>.${c5.reset}`);
|
|
10401
10588
|
}
|
|
10402
10589
|
}
|
|
10403
10590
|
function showKeybindings(tui) {
|
|
10404
|
-
tui.print(`${
|
|
10405
|
-
tui.print(`${
|
|
10406
|
-
tui.print(`${
|
|
10407
|
-
tui.print(`${
|
|
10408
|
-
tui.print(`${
|
|
10409
|
-
tui.print(`${
|
|
10410
|
-
tui.print(`${
|
|
10411
|
-
tui.print(`${
|
|
10412
|
-
tui.print(`${
|
|
10413
|
-
tui.print(`${
|
|
10591
|
+
tui.print(`${c5.gray}shortcuts:${c5.reset}`);
|
|
10592
|
+
tui.print(`${c5.gray} shift-tab${c5.reset} cycle auto-accept level (1\u20135)`);
|
|
10593
|
+
tui.print(`${c5.gray} shift-\u23CE${c5.reset} insert a newline (multiline input)`);
|
|
10594
|
+
tui.print(`${c5.gray} option-\u23CE${c5.reset} steer now \u2014 stops the current step and plays your message`);
|
|
10595
|
+
tui.print(`${c5.gray} /${c5.reset} open the command palette (type to filter)`);
|
|
10596
|
+
tui.print(`${c5.gray} !cmd${c5.reset} run a shell command on the session's machine (e.g. !ls); !! to send a literal !`);
|
|
10597
|
+
tui.print(`${c5.gray} ctrl-v${c5.reset} paste an image from the clipboard ([#Image 1])`);
|
|
10598
|
+
tui.print(`${c5.gray} \u2191 / \u2193${c5.reset} cycle past messages (on the input's top line)`);
|
|
10599
|
+
tui.print(`${c5.gray} \u2190${c5.reset} from the start of the input: select the [\u2699 n bg] badge (enter opens it)`);
|
|
10600
|
+
tui.print(`${c5.gray} ctrl-c${c5.reset} quit`);
|
|
10414
10601
|
}
|
|
10415
10602
|
async function runUpdateCommand(tui) {
|
|
10416
10603
|
const version = readVersion();
|
|
10417
10604
|
const result = await forceCheckForUpdate(version);
|
|
10418
10605
|
if (!result) {
|
|
10419
|
-
tui.print(`${
|
|
10606
|
+
tui.print(`${c5.green}\u2713${c5.reset} ${c5.gray}@standardagents/code${c5.reset} is up to date (v${version})`);
|
|
10420
10607
|
return;
|
|
10421
10608
|
}
|
|
10422
10609
|
const { latest } = result;
|
|
10423
10610
|
tui.print(`
|
|
10424
|
-
${
|
|
10611
|
+
${c5.yellow}\u27F3${c5.reset} Update available: ${c5.gray}v${version}${c5.reset} \u2192 ${c5.green}v${latest}${c5.reset}`);
|
|
10425
10612
|
const pm = detectPackageManager();
|
|
10426
10613
|
if (!pm) {
|
|
10427
|
-
tui.print(` ${
|
|
10614
|
+
tui.print(` ${c5.gray}This is a source checkout \u2014 pull the repo to update.${c5.reset}`);
|
|
10428
10615
|
return;
|
|
10429
10616
|
}
|
|
10430
10617
|
const { display } = updateCommand(pm);
|
|
@@ -10433,28 +10620,28 @@ async function runUpdateCommand(tui) {
|
|
|
10433
10620
|
{ label: "No, skip", value: "no" }
|
|
10434
10621
|
]);
|
|
10435
10622
|
if (choice === "yes") {
|
|
10436
|
-
tui.print(` ${
|
|
10623
|
+
tui.print(` ${c5.gray}Running ${display}\u2026${c5.reset}`);
|
|
10437
10624
|
const { ok, output: pmOutput } = await runUpdate(pm);
|
|
10438
10625
|
if (ok) {
|
|
10439
|
-
tui.print(` ${
|
|
10626
|
+
tui.print(` ${c5.green}\u2713${c5.reset} Updated to v${latest}. Restart to use the new version.`);
|
|
10440
10627
|
} else {
|
|
10441
|
-
tui.print(` ${
|
|
10628
|
+
tui.print(` ${c5.red}\u2717${c5.reset} Update failed:`);
|
|
10442
10629
|
for (const line of pmOutput.trim().split("\n").slice(-6)) {
|
|
10443
|
-
tui.print(` ${
|
|
10630
|
+
tui.print(` ${c5.dim}${line}${c5.reset}`);
|
|
10444
10631
|
}
|
|
10445
10632
|
}
|
|
10446
10633
|
} else {
|
|
10447
|
-
tui.print(` ${
|
|
10634
|
+
tui.print(` ${c5.gray}Skipped. Run /update later.${c5.reset}`);
|
|
10448
10635
|
}
|
|
10449
10636
|
}
|
|
10450
10637
|
async function runProcessMenu(tui, bg) {
|
|
10451
10638
|
const procs = await bg.list();
|
|
10452
10639
|
if (!procs.length) {
|
|
10453
|
-
tui.print(`${
|
|
10640
|
+
tui.print(`${c5.gray}No background processes for this session.${c5.reset}`);
|
|
10454
10641
|
return;
|
|
10455
10642
|
}
|
|
10456
10643
|
const items = procs.map((p) => {
|
|
10457
|
-
const status = p.status === "running" ? `${
|
|
10644
|
+
const status = p.status === "running" ? `${c5.green}running${c5.reset}` : `${c5.gray}${p.status}${typeof p.exitCode === "number" ? ` (exit ${p.exitCode})` : ""}${c5.reset}`;
|
|
10458
10645
|
return {
|
|
10459
10646
|
label: `${p.description || p.command}`,
|
|
10460
10647
|
hint: `${p.id} \xB7 ${status}`,
|
|
@@ -10462,22 +10649,22 @@ async function runProcessMenu(tui, bg) {
|
|
|
10462
10649
|
};
|
|
10463
10650
|
});
|
|
10464
10651
|
const picked = await tui.select(
|
|
10465
|
-
`${
|
|
10652
|
+
`${c5.bold}Background processes${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter to manage \xB7 esc to close)${c5.reset}`,
|
|
10466
10653
|
items
|
|
10467
10654
|
);
|
|
10468
10655
|
if (!picked) return;
|
|
10469
10656
|
const proc = procs.find((p) => p.id === picked);
|
|
10470
10657
|
if (!proc || proc.status !== "running") {
|
|
10471
|
-
tui.print(`${
|
|
10658
|
+
tui.print(`${c5.gray}${picked} is not running.${c5.reset}`);
|
|
10472
10659
|
return;
|
|
10473
10660
|
}
|
|
10474
|
-
const action = await tui.select(`${
|
|
10661
|
+
const action = await tui.select(`${c5.bold}${proc.description || proc.command}${c5.reset}`, [
|
|
10475
10662
|
{ label: "Stop this process", value: "stop" },
|
|
10476
10663
|
{ label: "Leave it running", value: "leave" }
|
|
10477
10664
|
]);
|
|
10478
10665
|
if (action === "stop") {
|
|
10479
10666
|
await bg.stop(picked);
|
|
10480
|
-
tui.print(`${
|
|
10667
|
+
tui.print(`${c5.gray}stopped ${picked}${c5.reset}`);
|
|
10481
10668
|
}
|
|
10482
10669
|
}
|
|
10483
10670
|
async function runApprovalsMenu(tui, perm, save) {
|
|
@@ -10485,7 +10672,7 @@ async function runApprovalsMenu(tui, perm, save) {
|
|
|
10485
10672
|
const risks = Array.from(perm.allowRisk).sort((a, b) => a - b);
|
|
10486
10673
|
if (!tools.length && !risks.length) {
|
|
10487
10674
|
tui.print(
|
|
10488
|
-
`${
|
|
10675
|
+
`${c5.gray}No pre-approved commands. At a permission prompt, choose "Always allow this tool" or "Allow all level N" to add some.${c5.reset}`
|
|
10489
10676
|
);
|
|
10490
10677
|
return;
|
|
10491
10678
|
}
|
|
@@ -10495,22 +10682,22 @@ async function runApprovalsMenu(tui, perm, save) {
|
|
|
10495
10682
|
{ label: "Clear all approvals", hint: "", value: "clear" }
|
|
10496
10683
|
];
|
|
10497
10684
|
const picked = await tui.select(
|
|
10498
|
-
`${
|
|
10685
|
+
`${c5.bold}Approved commands${c5.reset} ${c5.dim}(enter to revoke \xB7 esc to close)${c5.reset}`,
|
|
10499
10686
|
items
|
|
10500
10687
|
);
|
|
10501
10688
|
if (!picked) return;
|
|
10502
10689
|
if (picked === "clear") {
|
|
10503
10690
|
perm.alwaysAllow.clear();
|
|
10504
10691
|
perm.allowRisk.clear();
|
|
10505
|
-
tui.print(`${
|
|
10692
|
+
tui.print(`${c5.gray}cleared all approvals${c5.reset}`);
|
|
10506
10693
|
} else if (picked.startsWith("tool:")) {
|
|
10507
10694
|
const t = picked.slice(5);
|
|
10508
10695
|
perm.alwaysAllow.delete(t);
|
|
10509
|
-
tui.print(`${
|
|
10696
|
+
tui.print(`${c5.gray}revoked tool ${t}${c5.reset}`);
|
|
10510
10697
|
} else if (picked.startsWith("risk:")) {
|
|
10511
10698
|
const r = Number(picked.slice(5));
|
|
10512
10699
|
perm.allowRisk.delete(r);
|
|
10513
|
-
tui.print(`${
|
|
10700
|
+
tui.print(`${c5.gray}revoked level ${r}${c5.reset}`);
|
|
10514
10701
|
}
|
|
10515
10702
|
save();
|
|
10516
10703
|
}
|
|
@@ -10528,7 +10715,7 @@ async function runMcpMenu(tui, mcp) {
|
|
|
10528
10715
|
items.push({ label: "\uFF0B Install a new MCP server\u2026", hint: "find & install", value: INSTALL });
|
|
10529
10716
|
items.push({ label: "Add manually (name: command)\u2026", hint: "advanced", value: ADD_MANUAL });
|
|
10530
10717
|
const picked = await tui.select(
|
|
10531
|
-
`${
|
|
10718
|
+
`${c5.bold}MCP servers${c5.reset} ${c5.dim}(\u2191/\u2193 \xB7 enter \xB7 esc to close)${c5.reset}`,
|
|
10532
10719
|
items
|
|
10533
10720
|
);
|
|
10534
10721
|
if (!picked) return;
|
|
@@ -10542,7 +10729,7 @@ async function runMcpMenu(tui, mcp) {
|
|
|
10542
10729
|
}
|
|
10543
10730
|
const server = configured.find((s) => s.name === picked);
|
|
10544
10731
|
const isConnected = connected.has(picked);
|
|
10545
|
-
const action = await tui.select(`${
|
|
10732
|
+
const action = await tui.select(`${c5.bold}${picked}${c5.reset}`, [
|
|
10546
10733
|
{ label: "View tools", value: "tools" },
|
|
10547
10734
|
isConnected ? { label: "Disconnect", value: "disconnect" } : { label: "Connect", value: "connect" },
|
|
10548
10735
|
server.enabled ? { label: "Disable (don't auto-connect)", value: "disable" } : { label: "Enable (auto-connect on start)", value: "enable" },
|
|
@@ -10552,29 +10739,29 @@ async function runMcpMenu(tui, mcp) {
|
|
|
10552
10739
|
if (action === "tools") {
|
|
10553
10740
|
const entry = mcp.catalog().servers.find((e) => e.name === picked);
|
|
10554
10741
|
if (!entry || entry.status !== "connected") {
|
|
10555
|
-
tui.print(`${
|
|
10742
|
+
tui.print(`${c5.gray}${picked} is not connected \u2014 connect it to list tools.${c5.reset}`);
|
|
10556
10743
|
return;
|
|
10557
10744
|
}
|
|
10558
|
-
if (!entry.tools.length) tui.print(`${
|
|
10559
|
-
for (const t of entry.tools) tui.print(` ${
|
|
10560
|
-
if (entry.resources.length) tui.print(` ${
|
|
10745
|
+
if (!entry.tools.length) tui.print(`${c5.gray}${picked} exposes no tools.${c5.reset}`);
|
|
10746
|
+
for (const t of entry.tools) tui.print(` ${c5.cyan}${t.name}${c5.reset}${t.description ? ` ${c5.gray}\u2014 ${t.description}${c5.reset}` : ""}`);
|
|
10747
|
+
if (entry.resources.length) tui.print(` ${c5.gray}${entry.resources.length} resource(s)${c5.reset}`);
|
|
10561
10748
|
} else if (action === "connect") {
|
|
10562
10749
|
const res = await mcp.connect(server);
|
|
10563
|
-
tui.print(res.ok ? `${
|
|
10750
|
+
tui.print(res.ok ? `${c5.cyan}\u26A1 connected (${res.tools} tools)${c5.reset}` : `${c5.red}\u26A0 ${res.error}${c5.reset}`);
|
|
10564
10751
|
} else if (action === "disconnect") {
|
|
10565
10752
|
mcp.disconnect(picked);
|
|
10566
|
-
tui.print(`${
|
|
10753
|
+
tui.print(`${c5.gray}disconnected ${picked}${c5.reset}`);
|
|
10567
10754
|
} else if (action === "enable") {
|
|
10568
10755
|
mcp.setEnabled(picked, true);
|
|
10569
10756
|
const res = await mcp.connect(server);
|
|
10570
|
-
tui.print(res.ok ? `${
|
|
10757
|
+
tui.print(res.ok ? `${c5.cyan}\u26A1 enabled + connected (${res.tools} tools)${c5.reset}` : `${c5.red}\u26A0 enabled but failed: ${res.error}${c5.reset}`);
|
|
10571
10758
|
} else if (action === "disable") {
|
|
10572
10759
|
mcp.setEnabled(picked, false);
|
|
10573
10760
|
mcp.disconnect(picked);
|
|
10574
|
-
tui.print(`${
|
|
10761
|
+
tui.print(`${c5.gray}disabled + disconnected ${picked}${c5.reset}`);
|
|
10575
10762
|
} else if (action === "remove") {
|
|
10576
10763
|
mcp.remove(picked);
|
|
10577
|
-
tui.print(`${
|
|
10764
|
+
tui.print(`${c5.gray}removed ${picked}${c5.reset}`);
|
|
10578
10765
|
}
|
|
10579
10766
|
}
|
|
10580
10767
|
async function addMcpServer(tui, mcp) {
|
|
@@ -10585,13 +10772,13 @@ async function addMcpServer(tui, mcp) {
|
|
|
10585
10772
|
if (!spec) return;
|
|
10586
10773
|
const cfg = parseServerSpec(spec);
|
|
10587
10774
|
if (!cfg) {
|
|
10588
|
-
tui.print(`${
|
|
10775
|
+
tui.print(`${c5.yellow}couldn't parse that. Use name: command [args]${c5.reset}`);
|
|
10589
10776
|
return;
|
|
10590
10777
|
}
|
|
10591
|
-
tui.print(`${
|
|
10778
|
+
tui.print(`${c5.gray}connecting MCP "${cfg.name}" (${cfg.command} ${cfg.args.join(" ")})\u2026${c5.reset}`);
|
|
10592
10779
|
const res = await mcp.add(cfg);
|
|
10593
|
-
if (res.ok) tui.print(`${
|
|
10594
|
-
else tui.print(`${
|
|
10780
|
+
if (res.ok) tui.print(`${c5.cyan}\u26A1 MCP "${cfg.name}" connected${c5.reset} ${c5.gray}(${res.tools} tool${res.tools === 1 ? "" : "s"})${c5.reset}`);
|
|
10781
|
+
else tui.print(`${c5.red}\u26A0 MCP "${cfg.name}" failed:${c5.reset} ${c5.gray}${res.error}${c5.reset} ${c5.dim}(saved; retry from the MCP menu)${c5.reset}`);
|
|
10595
10782
|
}
|
|
10596
10783
|
async function installMcpServerFlow(tui, mcp) {
|
|
10597
10784
|
const query = await tui.prompt(
|