@4pm/cli 1.5.3 → 1.5.11
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 +305 -178
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -32912,7 +32912,7 @@ var deletedFilterSchema = external_exports.enum(["true", "false"]).transform((v)
|
|
|
32912
32912
|
|
|
32913
32913
|
// vendor/@4pm/dto/command.ts
|
|
32914
32914
|
var COMMAND_MAX_LEN = 8e3;
|
|
32915
|
-
var AI_PROMPT_MAX_LEN =
|
|
32915
|
+
var AI_PROMPT_MAX_LEN = 5e5;
|
|
32916
32916
|
var dispatchCommandRequestSchema = external_exports.object({
|
|
32917
32917
|
projectId: external_exports.string().uuid(),
|
|
32918
32918
|
// Target cli. Required unless `pick` is set — with `pick:"idle"` the server chooses the
|
|
@@ -33068,12 +33068,24 @@ var listMachineLinksQuerySchema = baseRequestSchema.extend({
|
|
|
33068
33068
|
var updateMachineLinkRequestSchema = external_exports.object({
|
|
33069
33069
|
hashcode3ExpiresAt: external_exports.string().datetime().nullable()
|
|
33070
33070
|
});
|
|
33071
|
+
var workerToolCopySchema = external_exports.object({
|
|
33072
|
+
/** Source machine-link id whose `toolSnapshot` supplies the manifest. */
|
|
33073
|
+
sourceLinkId: external_exports.string().uuid(),
|
|
33074
|
+
/** Target machine-link ids to queue the manifest for (pushed on their next connect). */
|
|
33075
|
+
targetLinkIds: external_exports.array(external_exports.string().uuid()).min(1).max(200)
|
|
33076
|
+
});
|
|
33071
33077
|
var workerToolInstallSchema = external_exports.object({
|
|
33072
33078
|
/** Catalog id or npm package name (validated npm-name shape on the cli). */
|
|
33073
33079
|
name: external_exports.string().trim().min(1).max(214),
|
|
33074
33080
|
/** Package manager to run the global install with. */
|
|
33075
33081
|
manager: external_exports.enum(["npm", "pnpm"])
|
|
33076
33082
|
});
|
|
33083
|
+
var workerToolAutoUpdateSchema = external_exports.object({
|
|
33084
|
+
/** Catalog id or installed extra's npm package name. */
|
|
33085
|
+
name: external_exports.string().trim().min(1).max(214),
|
|
33086
|
+
/** true = flag this tool for auto-update to @latest; false = clear it. */
|
|
33087
|
+
enabled: external_exports.boolean()
|
|
33088
|
+
});
|
|
33077
33089
|
var configWriteRequestSchema = external_exports.object({
|
|
33078
33090
|
config: external_exports.string().max(256 * 1024)
|
|
33079
33091
|
});
|
|
@@ -33683,6 +33695,22 @@ var WsChannels = {
|
|
|
33683
33695
|
TOOLS_UPDATE: "tools.update",
|
|
33684
33696
|
TOOLS_PROGRESS: "tools.progress",
|
|
33685
33697
|
TOOLS_DONE: "tools.done",
|
|
33698
|
+
// toggle per-tool auto-update (ADR-0253): request/reply — the cli persists the flag in
|
|
33699
|
+
// config.json (`autoUpdateTools`); the ADR-0074 idle daily tick later runs the flagged tools.
|
|
33700
|
+
// (ADR-0254: the flag is now persisted to the DB `toolSnapshot`; this channel is repurposed —
|
|
33701
|
+
// machine-0056 forwards it to an ONLINE worker only, to update its local config.json mirror
|
|
33702
|
+
// immediately instead of waiting for the next ws_token seed.)
|
|
33703
|
+
TOOLS_AUTOUPDATE: "tools.autoUpdate",
|
|
33704
|
+
// cli → server one-way (ADR-0254): report the worker's detected tool snapshot — the server
|
|
33705
|
+
// persists it to `MachineLink.toolSnapshot` for the DB-backed Tools panel + restore-on-boot.
|
|
33706
|
+
// Sent on the ADR-0074 daily idle tick, after each install/uninstall/update op, and on boot
|
|
33707
|
+
// AFTER restore completes. The read/restore direction rides `ws_token` (seeds the cli).
|
|
33708
|
+
TOOLS_REPORT: "tools.report",
|
|
33709
|
+
// server → cli (reply, ADR-0254): push a tool manifest to reconcile NOW — used by "copy tools"
|
|
33710
|
+
// to apply immediately to an online target (the offline path rides `ws_token.toolRestore` on the
|
|
33711
|
+
// next connect). The cli installs each missing/mismatched `name@version`, then reports; the reply
|
|
33712
|
+
// says whether it applied so the server can clear the pending manifest.
|
|
33713
|
+
TOOLS_RESTORE: "tools.restore",
|
|
33686
33714
|
// autonomous mode control (ADR-0152): read settings+status+books+approvals, tail logs, and
|
|
33687
33715
|
// write {settings|approvals|userTodo|cron} — the dashboard drives the worker's autonomous engine.
|
|
33688
33716
|
AUTONOMOUS_READ: "autonomous.read",
|
|
@@ -33843,7 +33871,7 @@ import { existsSync as existsSync13, mkdirSync as mkdirSync5, rmSync as rmSync4
|
|
|
33843
33871
|
import { randomUUID as randomCommandId } from "crypto";
|
|
33844
33872
|
|
|
33845
33873
|
// src/core/update-scheduler.ts
|
|
33846
|
-
import { spawn } from "child_process";
|
|
33874
|
+
import { spawn as spawn2 } from "child_process";
|
|
33847
33875
|
|
|
33848
33876
|
// src/common/logger/logger.ts
|
|
33849
33877
|
import { appendFile, mkdir, readdir, unlink } from "fs/promises";
|
|
@@ -33948,17 +33976,191 @@ ${content.slice(-LOG_UPLOAD_CONTENT_MAX)}`;
|
|
|
33948
33976
|
}
|
|
33949
33977
|
}
|
|
33950
33978
|
|
|
33979
|
+
// src/core/worker-tools.ts
|
|
33980
|
+
import { spawn } from "child_process";
|
|
33981
|
+
var NPM_NAME = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
33982
|
+
function run(cmd, args, onLine, timeoutMs = 18e4) {
|
|
33983
|
+
return new Promise((resolve8) => {
|
|
33984
|
+
let out = "";
|
|
33985
|
+
let buf = "";
|
|
33986
|
+
let child = null;
|
|
33987
|
+
const done = (code) => {
|
|
33988
|
+
clearTimeout(timer);
|
|
33989
|
+
if (onLine && buf.trim()) onLine(buf.trim());
|
|
33990
|
+
resolve8({ code, out });
|
|
33991
|
+
};
|
|
33992
|
+
const timer = setTimeout(() => {
|
|
33993
|
+
child?.kill();
|
|
33994
|
+
if (onLine) onLine(`timed out after ${Math.round(timeoutMs / 1e3)}s`);
|
|
33995
|
+
done(124);
|
|
33996
|
+
}, timeoutMs);
|
|
33997
|
+
timer.unref?.();
|
|
33998
|
+
const feed = (d) => {
|
|
33999
|
+
const text = d.toString("utf8");
|
|
34000
|
+
out += text;
|
|
34001
|
+
if (!onLine) return;
|
|
34002
|
+
buf += text;
|
|
34003
|
+
let nl;
|
|
34004
|
+
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
34005
|
+
const line = buf.slice(0, nl).replace(/\r$/, "");
|
|
34006
|
+
buf = buf.slice(nl + 1);
|
|
34007
|
+
onLine(line);
|
|
34008
|
+
}
|
|
34009
|
+
};
|
|
34010
|
+
try {
|
|
34011
|
+
child = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
34012
|
+
child.stdout?.on("data", feed);
|
|
34013
|
+
child.stderr?.on("data", feed);
|
|
34014
|
+
child.on("exit", (code) => done(code ?? 0));
|
|
34015
|
+
child.on("error", () => done(127));
|
|
34016
|
+
} catch {
|
|
34017
|
+
done(127);
|
|
34018
|
+
}
|
|
34019
|
+
});
|
|
34020
|
+
}
|
|
34021
|
+
async function detectOne(cmd, versionArg) {
|
|
34022
|
+
const { code, out } = await run(cmd, [versionArg], void 0, 15e3);
|
|
34023
|
+
if (code !== 0) return null;
|
|
34024
|
+
const first = out.split("\n").map((l) => l.trim()).find(Boolean);
|
|
34025
|
+
return first ?? "";
|
|
34026
|
+
}
|
|
34027
|
+
async function listExtras(autoUpdateSet = /* @__PURE__ */ new Set()) {
|
|
34028
|
+
const { out } = await run("npm", ["ls", "-g", "--depth=0", "--json"], void 0, 2e4);
|
|
34029
|
+
if (!out.trim()) return [];
|
|
34030
|
+
let deps = {};
|
|
34031
|
+
try {
|
|
34032
|
+
deps = JSON.parse(out).dependencies ?? {};
|
|
34033
|
+
} catch {
|
|
34034
|
+
return [];
|
|
34035
|
+
}
|
|
34036
|
+
const catalogPkgs = new Set(WORKER_TOOL_BUNDLED_GLOBALS);
|
|
34037
|
+
for (const t of WORKER_TOOL_CATALOG) {
|
|
34038
|
+
catalogPkgs.add(t.id);
|
|
34039
|
+
if (t.installPackage) catalogPkgs.add(t.installPackage);
|
|
34040
|
+
}
|
|
34041
|
+
return Object.entries(deps).filter(([name]) => !catalogPkgs.has(name)).map(([name, info]) => ({
|
|
34042
|
+
id: name,
|
|
34043
|
+
label: name,
|
|
34044
|
+
category: "runtime",
|
|
34045
|
+
installed: true,
|
|
34046
|
+
version: info.version ?? null,
|
|
34047
|
+
installable: true,
|
|
34048
|
+
updatable: true,
|
|
34049
|
+
autoUpdate: autoUpdateSet.has(name)
|
|
34050
|
+
}));
|
|
34051
|
+
}
|
|
34052
|
+
async function detectWorkerTools(autoUpdateTools = []) {
|
|
34053
|
+
const autoUpdateSet = new Set(autoUpdateTools);
|
|
34054
|
+
const catalog = await Promise.all(
|
|
34055
|
+
WORKER_TOOL_CATALOG.map(async (t) => {
|
|
34056
|
+
const version = await detectOne(t.id, t.versionArg);
|
|
34057
|
+
return {
|
|
34058
|
+
id: t.id,
|
|
34059
|
+
label: t.label,
|
|
34060
|
+
category: t.category,
|
|
34061
|
+
installed: version !== null,
|
|
34062
|
+
version,
|
|
34063
|
+
installable: t.installable,
|
|
34064
|
+
updatable: t.updatable,
|
|
34065
|
+
autoUpdate: autoUpdateSet.has(t.id)
|
|
34066
|
+
};
|
|
34067
|
+
})
|
|
34068
|
+
);
|
|
34069
|
+
const extras = await listExtras(autoUpdateSet).catch(() => []);
|
|
34070
|
+
return { catalog, extras };
|
|
34071
|
+
}
|
|
34072
|
+
function opPast(op) {
|
|
34073
|
+
return op === "install" ? "installed" : op === "uninstall" ? "uninstalled" : "updated";
|
|
34074
|
+
}
|
|
34075
|
+
function resolvePackage(name, op) {
|
|
34076
|
+
const entry = WORKER_TOOL_CATALOG.find((t) => t.id === name);
|
|
34077
|
+
if (entry) {
|
|
34078
|
+
const allowed = op === "update" ? entry.updatable : entry.installable;
|
|
34079
|
+
if (!allowed) {
|
|
34080
|
+
return { error: `"${name}" is a prerequisite and cannot be ${opPast(op)} from here.` };
|
|
34081
|
+
}
|
|
34082
|
+
return { pkg: entry.installPackage ?? name };
|
|
34083
|
+
}
|
|
34084
|
+
if (WORKER_TOOL_PREREQUISITE_IDS.includes(name)) {
|
|
34085
|
+
return { error: `"${name}" is a prerequisite and cannot be ${opPast(op)} from here.` };
|
|
34086
|
+
}
|
|
34087
|
+
if (WORKER_TOOL_BUNDLED_GLOBALS.includes(name)) {
|
|
34088
|
+
return { error: `"${name}" ships with Node and cannot be ${opPast(op)} from here.` };
|
|
34089
|
+
}
|
|
34090
|
+
if (!NPM_NAME.test(name)) return { error: `Invalid package name: "${name}".` };
|
|
34091
|
+
return { pkg: name };
|
|
34092
|
+
}
|
|
34093
|
+
function opArgs(op, manager, pkg) {
|
|
34094
|
+
if (op === "uninstall") return manager === "pnpm" ? ["remove", "-g", pkg] : ["uninstall", "-g", pkg];
|
|
34095
|
+
const target = op === "update" ? `${pkg}@latest` : pkg;
|
|
34096
|
+
return manager === "pnpm" ? ["add", "-g", target] : ["install", "-g", target];
|
|
34097
|
+
}
|
|
34098
|
+
async function runWorkerToolOp(op, name, manager, onLine) {
|
|
34099
|
+
const resolved = resolvePackage(name, op);
|
|
34100
|
+
if (resolved.error || !resolved.pkg) {
|
|
34101
|
+
return { ok: false, exitCode: 1, error: resolved.error ?? "Invalid package." };
|
|
34102
|
+
}
|
|
34103
|
+
onLine(`$ ${manager} ${opArgs(op, manager, resolved.pkg).join(" ")}`);
|
|
34104
|
+
const { code } = await run(manager, opArgs(op, manager, resolved.pkg), onLine);
|
|
34105
|
+
return { ok: code === 0, exitCode: code, error: code === 0 ? void 0 : `${manager} exited ${code}` };
|
|
34106
|
+
}
|
|
34107
|
+
function setToolAutoUpdate(profileDir2, name, enabled) {
|
|
34108
|
+
const resolved = resolvePackage(name, "update");
|
|
34109
|
+
if (resolved.error) return { ok: false, error: resolved.error };
|
|
34110
|
+
const current = readProfileConfig(profileDir2).autoUpdateTools ?? [];
|
|
34111
|
+
const set = new Set(current);
|
|
34112
|
+
if (enabled) set.add(name);
|
|
34113
|
+
else set.delete(name);
|
|
34114
|
+
writeProfileConfig(profileDir2, { autoUpdateTools: [...set] });
|
|
34115
|
+
return { ok: true };
|
|
34116
|
+
}
|
|
34117
|
+
async function autoUpdateFlaggedTools(tools, onLine) {
|
|
34118
|
+
for (const name of tools) {
|
|
34119
|
+
const res = await runWorkerToolOp("update", name, "npm", onLine).catch(
|
|
34120
|
+
(err) => ({ ok: false, exitCode: 1, error: String(err) })
|
|
34121
|
+
);
|
|
34122
|
+
onLine(res.ok ? `\u2713 ${name} updated` : `\u2717 ${name}: ${res.error ?? "failed"}`);
|
|
34123
|
+
}
|
|
34124
|
+
}
|
|
34125
|
+
async function installPinned(name, version, manager, onLine) {
|
|
34126
|
+
const resolved = resolvePackage(name, "update");
|
|
34127
|
+
if (resolved.error || !resolved.pkg) return { ok: false, error: resolved.error ?? "Invalid package." };
|
|
34128
|
+
const target = `${resolved.pkg}@${version}`;
|
|
34129
|
+
const args = manager === "pnpm" ? ["add", "-g", target] : ["install", "-g", target];
|
|
34130
|
+
onLine(`$ ${manager} ${args.join(" ")}`);
|
|
34131
|
+
const { code } = await run(manager, args, onLine);
|
|
34132
|
+
return { ok: code === 0, error: code === 0 ? void 0 : `${manager} exited ${code}` };
|
|
34133
|
+
}
|
|
34134
|
+
async function reconcileTools(manifest, onLine) {
|
|
34135
|
+
if (manifest.length === 0) return;
|
|
34136
|
+
const { catalog, extras } = await detectWorkerTools();
|
|
34137
|
+
const versionByName = new Map(
|
|
34138
|
+
[...catalog, ...extras].filter((r) => r.installed && r.version).map((r) => [r.id, r.version])
|
|
34139
|
+
);
|
|
34140
|
+
for (const entry of manifest) {
|
|
34141
|
+
const current = versionByName.get(entry.name);
|
|
34142
|
+
if (current && current.includes(entry.version)) continue;
|
|
34143
|
+
onLine(`Restoring ${entry.name}@${entry.version}\u2026`);
|
|
34144
|
+
const res = await installPinned(entry.name, entry.version, entry.manager, onLine).catch(
|
|
34145
|
+
(err) => ({ ok: false, error: String(err) })
|
|
34146
|
+
);
|
|
34147
|
+
onLine(res.ok ? `\u2713 ${entry.name}@${entry.version}` : `\u2717 ${entry.name}: ${res.error ?? "failed"}`);
|
|
34148
|
+
}
|
|
34149
|
+
}
|
|
34150
|
+
|
|
33951
34151
|
// src/core/update-scheduler.ts
|
|
33952
34152
|
var TICK_MS = 6e4;
|
|
33953
34153
|
var UpdateScheduler = class {
|
|
33954
|
-
constructor(serverUrl, profileDir2, bus) {
|
|
34154
|
+
constructor(serverUrl, profileDir2, bus, onToolsChanged) {
|
|
33955
34155
|
this.serverUrl = serverUrl;
|
|
33956
34156
|
this.profileDir = profileDir2;
|
|
33957
34157
|
this.bus = bus;
|
|
34158
|
+
this.onToolsChanged = onToolsChanged;
|
|
33958
34159
|
}
|
|
33959
34160
|
serverUrl;
|
|
33960
34161
|
profileDir;
|
|
33961
34162
|
bus;
|
|
34163
|
+
onToolsChanged;
|
|
33962
34164
|
policy = null;
|
|
33963
34165
|
timer = null;
|
|
33964
34166
|
/** Date key (YYYY-MM-DD in org tz) whose update already ran — enforces once per day. */
|
|
@@ -34030,6 +34232,7 @@ var UpdateScheduler = class {
|
|
|
34030
34232
|
async runUpdate() {
|
|
34031
34233
|
this.updating = true;
|
|
34032
34234
|
try {
|
|
34235
|
+
await this.updateFlaggedTools();
|
|
34033
34236
|
this.bus.log("Scheduled auto-update: checking for a new version\u2026");
|
|
34034
34237
|
const result = await updateToLatest(this.serverUrl);
|
|
34035
34238
|
if (result.action === "already-latest") {
|
|
@@ -34043,7 +34246,7 @@ var UpdateScheduler = class {
|
|
|
34043
34246
|
}
|
|
34044
34247
|
logger.info("update.scheduled.updated", { version: result.version });
|
|
34045
34248
|
this.bus.log(`Updated to ${result.version} \u2014 restarting to apply\u2026`);
|
|
34046
|
-
const child =
|
|
34249
|
+
const child = spawn2(process.execPath, process.argv.slice(1), {
|
|
34047
34250
|
stdio: "inherit",
|
|
34048
34251
|
env: { ...process.env, FOURPM_NO_UPDATE: "1" }
|
|
34049
34252
|
});
|
|
@@ -34054,16 +34257,26 @@ var UpdateScheduler = class {
|
|
|
34054
34257
|
this.updating = false;
|
|
34055
34258
|
}
|
|
34056
34259
|
}
|
|
34260
|
+
/** Update each tool flagged for auto-update in config.json to @latest (ADR-0253) — best-effort. */
|
|
34261
|
+
async updateFlaggedTools() {
|
|
34262
|
+
const tools = readProfileConfig(this.profileDir).autoUpdateTools ?? [];
|
|
34263
|
+
if (tools.length === 0) return;
|
|
34264
|
+
this.bus.log(`Scheduled tool auto-update: ${tools.join(", ")}\u2026`);
|
|
34265
|
+
await autoUpdateFlaggedTools(tools, (line) => logger.info("update.tool.line", { line })).catch(
|
|
34266
|
+
(err) => logger.warn("update.tool.error", { error: String(err) })
|
|
34267
|
+
);
|
|
34268
|
+
this.onToolsChanged?.();
|
|
34269
|
+
}
|
|
34057
34270
|
};
|
|
34058
34271
|
|
|
34059
34272
|
// src/core/executor.ts
|
|
34060
|
-
import { spawn as
|
|
34273
|
+
import { spawn as spawn3 } from "child_process";
|
|
34061
34274
|
var KILL_GRACE_MS = 5e3;
|
|
34062
34275
|
async function runCommand(dispatch, emit, opts) {
|
|
34063
34276
|
let seq = 0;
|
|
34064
34277
|
let child;
|
|
34065
34278
|
try {
|
|
34066
|
-
child =
|
|
34279
|
+
child = spawn3(dispatch.cmd, dispatch.args ?? [], {
|
|
34067
34280
|
cwd: dispatch.path,
|
|
34068
34281
|
env: { ...process.env, ...dispatch.env },
|
|
34069
34282
|
shell: false
|
|
@@ -34657,7 +34870,7 @@ function planLegacyRun(prompt, config, workingDir, resume, oneShot) {
|
|
|
34657
34870
|
}
|
|
34658
34871
|
|
|
34659
34872
|
// src/core/claude-usage.ts
|
|
34660
|
-
import { spawn as
|
|
34873
|
+
import { spawn as spawn4 } from "child_process";
|
|
34661
34874
|
import { readFileSync as readFileSync9 } from "fs";
|
|
34662
34875
|
import { join as join9 } from "path";
|
|
34663
34876
|
var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
@@ -34704,7 +34917,7 @@ function runUsageCli(dir2, aiCli2) {
|
|
|
34704
34917
|
}, 3e4);
|
|
34705
34918
|
timer.unref?.();
|
|
34706
34919
|
try {
|
|
34707
|
-
child =
|
|
34920
|
+
child = spawn4(aiCli2, ["/usage"], {
|
|
34708
34921
|
env: { ...process.env, CLAUDE_CONFIG_DIR: dir2 },
|
|
34709
34922
|
stdio: ["ignore", "pipe", "ignore"]
|
|
34710
34923
|
});
|
|
@@ -34774,7 +34987,7 @@ async function checkClaudeUsage(dirs, aiCli2 = "claude") {
|
|
|
34774
34987
|
// src/core/ai-assist.ts
|
|
34775
34988
|
import { execFile } from "child_process";
|
|
34776
34989
|
import { promisify } from "util";
|
|
34777
|
-
var
|
|
34990
|
+
var run2 = promisify(execFile);
|
|
34778
34991
|
function aiCli() {
|
|
34779
34992
|
return process.env.AI_CLI || "claude";
|
|
34780
34993
|
}
|
|
@@ -34817,7 +35030,7 @@ function parseAi(cli, stdout, prompt) {
|
|
|
34817
35030
|
}
|
|
34818
35031
|
async function runAi(prompt) {
|
|
34819
35032
|
const cli = aiCli();
|
|
34820
|
-
const { stdout } = await
|
|
35033
|
+
const { stdout } = await run2(cli, aiArgs(cli, prompt), {
|
|
34821
35034
|
timeout: 12e4,
|
|
34822
35035
|
maxBuffer: 8 * 1024 * 1024
|
|
34823
35036
|
});
|
|
@@ -35160,7 +35373,7 @@ async function writeWorkerFile(root, relPath, content) {
|
|
|
35160
35373
|
}
|
|
35161
35374
|
|
|
35162
35375
|
// src/core/support-answer.ts
|
|
35163
|
-
import { spawn as
|
|
35376
|
+
import { spawn as spawn5 } from "child_process";
|
|
35164
35377
|
import { execFile as execFile2 } from "child_process";
|
|
35165
35378
|
import { createHash as createHash3 } from "crypto";
|
|
35166
35379
|
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync12, statSync as statSync3 } from "fs";
|
|
@@ -35295,7 +35508,7 @@ function runClaudeOnce(cmd, profile, prompt, extraEnv) {
|
|
|
35295
35508
|
...extraEnv,
|
|
35296
35509
|
...profile ? { CLAUDE_CONFIG_DIR: profile.dir } : {}
|
|
35297
35510
|
};
|
|
35298
|
-
const child =
|
|
35511
|
+
const child = spawn5(cmd, args, { stdio: ["pipe", "pipe", "pipe"], env: env3 });
|
|
35299
35512
|
const parser = createAiStreamParser(cmd);
|
|
35300
35513
|
let out = "";
|
|
35301
35514
|
let err = "";
|
|
@@ -35365,7 +35578,7 @@ async function runSupportAnswer(req, ai, profileDir2) {
|
|
|
35365
35578
|
}
|
|
35366
35579
|
|
|
35367
35580
|
// src/core/knowledge-compose.ts
|
|
35368
|
-
import { spawn as
|
|
35581
|
+
import { spawn as spawn6 } from "child_process";
|
|
35369
35582
|
var COMPOSE_TIMEOUT_MS = 18e4;
|
|
35370
35583
|
function buildPrompt2(req) {
|
|
35371
35584
|
const focus = req.prompt ? `
|
|
@@ -35391,7 +35604,7 @@ function runOnce(cmd, profile, prompt, cwd2, extraEnv) {
|
|
|
35391
35604
|
...extraEnv,
|
|
35392
35605
|
...profile ? { CLAUDE_CONFIG_DIR: profile.dir } : {}
|
|
35393
35606
|
};
|
|
35394
|
-
const child =
|
|
35607
|
+
const child = spawn6(cmd, args, { stdio: ["pipe", "pipe", "pipe"], cwd: cwd2, env: env3 });
|
|
35395
35608
|
let out = "";
|
|
35396
35609
|
let err = "";
|
|
35397
35610
|
const timer = setTimeout(() => {
|
|
@@ -35439,10 +35652,10 @@ async function runKnowledgeCompose(req, ai, physicRoot) {
|
|
|
35439
35652
|
// src/core/autonomous.ts
|
|
35440
35653
|
import { readFile as readFile3, writeFile as writeFile2, chmod } from "fs/promises";
|
|
35441
35654
|
import { readFileSync as readFileSync13 } from "fs";
|
|
35442
|
-
import { execFile as execFile3, spawn as
|
|
35655
|
+
import { execFile as execFile3, spawn as spawn7 } from "child_process";
|
|
35443
35656
|
import { join as join13 } from "path";
|
|
35444
35657
|
import { promisify as promisify3 } from "util";
|
|
35445
|
-
var
|
|
35658
|
+
var run3 = promisify3(execFile3);
|
|
35446
35659
|
var SETTINGS_REL = ".claude/.autonomous.settings.json";
|
|
35447
35660
|
var HISTORIES_REL = ".claude/.autonomous.histories.json";
|
|
35448
35661
|
var APPROVALS_REL = ".claude/.autonomous.approvals.json";
|
|
@@ -35476,7 +35689,7 @@ function tickScript(root) {
|
|
|
35476
35689
|
}
|
|
35477
35690
|
async function crontabList() {
|
|
35478
35691
|
try {
|
|
35479
|
-
const { stdout } = await
|
|
35692
|
+
const { stdout } = await run3("crontab", ["-l"], { timeout: 1e4 });
|
|
35480
35693
|
return stdout;
|
|
35481
35694
|
} catch {
|
|
35482
35695
|
return "";
|
|
@@ -35484,7 +35697,7 @@ async function crontabList() {
|
|
|
35484
35697
|
}
|
|
35485
35698
|
async function crontabSet(content) {
|
|
35486
35699
|
return new Promise((resolve8) => {
|
|
35487
|
-
const child =
|
|
35700
|
+
const child = spawn7("crontab", ["-"], { stdio: ["pipe", "ignore", "ignore"] });
|
|
35488
35701
|
child.on("error", () => resolve8(false));
|
|
35489
35702
|
child.on("close", (code) => resolve8(code === 0));
|
|
35490
35703
|
child.stdin.end(content.endsWith("\n") ? content : content + "\n");
|
|
@@ -36215,15 +36428,15 @@ async function buildGraph(root, mode) {
|
|
|
36215
36428
|
// src/core/rag.ts
|
|
36216
36429
|
import { readFile as readFile9, writeFile as writeFile7, statfs as statfs2, mkdir as mkdir6 } from "fs/promises";
|
|
36217
36430
|
import { existsSync as existsSync10 } from "fs";
|
|
36218
|
-
import { execFile as execFile4, spawn as
|
|
36431
|
+
import { execFile as execFile4, spawn as spawn8 } from "child_process";
|
|
36219
36432
|
import { totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
36220
36433
|
import { join as join20 } from "path";
|
|
36221
36434
|
import { promisify as promisify4 } from "util";
|
|
36222
|
-
var
|
|
36435
|
+
var run4 = promisify4(execFile4);
|
|
36223
36436
|
var RAG_REL = ".claude/rag";
|
|
36224
36437
|
async function tryRun(cmd, args) {
|
|
36225
36438
|
try {
|
|
36226
|
-
const { stdout } = await
|
|
36439
|
+
const { stdout } = await run4(cmd, args, { timeout: 8e3 });
|
|
36227
36440
|
return { ok: true, out: stdout.trim() };
|
|
36228
36441
|
} catch {
|
|
36229
36442
|
return { ok: false, out: "" };
|
|
@@ -36402,7 +36615,7 @@ touch "${RAG_REL}/.indexing"
|
|
|
36402
36615
|
{ echo "[reindex] $(date)"; "$RAG_PY" "${RAG_REL}/rag_engine.py" reindex 2>&1; } >> "${RAG_REL}/index.log" 2>&1
|
|
36403
36616
|
rm -f "${RAG_REL}/.indexing"
|
|
36404
36617
|
`;
|
|
36405
|
-
const child =
|
|
36618
|
+
const child = spawn8("bash", ["-c", script], {
|
|
36406
36619
|
cwd: root,
|
|
36407
36620
|
env: { ...process.env, RAG_PY: py.bin },
|
|
36408
36621
|
detached: true,
|
|
@@ -36417,7 +36630,7 @@ async function ragQuery(root, queryText, k = 8) {
|
|
|
36417
36630
|
if (!existsSync10(join20(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
|
|
36418
36631
|
await ensureEngine(root);
|
|
36419
36632
|
try {
|
|
36420
|
-
const { stdout } = await
|
|
36633
|
+
const { stdout } = await run4(
|
|
36421
36634
|
py.bin,
|
|
36422
36635
|
[join20(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k, 1), 20))],
|
|
36423
36636
|
{ cwd: root, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
@@ -36452,7 +36665,7 @@ rm -f "${RAG_REL}/.installed"
|
|
|
36452
36665
|
} >> "${RAG_REL}/install.log" 2>&1
|
|
36453
36666
|
rm -f "${RAG_REL}/.installing"
|
|
36454
36667
|
`;
|
|
36455
|
-
const child =
|
|
36668
|
+
const child = spawn8("bash", ["-c", script], {
|
|
36456
36669
|
cwd: root,
|
|
36457
36670
|
env: { ...process.env, RAG_PY: py.bin, RAG_MODEL: model },
|
|
36458
36671
|
detached: true,
|
|
@@ -36467,7 +36680,7 @@ import { readFile as readFile10 } from "fs/promises";
|
|
|
36467
36680
|
import { execFile as execFile5 } from "child_process";
|
|
36468
36681
|
import { basename as basename3, dirname as dirname7, resolve as resolve5 } from "path";
|
|
36469
36682
|
import { promisify as promisify5 } from "util";
|
|
36470
|
-
var
|
|
36683
|
+
var run5 = promisify5(execFile5);
|
|
36471
36684
|
async function gitDiff(path) {
|
|
36472
36685
|
const target = resolve5(path);
|
|
36473
36686
|
const dir2 = dirname7(target);
|
|
@@ -36479,7 +36692,7 @@ async function gitDiff(path) {
|
|
|
36479
36692
|
}
|
|
36480
36693
|
let oldContent = "";
|
|
36481
36694
|
try {
|
|
36482
|
-
const { stdout } = await
|
|
36695
|
+
const { stdout } = await run5("git", ["show", `HEAD:./${base}`], {
|
|
36483
36696
|
cwd: dir2,
|
|
36484
36697
|
timeout: 15e3,
|
|
36485
36698
|
maxBuffer: 4 * 1024 * 1024
|
|
@@ -36493,10 +36706,10 @@ async function gitDiff(path) {
|
|
|
36493
36706
|
// src/core/git-env.ts
|
|
36494
36707
|
import { execFile as execFile6 } from "child_process";
|
|
36495
36708
|
import { promisify as promisify6 } from "util";
|
|
36496
|
-
var
|
|
36709
|
+
var run6 = promisify6(execFile6);
|
|
36497
36710
|
async function hasBinary(bin, args = ["--version"]) {
|
|
36498
36711
|
try {
|
|
36499
|
-
await
|
|
36712
|
+
await run6(bin, args, { timeout: 5e3 });
|
|
36500
36713
|
return true;
|
|
36501
36714
|
} catch {
|
|
36502
36715
|
return false;
|
|
@@ -36509,7 +36722,7 @@ async function checkGitEnv(provider) {
|
|
|
36509
36722
|
let account = null;
|
|
36510
36723
|
if (installed) {
|
|
36511
36724
|
try {
|
|
36512
|
-
const { stdout, stderr } = await
|
|
36725
|
+
const { stdout, stderr } = await run6(bin, ["auth", "status"], { timeout: 8e3 });
|
|
36513
36726
|
const out = `${stdout}
|
|
36514
36727
|
${stderr}`;
|
|
36515
36728
|
authenticated = /logged in|logged into|✓/i.test(out);
|
|
@@ -36526,7 +36739,7 @@ import { execFile as execFile7 } from "child_process";
|
|
|
36526
36739
|
import { readFile as readFile11, rm as rm3, mkdir as mkdir7 } from "fs/promises";
|
|
36527
36740
|
import { join as join21 } from "path";
|
|
36528
36741
|
import { promisify as promisify7 } from "util";
|
|
36529
|
-
var
|
|
36742
|
+
var run7 = promisify7(execFile7);
|
|
36530
36743
|
function keyPaths(profileDir2) {
|
|
36531
36744
|
const dir2 = join21(profileDir2, ".ssh");
|
|
36532
36745
|
const key = join21(dir2, "id_4pm");
|
|
@@ -36541,14 +36754,14 @@ async function readPublicKey(pub) {
|
|
|
36541
36754
|
}
|
|
36542
36755
|
async function fingerprintOf(pub) {
|
|
36543
36756
|
try {
|
|
36544
|
-
const { stdout } = await
|
|
36757
|
+
const { stdout } = await run7("ssh-keygen", ["-lf", pub], { timeout: 5e3 });
|
|
36545
36758
|
return stdout.match(/SHA256:[A-Za-z0-9+/=]+/)?.[0] ?? null;
|
|
36546
36759
|
} catch {
|
|
36547
36760
|
return null;
|
|
36548
36761
|
}
|
|
36549
36762
|
}
|
|
36550
36763
|
async function configureGit(key) {
|
|
36551
|
-
await
|
|
36764
|
+
await run7(
|
|
36552
36765
|
"git",
|
|
36553
36766
|
["config", "--global", "core.sshCommand", `ssh -i ${key} -o IdentitiesOnly=yes`],
|
|
36554
36767
|
{ timeout: 5e3 }
|
|
@@ -36559,7 +36772,7 @@ async function generate(profileDir2) {
|
|
|
36559
36772
|
await mkdir7(dir2, { recursive: true });
|
|
36560
36773
|
await rm3(key, { force: true });
|
|
36561
36774
|
await rm3(pub, { force: true });
|
|
36562
|
-
await
|
|
36775
|
+
await run7("ssh-keygen", ["-t", "ed25519", "-f", key, "-N", "", "-C", "4pm-deploy-key"], {
|
|
36563
36776
|
timeout: 15e3
|
|
36564
36777
|
});
|
|
36565
36778
|
await configureGit(key);
|
|
@@ -36569,7 +36782,7 @@ async function remove(profileDir2) {
|
|
|
36569
36782
|
const { key, pub } = keyPaths(profileDir2);
|
|
36570
36783
|
await rm3(key, { force: true });
|
|
36571
36784
|
await rm3(pub, { force: true });
|
|
36572
|
-
await
|
|
36785
|
+
await run7("git", ["config", "--global", "--unset", "core.sshCommand"], { timeout: 5e3 }).catch(
|
|
36573
36786
|
() => void 0
|
|
36574
36787
|
);
|
|
36575
36788
|
return { publicKey: null, fingerprint: null };
|
|
@@ -36594,7 +36807,7 @@ import { readdir as readdir6 } from "fs/promises";
|
|
|
36594
36807
|
import { execFile as execFile8 } from "child_process";
|
|
36595
36808
|
import { resolve as resolve6, sep as sep4 } from "path";
|
|
36596
36809
|
import { promisify as promisify8 } from "util";
|
|
36597
|
-
var
|
|
36810
|
+
var run8 = promisify8(execFile8);
|
|
36598
36811
|
var FS = "";
|
|
36599
36812
|
var RS = "";
|
|
36600
36813
|
function resolveRepo(root, repo) {
|
|
@@ -36605,7 +36818,7 @@ function resolveRepo(root, repo) {
|
|
|
36605
36818
|
}
|
|
36606
36819
|
async function git(cwd2, args) {
|
|
36607
36820
|
try {
|
|
36608
|
-
const { stdout } = await
|
|
36821
|
+
const { stdout } = await run8("git", args, {
|
|
36609
36822
|
cwd: cwd2,
|
|
36610
36823
|
timeout: 15e3,
|
|
36611
36824
|
maxBuffer: 8 * 1024 * 1024
|
|
@@ -36709,11 +36922,11 @@ async function gitCommitDiff(root, repo, hash, path) {
|
|
|
36709
36922
|
// src/core/git-commit-identity.ts
|
|
36710
36923
|
import { execFile as execFile9 } from "child_process";
|
|
36711
36924
|
import { promisify as promisify9 } from "util";
|
|
36712
|
-
var
|
|
36925
|
+
var run9 = promisify9(execFile9);
|
|
36713
36926
|
async function setCommitAuthor(cwd2, machineUsername) {
|
|
36714
36927
|
if (!machineUsername) return;
|
|
36715
36928
|
try {
|
|
36716
|
-
await
|
|
36929
|
+
await run9("git", ["config", "--local", "user.name", machineUsername], { cwd: cwd2, timeout: 8e3 });
|
|
36717
36930
|
} catch {
|
|
36718
36931
|
}
|
|
36719
36932
|
}
|
|
@@ -36724,7 +36937,7 @@ import { execFile as execFile10 } from "child_process";
|
|
|
36724
36937
|
import { dirname as dirname8, join as join22, resolve as resolve7 } from "path";
|
|
36725
36938
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
36726
36939
|
import { promisify as promisify10 } from "util";
|
|
36727
|
-
var
|
|
36940
|
+
var run10 = promisify10(execFile10);
|
|
36728
36941
|
function reposOf(spec) {
|
|
36729
36942
|
const repos = spec?.repos;
|
|
36730
36943
|
return Array.isArray(repos) ? repos : [];
|
|
@@ -36738,13 +36951,13 @@ async function provisionRepos(target, repos, emit, opts = {}) {
|
|
|
36738
36951
|
if (repo.primary) {
|
|
36739
36952
|
if (opts.clonePrimary && repo.url) {
|
|
36740
36953
|
emit("git", `Cloning ${repo.url}\u2026`);
|
|
36741
|
-
await
|
|
36954
|
+
await run10("git", ["clone", repo.url, target], { timeout: 12e4 });
|
|
36742
36955
|
continue;
|
|
36743
36956
|
}
|
|
36744
36957
|
emit("git", "Initializing the primary repo\u2026");
|
|
36745
|
-
await
|
|
36958
|
+
await run10("git", ["init"], { cwd: target, timeout: 2e4 });
|
|
36746
36959
|
if (repo.url) {
|
|
36747
|
-
await
|
|
36960
|
+
await run10("git", ["remote", "add", "origin", repo.url], { cwd: target, timeout: 2e4 }).catch(
|
|
36748
36961
|
() => void 0
|
|
36749
36962
|
);
|
|
36750
36963
|
}
|
|
@@ -36753,11 +36966,11 @@ async function provisionRepos(target, repos, emit, opts = {}) {
|
|
|
36753
36966
|
const dir2 = join22(target, subDirName(repo));
|
|
36754
36967
|
if (repo.url) {
|
|
36755
36968
|
emit("git", `Cloning sub-repo ${repo.url}\u2026`);
|
|
36756
|
-
await
|
|
36969
|
+
await run10("git", ["clone", repo.url, dir2], { timeout: 12e4 });
|
|
36757
36970
|
} else {
|
|
36758
36971
|
emit("git", `Initializing sub-repo ${subDirName(repo)}\u2026`);
|
|
36759
36972
|
await mkdir8(dir2, { recursive: true });
|
|
36760
|
-
await
|
|
36973
|
+
await run10("git", ["init"], { cwd: dir2, timeout: 2e4 });
|
|
36761
36974
|
}
|
|
36762
36975
|
}
|
|
36763
36976
|
}
|
|
@@ -37235,7 +37448,7 @@ function runSlashCommand(line, base) {
|
|
|
37235
37448
|
}
|
|
37236
37449
|
|
|
37237
37450
|
// src/core/memory-compact.ts
|
|
37238
|
-
import { spawn as
|
|
37451
|
+
import { spawn as spawn9 } from "child_process";
|
|
37239
37452
|
var COMPACT_TIMEOUT_MS = 6e4;
|
|
37240
37453
|
function buildPrompt3(input) {
|
|
37241
37454
|
return [
|
|
@@ -37263,7 +37476,7 @@ function runOnce2(cmd, profile, prompt, cwd2, extraEnv) {
|
|
|
37263
37476
|
...extraEnv,
|
|
37264
37477
|
...profile ? { CLAUDE_CONFIG_DIR: profile.dir } : {}
|
|
37265
37478
|
};
|
|
37266
|
-
const child =
|
|
37479
|
+
const child = spawn9(cmd, args, { stdio: ["pipe", "pipe", "pipe"], cwd: cwd2, env: env3 });
|
|
37267
37480
|
let out = "";
|
|
37268
37481
|
let err = "";
|
|
37269
37482
|
const timer = setTimeout(() => {
|
|
@@ -37351,132 +37564,6 @@ function applyConfigText(profileDir2, text) {
|
|
|
37351
37564
|
return { ok: true };
|
|
37352
37565
|
}
|
|
37353
37566
|
|
|
37354
|
-
// src/core/worker-tools.ts
|
|
37355
|
-
import { spawn as spawn9 } from "child_process";
|
|
37356
|
-
var NPM_NAME = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
37357
|
-
function run10(cmd, args, onLine, timeoutMs = 18e4) {
|
|
37358
|
-
return new Promise((resolve8) => {
|
|
37359
|
-
let out = "";
|
|
37360
|
-
let buf = "";
|
|
37361
|
-
let child = null;
|
|
37362
|
-
const done = (code) => {
|
|
37363
|
-
clearTimeout(timer);
|
|
37364
|
-
if (onLine && buf.trim()) onLine(buf.trim());
|
|
37365
|
-
resolve8({ code, out });
|
|
37366
|
-
};
|
|
37367
|
-
const timer = setTimeout(() => {
|
|
37368
|
-
child?.kill();
|
|
37369
|
-
if (onLine) onLine(`timed out after ${Math.round(timeoutMs / 1e3)}s`);
|
|
37370
|
-
done(124);
|
|
37371
|
-
}, timeoutMs);
|
|
37372
|
-
timer.unref?.();
|
|
37373
|
-
const feed = (d) => {
|
|
37374
|
-
const text = d.toString("utf8");
|
|
37375
|
-
out += text;
|
|
37376
|
-
if (!onLine) return;
|
|
37377
|
-
buf += text;
|
|
37378
|
-
let nl;
|
|
37379
|
-
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
37380
|
-
const line = buf.slice(0, nl).replace(/\r$/, "");
|
|
37381
|
-
buf = buf.slice(nl + 1);
|
|
37382
|
-
onLine(line);
|
|
37383
|
-
}
|
|
37384
|
-
};
|
|
37385
|
-
try {
|
|
37386
|
-
child = spawn9(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
37387
|
-
child.stdout?.on("data", feed);
|
|
37388
|
-
child.stderr?.on("data", feed);
|
|
37389
|
-
child.on("exit", (code) => done(code ?? 0));
|
|
37390
|
-
child.on("error", () => done(127));
|
|
37391
|
-
} catch {
|
|
37392
|
-
done(127);
|
|
37393
|
-
}
|
|
37394
|
-
});
|
|
37395
|
-
}
|
|
37396
|
-
async function detectOne(cmd, versionArg) {
|
|
37397
|
-
const { code, out } = await run10(cmd, [versionArg], void 0, 15e3);
|
|
37398
|
-
if (code !== 0) return null;
|
|
37399
|
-
const first = out.split("\n").map((l) => l.trim()).find(Boolean);
|
|
37400
|
-
return first ?? "";
|
|
37401
|
-
}
|
|
37402
|
-
async function listExtras() {
|
|
37403
|
-
const { out } = await run10("npm", ["ls", "-g", "--depth=0", "--json"], void 0, 2e4);
|
|
37404
|
-
if (!out.trim()) return [];
|
|
37405
|
-
let deps = {};
|
|
37406
|
-
try {
|
|
37407
|
-
deps = JSON.parse(out).dependencies ?? {};
|
|
37408
|
-
} catch {
|
|
37409
|
-
return [];
|
|
37410
|
-
}
|
|
37411
|
-
const catalogPkgs = new Set(WORKER_TOOL_BUNDLED_GLOBALS);
|
|
37412
|
-
for (const t of WORKER_TOOL_CATALOG) {
|
|
37413
|
-
catalogPkgs.add(t.id);
|
|
37414
|
-
if (t.installPackage) catalogPkgs.add(t.installPackage);
|
|
37415
|
-
}
|
|
37416
|
-
return Object.entries(deps).filter(([name]) => !catalogPkgs.has(name)).map(([name, info]) => ({
|
|
37417
|
-
id: name,
|
|
37418
|
-
label: name,
|
|
37419
|
-
category: "runtime",
|
|
37420
|
-
installed: true,
|
|
37421
|
-
version: info.version ?? null,
|
|
37422
|
-
installable: true,
|
|
37423
|
-
updatable: true
|
|
37424
|
-
}));
|
|
37425
|
-
}
|
|
37426
|
-
async function detectWorkerTools() {
|
|
37427
|
-
const catalog = await Promise.all(
|
|
37428
|
-
WORKER_TOOL_CATALOG.map(async (t) => {
|
|
37429
|
-
const version = await detectOne(t.id, t.versionArg);
|
|
37430
|
-
return {
|
|
37431
|
-
id: t.id,
|
|
37432
|
-
label: t.label,
|
|
37433
|
-
category: t.category,
|
|
37434
|
-
installed: version !== null,
|
|
37435
|
-
version,
|
|
37436
|
-
installable: t.installable,
|
|
37437
|
-
updatable: t.updatable
|
|
37438
|
-
};
|
|
37439
|
-
})
|
|
37440
|
-
);
|
|
37441
|
-
const extras = await listExtras().catch(() => []);
|
|
37442
|
-
return { catalog, extras };
|
|
37443
|
-
}
|
|
37444
|
-
function opPast(op) {
|
|
37445
|
-
return op === "install" ? "installed" : op === "uninstall" ? "uninstalled" : "updated";
|
|
37446
|
-
}
|
|
37447
|
-
function resolvePackage(name, op) {
|
|
37448
|
-
const entry = WORKER_TOOL_CATALOG.find((t) => t.id === name);
|
|
37449
|
-
if (entry) {
|
|
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.` };
|
|
37453
|
-
}
|
|
37454
|
-
return { pkg: entry.installPackage ?? name };
|
|
37455
|
-
}
|
|
37456
|
-
if (WORKER_TOOL_PREREQUISITE_IDS.includes(name)) {
|
|
37457
|
-
return { error: `"${name}" is a prerequisite and cannot be ${opPast(op)} from here.` };
|
|
37458
|
-
}
|
|
37459
|
-
if (WORKER_TOOL_BUNDLED_GLOBALS.includes(name)) {
|
|
37460
|
-
return { error: `"${name}" ships with Node and cannot be ${opPast(op)} from here.` };
|
|
37461
|
-
}
|
|
37462
|
-
if (!NPM_NAME.test(name)) return { error: `Invalid package name: "${name}".` };
|
|
37463
|
-
return { pkg: name };
|
|
37464
|
-
}
|
|
37465
|
-
function opArgs(op, manager, pkg) {
|
|
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];
|
|
37469
|
-
}
|
|
37470
|
-
async function runWorkerToolOp(op, name, manager, onLine) {
|
|
37471
|
-
const resolved = resolvePackage(name, op);
|
|
37472
|
-
if (resolved.error || !resolved.pkg) {
|
|
37473
|
-
return { ok: false, exitCode: 1, error: resolved.error ?? "Invalid package." };
|
|
37474
|
-
}
|
|
37475
|
-
onLine(`$ ${manager} ${opArgs(op, manager, resolved.pkg).join(" ")}`);
|
|
37476
|
-
const { code } = await run10(manager, opArgs(op, manager, resolved.pkg), onLine);
|
|
37477
|
-
return { ok: code === 0, exitCode: code, error: code === 0 ? void 0 : `${manager} exited ${code}` };
|
|
37478
|
-
}
|
|
37479
|
-
|
|
37480
37567
|
// src/core/ws-client.ts
|
|
37481
37568
|
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:";
|
|
37482
37569
|
var HEARTBEAT_MS = 3e4;
|
|
@@ -37518,7 +37605,9 @@ var WsClient = class {
|
|
|
37518
37605
|
this.updateScheduler = new UpdateScheduler(
|
|
37519
37606
|
context.credential.serverUrl,
|
|
37520
37607
|
context.profileDir,
|
|
37521
|
-
context.bus
|
|
37608
|
+
context.bus,
|
|
37609
|
+
// After the idle daily tick updates flagged tools, report the fresh snapshot (ADR-0254).
|
|
37610
|
+
() => void this.reportWorkerTools()
|
|
37522
37611
|
);
|
|
37523
37612
|
context.bus.onLocalSubmit((input) => void this.runLocalCommand(input));
|
|
37524
37613
|
context.bus.onReconnect(() => this.reconnectNow());
|
|
@@ -37635,6 +37724,20 @@ var WsClient = class {
|
|
|
37635
37724
|
get isStopped() {
|
|
37636
37725
|
return this.stopped;
|
|
37637
37726
|
}
|
|
37727
|
+
/**
|
|
37728
|
+
* Detect the worker's tools and report the snapshot to the server (ADR-0254, `tools.report`) so the
|
|
37729
|
+
* DB-backed Tools panel + restore target stay current. One-way, best-effort — a detect/send failure
|
|
37730
|
+
* never disrupts the session. Called after each tool op, after the daily tick, and after a restore.
|
|
37731
|
+
*/
|
|
37732
|
+
async reportWorkerTools() {
|
|
37733
|
+
try {
|
|
37734
|
+
const autoUpdate = readProfileConfig(this.context.profileDir).autoUpdateTools ?? [];
|
|
37735
|
+
const { catalog, extras } = await detectWorkerTools(autoUpdate);
|
|
37736
|
+
this.send(WsChannels.TOOLS_REPORT, { catalog, extras });
|
|
37737
|
+
} catch (err) {
|
|
37738
|
+
logger.warn("tools.report.error", { error: String(err) });
|
|
37739
|
+
}
|
|
37740
|
+
}
|
|
37638
37741
|
/**
|
|
37639
37742
|
* Force an immediate reconnect: wake the backoff sleep if waiting, else drop the live
|
|
37640
37743
|
* socket so the run() loop reconnects (the next backoff is skipped). No-op if stopped.
|
|
@@ -37785,7 +37888,10 @@ var WsClient = class {
|
|
|
37785
37888
|
projectAutoClearIdleMinutes: token.projectTokens?.autoClearIdleMinutes ?? 0,
|
|
37786
37889
|
// Project shared-AI-memory override mirrors (ADR-0245).
|
|
37787
37890
|
projectAiMemoryMode: token.aiMemory?.mode ?? "inherit",
|
|
37788
|
-
projectAiMemoryBudgetChars: token.aiMemory?.budgetChars ?? 0
|
|
37891
|
+
projectAiMemoryBudgetChars: token.aiMemory?.budgetChars ?? 0,
|
|
37892
|
+
// Per-tool auto-update flags (ADR-0253) are now DB-owned (ADR-0254) — mirror the server's
|
|
37893
|
+
// list locally so the idle daily tick (UpdateScheduler) can read it without a round-trip.
|
|
37894
|
+
autoUpdateTools: token.toolRestore?.autoUpdate ?? []
|
|
37789
37895
|
});
|
|
37790
37896
|
if (token.aiMemory && !this.aiMemory) this.aiMemory = token.aiMemory.text;
|
|
37791
37897
|
this.outboundReviewEnabled = token.outboundReview?.enabled === true;
|
|
@@ -38263,10 +38369,16 @@ var WsClient = class {
|
|
|
38263
38369
|
break;
|
|
38264
38370
|
}
|
|
38265
38371
|
case WsChannels.TOOLS_LIST:
|
|
38266
|
-
void detectWorkerTools().then(
|
|
38372
|
+
void detectWorkerTools(readProfileConfig(this.context.profileDir).autoUpdateTools ?? []).then(
|
|
38267
38373
|
(reply) => this.send(WsChannels.TOOLS_LIST, reply, message.id)
|
|
38268
38374
|
);
|
|
38269
38375
|
break;
|
|
38376
|
+
case WsChannels.TOOLS_AUTOUPDATE: {
|
|
38377
|
+
const req = payload;
|
|
38378
|
+
const res = setToolAutoUpdate(this.context.profileDir, req.name, req.enabled);
|
|
38379
|
+
this.send(WsChannels.TOOLS_AUTOUPDATE, res, message.id);
|
|
38380
|
+
break;
|
|
38381
|
+
}
|
|
38270
38382
|
case WsChannels.TOOLS_INSTALL:
|
|
38271
38383
|
case WsChannels.TOOLS_UNINSTALL:
|
|
38272
38384
|
case WsChannels.TOOLS_UPDATE: {
|
|
@@ -38278,13 +38390,28 @@ var WsClient = class {
|
|
|
38278
38390
|
req.name,
|
|
38279
38391
|
req.manager,
|
|
38280
38392
|
(line) => this.send(WsChannels.TOOLS_PROGRESS, { opId: req.opId, line })
|
|
38281
|
-
).then(
|
|
38282
|
-
|
|
38393
|
+
).then((res) => {
|
|
38394
|
+
this.send(WsChannels.TOOLS_DONE, {
|
|
38283
38395
|
opId: req.opId,
|
|
38284
38396
|
ok: res.ok,
|
|
38285
38397
|
exitCode: res.exitCode,
|
|
38286
38398
|
error: res.error
|
|
38287
|
-
})
|
|
38399
|
+
});
|
|
38400
|
+
void this.reportWorkerTools();
|
|
38401
|
+
});
|
|
38402
|
+
break;
|
|
38403
|
+
}
|
|
38404
|
+
case WsChannels.TOOLS_RESTORE: {
|
|
38405
|
+
const req = payload;
|
|
38406
|
+
void reconcileTools(
|
|
38407
|
+
req.manifest.map((m) => ({ name: m.name, version: m.version, manager: m.manager })),
|
|
38408
|
+
(line) => this.bus.log(line)
|
|
38409
|
+
).then(() => this.reportWorkerTools()).then(() => this.send(WsChannels.TOOLS_RESTORE, { ok: true }, message.id)).catch(
|
|
38410
|
+
(err) => this.send(
|
|
38411
|
+
WsChannels.TOOLS_RESTORE,
|
|
38412
|
+
{ ok: false, error: String(err) },
|
|
38413
|
+
message.id
|
|
38414
|
+
)
|
|
38288
38415
|
);
|
|
38289
38416
|
break;
|
|
38290
38417
|
}
|