@4pm/cli 1.5.14-hotfix → 1.5.16
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 +422 -197
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28599,7 +28599,7 @@ async function runLink(serverUrl, explicitProfile, token = null) {
|
|
|
28599
28599
|
|
|
28600
28600
|
// src/commands/start.ts
|
|
28601
28601
|
import { spawn as spawn10 } from "child_process";
|
|
28602
|
-
import { join as
|
|
28602
|
+
import { join as join30 } from "path";
|
|
28603
28603
|
|
|
28604
28604
|
// src/core/update.ts
|
|
28605
28605
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -28775,7 +28775,7 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
|
|
|
28775
28775
|
// src/core/ws-client.ts
|
|
28776
28776
|
init_wrapper();
|
|
28777
28777
|
import { randomUUID } from "crypto";
|
|
28778
|
-
import { basename as basename4, join as
|
|
28778
|
+
import { basename as basename4, join as join26 } from "path";
|
|
28779
28779
|
|
|
28780
28780
|
// vendor/@4pm/utils/helpers.ts
|
|
28781
28781
|
function backoffJitterMs(attempt, baseMs = 500, maxMs = 1e4) {
|
|
@@ -32913,6 +32913,29 @@ var deletedFilterSchema = external_exports.enum(["true", "false"]).transform((v)
|
|
|
32913
32913
|
// vendor/@4pm/dto/command.ts
|
|
32914
32914
|
var COMMAND_MAX_LEN = 8e3;
|
|
32915
32915
|
var AI_PROMPT_MAX_LEN = 5e5;
|
|
32916
|
+
var COMMAND_IMAGE_MAX_BYTES = 5 * 1024 * 1024;
|
|
32917
|
+
var COMMAND_IMAGE_MAX_COUNT = 10;
|
|
32918
|
+
var COMMAND_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
|
|
32919
|
+
function commandImageExt(mime) {
|
|
32920
|
+
switch (mime) {
|
|
32921
|
+
case "image/png":
|
|
32922
|
+
return "png";
|
|
32923
|
+
case "image/jpeg":
|
|
32924
|
+
return "jpg";
|
|
32925
|
+
case "image/webp":
|
|
32926
|
+
return "webp";
|
|
32927
|
+
case "image/gif":
|
|
32928
|
+
return "gif";
|
|
32929
|
+
default:
|
|
32930
|
+
return "bin";
|
|
32931
|
+
}
|
|
32932
|
+
}
|
|
32933
|
+
var commandImageRefSchema = external_exports.object({
|
|
32934
|
+
id: external_exports.string().min(1).max(200),
|
|
32935
|
+
placeholder: external_exports.string().min(1).max(40),
|
|
32936
|
+
name: external_exports.string().max(255),
|
|
32937
|
+
mime: external_exports.enum(COMMAND_IMAGE_MIME_TYPES)
|
|
32938
|
+
});
|
|
32916
32939
|
var dispatchCommandRequestSchema = external_exports.object({
|
|
32917
32940
|
projectId: external_exports.string().uuid(),
|
|
32918
32941
|
// Target cli. Required unless `pick` is set — with `pick:"idle"` the server chooses the
|
|
@@ -32946,6 +32969,13 @@ var dispatchCommandRequestSchema = external_exports.object({
|
|
|
32946
32969
|
* against the git allowlist (`isGitCommandAllowed`). Absent ⇒ normal console dispatch.
|
|
32947
32970
|
*/
|
|
32948
32971
|
gitOp: external_exports.enum(["read", "write"]).optional(),
|
|
32972
|
+
/**
|
|
32973
|
+
* Console image attachments (ADR-0257): up to `COMMAND_IMAGE_MAX_COUNT` uploaded images the
|
|
32974
|
+
* prompt references by `[Image#N]` placeholders. Only meaningful with `ai:true` on a full agent
|
|
32975
|
+
* run (one-shot spec-assist disallows `Read`). Rejected with `IMAGE_UPLOAD_BLOCKED` when the
|
|
32976
|
+
* project has `outboundReview.blockImages`.
|
|
32977
|
+
*/
|
|
32978
|
+
images: external_exports.array(commandImageRefSchema).max(COMMAND_IMAGE_MAX_COUNT).optional(),
|
|
32949
32979
|
/**
|
|
32950
32980
|
* One-shot AI mode (ADR-0249): the prompt is a text-in → text-out task (spec review /
|
|
32951
32981
|
* compose / suggest / generators) that must NOT trigger the AI CLI's agentic tool loop.
|
|
@@ -33802,6 +33832,9 @@ var WsChannels = {
|
|
|
33802
33832
|
// the worker uploads its pending log tail (+ any queued history) then acks, so the release
|
|
33803
33833
|
// sweep can finalise without waiting the full 5-minute timeout when the worker is online.
|
|
33804
33834
|
RENTAL_FLUSH: "rental.flush",
|
|
33835
|
+
// cli → server (reply): fetch a Console prompt image blob to materialize on the worker before
|
|
33836
|
+
// spawning the AI CLI (ADR-0257) — the server reads it from @4pm/storage (org-scoped by link).
|
|
33837
|
+
IMAGE_FETCH: "image.fetch",
|
|
33805
33838
|
// outbound review (ADR-0082): machine→server request, server→outbound evaluate, result back
|
|
33806
33839
|
REVIEW_REQUEST: "review.request",
|
|
33807
33840
|
REVIEW_EVALUATE: "review.evaluate",
|
|
@@ -33871,7 +33904,7 @@ function decryptPayload(key, payloadBase64, nonceBase64) {
|
|
|
33871
33904
|
}
|
|
33872
33905
|
|
|
33873
33906
|
// src/core/ws-client.ts
|
|
33874
|
-
import { existsSync as
|
|
33907
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync6, rmSync as rmSync5 } from "fs";
|
|
33875
33908
|
import { randomUUID as randomCommandId } from "crypto";
|
|
33876
33909
|
|
|
33877
33910
|
// src/core/update-scheduler.ts
|
|
@@ -33983,6 +34016,35 @@ ${content.slice(-LOG_UPLOAD_CONTENT_MAX)}`;
|
|
|
33983
34016
|
// src/core/worker-tools.ts
|
|
33984
34017
|
import { spawn } from "child_process";
|
|
33985
34018
|
var NPM_NAME = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
34019
|
+
var TOOL_INSTALL_TIMEOUT_DEFAULT_MS = 3e5;
|
|
34020
|
+
var TOOL_INSTALL_MAX_ATTEMPTS = 3;
|
|
34021
|
+
var TOOL_RETRY_BASE_MS = 5e3;
|
|
34022
|
+
function resolveInstallTimeoutMs(sec) {
|
|
34023
|
+
return sec && sec > 0 ? sec * 1e3 : TOOL_INSTALL_TIMEOUT_DEFAULT_MS;
|
|
34024
|
+
}
|
|
34025
|
+
function sleep(ms) {
|
|
34026
|
+
return new Promise((resolve8) => {
|
|
34027
|
+
const t = setTimeout(resolve8, ms);
|
|
34028
|
+
t.unref?.();
|
|
34029
|
+
});
|
|
34030
|
+
}
|
|
34031
|
+
function classifyToolFailure(code, out) {
|
|
34032
|
+
if (code === 124) return { reason: "timeout", retryable: true };
|
|
34033
|
+
const t = out.toLowerCase();
|
|
34034
|
+
if (/e404|not found|no such package|is not in this registry|404 not found/.test(t)) {
|
|
34035
|
+
return { reason: "not-found", retryable: false };
|
|
34036
|
+
}
|
|
34037
|
+
if (/etarget|no matching version|notarget/.test(t)) return { reason: "not-found", retryable: false };
|
|
34038
|
+
if (/unsupported_engine|npm error engine|err_pnpm_unsupported_engine/.test(t)) {
|
|
34039
|
+
return { reason: "engine", retryable: false };
|
|
34040
|
+
}
|
|
34041
|
+
if (/econnreset|etimedout|eai_again|enotfound|econnrefused|socket hang up|err_socket|network|network_request_failed|registry.*(50\d|timeout)|request to https?:\/\/.*failed/.test(
|
|
34042
|
+
t
|
|
34043
|
+
)) {
|
|
34044
|
+
return { reason: "network", retryable: true };
|
|
34045
|
+
}
|
|
34046
|
+
return { reason: "other", retryable: false };
|
|
34047
|
+
}
|
|
33986
34048
|
function run(cmd, args, onLine, timeoutMs = 18e4) {
|
|
33987
34049
|
return new Promise((resolve8) => {
|
|
33988
34050
|
let out = "";
|
|
@@ -34099,13 +34161,13 @@ function opArgs(op, manager, pkg) {
|
|
|
34099
34161
|
const target = op === "update" ? `${pkg}@latest` : pkg;
|
|
34100
34162
|
return manager === "pnpm" ? ["add", "-g", target] : ["install", "-g", target];
|
|
34101
34163
|
}
|
|
34102
|
-
async function runWorkerToolOp(op, name, manager, onLine) {
|
|
34164
|
+
async function runWorkerToolOp(op, name, manager, onLine, timeoutMs = TOOL_INSTALL_TIMEOUT_DEFAULT_MS) {
|
|
34103
34165
|
const resolved = resolvePackage(name, op);
|
|
34104
34166
|
if (resolved.error || !resolved.pkg) {
|
|
34105
34167
|
return { ok: false, exitCode: 1, error: resolved.error ?? "Invalid package." };
|
|
34106
34168
|
}
|
|
34107
34169
|
onLine(`$ ${manager} ${opArgs(op, manager, resolved.pkg).join(" ")}`);
|
|
34108
|
-
const { code } = await run(manager, opArgs(op, manager, resolved.pkg), onLine);
|
|
34170
|
+
const { code } = await run(manager, opArgs(op, manager, resolved.pkg), onLine, timeoutMs);
|
|
34109
34171
|
return { ok: code === 0, exitCode: code, error: code === 0 ? void 0 : `${manager} exited ${code}` };
|
|
34110
34172
|
}
|
|
34111
34173
|
function setToolAutoUpdate(profileDir2, name, enabled) {
|
|
@@ -34118,25 +34180,40 @@ function setToolAutoUpdate(profileDir2, name, enabled) {
|
|
|
34118
34180
|
writeProfileConfig(profileDir2, { autoUpdateTools: [...set] });
|
|
34119
34181
|
return { ok: true };
|
|
34120
34182
|
}
|
|
34121
|
-
async function autoUpdateFlaggedTools(tools, onLine) {
|
|
34183
|
+
async function autoUpdateFlaggedTools(tools, onLine, timeoutMs = TOOL_INSTALL_TIMEOUT_DEFAULT_MS) {
|
|
34122
34184
|
for (const name of tools) {
|
|
34123
|
-
const res = await runWorkerToolOp("update", name, "npm", onLine).catch(
|
|
34185
|
+
const res = await runWorkerToolOp("update", name, "npm", onLine, timeoutMs).catch(
|
|
34124
34186
|
(err) => ({ ok: false, exitCode: 1, error: String(err) })
|
|
34125
34187
|
);
|
|
34126
34188
|
onLine(res.ok ? `\u2713 ${name} updated` : `\u2717 ${name}: ${res.error ?? "failed"}`);
|
|
34127
34189
|
}
|
|
34128
34190
|
}
|
|
34129
|
-
async function installPinned(name, version, manager, onLine) {
|
|
34191
|
+
async function installPinned(name, version, manager, onLine, timeoutMs = TOOL_INSTALL_TIMEOUT_DEFAULT_MS) {
|
|
34130
34192
|
const resolved = resolvePackage(name, "update");
|
|
34131
|
-
if (resolved.error || !resolved.pkg)
|
|
34193
|
+
if (resolved.error || !resolved.pkg) {
|
|
34194
|
+
return { ok: false, error: resolved.error ?? "Invalid package.", reason: "other", retryable: false };
|
|
34195
|
+
}
|
|
34132
34196
|
const target = `${resolved.pkg}@${version}`;
|
|
34133
34197
|
const args = manager === "pnpm" ? ["add", "-g", target] : ["install", "-g", target];
|
|
34134
|
-
|
|
34135
|
-
|
|
34136
|
-
|
|
34137
|
-
}
|
|
34138
|
-
|
|
34139
|
-
|
|
34198
|
+
for (let attempt = 1; attempt <= TOOL_INSTALL_MAX_ATTEMPTS; attempt++) {
|
|
34199
|
+
const suffix = attempt > 1 ? ` (attempt ${attempt}/${TOOL_INSTALL_MAX_ATTEMPTS})` : "";
|
|
34200
|
+
onLine(`$ ${manager} ${args.join(" ")}${suffix}`);
|
|
34201
|
+
const { code, out } = await run(manager, args, onLine, timeoutMs);
|
|
34202
|
+
if (code === 0) return { ok: true };
|
|
34203
|
+
const { reason, retryable } = classifyToolFailure(code, out);
|
|
34204
|
+
if (!retryable || attempt === TOOL_INSTALL_MAX_ATTEMPTS) {
|
|
34205
|
+
return { ok: false, error: `${manager} exited ${code}`, reason, retryable };
|
|
34206
|
+
}
|
|
34207
|
+
const base = TOOL_RETRY_BASE_MS * 3 ** (attempt - 1);
|
|
34208
|
+
const wait = Math.round(base * (0.8 + Math.random() * 0.4));
|
|
34209
|
+
onLine(`retrying in ${Math.round(wait / 1e3)}s (${reason})\u2026`);
|
|
34210
|
+
await sleep(wait);
|
|
34211
|
+
}
|
|
34212
|
+
return { ok: false, error: `${manager} failed`, reason: "other", retryable: false };
|
|
34213
|
+
}
|
|
34214
|
+
async function reconcileTools(manifest, onLine, timeoutMs = TOOL_INSTALL_TIMEOUT_DEFAULT_MS) {
|
|
34215
|
+
const failed = [];
|
|
34216
|
+
if (manifest.length === 0) return failed;
|
|
34140
34217
|
const { catalog, extras } = await detectWorkerTools();
|
|
34141
34218
|
const versionByName = new Map(
|
|
34142
34219
|
[...catalog, ...extras].filter((r) => r.installed && r.version).map((r) => [r.id, r.version])
|
|
@@ -34145,11 +34222,27 @@ async function reconcileTools(manifest, onLine) {
|
|
|
34145
34222
|
const current = versionByName.get(entry.name);
|
|
34146
34223
|
if (current && current.includes(entry.version)) continue;
|
|
34147
34224
|
onLine(`Restoring ${entry.name}@${entry.version}\u2026`);
|
|
34148
|
-
const res = await installPinned(entry.name, entry.version, entry.manager, onLine).catch(
|
|
34149
|
-
(err) => ({
|
|
34225
|
+
const res = await installPinned(entry.name, entry.version, entry.manager, onLine, timeoutMs).catch(
|
|
34226
|
+
(err) => ({
|
|
34227
|
+
ok: false,
|
|
34228
|
+
error: String(err),
|
|
34229
|
+
reason: "other",
|
|
34230
|
+
retryable: false
|
|
34231
|
+
})
|
|
34150
34232
|
);
|
|
34151
|
-
|
|
34233
|
+
if (res.ok) {
|
|
34234
|
+
onLine(`\u2713 ${entry.name}@${entry.version}`);
|
|
34235
|
+
} else {
|
|
34236
|
+
onLine(`\u2717 ${entry.name}: ${res.error ?? "failed"}`);
|
|
34237
|
+
failed.push({
|
|
34238
|
+
name: entry.name,
|
|
34239
|
+
version: entry.version,
|
|
34240
|
+
reason: res.reason ?? "other",
|
|
34241
|
+
retryable: res.retryable ?? false
|
|
34242
|
+
});
|
|
34243
|
+
}
|
|
34152
34244
|
}
|
|
34245
|
+
return failed;
|
|
34153
34246
|
}
|
|
34154
34247
|
|
|
34155
34248
|
// src/core/update-scheduler.ts
|
|
@@ -34263,10 +34356,12 @@ var UpdateScheduler = class {
|
|
|
34263
34356
|
}
|
|
34264
34357
|
/** Update each tool flagged for auto-update in config.json to @latest (ADR-0253) — best-effort. */
|
|
34265
34358
|
async updateFlaggedTools() {
|
|
34266
|
-
const
|
|
34359
|
+
const config = readProfileConfig(this.profileDir);
|
|
34360
|
+
const tools = config.autoUpdateTools ?? [];
|
|
34267
34361
|
if (tools.length === 0) return;
|
|
34268
34362
|
this.bus.log(`Scheduled tool auto-update: ${tools.join(", ")}\u2026`);
|
|
34269
|
-
|
|
34363
|
+
const timeoutMs = resolveInstallTimeoutMs(config.toolInstallTimeoutSec);
|
|
34364
|
+
await autoUpdateFlaggedTools(tools, (line) => logger.info("update.tool.line", { line }), timeoutMs).catch(
|
|
34270
34365
|
(err) => logger.warn("update.tool.error", { error: String(err) })
|
|
34271
34366
|
);
|
|
34272
34367
|
this.onToolsChanged?.();
|
|
@@ -35251,23 +35346,86 @@ function finishCommand(commandId, exitCode, usage) {
|
|
|
35251
35346
|
persist();
|
|
35252
35347
|
}
|
|
35253
35348
|
|
|
35349
|
+
// src/core/command-images.ts
|
|
35350
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readdirSync as readdirSync3, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
35351
|
+
import { join as join11 } from "path";
|
|
35352
|
+
function attachmentsBaseDir(physicRoot) {
|
|
35353
|
+
return join11(physicRoot, ".4pm", "attachments");
|
|
35354
|
+
}
|
|
35355
|
+
var ATTACHMENT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
35356
|
+
async function materializeImages(physicRoot, commandId, images, fetch2) {
|
|
35357
|
+
const map = /* @__PURE__ */ new Map();
|
|
35358
|
+
if (!images.length) return map;
|
|
35359
|
+
const dir2 = join11(attachmentsBaseDir(physicRoot), commandId);
|
|
35360
|
+
mkdirSync3(dir2, { recursive: true });
|
|
35361
|
+
let n = 0;
|
|
35362
|
+
for (const img of images) {
|
|
35363
|
+
n += 1;
|
|
35364
|
+
try {
|
|
35365
|
+
const reply = await fetch2(img.id);
|
|
35366
|
+
if (!reply.dataBase64) {
|
|
35367
|
+
logger.warn("command.image.fetch.empty", { commandId, imageId: img.id, error: reply.error });
|
|
35368
|
+
continue;
|
|
35369
|
+
}
|
|
35370
|
+
const ext = commandImageExt(reply.mime ?? img.mime);
|
|
35371
|
+
const file = join11(dir2, `img-${n}.${ext}`);
|
|
35372
|
+
writeFileSync6(file, Buffer.from(reply.dataBase64, "base64"));
|
|
35373
|
+
map.set(img.placeholder, file);
|
|
35374
|
+
} catch (err) {
|
|
35375
|
+
logger.warn("command.image.fetch.failed", {
|
|
35376
|
+
commandId,
|
|
35377
|
+
imageId: img.id,
|
|
35378
|
+
error: err instanceof Error ? err.message : String(err)
|
|
35379
|
+
});
|
|
35380
|
+
}
|
|
35381
|
+
}
|
|
35382
|
+
return map;
|
|
35383
|
+
}
|
|
35384
|
+
function rewriteImagePlaceholders(prompt, paths) {
|
|
35385
|
+
let out = prompt;
|
|
35386
|
+
for (const [placeholder, file] of paths) {
|
|
35387
|
+
out = out.split(placeholder).join(`\`${file}\``);
|
|
35388
|
+
}
|
|
35389
|
+
return out;
|
|
35390
|
+
}
|
|
35391
|
+
function sweepOldAttachments(physicRoot, maxAgeMs = ATTACHMENT_MAX_AGE_MS) {
|
|
35392
|
+
const base = attachmentsBaseDir(physicRoot);
|
|
35393
|
+
if (!existsSync7(base)) return 0;
|
|
35394
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
35395
|
+
let removed = 0;
|
|
35396
|
+
try {
|
|
35397
|
+
for (const entry of readdirSync3(base)) {
|
|
35398
|
+
const dir2 = join11(base, entry);
|
|
35399
|
+
try {
|
|
35400
|
+
if (statSync2(dir2).mtimeMs < cutoff) {
|
|
35401
|
+
rmSync3(dir2, { recursive: true, force: true });
|
|
35402
|
+
removed += 1;
|
|
35403
|
+
}
|
|
35404
|
+
} catch {
|
|
35405
|
+
}
|
|
35406
|
+
}
|
|
35407
|
+
} catch {
|
|
35408
|
+
}
|
|
35409
|
+
return removed;
|
|
35410
|
+
}
|
|
35411
|
+
|
|
35254
35412
|
// src/core/command-output-store.ts
|
|
35255
35413
|
import {
|
|
35256
35414
|
appendFileSync,
|
|
35257
|
-
existsSync as
|
|
35258
|
-
mkdirSync as
|
|
35415
|
+
existsSync as existsSync8,
|
|
35416
|
+
mkdirSync as mkdirSync4,
|
|
35259
35417
|
readFileSync as readFileSync11,
|
|
35260
|
-
readdirSync as
|
|
35261
|
-
rmSync as
|
|
35262
|
-
statSync as
|
|
35418
|
+
readdirSync as readdirSync4,
|
|
35419
|
+
rmSync as rmSync4,
|
|
35420
|
+
statSync as statSync3
|
|
35263
35421
|
} from "fs";
|
|
35264
|
-
import { join as
|
|
35422
|
+
import { join as join12 } from "path";
|
|
35265
35423
|
var KEEP_FILES = 200;
|
|
35266
35424
|
var dir = null;
|
|
35267
35425
|
function initCommandOutput(profileDir2) {
|
|
35268
|
-
dir =
|
|
35426
|
+
dir = join12(profileDir2, "command-output");
|
|
35269
35427
|
try {
|
|
35270
|
-
|
|
35428
|
+
mkdirSync4(dir, { recursive: true });
|
|
35271
35429
|
prune();
|
|
35272
35430
|
} catch {
|
|
35273
35431
|
}
|
|
@@ -35275,14 +35433,14 @@ function initCommandOutput(profileDir2) {
|
|
|
35275
35433
|
function appendCommandOutput(commandId, text) {
|
|
35276
35434
|
if (!dir || !text) return;
|
|
35277
35435
|
try {
|
|
35278
|
-
appendFileSync(
|
|
35436
|
+
appendFileSync(join12(dir, `${commandId}.log`), text, "utf8");
|
|
35279
35437
|
} catch {
|
|
35280
35438
|
}
|
|
35281
35439
|
}
|
|
35282
35440
|
function readCommandOutput(commandId) {
|
|
35283
35441
|
if (!dir) return null;
|
|
35284
|
-
const path =
|
|
35285
|
-
if (!
|
|
35442
|
+
const path = join12(dir, `${commandId}.log`);
|
|
35443
|
+
if (!existsSync8(path)) return null;
|
|
35286
35444
|
try {
|
|
35287
35445
|
return readFileSync11(path, "utf8");
|
|
35288
35446
|
} catch {
|
|
@@ -35292,8 +35450,8 @@ function readCommandOutput(commandId) {
|
|
|
35292
35450
|
function prune() {
|
|
35293
35451
|
if (!dir) return;
|
|
35294
35452
|
try {
|
|
35295
|
-
const files =
|
|
35296
|
-
for (const { f } of files.slice(KEEP_FILES))
|
|
35453
|
+
const files = readdirSync4(dir).filter((f) => f.endsWith(".log")).map((f) => ({ f, t: statSync3(join12(dir, f)).mtimeMs })).sort((a, b) => b.t - a.t);
|
|
35454
|
+
for (const { f } of files.slice(KEEP_FILES)) rmSync4(join12(dir, f), { force: true });
|
|
35297
35455
|
} catch {
|
|
35298
35456
|
}
|
|
35299
35457
|
}
|
|
@@ -35301,9 +35459,9 @@ function pruneCommandOutputByAge(days) {
|
|
|
35301
35459
|
if (!dir || days <= 0) return;
|
|
35302
35460
|
const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
|
|
35303
35461
|
try {
|
|
35304
|
-
for (const f of
|
|
35305
|
-
const p =
|
|
35306
|
-
if (
|
|
35462
|
+
for (const f of readdirSync4(dir).filter((n) => n.endsWith(".log"))) {
|
|
35463
|
+
const p = join12(dir, f);
|
|
35464
|
+
if (statSync3(p).mtimeMs < cutoff) rmSync4(p, { force: true });
|
|
35307
35465
|
}
|
|
35308
35466
|
} catch {
|
|
35309
35467
|
}
|
|
@@ -35380,8 +35538,8 @@ async function writeWorkerFile(root, relPath, content) {
|
|
|
35380
35538
|
import { spawn as spawn5 } from "child_process";
|
|
35381
35539
|
import { execFile as execFile2 } from "child_process";
|
|
35382
35540
|
import { createHash as createHash3 } from "crypto";
|
|
35383
|
-
import { existsSync as
|
|
35384
|
-
import { join as
|
|
35541
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, readdirSync as readdirSync5, readFileSync as readFileSync12, statSync as statSync4 } from "fs";
|
|
35542
|
+
import { join as join13 } from "path";
|
|
35385
35543
|
import { promisify as promisify2 } from "util";
|
|
35386
35544
|
var NO_USAGE = { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };
|
|
35387
35545
|
var execFileAsync = promisify2(execFile2);
|
|
@@ -35389,11 +35547,11 @@ var MAX_CONTEXT_CHARS = 12e4;
|
|
|
35389
35547
|
var ANSWER_TIMEOUT_MS = 12e4;
|
|
35390
35548
|
var DOC_EXTENSIONS = [".md", ".mdx", ".txt"];
|
|
35391
35549
|
function kbRoot(profileDir2) {
|
|
35392
|
-
return
|
|
35550
|
+
return join13(profileDir2, "support-kb");
|
|
35393
35551
|
}
|
|
35394
35552
|
function repoDir(profileDir2, url, branch) {
|
|
35395
35553
|
const key = createHash3("sha1").update(`${url}#${branch}`).digest("hex").slice(0, 16);
|
|
35396
|
-
return
|
|
35554
|
+
return join13(kbRoot(profileDir2), key);
|
|
35397
35555
|
}
|
|
35398
35556
|
function authedUrl(url, token) {
|
|
35399
35557
|
if (!token) return url;
|
|
@@ -35402,10 +35560,10 @@ function authedUrl(url, token) {
|
|
|
35402
35560
|
}
|
|
35403
35561
|
async function ensureRepo(profileDir2, repo) {
|
|
35404
35562
|
const dir2 = repoDir(profileDir2, repo.url, repo.branch);
|
|
35405
|
-
if (
|
|
35563
|
+
if (existsSync9(join13(dir2, ".git"))) return dir2;
|
|
35406
35564
|
const url = authedUrl(repo.url, repo.token);
|
|
35407
35565
|
const gitEnv = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
|
|
35408
|
-
|
|
35566
|
+
mkdirSync5(kbRoot(profileDir2), { recursive: true });
|
|
35409
35567
|
await execFileAsync("git", ["clone", "--depth", "1", "--branch", repo.branch, url, dir2], {
|
|
35410
35568
|
env: gitEnv
|
|
35411
35569
|
});
|
|
@@ -35413,11 +35571,11 @@ async function ensureRepo(profileDir2, repo) {
|
|
|
35413
35571
|
}
|
|
35414
35572
|
async function refreshSupportKb(profileDir2) {
|
|
35415
35573
|
const root = kbRoot(profileDir2);
|
|
35416
|
-
if (!
|
|
35574
|
+
if (!existsSync9(root)) return;
|
|
35417
35575
|
const gitEnv = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
|
|
35418
|
-
for (const entry of
|
|
35419
|
-
const dir2 =
|
|
35420
|
-
if (!
|
|
35576
|
+
for (const entry of readdirSync5(root)) {
|
|
35577
|
+
const dir2 = join13(root, entry);
|
|
35578
|
+
if (!existsSync9(join13(dir2, ".git"))) continue;
|
|
35421
35579
|
try {
|
|
35422
35580
|
await execFileAsync("git", ["-C", dir2, "pull", "--ff-only"], { env: gitEnv });
|
|
35423
35581
|
} catch (err) {
|
|
@@ -35430,10 +35588,10 @@ function collectDocs(dir2) {
|
|
|
35430
35588
|
let total = 0;
|
|
35431
35589
|
const walk2 = (current) => {
|
|
35432
35590
|
if (total >= MAX_CONTEXT_CHARS) return;
|
|
35433
|
-
for (const entry of
|
|
35591
|
+
for (const entry of readdirSync5(current)) {
|
|
35434
35592
|
if (entry === ".git" || entry === "node_modules") continue;
|
|
35435
|
-
const full =
|
|
35436
|
-
const st =
|
|
35593
|
+
const full = join13(current, entry);
|
|
35594
|
+
const st = statSync4(full);
|
|
35437
35595
|
if (st.isDirectory()) {
|
|
35438
35596
|
walk2(full);
|
|
35439
35597
|
} else if (DOC_EXTENSIONS.some((ext) => entry.toLowerCase().endsWith(ext))) {
|
|
@@ -35657,7 +35815,7 @@ async function runKnowledgeCompose(req, ai, physicRoot) {
|
|
|
35657
35815
|
import { readFile as readFile3, writeFile as writeFile2, chmod } from "fs/promises";
|
|
35658
35816
|
import { readFileSync as readFileSync13 } from "fs";
|
|
35659
35817
|
import { execFile as execFile3, spawn as spawn7 } from "child_process";
|
|
35660
|
-
import { join as
|
|
35818
|
+
import { join as join14 } from "path";
|
|
35661
35819
|
import { promisify as promisify3 } from "util";
|
|
35662
35820
|
var run3 = promisify3(execFile3);
|
|
35663
35821
|
var SETTINGS_REL = ".claude/.autonomous.settings.json";
|
|
@@ -35689,7 +35847,7 @@ function parseJson(text) {
|
|
|
35689
35847
|
}
|
|
35690
35848
|
}
|
|
35691
35849
|
function tickScript(root) {
|
|
35692
|
-
return
|
|
35850
|
+
return join14(root, TICK_REL);
|
|
35693
35851
|
}
|
|
35694
35852
|
async function crontabList() {
|
|
35695
35853
|
try {
|
|
@@ -35712,8 +35870,8 @@ async function cronInstalled(root) {
|
|
|
35712
35870
|
return (await crontabList()).split("\n").some((l) => l.includes(script) && !l.trim().startsWith("#"));
|
|
35713
35871
|
}
|
|
35714
35872
|
async function getAutonomousStatus(root) {
|
|
35715
|
-
const settings = parseJson(await readText(
|
|
35716
|
-
const hist = parseJson(await readText(
|
|
35873
|
+
const settings = parseJson(await readText(join14(root, SETTINGS_REL))) ?? {};
|
|
35874
|
+
const hist = parseJson(await readText(join14(root, HISTORIES_REL))) ?? {};
|
|
35717
35875
|
const records = Array.isArray(hist.records) ? hist.records : [];
|
|
35718
35876
|
const last = records[records.length - 1];
|
|
35719
35877
|
const ticks = hist.ticks ?? {};
|
|
@@ -35730,9 +35888,9 @@ async function getAutonomousStatus(root) {
|
|
|
35730
35888
|
}
|
|
35731
35889
|
function isAutonomousRunning(root) {
|
|
35732
35890
|
try {
|
|
35733
|
-
const s = JSON.parse(readFileSync13(
|
|
35891
|
+
const s = JSON.parse(readFileSync13(join14(root, SETTINGS_REL), "utf8"));
|
|
35734
35892
|
if (s.paused === true) return false;
|
|
35735
|
-
const h = JSON.parse(readFileSync13(
|
|
35893
|
+
const h = JSON.parse(readFileSync13(join14(root, HISTORIES_REL), "utf8"));
|
|
35736
35894
|
const ts = h.records?.[h.records.length - 1]?.ts;
|
|
35737
35895
|
if (!ts) return false;
|
|
35738
35896
|
const t = Date.parse(ts.replace(" ", "T"));
|
|
@@ -35743,10 +35901,10 @@ function isAutonomousRunning(root) {
|
|
|
35743
35901
|
}
|
|
35744
35902
|
async function readAutonomous(root) {
|
|
35745
35903
|
const [settings, approvals, status, ...books] = await Promise.all([
|
|
35746
|
-
readText(
|
|
35747
|
-
readText(
|
|
35904
|
+
readText(join14(root, SETTINGS_REL)),
|
|
35905
|
+
readText(join14(root, APPROVALS_REL), "{}"),
|
|
35748
35906
|
getAutonomousStatus(root),
|
|
35749
|
-
...Object.values(BOOK_FILES).map((f) => readText(
|
|
35907
|
+
...Object.values(BOOK_FILES).map((f) => readText(join14(root, f)))
|
|
35750
35908
|
]);
|
|
35751
35909
|
const keys = Object.keys(BOOK_FILES);
|
|
35752
35910
|
const bookMap = {};
|
|
@@ -35755,7 +35913,7 @@ async function readAutonomous(root) {
|
|
|
35755
35913
|
}
|
|
35756
35914
|
async function readAutonomousLogs(root, date) {
|
|
35757
35915
|
const day = date && /^\d{4}-\d{2}-\d{2}$/.test(date) ? date : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
35758
|
-
const text = await readText(
|
|
35916
|
+
const text = await readText(join14(root, LOG_DIR_REL, `autonomous-tick-${day}.log`));
|
|
35759
35917
|
const lines = text ? text.split("\n").filter((l) => l.length > 0).slice(-500) : [];
|
|
35760
35918
|
return { date: day, lines };
|
|
35761
35919
|
}
|
|
@@ -35779,24 +35937,24 @@ async function writeAutonomous(root, req, by) {
|
|
|
35779
35937
|
switch (req.kind) {
|
|
35780
35938
|
case "settings": {
|
|
35781
35939
|
if (!parseJson(req.settings)) return { ok: false, error: "settings is not valid JSON" };
|
|
35782
|
-
await writeFile2(
|
|
35940
|
+
await writeFile2(join14(root, SETTINGS_REL), req.settings, "utf8");
|
|
35783
35941
|
break;
|
|
35784
35942
|
}
|
|
35785
35943
|
case "approvals": {
|
|
35786
|
-
const map = parseJson(await readText(
|
|
35944
|
+
const map = parseJson(await readText(join14(root, APPROVALS_REL), "{}")) ?? {};
|
|
35787
35945
|
if (req.approved) map[req.taskId] = { approved: true, by, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
35788
35946
|
else delete map[req.taskId];
|
|
35789
|
-
await writeFile2(
|
|
35947
|
+
await writeFile2(join14(root, APPROVALS_REL), JSON.stringify(map, null, 2) + "\n", "utf8");
|
|
35790
35948
|
break;
|
|
35791
35949
|
}
|
|
35792
35950
|
case "userTodo": {
|
|
35793
|
-
const cur = await readText(
|
|
35951
|
+
const cur = await readText(join14(root, BOOK_FILES.userTodo));
|
|
35794
35952
|
const block = `
|
|
35795
35953
|
|
|
35796
35954
|
> posted by ${by} at ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
35797
35955
|
${req.content.trim()}
|
|
35798
35956
|
`;
|
|
35799
|
-
await writeFile2(
|
|
35957
|
+
await writeFile2(join14(root, BOOK_FILES.userTodo), cur.trimEnd() + block, "utf8");
|
|
35800
35958
|
break;
|
|
35801
35959
|
}
|
|
35802
35960
|
case "cron": {
|
|
@@ -35813,7 +35971,7 @@ ${req.content.trim()}
|
|
|
35813
35971
|
|
|
35814
35972
|
// src/core/agents.ts
|
|
35815
35973
|
import { readFile as readFile4, writeFile as writeFile3, mkdir as mkdir3, readdir as readdir3, rm } from "fs/promises";
|
|
35816
|
-
import { join as
|
|
35974
|
+
import { join as join15 } from "path";
|
|
35817
35975
|
var AGENTS_REL = ".claude/agents";
|
|
35818
35976
|
var SKILLS_REL = ".claude/skills";
|
|
35819
35977
|
var MEMORY_DIRS = /* @__PURE__ */ new Set(["short-memory", "long-memory"]);
|
|
@@ -35837,17 +35995,17 @@ async function listAgents(root) {
|
|
|
35837
35995
|
const subagents = [];
|
|
35838
35996
|
const skills = [];
|
|
35839
35997
|
try {
|
|
35840
|
-
for (const e of await readdir3(
|
|
35998
|
+
for (const e of await readdir3(join15(root, AGENTS_REL), { withFileTypes: true })) {
|
|
35841
35999
|
if (!e.isFile() || !e.name.endsWith(".md") || MEMORY_DIRS.has(e.name)) continue;
|
|
35842
|
-
const fm = frontmatter(await readText2(
|
|
36000
|
+
const fm = frontmatter(await readText2(join15(root, AGENTS_REL, e.name)));
|
|
35843
36001
|
subagents.push({ name: e.name.replace(/\.md$/, ""), description: fm.description });
|
|
35844
36002
|
}
|
|
35845
36003
|
} catch {
|
|
35846
36004
|
}
|
|
35847
36005
|
try {
|
|
35848
|
-
for (const e of await readdir3(
|
|
36006
|
+
for (const e of await readdir3(join15(root, SKILLS_REL), { withFileTypes: true })) {
|
|
35849
36007
|
if (!e.isDirectory()) continue;
|
|
35850
|
-
const fm = frontmatter(await readText2(
|
|
36008
|
+
const fm = frontmatter(await readText2(join15(root, SKILLS_REL, e.name, "SKILL.md")));
|
|
35851
36009
|
skills.push({ name: e.name, description: fm.description });
|
|
35852
36010
|
}
|
|
35853
36011
|
} catch {
|
|
@@ -35858,15 +36016,15 @@ async function listAgents(root) {
|
|
|
35858
36016
|
}
|
|
35859
36017
|
function itemPath(root, kind, name) {
|
|
35860
36018
|
const n = safeName(name);
|
|
35861
|
-
return kind === "subagent" ?
|
|
36019
|
+
return kind === "subagent" ? join15(root, AGENTS_REL, `${n}.md`) : join15(root, SKILLS_REL, n, "SKILL.md");
|
|
35862
36020
|
}
|
|
35863
36021
|
async function readAgent(root, kind, name) {
|
|
35864
36022
|
const content = await readText2(itemPath(root, kind, name));
|
|
35865
36023
|
if (kind !== "subagent") return { content };
|
|
35866
36024
|
const n = safeName(name);
|
|
35867
36025
|
const [short, long] = await Promise.all([
|
|
35868
|
-
readText2(
|
|
35869
|
-
readText2(
|
|
36026
|
+
readText2(join15(root, AGENTS_REL, "short-memory", `${n}.md`)),
|
|
36027
|
+
readText2(join15(root, AGENTS_REL, "long-memory", `${n}.md`))
|
|
35870
36028
|
]);
|
|
35871
36029
|
return { content, memory: { short, long } };
|
|
35872
36030
|
}
|
|
@@ -35876,12 +36034,12 @@ async function writeAgent(root, req) {
|
|
|
35876
36034
|
try {
|
|
35877
36035
|
if (req.action === "delete") {
|
|
35878
36036
|
if (req.kind === "subagent") await rm(itemPath(root, "subagent", n), { force: true });
|
|
35879
|
-
else await rm(
|
|
36037
|
+
else await rm(join15(root, SKILLS_REL, n), { recursive: true, force: true });
|
|
35880
36038
|
return { ok: true };
|
|
35881
36039
|
}
|
|
35882
36040
|
if (req.content === void 0) return { ok: false, error: "content is required" };
|
|
35883
36041
|
const path = itemPath(root, req.kind, n);
|
|
35884
|
-
await mkdir3(
|
|
36042
|
+
await mkdir3(join15(path, ".."), { recursive: true });
|
|
35885
36043
|
await writeFile3(path, req.content, "utf8");
|
|
35886
36044
|
return { ok: true };
|
|
35887
36045
|
} catch (err) {
|
|
@@ -35892,7 +36050,7 @@ async function writeAgent(root, req) {
|
|
|
35892
36050
|
// src/core/packages.ts
|
|
35893
36051
|
import { createHash as createHash4 } from "crypto";
|
|
35894
36052
|
import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4, readdir as readdir4, rm as rm2, stat } from "fs/promises";
|
|
35895
|
-
import { dirname as dirname4, join as
|
|
36053
|
+
import { dirname as dirname4, join as join16, relative } from "path";
|
|
35896
36054
|
var AGENTS_REL2 = ".claude/agents";
|
|
35897
36055
|
var SKILLS_REL2 = ".claude/skills";
|
|
35898
36056
|
var MANIFEST_REL = ".claude/.4pm-packages.json";
|
|
@@ -35916,7 +36074,7 @@ async function listFilesRel(dir2) {
|
|
|
35916
36074
|
return;
|
|
35917
36075
|
}
|
|
35918
36076
|
for (const e of entries2) {
|
|
35919
|
-
const full =
|
|
36077
|
+
const full = join16(d, e.name);
|
|
35920
36078
|
if (e.isDirectory()) await walk2(full);
|
|
35921
36079
|
else if (e.isFile()) out.push(relative(dir2, full));
|
|
35922
36080
|
}
|
|
@@ -35927,16 +36085,16 @@ async function listFilesRel(dir2) {
|
|
|
35927
36085
|
async function readArtifactFiles(root, kind, slug) {
|
|
35928
36086
|
const files = [];
|
|
35929
36087
|
if (kind === "subagent") {
|
|
35930
|
-
const agent = await readMaybe(
|
|
36088
|
+
const agent = await readMaybe(join16(root, AGENTS_REL2, `${slug}.md`));
|
|
35931
36089
|
if (agent) files.push({ path: "agent.md", contentBase64: agent.toString("base64") });
|
|
35932
|
-
const short = await readMaybe(
|
|
36090
|
+
const short = await readMaybe(join16(root, AGENTS_REL2, "short-memory", `${slug}.md`));
|
|
35933
36091
|
if (short) files.push({ path: "short-memory.md", contentBase64: short.toString("base64") });
|
|
35934
|
-
const long = await readMaybe(
|
|
36092
|
+
const long = await readMaybe(join16(root, AGENTS_REL2, "long-memory", `${slug}.md`));
|
|
35935
36093
|
if (long) files.push({ path: "long-memory.md", contentBase64: long.toString("base64") });
|
|
35936
36094
|
} else {
|
|
35937
|
-
const base =
|
|
36095
|
+
const base = join16(root, SKILLS_REL2, slug);
|
|
35938
36096
|
for (const rel of await listFilesRel(base)) {
|
|
35939
|
-
const buf = await readMaybe(
|
|
36097
|
+
const buf = await readMaybe(join16(base, rel));
|
|
35940
36098
|
if (buf) files.push({ path: rel.split("\\").join("/"), contentBase64: buf.toString("base64") });
|
|
35941
36099
|
}
|
|
35942
36100
|
}
|
|
@@ -35954,7 +36112,7 @@ async function artifactSha256(root, kind, slug) {
|
|
|
35954
36112
|
return h.digest("hex");
|
|
35955
36113
|
}
|
|
35956
36114
|
async function artifactExists(root, kind, slug) {
|
|
35957
|
-
const target = kind === "subagent" ?
|
|
36115
|
+
const target = kind === "subagent" ? join16(root, AGENTS_REL2, `${slug}.md`) : join16(root, SKILLS_REL2, slug);
|
|
35958
36116
|
try {
|
|
35959
36117
|
await stat(target);
|
|
35960
36118
|
return true;
|
|
@@ -35963,7 +36121,7 @@ async function artifactExists(root, kind, slug) {
|
|
|
35963
36121
|
}
|
|
35964
36122
|
}
|
|
35965
36123
|
async function readManifest(root) {
|
|
35966
|
-
const buf = await readMaybe(
|
|
36124
|
+
const buf = await readMaybe(join16(root, MANIFEST_REL));
|
|
35967
36125
|
if (!buf) return {};
|
|
35968
36126
|
try {
|
|
35969
36127
|
return JSON.parse(buf.toString("utf8"));
|
|
@@ -35972,7 +36130,7 @@ async function readManifest(root) {
|
|
|
35972
36130
|
}
|
|
35973
36131
|
}
|
|
35974
36132
|
async function writeManifest(root, manifest) {
|
|
35975
|
-
const path =
|
|
36133
|
+
const path = join16(root, MANIFEST_REL);
|
|
35976
36134
|
await mkdir4(dirname4(path), { recursive: true });
|
|
35977
36135
|
await writeFile4(path, JSON.stringify(manifest, null, 2), "utf8");
|
|
35978
36136
|
}
|
|
@@ -35981,24 +36139,24 @@ async function writeArtifactFiles(root, kind, slug, files) {
|
|
|
35981
36139
|
if (kind === "subagent") {
|
|
35982
36140
|
const agent = byPath.get("agent.md");
|
|
35983
36141
|
if (agent) {
|
|
35984
|
-
await mkdir4(
|
|
35985
|
-
await writeFile4(
|
|
36142
|
+
await mkdir4(join16(root, AGENTS_REL2), { recursive: true });
|
|
36143
|
+
await writeFile4(join16(root, AGENTS_REL2, `${slug}.md`), agent);
|
|
35986
36144
|
}
|
|
35987
36145
|
const short = byPath.get("short-memory.md");
|
|
35988
36146
|
if (short) {
|
|
35989
|
-
await mkdir4(
|
|
35990
|
-
await writeFile4(
|
|
36147
|
+
await mkdir4(join16(root, AGENTS_REL2, "short-memory"), { recursive: true });
|
|
36148
|
+
await writeFile4(join16(root, AGENTS_REL2, "short-memory", `${slug}.md`), short);
|
|
35991
36149
|
}
|
|
35992
36150
|
const long = byPath.get("long-memory.md");
|
|
35993
36151
|
if (long) {
|
|
35994
|
-
await mkdir4(
|
|
35995
|
-
await writeFile4(
|
|
36152
|
+
await mkdir4(join16(root, AGENTS_REL2, "long-memory"), { recursive: true });
|
|
36153
|
+
await writeFile4(join16(root, AGENTS_REL2, "long-memory", `${slug}.md`), long);
|
|
35996
36154
|
}
|
|
35997
36155
|
} else {
|
|
35998
|
-
const base =
|
|
36156
|
+
const base = join16(root, SKILLS_REL2, slug);
|
|
35999
36157
|
for (const [rel, buf] of byPath) {
|
|
36000
36158
|
if (rel.includes("..") || rel.startsWith("/")) continue;
|
|
36001
|
-
const dest =
|
|
36159
|
+
const dest = join16(base, rel);
|
|
36002
36160
|
await mkdir4(dirname4(dest), { recursive: true });
|
|
36003
36161
|
await writeFile4(dest, buf);
|
|
36004
36162
|
}
|
|
@@ -36064,11 +36222,11 @@ async function removePackage(root, slug) {
|
|
|
36064
36222
|
const entry = manifest[n];
|
|
36065
36223
|
if (!entry) return { ok: false, error: "not installed" };
|
|
36066
36224
|
if (entry.kind === "subagent") {
|
|
36067
|
-
await rm2(
|
|
36068
|
-
await rm2(
|
|
36069
|
-
await rm2(
|
|
36225
|
+
await rm2(join16(root, AGENTS_REL2, `${n}.md`), { force: true });
|
|
36226
|
+
await rm2(join16(root, AGENTS_REL2, "short-memory", `${n}.md`), { force: true });
|
|
36227
|
+
await rm2(join16(root, AGENTS_REL2, "long-memory", `${n}.md`), { force: true });
|
|
36070
36228
|
} else {
|
|
36071
|
-
await rm2(
|
|
36229
|
+
await rm2(join16(root, SKILLS_REL2, n), { recursive: true, force: true });
|
|
36072
36230
|
}
|
|
36073
36231
|
delete manifest[n];
|
|
36074
36232
|
await writeManifest(root, manifest);
|
|
@@ -36079,7 +36237,7 @@ async function removePackage(root, slug) {
|
|
|
36079
36237
|
}
|
|
36080
36238
|
|
|
36081
36239
|
// src/core/network-probe.ts
|
|
36082
|
-
import { existsSync as
|
|
36240
|
+
import { existsSync as existsSync10, readFileSync as readFileSync14 } from "fs";
|
|
36083
36241
|
import { connect } from "net";
|
|
36084
36242
|
var CANARY_TARGETS = [
|
|
36085
36243
|
{ host: "1.1.1.1", port: 443 },
|
|
@@ -36088,7 +36246,7 @@ var CANARY_TARGETS = [
|
|
|
36088
36246
|
var PROBE_TIMEOUT_MS = 2500;
|
|
36089
36247
|
function detectContainerized() {
|
|
36090
36248
|
try {
|
|
36091
|
-
if (
|
|
36249
|
+
if (existsSync10("/.dockerenv")) return true;
|
|
36092
36250
|
const cgroup = readFileSync14("/proc/1/cgroup", "utf8");
|
|
36093
36251
|
return /docker|containerd|kubepods|libpod/.test(cgroup);
|
|
36094
36252
|
} catch {
|
|
@@ -36156,9 +36314,9 @@ async function probeNetwork() {
|
|
|
36156
36314
|
}
|
|
36157
36315
|
|
|
36158
36316
|
// src/core/git-auth.ts
|
|
36159
|
-
import { writeFileSync as
|
|
36317
|
+
import { writeFileSync as writeFileSync7 } from "fs";
|
|
36160
36318
|
import { homedir as homedir4 } from "os";
|
|
36161
|
-
import { join as
|
|
36319
|
+
import { join as join17 } from "path";
|
|
36162
36320
|
function tokenTarget(method) {
|
|
36163
36321
|
if (method === "gitlab-group-token") {
|
|
36164
36322
|
return { host: process.env.FOURPM_GIT_HOST || "gitlab.com", user: "oauth2" };
|
|
@@ -36176,7 +36334,7 @@ function applyGitAuth(method) {
|
|
|
36176
36334
|
try {
|
|
36177
36335
|
const line = `https://${target.user}:${token}@${target.host}
|
|
36178
36336
|
`;
|
|
36179
|
-
|
|
36337
|
+
writeFileSync7(join17(homedir4(), ".git-credentials"), line, { encoding: "utf8", mode: 384 });
|
|
36180
36338
|
return true;
|
|
36181
36339
|
} catch {
|
|
36182
36340
|
return false;
|
|
@@ -36185,7 +36343,7 @@ function applyGitAuth(method) {
|
|
|
36185
36343
|
|
|
36186
36344
|
// src/core/secrets.ts
|
|
36187
36345
|
import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
36188
|
-
import { join as
|
|
36346
|
+
import { join as join18 } from "path";
|
|
36189
36347
|
var SECURITY_REL = "AI_SECURITY.md";
|
|
36190
36348
|
var PLACEHOLDER_REL = "AI_PLACEHOLDER.md";
|
|
36191
36349
|
var SECRETS_REL = "project.secrets.json";
|
|
@@ -36198,7 +36356,7 @@ async function readText3(path, fallback = "") {
|
|
|
36198
36356
|
}
|
|
36199
36357
|
async function readSecretsObject(root) {
|
|
36200
36358
|
try {
|
|
36201
|
-
const v = JSON.parse(await readFile6(
|
|
36359
|
+
const v = JSON.parse(await readFile6(join18(root, SECRETS_REL), "utf8"));
|
|
36202
36360
|
return v && typeof v === "object" ? v : {};
|
|
36203
36361
|
} catch {
|
|
36204
36362
|
return {};
|
|
@@ -36217,8 +36375,8 @@ function placeholderKeys(text) {
|
|
|
36217
36375
|
}
|
|
36218
36376
|
async function readSecrets(root) {
|
|
36219
36377
|
const [security, placeholder, secrets] = await Promise.all([
|
|
36220
|
-
readText3(
|
|
36221
|
-
readText3(
|
|
36378
|
+
readText3(join18(root, SECURITY_REL)),
|
|
36379
|
+
readText3(join18(root, PLACEHOLDER_REL)),
|
|
36222
36380
|
readSecretsObject(root)
|
|
36223
36381
|
]);
|
|
36224
36382
|
const names = /* @__PURE__ */ new Set([...placeholderKeys(placeholder), ...Object.keys(secrets)]);
|
|
@@ -36231,14 +36389,14 @@ async function readSecrets(root) {
|
|
|
36231
36389
|
async function writeSecrets(root, req) {
|
|
36232
36390
|
try {
|
|
36233
36391
|
if (req.kind === "security") {
|
|
36234
|
-
await writeFile5(
|
|
36392
|
+
await writeFile5(join18(root, SECURITY_REL), req.content, "utf8");
|
|
36235
36393
|
} else if (req.kind === "placeholder") {
|
|
36236
|
-
await writeFile5(
|
|
36394
|
+
await writeFile5(join18(root, PLACEHOLDER_REL), req.content, "utf8");
|
|
36237
36395
|
} else {
|
|
36238
36396
|
const secrets = await readSecretsObject(root);
|
|
36239
36397
|
if (req.kind === "secret") secrets[req.key] = req.value;
|
|
36240
36398
|
else delete secrets[req.key];
|
|
36241
|
-
await writeFile5(
|
|
36399
|
+
await writeFile5(join18(root, SECRETS_REL), JSON.stringify(secrets, null, 2) + "\n", "utf8");
|
|
36242
36400
|
}
|
|
36243
36401
|
return { ok: true };
|
|
36244
36402
|
} catch (err) {
|
|
@@ -36248,13 +36406,13 @@ async function writeSecrets(root, req) {
|
|
|
36248
36406
|
|
|
36249
36407
|
// src/core/agent-tools.ts
|
|
36250
36408
|
import { readFile as readFile7, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
|
|
36251
|
-
import { dirname as dirname5, join as
|
|
36409
|
+
import { dirname as dirname5, join as join19 } from "path";
|
|
36252
36410
|
var SHARED_REL = ".claude/settings.json";
|
|
36253
36411
|
var LOCAL_REL = ".claude/settings.local.json";
|
|
36254
36412
|
var SECRETS_DENY = "Read(./project.secrets.json)";
|
|
36255
36413
|
var MODES = ["default", "acceptEdits", "plan", "bypassPermissions"];
|
|
36256
36414
|
function fileFor(root, scope) {
|
|
36257
|
-
return
|
|
36415
|
+
return join19(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
|
|
36258
36416
|
}
|
|
36259
36417
|
async function readSettingsObject(path) {
|
|
36260
36418
|
try {
|
|
@@ -36291,7 +36449,7 @@ async function writeAgentTools(root, scope, permissions) {
|
|
|
36291
36449
|
|
|
36292
36450
|
// src/core/graph.ts
|
|
36293
36451
|
import { readFile as readFile8, readdir as readdir5 } from "fs/promises";
|
|
36294
|
-
import { join as
|
|
36452
|
+
import { join as join20, relative as relative2, dirname as dirname6, resolve as resolve4, extname, basename as basename2 } from "path";
|
|
36295
36453
|
var MAX_FILES = 2e3;
|
|
36296
36454
|
var MAX_NODES = 3e3;
|
|
36297
36455
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "out", "coverage", ".turbo"]);
|
|
@@ -36311,7 +36469,7 @@ async function walk(root, accept) {
|
|
|
36311
36469
|
if (e.name.startsWith(".") && e.name !== ".claude") {
|
|
36312
36470
|
if (e.isDirectory()) continue;
|
|
36313
36471
|
}
|
|
36314
|
-
const full =
|
|
36472
|
+
const full = join20(dir2, e.name);
|
|
36315
36473
|
if (e.isDirectory()) {
|
|
36316
36474
|
if (!SKIP_DIRS.has(e.name)) stack.push(full);
|
|
36317
36475
|
} else if (accept(full)) {
|
|
@@ -36431,10 +36589,10 @@ async function buildGraph(root, mode) {
|
|
|
36431
36589
|
|
|
36432
36590
|
// src/core/rag.ts
|
|
36433
36591
|
import { readFile as readFile9, writeFile as writeFile7, statfs as statfs2, mkdir as mkdir6 } from "fs/promises";
|
|
36434
|
-
import { existsSync as
|
|
36592
|
+
import { existsSync as existsSync11 } from "fs";
|
|
36435
36593
|
import { execFile as execFile4, spawn as spawn8 } from "child_process";
|
|
36436
36594
|
import { totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
36437
|
-
import { join as
|
|
36595
|
+
import { join as join21 } from "path";
|
|
36438
36596
|
import { promisify as promisify4 } from "util";
|
|
36439
36597
|
var run4 = promisify4(execFile4);
|
|
36440
36598
|
var RAG_REL = ".claude/rag";
|
|
@@ -36455,7 +36613,7 @@ async function detectPython() {
|
|
|
36455
36613
|
}
|
|
36456
36614
|
async function installLog(root) {
|
|
36457
36615
|
try {
|
|
36458
|
-
const text = await readFile9(
|
|
36616
|
+
const text = await readFile9(join21(root, RAG_REL, "install.log"), "utf8");
|
|
36459
36617
|
return text.split("\n").filter((l) => l.length > 0).slice(-200);
|
|
36460
36618
|
} catch {
|
|
36461
36619
|
return [];
|
|
@@ -36517,14 +36675,14 @@ async function ragStatus(root) {
|
|
|
36517
36675
|
};
|
|
36518
36676
|
let modelName = "";
|
|
36519
36677
|
try {
|
|
36520
|
-
modelName = (await readFile9(
|
|
36678
|
+
modelName = (await readFile9(join21(root, RAG_REL, ".installed"), "utf8")).trim();
|
|
36521
36679
|
} catch {
|
|
36522
36680
|
modelName = "";
|
|
36523
36681
|
}
|
|
36524
36682
|
const modelPresent = modelName.length > 0;
|
|
36525
36683
|
return {
|
|
36526
36684
|
installed: fastembed && sqliteVec && modelPresent,
|
|
36527
|
-
installing:
|
|
36685
|
+
installing: existsSync11(join21(root, RAG_REL, ".installing")),
|
|
36528
36686
|
python: { found: py.found, version: py.version },
|
|
36529
36687
|
deps: { pip, fastembed, sqliteVec },
|
|
36530
36688
|
model: { present: modelPresent, name: modelName },
|
|
@@ -36535,9 +36693,9 @@ async function ragStatus(root) {
|
|
|
36535
36693
|
};
|
|
36536
36694
|
}
|
|
36537
36695
|
async function readIndexMeta(root) {
|
|
36538
|
-
const indexing =
|
|
36696
|
+
const indexing = existsSync11(join21(root, RAG_REL, ".indexing"));
|
|
36539
36697
|
try {
|
|
36540
|
-
const m = JSON.parse(await readFile9(
|
|
36698
|
+
const m = JSON.parse(await readFile9(join21(root, RAG_REL, ".index.json"), "utf8"));
|
|
36541
36699
|
return {
|
|
36542
36700
|
present: true,
|
|
36543
36701
|
indexing,
|
|
@@ -36604,14 +36762,14 @@ if __name__ == "__main__":
|
|
|
36604
36762
|
elif cmd == "query": query(sys.argv[2], int(sys.argv[3]) if len(sys.argv) > 3 else 8)
|
|
36605
36763
|
`;
|
|
36606
36764
|
async function ensureEngine(root) {
|
|
36607
|
-
await mkdir6(
|
|
36608
|
-
await writeFile7(
|
|
36765
|
+
await mkdir6(join21(root, RAG_REL), { recursive: true }).catch(() => void 0);
|
|
36766
|
+
await writeFile7(join21(root, RAG_REL, "rag_engine.py"), RAG_ENGINE_PY, "utf8");
|
|
36609
36767
|
}
|
|
36610
36768
|
async function ragReindex(root) {
|
|
36611
36769
|
const py = await detectPython();
|
|
36612
36770
|
if (!py.found) return { ok: false, started: false, error: "python3 not found" };
|
|
36613
|
-
if (!
|
|
36614
|
-
if (
|
|
36771
|
+
if (!existsSync11(join21(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
|
|
36772
|
+
if (existsSync11(join21(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
|
|
36615
36773
|
await ensureEngine(root);
|
|
36616
36774
|
const script = `
|
|
36617
36775
|
set +e
|
|
@@ -36631,12 +36789,12 @@ rm -f "${RAG_REL}/.indexing"
|
|
|
36631
36789
|
async function ragQuery(root, queryText, k = 8) {
|
|
36632
36790
|
const py = await detectPython();
|
|
36633
36791
|
if (!py.found) return { results: [], error: "python3 not found" };
|
|
36634
|
-
if (!
|
|
36792
|
+
if (!existsSync11(join21(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
|
|
36635
36793
|
await ensureEngine(root);
|
|
36636
36794
|
try {
|
|
36637
36795
|
const { stdout } = await run4(
|
|
36638
36796
|
py.bin,
|
|
36639
|
-
[
|
|
36797
|
+
[join21(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k, 1), 20))],
|
|
36640
36798
|
{ cwd: root, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
36641
36799
|
);
|
|
36642
36800
|
const parsed = JSON.parse(stdout.trim() || '{"results":[]}');
|
|
@@ -36651,9 +36809,9 @@ async function ragInstall(root, model) {
|
|
|
36651
36809
|
}
|
|
36652
36810
|
const py = await detectPython();
|
|
36653
36811
|
if (!py.found) return { ok: false, started: false, error: "python3 not found on the worker" };
|
|
36654
|
-
if (
|
|
36812
|
+
if (existsSync11(join21(root, RAG_REL, ".installing"))) return { ok: true, started: true };
|
|
36655
36813
|
try {
|
|
36656
|
-
await mkdir6(
|
|
36814
|
+
await mkdir6(join21(root, RAG_REL), { recursive: true });
|
|
36657
36815
|
} catch {
|
|
36658
36816
|
}
|
|
36659
36817
|
const script = `
|
|
@@ -36741,12 +36899,12 @@ ${stderr}`;
|
|
|
36741
36899
|
// src/core/git-ssh-key.ts
|
|
36742
36900
|
import { execFile as execFile7 } from "child_process";
|
|
36743
36901
|
import { readFile as readFile11, rm as rm3, mkdir as mkdir7 } from "fs/promises";
|
|
36744
|
-
import { join as
|
|
36902
|
+
import { join as join22 } from "path";
|
|
36745
36903
|
import { promisify as promisify7 } from "util";
|
|
36746
36904
|
var run7 = promisify7(execFile7);
|
|
36747
36905
|
function keyPaths(profileDir2) {
|
|
36748
|
-
const dir2 =
|
|
36749
|
-
const key =
|
|
36906
|
+
const dir2 = join22(profileDir2, ".ssh");
|
|
36907
|
+
const key = join22(dir2, "id_4pm");
|
|
36750
36908
|
return { dir: dir2, key, pub: `${key}.pub` };
|
|
36751
36909
|
}
|
|
36752
36910
|
async function readPublicKey(pub) {
|
|
@@ -36938,7 +37096,7 @@ async function setCommitAuthor(cwd2, machineUsername) {
|
|
|
36938
37096
|
// src/core/scaffold.ts
|
|
36939
37097
|
import { cp, mkdir as mkdir8, writeFile as writeFile8 } from "fs/promises";
|
|
36940
37098
|
import { execFile as execFile10 } from "child_process";
|
|
36941
|
-
import { dirname as dirname8, join as
|
|
37099
|
+
import { dirname as dirname8, join as join23, resolve as resolve7 } from "path";
|
|
36942
37100
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
36943
37101
|
import { promisify as promisify10 } from "util";
|
|
36944
37102
|
var run10 = promisify10(execFile10);
|
|
@@ -36967,7 +37125,7 @@ async function provisionRepos(target, repos, emit, opts = {}) {
|
|
|
36967
37125
|
}
|
|
36968
37126
|
continue;
|
|
36969
37127
|
}
|
|
36970
|
-
const dir2 =
|
|
37128
|
+
const dir2 = join23(target, subDirName(repo));
|
|
36971
37129
|
if (repo.url) {
|
|
36972
37130
|
emit("git", `Cloning sub-repo ${repo.url}\u2026`);
|
|
36973
37131
|
await run10("git", ["clone", repo.url, dir2], { timeout: 12e4 });
|
|
@@ -36987,14 +37145,14 @@ async function scaffoldProject(payload, profileDir2, onProgress) {
|
|
|
36987
37145
|
const emit = (step, message) => onProgress?.({ projectId: payload.projectId, step, message });
|
|
36988
37146
|
try {
|
|
36989
37147
|
const root = process.env.SCAFFOLD_ROOT ? resolve7(process.env.SCAFFOLD_ROOT) : resolve7(profileDir2);
|
|
36990
|
-
const target =
|
|
37148
|
+
const target = join23(root, payload.projectName);
|
|
36991
37149
|
await mkdir8(target, { recursive: true });
|
|
36992
37150
|
emit("copy", "Copying the sample template\u2026");
|
|
36993
37151
|
await cp(sampleDir(), target, { recursive: true, force: false, errorOnExist: false });
|
|
36994
37152
|
if (payload.spec) {
|
|
36995
37153
|
emit("spec", "Writing project.spec.json\u2026");
|
|
36996
37154
|
await writeFile8(
|
|
36997
|
-
|
|
37155
|
+
join23(target, "project.spec.json"),
|
|
36998
37156
|
JSON.stringify(payload.spec, null, 2),
|
|
36999
37157
|
"utf8"
|
|
37000
37158
|
);
|
|
@@ -37012,7 +37170,7 @@ async function aiInit(target, spec, emit) {
|
|
|
37012
37170
|
emit("ai-init", "AI init: subagents, README, CLAUDE.md\u2026");
|
|
37013
37171
|
const subagents = Array.isArray(spec.subagents) ? spec.subagents : [];
|
|
37014
37172
|
if (subagents.length > 0) {
|
|
37015
|
-
await mkdir8(
|
|
37173
|
+
await mkdir8(join23(target, ".claude", "agents"), { recursive: true });
|
|
37016
37174
|
for (const sa of subagents) {
|
|
37017
37175
|
const name = (sa.name || "").trim().replace(/[^\w.-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
37018
37176
|
if (!name) continue;
|
|
@@ -37022,17 +37180,17 @@ name: ${name}
|
|
|
37022
37180
|
|
|
37023
37181
|
${(sa.description || "").trim()}
|
|
37024
37182
|
`;
|
|
37025
|
-
await writeFile8(
|
|
37183
|
+
await writeFile8(join23(target, ".claude", "agents", `${name}.md`), body, "utf8");
|
|
37026
37184
|
}
|
|
37027
37185
|
}
|
|
37028
37186
|
const specJson = JSON.stringify(spec);
|
|
37029
37187
|
await generateFile(
|
|
37030
|
-
|
|
37188
|
+
join23(target, "README.md"),
|
|
37031
37189
|
`Write a concise README.md (Markdown only, no preamble) for this project from its spec JSON:
|
|
37032
37190
|
${specJson}`
|
|
37033
37191
|
);
|
|
37034
37192
|
await generateFile(
|
|
37035
|
-
|
|
37193
|
+
join23(target, "CLAUDE.md"),
|
|
37036
37194
|
`Write a CLAUDE.md (Markdown only, no preamble) with guidance/conventions for AI agents working in this project, derived from its spec JSON:
|
|
37037
37195
|
${specJson}`
|
|
37038
37196
|
);
|
|
@@ -37048,7 +37206,7 @@ async function addProject(payload, profileDir2, onProgress) {
|
|
|
37048
37206
|
const emit = (step, message) => onProgress?.({ projectId: payload.projectId, step, message });
|
|
37049
37207
|
try {
|
|
37050
37208
|
const root = process.env.SCAFFOLD_ROOT ? resolve7(process.env.SCAFFOLD_ROOT) : resolve7(profileDir2);
|
|
37051
|
-
const target =
|
|
37209
|
+
const target = join23(root, payload.projectName);
|
|
37052
37210
|
await mkdir8(target, { recursive: true });
|
|
37053
37211
|
const repos = payload.repos ?? [];
|
|
37054
37212
|
if (repos.length > 0) await provisionRepos(target, repos, emit, { clonePrimary: true });
|
|
@@ -37060,8 +37218,8 @@ async function addProject(payload, profileDir2, onProgress) {
|
|
|
37060
37218
|
}
|
|
37061
37219
|
|
|
37062
37220
|
// src/ui/slash-commands.ts
|
|
37063
|
-
import { existsSync as
|
|
37064
|
-
import { join as
|
|
37221
|
+
import { existsSync as existsSync12 } from "fs";
|
|
37222
|
+
import { join as join24 } from "path";
|
|
37065
37223
|
var CONFIG_KEYS = [
|
|
37066
37224
|
"aiCli",
|
|
37067
37225
|
"claudeHome",
|
|
@@ -37073,7 +37231,7 @@ var CONFIG_KEYS = [
|
|
|
37073
37231
|
"aiEnv"
|
|
37074
37232
|
];
|
|
37075
37233
|
function configPath(profileDir2) {
|
|
37076
|
-
return
|
|
37234
|
+
return join24(profileDir2, "config.json");
|
|
37077
37235
|
}
|
|
37078
37236
|
function parseConfigAssignment(key, valueTokens) {
|
|
37079
37237
|
const value = valueTokens.join(" ").trim();
|
|
@@ -37123,7 +37281,7 @@ function runConfig(ctx) {
|
|
|
37123
37281
|
ctx.print(`\u2714 ${verb} default config: ${path}`);
|
|
37124
37282
|
ctx.print("add claude profiles: /config set claudeHome .claude .claude-1 (tried in order)");
|
|
37125
37283
|
};
|
|
37126
|
-
if (
|
|
37284
|
+
if (existsSync12(path)) {
|
|
37127
37285
|
ctx.confirm(
|
|
37128
37286
|
`config exists: ${path} \u2014 replace it with defaults? (y/N)`,
|
|
37129
37287
|
() => writeDefaults("replaced with")
|
|
@@ -37208,7 +37366,7 @@ function runOutput(ctx) {
|
|
|
37208
37366
|
}
|
|
37209
37367
|
function runLogs(ctx) {
|
|
37210
37368
|
const n = Math.max(1, Math.min(200, Number(ctx.args[0]) || 20));
|
|
37211
|
-
const dir2 =
|
|
37369
|
+
const dir2 = join24(ctx.info.profileDir, "logs");
|
|
37212
37370
|
const lines = readRecentLogLines(dir2, n);
|
|
37213
37371
|
if (lines.length === 0) {
|
|
37214
37372
|
ctx.print("No log lines yet.");
|
|
@@ -37521,8 +37679,8 @@ async function runMemoryCompaction(ai, cwd2, input) {
|
|
|
37521
37679
|
}
|
|
37522
37680
|
|
|
37523
37681
|
// src/core/config-sync.ts
|
|
37524
|
-
import { existsSync as
|
|
37525
|
-
import { join as
|
|
37682
|
+
import { existsSync as existsSync13, readFileSync as readFileSync15 } from "fs";
|
|
37683
|
+
import { join as join25 } from "path";
|
|
37526
37684
|
var SERVER_MANAGED_KEYS = [
|
|
37527
37685
|
"physicPath",
|
|
37528
37686
|
"sessionSwitchPct",
|
|
@@ -37539,8 +37697,8 @@ var SERVER_MANAGED_KEYS = [
|
|
|
37539
37697
|
"projectAiMemoryBudgetChars"
|
|
37540
37698
|
];
|
|
37541
37699
|
function readConfigText(profileDir2) {
|
|
37542
|
-
const path =
|
|
37543
|
-
if (
|
|
37700
|
+
const path = join25(profileDir2, "config.json");
|
|
37701
|
+
if (existsSync13(path)) {
|
|
37544
37702
|
try {
|
|
37545
37703
|
return readFileSync15(path, "utf8");
|
|
37546
37704
|
} catch {
|
|
@@ -37610,8 +37768,9 @@ var WsClient = class {
|
|
|
37610
37768
|
context.credential.serverUrl,
|
|
37611
37769
|
context.profileDir,
|
|
37612
37770
|
context.bus,
|
|
37613
|
-
// After the idle daily tick updates flagged tools, report the fresh snapshot (ADR-0254).
|
|
37614
|
-
|
|
37771
|
+
// After the idle daily tick updates flagged tools, report the fresh snapshot (ADR-0254). The
|
|
37772
|
+
// `daily` trigger lets the server re-drive a still-failing restore on this spaced tick (ADR-0258).
|
|
37773
|
+
() => void this.reportWorkerTools("daily")
|
|
37615
37774
|
);
|
|
37616
37775
|
context.bus.onLocalSubmit((input) => void this.runLocalCommand(input));
|
|
37617
37776
|
context.bus.onReconnect(() => this.reconnectNow());
|
|
@@ -37731,13 +37890,21 @@ var WsClient = class {
|
|
|
37731
37890
|
/**
|
|
37732
37891
|
* Detect the worker's tools and report the snapshot to the server (ADR-0254, `tools.report`) so the
|
|
37733
37892
|
* DB-backed Tools panel + restore target stay current. One-way, best-effort — a detect/send failure
|
|
37734
|
-
* never disrupts the session. Called after each tool op, after the daily tick, and
|
|
37893
|
+
* never disrupts the session. Called after each tool op (`op`), after the daily tick (`daily`), and
|
|
37894
|
+
* after a restore reconcile (`boot`/`manual`). `trigger` gates the server re-drive (ADR-0258: only
|
|
37895
|
+
* `daily` re-drives a still-failing restore); `restoreFailed` is passed only after a reconcile — a
|
|
37896
|
+
* plain `op`/`daily` report omits it so the server preserves the last known failed set.
|
|
37735
37897
|
*/
|
|
37736
|
-
async reportWorkerTools() {
|
|
37898
|
+
async reportWorkerTools(trigger = "op", restoreFailed) {
|
|
37737
37899
|
try {
|
|
37738
37900
|
const autoUpdate = readProfileConfig(this.context.profileDir).autoUpdateTools ?? [];
|
|
37739
37901
|
const { catalog, extras } = await detectWorkerTools(autoUpdate);
|
|
37740
|
-
this.send(WsChannels.TOOLS_REPORT, {
|
|
37902
|
+
this.send(WsChannels.TOOLS_REPORT, {
|
|
37903
|
+
catalog,
|
|
37904
|
+
extras,
|
|
37905
|
+
trigger,
|
|
37906
|
+
...restoreFailed !== void 0 ? { restoreFailed } : {}
|
|
37907
|
+
});
|
|
37741
37908
|
} catch (err) {
|
|
37742
37909
|
logger.warn("tools.report.error", { error: String(err) });
|
|
37743
37910
|
}
|
|
@@ -37883,6 +38050,7 @@ var WsClient = class {
|
|
|
37883
38050
|
pruneCommandHistoryByAge(token.cliRetentionDays);
|
|
37884
38051
|
pruneCommandOutputByAge(token.cliRetentionDays);
|
|
37885
38052
|
}
|
|
38053
|
+
if (this.physicRoot) sweepOldAttachments(this.physicRoot);
|
|
37886
38054
|
writeProfileConfig(this.context.profileDir, {
|
|
37887
38055
|
sessionSwitchPct: token.projectTokens?.sessionSwitchPct ?? 0,
|
|
37888
38056
|
perPromptTokenLimit: token.projectTokens?.perPromptTokenLimit ?? 0,
|
|
@@ -38051,8 +38219,8 @@ var WsClient = class {
|
|
|
38051
38219
|
const oldRoot = this.physicFolderPath(sync.oldName);
|
|
38052
38220
|
if (oldRoot) void uninstallCron(oldRoot).catch(() => void 0);
|
|
38053
38221
|
}
|
|
38054
|
-
if (sync.oldName)
|
|
38055
|
-
|
|
38222
|
+
if (sync.oldName) rmSync5(join26(this.context.profileDir, sync.oldName), { recursive: true, force: true });
|
|
38223
|
+
mkdirSync6(join26(this.context.profileDir, sync.newName), { recursive: true });
|
|
38056
38224
|
this.physicRoot = this.physicFolderPath(sync.newName);
|
|
38057
38225
|
this.bus.setProject(sync.newName);
|
|
38058
38226
|
this.bus.log(`Physic project folder synced \u2192 ${sync.newName}`);
|
|
@@ -38063,7 +38231,7 @@ var WsClient = class {
|
|
|
38063
38231
|
if (del.name) {
|
|
38064
38232
|
const delRoot = this.physicFolderPath(del.name);
|
|
38065
38233
|
if (delRoot) void uninstallCron(delRoot).catch(() => void 0);
|
|
38066
|
-
|
|
38234
|
+
rmSync5(join26(this.context.profileDir, del.name), { recursive: true, force: true });
|
|
38067
38235
|
this.bus.log(`Physic project folder deleted (project removed) \u2192 ${del.name}`);
|
|
38068
38236
|
}
|
|
38069
38237
|
void manageSshKey("delete", this.context.profileDir).catch(() => void 0);
|
|
@@ -38104,7 +38272,13 @@ var WsClient = class {
|
|
|
38104
38272
|
if (dispatch.cmd.trimStart().startsWith("/")) {
|
|
38105
38273
|
void this.runWebSlashCommand(dispatch.cmd, dispatch.commandId);
|
|
38106
38274
|
} else {
|
|
38107
|
-
void this.runAiPrompt(
|
|
38275
|
+
void this.runAiPrompt(
|
|
38276
|
+
dispatch.cmd,
|
|
38277
|
+
dispatch.commandId,
|
|
38278
|
+
"server",
|
|
38279
|
+
dispatch.aiOneShot ?? false,
|
|
38280
|
+
dispatch.images
|
|
38281
|
+
);
|
|
38108
38282
|
}
|
|
38109
38283
|
break;
|
|
38110
38284
|
}
|
|
@@ -38406,11 +38580,15 @@ var WsClient = class {
|
|
|
38406
38580
|
const req = payload;
|
|
38407
38581
|
const op = message.channel === WsChannels.TOOLS_INSTALL ? "install" : message.channel === WsChannels.TOOLS_UPDATE ? "update" : "uninstall";
|
|
38408
38582
|
this.send(message.channel, { started: true }, message.id);
|
|
38583
|
+
const opTimeoutMs = resolveInstallTimeoutMs(
|
|
38584
|
+
readProfileConfig(this.context.profileDir).toolInstallTimeoutSec
|
|
38585
|
+
);
|
|
38409
38586
|
void runWorkerToolOp(
|
|
38410
38587
|
op,
|
|
38411
38588
|
req.name,
|
|
38412
38589
|
req.manager,
|
|
38413
|
-
(line) => this.send(WsChannels.TOOLS_PROGRESS, { opId: req.opId, line })
|
|
38590
|
+
(line) => this.send(WsChannels.TOOLS_PROGRESS, { opId: req.opId, line }),
|
|
38591
|
+
opTimeoutMs
|
|
38414
38592
|
).then((res) => {
|
|
38415
38593
|
this.send(WsChannels.TOOLS_DONE, {
|
|
38416
38594
|
opId: req.opId,
|
|
@@ -38424,16 +38602,45 @@ var WsClient = class {
|
|
|
38424
38602
|
}
|
|
38425
38603
|
case WsChannels.TOOLS_RESTORE: {
|
|
38426
38604
|
const req = payload;
|
|
38427
|
-
|
|
38428
|
-
|
|
38429
|
-
(
|
|
38430
|
-
).then(() => this.reportWorkerTools()).then(() => this.send(WsChannels.TOOLS_RESTORE, { ok: true }, message.id)).catch(
|
|
38431
|
-
(err) => this.send(
|
|
38432
|
-
WsChannels.TOOLS_RESTORE,
|
|
38433
|
-
{ ok: false, error: String(err) },
|
|
38434
|
-
message.id
|
|
38435
|
-
)
|
|
38605
|
+
const restoreTrigger = req.trigger === "manual" ? "manual" : "boot";
|
|
38606
|
+
const installTimeoutMs = resolveInstallTimeoutMs(
|
|
38607
|
+
readProfileConfig(this.context.profileDir).toolInstallTimeoutSec
|
|
38436
38608
|
);
|
|
38609
|
+
const manifest = req.manifest.map((m) => ({ name: m.name, version: m.version, manager: m.manager }));
|
|
38610
|
+
const opId = req.opId;
|
|
38611
|
+
if (opId) {
|
|
38612
|
+
this.send(WsChannels.TOOLS_RESTORE, { ok: true }, message.id);
|
|
38613
|
+
void reconcileTools(
|
|
38614
|
+
manifest,
|
|
38615
|
+
(line) => {
|
|
38616
|
+
this.bus.log(line);
|
|
38617
|
+
this.send(WsChannels.TOOLS_PROGRESS, { opId, line });
|
|
38618
|
+
},
|
|
38619
|
+
installTimeoutMs
|
|
38620
|
+
).then(async (restoreFailed) => {
|
|
38621
|
+
this.send(WsChannels.TOOLS_DONE, {
|
|
38622
|
+
opId,
|
|
38623
|
+
ok: restoreFailed.length === 0,
|
|
38624
|
+
exitCode: restoreFailed.length === 0 ? 0 : 1
|
|
38625
|
+
});
|
|
38626
|
+
await this.reportWorkerTools(restoreTrigger, restoreFailed);
|
|
38627
|
+
}).catch(
|
|
38628
|
+
(err) => this.send(WsChannels.TOOLS_DONE, {
|
|
38629
|
+
opId,
|
|
38630
|
+
ok: false,
|
|
38631
|
+
exitCode: 1,
|
|
38632
|
+
error: String(err)
|
|
38633
|
+
})
|
|
38634
|
+
);
|
|
38635
|
+
} else {
|
|
38636
|
+
void reconcileTools(manifest, (line) => this.bus.log(line), installTimeoutMs).then((restoreFailed) => this.reportWorkerTools(restoreTrigger, restoreFailed)).then(() => this.send(WsChannels.TOOLS_RESTORE, { ok: true }, message.id)).catch(
|
|
38637
|
+
(err) => this.send(
|
|
38638
|
+
WsChannels.TOOLS_RESTORE,
|
|
38639
|
+
{ ok: false, error: String(err) },
|
|
38640
|
+
message.id
|
|
38641
|
+
)
|
|
38642
|
+
);
|
|
38643
|
+
}
|
|
38437
38644
|
break;
|
|
38438
38645
|
}
|
|
38439
38646
|
case WsChannels.GIT_DIFF:
|
|
@@ -38481,7 +38688,7 @@ var WsClient = class {
|
|
|
38481
38688
|
}
|
|
38482
38689
|
case WsChannels.LOG_READ: {
|
|
38483
38690
|
const limit = Math.min(Math.max(payload.limit ?? 200, 1), 2e3);
|
|
38484
|
-
const lines = readRecentLogLines(
|
|
38691
|
+
const lines = readRecentLogLines(join26(this.context.profileDir, "logs"), limit);
|
|
38485
38692
|
this.send(WsChannels.LOG_READ, { lines }, message.id);
|
|
38486
38693
|
break;
|
|
38487
38694
|
}
|
|
@@ -38560,7 +38767,7 @@ var WsClient = class {
|
|
|
38560
38767
|
* exactly like typing the prompt in the cli (tries profiles, meters real tokens) instead
|
|
38561
38768
|
* of spawning the text as a raw command. Streams output to the transcript AND the server.
|
|
38562
38769
|
*/
|
|
38563
|
-
async runAiPrompt(prompt, commandId, origin, oneShot = false) {
|
|
38770
|
+
async runAiPrompt(prompt, commandId, origin, oneShot = false, images) {
|
|
38564
38771
|
const config = readProfileConfig(this.context.profileDir);
|
|
38565
38772
|
const projectTimeoutSec = config.projectAiRunTimeoutSec ?? 0;
|
|
38566
38773
|
const machineTimeoutSec = config.aiRunTimeoutSec ?? 0;
|
|
@@ -38618,7 +38825,18 @@ var WsClient = class {
|
|
|
38618
38825
|
text: `session ${this.usageSnapshot?.session.utilizationPct ?? 0}% \u2265 ${config.sessionSwitchPct}% \u2014 switching to Claude profile "${labelFromCredentialKey(startCred)}"`
|
|
38619
38826
|
});
|
|
38620
38827
|
}
|
|
38621
|
-
|
|
38828
|
+
if (this.physicRoot) sweepOldAttachments(this.physicRoot);
|
|
38829
|
+
let aiBodyPrompt = prompt;
|
|
38830
|
+
if (images?.length && this.physicRoot && !oneShot) {
|
|
38831
|
+
const paths = await materializeImages(
|
|
38832
|
+
this.physicRoot,
|
|
38833
|
+
commandId,
|
|
38834
|
+
images,
|
|
38835
|
+
(imageId) => this.imageFetch(commandId, imageId)
|
|
38836
|
+
);
|
|
38837
|
+
if (paths.size) aiBodyPrompt = rewriteImagePlaceholders(prompt, paths);
|
|
38838
|
+
}
|
|
38839
|
+
const guardedPrompt = this.restrictToFolder && this.physicRoot ? folderScopeGuard(this.physicRoot, aiBodyPrompt) : aiBodyPrompt;
|
|
38622
38840
|
const hint = unified ? { credential: startCred ?? workingCred } : { dir: startDir ?? workingDir };
|
|
38623
38841
|
const memCfg = resolveMemoryConfig(config);
|
|
38624
38842
|
const firstAttempt = planAiRun(guardedPrompt, config, hint).attempts[0];
|
|
@@ -39202,6 +39420,13 @@ ${guardedPrompt}`;
|
|
|
39202
39420
|
checkQuota(metric, amount = 1) {
|
|
39203
39421
|
return this.request(WsChannels.QUOTA_CHECK, { metric, amount });
|
|
39204
39422
|
}
|
|
39423
|
+
/** Fetch a Console prompt image blob from the server to materialize on the worker (ADR-0257). */
|
|
39424
|
+
imageFetch(commandId, imageId) {
|
|
39425
|
+
return this.request(WsChannels.IMAGE_FETCH, {
|
|
39426
|
+
commandId,
|
|
39427
|
+
imageId
|
|
39428
|
+
});
|
|
39429
|
+
}
|
|
39205
39430
|
/** usage.report batched to the server (ADR-0020); tags each event with the AI profile. */
|
|
39206
39431
|
reportUsage(events, profile) {
|
|
39207
39432
|
const tagged = profile ? events.map((e) => ({ ...e, profile })) : events;
|
|
@@ -39239,8 +39464,8 @@ ${guardedPrompt}`;
|
|
|
39239
39464
|
/** Create the physic folder at an absolute path if missing (best-effort). */
|
|
39240
39465
|
ensurePhysicFolderPath(folder) {
|
|
39241
39466
|
try {
|
|
39242
|
-
if (!
|
|
39243
|
-
|
|
39467
|
+
if (!existsSync14(folder)) {
|
|
39468
|
+
mkdirSync6(folder, { recursive: true });
|
|
39244
39469
|
this.bus.log(`Created physic project folder: ${folder}`);
|
|
39245
39470
|
}
|
|
39246
39471
|
} catch (err) {
|
|
@@ -39254,7 +39479,7 @@ ${guardedPrompt}`;
|
|
|
39254
39479
|
*/
|
|
39255
39480
|
physicFolderPath(projectName) {
|
|
39256
39481
|
const safe = projectName.replace(/[/\\]/g, "_").replace(/\.\./g, "_").trim();
|
|
39257
|
-
return safe ?
|
|
39482
|
+
return safe ? join26(this.context.profileDir, safe) : null;
|
|
39258
39483
|
}
|
|
39259
39484
|
/**
|
|
39260
39485
|
* Report machine.status: version + the state of the physic project being served
|
|
@@ -39269,7 +39494,7 @@ ${guardedPrompt}`;
|
|
|
39269
39494
|
projects: [],
|
|
39270
39495
|
version: CLI_VERSION,
|
|
39271
39496
|
physicPath,
|
|
39272
|
-
physicPathExists: physicPath ?
|
|
39497
|
+
physicPathExists: physicPath ? existsSync14(physicPath) : void 0,
|
|
39273
39498
|
// Real coarse state (ADR-0152): !paused and a recent tick (was hard-coded false).
|
|
39274
39499
|
autonomousRunning: physicPath ? isAutonomousRunning(physicPath) : false,
|
|
39275
39500
|
// The machine-user AI-run limit (0 = unlimited) + profile count so a dispatch can resolve the
|
|
@@ -39363,7 +39588,7 @@ ${guardedPrompt}`;
|
|
|
39363
39588
|
*/
|
|
39364
39589
|
uploadLog() {
|
|
39365
39590
|
try {
|
|
39366
|
-
const upload = readLogUpload(
|
|
39591
|
+
const upload = readLogUpload(join26(this.context.profileDir, "logs"));
|
|
39367
39592
|
if (!upload) return;
|
|
39368
39593
|
const sig = `${upload.fileName}:${upload.totalBytes}`;
|
|
39369
39594
|
if (sig === this.lastLogSig) return;
|
|
@@ -39391,10 +39616,10 @@ ${guardedPrompt}`;
|
|
|
39391
39616
|
};
|
|
39392
39617
|
|
|
39393
39618
|
// src/core/instance-lock.ts
|
|
39394
|
-
import { readFileSync as readFileSync16, rmSync as
|
|
39395
|
-
import { join as
|
|
39619
|
+
import { readFileSync as readFileSync16, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
|
|
39620
|
+
import { join as join27 } from "path";
|
|
39396
39621
|
function lockPath(profileDir2) {
|
|
39397
|
-
return
|
|
39622
|
+
return join27(profileDir2, "start.lock");
|
|
39398
39623
|
}
|
|
39399
39624
|
function isAlive(pid) {
|
|
39400
39625
|
try {
|
|
@@ -39414,13 +39639,13 @@ function lockHolder(profileDir2) {
|
|
|
39414
39639
|
}
|
|
39415
39640
|
function acquireInstanceLock(profileDir2) {
|
|
39416
39641
|
if (lockHolder(profileDir2) !== null) return false;
|
|
39417
|
-
|
|
39642
|
+
writeFileSync8(lockPath(profileDir2), String(process.pid), "utf8");
|
|
39418
39643
|
return true;
|
|
39419
39644
|
}
|
|
39420
39645
|
function releaseInstanceLock(profileDir2) {
|
|
39421
39646
|
try {
|
|
39422
39647
|
const pid = Number.parseInt(readFileSync16(lockPath(profileDir2), "utf8").trim(), 10);
|
|
39423
|
-
if (pid === process.pid)
|
|
39648
|
+
if (pid === process.pid) rmSync6(lockPath(profileDir2), { force: true });
|
|
39424
39649
|
} catch {
|
|
39425
39650
|
}
|
|
39426
39651
|
}
|
|
@@ -39693,8 +39918,8 @@ var SessionBus = class {
|
|
|
39693
39918
|
|
|
39694
39919
|
// src/core/control-server.ts
|
|
39695
39920
|
import { createServer } from "net";
|
|
39696
|
-
import { rmSync as
|
|
39697
|
-
import { join as
|
|
39921
|
+
import { rmSync as rmSync7 } from "fs";
|
|
39922
|
+
import { join as join28 } from "path";
|
|
39698
39923
|
|
|
39699
39924
|
// src/core/control-protocol.ts
|
|
39700
39925
|
var CONTROL_SOCKET_FILE = "control.sock";
|
|
@@ -39723,9 +39948,9 @@ function createFrameParser() {
|
|
|
39723
39948
|
|
|
39724
39949
|
// src/core/control-server.ts
|
|
39725
39950
|
function startControlServer(bus, profileDir2, info) {
|
|
39726
|
-
const socketPath =
|
|
39951
|
+
const socketPath = join28(profileDir2, CONTROL_SOCKET_FILE);
|
|
39727
39952
|
try {
|
|
39728
|
-
|
|
39953
|
+
rmSync7(socketPath, { force: true });
|
|
39729
39954
|
} catch {
|
|
39730
39955
|
}
|
|
39731
39956
|
const clients = /* @__PURE__ */ new Set();
|
|
@@ -39791,7 +40016,7 @@ function startControlServer(bus, profileDir2, info) {
|
|
|
39791
40016
|
clients.clear();
|
|
39792
40017
|
server.close();
|
|
39793
40018
|
try {
|
|
39794
|
-
|
|
40019
|
+
rmSync7(socketPath, { force: true });
|
|
39795
40020
|
} catch {
|
|
39796
40021
|
}
|
|
39797
40022
|
};
|
|
@@ -45454,15 +45679,15 @@ var import_react21 = __toESM(require_react(), 1);
|
|
|
45454
45679
|
var import_react26 = __toESM(require_react(), 1);
|
|
45455
45680
|
|
|
45456
45681
|
// src/core/input-history.ts
|
|
45457
|
-
import { existsSync as
|
|
45458
|
-
import { join as
|
|
45682
|
+
import { existsSync as existsSync16, readFileSync as readFileSync18, writeFileSync as writeFileSync9 } from "fs";
|
|
45683
|
+
import { join as join29 } from "path";
|
|
45459
45684
|
var MAX_HISTORY = 200;
|
|
45460
45685
|
function historyPath(profileDir2) {
|
|
45461
|
-
return
|
|
45686
|
+
return join29(profileDir2, "input-history.json");
|
|
45462
45687
|
}
|
|
45463
45688
|
function loadInputHistory(profileDir2) {
|
|
45464
45689
|
const path = historyPath(profileDir2);
|
|
45465
|
-
if (!
|
|
45690
|
+
if (!existsSync16(path)) return [];
|
|
45466
45691
|
try {
|
|
45467
45692
|
const data = JSON.parse(readFileSync18(path, "utf8"));
|
|
45468
45693
|
return Array.isArray(data) ? data.filter((x) => typeof x === "string") : [];
|
|
@@ -45475,7 +45700,7 @@ function appendInputHistory(profileDir2, line, current) {
|
|
|
45475
45700
|
if (!trimmed || current[current.length - 1] === trimmed) return current;
|
|
45476
45701
|
const next = [...current, trimmed].slice(-MAX_HISTORY);
|
|
45477
45702
|
try {
|
|
45478
|
-
|
|
45703
|
+
writeFileSync9(historyPath(profileDir2), JSON.stringify(next), "utf8");
|
|
45479
45704
|
} catch {
|
|
45480
45705
|
}
|
|
45481
45706
|
return next;
|
|
@@ -46079,7 +46304,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
46079
46304
|
return;
|
|
46080
46305
|
}
|
|
46081
46306
|
logger.configure({
|
|
46082
|
-
dir:
|
|
46307
|
+
dir: join30(profileDir2, "logs"),
|
|
46083
46308
|
level: resolveLogLevel(),
|
|
46084
46309
|
base: { version: CLI_VERSION, profile: profileName, scope: credential.scope }
|
|
46085
46310
|
});
|
|
@@ -46189,7 +46414,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
46189
46414
|
}
|
|
46190
46415
|
|
|
46191
46416
|
// src/commands/attach.ts
|
|
46192
|
-
import { join as
|
|
46417
|
+
import { join as join31 } from "path";
|
|
46193
46418
|
|
|
46194
46419
|
// src/core/control-client.ts
|
|
46195
46420
|
import { connect as connect2 } from "net";
|
|
@@ -46288,7 +46513,7 @@ async function runAttach(profileDir2, profileName) {
|
|
|
46288
46513
|
process.exitCode = 1;
|
|
46289
46514
|
return;
|
|
46290
46515
|
}
|
|
46291
|
-
const socketPath =
|
|
46516
|
+
const socketPath = join31(profileDir2, CONTROL_SOCKET_FILE);
|
|
46292
46517
|
const bus = new SessionBus();
|
|
46293
46518
|
let conn;
|
|
46294
46519
|
try {
|
|
@@ -46321,7 +46546,7 @@ async function runAttach(profileDir2, profileName) {
|
|
|
46321
46546
|
|
|
46322
46547
|
// src/commands/ai-login.ts
|
|
46323
46548
|
import { spawn as spawn11 } from "child_process";
|
|
46324
|
-
import { mkdirSync as
|
|
46549
|
+
import { mkdirSync as mkdirSync7 } from "fs";
|
|
46325
46550
|
import { basename as basename5 } from "path";
|
|
46326
46551
|
function runProviderLogin(cmd, envVar, dir2) {
|
|
46327
46552
|
return new Promise((resolve8) => {
|
|
@@ -46359,7 +46584,7 @@ async function runAiLogin(profileDir2, profileName, aiFilter) {
|
|
|
46359
46584
|
continue;
|
|
46360
46585
|
}
|
|
46361
46586
|
const dir2 = resolveHomePath(cred.profile);
|
|
46362
|
-
|
|
46587
|
+
mkdirSync7(dir2, { recursive: true });
|
|
46363
46588
|
console.log(`
|
|
46364
46589
|
\u25B6 Logging in ${cred.provider} \xB7 ${cred.profile} (${envVar}=${dir2})`);
|
|
46365
46590
|
console.log(` Complete the provider's own sign-in (claude: type \`/login\`; codex: follow its prompt), then exit.`);
|
|
@@ -46373,7 +46598,7 @@ Tip: for the credentials to survive \`docker rm\`/recreate, keep each profile pa
|
|
|
46373
46598
|
}
|
|
46374
46599
|
|
|
46375
46600
|
// src/commands/unlink.ts
|
|
46376
|
-
import { rmSync as
|
|
46601
|
+
import { rmSync as rmSync8 } from "fs";
|
|
46377
46602
|
async function runUnlink(profileDir2, profileName) {
|
|
46378
46603
|
const cred = readCredential(profileDir2, profileName);
|
|
46379
46604
|
if (!cred) {
|
|
@@ -46388,7 +46613,7 @@ async function runUnlink(profileDir2, profileName) {
|
|
|
46388
46613
|
`\u26A0 Could not reach the server to revoke \u2014 revoke it at web /machines. (${err.message})`
|
|
46389
46614
|
);
|
|
46390
46615
|
}
|
|
46391
|
-
|
|
46616
|
+
rmSync8(profileDir2, { recursive: true, force: true });
|
|
46392
46617
|
clearDefaultProfileIf(profileName);
|
|
46393
46618
|
console.log("\u2714 Deleted the profile and its physic project folder.");
|
|
46394
46619
|
}
|