@4pm/cli 1.5.11 → 1.5.15
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 +451 -200
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28323,14 +28323,14 @@ function defaultProfileConfig() {
|
|
|
28323
28323
|
webBlockedCommands: ["quit", "config"],
|
|
28324
28324
|
// Shared AI memory (ADR-0245) — off by default (opt-in; costs a compaction call per turn).
|
|
28325
28325
|
aiMemoryEnabled: false,
|
|
28326
|
-
aiMemoryBudgetChars:
|
|
28326
|
+
aiMemoryBudgetChars: 1e3
|
|
28327
28327
|
};
|
|
28328
28328
|
}
|
|
28329
28329
|
function resolveMemoryConfig(config) {
|
|
28330
28330
|
const mode = config.projectAiMemoryMode ?? "inherit";
|
|
28331
28331
|
const enabled = mode === "on" ? true : mode === "off" ? false : config.aiMemoryEnabled ?? false;
|
|
28332
28332
|
const projectBudget = config.projectAiMemoryBudgetChars ?? 0;
|
|
28333
|
-
const budgetChars = projectBudget > 0 ? projectBudget : config.aiMemoryBudgetChars ??
|
|
28333
|
+
const budgetChars = projectBudget > 0 ? projectBudget : config.aiMemoryBudgetChars ?? 1e3;
|
|
28334
28334
|
return { enabled, budgetChars };
|
|
28335
28335
|
}
|
|
28336
28336
|
function resolveIdleAutoClearMinutes(config) {
|
|
@@ -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.
|
|
@@ -33760,6 +33790,10 @@ var WsChannels = {
|
|
|
33760
33790
|
// server → cli: the project was deleted ⇒ delete the physic folder; the cli keeps
|
|
33761
33791
|
// its pairing and goes idle (ADR-0068).
|
|
33762
33792
|
PHYSIC_DELETE: "physic.delete",
|
|
33793
|
+
// server → cli (ADR-0256): a project's token settings were saved ⇒ push the fresh knobs to every
|
|
33794
|
+
// serving cli so a change (e.g. aiRunTimeoutSec) applies on the next run, not only after a
|
|
33795
|
+
// reconnect. `ws_token` seeds these on connect; this channel only carries updates.
|
|
33796
|
+
PROJECT_TOKENS: "project.tokens",
|
|
33763
33797
|
// command lifecycle
|
|
33764
33798
|
COMMAND_ANNOUNCE: "command.announce",
|
|
33765
33799
|
// console transcript sync (ADR-0150): the cli streams its authoritative SessionBus
|
|
@@ -33798,6 +33832,9 @@ var WsChannels = {
|
|
|
33798
33832
|
// the worker uploads its pending log tail (+ any queued history) then acks, so the release
|
|
33799
33833
|
// sweep can finalise without waiting the full 5-minute timeout when the worker is online.
|
|
33800
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",
|
|
33801
33838
|
// outbound review (ADR-0082): machine→server request, server→outbound evaluate, result back
|
|
33802
33839
|
REVIEW_REQUEST: "review.request",
|
|
33803
33840
|
REVIEW_EVALUATE: "review.evaluate",
|
|
@@ -33867,7 +33904,7 @@ function decryptPayload(key, payloadBase64, nonceBase64) {
|
|
|
33867
33904
|
}
|
|
33868
33905
|
|
|
33869
33906
|
// src/core/ws-client.ts
|
|
33870
|
-
import { existsSync as
|
|
33907
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync6, rmSync as rmSync5 } from "fs";
|
|
33871
33908
|
import { randomUUID as randomCommandId } from "crypto";
|
|
33872
33909
|
|
|
33873
33910
|
// src/core/update-scheduler.ts
|
|
@@ -33979,6 +34016,35 @@ ${content.slice(-LOG_UPLOAD_CONTENT_MAX)}`;
|
|
|
33979
34016
|
// src/core/worker-tools.ts
|
|
33980
34017
|
import { spawn } from "child_process";
|
|
33981
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
|
+
}
|
|
33982
34048
|
function run(cmd, args, onLine, timeoutMs = 18e4) {
|
|
33983
34049
|
return new Promise((resolve8) => {
|
|
33984
34050
|
let out = "";
|
|
@@ -34095,13 +34161,13 @@ function opArgs(op, manager, pkg) {
|
|
|
34095
34161
|
const target = op === "update" ? `${pkg}@latest` : pkg;
|
|
34096
34162
|
return manager === "pnpm" ? ["add", "-g", target] : ["install", "-g", target];
|
|
34097
34163
|
}
|
|
34098
|
-
async function runWorkerToolOp(op, name, manager, onLine) {
|
|
34164
|
+
async function runWorkerToolOp(op, name, manager, onLine, timeoutMs = TOOL_INSTALL_TIMEOUT_DEFAULT_MS) {
|
|
34099
34165
|
const resolved = resolvePackage(name, op);
|
|
34100
34166
|
if (resolved.error || !resolved.pkg) {
|
|
34101
34167
|
return { ok: false, exitCode: 1, error: resolved.error ?? "Invalid package." };
|
|
34102
34168
|
}
|
|
34103
34169
|
onLine(`$ ${manager} ${opArgs(op, manager, resolved.pkg).join(" ")}`);
|
|
34104
|
-
const { code } = await run(manager, opArgs(op, manager, resolved.pkg), onLine);
|
|
34170
|
+
const { code } = await run(manager, opArgs(op, manager, resolved.pkg), onLine, timeoutMs);
|
|
34105
34171
|
return { ok: code === 0, exitCode: code, error: code === 0 ? void 0 : `${manager} exited ${code}` };
|
|
34106
34172
|
}
|
|
34107
34173
|
function setToolAutoUpdate(profileDir2, name, enabled) {
|
|
@@ -34114,25 +34180,40 @@ function setToolAutoUpdate(profileDir2, name, enabled) {
|
|
|
34114
34180
|
writeProfileConfig(profileDir2, { autoUpdateTools: [...set] });
|
|
34115
34181
|
return { ok: true };
|
|
34116
34182
|
}
|
|
34117
|
-
async function autoUpdateFlaggedTools(tools, onLine) {
|
|
34183
|
+
async function autoUpdateFlaggedTools(tools, onLine, timeoutMs = TOOL_INSTALL_TIMEOUT_DEFAULT_MS) {
|
|
34118
34184
|
for (const name of tools) {
|
|
34119
|
-
const res = await runWorkerToolOp("update", name, "npm", onLine).catch(
|
|
34185
|
+
const res = await runWorkerToolOp("update", name, "npm", onLine, timeoutMs).catch(
|
|
34120
34186
|
(err) => ({ ok: false, exitCode: 1, error: String(err) })
|
|
34121
34187
|
);
|
|
34122
34188
|
onLine(res.ok ? `\u2713 ${name} updated` : `\u2717 ${name}: ${res.error ?? "failed"}`);
|
|
34123
34189
|
}
|
|
34124
34190
|
}
|
|
34125
|
-
async function installPinned(name, version, manager, onLine) {
|
|
34191
|
+
async function installPinned(name, version, manager, onLine, timeoutMs = TOOL_INSTALL_TIMEOUT_DEFAULT_MS) {
|
|
34126
34192
|
const resolved = resolvePackage(name, "update");
|
|
34127
|
-
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
|
+
}
|
|
34128
34196
|
const target = `${resolved.pkg}@${version}`;
|
|
34129
34197
|
const args = manager === "pnpm" ? ["add", "-g", target] : ["install", "-g", target];
|
|
34130
|
-
|
|
34131
|
-
|
|
34132
|
-
|
|
34133
|
-
}
|
|
34134
|
-
|
|
34135
|
-
|
|
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;
|
|
34136
34217
|
const { catalog, extras } = await detectWorkerTools();
|
|
34137
34218
|
const versionByName = new Map(
|
|
34138
34219
|
[...catalog, ...extras].filter((r) => r.installed && r.version).map((r) => [r.id, r.version])
|
|
@@ -34141,11 +34222,27 @@ async function reconcileTools(manifest, onLine) {
|
|
|
34141
34222
|
const current = versionByName.get(entry.name);
|
|
34142
34223
|
if (current && current.includes(entry.version)) continue;
|
|
34143
34224
|
onLine(`Restoring ${entry.name}@${entry.version}\u2026`);
|
|
34144
|
-
const res = await installPinned(entry.name, entry.version, entry.manager, onLine).catch(
|
|
34145
|
-
(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
|
+
})
|
|
34146
34232
|
);
|
|
34147
|
-
|
|
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
|
+
}
|
|
34148
34244
|
}
|
|
34245
|
+
return failed;
|
|
34149
34246
|
}
|
|
34150
34247
|
|
|
34151
34248
|
// src/core/update-scheduler.ts
|
|
@@ -34259,10 +34356,12 @@ var UpdateScheduler = class {
|
|
|
34259
34356
|
}
|
|
34260
34357
|
/** Update each tool flagged for auto-update in config.json to @latest (ADR-0253) — best-effort. */
|
|
34261
34358
|
async updateFlaggedTools() {
|
|
34262
|
-
const
|
|
34359
|
+
const config = readProfileConfig(this.profileDir);
|
|
34360
|
+
const tools = config.autoUpdateTools ?? [];
|
|
34263
34361
|
if (tools.length === 0) return;
|
|
34264
34362
|
this.bus.log(`Scheduled tool auto-update: ${tools.join(", ")}\u2026`);
|
|
34265
|
-
|
|
34363
|
+
const timeoutMs = resolveInstallTimeoutMs(config.toolInstallTimeoutSec);
|
|
34364
|
+
await autoUpdateFlaggedTools(tools, (line) => logger.info("update.tool.line", { line }), timeoutMs).catch(
|
|
34266
34365
|
(err) => logger.warn("update.tool.error", { error: String(err) })
|
|
34267
34366
|
);
|
|
34268
34367
|
this.onToolsChanged?.();
|
|
@@ -35247,23 +35346,86 @@ function finishCommand(commandId, exitCode, usage) {
|
|
|
35247
35346
|
persist();
|
|
35248
35347
|
}
|
|
35249
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
|
+
|
|
35250
35412
|
// src/core/command-output-store.ts
|
|
35251
35413
|
import {
|
|
35252
35414
|
appendFileSync,
|
|
35253
|
-
existsSync as
|
|
35254
|
-
mkdirSync as
|
|
35415
|
+
existsSync as existsSync8,
|
|
35416
|
+
mkdirSync as mkdirSync4,
|
|
35255
35417
|
readFileSync as readFileSync11,
|
|
35256
|
-
readdirSync as
|
|
35257
|
-
rmSync as
|
|
35258
|
-
statSync as
|
|
35418
|
+
readdirSync as readdirSync4,
|
|
35419
|
+
rmSync as rmSync4,
|
|
35420
|
+
statSync as statSync3
|
|
35259
35421
|
} from "fs";
|
|
35260
|
-
import { join as
|
|
35422
|
+
import { join as join12 } from "path";
|
|
35261
35423
|
var KEEP_FILES = 200;
|
|
35262
35424
|
var dir = null;
|
|
35263
35425
|
function initCommandOutput(profileDir2) {
|
|
35264
|
-
dir =
|
|
35426
|
+
dir = join12(profileDir2, "command-output");
|
|
35265
35427
|
try {
|
|
35266
|
-
|
|
35428
|
+
mkdirSync4(dir, { recursive: true });
|
|
35267
35429
|
prune();
|
|
35268
35430
|
} catch {
|
|
35269
35431
|
}
|
|
@@ -35271,14 +35433,14 @@ function initCommandOutput(profileDir2) {
|
|
|
35271
35433
|
function appendCommandOutput(commandId, text) {
|
|
35272
35434
|
if (!dir || !text) return;
|
|
35273
35435
|
try {
|
|
35274
|
-
appendFileSync(
|
|
35436
|
+
appendFileSync(join12(dir, `${commandId}.log`), text, "utf8");
|
|
35275
35437
|
} catch {
|
|
35276
35438
|
}
|
|
35277
35439
|
}
|
|
35278
35440
|
function readCommandOutput(commandId) {
|
|
35279
35441
|
if (!dir) return null;
|
|
35280
|
-
const path =
|
|
35281
|
-
if (!
|
|
35442
|
+
const path = join12(dir, `${commandId}.log`);
|
|
35443
|
+
if (!existsSync8(path)) return null;
|
|
35282
35444
|
try {
|
|
35283
35445
|
return readFileSync11(path, "utf8");
|
|
35284
35446
|
} catch {
|
|
@@ -35288,8 +35450,8 @@ function readCommandOutput(commandId) {
|
|
|
35288
35450
|
function prune() {
|
|
35289
35451
|
if (!dir) return;
|
|
35290
35452
|
try {
|
|
35291
|
-
const files =
|
|
35292
|
-
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 });
|
|
35293
35455
|
} catch {
|
|
35294
35456
|
}
|
|
35295
35457
|
}
|
|
@@ -35297,9 +35459,9 @@ function pruneCommandOutputByAge(days) {
|
|
|
35297
35459
|
if (!dir || days <= 0) return;
|
|
35298
35460
|
const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
|
|
35299
35461
|
try {
|
|
35300
|
-
for (const f of
|
|
35301
|
-
const p =
|
|
35302
|
-
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 });
|
|
35303
35465
|
}
|
|
35304
35466
|
} catch {
|
|
35305
35467
|
}
|
|
@@ -35376,8 +35538,8 @@ async function writeWorkerFile(root, relPath, content) {
|
|
|
35376
35538
|
import { spawn as spawn5 } from "child_process";
|
|
35377
35539
|
import { execFile as execFile2 } from "child_process";
|
|
35378
35540
|
import { createHash as createHash3 } from "crypto";
|
|
35379
|
-
import { existsSync as
|
|
35380
|
-
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";
|
|
35381
35543
|
import { promisify as promisify2 } from "util";
|
|
35382
35544
|
var NO_USAGE = { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };
|
|
35383
35545
|
var execFileAsync = promisify2(execFile2);
|
|
@@ -35385,11 +35547,11 @@ var MAX_CONTEXT_CHARS = 12e4;
|
|
|
35385
35547
|
var ANSWER_TIMEOUT_MS = 12e4;
|
|
35386
35548
|
var DOC_EXTENSIONS = [".md", ".mdx", ".txt"];
|
|
35387
35549
|
function kbRoot(profileDir2) {
|
|
35388
|
-
return
|
|
35550
|
+
return join13(profileDir2, "support-kb");
|
|
35389
35551
|
}
|
|
35390
35552
|
function repoDir(profileDir2, url, branch) {
|
|
35391
35553
|
const key = createHash3("sha1").update(`${url}#${branch}`).digest("hex").slice(0, 16);
|
|
35392
|
-
return
|
|
35554
|
+
return join13(kbRoot(profileDir2), key);
|
|
35393
35555
|
}
|
|
35394
35556
|
function authedUrl(url, token) {
|
|
35395
35557
|
if (!token) return url;
|
|
@@ -35398,10 +35560,10 @@ function authedUrl(url, token) {
|
|
|
35398
35560
|
}
|
|
35399
35561
|
async function ensureRepo(profileDir2, repo) {
|
|
35400
35562
|
const dir2 = repoDir(profileDir2, repo.url, repo.branch);
|
|
35401
|
-
if (
|
|
35563
|
+
if (existsSync9(join13(dir2, ".git"))) return dir2;
|
|
35402
35564
|
const url = authedUrl(repo.url, repo.token);
|
|
35403
35565
|
const gitEnv = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
|
|
35404
|
-
|
|
35566
|
+
mkdirSync5(kbRoot(profileDir2), { recursive: true });
|
|
35405
35567
|
await execFileAsync("git", ["clone", "--depth", "1", "--branch", repo.branch, url, dir2], {
|
|
35406
35568
|
env: gitEnv
|
|
35407
35569
|
});
|
|
@@ -35409,11 +35571,11 @@ async function ensureRepo(profileDir2, repo) {
|
|
|
35409
35571
|
}
|
|
35410
35572
|
async function refreshSupportKb(profileDir2) {
|
|
35411
35573
|
const root = kbRoot(profileDir2);
|
|
35412
|
-
if (!
|
|
35574
|
+
if (!existsSync9(root)) return;
|
|
35413
35575
|
const gitEnv = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
|
|
35414
|
-
for (const entry of
|
|
35415
|
-
const dir2 =
|
|
35416
|
-
if (!
|
|
35576
|
+
for (const entry of readdirSync5(root)) {
|
|
35577
|
+
const dir2 = join13(root, entry);
|
|
35578
|
+
if (!existsSync9(join13(dir2, ".git"))) continue;
|
|
35417
35579
|
try {
|
|
35418
35580
|
await execFileAsync("git", ["-C", dir2, "pull", "--ff-only"], { env: gitEnv });
|
|
35419
35581
|
} catch (err) {
|
|
@@ -35426,10 +35588,10 @@ function collectDocs(dir2) {
|
|
|
35426
35588
|
let total = 0;
|
|
35427
35589
|
const walk2 = (current) => {
|
|
35428
35590
|
if (total >= MAX_CONTEXT_CHARS) return;
|
|
35429
|
-
for (const entry of
|
|
35591
|
+
for (const entry of readdirSync5(current)) {
|
|
35430
35592
|
if (entry === ".git" || entry === "node_modules") continue;
|
|
35431
|
-
const full =
|
|
35432
|
-
const st =
|
|
35593
|
+
const full = join13(current, entry);
|
|
35594
|
+
const st = statSync4(full);
|
|
35433
35595
|
if (st.isDirectory()) {
|
|
35434
35596
|
walk2(full);
|
|
35435
35597
|
} else if (DOC_EXTENSIONS.some((ext) => entry.toLowerCase().endsWith(ext))) {
|
|
@@ -35653,7 +35815,7 @@ async function runKnowledgeCompose(req, ai, physicRoot) {
|
|
|
35653
35815
|
import { readFile as readFile3, writeFile as writeFile2, chmod } from "fs/promises";
|
|
35654
35816
|
import { readFileSync as readFileSync13 } from "fs";
|
|
35655
35817
|
import { execFile as execFile3, spawn as spawn7 } from "child_process";
|
|
35656
|
-
import { join as
|
|
35818
|
+
import { join as join14 } from "path";
|
|
35657
35819
|
import { promisify as promisify3 } from "util";
|
|
35658
35820
|
var run3 = promisify3(execFile3);
|
|
35659
35821
|
var SETTINGS_REL = ".claude/.autonomous.settings.json";
|
|
@@ -35685,7 +35847,7 @@ function parseJson(text) {
|
|
|
35685
35847
|
}
|
|
35686
35848
|
}
|
|
35687
35849
|
function tickScript(root) {
|
|
35688
|
-
return
|
|
35850
|
+
return join14(root, TICK_REL);
|
|
35689
35851
|
}
|
|
35690
35852
|
async function crontabList() {
|
|
35691
35853
|
try {
|
|
@@ -35708,8 +35870,8 @@ async function cronInstalled(root) {
|
|
|
35708
35870
|
return (await crontabList()).split("\n").some((l) => l.includes(script) && !l.trim().startsWith("#"));
|
|
35709
35871
|
}
|
|
35710
35872
|
async function getAutonomousStatus(root) {
|
|
35711
|
-
const settings = parseJson(await readText(
|
|
35712
|
-
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))) ?? {};
|
|
35713
35875
|
const records = Array.isArray(hist.records) ? hist.records : [];
|
|
35714
35876
|
const last = records[records.length - 1];
|
|
35715
35877
|
const ticks = hist.ticks ?? {};
|
|
@@ -35726,9 +35888,9 @@ async function getAutonomousStatus(root) {
|
|
|
35726
35888
|
}
|
|
35727
35889
|
function isAutonomousRunning(root) {
|
|
35728
35890
|
try {
|
|
35729
|
-
const s = JSON.parse(readFileSync13(
|
|
35891
|
+
const s = JSON.parse(readFileSync13(join14(root, SETTINGS_REL), "utf8"));
|
|
35730
35892
|
if (s.paused === true) return false;
|
|
35731
|
-
const h = JSON.parse(readFileSync13(
|
|
35893
|
+
const h = JSON.parse(readFileSync13(join14(root, HISTORIES_REL), "utf8"));
|
|
35732
35894
|
const ts = h.records?.[h.records.length - 1]?.ts;
|
|
35733
35895
|
if (!ts) return false;
|
|
35734
35896
|
const t = Date.parse(ts.replace(" ", "T"));
|
|
@@ -35739,10 +35901,10 @@ function isAutonomousRunning(root) {
|
|
|
35739
35901
|
}
|
|
35740
35902
|
async function readAutonomous(root) {
|
|
35741
35903
|
const [settings, approvals, status, ...books] = await Promise.all([
|
|
35742
|
-
readText(
|
|
35743
|
-
readText(
|
|
35904
|
+
readText(join14(root, SETTINGS_REL)),
|
|
35905
|
+
readText(join14(root, APPROVALS_REL), "{}"),
|
|
35744
35906
|
getAutonomousStatus(root),
|
|
35745
|
-
...Object.values(BOOK_FILES).map((f) => readText(
|
|
35907
|
+
...Object.values(BOOK_FILES).map((f) => readText(join14(root, f)))
|
|
35746
35908
|
]);
|
|
35747
35909
|
const keys = Object.keys(BOOK_FILES);
|
|
35748
35910
|
const bookMap = {};
|
|
@@ -35751,7 +35913,7 @@ async function readAutonomous(root) {
|
|
|
35751
35913
|
}
|
|
35752
35914
|
async function readAutonomousLogs(root, date) {
|
|
35753
35915
|
const day = date && /^\d{4}-\d{2}-\d{2}$/.test(date) ? date : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
35754
|
-
const text = await readText(
|
|
35916
|
+
const text = await readText(join14(root, LOG_DIR_REL, `autonomous-tick-${day}.log`));
|
|
35755
35917
|
const lines = text ? text.split("\n").filter((l) => l.length > 0).slice(-500) : [];
|
|
35756
35918
|
return { date: day, lines };
|
|
35757
35919
|
}
|
|
@@ -35775,24 +35937,24 @@ async function writeAutonomous(root, req, by) {
|
|
|
35775
35937
|
switch (req.kind) {
|
|
35776
35938
|
case "settings": {
|
|
35777
35939
|
if (!parseJson(req.settings)) return { ok: false, error: "settings is not valid JSON" };
|
|
35778
|
-
await writeFile2(
|
|
35940
|
+
await writeFile2(join14(root, SETTINGS_REL), req.settings, "utf8");
|
|
35779
35941
|
break;
|
|
35780
35942
|
}
|
|
35781
35943
|
case "approvals": {
|
|
35782
|
-
const map = parseJson(await readText(
|
|
35944
|
+
const map = parseJson(await readText(join14(root, APPROVALS_REL), "{}")) ?? {};
|
|
35783
35945
|
if (req.approved) map[req.taskId] = { approved: true, by, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
35784
35946
|
else delete map[req.taskId];
|
|
35785
|
-
await writeFile2(
|
|
35947
|
+
await writeFile2(join14(root, APPROVALS_REL), JSON.stringify(map, null, 2) + "\n", "utf8");
|
|
35786
35948
|
break;
|
|
35787
35949
|
}
|
|
35788
35950
|
case "userTodo": {
|
|
35789
|
-
const cur = await readText(
|
|
35951
|
+
const cur = await readText(join14(root, BOOK_FILES.userTodo));
|
|
35790
35952
|
const block = `
|
|
35791
35953
|
|
|
35792
35954
|
> posted by ${by} at ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
35793
35955
|
${req.content.trim()}
|
|
35794
35956
|
`;
|
|
35795
|
-
await writeFile2(
|
|
35957
|
+
await writeFile2(join14(root, BOOK_FILES.userTodo), cur.trimEnd() + block, "utf8");
|
|
35796
35958
|
break;
|
|
35797
35959
|
}
|
|
35798
35960
|
case "cron": {
|
|
@@ -35809,7 +35971,7 @@ ${req.content.trim()}
|
|
|
35809
35971
|
|
|
35810
35972
|
// src/core/agents.ts
|
|
35811
35973
|
import { readFile as readFile4, writeFile as writeFile3, mkdir as mkdir3, readdir as readdir3, rm } from "fs/promises";
|
|
35812
|
-
import { join as
|
|
35974
|
+
import { join as join15 } from "path";
|
|
35813
35975
|
var AGENTS_REL = ".claude/agents";
|
|
35814
35976
|
var SKILLS_REL = ".claude/skills";
|
|
35815
35977
|
var MEMORY_DIRS = /* @__PURE__ */ new Set(["short-memory", "long-memory"]);
|
|
@@ -35833,17 +35995,17 @@ async function listAgents(root) {
|
|
|
35833
35995
|
const subagents = [];
|
|
35834
35996
|
const skills = [];
|
|
35835
35997
|
try {
|
|
35836
|
-
for (const e of await readdir3(
|
|
35998
|
+
for (const e of await readdir3(join15(root, AGENTS_REL), { withFileTypes: true })) {
|
|
35837
35999
|
if (!e.isFile() || !e.name.endsWith(".md") || MEMORY_DIRS.has(e.name)) continue;
|
|
35838
|
-
const fm = frontmatter(await readText2(
|
|
36000
|
+
const fm = frontmatter(await readText2(join15(root, AGENTS_REL, e.name)));
|
|
35839
36001
|
subagents.push({ name: e.name.replace(/\.md$/, ""), description: fm.description });
|
|
35840
36002
|
}
|
|
35841
36003
|
} catch {
|
|
35842
36004
|
}
|
|
35843
36005
|
try {
|
|
35844
|
-
for (const e of await readdir3(
|
|
36006
|
+
for (const e of await readdir3(join15(root, SKILLS_REL), { withFileTypes: true })) {
|
|
35845
36007
|
if (!e.isDirectory()) continue;
|
|
35846
|
-
const fm = frontmatter(await readText2(
|
|
36008
|
+
const fm = frontmatter(await readText2(join15(root, SKILLS_REL, e.name, "SKILL.md")));
|
|
35847
36009
|
skills.push({ name: e.name, description: fm.description });
|
|
35848
36010
|
}
|
|
35849
36011
|
} catch {
|
|
@@ -35854,15 +36016,15 @@ async function listAgents(root) {
|
|
|
35854
36016
|
}
|
|
35855
36017
|
function itemPath(root, kind, name) {
|
|
35856
36018
|
const n = safeName(name);
|
|
35857
|
-
return kind === "subagent" ?
|
|
36019
|
+
return kind === "subagent" ? join15(root, AGENTS_REL, `${n}.md`) : join15(root, SKILLS_REL, n, "SKILL.md");
|
|
35858
36020
|
}
|
|
35859
36021
|
async function readAgent(root, kind, name) {
|
|
35860
36022
|
const content = await readText2(itemPath(root, kind, name));
|
|
35861
36023
|
if (kind !== "subagent") return { content };
|
|
35862
36024
|
const n = safeName(name);
|
|
35863
36025
|
const [short, long] = await Promise.all([
|
|
35864
|
-
readText2(
|
|
35865
|
-
readText2(
|
|
36026
|
+
readText2(join15(root, AGENTS_REL, "short-memory", `${n}.md`)),
|
|
36027
|
+
readText2(join15(root, AGENTS_REL, "long-memory", `${n}.md`))
|
|
35866
36028
|
]);
|
|
35867
36029
|
return { content, memory: { short, long } };
|
|
35868
36030
|
}
|
|
@@ -35872,12 +36034,12 @@ async function writeAgent(root, req) {
|
|
|
35872
36034
|
try {
|
|
35873
36035
|
if (req.action === "delete") {
|
|
35874
36036
|
if (req.kind === "subagent") await rm(itemPath(root, "subagent", n), { force: true });
|
|
35875
|
-
else await rm(
|
|
36037
|
+
else await rm(join15(root, SKILLS_REL, n), { recursive: true, force: true });
|
|
35876
36038
|
return { ok: true };
|
|
35877
36039
|
}
|
|
35878
36040
|
if (req.content === void 0) return { ok: false, error: "content is required" };
|
|
35879
36041
|
const path = itemPath(root, req.kind, n);
|
|
35880
|
-
await mkdir3(
|
|
36042
|
+
await mkdir3(join15(path, ".."), { recursive: true });
|
|
35881
36043
|
await writeFile3(path, req.content, "utf8");
|
|
35882
36044
|
return { ok: true };
|
|
35883
36045
|
} catch (err) {
|
|
@@ -35888,7 +36050,7 @@ async function writeAgent(root, req) {
|
|
|
35888
36050
|
// src/core/packages.ts
|
|
35889
36051
|
import { createHash as createHash4 } from "crypto";
|
|
35890
36052
|
import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4, readdir as readdir4, rm as rm2, stat } from "fs/promises";
|
|
35891
|
-
import { dirname as dirname4, join as
|
|
36053
|
+
import { dirname as dirname4, join as join16, relative } from "path";
|
|
35892
36054
|
var AGENTS_REL2 = ".claude/agents";
|
|
35893
36055
|
var SKILLS_REL2 = ".claude/skills";
|
|
35894
36056
|
var MANIFEST_REL = ".claude/.4pm-packages.json";
|
|
@@ -35912,7 +36074,7 @@ async function listFilesRel(dir2) {
|
|
|
35912
36074
|
return;
|
|
35913
36075
|
}
|
|
35914
36076
|
for (const e of entries2) {
|
|
35915
|
-
const full =
|
|
36077
|
+
const full = join16(d, e.name);
|
|
35916
36078
|
if (e.isDirectory()) await walk2(full);
|
|
35917
36079
|
else if (e.isFile()) out.push(relative(dir2, full));
|
|
35918
36080
|
}
|
|
@@ -35923,16 +36085,16 @@ async function listFilesRel(dir2) {
|
|
|
35923
36085
|
async function readArtifactFiles(root, kind, slug) {
|
|
35924
36086
|
const files = [];
|
|
35925
36087
|
if (kind === "subagent") {
|
|
35926
|
-
const agent = await readMaybe(
|
|
36088
|
+
const agent = await readMaybe(join16(root, AGENTS_REL2, `${slug}.md`));
|
|
35927
36089
|
if (agent) files.push({ path: "agent.md", contentBase64: agent.toString("base64") });
|
|
35928
|
-
const short = await readMaybe(
|
|
36090
|
+
const short = await readMaybe(join16(root, AGENTS_REL2, "short-memory", `${slug}.md`));
|
|
35929
36091
|
if (short) files.push({ path: "short-memory.md", contentBase64: short.toString("base64") });
|
|
35930
|
-
const long = await readMaybe(
|
|
36092
|
+
const long = await readMaybe(join16(root, AGENTS_REL2, "long-memory", `${slug}.md`));
|
|
35931
36093
|
if (long) files.push({ path: "long-memory.md", contentBase64: long.toString("base64") });
|
|
35932
36094
|
} else {
|
|
35933
|
-
const base =
|
|
36095
|
+
const base = join16(root, SKILLS_REL2, slug);
|
|
35934
36096
|
for (const rel of await listFilesRel(base)) {
|
|
35935
|
-
const buf = await readMaybe(
|
|
36097
|
+
const buf = await readMaybe(join16(base, rel));
|
|
35936
36098
|
if (buf) files.push({ path: rel.split("\\").join("/"), contentBase64: buf.toString("base64") });
|
|
35937
36099
|
}
|
|
35938
36100
|
}
|
|
@@ -35950,7 +36112,7 @@ async function artifactSha256(root, kind, slug) {
|
|
|
35950
36112
|
return h.digest("hex");
|
|
35951
36113
|
}
|
|
35952
36114
|
async function artifactExists(root, kind, slug) {
|
|
35953
|
-
const target = kind === "subagent" ?
|
|
36115
|
+
const target = kind === "subagent" ? join16(root, AGENTS_REL2, `${slug}.md`) : join16(root, SKILLS_REL2, slug);
|
|
35954
36116
|
try {
|
|
35955
36117
|
await stat(target);
|
|
35956
36118
|
return true;
|
|
@@ -35959,7 +36121,7 @@ async function artifactExists(root, kind, slug) {
|
|
|
35959
36121
|
}
|
|
35960
36122
|
}
|
|
35961
36123
|
async function readManifest(root) {
|
|
35962
|
-
const buf = await readMaybe(
|
|
36124
|
+
const buf = await readMaybe(join16(root, MANIFEST_REL));
|
|
35963
36125
|
if (!buf) return {};
|
|
35964
36126
|
try {
|
|
35965
36127
|
return JSON.parse(buf.toString("utf8"));
|
|
@@ -35968,7 +36130,7 @@ async function readManifest(root) {
|
|
|
35968
36130
|
}
|
|
35969
36131
|
}
|
|
35970
36132
|
async function writeManifest(root, manifest) {
|
|
35971
|
-
const path =
|
|
36133
|
+
const path = join16(root, MANIFEST_REL);
|
|
35972
36134
|
await mkdir4(dirname4(path), { recursive: true });
|
|
35973
36135
|
await writeFile4(path, JSON.stringify(manifest, null, 2), "utf8");
|
|
35974
36136
|
}
|
|
@@ -35977,24 +36139,24 @@ async function writeArtifactFiles(root, kind, slug, files) {
|
|
|
35977
36139
|
if (kind === "subagent") {
|
|
35978
36140
|
const agent = byPath.get("agent.md");
|
|
35979
36141
|
if (agent) {
|
|
35980
|
-
await mkdir4(
|
|
35981
|
-
await writeFile4(
|
|
36142
|
+
await mkdir4(join16(root, AGENTS_REL2), { recursive: true });
|
|
36143
|
+
await writeFile4(join16(root, AGENTS_REL2, `${slug}.md`), agent);
|
|
35982
36144
|
}
|
|
35983
36145
|
const short = byPath.get("short-memory.md");
|
|
35984
36146
|
if (short) {
|
|
35985
|
-
await mkdir4(
|
|
35986
|
-
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);
|
|
35987
36149
|
}
|
|
35988
36150
|
const long = byPath.get("long-memory.md");
|
|
35989
36151
|
if (long) {
|
|
35990
|
-
await mkdir4(
|
|
35991
|
-
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);
|
|
35992
36154
|
}
|
|
35993
36155
|
} else {
|
|
35994
|
-
const base =
|
|
36156
|
+
const base = join16(root, SKILLS_REL2, slug);
|
|
35995
36157
|
for (const [rel, buf] of byPath) {
|
|
35996
36158
|
if (rel.includes("..") || rel.startsWith("/")) continue;
|
|
35997
|
-
const dest =
|
|
36159
|
+
const dest = join16(base, rel);
|
|
35998
36160
|
await mkdir4(dirname4(dest), { recursive: true });
|
|
35999
36161
|
await writeFile4(dest, buf);
|
|
36000
36162
|
}
|
|
@@ -36060,11 +36222,11 @@ async function removePackage(root, slug) {
|
|
|
36060
36222
|
const entry = manifest[n];
|
|
36061
36223
|
if (!entry) return { ok: false, error: "not installed" };
|
|
36062
36224
|
if (entry.kind === "subagent") {
|
|
36063
|
-
await rm2(
|
|
36064
|
-
await rm2(
|
|
36065
|
-
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 });
|
|
36066
36228
|
} else {
|
|
36067
|
-
await rm2(
|
|
36229
|
+
await rm2(join16(root, SKILLS_REL2, n), { recursive: true, force: true });
|
|
36068
36230
|
}
|
|
36069
36231
|
delete manifest[n];
|
|
36070
36232
|
await writeManifest(root, manifest);
|
|
@@ -36075,7 +36237,7 @@ async function removePackage(root, slug) {
|
|
|
36075
36237
|
}
|
|
36076
36238
|
|
|
36077
36239
|
// src/core/network-probe.ts
|
|
36078
|
-
import { existsSync as
|
|
36240
|
+
import { existsSync as existsSync10, readFileSync as readFileSync14 } from "fs";
|
|
36079
36241
|
import { connect } from "net";
|
|
36080
36242
|
var CANARY_TARGETS = [
|
|
36081
36243
|
{ host: "1.1.1.1", port: 443 },
|
|
@@ -36084,7 +36246,7 @@ var CANARY_TARGETS = [
|
|
|
36084
36246
|
var PROBE_TIMEOUT_MS = 2500;
|
|
36085
36247
|
function detectContainerized() {
|
|
36086
36248
|
try {
|
|
36087
|
-
if (
|
|
36249
|
+
if (existsSync10("/.dockerenv")) return true;
|
|
36088
36250
|
const cgroup = readFileSync14("/proc/1/cgroup", "utf8");
|
|
36089
36251
|
return /docker|containerd|kubepods|libpod/.test(cgroup);
|
|
36090
36252
|
} catch {
|
|
@@ -36152,9 +36314,9 @@ async function probeNetwork() {
|
|
|
36152
36314
|
}
|
|
36153
36315
|
|
|
36154
36316
|
// src/core/git-auth.ts
|
|
36155
|
-
import { writeFileSync as
|
|
36317
|
+
import { writeFileSync as writeFileSync7 } from "fs";
|
|
36156
36318
|
import { homedir as homedir4 } from "os";
|
|
36157
|
-
import { join as
|
|
36319
|
+
import { join as join17 } from "path";
|
|
36158
36320
|
function tokenTarget(method) {
|
|
36159
36321
|
if (method === "gitlab-group-token") {
|
|
36160
36322
|
return { host: process.env.FOURPM_GIT_HOST || "gitlab.com", user: "oauth2" };
|
|
@@ -36172,7 +36334,7 @@ function applyGitAuth(method) {
|
|
|
36172
36334
|
try {
|
|
36173
36335
|
const line = `https://${target.user}:${token}@${target.host}
|
|
36174
36336
|
`;
|
|
36175
|
-
|
|
36337
|
+
writeFileSync7(join17(homedir4(), ".git-credentials"), line, { encoding: "utf8", mode: 384 });
|
|
36176
36338
|
return true;
|
|
36177
36339
|
} catch {
|
|
36178
36340
|
return false;
|
|
@@ -36181,7 +36343,7 @@ function applyGitAuth(method) {
|
|
|
36181
36343
|
|
|
36182
36344
|
// src/core/secrets.ts
|
|
36183
36345
|
import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
36184
|
-
import { join as
|
|
36346
|
+
import { join as join18 } from "path";
|
|
36185
36347
|
var SECURITY_REL = "AI_SECURITY.md";
|
|
36186
36348
|
var PLACEHOLDER_REL = "AI_PLACEHOLDER.md";
|
|
36187
36349
|
var SECRETS_REL = "project.secrets.json";
|
|
@@ -36194,7 +36356,7 @@ async function readText3(path, fallback = "") {
|
|
|
36194
36356
|
}
|
|
36195
36357
|
async function readSecretsObject(root) {
|
|
36196
36358
|
try {
|
|
36197
|
-
const v = JSON.parse(await readFile6(
|
|
36359
|
+
const v = JSON.parse(await readFile6(join18(root, SECRETS_REL), "utf8"));
|
|
36198
36360
|
return v && typeof v === "object" ? v : {};
|
|
36199
36361
|
} catch {
|
|
36200
36362
|
return {};
|
|
@@ -36213,8 +36375,8 @@ function placeholderKeys(text) {
|
|
|
36213
36375
|
}
|
|
36214
36376
|
async function readSecrets(root) {
|
|
36215
36377
|
const [security, placeholder, secrets] = await Promise.all([
|
|
36216
|
-
readText3(
|
|
36217
|
-
readText3(
|
|
36378
|
+
readText3(join18(root, SECURITY_REL)),
|
|
36379
|
+
readText3(join18(root, PLACEHOLDER_REL)),
|
|
36218
36380
|
readSecretsObject(root)
|
|
36219
36381
|
]);
|
|
36220
36382
|
const names = /* @__PURE__ */ new Set([...placeholderKeys(placeholder), ...Object.keys(secrets)]);
|
|
@@ -36227,14 +36389,14 @@ async function readSecrets(root) {
|
|
|
36227
36389
|
async function writeSecrets(root, req) {
|
|
36228
36390
|
try {
|
|
36229
36391
|
if (req.kind === "security") {
|
|
36230
|
-
await writeFile5(
|
|
36392
|
+
await writeFile5(join18(root, SECURITY_REL), req.content, "utf8");
|
|
36231
36393
|
} else if (req.kind === "placeholder") {
|
|
36232
|
-
await writeFile5(
|
|
36394
|
+
await writeFile5(join18(root, PLACEHOLDER_REL), req.content, "utf8");
|
|
36233
36395
|
} else {
|
|
36234
36396
|
const secrets = await readSecretsObject(root);
|
|
36235
36397
|
if (req.kind === "secret") secrets[req.key] = req.value;
|
|
36236
36398
|
else delete secrets[req.key];
|
|
36237
|
-
await writeFile5(
|
|
36399
|
+
await writeFile5(join18(root, SECRETS_REL), JSON.stringify(secrets, null, 2) + "\n", "utf8");
|
|
36238
36400
|
}
|
|
36239
36401
|
return { ok: true };
|
|
36240
36402
|
} catch (err) {
|
|
@@ -36244,13 +36406,13 @@ async function writeSecrets(root, req) {
|
|
|
36244
36406
|
|
|
36245
36407
|
// src/core/agent-tools.ts
|
|
36246
36408
|
import { readFile as readFile7, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
|
|
36247
|
-
import { dirname as dirname5, join as
|
|
36409
|
+
import { dirname as dirname5, join as join19 } from "path";
|
|
36248
36410
|
var SHARED_REL = ".claude/settings.json";
|
|
36249
36411
|
var LOCAL_REL = ".claude/settings.local.json";
|
|
36250
36412
|
var SECRETS_DENY = "Read(./project.secrets.json)";
|
|
36251
36413
|
var MODES = ["default", "acceptEdits", "plan", "bypassPermissions"];
|
|
36252
36414
|
function fileFor(root, scope) {
|
|
36253
|
-
return
|
|
36415
|
+
return join19(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
|
|
36254
36416
|
}
|
|
36255
36417
|
async function readSettingsObject(path) {
|
|
36256
36418
|
try {
|
|
@@ -36287,7 +36449,7 @@ async function writeAgentTools(root, scope, permissions) {
|
|
|
36287
36449
|
|
|
36288
36450
|
// src/core/graph.ts
|
|
36289
36451
|
import { readFile as readFile8, readdir as readdir5 } from "fs/promises";
|
|
36290
|
-
import { join as
|
|
36452
|
+
import { join as join20, relative as relative2, dirname as dirname6, resolve as resolve4, extname, basename as basename2 } from "path";
|
|
36291
36453
|
var MAX_FILES = 2e3;
|
|
36292
36454
|
var MAX_NODES = 3e3;
|
|
36293
36455
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "out", "coverage", ".turbo"]);
|
|
@@ -36307,7 +36469,7 @@ async function walk(root, accept) {
|
|
|
36307
36469
|
if (e.name.startsWith(".") && e.name !== ".claude") {
|
|
36308
36470
|
if (e.isDirectory()) continue;
|
|
36309
36471
|
}
|
|
36310
|
-
const full =
|
|
36472
|
+
const full = join20(dir2, e.name);
|
|
36311
36473
|
if (e.isDirectory()) {
|
|
36312
36474
|
if (!SKIP_DIRS.has(e.name)) stack.push(full);
|
|
36313
36475
|
} else if (accept(full)) {
|
|
@@ -36427,10 +36589,10 @@ async function buildGraph(root, mode) {
|
|
|
36427
36589
|
|
|
36428
36590
|
// src/core/rag.ts
|
|
36429
36591
|
import { readFile as readFile9, writeFile as writeFile7, statfs as statfs2, mkdir as mkdir6 } from "fs/promises";
|
|
36430
|
-
import { existsSync as
|
|
36592
|
+
import { existsSync as existsSync11 } from "fs";
|
|
36431
36593
|
import { execFile as execFile4, spawn as spawn8 } from "child_process";
|
|
36432
36594
|
import { totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
36433
|
-
import { join as
|
|
36595
|
+
import { join as join21 } from "path";
|
|
36434
36596
|
import { promisify as promisify4 } from "util";
|
|
36435
36597
|
var run4 = promisify4(execFile4);
|
|
36436
36598
|
var RAG_REL = ".claude/rag";
|
|
@@ -36451,7 +36613,7 @@ async function detectPython() {
|
|
|
36451
36613
|
}
|
|
36452
36614
|
async function installLog(root) {
|
|
36453
36615
|
try {
|
|
36454
|
-
const text = await readFile9(
|
|
36616
|
+
const text = await readFile9(join21(root, RAG_REL, "install.log"), "utf8");
|
|
36455
36617
|
return text.split("\n").filter((l) => l.length > 0).slice(-200);
|
|
36456
36618
|
} catch {
|
|
36457
36619
|
return [];
|
|
@@ -36513,14 +36675,14 @@ async function ragStatus(root) {
|
|
|
36513
36675
|
};
|
|
36514
36676
|
let modelName = "";
|
|
36515
36677
|
try {
|
|
36516
|
-
modelName = (await readFile9(
|
|
36678
|
+
modelName = (await readFile9(join21(root, RAG_REL, ".installed"), "utf8")).trim();
|
|
36517
36679
|
} catch {
|
|
36518
36680
|
modelName = "";
|
|
36519
36681
|
}
|
|
36520
36682
|
const modelPresent = modelName.length > 0;
|
|
36521
36683
|
return {
|
|
36522
36684
|
installed: fastembed && sqliteVec && modelPresent,
|
|
36523
|
-
installing:
|
|
36685
|
+
installing: existsSync11(join21(root, RAG_REL, ".installing")),
|
|
36524
36686
|
python: { found: py.found, version: py.version },
|
|
36525
36687
|
deps: { pip, fastembed, sqliteVec },
|
|
36526
36688
|
model: { present: modelPresent, name: modelName },
|
|
@@ -36531,9 +36693,9 @@ async function ragStatus(root) {
|
|
|
36531
36693
|
};
|
|
36532
36694
|
}
|
|
36533
36695
|
async function readIndexMeta(root) {
|
|
36534
|
-
const indexing =
|
|
36696
|
+
const indexing = existsSync11(join21(root, RAG_REL, ".indexing"));
|
|
36535
36697
|
try {
|
|
36536
|
-
const m = JSON.parse(await readFile9(
|
|
36698
|
+
const m = JSON.parse(await readFile9(join21(root, RAG_REL, ".index.json"), "utf8"));
|
|
36537
36699
|
return {
|
|
36538
36700
|
present: true,
|
|
36539
36701
|
indexing,
|
|
@@ -36600,14 +36762,14 @@ if __name__ == "__main__":
|
|
|
36600
36762
|
elif cmd == "query": query(sys.argv[2], int(sys.argv[3]) if len(sys.argv) > 3 else 8)
|
|
36601
36763
|
`;
|
|
36602
36764
|
async function ensureEngine(root) {
|
|
36603
|
-
await mkdir6(
|
|
36604
|
-
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");
|
|
36605
36767
|
}
|
|
36606
36768
|
async function ragReindex(root) {
|
|
36607
36769
|
const py = await detectPython();
|
|
36608
36770
|
if (!py.found) return { ok: false, started: false, error: "python3 not found" };
|
|
36609
|
-
if (!
|
|
36610
|
-
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 };
|
|
36611
36773
|
await ensureEngine(root);
|
|
36612
36774
|
const script = `
|
|
36613
36775
|
set +e
|
|
@@ -36627,12 +36789,12 @@ rm -f "${RAG_REL}/.indexing"
|
|
|
36627
36789
|
async function ragQuery(root, queryText, k = 8) {
|
|
36628
36790
|
const py = await detectPython();
|
|
36629
36791
|
if (!py.found) return { results: [], error: "python3 not found" };
|
|
36630
|
-
if (!
|
|
36792
|
+
if (!existsSync11(join21(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
|
|
36631
36793
|
await ensureEngine(root);
|
|
36632
36794
|
try {
|
|
36633
36795
|
const { stdout } = await run4(
|
|
36634
36796
|
py.bin,
|
|
36635
|
-
[
|
|
36797
|
+
[join21(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k, 1), 20))],
|
|
36636
36798
|
{ cwd: root, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
36637
36799
|
);
|
|
36638
36800
|
const parsed = JSON.parse(stdout.trim() || '{"results":[]}');
|
|
@@ -36647,9 +36809,9 @@ async function ragInstall(root, model) {
|
|
|
36647
36809
|
}
|
|
36648
36810
|
const py = await detectPython();
|
|
36649
36811
|
if (!py.found) return { ok: false, started: false, error: "python3 not found on the worker" };
|
|
36650
|
-
if (
|
|
36812
|
+
if (existsSync11(join21(root, RAG_REL, ".installing"))) return { ok: true, started: true };
|
|
36651
36813
|
try {
|
|
36652
|
-
await mkdir6(
|
|
36814
|
+
await mkdir6(join21(root, RAG_REL), { recursive: true });
|
|
36653
36815
|
} catch {
|
|
36654
36816
|
}
|
|
36655
36817
|
const script = `
|
|
@@ -36737,12 +36899,12 @@ ${stderr}`;
|
|
|
36737
36899
|
// src/core/git-ssh-key.ts
|
|
36738
36900
|
import { execFile as execFile7 } from "child_process";
|
|
36739
36901
|
import { readFile as readFile11, rm as rm3, mkdir as mkdir7 } from "fs/promises";
|
|
36740
|
-
import { join as
|
|
36902
|
+
import { join as join22 } from "path";
|
|
36741
36903
|
import { promisify as promisify7 } from "util";
|
|
36742
36904
|
var run7 = promisify7(execFile7);
|
|
36743
36905
|
function keyPaths(profileDir2) {
|
|
36744
|
-
const dir2 =
|
|
36745
|
-
const key =
|
|
36906
|
+
const dir2 = join22(profileDir2, ".ssh");
|
|
36907
|
+
const key = join22(dir2, "id_4pm");
|
|
36746
36908
|
return { dir: dir2, key, pub: `${key}.pub` };
|
|
36747
36909
|
}
|
|
36748
36910
|
async function readPublicKey(pub) {
|
|
@@ -36934,7 +37096,7 @@ async function setCommitAuthor(cwd2, machineUsername) {
|
|
|
36934
37096
|
// src/core/scaffold.ts
|
|
36935
37097
|
import { cp, mkdir as mkdir8, writeFile as writeFile8 } from "fs/promises";
|
|
36936
37098
|
import { execFile as execFile10 } from "child_process";
|
|
36937
|
-
import { dirname as dirname8, join as
|
|
37099
|
+
import { dirname as dirname8, join as join23, resolve as resolve7 } from "path";
|
|
36938
37100
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
36939
37101
|
import { promisify as promisify10 } from "util";
|
|
36940
37102
|
var run10 = promisify10(execFile10);
|
|
@@ -36963,7 +37125,7 @@ async function provisionRepos(target, repos, emit, opts = {}) {
|
|
|
36963
37125
|
}
|
|
36964
37126
|
continue;
|
|
36965
37127
|
}
|
|
36966
|
-
const dir2 =
|
|
37128
|
+
const dir2 = join23(target, subDirName(repo));
|
|
36967
37129
|
if (repo.url) {
|
|
36968
37130
|
emit("git", `Cloning sub-repo ${repo.url}\u2026`);
|
|
36969
37131
|
await run10("git", ["clone", repo.url, dir2], { timeout: 12e4 });
|
|
@@ -36983,14 +37145,14 @@ async function scaffoldProject(payload, profileDir2, onProgress) {
|
|
|
36983
37145
|
const emit = (step, message) => onProgress?.({ projectId: payload.projectId, step, message });
|
|
36984
37146
|
try {
|
|
36985
37147
|
const root = process.env.SCAFFOLD_ROOT ? resolve7(process.env.SCAFFOLD_ROOT) : resolve7(profileDir2);
|
|
36986
|
-
const target =
|
|
37148
|
+
const target = join23(root, payload.projectName);
|
|
36987
37149
|
await mkdir8(target, { recursive: true });
|
|
36988
37150
|
emit("copy", "Copying the sample template\u2026");
|
|
36989
37151
|
await cp(sampleDir(), target, { recursive: true, force: false, errorOnExist: false });
|
|
36990
37152
|
if (payload.spec) {
|
|
36991
37153
|
emit("spec", "Writing project.spec.json\u2026");
|
|
36992
37154
|
await writeFile8(
|
|
36993
|
-
|
|
37155
|
+
join23(target, "project.spec.json"),
|
|
36994
37156
|
JSON.stringify(payload.spec, null, 2),
|
|
36995
37157
|
"utf8"
|
|
36996
37158
|
);
|
|
@@ -37008,7 +37170,7 @@ async function aiInit(target, spec, emit) {
|
|
|
37008
37170
|
emit("ai-init", "AI init: subagents, README, CLAUDE.md\u2026");
|
|
37009
37171
|
const subagents = Array.isArray(spec.subagents) ? spec.subagents : [];
|
|
37010
37172
|
if (subagents.length > 0) {
|
|
37011
|
-
await mkdir8(
|
|
37173
|
+
await mkdir8(join23(target, ".claude", "agents"), { recursive: true });
|
|
37012
37174
|
for (const sa of subagents) {
|
|
37013
37175
|
const name = (sa.name || "").trim().replace(/[^\w.-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
37014
37176
|
if (!name) continue;
|
|
@@ -37018,17 +37180,17 @@ name: ${name}
|
|
|
37018
37180
|
|
|
37019
37181
|
${(sa.description || "").trim()}
|
|
37020
37182
|
`;
|
|
37021
|
-
await writeFile8(
|
|
37183
|
+
await writeFile8(join23(target, ".claude", "agents", `${name}.md`), body, "utf8");
|
|
37022
37184
|
}
|
|
37023
37185
|
}
|
|
37024
37186
|
const specJson = JSON.stringify(spec);
|
|
37025
37187
|
await generateFile(
|
|
37026
|
-
|
|
37188
|
+
join23(target, "README.md"),
|
|
37027
37189
|
`Write a concise README.md (Markdown only, no preamble) for this project from its spec JSON:
|
|
37028
37190
|
${specJson}`
|
|
37029
37191
|
);
|
|
37030
37192
|
await generateFile(
|
|
37031
|
-
|
|
37193
|
+
join23(target, "CLAUDE.md"),
|
|
37032
37194
|
`Write a CLAUDE.md (Markdown only, no preamble) with guidance/conventions for AI agents working in this project, derived from its spec JSON:
|
|
37033
37195
|
${specJson}`
|
|
37034
37196
|
);
|
|
@@ -37044,7 +37206,7 @@ async function addProject(payload, profileDir2, onProgress) {
|
|
|
37044
37206
|
const emit = (step, message) => onProgress?.({ projectId: payload.projectId, step, message });
|
|
37045
37207
|
try {
|
|
37046
37208
|
const root = process.env.SCAFFOLD_ROOT ? resolve7(process.env.SCAFFOLD_ROOT) : resolve7(profileDir2);
|
|
37047
|
-
const target =
|
|
37209
|
+
const target = join23(root, payload.projectName);
|
|
37048
37210
|
await mkdir8(target, { recursive: true });
|
|
37049
37211
|
const repos = payload.repos ?? [];
|
|
37050
37212
|
if (repos.length > 0) await provisionRepos(target, repos, emit, { clonePrimary: true });
|
|
@@ -37056,8 +37218,8 @@ async function addProject(payload, profileDir2, onProgress) {
|
|
|
37056
37218
|
}
|
|
37057
37219
|
|
|
37058
37220
|
// src/ui/slash-commands.ts
|
|
37059
|
-
import { existsSync as
|
|
37060
|
-
import { join as
|
|
37221
|
+
import { existsSync as existsSync12 } from "fs";
|
|
37222
|
+
import { join as join24 } from "path";
|
|
37061
37223
|
var CONFIG_KEYS = [
|
|
37062
37224
|
"aiCli",
|
|
37063
37225
|
"claudeHome",
|
|
@@ -37069,7 +37231,7 @@ var CONFIG_KEYS = [
|
|
|
37069
37231
|
"aiEnv"
|
|
37070
37232
|
];
|
|
37071
37233
|
function configPath(profileDir2) {
|
|
37072
|
-
return
|
|
37234
|
+
return join24(profileDir2, "config.json");
|
|
37073
37235
|
}
|
|
37074
37236
|
function parseConfigAssignment(key, valueTokens) {
|
|
37075
37237
|
const value = valueTokens.join(" ").trim();
|
|
@@ -37119,7 +37281,7 @@ function runConfig(ctx) {
|
|
|
37119
37281
|
ctx.print(`\u2714 ${verb} default config: ${path}`);
|
|
37120
37282
|
ctx.print("add claude profiles: /config set claudeHome .claude .claude-1 (tried in order)");
|
|
37121
37283
|
};
|
|
37122
|
-
if (
|
|
37284
|
+
if (existsSync12(path)) {
|
|
37123
37285
|
ctx.confirm(
|
|
37124
37286
|
`config exists: ${path} \u2014 replace it with defaults? (y/N)`,
|
|
37125
37287
|
() => writeDefaults("replaced with")
|
|
@@ -37204,7 +37366,7 @@ function runOutput(ctx) {
|
|
|
37204
37366
|
}
|
|
37205
37367
|
function runLogs(ctx) {
|
|
37206
37368
|
const n = Math.max(1, Math.min(200, Number(ctx.args[0]) || 20));
|
|
37207
|
-
const dir2 =
|
|
37369
|
+
const dir2 = join24(ctx.info.profileDir, "logs");
|
|
37208
37370
|
const lines = readRecentLogLines(dir2, n);
|
|
37209
37371
|
if (lines.length === 0) {
|
|
37210
37372
|
ctx.print("No log lines yet.");
|
|
@@ -37517,8 +37679,8 @@ async function runMemoryCompaction(ai, cwd2, input) {
|
|
|
37517
37679
|
}
|
|
37518
37680
|
|
|
37519
37681
|
// src/core/config-sync.ts
|
|
37520
|
-
import { existsSync as
|
|
37521
|
-
import { join as
|
|
37682
|
+
import { existsSync as existsSync13, readFileSync as readFileSync15 } from "fs";
|
|
37683
|
+
import { join as join25 } from "path";
|
|
37522
37684
|
var SERVER_MANAGED_KEYS = [
|
|
37523
37685
|
"physicPath",
|
|
37524
37686
|
"sessionSwitchPct",
|
|
@@ -37535,8 +37697,8 @@ var SERVER_MANAGED_KEYS = [
|
|
|
37535
37697
|
"projectAiMemoryBudgetChars"
|
|
37536
37698
|
];
|
|
37537
37699
|
function readConfigText(profileDir2) {
|
|
37538
|
-
const path =
|
|
37539
|
-
if (
|
|
37700
|
+
const path = join25(profileDir2, "config.json");
|
|
37701
|
+
if (existsSync13(path)) {
|
|
37540
37702
|
try {
|
|
37541
37703
|
return readFileSync15(path, "utf8");
|
|
37542
37704
|
} catch {
|
|
@@ -37606,8 +37768,9 @@ var WsClient = class {
|
|
|
37606
37768
|
context.credential.serverUrl,
|
|
37607
37769
|
context.profileDir,
|
|
37608
37770
|
context.bus,
|
|
37609
|
-
// After the idle daily tick updates flagged tools, report the fresh snapshot (ADR-0254).
|
|
37610
|
-
|
|
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")
|
|
37611
37774
|
);
|
|
37612
37775
|
context.bus.onLocalSubmit((input) => void this.runLocalCommand(input));
|
|
37613
37776
|
context.bus.onReconnect(() => this.reconnectNow());
|
|
@@ -37727,13 +37890,21 @@ var WsClient = class {
|
|
|
37727
37890
|
/**
|
|
37728
37891
|
* Detect the worker's tools and report the snapshot to the server (ADR-0254, `tools.report`) so the
|
|
37729
37892
|
* 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
|
|
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.
|
|
37731
37897
|
*/
|
|
37732
|
-
async reportWorkerTools() {
|
|
37898
|
+
async reportWorkerTools(trigger = "op", restoreFailed) {
|
|
37733
37899
|
try {
|
|
37734
37900
|
const autoUpdate = readProfileConfig(this.context.profileDir).autoUpdateTools ?? [];
|
|
37735
37901
|
const { catalog, extras } = await detectWorkerTools(autoUpdate);
|
|
37736
|
-
this.send(WsChannels.TOOLS_REPORT, {
|
|
37902
|
+
this.send(WsChannels.TOOLS_REPORT, {
|
|
37903
|
+
catalog,
|
|
37904
|
+
extras,
|
|
37905
|
+
trigger,
|
|
37906
|
+
...restoreFailed !== void 0 ? { restoreFailed } : {}
|
|
37907
|
+
});
|
|
37737
37908
|
} catch (err) {
|
|
37738
37909
|
logger.warn("tools.report.error", { error: String(err) });
|
|
37739
37910
|
}
|
|
@@ -37879,6 +38050,7 @@ var WsClient = class {
|
|
|
37879
38050
|
pruneCommandHistoryByAge(token.cliRetentionDays);
|
|
37880
38051
|
pruneCommandOutputByAge(token.cliRetentionDays);
|
|
37881
38052
|
}
|
|
38053
|
+
if (this.physicRoot) sweepOldAttachments(this.physicRoot);
|
|
37882
38054
|
writeProfileConfig(this.context.profileDir, {
|
|
37883
38055
|
sessionSwitchPct: token.projectTokens?.sessionSwitchPct ?? 0,
|
|
37884
38056
|
perPromptTokenLimit: token.projectTokens?.perPromptTokenLimit ?? 0,
|
|
@@ -38047,8 +38219,8 @@ var WsClient = class {
|
|
|
38047
38219
|
const oldRoot = this.physicFolderPath(sync.oldName);
|
|
38048
38220
|
if (oldRoot) void uninstallCron(oldRoot).catch(() => void 0);
|
|
38049
38221
|
}
|
|
38050
|
-
if (sync.oldName)
|
|
38051
|
-
|
|
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 });
|
|
38052
38224
|
this.physicRoot = this.physicFolderPath(sync.newName);
|
|
38053
38225
|
this.bus.setProject(sync.newName);
|
|
38054
38226
|
this.bus.log(`Physic project folder synced \u2192 ${sync.newName}`);
|
|
@@ -38059,7 +38231,7 @@ var WsClient = class {
|
|
|
38059
38231
|
if (del.name) {
|
|
38060
38232
|
const delRoot = this.physicFolderPath(del.name);
|
|
38061
38233
|
if (delRoot) void uninstallCron(delRoot).catch(() => void 0);
|
|
38062
|
-
|
|
38234
|
+
rmSync5(join26(this.context.profileDir, del.name), { recursive: true, force: true });
|
|
38063
38235
|
this.bus.log(`Physic project folder deleted (project removed) \u2192 ${del.name}`);
|
|
38064
38236
|
}
|
|
38065
38237
|
void manageSshKey("delete", this.context.profileDir).catch(() => void 0);
|
|
@@ -38067,6 +38239,23 @@ var WsClient = class {
|
|
|
38067
38239
|
this.physicRoot = null;
|
|
38068
38240
|
break;
|
|
38069
38241
|
}
|
|
38242
|
+
case WsChannels.PROJECT_TOKENS: {
|
|
38243
|
+
const tokens = payload;
|
|
38244
|
+
writeProfileConfig(this.context.profileDir, {
|
|
38245
|
+
sessionSwitchPct: tokens.sessionSwitchPct ?? 0,
|
|
38246
|
+
perPromptTokenLimit: tokens.perPromptTokenLimit ?? 0,
|
|
38247
|
+
projectAiRunTimeoutSec: tokens.aiRunTimeoutSec ?? 0,
|
|
38248
|
+
projectAutoClearIdleMinutes: tokens.autoClearIdleMinutes ?? 0,
|
|
38249
|
+
projectAiMemoryMode: tokens.memory?.mode ?? "inherit",
|
|
38250
|
+
projectAiMemoryBudgetChars: tokens.memory?.budgetChars ?? 0
|
|
38251
|
+
});
|
|
38252
|
+
this.restrictToFolder = tokens.restrictToFolder === true;
|
|
38253
|
+
logger.info("project.tokens.applied", {
|
|
38254
|
+
aiRunTimeoutSec: tokens.aiRunTimeoutSec ?? 0,
|
|
38255
|
+
autoClearIdleMinutes: tokens.autoClearIdleMinutes ?? 0
|
|
38256
|
+
});
|
|
38257
|
+
break;
|
|
38258
|
+
}
|
|
38070
38259
|
case WsChannels.COMMAND_DISPATCH: {
|
|
38071
38260
|
const dispatch = payload;
|
|
38072
38261
|
if (this.handledCommands.has(dispatch.commandId)) {
|
|
@@ -38083,7 +38272,13 @@ var WsClient = class {
|
|
|
38083
38272
|
if (dispatch.cmd.trimStart().startsWith("/")) {
|
|
38084
38273
|
void this.runWebSlashCommand(dispatch.cmd, dispatch.commandId);
|
|
38085
38274
|
} else {
|
|
38086
|
-
void this.runAiPrompt(
|
|
38275
|
+
void this.runAiPrompt(
|
|
38276
|
+
dispatch.cmd,
|
|
38277
|
+
dispatch.commandId,
|
|
38278
|
+
"server",
|
|
38279
|
+
dispatch.aiOneShot ?? false,
|
|
38280
|
+
dispatch.images
|
|
38281
|
+
);
|
|
38087
38282
|
}
|
|
38088
38283
|
break;
|
|
38089
38284
|
}
|
|
@@ -38385,11 +38580,15 @@ var WsClient = class {
|
|
|
38385
38580
|
const req = payload;
|
|
38386
38581
|
const op = message.channel === WsChannels.TOOLS_INSTALL ? "install" : message.channel === WsChannels.TOOLS_UPDATE ? "update" : "uninstall";
|
|
38387
38582
|
this.send(message.channel, { started: true }, message.id);
|
|
38583
|
+
const opTimeoutMs = resolveInstallTimeoutMs(
|
|
38584
|
+
readProfileConfig(this.context.profileDir).toolInstallTimeoutSec
|
|
38585
|
+
);
|
|
38388
38586
|
void runWorkerToolOp(
|
|
38389
38587
|
op,
|
|
38390
38588
|
req.name,
|
|
38391
38589
|
req.manager,
|
|
38392
|
-
(line) => this.send(WsChannels.TOOLS_PROGRESS, { opId: req.opId, line })
|
|
38590
|
+
(line) => this.send(WsChannels.TOOLS_PROGRESS, { opId: req.opId, line }),
|
|
38591
|
+
opTimeoutMs
|
|
38393
38592
|
).then((res) => {
|
|
38394
38593
|
this.send(WsChannels.TOOLS_DONE, {
|
|
38395
38594
|
opId: req.opId,
|
|
@@ -38403,16 +38602,45 @@ var WsClient = class {
|
|
|
38403
38602
|
}
|
|
38404
38603
|
case WsChannels.TOOLS_RESTORE: {
|
|
38405
38604
|
const req = payload;
|
|
38406
|
-
|
|
38407
|
-
|
|
38408
|
-
(
|
|
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
|
-
)
|
|
38605
|
+
const restoreTrigger = req.trigger === "manual" ? "manual" : "boot";
|
|
38606
|
+
const installTimeoutMs = resolveInstallTimeoutMs(
|
|
38607
|
+
readProfileConfig(this.context.profileDir).toolInstallTimeoutSec
|
|
38415
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
|
+
}
|
|
38416
38644
|
break;
|
|
38417
38645
|
}
|
|
38418
38646
|
case WsChannels.GIT_DIFF:
|
|
@@ -38460,7 +38688,7 @@ var WsClient = class {
|
|
|
38460
38688
|
}
|
|
38461
38689
|
case WsChannels.LOG_READ: {
|
|
38462
38690
|
const limit = Math.min(Math.max(payload.limit ?? 200, 1), 2e3);
|
|
38463
|
-
const lines = readRecentLogLines(
|
|
38691
|
+
const lines = readRecentLogLines(join26(this.context.profileDir, "logs"), limit);
|
|
38464
38692
|
this.send(WsChannels.LOG_READ, { lines }, message.id);
|
|
38465
38693
|
break;
|
|
38466
38694
|
}
|
|
@@ -38539,7 +38767,7 @@ var WsClient = class {
|
|
|
38539
38767
|
* exactly like typing the prompt in the cli (tries profiles, meters real tokens) instead
|
|
38540
38768
|
* of spawning the text as a raw command. Streams output to the transcript AND the server.
|
|
38541
38769
|
*/
|
|
38542
|
-
async runAiPrompt(prompt, commandId, origin, oneShot = false) {
|
|
38770
|
+
async runAiPrompt(prompt, commandId, origin, oneShot = false, images) {
|
|
38543
38771
|
const config = readProfileConfig(this.context.profileDir);
|
|
38544
38772
|
const projectTimeoutSec = config.projectAiRunTimeoutSec ?? 0;
|
|
38545
38773
|
const machineTimeoutSec = config.aiRunTimeoutSec ?? 0;
|
|
@@ -38597,7 +38825,18 @@ var WsClient = class {
|
|
|
38597
38825
|
text: `session ${this.usageSnapshot?.session.utilizationPct ?? 0}% \u2265 ${config.sessionSwitchPct}% \u2014 switching to Claude profile "${labelFromCredentialKey(startCred)}"`
|
|
38598
38826
|
});
|
|
38599
38827
|
}
|
|
38600
|
-
|
|
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;
|
|
38601
38840
|
const hint = unified ? { credential: startCred ?? workingCred } : { dir: startDir ?? workingDir };
|
|
38602
38841
|
const memCfg = resolveMemoryConfig(config);
|
|
38603
38842
|
const firstAttempt = planAiRun(guardedPrompt, config, hint).attempts[0];
|
|
@@ -39181,6 +39420,13 @@ ${guardedPrompt}`;
|
|
|
39181
39420
|
checkQuota(metric, amount = 1) {
|
|
39182
39421
|
return this.request(WsChannels.QUOTA_CHECK, { metric, amount });
|
|
39183
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
|
+
}
|
|
39184
39430
|
/** usage.report batched to the server (ADR-0020); tags each event with the AI profile. */
|
|
39185
39431
|
reportUsage(events, profile) {
|
|
39186
39432
|
const tagged = profile ? events.map((e) => ({ ...e, profile })) : events;
|
|
@@ -39218,8 +39464,8 @@ ${guardedPrompt}`;
|
|
|
39218
39464
|
/** Create the physic folder at an absolute path if missing (best-effort). */
|
|
39219
39465
|
ensurePhysicFolderPath(folder) {
|
|
39220
39466
|
try {
|
|
39221
|
-
if (!
|
|
39222
|
-
|
|
39467
|
+
if (!existsSync14(folder)) {
|
|
39468
|
+
mkdirSync6(folder, { recursive: true });
|
|
39223
39469
|
this.bus.log(`Created physic project folder: ${folder}`);
|
|
39224
39470
|
}
|
|
39225
39471
|
} catch (err) {
|
|
@@ -39233,7 +39479,7 @@ ${guardedPrompt}`;
|
|
|
39233
39479
|
*/
|
|
39234
39480
|
physicFolderPath(projectName) {
|
|
39235
39481
|
const safe = projectName.replace(/[/\\]/g, "_").replace(/\.\./g, "_").trim();
|
|
39236
|
-
return safe ?
|
|
39482
|
+
return safe ? join26(this.context.profileDir, safe) : null;
|
|
39237
39483
|
}
|
|
39238
39484
|
/**
|
|
39239
39485
|
* Report machine.status: version + the state of the physic project being served
|
|
@@ -39242,14 +39488,19 @@ ${guardedPrompt}`;
|
|
|
39242
39488
|
sendMachineStatus() {
|
|
39243
39489
|
const config = readProfileConfig(this.context.profileDir);
|
|
39244
39490
|
const physicPath = config.physicPath ?? null;
|
|
39491
|
+
const aiProfileCount = Array.isArray(config.aiProfiles) ? config.aiProfiles.length : (config.claudeHome?.length ?? 0) + (config.codexHome?.length ?? 0) + (config.antigravityHome?.length ?? 0);
|
|
39245
39492
|
this.send(WsChannels.MACHINE_STATUS, {
|
|
39246
39493
|
status: "online",
|
|
39247
39494
|
projects: [],
|
|
39248
39495
|
version: CLI_VERSION,
|
|
39249
39496
|
physicPath,
|
|
39250
|
-
physicPathExists: physicPath ?
|
|
39497
|
+
physicPathExists: physicPath ? existsSync14(physicPath) : void 0,
|
|
39251
39498
|
// Real coarse state (ADR-0152): !paused and a recent tick (was hard-coded false).
|
|
39252
|
-
autonomousRunning: physicPath ? isAutonomousRunning(physicPath) : false
|
|
39499
|
+
autonomousRunning: physicPath ? isAutonomousRunning(physicPath) : false,
|
|
39500
|
+
// The machine-user AI-run limit (0 = unlimited) + profile count so a dispatch can resolve the
|
|
39501
|
+
// effective timeout and derive the SSE reply windows server-side (ADR-0256).
|
|
39502
|
+
aiRunTimeoutSec: config.aiRunTimeoutSec ?? 0,
|
|
39503
|
+
aiProfileCount
|
|
39253
39504
|
});
|
|
39254
39505
|
}
|
|
39255
39506
|
/** Heartbeat every 30s: ping + periodically re-report machine.status. */
|
|
@@ -39337,7 +39588,7 @@ ${guardedPrompt}`;
|
|
|
39337
39588
|
*/
|
|
39338
39589
|
uploadLog() {
|
|
39339
39590
|
try {
|
|
39340
|
-
const upload = readLogUpload(
|
|
39591
|
+
const upload = readLogUpload(join26(this.context.profileDir, "logs"));
|
|
39341
39592
|
if (!upload) return;
|
|
39342
39593
|
const sig = `${upload.fileName}:${upload.totalBytes}`;
|
|
39343
39594
|
if (sig === this.lastLogSig) return;
|
|
@@ -39365,10 +39616,10 @@ ${guardedPrompt}`;
|
|
|
39365
39616
|
};
|
|
39366
39617
|
|
|
39367
39618
|
// src/core/instance-lock.ts
|
|
39368
|
-
import { readFileSync as readFileSync16, rmSync as
|
|
39369
|
-
import { join as
|
|
39619
|
+
import { readFileSync as readFileSync16, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
|
|
39620
|
+
import { join as join27 } from "path";
|
|
39370
39621
|
function lockPath(profileDir2) {
|
|
39371
|
-
return
|
|
39622
|
+
return join27(profileDir2, "start.lock");
|
|
39372
39623
|
}
|
|
39373
39624
|
function isAlive(pid) {
|
|
39374
39625
|
try {
|
|
@@ -39388,13 +39639,13 @@ function lockHolder(profileDir2) {
|
|
|
39388
39639
|
}
|
|
39389
39640
|
function acquireInstanceLock(profileDir2) {
|
|
39390
39641
|
if (lockHolder(profileDir2) !== null) return false;
|
|
39391
|
-
|
|
39642
|
+
writeFileSync8(lockPath(profileDir2), String(process.pid), "utf8");
|
|
39392
39643
|
return true;
|
|
39393
39644
|
}
|
|
39394
39645
|
function releaseInstanceLock(profileDir2) {
|
|
39395
39646
|
try {
|
|
39396
39647
|
const pid = Number.parseInt(readFileSync16(lockPath(profileDir2), "utf8").trim(), 10);
|
|
39397
|
-
if (pid === process.pid)
|
|
39648
|
+
if (pid === process.pid) rmSync6(lockPath(profileDir2), { force: true });
|
|
39398
39649
|
} catch {
|
|
39399
39650
|
}
|
|
39400
39651
|
}
|
|
@@ -39667,8 +39918,8 @@ var SessionBus = class {
|
|
|
39667
39918
|
|
|
39668
39919
|
// src/core/control-server.ts
|
|
39669
39920
|
import { createServer } from "net";
|
|
39670
|
-
import { rmSync as
|
|
39671
|
-
import { join as
|
|
39921
|
+
import { rmSync as rmSync7 } from "fs";
|
|
39922
|
+
import { join as join28 } from "path";
|
|
39672
39923
|
|
|
39673
39924
|
// src/core/control-protocol.ts
|
|
39674
39925
|
var CONTROL_SOCKET_FILE = "control.sock";
|
|
@@ -39697,9 +39948,9 @@ function createFrameParser() {
|
|
|
39697
39948
|
|
|
39698
39949
|
// src/core/control-server.ts
|
|
39699
39950
|
function startControlServer(bus, profileDir2, info) {
|
|
39700
|
-
const socketPath =
|
|
39951
|
+
const socketPath = join28(profileDir2, CONTROL_SOCKET_FILE);
|
|
39701
39952
|
try {
|
|
39702
|
-
|
|
39953
|
+
rmSync7(socketPath, { force: true });
|
|
39703
39954
|
} catch {
|
|
39704
39955
|
}
|
|
39705
39956
|
const clients = /* @__PURE__ */ new Set();
|
|
@@ -39765,7 +40016,7 @@ function startControlServer(bus, profileDir2, info) {
|
|
|
39765
40016
|
clients.clear();
|
|
39766
40017
|
server.close();
|
|
39767
40018
|
try {
|
|
39768
|
-
|
|
40019
|
+
rmSync7(socketPath, { force: true });
|
|
39769
40020
|
} catch {
|
|
39770
40021
|
}
|
|
39771
40022
|
};
|
|
@@ -45428,15 +45679,15 @@ var import_react21 = __toESM(require_react(), 1);
|
|
|
45428
45679
|
var import_react26 = __toESM(require_react(), 1);
|
|
45429
45680
|
|
|
45430
45681
|
// src/core/input-history.ts
|
|
45431
|
-
import { existsSync as
|
|
45432
|
-
import { join as
|
|
45682
|
+
import { existsSync as existsSync16, readFileSync as readFileSync18, writeFileSync as writeFileSync9 } from "fs";
|
|
45683
|
+
import { join as join29 } from "path";
|
|
45433
45684
|
var MAX_HISTORY = 200;
|
|
45434
45685
|
function historyPath(profileDir2) {
|
|
45435
|
-
return
|
|
45686
|
+
return join29(profileDir2, "input-history.json");
|
|
45436
45687
|
}
|
|
45437
45688
|
function loadInputHistory(profileDir2) {
|
|
45438
45689
|
const path = historyPath(profileDir2);
|
|
45439
|
-
if (!
|
|
45690
|
+
if (!existsSync16(path)) return [];
|
|
45440
45691
|
try {
|
|
45441
45692
|
const data = JSON.parse(readFileSync18(path, "utf8"));
|
|
45442
45693
|
return Array.isArray(data) ? data.filter((x) => typeof x === "string") : [];
|
|
@@ -45449,7 +45700,7 @@ function appendInputHistory(profileDir2, line, current) {
|
|
|
45449
45700
|
if (!trimmed || current[current.length - 1] === trimmed) return current;
|
|
45450
45701
|
const next = [...current, trimmed].slice(-MAX_HISTORY);
|
|
45451
45702
|
try {
|
|
45452
|
-
|
|
45703
|
+
writeFileSync9(historyPath(profileDir2), JSON.stringify(next), "utf8");
|
|
45453
45704
|
} catch {
|
|
45454
45705
|
}
|
|
45455
45706
|
return next;
|
|
@@ -46053,7 +46304,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
46053
46304
|
return;
|
|
46054
46305
|
}
|
|
46055
46306
|
logger.configure({
|
|
46056
|
-
dir:
|
|
46307
|
+
dir: join30(profileDir2, "logs"),
|
|
46057
46308
|
level: resolveLogLevel(),
|
|
46058
46309
|
base: { version: CLI_VERSION, profile: profileName, scope: credential.scope }
|
|
46059
46310
|
});
|
|
@@ -46163,7 +46414,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
46163
46414
|
}
|
|
46164
46415
|
|
|
46165
46416
|
// src/commands/attach.ts
|
|
46166
|
-
import { join as
|
|
46417
|
+
import { join as join31 } from "path";
|
|
46167
46418
|
|
|
46168
46419
|
// src/core/control-client.ts
|
|
46169
46420
|
import { connect as connect2 } from "net";
|
|
@@ -46262,7 +46513,7 @@ async function runAttach(profileDir2, profileName) {
|
|
|
46262
46513
|
process.exitCode = 1;
|
|
46263
46514
|
return;
|
|
46264
46515
|
}
|
|
46265
|
-
const socketPath =
|
|
46516
|
+
const socketPath = join31(profileDir2, CONTROL_SOCKET_FILE);
|
|
46266
46517
|
const bus = new SessionBus();
|
|
46267
46518
|
let conn;
|
|
46268
46519
|
try {
|
|
@@ -46295,7 +46546,7 @@ async function runAttach(profileDir2, profileName) {
|
|
|
46295
46546
|
|
|
46296
46547
|
// src/commands/ai-login.ts
|
|
46297
46548
|
import { spawn as spawn11 } from "child_process";
|
|
46298
|
-
import { mkdirSync as
|
|
46549
|
+
import { mkdirSync as mkdirSync7 } from "fs";
|
|
46299
46550
|
import { basename as basename5 } from "path";
|
|
46300
46551
|
function runProviderLogin(cmd, envVar, dir2) {
|
|
46301
46552
|
return new Promise((resolve8) => {
|
|
@@ -46333,7 +46584,7 @@ async function runAiLogin(profileDir2, profileName, aiFilter) {
|
|
|
46333
46584
|
continue;
|
|
46334
46585
|
}
|
|
46335
46586
|
const dir2 = resolveHomePath(cred.profile);
|
|
46336
|
-
|
|
46587
|
+
mkdirSync7(dir2, { recursive: true });
|
|
46337
46588
|
console.log(`
|
|
46338
46589
|
\u25B6 Logging in ${cred.provider} \xB7 ${cred.profile} (${envVar}=${dir2})`);
|
|
46339
46590
|
console.log(` Complete the provider's own sign-in (claude: type \`/login\`; codex: follow its prompt), then exit.`);
|
|
@@ -46347,7 +46598,7 @@ Tip: for the credentials to survive \`docker rm\`/recreate, keep each profile pa
|
|
|
46347
46598
|
}
|
|
46348
46599
|
|
|
46349
46600
|
// src/commands/unlink.ts
|
|
46350
|
-
import { rmSync as
|
|
46601
|
+
import { rmSync as rmSync8 } from "fs";
|
|
46351
46602
|
async function runUnlink(profileDir2, profileName) {
|
|
46352
46603
|
const cred = readCredential(profileDir2, profileName);
|
|
46353
46604
|
if (!cred) {
|
|
@@ -46362,7 +46613,7 @@ async function runUnlink(profileDir2, profileName) {
|
|
|
46362
46613
|
`\u26A0 Could not reach the server to revoke \u2014 revoke it at web /machines. (${err.message})`
|
|
46363
46614
|
);
|
|
46364
46615
|
}
|
|
46365
|
-
|
|
46616
|
+
rmSync8(profileDir2, { recursive: true, force: true });
|
|
46366
46617
|
clearDefaultProfileIf(profileName);
|
|
46367
46618
|
console.log("\u2714 Deleted the profile and its physic project folder.");
|
|
46368
46619
|
}
|