@4pm/cli 1.5.1 → 1.5.3
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 +215 -39
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28834,18 +28834,21 @@ function isValidRoleSet(roles) {
|
|
|
28834
28834
|
|
|
28835
28835
|
// vendor/@4pm/constants/worker-tools.ts
|
|
28836
28836
|
var WORKER_TOOL_CATALOG = [
|
|
28837
|
-
{ id: "git", label: "Git", category: "vcs", versionArg: "--version", installPackage: null, installable: false },
|
|
28838
|
-
{ id: "node", label: "Node.js", category: "runtime", versionArg: "--version", installPackage: null, installable: false },
|
|
28839
|
-
{ id: "npm", label: "npm", category: "runtime", versionArg: "--version", installPackage: null, installable: false },
|
|
28840
|
-
{ id: "pnpm", label: "pnpm", category: "runtime", versionArg: "--version", installPackage: "pnpm", installable: false },
|
|
28841
|
-
{ id: "claude", label: "Claude Code", category: "ai-cli", versionArg: "--version", installPackage: "@anthropic-ai/claude-code", installable: false },
|
|
28842
|
-
{ id: "codex", label: "Codex", category: "ai-cli", versionArg: "--version", installPackage: "@openai/codex", installable: false },
|
|
28843
|
-
{ id: "gh", label: "GitHub CLI", category: "vcs", versionArg: "--version", installPackage: null, installable: false },
|
|
28844
|
-
{ id: "glab", label: "GitLab CLI", category: "vcs", versionArg: "--version", installPackage: null, installable: false }
|
|
28837
|
+
{ id: "git", label: "Git", category: "vcs", versionArg: "--version", installPackage: null, installable: false, updatable: false },
|
|
28838
|
+
{ id: "node", label: "Node.js", category: "runtime", versionArg: "--version", installPackage: null, installable: false, updatable: false },
|
|
28839
|
+
{ id: "npm", label: "npm", category: "runtime", versionArg: "--version", installPackage: null, installable: false, updatable: false },
|
|
28840
|
+
{ id: "pnpm", label: "pnpm", category: "runtime", versionArg: "--version", installPackage: "pnpm", installable: false, updatable: true },
|
|
28841
|
+
{ id: "claude", label: "Claude Code", category: "ai-cli", versionArg: "--version", installPackage: "@anthropic-ai/claude-code", installable: false, updatable: true },
|
|
28842
|
+
{ id: "codex", label: "Codex", category: "ai-cli", versionArg: "--version", installPackage: "@openai/codex", installable: false, updatable: true },
|
|
28843
|
+
{ id: "gh", label: "GitHub CLI", category: "vcs", versionArg: "--version", installPackage: null, installable: false, updatable: false },
|
|
28844
|
+
{ id: "glab", label: "GitLab CLI", category: "vcs", versionArg: "--version", installPackage: null, installable: false, updatable: false }
|
|
28845
28845
|
];
|
|
28846
28846
|
var WORKER_TOOL_PREREQUISITE_IDS = WORKER_TOOL_CATALOG.filter(
|
|
28847
28847
|
(t) => !t.installable
|
|
28848
28848
|
).map((t) => t.id);
|
|
28849
|
+
var WORKER_TOOL_UPDATABLE_IDS = WORKER_TOOL_CATALOG.filter(
|
|
28850
|
+
(t) => t.updatable
|
|
28851
|
+
).map((t) => t.id);
|
|
28849
28852
|
var WORKER_TOOL_BUNDLED_GLOBALS = ["npm", "corepack"];
|
|
28850
28853
|
|
|
28851
28854
|
// vendor/@4pm/constants/ws.ts
|
|
@@ -32990,6 +32993,7 @@ var CLI_SLASH_COMMANDS = [
|
|
|
32990
32993
|
{ name: "collapse", usage: "/collapse [N]", description: "Collapse an expanded \u25B8[N] block" },
|
|
32991
32994
|
{ name: "reconnect", usage: "/reconnect", description: "Reconnect to the server now (skip the backoff)" },
|
|
32992
32995
|
{ name: "whoami", usage: "/whoami", description: "Show this machine account + its teams & projects" },
|
|
32996
|
+
{ name: "ai-profile", usage: "/ai-profile [list|use <n>|reset]", description: "List AI profiles / switch which one runs" },
|
|
32993
32997
|
{ name: "config", usage: "/config [show|init|set <k> <v>|delete <k>]", description: "View / init / update / delete profile config" },
|
|
32994
32998
|
{ name: "claude-cmd", usage: "/claude-cmd /context", description: "Run an AI-CLI slash command (e.g. /context, /usage)" },
|
|
32995
32999
|
{ name: "clear", usage: "/clear", description: "Clear the transcript" },
|
|
@@ -33038,7 +33042,10 @@ var listWorkersQuerySchema = baseRequestSchema.extend({
|
|
|
33038
33042
|
/** Only workers whose live CPU/memory/disk usage % ≥ the given threshold (0–100). */
|
|
33039
33043
|
minCpuPct: external_exports.coerce.number().min(0).max(100).optional(),
|
|
33040
33044
|
minMemPct: external_exports.coerce.number().min(0).max(100).optional(),
|
|
33041
|
-
minDiskPct: external_exports.coerce.number().min(0).max(100).optional()
|
|
33045
|
+
minDiskPct: external_exports.coerce.number().min(0).max(100).optional(),
|
|
33046
|
+
/** Hide workers with no cli attached (clis = 0). The web defaults this on; unchecking the
|
|
33047
|
+
* "show empty CLIs" box drops it so empty workers are listed too. */
|
|
33048
|
+
hideEmptyClis: external_exports.enum(["true", "false"]).transform((v) => v === "true").optional()
|
|
33042
33049
|
});
|
|
33043
33050
|
var updateWorkerRequestSchema = external_exports.object({
|
|
33044
33051
|
name: external_exports.string().min(1).max(100).optional()
|
|
@@ -33671,6 +33678,9 @@ var WsChannels = {
|
|
|
33671
33678
|
TOOLS_LIST: "tools.list",
|
|
33672
33679
|
TOOLS_INSTALL: "tools.install",
|
|
33673
33680
|
TOOLS_UNINSTALL: "tools.uninstall",
|
|
33681
|
+
// update-to-latest of an npm-distributed catalog tool or an extra (ADR-0252): same streamed
|
|
33682
|
+
// shape as install/uninstall — the cli reinstalls the package at `@latest`.
|
|
33683
|
+
TOOLS_UPDATE: "tools.update",
|
|
33674
33684
|
TOOLS_PROGRESS: "tools.progress",
|
|
33675
33685
|
TOOLS_DONE: "tools.done",
|
|
33676
33686
|
// autonomous mode control (ADR-0152): read settings+status+books+approvals, tail logs, and
|
|
@@ -34051,11 +34061,29 @@ import { spawn as spawn2 } from "child_process";
|
|
|
34051
34061
|
var KILL_GRACE_MS = 5e3;
|
|
34052
34062
|
async function runCommand(dispatch, emit, opts) {
|
|
34053
34063
|
let seq = 0;
|
|
34054
|
-
|
|
34055
|
-
|
|
34056
|
-
|
|
34057
|
-
|
|
34064
|
+
let child;
|
|
34065
|
+
try {
|
|
34066
|
+
child = spawn2(dispatch.cmd, dispatch.args ?? [], {
|
|
34067
|
+
cwd: dispatch.path,
|
|
34068
|
+
env: { ...process.env, ...dispatch.env },
|
|
34069
|
+
shell: false
|
|
34070
|
+
});
|
|
34071
|
+
} catch (err) {
|
|
34072
|
+
emit({
|
|
34073
|
+
commandId: dispatch.commandId,
|
|
34074
|
+
seq: seq++,
|
|
34075
|
+
chunk: `spawn error: ${err.message}
|
|
34076
|
+
`,
|
|
34077
|
+
done: true,
|
|
34078
|
+
exitCode: -1
|
|
34079
|
+
});
|
|
34080
|
+
return;
|
|
34081
|
+
}
|
|
34082
|
+
child.stdin?.on("error", () => {
|
|
34058
34083
|
});
|
|
34084
|
+
if (opts?.stdin != null) {
|
|
34085
|
+
child.stdin?.write(opts.stdin);
|
|
34086
|
+
}
|
|
34059
34087
|
child.stdin?.end();
|
|
34060
34088
|
const timeoutMs = opts?.timeoutMs && opts.timeoutMs > 0 ? opts.timeoutMs : 0;
|
|
34061
34089
|
let timedOut = false;
|
|
@@ -34255,12 +34283,16 @@ function isOutOfCredits(text) {
|
|
|
34255
34283
|
text
|
|
34256
34284
|
);
|
|
34257
34285
|
}
|
|
34258
|
-
function runAttempt(commandId, cmd, args, cwd2, env3, onChunk, timeoutMs) {
|
|
34286
|
+
function runAttempt(commandId, cmd, args, stdin, cwd2, env3, onChunk, timeoutMs) {
|
|
34259
34287
|
return new Promise((resolve8) => {
|
|
34260
|
-
|
|
34261
|
-
|
|
34262
|
-
|
|
34263
|
-
|
|
34288
|
+
runCommand(
|
|
34289
|
+
{ commandId, cmd, args, path: cwd2, env: env3 },
|
|
34290
|
+
(out) => {
|
|
34291
|
+
if (out.chunk) onChunk(out.chunk);
|
|
34292
|
+
if (out.done) resolve8(out.exitCode ?? -1);
|
|
34293
|
+
},
|
|
34294
|
+
{ stdin, ...timeoutMs > 0 ? { timeoutMs } : {} }
|
|
34295
|
+
).catch(() => resolve8(-1));
|
|
34264
34296
|
});
|
|
34265
34297
|
}
|
|
34266
34298
|
async function runAiFailover(plan, commandId, cwd2, handlers, timeoutMs = 0) {
|
|
@@ -34281,7 +34313,7 @@ async function runAiFailover(plan, commandId, cwd2, handlers, timeoutMs = 0) {
|
|
|
34281
34313
|
handlers.onChunk(text);
|
|
34282
34314
|
}
|
|
34283
34315
|
};
|
|
34284
|
-
finalExit = await runAttempt(commandId, attempt.cmd, attempt.args, cwd2, attempt.env, emit, timeoutMs);
|
|
34316
|
+
finalExit = await runAttempt(commandId, attempt.cmd, attempt.args, attempt.stdin, cwd2, attempt.env, emit, timeoutMs);
|
|
34285
34317
|
const tail = parser.flush();
|
|
34286
34318
|
if (tail) {
|
|
34287
34319
|
captured += tail;
|
|
@@ -34358,6 +34390,52 @@ function setWorkingCredential(profileDir2, key) {
|
|
|
34358
34390
|
} catch {
|
|
34359
34391
|
}
|
|
34360
34392
|
}
|
|
34393
|
+
var PINNED_CREDENTIAL_KEY = "__pinned__";
|
|
34394
|
+
function getPinnedCredential(profileDir2) {
|
|
34395
|
+
return readState(profileDir2)[PINNED_CREDENTIAL_KEY] ?? null;
|
|
34396
|
+
}
|
|
34397
|
+
function setPinnedCredential(profileDir2, key) {
|
|
34398
|
+
const state = readState(profileDir2);
|
|
34399
|
+
if (state[PINNED_CREDENTIAL_KEY] === key) return;
|
|
34400
|
+
state[PINNED_CREDENTIAL_KEY] = key;
|
|
34401
|
+
try {
|
|
34402
|
+
writeFileSync4(statePath(profileDir2), JSON.stringify(state, null, 2), "utf8");
|
|
34403
|
+
} catch {
|
|
34404
|
+
}
|
|
34405
|
+
}
|
|
34406
|
+
function clearPinnedCredential(profileDir2) {
|
|
34407
|
+
const state = readState(profileDir2);
|
|
34408
|
+
if (!(PINNED_CREDENTIAL_KEY in state)) return;
|
|
34409
|
+
delete state[PINNED_CREDENTIAL_KEY];
|
|
34410
|
+
try {
|
|
34411
|
+
writeFileSync4(statePath(profileDir2), JSON.stringify(state, null, 2), "utf8");
|
|
34412
|
+
} catch {
|
|
34413
|
+
}
|
|
34414
|
+
}
|
|
34415
|
+
|
|
34416
|
+
// src/utils/time.ts
|
|
34417
|
+
function formatTimestampInZone(date, timeZone) {
|
|
34418
|
+
const build = (tz) => {
|
|
34419
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
34420
|
+
...tz ? { timeZone: tz } : {},
|
|
34421
|
+
hour12: false,
|
|
34422
|
+
year: "numeric",
|
|
34423
|
+
month: "2-digit",
|
|
34424
|
+
day: "2-digit",
|
|
34425
|
+
hour: "2-digit",
|
|
34426
|
+
minute: "2-digit",
|
|
34427
|
+
second: "2-digit"
|
|
34428
|
+
}).formatToParts(date);
|
|
34429
|
+
const get2 = (type) => parts.find((p) => p.type === type)?.value ?? "";
|
|
34430
|
+
const hour = get2("hour") === "24" ? "00" : get2("hour");
|
|
34431
|
+
return `${get2("year")}/${get2("month")}/${get2("day")} ${hour}:${get2("minute")}:${get2("second")}`;
|
|
34432
|
+
};
|
|
34433
|
+
try {
|
|
34434
|
+
return build(timeZone);
|
|
34435
|
+
} catch {
|
|
34436
|
+
return build();
|
|
34437
|
+
}
|
|
34438
|
+
}
|
|
34361
34439
|
|
|
34362
34440
|
// src/utils/ai-cli.ts
|
|
34363
34441
|
import { readFileSync as readFileSync8 } from "fs";
|
|
@@ -34463,7 +34541,7 @@ function folderScopeGuard(folder, prompt) {
|
|
|
34463
34541
|
prompt
|
|
34464
34542
|
].join("\n");
|
|
34465
34543
|
}
|
|
34466
|
-
function buildRunArgs(profile, cmd,
|
|
34544
|
+
function buildRunArgs(profile, cmd, resumeId, oneShot = false) {
|
|
34467
34545
|
const extras = profile.args ?? [];
|
|
34468
34546
|
const model = profile.model?.trim();
|
|
34469
34547
|
const modelArgs = model ? ["--model", model] : [];
|
|
@@ -34471,7 +34549,7 @@ function buildRunArgs(profile, cmd, prompt, resumeId, oneShot = false) {
|
|
|
34471
34549
|
const turns = profile.maxTurns;
|
|
34472
34550
|
const perProfileTurns = !oneShot && cmd.includes("claude") && typeof turns === "number" && turns > 0 ? [`--max-turns=${Math.floor(turns)}`] : [];
|
|
34473
34551
|
const oneShotExtra = oneShot ? oneShotArgs(cmd) : perProfileTurns;
|
|
34474
|
-
return [...requiredArgs(cmd), ...resumeArgs, ...extras, ...modelArgs, ...oneShotExtra
|
|
34552
|
+
return [...requiredArgs(cmd), ...resumeArgs, ...extras, ...modelArgs, ...oneShotExtra];
|
|
34475
34553
|
}
|
|
34476
34554
|
function claudeHomeDirs(config) {
|
|
34477
34555
|
const dirs = profileDirs(claudeProfilesOf(config));
|
|
@@ -34522,7 +34600,7 @@ function planUnifiedRun(prompt, config, workingCredential, resume, oneShot) {
|
|
|
34522
34600
|
return {
|
|
34523
34601
|
cmd: active,
|
|
34524
34602
|
attempts: [
|
|
34525
|
-
{ cmd: active, label: "default", dir: null, key: null, args: buildRunArgs({ profile: "" }, active,
|
|
34603
|
+
{ cmd: active, label: "default", dir: null, key: null, args: buildRunArgs({ profile: "" }, active, void 0, oneShot), stdin: prompt, env: { ...baseEnv } }
|
|
34526
34604
|
]
|
|
34527
34605
|
};
|
|
34528
34606
|
}
|
|
@@ -34540,7 +34618,8 @@ function planUnifiedRun(prompt, config, workingCredential, resume, oneShot) {
|
|
|
34540
34618
|
label: cred.label?.trim() || profileDisplayLabel(dir2),
|
|
34541
34619
|
dir: dir2,
|
|
34542
34620
|
key,
|
|
34543
|
-
args: buildRunArgs(cred, cmd,
|
|
34621
|
+
args: buildRunArgs(cred, cmd, resume.get(key), oneShot),
|
|
34622
|
+
stdin: prompt,
|
|
34544
34623
|
env: envVar ? { ...baseEnv, [envVar]: dir2 } : { ...baseEnv }
|
|
34545
34624
|
}));
|
|
34546
34625
|
return { cmd: attempts[0]?.cmd ?? DEFAULT_AI_CLI, attempts };
|
|
@@ -34552,7 +34631,7 @@ function planLegacyRun(prompt, config, workingDir, resume, oneShot) {
|
|
|
34552
34631
|
const defaultPlan = {
|
|
34553
34632
|
cmd,
|
|
34554
34633
|
attempts: [
|
|
34555
|
-
{ cmd, label: "default", dir: null, key: null, args: buildRunArgs({ profile: "" }, cmd,
|
|
34634
|
+
{ cmd, label: "default", dir: null, key: null, args: buildRunArgs({ profile: "" }, cmd, void 0, oneShot), stdin: prompt, env: { ...baseEnv } }
|
|
34556
34635
|
]
|
|
34557
34636
|
};
|
|
34558
34637
|
if (!envVar) return defaultPlan;
|
|
@@ -34569,7 +34648,8 @@ function planLegacyRun(prompt, config, workingDir, resume, oneShot) {
|
|
|
34569
34648
|
label: profileDisplayLabel(dir2),
|
|
34570
34649
|
dir: dir2,
|
|
34571
34650
|
key,
|
|
34572
|
-
args: buildRunArgs(profile, cmd,
|
|
34651
|
+
args: buildRunArgs(profile, cmd, resume.get(key), oneShot),
|
|
34652
|
+
stdin: prompt,
|
|
34573
34653
|
env: { ...baseEnv, [envVar]: dir2 }
|
|
34574
34654
|
};
|
|
34575
34655
|
});
|
|
@@ -36948,6 +37028,70 @@ function runWhoami(ctx) {
|
|
|
36948
37028
|
ctx.print(`projects: ${w.projects.length ? w.projects.map((p) => p.name).join(", ") : "(none)"}`);
|
|
36949
37029
|
});
|
|
36950
37030
|
}
|
|
37031
|
+
function aiProfileLabel(c) {
|
|
37032
|
+
return c.label?.trim() || profileDisplayLabel(resolveHomePath(c.profile));
|
|
37033
|
+
}
|
|
37034
|
+
function runAiProfile(ctx) {
|
|
37035
|
+
const dir2 = ctx.info.profileDir;
|
|
37036
|
+
const config = readProfileConfig(dir2);
|
|
37037
|
+
const list = Array.isArray(config.aiProfiles) ? config.aiProfiles : [];
|
|
37038
|
+
const [sub, ...rest] = ctx.args;
|
|
37039
|
+
const action = (sub ?? "list").toLowerCase();
|
|
37040
|
+
if (action === "list") {
|
|
37041
|
+
if (list.length === 0) {
|
|
37042
|
+
ctx.print("No AI profiles configured (config.aiProfiles is empty).", "warn");
|
|
37043
|
+
ctx.print("This worker may use a legacy per-provider config \u2014 /config show to inspect.");
|
|
37044
|
+
return;
|
|
37045
|
+
}
|
|
37046
|
+
const pinned = getPinnedCredential(dir2);
|
|
37047
|
+
const working = getWorkingCredential(dir2);
|
|
37048
|
+
ctx.print("AI profiles (config order = failover priority) \xB7 /ai-profile use <n> to switch:");
|
|
37049
|
+
list.forEach((c, i) => {
|
|
37050
|
+
const usable = isUsableCredential(c);
|
|
37051
|
+
const key = usable ? credentialKey(c.provider, resolveHomePath(c.profile)) : null;
|
|
37052
|
+
const flags = [];
|
|
37053
|
+
if (key && key === pinned) flags.push("pinned");
|
|
37054
|
+
else if (key && !pinned && key === working) flags.push("active");
|
|
37055
|
+
if (c.enabled === false) flags.push("disabled");
|
|
37056
|
+
const model = c.model?.trim() ? ` \xB7 model=${c.model.trim()}` : "";
|
|
37057
|
+
const flagStr = flags.length ? ` (${flags.join(", ")})` : "";
|
|
37058
|
+
const marker = key && key === pinned ? "*" : " ";
|
|
37059
|
+
ctx.print(` ${marker}[${i + 1}] ${c.provider.padEnd(11)} ${aiProfileLabel(c)}${model}${flagStr}`);
|
|
37060
|
+
});
|
|
37061
|
+
if (pinned) {
|
|
37062
|
+
ctx.print("A profile is pinned \u2014 tried first every prompt; /ai-profile reset returns to auto.");
|
|
37063
|
+
}
|
|
37064
|
+
return;
|
|
37065
|
+
}
|
|
37066
|
+
if (action === "reset" || action === "auto" || action === "clear") {
|
|
37067
|
+
clearPinnedCredential(dir2);
|
|
37068
|
+
ctx.print("\u2714 AI profile pin cleared \u2014 failover is back to automatic (config order / remembered).");
|
|
37069
|
+
const working = getWorkingCredential(dir2);
|
|
37070
|
+
ctx.setActiveProfile(working ? labelFromCredentialKey(working) : null);
|
|
37071
|
+
return;
|
|
37072
|
+
}
|
|
37073
|
+
if (action === "use" || action === "switch") {
|
|
37074
|
+
const n = Number(rest[0]);
|
|
37075
|
+
if (!Number.isInteger(n) || n < 1 || n > list.length) {
|
|
37076
|
+
ctx.print(`usage: /ai-profile use <n> (n = 1-${list.length || "?"} from /ai-profile list)`, "error");
|
|
37077
|
+
return;
|
|
37078
|
+
}
|
|
37079
|
+
const cred = list[n - 1];
|
|
37080
|
+
if (!isUsableCredential(cred)) {
|
|
37081
|
+
const why = cred.enabled === false ? "it is disabled" : "it has no usable profile dir / provider";
|
|
37082
|
+
ctx.print(`Profile [${n}] "${aiProfileLabel(cred)}" can't be used \u2014 ${why}.`, "error");
|
|
37083
|
+
return;
|
|
37084
|
+
}
|
|
37085
|
+
const credDir = resolveHomePath(cred.profile);
|
|
37086
|
+
setPinnedCredential(dir2, credentialKey(cred.provider, credDir));
|
|
37087
|
+
ctx.setActiveProfile(profileDisplayLabel(credDir));
|
|
37088
|
+
ctx.print(
|
|
37089
|
+
`\u2714 switched to ${cred.provider} profile "${aiProfileLabel(cred)}" \u2014 tried first from the next prompt (failover keeps the rest as backup).`
|
|
37090
|
+
);
|
|
37091
|
+
return;
|
|
37092
|
+
}
|
|
37093
|
+
ctx.print(`Unknown /ai-profile subcommand "${sub}". Use: list | use <n> | reset.`, "error");
|
|
37094
|
+
}
|
|
36951
37095
|
function runFold(ctx, action) {
|
|
36952
37096
|
if (ctx.maxBlock === 0) {
|
|
36953
37097
|
ctx.print("No collapsible blocks on screen.", "warn");
|
|
@@ -37037,6 +37181,12 @@ var COMMANDS = [
|
|
|
37037
37181
|
description: "Show this machine account + its teams & projects",
|
|
37038
37182
|
run: runWhoami
|
|
37039
37183
|
},
|
|
37184
|
+
{
|
|
37185
|
+
name: "ai-profile",
|
|
37186
|
+
usage: "/ai-profile [list|use <n>|reset]",
|
|
37187
|
+
description: "List AI profiles / switch which one runs (ADR-0250)",
|
|
37188
|
+
run: runAiProfile
|
|
37189
|
+
},
|
|
37040
37190
|
{
|
|
37041
37191
|
name: "config",
|
|
37042
37192
|
usage: "/config [show|init|set <k> <v>|delete <k>]",
|
|
@@ -37269,7 +37419,8 @@ async function listExtras() {
|
|
|
37269
37419
|
category: "runtime",
|
|
37270
37420
|
installed: true,
|
|
37271
37421
|
version: info.version ?? null,
|
|
37272
|
-
installable: true
|
|
37422
|
+
installable: true,
|
|
37423
|
+
updatable: true
|
|
37273
37424
|
}));
|
|
37274
37425
|
}
|
|
37275
37426
|
async function detectWorkerTools() {
|
|
@@ -37282,33 +37433,39 @@ async function detectWorkerTools() {
|
|
|
37282
37433
|
category: t.category,
|
|
37283
37434
|
installed: version !== null,
|
|
37284
37435
|
version,
|
|
37285
|
-
installable: t.installable
|
|
37436
|
+
installable: t.installable,
|
|
37437
|
+
updatable: t.updatable
|
|
37286
37438
|
};
|
|
37287
37439
|
})
|
|
37288
37440
|
);
|
|
37289
37441
|
const extras = await listExtras().catch(() => []);
|
|
37290
37442
|
return { catalog, extras };
|
|
37291
37443
|
}
|
|
37444
|
+
function opPast(op) {
|
|
37445
|
+
return op === "install" ? "installed" : op === "uninstall" ? "uninstalled" : "updated";
|
|
37446
|
+
}
|
|
37292
37447
|
function resolvePackage(name, op) {
|
|
37293
37448
|
const entry = WORKER_TOOL_CATALOG.find((t) => t.id === name);
|
|
37294
37449
|
if (entry) {
|
|
37295
|
-
|
|
37296
|
-
|
|
37450
|
+
const allowed = op === "update" ? entry.updatable : entry.installable;
|
|
37451
|
+
if (!allowed) {
|
|
37452
|
+
return { error: `"${name}" is a prerequisite and cannot be ${opPast(op)} from here.` };
|
|
37297
37453
|
}
|
|
37298
37454
|
return { pkg: entry.installPackage ?? name };
|
|
37299
37455
|
}
|
|
37300
37456
|
if (WORKER_TOOL_PREREQUISITE_IDS.includes(name)) {
|
|
37301
|
-
return { error: `"${name}" is a prerequisite and cannot be ${op}
|
|
37457
|
+
return { error: `"${name}" is a prerequisite and cannot be ${opPast(op)} from here.` };
|
|
37302
37458
|
}
|
|
37303
37459
|
if (WORKER_TOOL_BUNDLED_GLOBALS.includes(name)) {
|
|
37304
|
-
return { error: `"${name}" ships with Node and cannot be ${op}
|
|
37460
|
+
return { error: `"${name}" ships with Node and cannot be ${opPast(op)} from here.` };
|
|
37305
37461
|
}
|
|
37306
37462
|
if (!NPM_NAME.test(name)) return { error: `Invalid package name: "${name}".` };
|
|
37307
37463
|
return { pkg: name };
|
|
37308
37464
|
}
|
|
37309
37465
|
function opArgs(op, manager, pkg) {
|
|
37310
|
-
if (
|
|
37311
|
-
|
|
37466
|
+
if (op === "uninstall") return manager === "pnpm" ? ["remove", "-g", pkg] : ["uninstall", "-g", pkg];
|
|
37467
|
+
const target = op === "update" ? `${pkg}@latest` : pkg;
|
|
37468
|
+
return manager === "pnpm" ? ["add", "-g", target] : ["install", "-g", target];
|
|
37312
37469
|
}
|
|
37313
37470
|
async function runWorkerToolOp(op, name, manager, onLine) {
|
|
37314
37471
|
const resolved = resolvePackage(name, op);
|
|
@@ -37417,6 +37574,9 @@ var WsClient = class {
|
|
|
37417
37574
|
reconnectEntryId = null;
|
|
37418
37575
|
/** Org-configured reconnect cap (seconds) from the last ws_token — overrides env. */
|
|
37419
37576
|
orgReconnectMaxSec = null;
|
|
37577
|
+
/** Org timezone (IANA) from the last ws_token (ADR-0132) — a rented worker follows its renter's
|
|
37578
|
+
* org; used to stamp the AI-run start time on the `aireq` transcript marker (ADR-0249). */
|
|
37579
|
+
orgTimezone = "UTC";
|
|
37420
37580
|
/** Env override for the reconnect cap (seconds); resolved once at construction. */
|
|
37421
37581
|
envReconnectMaxSec = envReconnectMaxSec();
|
|
37422
37582
|
/** Process start (ms) — uptime for a web-dispatched `/status` (ADR-0249). */
|
|
@@ -37601,6 +37761,9 @@ var WsClient = class {
|
|
|
37601
37761
|
if (typeof token.reconnectMaxBackoffSec === "number") {
|
|
37602
37762
|
this.orgReconnectMaxSec = token.reconnectMaxBackoffSec;
|
|
37603
37763
|
}
|
|
37764
|
+
if (typeof token.timezone === "string" && token.timezone) {
|
|
37765
|
+
this.orgTimezone = token.timezone;
|
|
37766
|
+
}
|
|
37604
37767
|
if (typeof token.autoUpdateDaily === "boolean") {
|
|
37605
37768
|
this.updateScheduler.configure({
|
|
37606
37769
|
autoUpdateDaily: token.autoUpdateDaily,
|
|
@@ -38105,9 +38268,10 @@ var WsClient = class {
|
|
|
38105
38268
|
);
|
|
38106
38269
|
break;
|
|
38107
38270
|
case WsChannels.TOOLS_INSTALL:
|
|
38108
|
-
case WsChannels.TOOLS_UNINSTALL:
|
|
38271
|
+
case WsChannels.TOOLS_UNINSTALL:
|
|
38272
|
+
case WsChannels.TOOLS_UPDATE: {
|
|
38109
38273
|
const req = payload;
|
|
38110
|
-
const op = message.channel === WsChannels.TOOLS_INSTALL ? "install" : "uninstall";
|
|
38274
|
+
const op = message.channel === WsChannels.TOOLS_INSTALL ? "install" : message.channel === WsChannels.TOOLS_UPDATE ? "update" : "uninstall";
|
|
38111
38275
|
this.send(message.channel, { started: true }, message.id);
|
|
38112
38276
|
void runWorkerToolOp(
|
|
38113
38277
|
op,
|
|
@@ -38288,9 +38452,10 @@ var WsClient = class {
|
|
|
38288
38452
|
const unified = isUnifiedConfig(config);
|
|
38289
38453
|
const mode = config.aiFailoverMode ?? "remember";
|
|
38290
38454
|
const workingDir = unified ? null : getWorkingProfile(this.context.profileDir, cmd);
|
|
38291
|
-
const
|
|
38455
|
+
const pinnedCred = unified ? getPinnedCredential(this.context.profileDir) : null;
|
|
38456
|
+
const workingCred = unified ? pinnedCred ?? (mode === "remember" ? getWorkingCredential(this.context.profileDir) : null) : null;
|
|
38292
38457
|
const startDir = unified ? null : this.profileUnderSessionPressure(config, cmd, workingDir);
|
|
38293
|
-
const startCred = unified ? this.credentialUnderSessionPressure(config, workingCred) : null;
|
|
38458
|
+
const startCred = unified && !pinnedCred ? this.credentialUnderSessionPressure(config, workingCred) : null;
|
|
38294
38459
|
if (startDir && startDir !== workingDir) {
|
|
38295
38460
|
this.bus.push({
|
|
38296
38461
|
source: origin,
|
|
@@ -38322,7 +38487,8 @@ ${guardedPrompt}`;
|
|
|
38322
38487
|
}
|
|
38323
38488
|
const plan = planAiRun(effectivePrompt, config, hint, resume, oneShot);
|
|
38324
38489
|
const markerArgs = plan.attempts[0]?.args ?? [];
|
|
38325
|
-
|
|
38490
|
+
const startStamp = formatTimestampInZone(/* @__PURE__ */ new Date(), this.orgTimezone);
|
|
38491
|
+
this.bus.push({ source: origin, kind: "aireq", text: `${startStamp} ${plan.cmd} \u2039 ${prompt}` });
|
|
38326
38492
|
logger.info("command.ai", { commandId, origin, cmd: plan.cmd, profiles: plan.attempts.length });
|
|
38327
38493
|
if (origin === "local") {
|
|
38328
38494
|
this.send(WsChannels.COMMAND_ANNOUNCE, {
|
|
@@ -38557,6 +38723,8 @@ ${guardedPrompt}`;
|
|
|
38557
38723
|
submitAi: () => {
|
|
38558
38724
|
},
|
|
38559
38725
|
// only /claude-cmd uses this, and it is special-cased above
|
|
38726
|
+
// /ai-profile use/reset from the web Console updates the shared header too (ADR-0250).
|
|
38727
|
+
setActiveProfile: (label) => this.bus.setActiveProfile(label),
|
|
38560
38728
|
reconnect: () => this.reconnectNow(),
|
|
38561
38729
|
expand: () => emit("Use the web console's fold viewer to expand blocks."),
|
|
38562
38730
|
collapse: () => emit("Use the web console's fold viewer to collapse blocks."),
|
|
@@ -45263,12 +45431,17 @@ function textColor(line) {
|
|
|
45263
45431
|
if (line.kind === "exit") return "green";
|
|
45264
45432
|
return void 0;
|
|
45265
45433
|
}
|
|
45434
|
+
var START_STAMP_RE = /^(\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2} )/;
|
|
45266
45435
|
function AiMarkerLine({ line }) {
|
|
45267
45436
|
const arrow = line.kind === "aireq" ? "\u2039" : "\u203A";
|
|
45268
45437
|
const idx = line.text.indexOf(arrow);
|
|
45269
45438
|
if (idx < 0) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: line.text || " " });
|
|
45439
|
+
const before = line.text.slice(0, idx);
|
|
45440
|
+
const stamp = line.kind === "aireq" ? START_STAMP_RE.exec(before)?.[1] ?? "" : "";
|
|
45441
|
+
const name = stamp ? before.slice(stamp.length) : before;
|
|
45270
45442
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
|
|
45271
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, {
|
|
45443
|
+
stamp ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: stamp }) : null,
|
|
45444
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "cyan", bold: true, children: name }),
|
|
45272
45445
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: arrow }),
|
|
45273
45446
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: line.text.slice(idx + 1) })
|
|
45274
45447
|
] });
|
|
@@ -45685,6 +45858,9 @@ function App2({
|
|
|
45685
45858
|
setPendingConfirm({ prompt, onYes });
|
|
45686
45859
|
},
|
|
45687
45860
|
submitAi: (aiInput) => bus.submitLocal(aiInput),
|
|
45861
|
+
// /ai-profile use/reset updates the header's active-profile label at once (ADR-0250);
|
|
45862
|
+
// the bus event feeds the React state (see the onActiveProfile subscription above).
|
|
45863
|
+
setActiveProfile: (label) => bus.setActiveProfile(label),
|
|
45688
45864
|
reconnect: () => bus.requestReconnect(),
|
|
45689
45865
|
expand: expandFold,
|
|
45690
45866
|
collapse: collapseFold,
|