@4pm/cli 1.5.1 → 1.5.2-b
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 +416 -194
- 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()
|
|
@@ -33067,6 +33074,12 @@ var workerToolInstallSchema = external_exports.object({
|
|
|
33067
33074
|
/** Package manager to run the global install with. */
|
|
33068
33075
|
manager: external_exports.enum(["npm", "pnpm"])
|
|
33069
33076
|
});
|
|
33077
|
+
var workerToolAutoUpdateSchema = external_exports.object({
|
|
33078
|
+
/** Catalog id or installed extra's npm package name. */
|
|
33079
|
+
name: external_exports.string().trim().min(1).max(214),
|
|
33080
|
+
/** true = flag this tool for auto-update to @latest; false = clear it. */
|
|
33081
|
+
enabled: external_exports.boolean()
|
|
33082
|
+
});
|
|
33070
33083
|
var configWriteRequestSchema = external_exports.object({
|
|
33071
33084
|
config: external_exports.string().max(256 * 1024)
|
|
33072
33085
|
});
|
|
@@ -33671,8 +33684,14 @@ var WsChannels = {
|
|
|
33671
33684
|
TOOLS_LIST: "tools.list",
|
|
33672
33685
|
TOOLS_INSTALL: "tools.install",
|
|
33673
33686
|
TOOLS_UNINSTALL: "tools.uninstall",
|
|
33687
|
+
// update-to-latest of an npm-distributed catalog tool or an extra (ADR-0252): same streamed
|
|
33688
|
+
// shape as install/uninstall — the cli reinstalls the package at `@latest`.
|
|
33689
|
+
TOOLS_UPDATE: "tools.update",
|
|
33674
33690
|
TOOLS_PROGRESS: "tools.progress",
|
|
33675
33691
|
TOOLS_DONE: "tools.done",
|
|
33692
|
+
// toggle per-tool auto-update (ADR-0253): request/reply — the cli persists the flag in
|
|
33693
|
+
// config.json (`autoUpdateTools`); the ADR-0074 idle daily tick later runs the flagged tools.
|
|
33694
|
+
TOOLS_AUTOUPDATE: "tools.autoUpdate",
|
|
33676
33695
|
// autonomous mode control (ADR-0152): read settings+status+books+approvals, tail logs, and
|
|
33677
33696
|
// write {settings|approvals|userTodo|cron} — the dashboard drives the worker's autonomous engine.
|
|
33678
33697
|
AUTONOMOUS_READ: "autonomous.read",
|
|
@@ -33833,7 +33852,7 @@ import { existsSync as existsSync13, mkdirSync as mkdirSync5, rmSync as rmSync4
|
|
|
33833
33852
|
import { randomUUID as randomCommandId } from "crypto";
|
|
33834
33853
|
|
|
33835
33854
|
// src/core/update-scheduler.ts
|
|
33836
|
-
import { spawn } from "child_process";
|
|
33855
|
+
import { spawn as spawn2 } from "child_process";
|
|
33837
33856
|
|
|
33838
33857
|
// src/common/logger/logger.ts
|
|
33839
33858
|
import { appendFile, mkdir, readdir, unlink } from "fs/promises";
|
|
@@ -33938,6 +33957,153 @@ ${content.slice(-LOG_UPLOAD_CONTENT_MAX)}`;
|
|
|
33938
33957
|
}
|
|
33939
33958
|
}
|
|
33940
33959
|
|
|
33960
|
+
// src/core/worker-tools.ts
|
|
33961
|
+
import { spawn } from "child_process";
|
|
33962
|
+
var NPM_NAME = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
33963
|
+
function run(cmd, args, onLine, timeoutMs = 18e4) {
|
|
33964
|
+
return new Promise((resolve8) => {
|
|
33965
|
+
let out = "";
|
|
33966
|
+
let buf = "";
|
|
33967
|
+
let child = null;
|
|
33968
|
+
const done = (code) => {
|
|
33969
|
+
clearTimeout(timer);
|
|
33970
|
+
if (onLine && buf.trim()) onLine(buf.trim());
|
|
33971
|
+
resolve8({ code, out });
|
|
33972
|
+
};
|
|
33973
|
+
const timer = setTimeout(() => {
|
|
33974
|
+
child?.kill();
|
|
33975
|
+
if (onLine) onLine(`timed out after ${Math.round(timeoutMs / 1e3)}s`);
|
|
33976
|
+
done(124);
|
|
33977
|
+
}, timeoutMs);
|
|
33978
|
+
timer.unref?.();
|
|
33979
|
+
const feed = (d) => {
|
|
33980
|
+
const text = d.toString("utf8");
|
|
33981
|
+
out += text;
|
|
33982
|
+
if (!onLine) return;
|
|
33983
|
+
buf += text;
|
|
33984
|
+
let nl;
|
|
33985
|
+
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
33986
|
+
const line = buf.slice(0, nl).replace(/\r$/, "");
|
|
33987
|
+
buf = buf.slice(nl + 1);
|
|
33988
|
+
onLine(line);
|
|
33989
|
+
}
|
|
33990
|
+
};
|
|
33991
|
+
try {
|
|
33992
|
+
child = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
33993
|
+
child.stdout?.on("data", feed);
|
|
33994
|
+
child.stderr?.on("data", feed);
|
|
33995
|
+
child.on("exit", (code) => done(code ?? 0));
|
|
33996
|
+
child.on("error", () => done(127));
|
|
33997
|
+
} catch {
|
|
33998
|
+
done(127);
|
|
33999
|
+
}
|
|
34000
|
+
});
|
|
34001
|
+
}
|
|
34002
|
+
async function detectOne(cmd, versionArg) {
|
|
34003
|
+
const { code, out } = await run(cmd, [versionArg], void 0, 15e3);
|
|
34004
|
+
if (code !== 0) return null;
|
|
34005
|
+
const first = out.split("\n").map((l) => l.trim()).find(Boolean);
|
|
34006
|
+
return first ?? "";
|
|
34007
|
+
}
|
|
34008
|
+
async function listExtras(autoUpdateSet = /* @__PURE__ */ new Set()) {
|
|
34009
|
+
const { out } = await run("npm", ["ls", "-g", "--depth=0", "--json"], void 0, 2e4);
|
|
34010
|
+
if (!out.trim()) return [];
|
|
34011
|
+
let deps = {};
|
|
34012
|
+
try {
|
|
34013
|
+
deps = JSON.parse(out).dependencies ?? {};
|
|
34014
|
+
} catch {
|
|
34015
|
+
return [];
|
|
34016
|
+
}
|
|
34017
|
+
const catalogPkgs = new Set(WORKER_TOOL_BUNDLED_GLOBALS);
|
|
34018
|
+
for (const t of WORKER_TOOL_CATALOG) {
|
|
34019
|
+
catalogPkgs.add(t.id);
|
|
34020
|
+
if (t.installPackage) catalogPkgs.add(t.installPackage);
|
|
34021
|
+
}
|
|
34022
|
+
return Object.entries(deps).filter(([name]) => !catalogPkgs.has(name)).map(([name, info]) => ({
|
|
34023
|
+
id: name,
|
|
34024
|
+
label: name,
|
|
34025
|
+
category: "runtime",
|
|
34026
|
+
installed: true,
|
|
34027
|
+
version: info.version ?? null,
|
|
34028
|
+
installable: true,
|
|
34029
|
+
updatable: true,
|
|
34030
|
+
autoUpdate: autoUpdateSet.has(name)
|
|
34031
|
+
}));
|
|
34032
|
+
}
|
|
34033
|
+
async function detectWorkerTools(autoUpdateTools = []) {
|
|
34034
|
+
const autoUpdateSet = new Set(autoUpdateTools);
|
|
34035
|
+
const catalog = await Promise.all(
|
|
34036
|
+
WORKER_TOOL_CATALOG.map(async (t) => {
|
|
34037
|
+
const version = await detectOne(t.id, t.versionArg);
|
|
34038
|
+
return {
|
|
34039
|
+
id: t.id,
|
|
34040
|
+
label: t.label,
|
|
34041
|
+
category: t.category,
|
|
34042
|
+
installed: version !== null,
|
|
34043
|
+
version,
|
|
34044
|
+
installable: t.installable,
|
|
34045
|
+
updatable: t.updatable,
|
|
34046
|
+
autoUpdate: autoUpdateSet.has(t.id)
|
|
34047
|
+
};
|
|
34048
|
+
})
|
|
34049
|
+
);
|
|
34050
|
+
const extras = await listExtras(autoUpdateSet).catch(() => []);
|
|
34051
|
+
return { catalog, extras };
|
|
34052
|
+
}
|
|
34053
|
+
function opPast(op) {
|
|
34054
|
+
return op === "install" ? "installed" : op === "uninstall" ? "uninstalled" : "updated";
|
|
34055
|
+
}
|
|
34056
|
+
function resolvePackage(name, op) {
|
|
34057
|
+
const entry = WORKER_TOOL_CATALOG.find((t) => t.id === name);
|
|
34058
|
+
if (entry) {
|
|
34059
|
+
const allowed = op === "update" ? entry.updatable : entry.installable;
|
|
34060
|
+
if (!allowed) {
|
|
34061
|
+
return { error: `"${name}" is a prerequisite and cannot be ${opPast(op)} from here.` };
|
|
34062
|
+
}
|
|
34063
|
+
return { pkg: entry.installPackage ?? name };
|
|
34064
|
+
}
|
|
34065
|
+
if (WORKER_TOOL_PREREQUISITE_IDS.includes(name)) {
|
|
34066
|
+
return { error: `"${name}" is a prerequisite and cannot be ${opPast(op)} from here.` };
|
|
34067
|
+
}
|
|
34068
|
+
if (WORKER_TOOL_BUNDLED_GLOBALS.includes(name)) {
|
|
34069
|
+
return { error: `"${name}" ships with Node and cannot be ${opPast(op)} from here.` };
|
|
34070
|
+
}
|
|
34071
|
+
if (!NPM_NAME.test(name)) return { error: `Invalid package name: "${name}".` };
|
|
34072
|
+
return { pkg: name };
|
|
34073
|
+
}
|
|
34074
|
+
function opArgs(op, manager, pkg) {
|
|
34075
|
+
if (op === "uninstall") return manager === "pnpm" ? ["remove", "-g", pkg] : ["uninstall", "-g", pkg];
|
|
34076
|
+
const target = op === "update" ? `${pkg}@latest` : pkg;
|
|
34077
|
+
return manager === "pnpm" ? ["add", "-g", target] : ["install", "-g", target];
|
|
34078
|
+
}
|
|
34079
|
+
async function runWorkerToolOp(op, name, manager, onLine) {
|
|
34080
|
+
const resolved = resolvePackage(name, op);
|
|
34081
|
+
if (resolved.error || !resolved.pkg) {
|
|
34082
|
+
return { ok: false, exitCode: 1, error: resolved.error ?? "Invalid package." };
|
|
34083
|
+
}
|
|
34084
|
+
onLine(`$ ${manager} ${opArgs(op, manager, resolved.pkg).join(" ")}`);
|
|
34085
|
+
const { code } = await run(manager, opArgs(op, manager, resolved.pkg), onLine);
|
|
34086
|
+
return { ok: code === 0, exitCode: code, error: code === 0 ? void 0 : `${manager} exited ${code}` };
|
|
34087
|
+
}
|
|
34088
|
+
function setToolAutoUpdate(profileDir2, name, enabled) {
|
|
34089
|
+
const resolved = resolvePackage(name, "update");
|
|
34090
|
+
if (resolved.error) return { ok: false, error: resolved.error };
|
|
34091
|
+
const current = readProfileConfig(profileDir2).autoUpdateTools ?? [];
|
|
34092
|
+
const set = new Set(current);
|
|
34093
|
+
if (enabled) set.add(name);
|
|
34094
|
+
else set.delete(name);
|
|
34095
|
+
writeProfileConfig(profileDir2, { autoUpdateTools: [...set] });
|
|
34096
|
+
return { ok: true };
|
|
34097
|
+
}
|
|
34098
|
+
async function autoUpdateFlaggedTools(tools, onLine) {
|
|
34099
|
+
for (const name of tools) {
|
|
34100
|
+
const res = await runWorkerToolOp("update", name, "npm", onLine).catch(
|
|
34101
|
+
(err) => ({ ok: false, exitCode: 1, error: String(err) })
|
|
34102
|
+
);
|
|
34103
|
+
onLine(res.ok ? `\u2713 ${name} updated` : `\u2717 ${name}: ${res.error ?? "failed"}`);
|
|
34104
|
+
}
|
|
34105
|
+
}
|
|
34106
|
+
|
|
33941
34107
|
// src/core/update-scheduler.ts
|
|
33942
34108
|
var TICK_MS = 6e4;
|
|
33943
34109
|
var UpdateScheduler = class {
|
|
@@ -34020,6 +34186,7 @@ var UpdateScheduler = class {
|
|
|
34020
34186
|
async runUpdate() {
|
|
34021
34187
|
this.updating = true;
|
|
34022
34188
|
try {
|
|
34189
|
+
await this.updateFlaggedTools();
|
|
34023
34190
|
this.bus.log("Scheduled auto-update: checking for a new version\u2026");
|
|
34024
34191
|
const result = await updateToLatest(this.serverUrl);
|
|
34025
34192
|
if (result.action === "already-latest") {
|
|
@@ -34033,7 +34200,7 @@ var UpdateScheduler = class {
|
|
|
34033
34200
|
}
|
|
34034
34201
|
logger.info("update.scheduled.updated", { version: result.version });
|
|
34035
34202
|
this.bus.log(`Updated to ${result.version} \u2014 restarting to apply\u2026`);
|
|
34036
|
-
const child =
|
|
34203
|
+
const child = spawn2(process.execPath, process.argv.slice(1), {
|
|
34037
34204
|
stdio: "inherit",
|
|
34038
34205
|
env: { ...process.env, FOURPM_NO_UPDATE: "1" }
|
|
34039
34206
|
});
|
|
@@ -34044,18 +34211,45 @@ var UpdateScheduler = class {
|
|
|
34044
34211
|
this.updating = false;
|
|
34045
34212
|
}
|
|
34046
34213
|
}
|
|
34214
|
+
/** Update each tool flagged for auto-update in config.json to @latest (ADR-0253) — best-effort. */
|
|
34215
|
+
async updateFlaggedTools() {
|
|
34216
|
+
const tools = readProfileConfig(this.profileDir).autoUpdateTools ?? [];
|
|
34217
|
+
if (tools.length === 0) return;
|
|
34218
|
+
this.bus.log(`Scheduled tool auto-update: ${tools.join(", ")}\u2026`);
|
|
34219
|
+
await autoUpdateFlaggedTools(tools, (line) => logger.info("update.tool.line", { line })).catch(
|
|
34220
|
+
(err) => logger.warn("update.tool.error", { error: String(err) })
|
|
34221
|
+
);
|
|
34222
|
+
}
|
|
34047
34223
|
};
|
|
34048
34224
|
|
|
34049
34225
|
// src/core/executor.ts
|
|
34050
|
-
import { spawn as
|
|
34226
|
+
import { spawn as spawn3 } from "child_process";
|
|
34051
34227
|
var KILL_GRACE_MS = 5e3;
|
|
34052
34228
|
async function runCommand(dispatch, emit, opts) {
|
|
34053
34229
|
let seq = 0;
|
|
34054
|
-
|
|
34055
|
-
|
|
34056
|
-
|
|
34057
|
-
|
|
34230
|
+
let child;
|
|
34231
|
+
try {
|
|
34232
|
+
child = spawn3(dispatch.cmd, dispatch.args ?? [], {
|
|
34233
|
+
cwd: dispatch.path,
|
|
34234
|
+
env: { ...process.env, ...dispatch.env },
|
|
34235
|
+
shell: false
|
|
34236
|
+
});
|
|
34237
|
+
} catch (err) {
|
|
34238
|
+
emit({
|
|
34239
|
+
commandId: dispatch.commandId,
|
|
34240
|
+
seq: seq++,
|
|
34241
|
+
chunk: `spawn error: ${err.message}
|
|
34242
|
+
`,
|
|
34243
|
+
done: true,
|
|
34244
|
+
exitCode: -1
|
|
34245
|
+
});
|
|
34246
|
+
return;
|
|
34247
|
+
}
|
|
34248
|
+
child.stdin?.on("error", () => {
|
|
34058
34249
|
});
|
|
34250
|
+
if (opts?.stdin != null) {
|
|
34251
|
+
child.stdin?.write(opts.stdin);
|
|
34252
|
+
}
|
|
34059
34253
|
child.stdin?.end();
|
|
34060
34254
|
const timeoutMs = opts?.timeoutMs && opts.timeoutMs > 0 ? opts.timeoutMs : 0;
|
|
34061
34255
|
let timedOut = false;
|
|
@@ -34255,12 +34449,16 @@ function isOutOfCredits(text) {
|
|
|
34255
34449
|
text
|
|
34256
34450
|
);
|
|
34257
34451
|
}
|
|
34258
|
-
function runAttempt(commandId, cmd, args, cwd2, env3, onChunk, timeoutMs) {
|
|
34452
|
+
function runAttempt(commandId, cmd, args, stdin, cwd2, env3, onChunk, timeoutMs) {
|
|
34259
34453
|
return new Promise((resolve8) => {
|
|
34260
|
-
|
|
34261
|
-
|
|
34262
|
-
|
|
34263
|
-
|
|
34454
|
+
runCommand(
|
|
34455
|
+
{ commandId, cmd, args, path: cwd2, env: env3 },
|
|
34456
|
+
(out) => {
|
|
34457
|
+
if (out.chunk) onChunk(out.chunk);
|
|
34458
|
+
if (out.done) resolve8(out.exitCode ?? -1);
|
|
34459
|
+
},
|
|
34460
|
+
{ stdin, ...timeoutMs > 0 ? { timeoutMs } : {} }
|
|
34461
|
+
).catch(() => resolve8(-1));
|
|
34264
34462
|
});
|
|
34265
34463
|
}
|
|
34266
34464
|
async function runAiFailover(plan, commandId, cwd2, handlers, timeoutMs = 0) {
|
|
@@ -34281,7 +34479,7 @@ async function runAiFailover(plan, commandId, cwd2, handlers, timeoutMs = 0) {
|
|
|
34281
34479
|
handlers.onChunk(text);
|
|
34282
34480
|
}
|
|
34283
34481
|
};
|
|
34284
|
-
finalExit = await runAttempt(commandId, attempt.cmd, attempt.args, cwd2, attempt.env, emit, timeoutMs);
|
|
34482
|
+
finalExit = await runAttempt(commandId, attempt.cmd, attempt.args, attempt.stdin, cwd2, attempt.env, emit, timeoutMs);
|
|
34285
34483
|
const tail = parser.flush();
|
|
34286
34484
|
if (tail) {
|
|
34287
34485
|
captured += tail;
|
|
@@ -34358,6 +34556,52 @@ function setWorkingCredential(profileDir2, key) {
|
|
|
34358
34556
|
} catch {
|
|
34359
34557
|
}
|
|
34360
34558
|
}
|
|
34559
|
+
var PINNED_CREDENTIAL_KEY = "__pinned__";
|
|
34560
|
+
function getPinnedCredential(profileDir2) {
|
|
34561
|
+
return readState(profileDir2)[PINNED_CREDENTIAL_KEY] ?? null;
|
|
34562
|
+
}
|
|
34563
|
+
function setPinnedCredential(profileDir2, key) {
|
|
34564
|
+
const state = readState(profileDir2);
|
|
34565
|
+
if (state[PINNED_CREDENTIAL_KEY] === key) return;
|
|
34566
|
+
state[PINNED_CREDENTIAL_KEY] = key;
|
|
34567
|
+
try {
|
|
34568
|
+
writeFileSync4(statePath(profileDir2), JSON.stringify(state, null, 2), "utf8");
|
|
34569
|
+
} catch {
|
|
34570
|
+
}
|
|
34571
|
+
}
|
|
34572
|
+
function clearPinnedCredential(profileDir2) {
|
|
34573
|
+
const state = readState(profileDir2);
|
|
34574
|
+
if (!(PINNED_CREDENTIAL_KEY in state)) return;
|
|
34575
|
+
delete state[PINNED_CREDENTIAL_KEY];
|
|
34576
|
+
try {
|
|
34577
|
+
writeFileSync4(statePath(profileDir2), JSON.stringify(state, null, 2), "utf8");
|
|
34578
|
+
} catch {
|
|
34579
|
+
}
|
|
34580
|
+
}
|
|
34581
|
+
|
|
34582
|
+
// src/utils/time.ts
|
|
34583
|
+
function formatTimestampInZone(date, timeZone) {
|
|
34584
|
+
const build = (tz) => {
|
|
34585
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
34586
|
+
...tz ? { timeZone: tz } : {},
|
|
34587
|
+
hour12: false,
|
|
34588
|
+
year: "numeric",
|
|
34589
|
+
month: "2-digit",
|
|
34590
|
+
day: "2-digit",
|
|
34591
|
+
hour: "2-digit",
|
|
34592
|
+
minute: "2-digit",
|
|
34593
|
+
second: "2-digit"
|
|
34594
|
+
}).formatToParts(date);
|
|
34595
|
+
const get2 = (type) => parts.find((p) => p.type === type)?.value ?? "";
|
|
34596
|
+
const hour = get2("hour") === "24" ? "00" : get2("hour");
|
|
34597
|
+
return `${get2("year")}/${get2("month")}/${get2("day")} ${hour}:${get2("minute")}:${get2("second")}`;
|
|
34598
|
+
};
|
|
34599
|
+
try {
|
|
34600
|
+
return build(timeZone);
|
|
34601
|
+
} catch {
|
|
34602
|
+
return build();
|
|
34603
|
+
}
|
|
34604
|
+
}
|
|
34361
34605
|
|
|
34362
34606
|
// src/utils/ai-cli.ts
|
|
34363
34607
|
import { readFileSync as readFileSync8 } from "fs";
|
|
@@ -34463,7 +34707,7 @@ function folderScopeGuard(folder, prompt) {
|
|
|
34463
34707
|
prompt
|
|
34464
34708
|
].join("\n");
|
|
34465
34709
|
}
|
|
34466
|
-
function buildRunArgs(profile, cmd,
|
|
34710
|
+
function buildRunArgs(profile, cmd, resumeId, oneShot = false) {
|
|
34467
34711
|
const extras = profile.args ?? [];
|
|
34468
34712
|
const model = profile.model?.trim();
|
|
34469
34713
|
const modelArgs = model ? ["--model", model] : [];
|
|
@@ -34471,7 +34715,7 @@ function buildRunArgs(profile, cmd, prompt, resumeId, oneShot = false) {
|
|
|
34471
34715
|
const turns = profile.maxTurns;
|
|
34472
34716
|
const perProfileTurns = !oneShot && cmd.includes("claude") && typeof turns === "number" && turns > 0 ? [`--max-turns=${Math.floor(turns)}`] : [];
|
|
34473
34717
|
const oneShotExtra = oneShot ? oneShotArgs(cmd) : perProfileTurns;
|
|
34474
|
-
return [...requiredArgs(cmd), ...resumeArgs, ...extras, ...modelArgs, ...oneShotExtra
|
|
34718
|
+
return [...requiredArgs(cmd), ...resumeArgs, ...extras, ...modelArgs, ...oneShotExtra];
|
|
34475
34719
|
}
|
|
34476
34720
|
function claudeHomeDirs(config) {
|
|
34477
34721
|
const dirs = profileDirs(claudeProfilesOf(config));
|
|
@@ -34522,7 +34766,7 @@ function planUnifiedRun(prompt, config, workingCredential, resume, oneShot) {
|
|
|
34522
34766
|
return {
|
|
34523
34767
|
cmd: active,
|
|
34524
34768
|
attempts: [
|
|
34525
|
-
{ cmd: active, label: "default", dir: null, key: null, args: buildRunArgs({ profile: "" }, active,
|
|
34769
|
+
{ cmd: active, label: "default", dir: null, key: null, args: buildRunArgs({ profile: "" }, active, void 0, oneShot), stdin: prompt, env: { ...baseEnv } }
|
|
34526
34770
|
]
|
|
34527
34771
|
};
|
|
34528
34772
|
}
|
|
@@ -34540,7 +34784,8 @@ function planUnifiedRun(prompt, config, workingCredential, resume, oneShot) {
|
|
|
34540
34784
|
label: cred.label?.trim() || profileDisplayLabel(dir2),
|
|
34541
34785
|
dir: dir2,
|
|
34542
34786
|
key,
|
|
34543
|
-
args: buildRunArgs(cred, cmd,
|
|
34787
|
+
args: buildRunArgs(cred, cmd, resume.get(key), oneShot),
|
|
34788
|
+
stdin: prompt,
|
|
34544
34789
|
env: envVar ? { ...baseEnv, [envVar]: dir2 } : { ...baseEnv }
|
|
34545
34790
|
}));
|
|
34546
34791
|
return { cmd: attempts[0]?.cmd ?? DEFAULT_AI_CLI, attempts };
|
|
@@ -34552,7 +34797,7 @@ function planLegacyRun(prompt, config, workingDir, resume, oneShot) {
|
|
|
34552
34797
|
const defaultPlan = {
|
|
34553
34798
|
cmd,
|
|
34554
34799
|
attempts: [
|
|
34555
|
-
{ cmd, label: "default", dir: null, key: null, args: buildRunArgs({ profile: "" }, cmd,
|
|
34800
|
+
{ cmd, label: "default", dir: null, key: null, args: buildRunArgs({ profile: "" }, cmd, void 0, oneShot), stdin: prompt, env: { ...baseEnv } }
|
|
34556
34801
|
]
|
|
34557
34802
|
};
|
|
34558
34803
|
if (!envVar) return defaultPlan;
|
|
@@ -34569,7 +34814,8 @@ function planLegacyRun(prompt, config, workingDir, resume, oneShot) {
|
|
|
34569
34814
|
label: profileDisplayLabel(dir2),
|
|
34570
34815
|
dir: dir2,
|
|
34571
34816
|
key,
|
|
34572
|
-
args: buildRunArgs(profile, cmd,
|
|
34817
|
+
args: buildRunArgs(profile, cmd, resume.get(key), oneShot),
|
|
34818
|
+
stdin: prompt,
|
|
34573
34819
|
env: { ...baseEnv, [envVar]: dir2 }
|
|
34574
34820
|
};
|
|
34575
34821
|
});
|
|
@@ -34577,7 +34823,7 @@ function planLegacyRun(prompt, config, workingDir, resume, oneShot) {
|
|
|
34577
34823
|
}
|
|
34578
34824
|
|
|
34579
34825
|
// src/core/claude-usage.ts
|
|
34580
|
-
import { spawn as
|
|
34826
|
+
import { spawn as spawn4 } from "child_process";
|
|
34581
34827
|
import { readFileSync as readFileSync9 } from "fs";
|
|
34582
34828
|
import { join as join9 } from "path";
|
|
34583
34829
|
var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
@@ -34624,7 +34870,7 @@ function runUsageCli(dir2, aiCli2) {
|
|
|
34624
34870
|
}, 3e4);
|
|
34625
34871
|
timer.unref?.();
|
|
34626
34872
|
try {
|
|
34627
|
-
child =
|
|
34873
|
+
child = spawn4(aiCli2, ["/usage"], {
|
|
34628
34874
|
env: { ...process.env, CLAUDE_CONFIG_DIR: dir2 },
|
|
34629
34875
|
stdio: ["ignore", "pipe", "ignore"]
|
|
34630
34876
|
});
|
|
@@ -34694,7 +34940,7 @@ async function checkClaudeUsage(dirs, aiCli2 = "claude") {
|
|
|
34694
34940
|
// src/core/ai-assist.ts
|
|
34695
34941
|
import { execFile } from "child_process";
|
|
34696
34942
|
import { promisify } from "util";
|
|
34697
|
-
var
|
|
34943
|
+
var run2 = promisify(execFile);
|
|
34698
34944
|
function aiCli() {
|
|
34699
34945
|
return process.env.AI_CLI || "claude";
|
|
34700
34946
|
}
|
|
@@ -34737,7 +34983,7 @@ function parseAi(cli, stdout, prompt) {
|
|
|
34737
34983
|
}
|
|
34738
34984
|
async function runAi(prompt) {
|
|
34739
34985
|
const cli = aiCli();
|
|
34740
|
-
const { stdout } = await
|
|
34986
|
+
const { stdout } = await run2(cli, aiArgs(cli, prompt), {
|
|
34741
34987
|
timeout: 12e4,
|
|
34742
34988
|
maxBuffer: 8 * 1024 * 1024
|
|
34743
34989
|
});
|
|
@@ -35080,7 +35326,7 @@ async function writeWorkerFile(root, relPath, content) {
|
|
|
35080
35326
|
}
|
|
35081
35327
|
|
|
35082
35328
|
// src/core/support-answer.ts
|
|
35083
|
-
import { spawn as
|
|
35329
|
+
import { spawn as spawn5 } from "child_process";
|
|
35084
35330
|
import { execFile as execFile2 } from "child_process";
|
|
35085
35331
|
import { createHash as createHash3 } from "crypto";
|
|
35086
35332
|
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync12, statSync as statSync3 } from "fs";
|
|
@@ -35215,7 +35461,7 @@ function runClaudeOnce(cmd, profile, prompt, extraEnv) {
|
|
|
35215
35461
|
...extraEnv,
|
|
35216
35462
|
...profile ? { CLAUDE_CONFIG_DIR: profile.dir } : {}
|
|
35217
35463
|
};
|
|
35218
|
-
const child =
|
|
35464
|
+
const child = spawn5(cmd, args, { stdio: ["pipe", "pipe", "pipe"], env: env3 });
|
|
35219
35465
|
const parser = createAiStreamParser(cmd);
|
|
35220
35466
|
let out = "";
|
|
35221
35467
|
let err = "";
|
|
@@ -35285,7 +35531,7 @@ async function runSupportAnswer(req, ai, profileDir2) {
|
|
|
35285
35531
|
}
|
|
35286
35532
|
|
|
35287
35533
|
// src/core/knowledge-compose.ts
|
|
35288
|
-
import { spawn as
|
|
35534
|
+
import { spawn as spawn6 } from "child_process";
|
|
35289
35535
|
var COMPOSE_TIMEOUT_MS = 18e4;
|
|
35290
35536
|
function buildPrompt2(req) {
|
|
35291
35537
|
const focus = req.prompt ? `
|
|
@@ -35311,7 +35557,7 @@ function runOnce(cmd, profile, prompt, cwd2, extraEnv) {
|
|
|
35311
35557
|
...extraEnv,
|
|
35312
35558
|
...profile ? { CLAUDE_CONFIG_DIR: profile.dir } : {}
|
|
35313
35559
|
};
|
|
35314
|
-
const child =
|
|
35560
|
+
const child = spawn6(cmd, args, { stdio: ["pipe", "pipe", "pipe"], cwd: cwd2, env: env3 });
|
|
35315
35561
|
let out = "";
|
|
35316
35562
|
let err = "";
|
|
35317
35563
|
const timer = setTimeout(() => {
|
|
@@ -35359,10 +35605,10 @@ async function runKnowledgeCompose(req, ai, physicRoot) {
|
|
|
35359
35605
|
// src/core/autonomous.ts
|
|
35360
35606
|
import { readFile as readFile3, writeFile as writeFile2, chmod } from "fs/promises";
|
|
35361
35607
|
import { readFileSync as readFileSync13 } from "fs";
|
|
35362
|
-
import { execFile as execFile3, spawn as
|
|
35608
|
+
import { execFile as execFile3, spawn as spawn7 } from "child_process";
|
|
35363
35609
|
import { join as join13 } from "path";
|
|
35364
35610
|
import { promisify as promisify3 } from "util";
|
|
35365
|
-
var
|
|
35611
|
+
var run3 = promisify3(execFile3);
|
|
35366
35612
|
var SETTINGS_REL = ".claude/.autonomous.settings.json";
|
|
35367
35613
|
var HISTORIES_REL = ".claude/.autonomous.histories.json";
|
|
35368
35614
|
var APPROVALS_REL = ".claude/.autonomous.approvals.json";
|
|
@@ -35396,7 +35642,7 @@ function tickScript(root) {
|
|
|
35396
35642
|
}
|
|
35397
35643
|
async function crontabList() {
|
|
35398
35644
|
try {
|
|
35399
|
-
const { stdout } = await
|
|
35645
|
+
const { stdout } = await run3("crontab", ["-l"], { timeout: 1e4 });
|
|
35400
35646
|
return stdout;
|
|
35401
35647
|
} catch {
|
|
35402
35648
|
return "";
|
|
@@ -35404,7 +35650,7 @@ async function crontabList() {
|
|
|
35404
35650
|
}
|
|
35405
35651
|
async function crontabSet(content) {
|
|
35406
35652
|
return new Promise((resolve8) => {
|
|
35407
|
-
const child =
|
|
35653
|
+
const child = spawn7("crontab", ["-"], { stdio: ["pipe", "ignore", "ignore"] });
|
|
35408
35654
|
child.on("error", () => resolve8(false));
|
|
35409
35655
|
child.on("close", (code) => resolve8(code === 0));
|
|
35410
35656
|
child.stdin.end(content.endsWith("\n") ? content : content + "\n");
|
|
@@ -36135,15 +36381,15 @@ async function buildGraph(root, mode) {
|
|
|
36135
36381
|
// src/core/rag.ts
|
|
36136
36382
|
import { readFile as readFile9, writeFile as writeFile7, statfs as statfs2, mkdir as mkdir6 } from "fs/promises";
|
|
36137
36383
|
import { existsSync as existsSync10 } from "fs";
|
|
36138
|
-
import { execFile as execFile4, spawn as
|
|
36384
|
+
import { execFile as execFile4, spawn as spawn8 } from "child_process";
|
|
36139
36385
|
import { totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
36140
36386
|
import { join as join20 } from "path";
|
|
36141
36387
|
import { promisify as promisify4 } from "util";
|
|
36142
|
-
var
|
|
36388
|
+
var run4 = promisify4(execFile4);
|
|
36143
36389
|
var RAG_REL = ".claude/rag";
|
|
36144
36390
|
async function tryRun(cmd, args) {
|
|
36145
36391
|
try {
|
|
36146
|
-
const { stdout } = await
|
|
36392
|
+
const { stdout } = await run4(cmd, args, { timeout: 8e3 });
|
|
36147
36393
|
return { ok: true, out: stdout.trim() };
|
|
36148
36394
|
} catch {
|
|
36149
36395
|
return { ok: false, out: "" };
|
|
@@ -36322,7 +36568,7 @@ touch "${RAG_REL}/.indexing"
|
|
|
36322
36568
|
{ echo "[reindex] $(date)"; "$RAG_PY" "${RAG_REL}/rag_engine.py" reindex 2>&1; } >> "${RAG_REL}/index.log" 2>&1
|
|
36323
36569
|
rm -f "${RAG_REL}/.indexing"
|
|
36324
36570
|
`;
|
|
36325
|
-
const child =
|
|
36571
|
+
const child = spawn8("bash", ["-c", script], {
|
|
36326
36572
|
cwd: root,
|
|
36327
36573
|
env: { ...process.env, RAG_PY: py.bin },
|
|
36328
36574
|
detached: true,
|
|
@@ -36337,7 +36583,7 @@ async function ragQuery(root, queryText, k = 8) {
|
|
|
36337
36583
|
if (!existsSync10(join20(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
|
|
36338
36584
|
await ensureEngine(root);
|
|
36339
36585
|
try {
|
|
36340
|
-
const { stdout } = await
|
|
36586
|
+
const { stdout } = await run4(
|
|
36341
36587
|
py.bin,
|
|
36342
36588
|
[join20(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k, 1), 20))],
|
|
36343
36589
|
{ cwd: root, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
@@ -36372,7 +36618,7 @@ rm -f "${RAG_REL}/.installed"
|
|
|
36372
36618
|
} >> "${RAG_REL}/install.log" 2>&1
|
|
36373
36619
|
rm -f "${RAG_REL}/.installing"
|
|
36374
36620
|
`;
|
|
36375
|
-
const child =
|
|
36621
|
+
const child = spawn8("bash", ["-c", script], {
|
|
36376
36622
|
cwd: root,
|
|
36377
36623
|
env: { ...process.env, RAG_PY: py.bin, RAG_MODEL: model },
|
|
36378
36624
|
detached: true,
|
|
@@ -36387,7 +36633,7 @@ import { readFile as readFile10 } from "fs/promises";
|
|
|
36387
36633
|
import { execFile as execFile5 } from "child_process";
|
|
36388
36634
|
import { basename as basename3, dirname as dirname7, resolve as resolve5 } from "path";
|
|
36389
36635
|
import { promisify as promisify5 } from "util";
|
|
36390
|
-
var
|
|
36636
|
+
var run5 = promisify5(execFile5);
|
|
36391
36637
|
async function gitDiff(path) {
|
|
36392
36638
|
const target = resolve5(path);
|
|
36393
36639
|
const dir2 = dirname7(target);
|
|
@@ -36399,7 +36645,7 @@ async function gitDiff(path) {
|
|
|
36399
36645
|
}
|
|
36400
36646
|
let oldContent = "";
|
|
36401
36647
|
try {
|
|
36402
|
-
const { stdout } = await
|
|
36648
|
+
const { stdout } = await run5("git", ["show", `HEAD:./${base}`], {
|
|
36403
36649
|
cwd: dir2,
|
|
36404
36650
|
timeout: 15e3,
|
|
36405
36651
|
maxBuffer: 4 * 1024 * 1024
|
|
@@ -36413,10 +36659,10 @@ async function gitDiff(path) {
|
|
|
36413
36659
|
// src/core/git-env.ts
|
|
36414
36660
|
import { execFile as execFile6 } from "child_process";
|
|
36415
36661
|
import { promisify as promisify6 } from "util";
|
|
36416
|
-
var
|
|
36662
|
+
var run6 = promisify6(execFile6);
|
|
36417
36663
|
async function hasBinary(bin, args = ["--version"]) {
|
|
36418
36664
|
try {
|
|
36419
|
-
await
|
|
36665
|
+
await run6(bin, args, { timeout: 5e3 });
|
|
36420
36666
|
return true;
|
|
36421
36667
|
} catch {
|
|
36422
36668
|
return false;
|
|
@@ -36429,7 +36675,7 @@ async function checkGitEnv(provider) {
|
|
|
36429
36675
|
let account = null;
|
|
36430
36676
|
if (installed) {
|
|
36431
36677
|
try {
|
|
36432
|
-
const { stdout, stderr } = await
|
|
36678
|
+
const { stdout, stderr } = await run6(bin, ["auth", "status"], { timeout: 8e3 });
|
|
36433
36679
|
const out = `${stdout}
|
|
36434
36680
|
${stderr}`;
|
|
36435
36681
|
authenticated = /logged in|logged into|✓/i.test(out);
|
|
@@ -36446,7 +36692,7 @@ import { execFile as execFile7 } from "child_process";
|
|
|
36446
36692
|
import { readFile as readFile11, rm as rm3, mkdir as mkdir7 } from "fs/promises";
|
|
36447
36693
|
import { join as join21 } from "path";
|
|
36448
36694
|
import { promisify as promisify7 } from "util";
|
|
36449
|
-
var
|
|
36695
|
+
var run7 = promisify7(execFile7);
|
|
36450
36696
|
function keyPaths(profileDir2) {
|
|
36451
36697
|
const dir2 = join21(profileDir2, ".ssh");
|
|
36452
36698
|
const key = join21(dir2, "id_4pm");
|
|
@@ -36461,14 +36707,14 @@ async function readPublicKey(pub) {
|
|
|
36461
36707
|
}
|
|
36462
36708
|
async function fingerprintOf(pub) {
|
|
36463
36709
|
try {
|
|
36464
|
-
const { stdout } = await
|
|
36710
|
+
const { stdout } = await run7("ssh-keygen", ["-lf", pub], { timeout: 5e3 });
|
|
36465
36711
|
return stdout.match(/SHA256:[A-Za-z0-9+/=]+/)?.[0] ?? null;
|
|
36466
36712
|
} catch {
|
|
36467
36713
|
return null;
|
|
36468
36714
|
}
|
|
36469
36715
|
}
|
|
36470
36716
|
async function configureGit(key) {
|
|
36471
|
-
await
|
|
36717
|
+
await run7(
|
|
36472
36718
|
"git",
|
|
36473
36719
|
["config", "--global", "core.sshCommand", `ssh -i ${key} -o IdentitiesOnly=yes`],
|
|
36474
36720
|
{ timeout: 5e3 }
|
|
@@ -36479,7 +36725,7 @@ async function generate(profileDir2) {
|
|
|
36479
36725
|
await mkdir7(dir2, { recursive: true });
|
|
36480
36726
|
await rm3(key, { force: true });
|
|
36481
36727
|
await rm3(pub, { force: true });
|
|
36482
|
-
await
|
|
36728
|
+
await run7("ssh-keygen", ["-t", "ed25519", "-f", key, "-N", "", "-C", "4pm-deploy-key"], {
|
|
36483
36729
|
timeout: 15e3
|
|
36484
36730
|
});
|
|
36485
36731
|
await configureGit(key);
|
|
@@ -36489,7 +36735,7 @@ async function remove(profileDir2) {
|
|
|
36489
36735
|
const { key, pub } = keyPaths(profileDir2);
|
|
36490
36736
|
await rm3(key, { force: true });
|
|
36491
36737
|
await rm3(pub, { force: true });
|
|
36492
|
-
await
|
|
36738
|
+
await run7("git", ["config", "--global", "--unset", "core.sshCommand"], { timeout: 5e3 }).catch(
|
|
36493
36739
|
() => void 0
|
|
36494
36740
|
);
|
|
36495
36741
|
return { publicKey: null, fingerprint: null };
|
|
@@ -36514,7 +36760,7 @@ import { readdir as readdir6 } from "fs/promises";
|
|
|
36514
36760
|
import { execFile as execFile8 } from "child_process";
|
|
36515
36761
|
import { resolve as resolve6, sep as sep4 } from "path";
|
|
36516
36762
|
import { promisify as promisify8 } from "util";
|
|
36517
|
-
var
|
|
36763
|
+
var run8 = promisify8(execFile8);
|
|
36518
36764
|
var FS = "";
|
|
36519
36765
|
var RS = "";
|
|
36520
36766
|
function resolveRepo(root, repo) {
|
|
@@ -36525,7 +36771,7 @@ function resolveRepo(root, repo) {
|
|
|
36525
36771
|
}
|
|
36526
36772
|
async function git(cwd2, args) {
|
|
36527
36773
|
try {
|
|
36528
|
-
const { stdout } = await
|
|
36774
|
+
const { stdout } = await run8("git", args, {
|
|
36529
36775
|
cwd: cwd2,
|
|
36530
36776
|
timeout: 15e3,
|
|
36531
36777
|
maxBuffer: 8 * 1024 * 1024
|
|
@@ -36629,11 +36875,11 @@ async function gitCommitDiff(root, repo, hash, path) {
|
|
|
36629
36875
|
// src/core/git-commit-identity.ts
|
|
36630
36876
|
import { execFile as execFile9 } from "child_process";
|
|
36631
36877
|
import { promisify as promisify9 } from "util";
|
|
36632
|
-
var
|
|
36878
|
+
var run9 = promisify9(execFile9);
|
|
36633
36879
|
async function setCommitAuthor(cwd2, machineUsername) {
|
|
36634
36880
|
if (!machineUsername) return;
|
|
36635
36881
|
try {
|
|
36636
|
-
await
|
|
36882
|
+
await run9("git", ["config", "--local", "user.name", machineUsername], { cwd: cwd2, timeout: 8e3 });
|
|
36637
36883
|
} catch {
|
|
36638
36884
|
}
|
|
36639
36885
|
}
|
|
@@ -36644,7 +36890,7 @@ import { execFile as execFile10 } from "child_process";
|
|
|
36644
36890
|
import { dirname as dirname8, join as join22, resolve as resolve7 } from "path";
|
|
36645
36891
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
36646
36892
|
import { promisify as promisify10 } from "util";
|
|
36647
|
-
var
|
|
36893
|
+
var run10 = promisify10(execFile10);
|
|
36648
36894
|
function reposOf(spec) {
|
|
36649
36895
|
const repos = spec?.repos;
|
|
36650
36896
|
return Array.isArray(repos) ? repos : [];
|
|
@@ -36658,13 +36904,13 @@ async function provisionRepos(target, repos, emit, opts = {}) {
|
|
|
36658
36904
|
if (repo.primary) {
|
|
36659
36905
|
if (opts.clonePrimary && repo.url) {
|
|
36660
36906
|
emit("git", `Cloning ${repo.url}\u2026`);
|
|
36661
|
-
await
|
|
36907
|
+
await run10("git", ["clone", repo.url, target], { timeout: 12e4 });
|
|
36662
36908
|
continue;
|
|
36663
36909
|
}
|
|
36664
36910
|
emit("git", "Initializing the primary repo\u2026");
|
|
36665
|
-
await
|
|
36911
|
+
await run10("git", ["init"], { cwd: target, timeout: 2e4 });
|
|
36666
36912
|
if (repo.url) {
|
|
36667
|
-
await
|
|
36913
|
+
await run10("git", ["remote", "add", "origin", repo.url], { cwd: target, timeout: 2e4 }).catch(
|
|
36668
36914
|
() => void 0
|
|
36669
36915
|
);
|
|
36670
36916
|
}
|
|
@@ -36673,11 +36919,11 @@ async function provisionRepos(target, repos, emit, opts = {}) {
|
|
|
36673
36919
|
const dir2 = join22(target, subDirName(repo));
|
|
36674
36920
|
if (repo.url) {
|
|
36675
36921
|
emit("git", `Cloning sub-repo ${repo.url}\u2026`);
|
|
36676
|
-
await
|
|
36922
|
+
await run10("git", ["clone", repo.url, dir2], { timeout: 12e4 });
|
|
36677
36923
|
} else {
|
|
36678
36924
|
emit("git", `Initializing sub-repo ${subDirName(repo)}\u2026`);
|
|
36679
36925
|
await mkdir8(dir2, { recursive: true });
|
|
36680
|
-
await
|
|
36926
|
+
await run10("git", ["init"], { cwd: dir2, timeout: 2e4 });
|
|
36681
36927
|
}
|
|
36682
36928
|
}
|
|
36683
36929
|
}
|
|
@@ -36948,6 +37194,70 @@ function runWhoami(ctx) {
|
|
|
36948
37194
|
ctx.print(`projects: ${w.projects.length ? w.projects.map((p) => p.name).join(", ") : "(none)"}`);
|
|
36949
37195
|
});
|
|
36950
37196
|
}
|
|
37197
|
+
function aiProfileLabel(c) {
|
|
37198
|
+
return c.label?.trim() || profileDisplayLabel(resolveHomePath(c.profile));
|
|
37199
|
+
}
|
|
37200
|
+
function runAiProfile(ctx) {
|
|
37201
|
+
const dir2 = ctx.info.profileDir;
|
|
37202
|
+
const config = readProfileConfig(dir2);
|
|
37203
|
+
const list = Array.isArray(config.aiProfiles) ? config.aiProfiles : [];
|
|
37204
|
+
const [sub, ...rest] = ctx.args;
|
|
37205
|
+
const action = (sub ?? "list").toLowerCase();
|
|
37206
|
+
if (action === "list") {
|
|
37207
|
+
if (list.length === 0) {
|
|
37208
|
+
ctx.print("No AI profiles configured (config.aiProfiles is empty).", "warn");
|
|
37209
|
+
ctx.print("This worker may use a legacy per-provider config \u2014 /config show to inspect.");
|
|
37210
|
+
return;
|
|
37211
|
+
}
|
|
37212
|
+
const pinned = getPinnedCredential(dir2);
|
|
37213
|
+
const working = getWorkingCredential(dir2);
|
|
37214
|
+
ctx.print("AI profiles (config order = failover priority) \xB7 /ai-profile use <n> to switch:");
|
|
37215
|
+
list.forEach((c, i) => {
|
|
37216
|
+
const usable = isUsableCredential(c);
|
|
37217
|
+
const key = usable ? credentialKey(c.provider, resolveHomePath(c.profile)) : null;
|
|
37218
|
+
const flags = [];
|
|
37219
|
+
if (key && key === pinned) flags.push("pinned");
|
|
37220
|
+
else if (key && !pinned && key === working) flags.push("active");
|
|
37221
|
+
if (c.enabled === false) flags.push("disabled");
|
|
37222
|
+
const model = c.model?.trim() ? ` \xB7 model=${c.model.trim()}` : "";
|
|
37223
|
+
const flagStr = flags.length ? ` (${flags.join(", ")})` : "";
|
|
37224
|
+
const marker = key && key === pinned ? "*" : " ";
|
|
37225
|
+
ctx.print(` ${marker}[${i + 1}] ${c.provider.padEnd(11)} ${aiProfileLabel(c)}${model}${flagStr}`);
|
|
37226
|
+
});
|
|
37227
|
+
if (pinned) {
|
|
37228
|
+
ctx.print("A profile is pinned \u2014 tried first every prompt; /ai-profile reset returns to auto.");
|
|
37229
|
+
}
|
|
37230
|
+
return;
|
|
37231
|
+
}
|
|
37232
|
+
if (action === "reset" || action === "auto" || action === "clear") {
|
|
37233
|
+
clearPinnedCredential(dir2);
|
|
37234
|
+
ctx.print("\u2714 AI profile pin cleared \u2014 failover is back to automatic (config order / remembered).");
|
|
37235
|
+
const working = getWorkingCredential(dir2);
|
|
37236
|
+
ctx.setActiveProfile(working ? labelFromCredentialKey(working) : null);
|
|
37237
|
+
return;
|
|
37238
|
+
}
|
|
37239
|
+
if (action === "use" || action === "switch") {
|
|
37240
|
+
const n = Number(rest[0]);
|
|
37241
|
+
if (!Number.isInteger(n) || n < 1 || n > list.length) {
|
|
37242
|
+
ctx.print(`usage: /ai-profile use <n> (n = 1-${list.length || "?"} from /ai-profile list)`, "error");
|
|
37243
|
+
return;
|
|
37244
|
+
}
|
|
37245
|
+
const cred = list[n - 1];
|
|
37246
|
+
if (!isUsableCredential(cred)) {
|
|
37247
|
+
const why = cred.enabled === false ? "it is disabled" : "it has no usable profile dir / provider";
|
|
37248
|
+
ctx.print(`Profile [${n}] "${aiProfileLabel(cred)}" can't be used \u2014 ${why}.`, "error");
|
|
37249
|
+
return;
|
|
37250
|
+
}
|
|
37251
|
+
const credDir = resolveHomePath(cred.profile);
|
|
37252
|
+
setPinnedCredential(dir2, credentialKey(cred.provider, credDir));
|
|
37253
|
+
ctx.setActiveProfile(profileDisplayLabel(credDir));
|
|
37254
|
+
ctx.print(
|
|
37255
|
+
`\u2714 switched to ${cred.provider} profile "${aiProfileLabel(cred)}" \u2014 tried first from the next prompt (failover keeps the rest as backup).`
|
|
37256
|
+
);
|
|
37257
|
+
return;
|
|
37258
|
+
}
|
|
37259
|
+
ctx.print(`Unknown /ai-profile subcommand "${sub}". Use: list | use <n> | reset.`, "error");
|
|
37260
|
+
}
|
|
36951
37261
|
function runFold(ctx, action) {
|
|
36952
37262
|
if (ctx.maxBlock === 0) {
|
|
36953
37263
|
ctx.print("No collapsible blocks on screen.", "warn");
|
|
@@ -37037,6 +37347,12 @@ var COMMANDS = [
|
|
|
37037
37347
|
description: "Show this machine account + its teams & projects",
|
|
37038
37348
|
run: runWhoami
|
|
37039
37349
|
},
|
|
37350
|
+
{
|
|
37351
|
+
name: "ai-profile",
|
|
37352
|
+
usage: "/ai-profile [list|use <n>|reset]",
|
|
37353
|
+
description: "List AI profiles / switch which one runs (ADR-0250)",
|
|
37354
|
+
run: runAiProfile
|
|
37355
|
+
},
|
|
37040
37356
|
{
|
|
37041
37357
|
name: "config",
|
|
37042
37358
|
usage: "/config [show|init|set <k> <v>|delete <k>]",
|
|
@@ -37085,7 +37401,7 @@ function runSlashCommand(line, base) {
|
|
|
37085
37401
|
}
|
|
37086
37402
|
|
|
37087
37403
|
// src/core/memory-compact.ts
|
|
37088
|
-
import { spawn as
|
|
37404
|
+
import { spawn as spawn9 } from "child_process";
|
|
37089
37405
|
var COMPACT_TIMEOUT_MS = 6e4;
|
|
37090
37406
|
function buildPrompt3(input) {
|
|
37091
37407
|
return [
|
|
@@ -37113,7 +37429,7 @@ function runOnce2(cmd, profile, prompt, cwd2, extraEnv) {
|
|
|
37113
37429
|
...extraEnv,
|
|
37114
37430
|
...profile ? { CLAUDE_CONFIG_DIR: profile.dir } : {}
|
|
37115
37431
|
};
|
|
37116
|
-
const child =
|
|
37432
|
+
const child = spawn9(cmd, args, { stdio: ["pipe", "pipe", "pipe"], cwd: cwd2, env: env3 });
|
|
37117
37433
|
let out = "";
|
|
37118
37434
|
let err = "";
|
|
37119
37435
|
const timer = setTimeout(() => {
|
|
@@ -37201,125 +37517,6 @@ function applyConfigText(profileDir2, text) {
|
|
|
37201
37517
|
return { ok: true };
|
|
37202
37518
|
}
|
|
37203
37519
|
|
|
37204
|
-
// src/core/worker-tools.ts
|
|
37205
|
-
import { spawn as spawn9 } from "child_process";
|
|
37206
|
-
var NPM_NAME = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
37207
|
-
function run10(cmd, args, onLine, timeoutMs = 18e4) {
|
|
37208
|
-
return new Promise((resolve8) => {
|
|
37209
|
-
let out = "";
|
|
37210
|
-
let buf = "";
|
|
37211
|
-
let child = null;
|
|
37212
|
-
const done = (code) => {
|
|
37213
|
-
clearTimeout(timer);
|
|
37214
|
-
if (onLine && buf.trim()) onLine(buf.trim());
|
|
37215
|
-
resolve8({ code, out });
|
|
37216
|
-
};
|
|
37217
|
-
const timer = setTimeout(() => {
|
|
37218
|
-
child?.kill();
|
|
37219
|
-
if (onLine) onLine(`timed out after ${Math.round(timeoutMs / 1e3)}s`);
|
|
37220
|
-
done(124);
|
|
37221
|
-
}, timeoutMs);
|
|
37222
|
-
timer.unref?.();
|
|
37223
|
-
const feed = (d) => {
|
|
37224
|
-
const text = d.toString("utf8");
|
|
37225
|
-
out += text;
|
|
37226
|
-
if (!onLine) return;
|
|
37227
|
-
buf += text;
|
|
37228
|
-
let nl;
|
|
37229
|
-
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
37230
|
-
const line = buf.slice(0, nl).replace(/\r$/, "");
|
|
37231
|
-
buf = buf.slice(nl + 1);
|
|
37232
|
-
onLine(line);
|
|
37233
|
-
}
|
|
37234
|
-
};
|
|
37235
|
-
try {
|
|
37236
|
-
child = spawn9(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
37237
|
-
child.stdout?.on("data", feed);
|
|
37238
|
-
child.stderr?.on("data", feed);
|
|
37239
|
-
child.on("exit", (code) => done(code ?? 0));
|
|
37240
|
-
child.on("error", () => done(127));
|
|
37241
|
-
} catch {
|
|
37242
|
-
done(127);
|
|
37243
|
-
}
|
|
37244
|
-
});
|
|
37245
|
-
}
|
|
37246
|
-
async function detectOne(cmd, versionArg) {
|
|
37247
|
-
const { code, out } = await run10(cmd, [versionArg], void 0, 15e3);
|
|
37248
|
-
if (code !== 0) return null;
|
|
37249
|
-
const first = out.split("\n").map((l) => l.trim()).find(Boolean);
|
|
37250
|
-
return first ?? "";
|
|
37251
|
-
}
|
|
37252
|
-
async function listExtras() {
|
|
37253
|
-
const { out } = await run10("npm", ["ls", "-g", "--depth=0", "--json"], void 0, 2e4);
|
|
37254
|
-
if (!out.trim()) return [];
|
|
37255
|
-
let deps = {};
|
|
37256
|
-
try {
|
|
37257
|
-
deps = JSON.parse(out).dependencies ?? {};
|
|
37258
|
-
} catch {
|
|
37259
|
-
return [];
|
|
37260
|
-
}
|
|
37261
|
-
const catalogPkgs = new Set(WORKER_TOOL_BUNDLED_GLOBALS);
|
|
37262
|
-
for (const t of WORKER_TOOL_CATALOG) {
|
|
37263
|
-
catalogPkgs.add(t.id);
|
|
37264
|
-
if (t.installPackage) catalogPkgs.add(t.installPackage);
|
|
37265
|
-
}
|
|
37266
|
-
return Object.entries(deps).filter(([name]) => !catalogPkgs.has(name)).map(([name, info]) => ({
|
|
37267
|
-
id: name,
|
|
37268
|
-
label: name,
|
|
37269
|
-
category: "runtime",
|
|
37270
|
-
installed: true,
|
|
37271
|
-
version: info.version ?? null,
|
|
37272
|
-
installable: true
|
|
37273
|
-
}));
|
|
37274
|
-
}
|
|
37275
|
-
async function detectWorkerTools() {
|
|
37276
|
-
const catalog = await Promise.all(
|
|
37277
|
-
WORKER_TOOL_CATALOG.map(async (t) => {
|
|
37278
|
-
const version = await detectOne(t.id, t.versionArg);
|
|
37279
|
-
return {
|
|
37280
|
-
id: t.id,
|
|
37281
|
-
label: t.label,
|
|
37282
|
-
category: t.category,
|
|
37283
|
-
installed: version !== null,
|
|
37284
|
-
version,
|
|
37285
|
-
installable: t.installable
|
|
37286
|
-
};
|
|
37287
|
-
})
|
|
37288
|
-
);
|
|
37289
|
-
const extras = await listExtras().catch(() => []);
|
|
37290
|
-
return { catalog, extras };
|
|
37291
|
-
}
|
|
37292
|
-
function resolvePackage(name, op) {
|
|
37293
|
-
const entry = WORKER_TOOL_CATALOG.find((t) => t.id === name);
|
|
37294
|
-
if (entry) {
|
|
37295
|
-
if (!entry.installable) {
|
|
37296
|
-
return { error: `"${name}" is a prerequisite and cannot be ${op}ed from here.` };
|
|
37297
|
-
}
|
|
37298
|
-
return { pkg: entry.installPackage ?? name };
|
|
37299
|
-
}
|
|
37300
|
-
if (WORKER_TOOL_PREREQUISITE_IDS.includes(name)) {
|
|
37301
|
-
return { error: `"${name}" is a prerequisite and cannot be ${op}ed from here.` };
|
|
37302
|
-
}
|
|
37303
|
-
if (WORKER_TOOL_BUNDLED_GLOBALS.includes(name)) {
|
|
37304
|
-
return { error: `"${name}" ships with Node and cannot be ${op}ed from here.` };
|
|
37305
|
-
}
|
|
37306
|
-
if (!NPM_NAME.test(name)) return { error: `Invalid package name: "${name}".` };
|
|
37307
|
-
return { pkg: name };
|
|
37308
|
-
}
|
|
37309
|
-
function opArgs(op, manager, pkg) {
|
|
37310
|
-
if (manager === "pnpm") return [op === "install" ? "add" : "remove", "-g", pkg];
|
|
37311
|
-
return [op === "install" ? "install" : "uninstall", "-g", pkg];
|
|
37312
|
-
}
|
|
37313
|
-
async function runWorkerToolOp(op, name, manager, onLine) {
|
|
37314
|
-
const resolved = resolvePackage(name, op);
|
|
37315
|
-
if (resolved.error || !resolved.pkg) {
|
|
37316
|
-
return { ok: false, exitCode: 1, error: resolved.error ?? "Invalid package." };
|
|
37317
|
-
}
|
|
37318
|
-
onLine(`$ ${manager} ${opArgs(op, manager, resolved.pkg).join(" ")}`);
|
|
37319
|
-
const { code } = await run10(manager, opArgs(op, manager, resolved.pkg), onLine);
|
|
37320
|
-
return { ok: code === 0, exitCode: code, error: code === 0 ? void 0 : `${manager} exited ${code}` };
|
|
37321
|
-
}
|
|
37322
|
-
|
|
37323
37520
|
// src/core/ws-client.ts
|
|
37324
37521
|
var MEMORY_SEED_HEADER = "CONTEXT MEMORY from earlier in this conversation (may span prior sessions/accounts). Use it as background; do not repeat it back unless relevant:";
|
|
37325
37522
|
var HEARTBEAT_MS = 3e4;
|
|
@@ -37417,6 +37614,9 @@ var WsClient = class {
|
|
|
37417
37614
|
reconnectEntryId = null;
|
|
37418
37615
|
/** Org-configured reconnect cap (seconds) from the last ws_token — overrides env. */
|
|
37419
37616
|
orgReconnectMaxSec = null;
|
|
37617
|
+
/** Org timezone (IANA) from the last ws_token (ADR-0132) — a rented worker follows its renter's
|
|
37618
|
+
* org; used to stamp the AI-run start time on the `aireq` transcript marker (ADR-0249). */
|
|
37619
|
+
orgTimezone = "UTC";
|
|
37420
37620
|
/** Env override for the reconnect cap (seconds); resolved once at construction. */
|
|
37421
37621
|
envReconnectMaxSec = envReconnectMaxSec();
|
|
37422
37622
|
/** Process start (ms) — uptime for a web-dispatched `/status` (ADR-0249). */
|
|
@@ -37601,6 +37801,9 @@ var WsClient = class {
|
|
|
37601
37801
|
if (typeof token.reconnectMaxBackoffSec === "number") {
|
|
37602
37802
|
this.orgReconnectMaxSec = token.reconnectMaxBackoffSec;
|
|
37603
37803
|
}
|
|
37804
|
+
if (typeof token.timezone === "string" && token.timezone) {
|
|
37805
|
+
this.orgTimezone = token.timezone;
|
|
37806
|
+
}
|
|
37604
37807
|
if (typeof token.autoUpdateDaily === "boolean") {
|
|
37605
37808
|
this.updateScheduler.configure({
|
|
37606
37809
|
autoUpdateDaily: token.autoUpdateDaily,
|
|
@@ -38100,14 +38303,21 @@ var WsClient = class {
|
|
|
38100
38303
|
break;
|
|
38101
38304
|
}
|
|
38102
38305
|
case WsChannels.TOOLS_LIST:
|
|
38103
|
-
void detectWorkerTools().then(
|
|
38306
|
+
void detectWorkerTools(readProfileConfig(this.context.profileDir).autoUpdateTools ?? []).then(
|
|
38104
38307
|
(reply) => this.send(WsChannels.TOOLS_LIST, reply, message.id)
|
|
38105
38308
|
);
|
|
38106
38309
|
break;
|
|
38310
|
+
case WsChannels.TOOLS_AUTOUPDATE: {
|
|
38311
|
+
const req = payload;
|
|
38312
|
+
const res = setToolAutoUpdate(this.context.profileDir, req.name, req.enabled);
|
|
38313
|
+
this.send(WsChannels.TOOLS_AUTOUPDATE, res, message.id);
|
|
38314
|
+
break;
|
|
38315
|
+
}
|
|
38107
38316
|
case WsChannels.TOOLS_INSTALL:
|
|
38108
|
-
case WsChannels.TOOLS_UNINSTALL:
|
|
38317
|
+
case WsChannels.TOOLS_UNINSTALL:
|
|
38318
|
+
case WsChannels.TOOLS_UPDATE: {
|
|
38109
38319
|
const req = payload;
|
|
38110
|
-
const op = message.channel === WsChannels.TOOLS_INSTALL ? "install" : "uninstall";
|
|
38320
|
+
const op = message.channel === WsChannels.TOOLS_INSTALL ? "install" : message.channel === WsChannels.TOOLS_UPDATE ? "update" : "uninstall";
|
|
38111
38321
|
this.send(message.channel, { started: true }, message.id);
|
|
38112
38322
|
void runWorkerToolOp(
|
|
38113
38323
|
op,
|
|
@@ -38288,9 +38498,10 @@ var WsClient = class {
|
|
|
38288
38498
|
const unified = isUnifiedConfig(config);
|
|
38289
38499
|
const mode = config.aiFailoverMode ?? "remember";
|
|
38290
38500
|
const workingDir = unified ? null : getWorkingProfile(this.context.profileDir, cmd);
|
|
38291
|
-
const
|
|
38501
|
+
const pinnedCred = unified ? getPinnedCredential(this.context.profileDir) : null;
|
|
38502
|
+
const workingCred = unified ? pinnedCred ?? (mode === "remember" ? getWorkingCredential(this.context.profileDir) : null) : null;
|
|
38292
38503
|
const startDir = unified ? null : this.profileUnderSessionPressure(config, cmd, workingDir);
|
|
38293
|
-
const startCred = unified ? this.credentialUnderSessionPressure(config, workingCred) : null;
|
|
38504
|
+
const startCred = unified && !pinnedCred ? this.credentialUnderSessionPressure(config, workingCred) : null;
|
|
38294
38505
|
if (startDir && startDir !== workingDir) {
|
|
38295
38506
|
this.bus.push({
|
|
38296
38507
|
source: origin,
|
|
@@ -38322,7 +38533,8 @@ ${guardedPrompt}`;
|
|
|
38322
38533
|
}
|
|
38323
38534
|
const plan = planAiRun(effectivePrompt, config, hint, resume, oneShot);
|
|
38324
38535
|
const markerArgs = plan.attempts[0]?.args ?? [];
|
|
38325
|
-
|
|
38536
|
+
const startStamp = formatTimestampInZone(/* @__PURE__ */ new Date(), this.orgTimezone);
|
|
38537
|
+
this.bus.push({ source: origin, kind: "aireq", text: `${startStamp} ${plan.cmd} \u2039 ${prompt}` });
|
|
38326
38538
|
logger.info("command.ai", { commandId, origin, cmd: plan.cmd, profiles: plan.attempts.length });
|
|
38327
38539
|
if (origin === "local") {
|
|
38328
38540
|
this.send(WsChannels.COMMAND_ANNOUNCE, {
|
|
@@ -38557,6 +38769,8 @@ ${guardedPrompt}`;
|
|
|
38557
38769
|
submitAi: () => {
|
|
38558
38770
|
},
|
|
38559
38771
|
// only /claude-cmd uses this, and it is special-cased above
|
|
38772
|
+
// /ai-profile use/reset from the web Console updates the shared header too (ADR-0250).
|
|
38773
|
+
setActiveProfile: (label) => this.bus.setActiveProfile(label),
|
|
38560
38774
|
reconnect: () => this.reconnectNow(),
|
|
38561
38775
|
expand: () => emit("Use the web console's fold viewer to expand blocks."),
|
|
38562
38776
|
collapse: () => emit("Use the web console's fold viewer to collapse blocks."),
|
|
@@ -45263,12 +45477,17 @@ function textColor(line) {
|
|
|
45263
45477
|
if (line.kind === "exit") return "green";
|
|
45264
45478
|
return void 0;
|
|
45265
45479
|
}
|
|
45480
|
+
var START_STAMP_RE = /^(\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2} )/;
|
|
45266
45481
|
function AiMarkerLine({ line }) {
|
|
45267
45482
|
const arrow = line.kind === "aireq" ? "\u2039" : "\u203A";
|
|
45268
45483
|
const idx = line.text.indexOf(arrow);
|
|
45269
45484
|
if (idx < 0) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: line.text || " " });
|
|
45485
|
+
const before = line.text.slice(0, idx);
|
|
45486
|
+
const stamp = line.kind === "aireq" ? START_STAMP_RE.exec(before)?.[1] ?? "" : "";
|
|
45487
|
+
const name = stamp ? before.slice(stamp.length) : before;
|
|
45270
45488
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
|
|
45271
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, {
|
|
45489
|
+
stamp ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: stamp }) : null,
|
|
45490
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "cyan", bold: true, children: name }),
|
|
45272
45491
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: arrow }),
|
|
45273
45492
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: line.text.slice(idx + 1) })
|
|
45274
45493
|
] });
|
|
@@ -45685,6 +45904,9 @@ function App2({
|
|
|
45685
45904
|
setPendingConfirm({ prompt, onYes });
|
|
45686
45905
|
},
|
|
45687
45906
|
submitAi: (aiInput) => bus.submitLocal(aiInput),
|
|
45907
|
+
// /ai-profile use/reset updates the header's active-profile label at once (ADR-0250);
|
|
45908
|
+
// the bus event feeds the React state (see the onActiveProfile subscription above).
|
|
45909
|
+
setActiveProfile: (label) => bus.setActiveProfile(label),
|
|
45688
45910
|
reconnect: () => bus.requestReconnect(),
|
|
45689
45911
|
expand: expandFold,
|
|
45690
45912
|
collapse: collapseFold,
|