@4pm/cli 1.18.0 → 1.19.0
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 +578 -244
- package/dist/project-sample/.claude/AUTONOMOUS-CRON.md +14 -22
- package/dist/project-sample/.claude/AUTONOMOUS.md +31 -36
- package/dist/project-sample/.claude/hooks/autonomous-tick.sh +8 -232
- package/dist/project-sample/.claude/templates/AI_TODO.empty.md +2 -2
- package/dist/project-sample/.claude/templates/README.md +1 -1
- package/dist/project-sample/AI_TODO.md +2 -2
- package/package.json +1 -1
- package/dist/project-sample/.claude/.autonomous.settings.json +0 -22
- package/dist/project-sample/.claude/commands/auto-cycle.md +0 -23
- package/dist/project-sample/.claude/hooks/__pycache__/autonomous-history.cpython-312.pyc +0 -0
- package/dist/project-sample/.claude/hooks/autonomous-history.py +0 -193
- package/dist/project-sample/.claude/skills/check-usage/SKILL.md +0 -40
- package/dist/project-sample/.claude/skills/check-usage/check_usage.py +0 -161
package/dist/index.js
CHANGED
|
@@ -2283,7 +2283,7 @@ var require_websocket = __commonJS({
|
|
|
2283
2283
|
var http = __require("http");
|
|
2284
2284
|
var net = __require("net");
|
|
2285
2285
|
var tls = __require("tls");
|
|
2286
|
-
var { randomBytes:
|
|
2286
|
+
var { randomBytes: randomBytes4, createHash: createHash5 } = __require("crypto");
|
|
2287
2287
|
var { Duplex, Readable } = __require("stream");
|
|
2288
2288
|
var { URL: URL2 } = __require("url");
|
|
2289
2289
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -2821,7 +2821,7 @@ var require_websocket = __commonJS({
|
|
|
2821
2821
|
}
|
|
2822
2822
|
}
|
|
2823
2823
|
const defaultPort = isSecure ? 443 : 80;
|
|
2824
|
-
const key =
|
|
2824
|
+
const key = randomBytes4(16).toString("base64");
|
|
2825
2825
|
const request = isSecure ? https.request : http.request;
|
|
2826
2826
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
2827
2827
|
let perMessageDeflate;
|
|
@@ -28199,7 +28199,7 @@ import { createInterface } from "readline/promises";
|
|
|
28199
28199
|
|
|
28200
28200
|
// src/core/credential.ts
|
|
28201
28201
|
import {
|
|
28202
|
-
chmodSync,
|
|
28202
|
+
chmodSync as chmodSync2,
|
|
28203
28203
|
existsSync as existsSync2,
|
|
28204
28204
|
readFileSync as readFileSync2,
|
|
28205
28205
|
rmSync as rmSync2,
|
|
@@ -28220,11 +28220,17 @@ import { homedir } from "os";
|
|
|
28220
28220
|
import { join } from "path";
|
|
28221
28221
|
|
|
28222
28222
|
// src/common/io/ensure-dir.ts
|
|
28223
|
-
import { mkdirSync } from "fs";
|
|
28223
|
+
import { chmodSync, mkdirSync } from "fs";
|
|
28224
28224
|
var CLI_IMAGE = "ghcr.io/4azuo/4pm-cli:full";
|
|
28225
|
-
function ensureDir(dir2) {
|
|
28225
|
+
function ensureDir(dir2, mode) {
|
|
28226
28226
|
try {
|
|
28227
|
-
mkdirSync(dir2, { recursive: true });
|
|
28227
|
+
mkdirSync(dir2, { recursive: true, ...mode !== void 0 ? { mode } : {} });
|
|
28228
|
+
if (mode !== void 0) {
|
|
28229
|
+
try {
|
|
28230
|
+
chmodSync(dir2, mode);
|
|
28231
|
+
} catch {
|
|
28232
|
+
}
|
|
28233
|
+
}
|
|
28228
28234
|
} catch (err) {
|
|
28229
28235
|
const code = err.code;
|
|
28230
28236
|
if (code === "EACCES" || code === "EPERM") {
|
|
@@ -28255,7 +28261,7 @@ function defaultProfileName() {
|
|
|
28255
28261
|
return DEFAULT_PROFILE;
|
|
28256
28262
|
}
|
|
28257
28263
|
function writeDefaultProfile(userId) {
|
|
28258
|
-
ensureDir(join(homedir(), ".4pm"));
|
|
28264
|
+
ensureDir(join(homedir(), ".4pm"), 448);
|
|
28259
28265
|
writeFileSync(defaultPointerPath(), userId, "utf8");
|
|
28260
28266
|
}
|
|
28261
28267
|
function clearDefaultProfileIf(name) {
|
|
@@ -28292,7 +28298,7 @@ function listProfiles() {
|
|
|
28292
28298
|
}
|
|
28293
28299
|
function profileDir(name) {
|
|
28294
28300
|
const dir2 = join(homedir(), ".4pm", "profiles", name);
|
|
28295
|
-
ensureDir(dir2);
|
|
28301
|
+
ensureDir(dir2, 448);
|
|
28296
28302
|
return dir2;
|
|
28297
28303
|
}
|
|
28298
28304
|
function defaultProfileConfig() {
|
|
@@ -28417,7 +28423,7 @@ function readCredential(profileDir2, profileName) {
|
|
|
28417
28423
|
function writeCredential(profileDir2, credential) {
|
|
28418
28424
|
const path = credentialPath(profileDir2);
|
|
28419
28425
|
writeFileSync2(path, JSON.stringify(credential, null, 2), "utf8");
|
|
28420
|
-
|
|
28426
|
+
chmodSync2(path, 384);
|
|
28421
28427
|
}
|
|
28422
28428
|
function deleteCredential(profileDir2) {
|
|
28423
28429
|
rmSync2(credentialPath(profileDir2), { force: true });
|
|
@@ -28877,7 +28883,8 @@ var en_default = {
|
|
|
28877
28883
|
"autoRun.done": "Autonomous cycle complete.",
|
|
28878
28884
|
"autoRun.failed": "Autonomous cycle failed to dispatch: {note}",
|
|
28879
28885
|
"autoRun.noDaemon": 'No running daemon for profile "{profile}" \u2014 start it first: 4pm start --profile {profile}',
|
|
28880
|
-
"autoRun.timeout": "Autonomous cycle exceeded the time limit \u2014 aborting."
|
|
28886
|
+
"autoRun.timeout": "Autonomous cycle exceeded the time limit \u2014 aborting.",
|
|
28887
|
+
"autoRun.authFailed": "Control-channel authentication failed \u2014 the daemon rejected the token."
|
|
28881
28888
|
};
|
|
28882
28889
|
|
|
28883
28890
|
// src/i18n/messages/vi.json
|
|
@@ -29059,7 +29066,8 @@ var vi_default = {
|
|
|
29059
29066
|
"autoRun.done": "Chu k\u1EF3 t\u1EF1 \u0111\u1ED9ng \u0111\xE3 ho\xE0n t\u1EA5t.",
|
|
29060
29067
|
"autoRun.failed": "Kh\xF4ng \u0111i\u1EC1u ph\u1ED1i \u0111\u01B0\u1EE3c chu k\u1EF3 t\u1EF1 \u0111\u1ED9ng: {note}",
|
|
29061
29068
|
"autoRun.noDaemon": 'Kh\xF4ng c\xF3 ti\u1EBFn tr\xECnh n\u1EC1n n\xE0o cho h\u1ED3 s\u01A1 "{profile}" \u2014 h\xE3y kh\u1EDFi \u0111\u1ED9ng tr\u01B0\u1EDBc: 4pm start --profile {profile}',
|
|
29062
|
-
"autoRun.timeout": "Chu k\u1EF3 t\u1EF1 \u0111\u1ED9ng v\u01B0\u1EE3t qu\xE1 gi\u1EDBi h\u1EA1n th\u1EDDi gian \u2014 \u0111ang h\u1EE7y."
|
|
29069
|
+
"autoRun.timeout": "Chu k\u1EF3 t\u1EF1 \u0111\u1ED9ng v\u01B0\u1EE3t qu\xE1 gi\u1EDBi h\u1EA1n th\u1EDDi gian \u2014 \u0111ang h\u1EE7y.",
|
|
29070
|
+
"autoRun.authFailed": "X\xE1c th\u1EF1c k\xEAnh \u0111i\u1EC1u khi\u1EC3n th\u1EA5t b\u1EA1i \u2014 daemon t\u1EEB ch\u1ED1i token."
|
|
29063
29071
|
};
|
|
29064
29072
|
|
|
29065
29073
|
// src/i18n/messages/ja.json
|
|
@@ -29241,7 +29249,8 @@ var ja_default = {
|
|
|
29241
29249
|
"autoRun.done": "\u81EA\u5F8B\u30B5\u30A4\u30AF\u30EB\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F\u3002",
|
|
29242
29250
|
"autoRun.failed": "\u81EA\u5F8B\u30B5\u30A4\u30AF\u30EB\u306E\u30C7\u30A3\u30B9\u30D1\u30C3\u30C1\u306B\u5931\u6557\u3057\u307E\u3057\u305F: {note}",
|
|
29243
29251
|
"autoRun.noDaemon": '\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB "{profile}" \u306E\u30C7\u30FC\u30E2\u30F3\u304C\u5B9F\u884C\u3055\u308C\u3066\u3044\u307E\u305B\u3093 \u2014 \u5148\u306B\u8D77\u52D5\u3057\u3066\u304F\u3060\u3055\u3044: 4pm start --profile {profile}',
|
|
29244
|
-
"autoRun.timeout": "\u81EA\u5F8B\u30B5\u30A4\u30AF\u30EB\u304C\u5236\u9650\u6642\u9593\u3092\u8D85\u3048\u307E\u3057\u305F \u2014 \u4E2D\u6B62\u3057\u307E\u3059\u3002"
|
|
29252
|
+
"autoRun.timeout": "\u81EA\u5F8B\u30B5\u30A4\u30AF\u30EB\u304C\u5236\u9650\u6642\u9593\u3092\u8D85\u3048\u307E\u3057\u305F \u2014 \u4E2D\u6B62\u3057\u307E\u3059\u3002",
|
|
29253
|
+
"autoRun.authFailed": "\u30B3\u30F3\u30C8\u30ED\u30FC\u30EB\u30C1\u30E3\u30CD\u30EB\u306E\u8A8D\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F \u2014 \u30C7\u30FC\u30E2\u30F3\u304C\u30C8\u30FC\u30AF\u30F3\u3092\u62D2\u5426\u3057\u307E\u3057\u305F\u3002"
|
|
29245
29254
|
};
|
|
29246
29255
|
|
|
29247
29256
|
// src/i18n/messages/zh.json
|
|
@@ -29423,7 +29432,8 @@ var zh_default = {
|
|
|
29423
29432
|
"autoRun.done": "\u81EA\u4E3B\u5FAA\u73AF\u5DF2\u5B8C\u6210\u3002",
|
|
29424
29433
|
"autoRun.failed": "\u81EA\u4E3B\u5FAA\u73AF\u8C03\u5EA6\u5931\u8D25\uFF1A{note}",
|
|
29425
29434
|
"autoRun.noDaemon": "\u914D\u7F6E\u6587\u4EF6 \u201C{profile}\u201D \u6CA1\u6709\u6B63\u5728\u8FD0\u884C\u7684\u5B88\u62A4\u8FDB\u7A0B \u2014 \u8BF7\u5148\u542F\u52A8\uFF1A4pm start --profile {profile}",
|
|
29426
|
-
"autoRun.timeout": "\u81EA\u4E3B\u5FAA\u73AF\u8D85\u8FC7\u65F6\u95F4\u9650\u5236 \u2014 \u6B63\u5728\u4E2D\u6B62\u3002"
|
|
29435
|
+
"autoRun.timeout": "\u81EA\u4E3B\u5FAA\u73AF\u8D85\u8FC7\u65F6\u95F4\u9650\u5236 \u2014 \u6B63\u5728\u4E2D\u6B62\u3002",
|
|
29436
|
+
"autoRun.authFailed": "\u63A7\u5236\u901A\u9053\u8BA4\u8BC1\u5931\u8D25 \u2014 \u5B88\u62A4\u8FDB\u7A0B\u62D2\u7EDD\u4E86\u4EE4\u724C\u3002"
|
|
29427
29437
|
};
|
|
29428
29438
|
|
|
29429
29439
|
// src/i18n/messages/index.ts
|
|
@@ -29510,7 +29520,7 @@ async function runLink(serverUrl, explicitProfile, token = null) {
|
|
|
29510
29520
|
|
|
29511
29521
|
// src/commands/start.ts
|
|
29512
29522
|
import { spawn as spawn10 } from "child_process";
|
|
29513
|
-
import { join as
|
|
29523
|
+
import { join as join36 } from "path";
|
|
29514
29524
|
|
|
29515
29525
|
// src/core/update.ts
|
|
29516
29526
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -32723,7 +32733,7 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
|
|
|
32723
32733
|
// src/core/ws-client.ts
|
|
32724
32734
|
init_wrapper();
|
|
32725
32735
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
32726
|
-
import { basename as basename7, join as
|
|
32736
|
+
import { basename as basename7, join as join31 } from "path";
|
|
32727
32737
|
import { existsSync as existsSync16, mkdirSync as mkdirSync7 } from "fs";
|
|
32728
32738
|
|
|
32729
32739
|
// vendor/@4pm/utils/helpers.ts
|
|
@@ -39887,14 +39897,122 @@ function reportToolResult(tool, ok, message) {
|
|
|
39887
39897
|
}
|
|
39888
39898
|
|
|
39889
39899
|
// src/core/autonomous.ts
|
|
39890
|
-
import { readFile as
|
|
39891
|
-
import { readFileSync as
|
|
39900
|
+
import { readFile as readFile4, writeFile as writeFile4, chmod } from "fs/promises";
|
|
39901
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
39892
39902
|
import { execFile as execFile4, spawn as spawn5 } from "child_process";
|
|
39893
|
-
import { join as
|
|
39903
|
+
import { join as join17 } from "path";
|
|
39894
39904
|
import { promisify as promisify4 } from "util";
|
|
39895
|
-
|
|
39896
|
-
|
|
39905
|
+
|
|
39906
|
+
// src/core/autonomous-config.ts
|
|
39907
|
+
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
39908
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
39909
|
+
import { join as join15 } from "path";
|
|
39910
|
+
var DEFAULT_AUTONOMOUS_CONFIG = {
|
|
39911
|
+
paused: true,
|
|
39912
|
+
cronSchedule: "*/10 * * * *",
|
|
39913
|
+
quietHours: "",
|
|
39914
|
+
maxTicksPerDay: -1,
|
|
39915
|
+
stopOnConsecutiveFailures: 3,
|
|
39916
|
+
logRetentionDays: 14,
|
|
39917
|
+
model: "",
|
|
39918
|
+
maxSessionPct: 80,
|
|
39919
|
+
maxWeeklyPct: 90
|
|
39920
|
+
};
|
|
39921
|
+
function autonomousConfigPath(profileDir2) {
|
|
39922
|
+
return join15(profileDir2, "autonomous.config.json");
|
|
39923
|
+
}
|
|
39924
|
+
function coerce2(raw) {
|
|
39925
|
+
const o = raw && typeof raw === "object" ? raw : {};
|
|
39926
|
+
const d = DEFAULT_AUTONOMOUS_CONFIG;
|
|
39927
|
+
const num = (v2, def) => typeof v2 === "number" && Number.isFinite(v2) ? v2 : def;
|
|
39928
|
+
const str = (v2, def) => typeof v2 === "string" ? v2 : def;
|
|
39929
|
+
return {
|
|
39930
|
+
paused: typeof o.paused === "boolean" ? o.paused : d.paused,
|
|
39931
|
+
cronSchedule: str(o.cronSchedule, d.cronSchedule),
|
|
39932
|
+
quietHours: str(o.quietHours, d.quietHours),
|
|
39933
|
+
maxTicksPerDay: num(o.maxTicksPerDay, d.maxTicksPerDay),
|
|
39934
|
+
stopOnConsecutiveFailures: num(o.stopOnConsecutiveFailures, d.stopOnConsecutiveFailures),
|
|
39935
|
+
logRetentionDays: num(o.logRetentionDays, d.logRetentionDays),
|
|
39936
|
+
model: str(o.model, d.model),
|
|
39937
|
+
maxSessionPct: num(o.maxSessionPct, d.maxSessionPct),
|
|
39938
|
+
maxWeeklyPct: num(o.maxWeeklyPct, d.maxWeeklyPct)
|
|
39939
|
+
};
|
|
39940
|
+
}
|
|
39941
|
+
async function readAutonomousConfig(profileDir2) {
|
|
39942
|
+
try {
|
|
39943
|
+
return coerce2(JSON.parse(await readFile2(autonomousConfigPath(profileDir2), "utf8")));
|
|
39944
|
+
} catch {
|
|
39945
|
+
return { ...DEFAULT_AUTONOMOUS_CONFIG };
|
|
39946
|
+
}
|
|
39947
|
+
}
|
|
39948
|
+
function readAutonomousConfigSync(profileDir2) {
|
|
39949
|
+
try {
|
|
39950
|
+
return coerce2(JSON.parse(readFileSync13(autonomousConfigPath(profileDir2), "utf8")));
|
|
39951
|
+
} catch {
|
|
39952
|
+
return { ...DEFAULT_AUTONOMOUS_CONFIG };
|
|
39953
|
+
}
|
|
39954
|
+
}
|
|
39955
|
+
async function readAutonomousConfigText(profileDir2) {
|
|
39956
|
+
try {
|
|
39957
|
+
return await readFile2(autonomousConfigPath(profileDir2), "utf8");
|
|
39958
|
+
} catch {
|
|
39959
|
+
return JSON.stringify(DEFAULT_AUTONOMOUS_CONFIG, null, 2) + "\n";
|
|
39960
|
+
}
|
|
39961
|
+
}
|
|
39962
|
+
async function writeAutonomousConfig(profileDir2, raw) {
|
|
39963
|
+
const cfg = coerce2(raw);
|
|
39964
|
+
await writeFile2(autonomousConfigPath(profileDir2), JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
39965
|
+
}
|
|
39966
|
+
function isInQuietHours(quietHours, now = /* @__PURE__ */ new Date()) {
|
|
39967
|
+
const m2 = /^(\d{1,2}):(\d{2})-(\d{1,2}):(\d{2})$/.exec(quietHours.trim());
|
|
39968
|
+
if (!m2) return false;
|
|
39969
|
+
const toMin = (h, mi2) => Number(h) * 60 + Number(mi2);
|
|
39970
|
+
const start = toMin(m2[1], m2[2]);
|
|
39971
|
+
const end = toMin(m2[3], m2[4]);
|
|
39972
|
+
const cur = now.getHours() * 60 + now.getMinutes();
|
|
39973
|
+
return start <= end ? cur >= start && cur < end : cur >= start || cur < end;
|
|
39974
|
+
}
|
|
39975
|
+
|
|
39976
|
+
// src/core/autonomous-history.ts
|
|
39977
|
+
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
39978
|
+
import { join as join16 } from "path";
|
|
39897
39979
|
var HISTORIES_REL = ".claude/.autonomous.histories.json";
|
|
39980
|
+
var MAX_RECORDS = 50;
|
|
39981
|
+
var SKELETON = { cronApplied: "", consecutiveFails: 0, ticks: { day: "", count: 0 }, records: [] };
|
|
39982
|
+
function historiesPath(root) {
|
|
39983
|
+
return join16(root, HISTORIES_REL);
|
|
39984
|
+
}
|
|
39985
|
+
async function readHistories(root) {
|
|
39986
|
+
try {
|
|
39987
|
+
const d = JSON.parse(await readFile3(historiesPath(root), "utf8"));
|
|
39988
|
+
const ticks = d.ticks ?? {};
|
|
39989
|
+
return {
|
|
39990
|
+
cronApplied: typeof d.cronApplied === "string" ? d.cronApplied : typeof d.cron_applied === "string" ? d.cron_applied : "",
|
|
39991
|
+
consecutiveFails: typeof d.consecutiveFails === "number" ? d.consecutiveFails : typeof d.consecutive_fails === "number" ? d.consecutive_fails : 0,
|
|
39992
|
+
ticks: { day: typeof ticks.day === "string" ? ticks.day : "", count: typeof ticks.count === "number" ? ticks.count : 0 },
|
|
39993
|
+
records: Array.isArray(d.records) ? d.records : []
|
|
39994
|
+
};
|
|
39995
|
+
} catch {
|
|
39996
|
+
return { ...SKELETON, ticks: { ...SKELETON.ticks }, records: [] };
|
|
39997
|
+
}
|
|
39998
|
+
}
|
|
39999
|
+
async function writeHistories(root, h) {
|
|
40000
|
+
try {
|
|
40001
|
+
await writeFile3(historiesPath(root), JSON.stringify(h, null, 2) + "\n", "utf8");
|
|
40002
|
+
} catch {
|
|
40003
|
+
}
|
|
40004
|
+
}
|
|
40005
|
+
function todayTickCount(h, today) {
|
|
40006
|
+
return h.ticks.day === today ? h.ticks.count : 0;
|
|
40007
|
+
}
|
|
40008
|
+
function pushRecord(h, rec) {
|
|
40009
|
+
h.records.push(rec);
|
|
40010
|
+
if (h.records.length > MAX_RECORDS) h.records = h.records.slice(-MAX_RECORDS);
|
|
40011
|
+
return h;
|
|
40012
|
+
}
|
|
40013
|
+
|
|
40014
|
+
// src/core/autonomous.ts
|
|
40015
|
+
var run4 = promisify4(execFile4);
|
|
39898
40016
|
var APPROVALS_REL = ".claude/.autonomous.approvals.json";
|
|
39899
40017
|
var AUTHORS_REL = ".claude/.autonomous.authors.json";
|
|
39900
40018
|
var TICK_REL = ".claude/hooks/autonomous-tick.sh";
|
|
@@ -39906,10 +40024,9 @@ var BOOK_FILES = {
|
|
|
39906
40024
|
aiDone: "AI_DONE.md",
|
|
39907
40025
|
userQa: "USER_QA.md"
|
|
39908
40026
|
};
|
|
39909
|
-
var DEFAULT_CRON = "*/10 * * * *";
|
|
39910
40027
|
async function readText(path, fallback = "") {
|
|
39911
40028
|
try {
|
|
39912
|
-
return await
|
|
40029
|
+
return await readFile4(path, "utf8");
|
|
39913
40030
|
} catch {
|
|
39914
40031
|
return fallback;
|
|
39915
40032
|
}
|
|
@@ -39955,7 +40072,7 @@ function tableRowsById(md) {
|
|
|
39955
40072
|
return rows;
|
|
39956
40073
|
}
|
|
39957
40074
|
function tickScript(root) {
|
|
39958
|
-
return
|
|
40075
|
+
return join17(root, TICK_REL);
|
|
39959
40076
|
}
|
|
39960
40077
|
async function crontabList() {
|
|
39961
40078
|
try {
|
|
@@ -39977,28 +40094,28 @@ async function cronInstalled(root) {
|
|
|
39977
40094
|
const script = tickScript(root);
|
|
39978
40095
|
return (await crontabList()).split("\n").some((l) => l.includes(script) && !l.trim().startsWith("#"));
|
|
39979
40096
|
}
|
|
39980
|
-
async function getAutonomousStatus(root) {
|
|
39981
|
-
const
|
|
39982
|
-
|
|
39983
|
-
|
|
39984
|
-
|
|
39985
|
-
|
|
40097
|
+
async function getAutonomousStatus(root, profileDir2) {
|
|
40098
|
+
const [cfg, hist, installed] = await Promise.all([
|
|
40099
|
+
readAutonomousConfig(profileDir2),
|
|
40100
|
+
readHistories(root),
|
|
40101
|
+
cronInstalled(root)
|
|
40102
|
+
]);
|
|
40103
|
+
const last = hist.records[hist.records.length - 1];
|
|
39986
40104
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
39987
40105
|
return {
|
|
39988
|
-
installed
|
|
39989
|
-
paused:
|
|
39990
|
-
cronSchedule:
|
|
39991
|
-
lastTickAt: last
|
|
39992
|
-
lastResult: last
|
|
39993
|
-
consecutiveFails:
|
|
39994
|
-
todayTicks: ticks.day === today
|
|
40106
|
+
installed,
|
|
40107
|
+
paused: cfg.paused,
|
|
40108
|
+
cronSchedule: cfg.cronSchedule,
|
|
40109
|
+
lastTickAt: last?.ts ?? null,
|
|
40110
|
+
lastResult: last?.status ?? null,
|
|
40111
|
+
consecutiveFails: hist.consecutiveFails,
|
|
40112
|
+
todayTicks: hist.ticks.day === today ? hist.ticks.count : 0
|
|
39995
40113
|
};
|
|
39996
40114
|
}
|
|
39997
|
-
function isAutonomousRunning(root) {
|
|
40115
|
+
function isAutonomousRunning(root, profileDir2) {
|
|
39998
40116
|
try {
|
|
39999
|
-
|
|
40000
|
-
|
|
40001
|
-
const h = JSON.parse(readFileSync13(join15(root, HISTORIES_REL), "utf8"));
|
|
40117
|
+
if (readAutonomousConfigSync(profileDir2).paused) return false;
|
|
40118
|
+
const h = JSON.parse(readFileSync14(join17(root, ".claude/.autonomous.histories.json"), "utf8"));
|
|
40002
40119
|
const ts2 = h.records?.[h.records.length - 1]?.ts;
|
|
40003
40120
|
if (!ts2) return false;
|
|
40004
40121
|
const t2 = Date.parse(ts2.replace(" ", "T"));
|
|
@@ -40007,13 +40124,13 @@ function isAutonomousRunning(root) {
|
|
|
40007
40124
|
return false;
|
|
40008
40125
|
}
|
|
40009
40126
|
}
|
|
40010
|
-
async function readAutonomous(root) {
|
|
40127
|
+
async function readAutonomous(root, profileDir2) {
|
|
40011
40128
|
const [settings, approvals, authors, status, ...books] = await Promise.all([
|
|
40012
|
-
|
|
40013
|
-
readText(
|
|
40014
|
-
readText(
|
|
40015
|
-
getAutonomousStatus(root),
|
|
40016
|
-
...Object.values(BOOK_FILES).map((f2) => readText(
|
|
40129
|
+
readAutonomousConfigText(profileDir2),
|
|
40130
|
+
readText(join17(root, APPROVALS_REL), "{}"),
|
|
40131
|
+
readText(join17(root, AUTHORS_REL), "{}"),
|
|
40132
|
+
getAutonomousStatus(root, profileDir2),
|
|
40133
|
+
...Object.values(BOOK_FILES).map((f2) => readText(join17(root, f2)))
|
|
40017
40134
|
]);
|
|
40018
40135
|
const keys = Object.keys(BOOK_FILES);
|
|
40019
40136
|
const bookMap = {};
|
|
@@ -40022,18 +40139,21 @@ async function readAutonomous(root) {
|
|
|
40022
40139
|
}
|
|
40023
40140
|
async function readAutonomousLogs(root, date) {
|
|
40024
40141
|
const day = date && /^\d{4}-\d{2}-\d{2}$/.test(date) ? date : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
40025
|
-
const text = await readText(
|
|
40142
|
+
const text = await readText(join17(root, LOG_DIR_REL, `autonomous-tick-${day}.log`));
|
|
40026
40143
|
const lines = text ? text.split("\n").filter((l) => l.length > 0).slice(-500) : [];
|
|
40027
40144
|
return { date: day, lines };
|
|
40028
40145
|
}
|
|
40029
|
-
async function installCron(root) {
|
|
40146
|
+
async function installCron(root, profileDir2) {
|
|
40030
40147
|
const script = tickScript(root);
|
|
40031
|
-
const
|
|
40148
|
+
const cfg = await readAutonomousConfig(profileDir2);
|
|
40032
40149
|
await chmod(script, 493).catch(() => void 0);
|
|
40033
40150
|
const kept = (await crontabList()).split("\n").filter((l) => l.trim() && !l.includes(script));
|
|
40034
|
-
kept.push(`${
|
|
40151
|
+
kept.push(`${cfg.cronSchedule} ${script}`);
|
|
40035
40152
|
await crontabSet(kept.join("\n"));
|
|
40036
40153
|
}
|
|
40154
|
+
async function syncCron(root, profileDir2) {
|
|
40155
|
+
if (await cronInstalled(root)) await installCron(root, profileDir2);
|
|
40156
|
+
}
|
|
40037
40157
|
async function uninstallCron(root) {
|
|
40038
40158
|
const script = tickScript(root);
|
|
40039
40159
|
const cur = await crontabList();
|
|
@@ -40041,82 +40161,84 @@ async function uninstallCron(root) {
|
|
|
40041
40161
|
const kept = cur.split("\n").filter((l) => l.trim() && !l.includes(script));
|
|
40042
40162
|
await crontabSet(kept.join("\n"));
|
|
40043
40163
|
}
|
|
40044
|
-
async function writeAutonomous(root, req, by) {
|
|
40164
|
+
async function writeAutonomous(root, profileDir2, req, by) {
|
|
40045
40165
|
try {
|
|
40046
40166
|
switch (req.kind) {
|
|
40047
40167
|
case "settings": {
|
|
40048
|
-
|
|
40049
|
-
|
|
40168
|
+
const parsed = parseJson(req.settings);
|
|
40169
|
+
if (!parsed) return { ok: false, error: "settings is not valid JSON" };
|
|
40170
|
+
await writeAutonomousConfig(profileDir2, parsed);
|
|
40171
|
+
await syncCron(root, profileDir2);
|
|
40050
40172
|
break;
|
|
40051
40173
|
}
|
|
40052
40174
|
case "approvals": {
|
|
40053
40175
|
if (req.approved && !req.byIsAdmin) {
|
|
40054
|
-
const authors = await readJsonMap(
|
|
40176
|
+
const authors = await readJsonMap(join17(root, AUTHORS_REL));
|
|
40055
40177
|
if (entryBy(authors, req.taskId) === by) {
|
|
40056
40178
|
return { ok: false, code: "APPROVAL_SELF", failedId: req.taskId, error: "self-approval blocked" };
|
|
40057
40179
|
}
|
|
40058
40180
|
}
|
|
40059
|
-
const map = parseJson(await readText(
|
|
40181
|
+
const map = parseJson(await readText(join17(root, APPROVALS_REL), "{}")) ?? {};
|
|
40060
40182
|
if (req.approved) map[req.taskId] = { approved: true, by, byLabel: req.byLabel ?? by, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
40061
40183
|
else delete map[req.taskId];
|
|
40062
|
-
await
|
|
40184
|
+
await writeFile4(join17(root, APPROVALS_REL), JSON.stringify(map, null, 2) + "\n", "utf8");
|
|
40063
40185
|
break;
|
|
40064
40186
|
}
|
|
40065
40187
|
case "approvalsBatch": {
|
|
40066
40188
|
if (!req.byIsAdmin) {
|
|
40067
|
-
const authors = await readJsonMap(
|
|
40189
|
+
const authors = await readJsonMap(join17(root, AUTHORS_REL));
|
|
40068
40190
|
const selfId = req.approve.find((id) => entryBy(authors, id) === by);
|
|
40069
40191
|
if (selfId) return { ok: false, code: "APPROVAL_SELF", failedId: selfId, error: "self-approval blocked" };
|
|
40070
40192
|
}
|
|
40071
|
-
const map = parseJson(await readText(
|
|
40193
|
+
const map = parseJson(await readText(join17(root, APPROVALS_REL), "{}")) ?? {};
|
|
40072
40194
|
const at2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
40073
40195
|
const byLabel = req.byLabel ?? by;
|
|
40074
40196
|
for (const taskId of req.approve) map[taskId] = { approved: true, by, byLabel, at: at2 };
|
|
40075
40197
|
for (const taskId of req.unapprove) delete map[taskId];
|
|
40076
|
-
await
|
|
40198
|
+
await writeFile4(join17(root, APPROVALS_REL), JSON.stringify(map, null, 2) + "\n", "utf8");
|
|
40077
40199
|
break;
|
|
40078
40200
|
}
|
|
40079
40201
|
case "userTodo": {
|
|
40080
|
-
const cur = await readText(
|
|
40202
|
+
const cur = await readText(join17(root, BOOK_FILES.userTodo));
|
|
40081
40203
|
const ids = [...cur.matchAll(/REQ-(\d{4})-(\d{4})/g)];
|
|
40082
40204
|
const maxGroup = ids.reduce((m2, g2) => Math.max(m2, Number(g2[1])), 0);
|
|
40083
40205
|
const id = `REQ-${String(maxGroup + 1).padStart(4, "0")}-0001`;
|
|
40084
40206
|
const row = `| ${id} | | | ${tableCell(req.content)} |
|
|
40085
40207
|
`;
|
|
40086
|
-
await
|
|
40087
|
-
const authors = await readJsonMap(
|
|
40208
|
+
await writeFile4(join17(root, BOOK_FILES.userTodo), cur.replace(/\s*$/, "\n") + row, "utf8");
|
|
40209
|
+
const authors = await readJsonMap(join17(root, AUTHORS_REL));
|
|
40088
40210
|
authors[id] = { by, byLabel: req.byLabel ?? by, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
40089
|
-
await
|
|
40211
|
+
await writeFile4(join17(root, AUTHORS_REL), JSON.stringify(authors, null, 2) + "\n", "utf8");
|
|
40090
40212
|
break;
|
|
40091
40213
|
}
|
|
40092
40214
|
case "bookSave": {
|
|
40093
40215
|
const file = BOOK_FILES[req.book];
|
|
40094
|
-
const prev = tableRowsById(await readText(
|
|
40216
|
+
const prev = tableRowsById(await readText(join17(root, file)));
|
|
40095
40217
|
const next = tableRowsById(req.content);
|
|
40096
|
-
const authors = await readJsonMap(
|
|
40218
|
+
const authors = await readJsonMap(join17(root, AUTHORS_REL));
|
|
40097
40219
|
const at2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
40098
40220
|
const byLabel = req.byLabel ?? by;
|
|
40099
40221
|
for (const [id, cells] of next) {
|
|
40100
40222
|
if (prev.get(id) !== cells) authors[id] = { by, byLabel, at: at2 };
|
|
40101
40223
|
}
|
|
40102
|
-
await
|
|
40103
|
-
await
|
|
40224
|
+
await writeFile4(join17(root, file), req.content, "utf8");
|
|
40225
|
+
await writeFile4(join17(root, AUTHORS_REL), JSON.stringify(authors, null, 2) + "\n", "utf8");
|
|
40104
40226
|
break;
|
|
40105
40227
|
}
|
|
40106
40228
|
case "cron": {
|
|
40107
|
-
if (req.action === "install") await installCron(root);
|
|
40229
|
+
if (req.action === "install") await installCron(root, profileDir2);
|
|
40108
40230
|
else await uninstallCron(root);
|
|
40109
40231
|
break;
|
|
40110
40232
|
}
|
|
40111
40233
|
}
|
|
40112
|
-
return { ok: true, status: await getAutonomousStatus(root) };
|
|
40234
|
+
return { ok: true, status: await getAutonomousStatus(root, profileDir2) };
|
|
40113
40235
|
} catch (err) {
|
|
40114
40236
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
40115
40237
|
}
|
|
40116
40238
|
}
|
|
40117
40239
|
|
|
40118
40240
|
// src/core/network-probe.ts
|
|
40119
|
-
import { existsSync as existsSync11, readFileSync as
|
|
40241
|
+
import { existsSync as existsSync11, readFileSync as readFileSync15 } from "fs";
|
|
40120
40242
|
import { connect } from "net";
|
|
40121
40243
|
var CANARY_TARGETS = [
|
|
40122
40244
|
{ host: "1.1.1.1", port: 443 },
|
|
@@ -40126,7 +40248,7 @@ var PROBE_TIMEOUT_MS = 2500;
|
|
|
40126
40248
|
function detectContainerized() {
|
|
40127
40249
|
try {
|
|
40128
40250
|
if (existsSync11("/.dockerenv")) return true;
|
|
40129
|
-
const cgroup =
|
|
40251
|
+
const cgroup = readFileSync15("/proc/1/cgroup", "utf8");
|
|
40130
40252
|
return /docker|containerd|kubepods|libpod/.test(cgroup);
|
|
40131
40253
|
} catch {
|
|
40132
40254
|
return false;
|
|
@@ -40160,7 +40282,7 @@ function probeInbound() {
|
|
|
40160
40282
|
const isExposed = (path, loopbackHexPrefixes) => {
|
|
40161
40283
|
let body;
|
|
40162
40284
|
try {
|
|
40163
|
-
body =
|
|
40285
|
+
body = readFileSync15(path, "utf8");
|
|
40164
40286
|
} catch {
|
|
40165
40287
|
return false;
|
|
40166
40288
|
}
|
|
@@ -40195,7 +40317,7 @@ async function probeNetwork() {
|
|
|
40195
40317
|
// src/core/git-auth.ts
|
|
40196
40318
|
import { writeFileSync as writeFileSync8 } from "fs";
|
|
40197
40319
|
import { homedir as homedir4 } from "os";
|
|
40198
|
-
import { join as
|
|
40320
|
+
import { join as join18 } from "path";
|
|
40199
40321
|
function tokenTarget(method) {
|
|
40200
40322
|
if (method === "gitlab-group-token") {
|
|
40201
40323
|
return { host: process.env.FOURPM_GIT_HOST || "gitlab.com", user: "oauth2" };
|
|
@@ -40213,7 +40335,7 @@ function applyGitAuth(method) {
|
|
|
40213
40335
|
try {
|
|
40214
40336
|
const line = `https://${target.user}:${token}@${target.host}
|
|
40215
40337
|
`;
|
|
40216
|
-
writeFileSync8(
|
|
40338
|
+
writeFileSync8(join18(homedir4(), ".git-credentials"), line, { encoding: "utf8", mode: 384 });
|
|
40217
40339
|
return true;
|
|
40218
40340
|
} catch {
|
|
40219
40341
|
return false;
|
|
@@ -40451,7 +40573,7 @@ function formatTimestampInZone(date, timeZone) {
|
|
|
40451
40573
|
|
|
40452
40574
|
// src/ui/slash-commands.ts
|
|
40453
40575
|
import { existsSync as existsSync12 } from "fs";
|
|
40454
|
-
import { join as
|
|
40576
|
+
import { join as join19 } from "path";
|
|
40455
40577
|
var CONFIG_KEYS = [
|
|
40456
40578
|
"aiCli",
|
|
40457
40579
|
"claudeHome",
|
|
@@ -40463,7 +40585,7 @@ var CONFIG_KEYS = [
|
|
|
40463
40585
|
"aiEnv"
|
|
40464
40586
|
];
|
|
40465
40587
|
function configPath(profileDir2) {
|
|
40466
|
-
return
|
|
40588
|
+
return join19(profileDir2, "config.json");
|
|
40467
40589
|
}
|
|
40468
40590
|
function parseConfigAssignment(key, valueTokens) {
|
|
40469
40591
|
const value = valueTokens.join(" ").trim();
|
|
@@ -40598,7 +40720,7 @@ function runOutput(ctx) {
|
|
|
40598
40720
|
}
|
|
40599
40721
|
function runLogs(ctx) {
|
|
40600
40722
|
const n = Math.max(1, Math.min(200, Number(ctx.args[0]) || 20));
|
|
40601
|
-
const dir2 =
|
|
40723
|
+
const dir2 = join19(ctx.info.profileDir, "logs");
|
|
40602
40724
|
const lines = readRecentLogLines(dir2, n);
|
|
40603
40725
|
if (lines.length === 0) {
|
|
40604
40726
|
ctx.print(t("slash.logs.none"));
|
|
@@ -41438,6 +41560,8 @@ function rejectByReview(ctx, commandId, origin, prompt, reasons) {
|
|
|
41438
41560
|
|
|
41439
41561
|
// src/core/autonomous-cycle.ts
|
|
41440
41562
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
41563
|
+
import { appendFile as appendFile2, mkdir as mkdir3, readFile as readFile5, readdir as readdir2, rm as rm2, stat } from "fs/promises";
|
|
41564
|
+
import { join as join20 } from "path";
|
|
41441
41565
|
function buildAutonomousCyclePrompt() {
|
|
41442
41566
|
return `You are running UNATTENDED as the 4PM autonomous cycle (headless, one tick). Do EXACTLY ONE small
|
|
41443
41567
|
work cycle with the steps below, then STOP. Keep each cycle to ONE small task so you never run out of
|
|
@@ -41520,20 +41644,165 @@ non-fast-forward \u21D2 \`git pull --rebase origin "$BASE"\` then push again).
|
|
|
41520
41644
|
|
|
41521
41645
|
## Step 8 \u2014 Stop
|
|
41522
41646
|
Print a one-line summary (task id + PR link or incident). STOP \u2014 the next cron tick runs the next cycle.
|
|
41523
|
-
The
|
|
41647
|
+
The 4PM cli serializes cycles (one at a time); you do NOT manage any run lock.`;
|
|
41524
41648
|
}
|
|
41649
|
+
var running = /* @__PURE__ */ new Set();
|
|
41525
41650
|
async function runAutonomousCycle(ctx) {
|
|
41526
|
-
const
|
|
41651
|
+
const root = ctx.physicRoot;
|
|
41652
|
+
const profileDir2 = ctx.profileDir;
|
|
41653
|
+
if (!root) {
|
|
41654
|
+
ctx.bus.autonomousDone(false, "no served project");
|
|
41655
|
+
return;
|
|
41656
|
+
}
|
|
41657
|
+
if (running.has(root)) {
|
|
41658
|
+
ctx.bus.autonomousDone(false, "a cycle is already running");
|
|
41659
|
+
return;
|
|
41660
|
+
}
|
|
41661
|
+
running.add(root);
|
|
41662
|
+
const log = (line) => dayLog(root, line);
|
|
41527
41663
|
try {
|
|
41528
|
-
|
|
41529
|
-
|
|
41664
|
+
const cfg = await readAutonomousConfig(profileDir2);
|
|
41665
|
+
await pruneLogs(root, cfg.logRetentionDays);
|
|
41666
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
41667
|
+
if (!ctx.isReady) {
|
|
41668
|
+
await log("[skip] cli offline (no WS connection)");
|
|
41669
|
+
ctx.bus.autonomousDone(false, "cli offline");
|
|
41670
|
+
return;
|
|
41671
|
+
}
|
|
41672
|
+
if (cfg.paused) {
|
|
41673
|
+
await log("[skip] paused");
|
|
41674
|
+
ctx.bus.autonomousDone(false, "paused");
|
|
41675
|
+
return;
|
|
41676
|
+
}
|
|
41677
|
+
if (isInQuietHours(cfg.quietHours)) {
|
|
41678
|
+
await log("[skip] within quiet hours");
|
|
41679
|
+
ctx.bus.autonomousDone(false, "quiet hours");
|
|
41680
|
+
return;
|
|
41681
|
+
}
|
|
41682
|
+
let hist = await readHistories(root);
|
|
41683
|
+
if (cfg.maxTicksPerDay > 0 && todayTickCount(hist, today) >= cfg.maxTicksPerDay) {
|
|
41684
|
+
await log(`[skip] reached max ticks/day (${cfg.maxTicksPerDay})`);
|
|
41685
|
+
ctx.bus.autonomousDone(false, "max ticks reached");
|
|
41686
|
+
return;
|
|
41687
|
+
}
|
|
41688
|
+
if (!await hasWork(root)) {
|
|
41689
|
+
await log("[skip] no approved USER_TODO/USER_QA/AI_TODO work, AI_PROGRESS empty");
|
|
41690
|
+
ctx.bus.autonomousDone(false, "no work");
|
|
41691
|
+
return;
|
|
41692
|
+
}
|
|
41693
|
+
if (await allAiProfilesExhausted(ctx, cfg)) {
|
|
41694
|
+
await log(`[skip] all AI profiles over usage limit (\u2265 ${cfg.maxSessionPct}%/${cfg.maxWeeklyPct}%)`);
|
|
41695
|
+
await recordRun(root, hist, "skip", "all AI profiles over usage limit", today);
|
|
41696
|
+
ctx.bus.autonomousDone(false, "usage limit");
|
|
41697
|
+
return;
|
|
41698
|
+
}
|
|
41699
|
+
hist.ticks = { day: today, count: todayTickCount(hist, today) + 1 };
|
|
41700
|
+
await writeHistories(root, hist);
|
|
41701
|
+
await log(`[run] cycle (tick ${hist.ticks.count}/${today})`);
|
|
41702
|
+
const commandId = randomUUID2();
|
|
41703
|
+
const override = cfg.model ? { model: cfg.model } : void 0;
|
|
41704
|
+
try {
|
|
41705
|
+
await runAiPrompt(ctx, buildAutonomousCyclePrompt(), commandId, "local", false, void 0, override, false, true);
|
|
41706
|
+
hist = await readHistories(root);
|
|
41707
|
+
hist.consecutiveFails = 0;
|
|
41708
|
+
await recordRun(root, hist, "success", "cycle complete", today);
|
|
41709
|
+
await log("[done] cycle complete");
|
|
41710
|
+
ctx.bus.autonomousDone(true);
|
|
41711
|
+
} catch (err) {
|
|
41712
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
41713
|
+
hist = await readHistories(root);
|
|
41714
|
+
hist.consecutiveFails += 1;
|
|
41715
|
+
await recordRun(root, hist, "failure", msg, today);
|
|
41716
|
+
await log(`[warn] cycle failed: ${msg}`);
|
|
41717
|
+
if (cfg.stopOnConsecutiveFailures > 0 && hist.consecutiveFails >= cfg.stopOnConsecutiveFailures) {
|
|
41718
|
+
await writeAutonomousConfig(profileDir2, { ...cfg, paused: true });
|
|
41719
|
+
await log(`[stop] ${hist.consecutiveFails} consecutive failures \u2192 paused`);
|
|
41720
|
+
}
|
|
41721
|
+
ctx.bus.autonomousDone(false, msg);
|
|
41722
|
+
}
|
|
41530
41723
|
} catch (err) {
|
|
41531
41724
|
ctx.bus.autonomousDone(false, err instanceof Error ? err.message : String(err));
|
|
41725
|
+
} finally {
|
|
41726
|
+
running.delete(root);
|
|
41532
41727
|
}
|
|
41533
41728
|
}
|
|
41729
|
+
async function dayLog(root, line) {
|
|
41730
|
+
try {
|
|
41731
|
+
const dir2 = join20(root, ".claude", "logs");
|
|
41732
|
+
await mkdir3(dir2, { recursive: true });
|
|
41733
|
+
const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
41734
|
+
const ts2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
41735
|
+
await appendFile2(join20(dir2, `autonomous-tick-${day}.log`), `${ts2} ${line}
|
|
41736
|
+
`, "utf8");
|
|
41737
|
+
} catch {
|
|
41738
|
+
}
|
|
41739
|
+
}
|
|
41740
|
+
async function pruneLogs(root, retentionDays) {
|
|
41741
|
+
if (!(retentionDays > 0)) return;
|
|
41742
|
+
try {
|
|
41743
|
+
const dir2 = join20(root, ".claude", "logs");
|
|
41744
|
+
const cutoff = Date.now() - retentionDays * 864e5;
|
|
41745
|
+
for (const f2 of await readdir2(dir2)) {
|
|
41746
|
+
if (!/^autonomous-tick-.*\.log$/.test(f2)) continue;
|
|
41747
|
+
const st2 = await stat(join20(dir2, f2)).catch(() => null);
|
|
41748
|
+
if (st2 && st2.mtimeMs < cutoff) await rm2(join20(dir2, f2), { force: true }).catch(() => void 0);
|
|
41749
|
+
}
|
|
41750
|
+
} catch {
|
|
41751
|
+
}
|
|
41752
|
+
}
|
|
41753
|
+
async function recordRun(root, hist, status, note, today) {
|
|
41754
|
+
const ts2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
41755
|
+
pushRecord(hist, { ts: ts2, status, note, tick: `${today} #${hist.ticks.count}` });
|
|
41756
|
+
await writeHistories(root, hist);
|
|
41757
|
+
}
|
|
41758
|
+
async function readJsonObject(path) {
|
|
41759
|
+
try {
|
|
41760
|
+
return JSON.parse(await readFile5(path, "utf8"));
|
|
41761
|
+
} catch {
|
|
41762
|
+
return {};
|
|
41763
|
+
}
|
|
41764
|
+
}
|
|
41765
|
+
async function hasWork(root) {
|
|
41766
|
+
const approvals = await readJsonObject(join20(root, ".claude/.autonomous.approvals.json"));
|
|
41767
|
+
const approved = Object.entries(approvals).filter(([, v2]) => v2 && typeof v2 === "object" && v2.approved === true).map(([k2]) => k2);
|
|
41768
|
+
const read = (f2) => readFile5(join20(root, f2), "utf8").catch(() => "");
|
|
41769
|
+
const [userTodo, userQa, aiTodo, aiProgress] = await Promise.all([
|
|
41770
|
+
read("USER_TODO.md"),
|
|
41771
|
+
read("USER_QA.md"),
|
|
41772
|
+
read("AI_TODO.md"),
|
|
41773
|
+
read("AI_PROGRESS.md")
|
|
41774
|
+
]);
|
|
41775
|
+
const anyApproved = (prefix, text) => approved.some((id) => id.startsWith(prefix) && text.includes(id));
|
|
41776
|
+
return anyApproved("REQ-", userTodo) || anyApproved("QA-", userQa) || anyApproved("TSK-", aiTodo) || /TSK-\d{4}-\d{4}/.test(aiProgress);
|
|
41777
|
+
}
|
|
41778
|
+
function hasUsableCodex(config) {
|
|
41779
|
+
const mixed = config.aiProfiles?.some(
|
|
41780
|
+
(c) => c.provider === "codex" && c.enabled !== false && Boolean(c.profile?.trim())
|
|
41781
|
+
);
|
|
41782
|
+
const legacy = (config.codexHome ?? []).some((p2) => p2.enabled !== false && Boolean(p2.profile?.trim()));
|
|
41783
|
+
return Boolean(mixed || legacy);
|
|
41784
|
+
}
|
|
41785
|
+
async function allAiProfilesExhausted(ctx, cfg) {
|
|
41786
|
+
const config = readProfileConfig(ctx.profileDir);
|
|
41787
|
+
const active = activeProvider(config);
|
|
41788
|
+
if (active === "codex" || active === "antigravity") return false;
|
|
41789
|
+
if (active === null && hasUsableCodex(config)) return false;
|
|
41790
|
+
const dirs = claudeHomeDirs(config);
|
|
41791
|
+
if (dirs.length === 0) return false;
|
|
41792
|
+
const usages = await Promise.all(dirs.map((d) => checkClaudeUsage([d]).catch(() => null)));
|
|
41793
|
+
let readAny = false;
|
|
41794
|
+
for (const u2 of usages) {
|
|
41795
|
+
if (!u2) continue;
|
|
41796
|
+
readAny = true;
|
|
41797
|
+
if (u2.session.utilizationPct < cfg.maxSessionPct && u2.weekly.utilizationPct < cfg.maxWeeklyPct) {
|
|
41798
|
+
return false;
|
|
41799
|
+
}
|
|
41800
|
+
}
|
|
41801
|
+
return readAny;
|
|
41802
|
+
}
|
|
41534
41803
|
|
|
41535
41804
|
// src/core/fs-browse.ts
|
|
41536
|
-
import { readdir as
|
|
41805
|
+
import { readdir as readdir3 } from "fs/promises";
|
|
41537
41806
|
import { resolve as resolve2, sep as sep2 } from "path";
|
|
41538
41807
|
async function listDir(path, root) {
|
|
41539
41808
|
if (!root) return { path: "", entries: [] };
|
|
@@ -41541,7 +41810,7 @@ async function listDir(path, root) {
|
|
|
41541
41810
|
const requested = resolve2(base, path && path.trim() ? path : ".");
|
|
41542
41811
|
const target = requested === base || requested.startsWith(base + sep2) ? requested : base;
|
|
41543
41812
|
try {
|
|
41544
|
-
const dirents = await
|
|
41813
|
+
const dirents = await readdir3(target, { withFileTypes: true });
|
|
41545
41814
|
const entries2 = dirents.map((d) => ({
|
|
41546
41815
|
name: d.name,
|
|
41547
41816
|
type: d.isDirectory() ? "dir" : "file"
|
|
@@ -41556,7 +41825,7 @@ async function listDir(path, root) {
|
|
|
41556
41825
|
}
|
|
41557
41826
|
|
|
41558
41827
|
// src/core/fs-read.ts
|
|
41559
|
-
import { readFile as
|
|
41828
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
41560
41829
|
import { resolve as resolve3, sep as sep3 } from "path";
|
|
41561
41830
|
var MAX_BYTES = 256 * 1024;
|
|
41562
41831
|
function resolveInRoot(root, relPath) {
|
|
@@ -41571,7 +41840,7 @@ async function readWorkerFile(root, path) {
|
|
|
41571
41840
|
return { path, content: "", truncated: false };
|
|
41572
41841
|
}
|
|
41573
41842
|
try {
|
|
41574
|
-
const buf = await
|
|
41843
|
+
const buf = await readFile6(target);
|
|
41575
41844
|
const truncated = buf.byteLength > MAX_BYTES;
|
|
41576
41845
|
const content = buf.subarray(0, MAX_BYTES).toString("utf8");
|
|
41577
41846
|
return { path: target, content, truncated };
|
|
@@ -41581,7 +41850,7 @@ async function readWorkerFile(root, path) {
|
|
|
41581
41850
|
}
|
|
41582
41851
|
|
|
41583
41852
|
// src/core/fs-write.ts
|
|
41584
|
-
import { mkdir as
|
|
41853
|
+
import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
|
|
41585
41854
|
import { dirname as dirname4, resolve as resolve4, sep as sep4 } from "path";
|
|
41586
41855
|
var MAX_BYTES2 = 2 * 1024 * 1024;
|
|
41587
41856
|
function resolveInRoot2(root, relPath) {
|
|
@@ -41600,8 +41869,8 @@ async function writeWorkerFile(root, relPath, content) {
|
|
|
41600
41869
|
return { ok: false, path: target, bytes: 0, error: `content too large (max ${MAX_BYTES2} bytes)` };
|
|
41601
41870
|
}
|
|
41602
41871
|
try {
|
|
41603
|
-
await
|
|
41604
|
-
await
|
|
41872
|
+
await mkdir4(dirname4(target), { recursive: true });
|
|
41873
|
+
await writeFile5(target, content, "utf8");
|
|
41605
41874
|
return { ok: true, path: target, bytes };
|
|
41606
41875
|
} catch (err) {
|
|
41607
41876
|
return { ok: false, path: target, bytes: 0, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -41609,7 +41878,7 @@ async function writeWorkerFile(root, relPath, content) {
|
|
|
41609
41878
|
}
|
|
41610
41879
|
|
|
41611
41880
|
// src/core/fs-mutate.ts
|
|
41612
|
-
import { mkdir as
|
|
41881
|
+
import { mkdir as mkdir5, writeFile as writeFile6, rename, rm as rm3, stat as stat2 } from "fs/promises";
|
|
41613
41882
|
import { basename as basename3, dirname as dirname5, resolve as resolve5, sep as sep5 } from "path";
|
|
41614
41883
|
var MAX_BYTES3 = 2 * 1024 * 1024;
|
|
41615
41884
|
function resolveInRoot3(root, rel) {
|
|
@@ -41619,14 +41888,14 @@ function resolveInRoot3(root, rel) {
|
|
|
41619
41888
|
}
|
|
41620
41889
|
async function isDir(p2) {
|
|
41621
41890
|
try {
|
|
41622
|
-
return (await
|
|
41891
|
+
return (await stat2(p2)).isDirectory();
|
|
41623
41892
|
} catch {
|
|
41624
41893
|
return false;
|
|
41625
41894
|
}
|
|
41626
41895
|
}
|
|
41627
41896
|
async function exists(p2) {
|
|
41628
41897
|
try {
|
|
41629
|
-
await
|
|
41898
|
+
await stat2(p2);
|
|
41630
41899
|
return true;
|
|
41631
41900
|
} catch {
|
|
41632
41901
|
return false;
|
|
@@ -41640,7 +41909,7 @@ async function mutateFs(root, req) {
|
|
|
41640
41909
|
case "mkdir": {
|
|
41641
41910
|
const target = resolveInRoot3(root, req.path ?? "");
|
|
41642
41911
|
if (!target || target === resolve5(root)) return escaped(req.path ?? "");
|
|
41643
|
-
await
|
|
41912
|
+
await mkdir5(target, { recursive: true });
|
|
41644
41913
|
return { ok: true, path: target };
|
|
41645
41914
|
}
|
|
41646
41915
|
case "create": {
|
|
@@ -41651,8 +41920,8 @@ async function mutateFs(root, req) {
|
|
|
41651
41920
|
if (Buffer.byteLength(content, "utf8") > MAX_BYTES3) {
|
|
41652
41921
|
return { ok: false, path: target, error: `content too large (max ${MAX_BYTES3} bytes)` };
|
|
41653
41922
|
}
|
|
41654
|
-
await
|
|
41655
|
-
await
|
|
41923
|
+
await mkdir5(dirname5(target), { recursive: true });
|
|
41924
|
+
await writeFile6(target, content, "utf8");
|
|
41656
41925
|
return { ok: true, path: target };
|
|
41657
41926
|
}
|
|
41658
41927
|
case "move": {
|
|
@@ -41663,7 +41932,7 @@ async function mutateFs(root, req) {
|
|
|
41663
41932
|
if (await isDir(to2)) to2 = resolve5(to2, basename3(from));
|
|
41664
41933
|
if (to2 !== resolve5(root) && !to2.startsWith(resolve5(root) + sep5)) return escaped(req.to ?? "");
|
|
41665
41934
|
if (await exists(to2)) return { ok: false, path: to2, error: "destination already exists" };
|
|
41666
|
-
await
|
|
41935
|
+
await mkdir5(dirname5(to2), { recursive: true });
|
|
41667
41936
|
await rename(from, to2);
|
|
41668
41937
|
return { ok: true, path: to2 };
|
|
41669
41938
|
}
|
|
@@ -41671,7 +41940,7 @@ async function mutateFs(root, req) {
|
|
|
41671
41940
|
const target = resolveInRoot3(root, req.path ?? "");
|
|
41672
41941
|
if (!target) return escaped(req.path ?? "");
|
|
41673
41942
|
if (target === resolve5(root)) return { ok: false, path: target, error: "cannot delete the project root" };
|
|
41674
|
-
await
|
|
41943
|
+
await rm3(target, { recursive: true, force: true });
|
|
41675
41944
|
return { ok: true, path: target };
|
|
41676
41945
|
}
|
|
41677
41946
|
default:
|
|
@@ -41683,7 +41952,7 @@ async function mutateFs(root, req) {
|
|
|
41683
41952
|
}
|
|
41684
41953
|
|
|
41685
41954
|
// src/core/fs-transfer.ts
|
|
41686
|
-
import { mkdir as
|
|
41955
|
+
import { mkdir as mkdir6, readFile as readFile7, stat as stat3, writeFile as writeFile7 } from "fs/promises";
|
|
41687
41956
|
import { basename as basename4, dirname as dirname6, extname, resolve as resolve6, sep as sep6 } from "path";
|
|
41688
41957
|
function resolveInRoot4(root, relPath) {
|
|
41689
41958
|
if (!root) return null;
|
|
@@ -41727,8 +41996,8 @@ async function uploadBinaryFile(root, relPath, contentBase64) {
|
|
|
41727
41996
|
return { ok: false, path: target, bytes: 0, error: `file too large (max ${FS_TRANSFER_MAX_BYTES} bytes)` };
|
|
41728
41997
|
}
|
|
41729
41998
|
try {
|
|
41730
|
-
await
|
|
41731
|
-
await
|
|
41999
|
+
await mkdir6(dirname6(target), { recursive: true });
|
|
42000
|
+
await writeFile7(target, buf);
|
|
41732
42001
|
return { ok: true, path: target, bytes: buf.byteLength };
|
|
41733
42002
|
} catch (err) {
|
|
41734
42003
|
return { ok: false, path: target, bytes: 0, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -41738,12 +42007,12 @@ async function downloadBinaryFile(root, relPath) {
|
|
|
41738
42007
|
const target = resolveInRoot4(root, relPath);
|
|
41739
42008
|
if (!target) return { error: "path escapes the project root" };
|
|
41740
42009
|
try {
|
|
41741
|
-
const info = await
|
|
42010
|
+
const info = await stat3(target);
|
|
41742
42011
|
if (!info.isFile()) return { error: "not a file" };
|
|
41743
42012
|
if (info.size > FS_TRANSFER_MAX_BYTES) {
|
|
41744
42013
|
return { error: `file too large (max ${FS_TRANSFER_MAX_BYTES} bytes)` };
|
|
41745
42014
|
}
|
|
41746
|
-
const buf = await
|
|
42015
|
+
const buf = await readFile7(target);
|
|
41747
42016
|
return {
|
|
41748
42017
|
contentBase64: buf.toString("base64"),
|
|
41749
42018
|
contentType: guessMime(target),
|
|
@@ -41802,7 +42071,7 @@ function handleFsChannels(ctx, message, payload) {
|
|
|
41802
42071
|
}
|
|
41803
42072
|
|
|
41804
42073
|
// src/core/git-diff.ts
|
|
41805
|
-
import { readFile as
|
|
42074
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
41806
42075
|
import { execFile as execFile6 } from "child_process";
|
|
41807
42076
|
import { basename as basename5, dirname as dirname7, resolve as resolve7, sep as sep7 } from "path";
|
|
41808
42077
|
import { promisify as promisify6 } from "util";
|
|
@@ -41829,7 +42098,7 @@ async function gitDiff(root, path) {
|
|
|
41829
42098
|
const base = basename5(target);
|
|
41830
42099
|
let newBuf = null;
|
|
41831
42100
|
try {
|
|
41832
|
-
newBuf = await
|
|
42101
|
+
newBuf = await readFile8(target);
|
|
41833
42102
|
} catch {
|
|
41834
42103
|
}
|
|
41835
42104
|
let oldBuf = null;
|
|
@@ -41896,18 +42165,18 @@ ${stderr}`;
|
|
|
41896
42165
|
|
|
41897
42166
|
// src/core/git-ssh-key.ts
|
|
41898
42167
|
import { execFile as execFile8 } from "child_process";
|
|
41899
|
-
import { readFile as
|
|
41900
|
-
import { join as
|
|
42168
|
+
import { readFile as readFile9, rm as rm4, mkdir as mkdir7 } from "fs/promises";
|
|
42169
|
+
import { join as join21 } from "path";
|
|
41901
42170
|
import { promisify as promisify8 } from "util";
|
|
41902
42171
|
var run8 = promisify8(execFile8);
|
|
41903
42172
|
function keyPaths(profileDir2) {
|
|
41904
|
-
const dir2 =
|
|
41905
|
-
const key =
|
|
42173
|
+
const dir2 = join21(profileDir2, ".ssh");
|
|
42174
|
+
const key = join21(dir2, "id_4pm");
|
|
41906
42175
|
return { dir: dir2, key, pub: `${key}.pub` };
|
|
41907
42176
|
}
|
|
41908
42177
|
async function readPublicKey(pub) {
|
|
41909
42178
|
try {
|
|
41910
|
-
return (await
|
|
42179
|
+
return (await readFile9(pub, "utf8")).trim();
|
|
41911
42180
|
} catch {
|
|
41912
42181
|
return null;
|
|
41913
42182
|
}
|
|
@@ -41929,9 +42198,9 @@ async function configureGit(key) {
|
|
|
41929
42198
|
}
|
|
41930
42199
|
async function generate(profileDir2) {
|
|
41931
42200
|
const { dir: dir2, key, pub } = keyPaths(profileDir2);
|
|
41932
|
-
await
|
|
41933
|
-
await
|
|
41934
|
-
await
|
|
42201
|
+
await mkdir7(dir2, { recursive: true });
|
|
42202
|
+
await rm4(key, { force: true });
|
|
42203
|
+
await rm4(pub, { force: true });
|
|
41935
42204
|
await run8("ssh-keygen", ["-t", "ed25519", "-f", key, "-N", "", "-C", "4pm-deploy-key"], {
|
|
41936
42205
|
timeout: 15e3
|
|
41937
42206
|
});
|
|
@@ -41940,8 +42209,8 @@ async function generate(profileDir2) {
|
|
|
41940
42209
|
}
|
|
41941
42210
|
async function remove(profileDir2) {
|
|
41942
42211
|
const { key, pub } = keyPaths(profileDir2);
|
|
41943
|
-
await
|
|
41944
|
-
await
|
|
42212
|
+
await rm4(key, { force: true });
|
|
42213
|
+
await rm4(pub, { force: true });
|
|
41945
42214
|
await run8("git", ["config", "--global", "--unset", "core.sshCommand"], { timeout: 5e3 }).catch(
|
|
41946
42215
|
() => void 0
|
|
41947
42216
|
);
|
|
@@ -42115,7 +42384,7 @@ function handleGitChannels(ctx, message, payload) {
|
|
|
42115
42384
|
}
|
|
42116
42385
|
|
|
42117
42386
|
// src/core/ws-client/handlers/misc.ts
|
|
42118
|
-
import { join as
|
|
42387
|
+
import { join as join22 } from "path";
|
|
42119
42388
|
|
|
42120
42389
|
// src/core/outbound-review.ts
|
|
42121
42390
|
function compilePatterns(sources, fallback) {
|
|
@@ -42197,7 +42466,7 @@ function handleMiscChannels(ctx, message, payload) {
|
|
|
42197
42466
|
}
|
|
42198
42467
|
case WsChannels.LOG_READ: {
|
|
42199
42468
|
const limit = Math.min(Math.max(payload.limit ?? 200, 1), 2e3);
|
|
42200
|
-
const lines = readRecentLogLines(
|
|
42469
|
+
const lines = readRecentLogLines(join22(ctx.profileDir, "logs"), limit);
|
|
42201
42470
|
ctx.send(WsChannels.LOG_READ, { lines }, message.id);
|
|
42202
42471
|
return true;
|
|
42203
42472
|
}
|
|
@@ -42229,14 +42498,14 @@ function handleMiscChannels(ctx, message, payload) {
|
|
|
42229
42498
|
|
|
42230
42499
|
// src/core/ws-client/handlers/project.ts
|
|
42231
42500
|
import { existsSync as existsSync13, mkdirSync as mkdirSync6, renameSync, rmSync as rmSync6 } from "fs";
|
|
42232
|
-
import { join as
|
|
42501
|
+
import { join as join23 } from "path";
|
|
42233
42502
|
function handleProjectChannels(ctx, message, payload) {
|
|
42234
42503
|
switch (message.channel) {
|
|
42235
42504
|
case WsChannels.PHYSIC_SYNC: {
|
|
42236
42505
|
const sync = payload;
|
|
42237
42506
|
const oldName = sync.oldName;
|
|
42238
|
-
const oldPath = oldName ?
|
|
42239
|
-
const newPath =
|
|
42507
|
+
const oldPath = oldName ? join23(ctx.profileDir, oldName) : null;
|
|
42508
|
+
const newPath = join23(ctx.profileDir, sync.newName);
|
|
42240
42509
|
if (oldName) {
|
|
42241
42510
|
const oldRoot = ctx.physicFolderPath(oldName);
|
|
42242
42511
|
if (oldRoot) void uninstallCron(oldRoot).catch(() => void 0);
|
|
@@ -42258,7 +42527,7 @@ function handleProjectChannels(ctx, message, payload) {
|
|
|
42258
42527
|
if (del.name) {
|
|
42259
42528
|
const delRoot = ctx.physicFolderPath(del.name);
|
|
42260
42529
|
if (delRoot) void uninstallCron(delRoot).catch(() => void 0);
|
|
42261
|
-
rmSync6(
|
|
42530
|
+
rmSync6(join23(ctx.profileDir, del.name), { recursive: true, force: true });
|
|
42262
42531
|
ctx.bus.log(t("project.folderDeleted", { name: del.name }));
|
|
42263
42532
|
}
|
|
42264
42533
|
void manageSshKey("delete", ctx.profileDir).catch(() => void 0);
|
|
@@ -42527,8 +42796,8 @@ function handleToolsChannels(ctx, message, payload) {
|
|
|
42527
42796
|
}
|
|
42528
42797
|
|
|
42529
42798
|
// src/core/agents.ts
|
|
42530
|
-
import { readFile as
|
|
42531
|
-
import { join as
|
|
42799
|
+
import { readFile as readFile10, writeFile as writeFile8, mkdir as mkdir8, readdir as readdir4, rm as rm5 } from "fs/promises";
|
|
42800
|
+
import { join as join24 } from "path";
|
|
42532
42801
|
var AGENTS_REL = ".claude/agents";
|
|
42533
42802
|
var SKILLS_REL = ".claude/skills";
|
|
42534
42803
|
var MEMORY_DIRS = /* @__PURE__ */ new Set(["short-memory", "long-memory"]);
|
|
@@ -42537,7 +42806,7 @@ function safeName(name) {
|
|
|
42537
42806
|
}
|
|
42538
42807
|
async function readText2(path, fallback = "") {
|
|
42539
42808
|
try {
|
|
42540
|
-
return await
|
|
42809
|
+
return await readFile10(path, "utf8");
|
|
42541
42810
|
} catch {
|
|
42542
42811
|
return fallback;
|
|
42543
42812
|
}
|
|
@@ -42552,17 +42821,17 @@ async function listAgents(root) {
|
|
|
42552
42821
|
const subagents = [];
|
|
42553
42822
|
const skills = [];
|
|
42554
42823
|
try {
|
|
42555
|
-
for (const e of await
|
|
42824
|
+
for (const e of await readdir4(join24(root, AGENTS_REL), { withFileTypes: true })) {
|
|
42556
42825
|
if (!e.isFile() || !e.name.endsWith(".md") || MEMORY_DIRS.has(e.name)) continue;
|
|
42557
|
-
const fm = frontmatter(await readText2(
|
|
42826
|
+
const fm = frontmatter(await readText2(join24(root, AGENTS_REL, e.name)));
|
|
42558
42827
|
subagents.push({ name: e.name.replace(/\.md$/, ""), description: fm.description });
|
|
42559
42828
|
}
|
|
42560
42829
|
} catch {
|
|
42561
42830
|
}
|
|
42562
42831
|
try {
|
|
42563
|
-
for (const e of await
|
|
42832
|
+
for (const e of await readdir4(join24(root, SKILLS_REL), { withFileTypes: true })) {
|
|
42564
42833
|
if (!e.isDirectory()) continue;
|
|
42565
|
-
const fm = frontmatter(await readText2(
|
|
42834
|
+
const fm = frontmatter(await readText2(join24(root, SKILLS_REL, e.name, "SKILL.md")));
|
|
42566
42835
|
skills.push({ name: e.name, description: fm.description });
|
|
42567
42836
|
}
|
|
42568
42837
|
} catch {
|
|
@@ -42573,15 +42842,15 @@ async function listAgents(root) {
|
|
|
42573
42842
|
}
|
|
42574
42843
|
function itemPath(root, kind, name) {
|
|
42575
42844
|
const n = safeName(name);
|
|
42576
|
-
return kind === "subagent" ?
|
|
42845
|
+
return kind === "subagent" ? join24(root, AGENTS_REL, `${n}.md`) : join24(root, SKILLS_REL, n, "SKILL.md");
|
|
42577
42846
|
}
|
|
42578
42847
|
async function readAgent(root, kind, name) {
|
|
42579
42848
|
const content = await readText2(itemPath(root, kind, name));
|
|
42580
42849
|
if (kind !== "subagent") return { content };
|
|
42581
42850
|
const n = safeName(name);
|
|
42582
42851
|
const [short, long] = await Promise.all([
|
|
42583
|
-
readText2(
|
|
42584
|
-
readText2(
|
|
42852
|
+
readText2(join24(root, AGENTS_REL, "short-memory", `${n}.md`)),
|
|
42853
|
+
readText2(join24(root, AGENTS_REL, "long-memory", `${n}.md`))
|
|
42585
42854
|
]);
|
|
42586
42855
|
return { content, memory: { short, long } };
|
|
42587
42856
|
}
|
|
@@ -42590,14 +42859,14 @@ async function writeAgent(root, req) {
|
|
|
42590
42859
|
if (!n) return { ok: false, error: "invalid name" };
|
|
42591
42860
|
try {
|
|
42592
42861
|
if (req.action === "delete") {
|
|
42593
|
-
if (req.kind === "subagent") await
|
|
42594
|
-
else await
|
|
42862
|
+
if (req.kind === "subagent") await rm5(itemPath(root, "subagent", n), { force: true });
|
|
42863
|
+
else await rm5(join24(root, SKILLS_REL, n), { recursive: true, force: true });
|
|
42595
42864
|
return { ok: true };
|
|
42596
42865
|
}
|
|
42597
42866
|
if (req.content === void 0) return { ok: false, error: "content is required" };
|
|
42598
42867
|
const path = itemPath(root, req.kind, n);
|
|
42599
|
-
await
|
|
42600
|
-
await
|
|
42868
|
+
await mkdir8(join24(path, ".."), { recursive: true });
|
|
42869
|
+
await writeFile8(path, req.content, "utf8");
|
|
42601
42870
|
return { ok: true };
|
|
42602
42871
|
} catch (err) {
|
|
42603
42872
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -42605,21 +42874,21 @@ async function writeAgent(root, req) {
|
|
|
42605
42874
|
}
|
|
42606
42875
|
|
|
42607
42876
|
// src/core/secrets.ts
|
|
42608
|
-
import { readFile as
|
|
42609
|
-
import { join as
|
|
42877
|
+
import { readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
|
|
42878
|
+
import { join as join25 } from "path";
|
|
42610
42879
|
var SECURITY_REL = "AI_SECURITY.md";
|
|
42611
42880
|
var PLACEHOLDER_REL = "AI_PLACEHOLDER.md";
|
|
42612
42881
|
var SECRETS_REL = "project.secrets.json";
|
|
42613
42882
|
async function readText3(path, fallback = "") {
|
|
42614
42883
|
try {
|
|
42615
|
-
return await
|
|
42884
|
+
return await readFile11(path, "utf8");
|
|
42616
42885
|
} catch {
|
|
42617
42886
|
return fallback;
|
|
42618
42887
|
}
|
|
42619
42888
|
}
|
|
42620
42889
|
async function readSecretsObject(root) {
|
|
42621
42890
|
try {
|
|
42622
|
-
const v2 = JSON.parse(await
|
|
42891
|
+
const v2 = JSON.parse(await readFile11(join25(root, SECRETS_REL), "utf8"));
|
|
42623
42892
|
return v2 && typeof v2 === "object" ? v2 : {};
|
|
42624
42893
|
} catch {
|
|
42625
42894
|
return {};
|
|
@@ -42638,8 +42907,8 @@ function placeholderKeys(text) {
|
|
|
42638
42907
|
}
|
|
42639
42908
|
async function readSecrets(root) {
|
|
42640
42909
|
const [security, placeholder, secrets] = await Promise.all([
|
|
42641
|
-
readText3(
|
|
42642
|
-
readText3(
|
|
42910
|
+
readText3(join25(root, SECURITY_REL)),
|
|
42911
|
+
readText3(join25(root, PLACEHOLDER_REL)),
|
|
42643
42912
|
readSecretsObject(root)
|
|
42644
42913
|
]);
|
|
42645
42914
|
const names = /* @__PURE__ */ new Set([...placeholderKeys(placeholder), ...Object.keys(secrets)]);
|
|
@@ -42652,14 +42921,14 @@ async function readSecrets(root) {
|
|
|
42652
42921
|
async function writeSecrets(root, req) {
|
|
42653
42922
|
try {
|
|
42654
42923
|
if (req.kind === "security") {
|
|
42655
|
-
await
|
|
42924
|
+
await writeFile9(join25(root, SECURITY_REL), req.content, "utf8");
|
|
42656
42925
|
} else if (req.kind === "placeholder") {
|
|
42657
|
-
await
|
|
42926
|
+
await writeFile9(join25(root, PLACEHOLDER_REL), req.content, "utf8");
|
|
42658
42927
|
} else {
|
|
42659
42928
|
const secrets = await readSecretsObject(root);
|
|
42660
42929
|
if (req.kind === "secret") secrets[req.key] = req.value;
|
|
42661
42930
|
else delete secrets[req.key];
|
|
42662
|
-
await
|
|
42931
|
+
await writeFile9(join25(root, SECRETS_REL), JSON.stringify(secrets, null, 2) + "\n", "utf8");
|
|
42663
42932
|
}
|
|
42664
42933
|
return { ok: true };
|
|
42665
42934
|
} catch (err) {
|
|
@@ -42668,18 +42937,18 @@ async function writeSecrets(root, req) {
|
|
|
42668
42937
|
}
|
|
42669
42938
|
|
|
42670
42939
|
// src/core/agent-tools.ts
|
|
42671
|
-
import { readFile as
|
|
42672
|
-
import { dirname as dirname8, join as
|
|
42940
|
+
import { readFile as readFile12, writeFile as writeFile10, mkdir as mkdir9 } from "fs/promises";
|
|
42941
|
+
import { dirname as dirname8, join as join26 } from "path";
|
|
42673
42942
|
var SHARED_REL = ".claude/settings.json";
|
|
42674
42943
|
var LOCAL_REL = ".claude/settings.local.json";
|
|
42675
42944
|
var SECRETS_DENY = "Read(./project.secrets.json)";
|
|
42676
42945
|
var MODES = ["default", "acceptEdits", "plan", "bypassPermissions"];
|
|
42677
42946
|
function fileFor(root, scope) {
|
|
42678
|
-
return
|
|
42947
|
+
return join26(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
|
|
42679
42948
|
}
|
|
42680
42949
|
async function readSettingsObject(path) {
|
|
42681
42950
|
try {
|
|
42682
|
-
const v2 = JSON.parse(await
|
|
42951
|
+
const v2 = JSON.parse(await readFile12(path, "utf8"));
|
|
42683
42952
|
return v2 && typeof v2 === "object" ? v2 : {};
|
|
42684
42953
|
} catch {
|
|
42685
42954
|
return {};
|
|
@@ -42702,8 +42971,8 @@ async function writeAgentTools(root, scope, permissions) {
|
|
|
42702
42971
|
const next = normalizePermissions(permissions);
|
|
42703
42972
|
if (scope === "shared" && !next.deny.includes(SECRETS_DENY)) next.deny = [...next.deny, SECRETS_DENY];
|
|
42704
42973
|
obj.permissions = next;
|
|
42705
|
-
await
|
|
42706
|
-
await
|
|
42974
|
+
await mkdir9(dirname8(path), { recursive: true });
|
|
42975
|
+
await writeFile10(path, JSON.stringify(obj, null, 2) + "\n", "utf8");
|
|
42707
42976
|
return { ok: true };
|
|
42708
42977
|
} catch (err) {
|
|
42709
42978
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -42712,8 +42981,8 @@ async function writeAgentTools(root, scope, permissions) {
|
|
|
42712
42981
|
|
|
42713
42982
|
// src/core/packages.ts
|
|
42714
42983
|
import { createHash as createHash4 } from "crypto";
|
|
42715
|
-
import { readFile as
|
|
42716
|
-
import { dirname as dirname9, join as
|
|
42984
|
+
import { readFile as readFile13, writeFile as writeFile11, mkdir as mkdir10, readdir as readdir5, rm as rm6, stat as stat4 } from "fs/promises";
|
|
42985
|
+
import { dirname as dirname9, join as join27, relative } from "path";
|
|
42717
42986
|
var AGENTS_REL2 = ".claude/agents";
|
|
42718
42987
|
var SKILLS_REL2 = ".claude/skills";
|
|
42719
42988
|
var MANIFEST_REL = ".claude/.4pm-packages.json";
|
|
@@ -42722,7 +42991,7 @@ function safeName2(name) {
|
|
|
42722
42991
|
}
|
|
42723
42992
|
async function readMaybe(path) {
|
|
42724
42993
|
try {
|
|
42725
|
-
return await
|
|
42994
|
+
return await readFile13(path);
|
|
42726
42995
|
} catch {
|
|
42727
42996
|
return null;
|
|
42728
42997
|
}
|
|
@@ -42732,12 +43001,12 @@ async function listFilesRel(dir2) {
|
|
|
42732
43001
|
async function walk2(d) {
|
|
42733
43002
|
let entries2;
|
|
42734
43003
|
try {
|
|
42735
|
-
entries2 = await
|
|
43004
|
+
entries2 = await readdir5(d, { withFileTypes: true });
|
|
42736
43005
|
} catch {
|
|
42737
43006
|
return;
|
|
42738
43007
|
}
|
|
42739
43008
|
for (const e of entries2) {
|
|
42740
|
-
const full =
|
|
43009
|
+
const full = join27(d, e.name);
|
|
42741
43010
|
if (e.isDirectory()) await walk2(full);
|
|
42742
43011
|
else if (e.isFile()) out.push(relative(dir2, full));
|
|
42743
43012
|
}
|
|
@@ -42748,16 +43017,16 @@ async function listFilesRel(dir2) {
|
|
|
42748
43017
|
async function readArtifactFiles(root, kind, slug) {
|
|
42749
43018
|
const files = [];
|
|
42750
43019
|
if (kind === "subagent") {
|
|
42751
|
-
const agent = await readMaybe(
|
|
43020
|
+
const agent = await readMaybe(join27(root, AGENTS_REL2, `${slug}.md`));
|
|
42752
43021
|
if (agent) files.push({ path: "agent.md", contentBase64: agent.toString("base64") });
|
|
42753
|
-
const short = await readMaybe(
|
|
43022
|
+
const short = await readMaybe(join27(root, AGENTS_REL2, "short-memory", `${slug}.md`));
|
|
42754
43023
|
if (short) files.push({ path: "short-memory.md", contentBase64: short.toString("base64") });
|
|
42755
|
-
const long = await readMaybe(
|
|
43024
|
+
const long = await readMaybe(join27(root, AGENTS_REL2, "long-memory", `${slug}.md`));
|
|
42756
43025
|
if (long) files.push({ path: "long-memory.md", contentBase64: long.toString("base64") });
|
|
42757
43026
|
} else {
|
|
42758
|
-
const base =
|
|
43027
|
+
const base = join27(root, SKILLS_REL2, slug);
|
|
42759
43028
|
for (const rel of await listFilesRel(base)) {
|
|
42760
|
-
const buf = await readMaybe(
|
|
43029
|
+
const buf = await readMaybe(join27(base, rel));
|
|
42761
43030
|
if (buf) files.push({ path: rel.split("\\").join("/"), contentBase64: buf.toString("base64") });
|
|
42762
43031
|
}
|
|
42763
43032
|
}
|
|
@@ -42775,16 +43044,16 @@ async function artifactSha256(root, kind, slug) {
|
|
|
42775
43044
|
return h.digest("hex");
|
|
42776
43045
|
}
|
|
42777
43046
|
async function artifactExists(root, kind, slug) {
|
|
42778
|
-
const target = kind === "subagent" ?
|
|
43047
|
+
const target = kind === "subagent" ? join27(root, AGENTS_REL2, `${slug}.md`) : join27(root, SKILLS_REL2, slug);
|
|
42779
43048
|
try {
|
|
42780
|
-
await
|
|
43049
|
+
await stat4(target);
|
|
42781
43050
|
return true;
|
|
42782
43051
|
} catch {
|
|
42783
43052
|
return false;
|
|
42784
43053
|
}
|
|
42785
43054
|
}
|
|
42786
43055
|
async function readManifest(root) {
|
|
42787
|
-
const buf = await readMaybe(
|
|
43056
|
+
const buf = await readMaybe(join27(root, MANIFEST_REL));
|
|
42788
43057
|
if (!buf) return {};
|
|
42789
43058
|
try {
|
|
42790
43059
|
return JSON.parse(buf.toString("utf8"));
|
|
@@ -42793,35 +43062,35 @@ async function readManifest(root) {
|
|
|
42793
43062
|
}
|
|
42794
43063
|
}
|
|
42795
43064
|
async function writeManifest(root, manifest) {
|
|
42796
|
-
const path =
|
|
42797
|
-
await
|
|
42798
|
-
await
|
|
43065
|
+
const path = join27(root, MANIFEST_REL);
|
|
43066
|
+
await mkdir10(dirname9(path), { recursive: true });
|
|
43067
|
+
await writeFile11(path, JSON.stringify(manifest, null, 2), "utf8");
|
|
42799
43068
|
}
|
|
42800
43069
|
async function writeArtifactFiles(root, kind, slug, files) {
|
|
42801
43070
|
const byPath = new Map(files.map((f2) => [f2.path, Buffer.from(f2.contentBase64, "base64")]));
|
|
42802
43071
|
if (kind === "subagent") {
|
|
42803
43072
|
const agent = byPath.get("agent.md");
|
|
42804
43073
|
if (agent) {
|
|
42805
|
-
await
|
|
42806
|
-
await
|
|
43074
|
+
await mkdir10(join27(root, AGENTS_REL2), { recursive: true });
|
|
43075
|
+
await writeFile11(join27(root, AGENTS_REL2, `${slug}.md`), agent);
|
|
42807
43076
|
}
|
|
42808
43077
|
const short = byPath.get("short-memory.md");
|
|
42809
43078
|
if (short) {
|
|
42810
|
-
await
|
|
42811
|
-
await
|
|
43079
|
+
await mkdir10(join27(root, AGENTS_REL2, "short-memory"), { recursive: true });
|
|
43080
|
+
await writeFile11(join27(root, AGENTS_REL2, "short-memory", `${slug}.md`), short);
|
|
42812
43081
|
}
|
|
42813
43082
|
const long = byPath.get("long-memory.md");
|
|
42814
43083
|
if (long) {
|
|
42815
|
-
await
|
|
42816
|
-
await
|
|
43084
|
+
await mkdir10(join27(root, AGENTS_REL2, "long-memory"), { recursive: true });
|
|
43085
|
+
await writeFile11(join27(root, AGENTS_REL2, "long-memory", `${slug}.md`), long);
|
|
42817
43086
|
}
|
|
42818
43087
|
} else {
|
|
42819
|
-
const base =
|
|
43088
|
+
const base = join27(root, SKILLS_REL2, slug);
|
|
42820
43089
|
for (const [rel, buf] of byPath) {
|
|
42821
43090
|
if (rel.includes("..") || rel.startsWith("/")) continue;
|
|
42822
|
-
const dest =
|
|
42823
|
-
await
|
|
42824
|
-
await
|
|
43091
|
+
const dest = join27(base, rel);
|
|
43092
|
+
await mkdir10(dirname9(dest), { recursive: true });
|
|
43093
|
+
await writeFile11(dest, buf);
|
|
42825
43094
|
}
|
|
42826
43095
|
}
|
|
42827
43096
|
}
|
|
@@ -42885,11 +43154,11 @@ async function removePackage(root, slug) {
|
|
|
42885
43154
|
const entry = manifest[n];
|
|
42886
43155
|
if (!entry) return { ok: false, error: "not installed" };
|
|
42887
43156
|
if (entry.kind === "subagent") {
|
|
42888
|
-
await
|
|
42889
|
-
await
|
|
42890
|
-
await
|
|
43157
|
+
await rm6(join27(root, AGENTS_REL2, `${n}.md`), { force: true });
|
|
43158
|
+
await rm6(join27(root, AGENTS_REL2, "short-memory", `${n}.md`), { force: true });
|
|
43159
|
+
await rm6(join27(root, AGENTS_REL2, "long-memory", `${n}.md`), { force: true });
|
|
42891
43160
|
} else {
|
|
42892
|
-
await
|
|
43161
|
+
await rm6(join27(root, SKILLS_REL2, n), { recursive: true, force: true });
|
|
42893
43162
|
}
|
|
42894
43163
|
delete manifest[n];
|
|
42895
43164
|
await writeManifest(root, manifest);
|
|
@@ -42900,8 +43169,8 @@ async function removePackage(root, slug) {
|
|
|
42900
43169
|
}
|
|
42901
43170
|
|
|
42902
43171
|
// src/core/graph.ts
|
|
42903
|
-
import { readFile as
|
|
42904
|
-
import { join as
|
|
43172
|
+
import { readFile as readFile14, readdir as readdir6 } from "fs/promises";
|
|
43173
|
+
import { join as join28, relative as relative2, dirname as dirname10, resolve as resolve9, extname as extname2, basename as basename6 } from "path";
|
|
42905
43174
|
var MAX_FILES = 2e3;
|
|
42906
43175
|
var MAX_NODES = 3e3;
|
|
42907
43176
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "out", "coverage", ".turbo"]);
|
|
@@ -42913,7 +43182,7 @@ async function walk(root, accept) {
|
|
|
42913
43182
|
const dir2 = stack.pop();
|
|
42914
43183
|
let entries2;
|
|
42915
43184
|
try {
|
|
42916
|
-
entries2 = await
|
|
43185
|
+
entries2 = await readdir6(dir2, { withFileTypes: true });
|
|
42917
43186
|
} catch {
|
|
42918
43187
|
continue;
|
|
42919
43188
|
}
|
|
@@ -42921,7 +43190,7 @@ async function walk(root, accept) {
|
|
|
42921
43190
|
if (e.name.startsWith(".") && e.name !== ".claude") {
|
|
42922
43191
|
if (e.isDirectory()) continue;
|
|
42923
43192
|
}
|
|
42924
|
-
const full =
|
|
43193
|
+
const full = join28(dir2, e.name);
|
|
42925
43194
|
if (e.isDirectory()) {
|
|
42926
43195
|
if (!SKIP_DIRS.has(e.name)) stack.push(full);
|
|
42927
43196
|
} else if (accept(full)) {
|
|
@@ -42949,7 +43218,7 @@ async function buildDocs(root) {
|
|
|
42949
43218
|
const rel = relative2(root, f2).split("\\").join("/");
|
|
42950
43219
|
let text = "";
|
|
42951
43220
|
try {
|
|
42952
|
-
text = await
|
|
43221
|
+
text = await readFile14(f2, "utf8");
|
|
42953
43222
|
} catch {
|
|
42954
43223
|
continue;
|
|
42955
43224
|
}
|
|
@@ -42988,7 +43257,7 @@ async function buildCode(root) {
|
|
|
42988
43257
|
const rel = relative2(root, f2).split("\\").join("/");
|
|
42989
43258
|
let text = "";
|
|
42990
43259
|
try {
|
|
42991
|
-
text = await
|
|
43260
|
+
text = await readFile14(f2, "utf8");
|
|
42992
43261
|
} catch {
|
|
42993
43262
|
continue;
|
|
42994
43263
|
}
|
|
@@ -43040,11 +43309,11 @@ async function buildGraph(root, mode) {
|
|
|
43040
43309
|
}
|
|
43041
43310
|
|
|
43042
43311
|
// src/core/rag.ts
|
|
43043
|
-
import { readFile as
|
|
43312
|
+
import { readFile as readFile15, writeFile as writeFile12, statfs as statfs2, mkdir as mkdir11 } from "fs/promises";
|
|
43044
43313
|
import { existsSync as existsSync14 } from "fs";
|
|
43045
43314
|
import { execFile as execFile10, spawn as spawn9 } from "child_process";
|
|
43046
43315
|
import { totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
43047
|
-
import { join as
|
|
43316
|
+
import { join as join29 } from "path";
|
|
43048
43317
|
import { promisify as promisify10 } from "util";
|
|
43049
43318
|
var run10 = promisify10(execFile10);
|
|
43050
43319
|
var RAG_REL = ".claude/rag";
|
|
@@ -43065,7 +43334,7 @@ async function detectPython() {
|
|
|
43065
43334
|
}
|
|
43066
43335
|
async function installLog(root) {
|
|
43067
43336
|
try {
|
|
43068
|
-
const text = await
|
|
43337
|
+
const text = await readFile15(join29(root, RAG_REL, "install.log"), "utf8");
|
|
43069
43338
|
return text.split("\n").filter((l) => l.length > 0).slice(-200);
|
|
43070
43339
|
} catch {
|
|
43071
43340
|
return [];
|
|
@@ -43127,14 +43396,14 @@ async function ragStatus(root) {
|
|
|
43127
43396
|
};
|
|
43128
43397
|
let modelName = "";
|
|
43129
43398
|
try {
|
|
43130
|
-
modelName = (await
|
|
43399
|
+
modelName = (await readFile15(join29(root, RAG_REL, ".installed"), "utf8")).trim();
|
|
43131
43400
|
} catch {
|
|
43132
43401
|
modelName = "";
|
|
43133
43402
|
}
|
|
43134
43403
|
const modelPresent = modelName.length > 0;
|
|
43135
43404
|
return {
|
|
43136
43405
|
installed: fastembed && sqliteVec && modelPresent,
|
|
43137
|
-
installing: existsSync14(
|
|
43406
|
+
installing: existsSync14(join29(root, RAG_REL, ".installing")),
|
|
43138
43407
|
python: { found: py.found, version: py.version },
|
|
43139
43408
|
deps: { pip, fastembed, sqliteVec },
|
|
43140
43409
|
model: { present: modelPresent, name: modelName },
|
|
@@ -43145,9 +43414,9 @@ async function ragStatus(root) {
|
|
|
43145
43414
|
};
|
|
43146
43415
|
}
|
|
43147
43416
|
async function readIndexMeta(root) {
|
|
43148
|
-
const indexing = existsSync14(
|
|
43417
|
+
const indexing = existsSync14(join29(root, RAG_REL, ".indexing"));
|
|
43149
43418
|
try {
|
|
43150
|
-
const m2 = JSON.parse(await
|
|
43419
|
+
const m2 = JSON.parse(await readFile15(join29(root, RAG_REL, ".index.json"), "utf8"));
|
|
43151
43420
|
return {
|
|
43152
43421
|
present: true,
|
|
43153
43422
|
indexing,
|
|
@@ -43214,14 +43483,14 @@ if __name__ == "__main__":
|
|
|
43214
43483
|
elif cmd == "query": query(sys.argv[2], int(sys.argv[3]) if len(sys.argv) > 3 else 8)
|
|
43215
43484
|
`;
|
|
43216
43485
|
async function ensureEngine(root) {
|
|
43217
|
-
await
|
|
43218
|
-
await
|
|
43486
|
+
await mkdir11(join29(root, RAG_REL), { recursive: true }).catch(() => void 0);
|
|
43487
|
+
await writeFile12(join29(root, RAG_REL, "rag_engine.py"), RAG_ENGINE_PY, "utf8");
|
|
43219
43488
|
}
|
|
43220
43489
|
async function ragReindex(root) {
|
|
43221
43490
|
const py = await detectPython();
|
|
43222
43491
|
if (!py.found) return { ok: false, started: false, error: "python3 not found" };
|
|
43223
|
-
if (!existsSync14(
|
|
43224
|
-
if (existsSync14(
|
|
43492
|
+
if (!existsSync14(join29(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
|
|
43493
|
+
if (existsSync14(join29(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
|
|
43225
43494
|
await ensureEngine(root);
|
|
43226
43495
|
const script = `
|
|
43227
43496
|
set +e
|
|
@@ -43241,12 +43510,12 @@ rm -f "${RAG_REL}/.indexing"
|
|
|
43241
43510
|
async function ragQuery(root, queryText, k2 = 8) {
|
|
43242
43511
|
const py = await detectPython();
|
|
43243
43512
|
if (!py.found) return { results: [], error: "python3 not found" };
|
|
43244
|
-
if (!existsSync14(
|
|
43513
|
+
if (!existsSync14(join29(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
|
|
43245
43514
|
await ensureEngine(root);
|
|
43246
43515
|
try {
|
|
43247
43516
|
const { stdout } = await run10(
|
|
43248
43517
|
py.bin,
|
|
43249
|
-
[
|
|
43518
|
+
[join29(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k2, 1), 20))],
|
|
43250
43519
|
{ cwd: root, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
43251
43520
|
);
|
|
43252
43521
|
const parsed = JSON.parse(stdout.trim() || '{"results":[]}');
|
|
@@ -43261,9 +43530,9 @@ async function ragInstall(root, model) {
|
|
|
43261
43530
|
}
|
|
43262
43531
|
const py = await detectPython();
|
|
43263
43532
|
if (!py.found) return { ok: false, started: false, error: "python3 not found on the worker" };
|
|
43264
|
-
if (existsSync14(
|
|
43533
|
+
if (existsSync14(join29(root, RAG_REL, ".installing"))) return { ok: true, started: true };
|
|
43265
43534
|
try {
|
|
43266
|
-
await
|
|
43535
|
+
await mkdir11(join29(root, RAG_REL), { recursive: true });
|
|
43267
43536
|
} catch {
|
|
43268
43537
|
}
|
|
43269
43538
|
const script = `
|
|
@@ -43290,8 +43559,8 @@ rm -f "${RAG_REL}/.installing"
|
|
|
43290
43559
|
}
|
|
43291
43560
|
|
|
43292
43561
|
// src/core/config-sync.ts
|
|
43293
|
-
import { existsSync as existsSync15, readFileSync as
|
|
43294
|
-
import { join as
|
|
43562
|
+
import { existsSync as existsSync15, readFileSync as readFileSync16 } from "fs";
|
|
43563
|
+
import { join as join30 } from "path";
|
|
43295
43564
|
var SERVER_MANAGED_KEYS = [
|
|
43296
43565
|
"physicPath",
|
|
43297
43566
|
"sessionSwitchPct",
|
|
@@ -43308,10 +43577,10 @@ var SERVER_MANAGED_KEYS = [
|
|
|
43308
43577
|
"projectAiMemoryBudgetChars"
|
|
43309
43578
|
];
|
|
43310
43579
|
function readConfigText(profileDir2) {
|
|
43311
|
-
const path =
|
|
43580
|
+
const path = join30(profileDir2, "config.json");
|
|
43312
43581
|
if (existsSync15(path)) {
|
|
43313
43582
|
try {
|
|
43314
|
-
return
|
|
43583
|
+
return readFileSync16(path, "utf8");
|
|
43315
43584
|
} catch {
|
|
43316
43585
|
}
|
|
43317
43586
|
}
|
|
@@ -43342,7 +43611,7 @@ function handleWorkerChannels(ctx, message, payload) {
|
|
|
43342
43611
|
switch (message.channel) {
|
|
43343
43612
|
case WsChannels.AUTONOMOUS_READ:
|
|
43344
43613
|
if (ctx.physicRoot) {
|
|
43345
|
-
void readAutonomous(ctx.physicRoot).then(
|
|
43614
|
+
void readAutonomous(ctx.physicRoot, ctx.profileDir).then(
|
|
43346
43615
|
(reply) => ctx.send(WsChannels.AUTONOMOUS_READ, reply, message.id)
|
|
43347
43616
|
);
|
|
43348
43617
|
}
|
|
@@ -43359,7 +43628,7 @@ function handleWorkerChannels(ctx, message, payload) {
|
|
|
43359
43628
|
case WsChannels.AUTONOMOUS_WRITE: {
|
|
43360
43629
|
const req = payload;
|
|
43361
43630
|
if (ctx.physicRoot) {
|
|
43362
|
-
void writeAutonomous(ctx.physicRoot, req, req.by ?? "unknown").then(
|
|
43631
|
+
void writeAutonomous(ctx.physicRoot, ctx.profileDir, req, req.by ?? "unknown").then(
|
|
43363
43632
|
(reply) => ctx.send(WsChannels.AUTONOMOUS_WRITE, reply, message.id)
|
|
43364
43633
|
);
|
|
43365
43634
|
}
|
|
@@ -44201,7 +44470,7 @@ var WsClient = class {
|
|
|
44201
44470
|
*/
|
|
44202
44471
|
physicFolderPath(projectName) {
|
|
44203
44472
|
const safe = projectName.replace(/[/\\]/g, "_").replace(/\.\./g, "_").trim();
|
|
44204
|
-
return safe ?
|
|
44473
|
+
return safe ? join31(this.context.profileDir, safe) : null;
|
|
44205
44474
|
}
|
|
44206
44475
|
/**
|
|
44207
44476
|
* Report machine.status: version + the state of the physic project being served
|
|
@@ -44218,7 +44487,7 @@ var WsClient = class {
|
|
|
44218
44487
|
physicPath,
|
|
44219
44488
|
physicPathExists: physicPath ? existsSync16(physicPath) : void 0,
|
|
44220
44489
|
// Real coarse state (ADR-0152): !paused and a recent tick (was hard-coded false).
|
|
44221
|
-
autonomousRunning: physicPath ? isAutonomousRunning(physicPath) : false,
|
|
44490
|
+
autonomousRunning: physicPath ? isAutonomousRunning(physicPath, this.context.profileDir) : false,
|
|
44222
44491
|
// The machine-user AI-run limit (0 = unlimited) + profile count so a dispatch can resolve the
|
|
44223
44492
|
// effective timeout and derive the SSE reply windows server-side (ADR-0256).
|
|
44224
44493
|
aiRunTimeoutSec: config.aiRunTimeoutSec ?? 0,
|
|
@@ -44310,7 +44579,7 @@ var WsClient = class {
|
|
|
44310
44579
|
*/
|
|
44311
44580
|
uploadLog() {
|
|
44312
44581
|
try {
|
|
44313
|
-
const upload = readLogUpload(
|
|
44582
|
+
const upload = readLogUpload(join31(this.context.profileDir, "logs"));
|
|
44314
44583
|
if (!upload) return;
|
|
44315
44584
|
const sig = `${upload.fileName}:${upload.totalBytes}`;
|
|
44316
44585
|
if (sig === this.lastLogSig) return;
|
|
@@ -44472,10 +44741,10 @@ var WsClient = class {
|
|
|
44472
44741
|
};
|
|
44473
44742
|
|
|
44474
44743
|
// src/core/instance-lock.ts
|
|
44475
|
-
import { readFileSync as
|
|
44476
|
-
import { join as
|
|
44744
|
+
import { readFileSync as readFileSync17, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "fs";
|
|
44745
|
+
import { join as join32 } from "path";
|
|
44477
44746
|
function lockPath(profileDir2) {
|
|
44478
|
-
return
|
|
44747
|
+
return join32(profileDir2, "start.lock");
|
|
44479
44748
|
}
|
|
44480
44749
|
function isAlive(pid) {
|
|
44481
44750
|
try {
|
|
@@ -44487,7 +44756,7 @@ function isAlive(pid) {
|
|
|
44487
44756
|
}
|
|
44488
44757
|
function lockHolder(profileDir2) {
|
|
44489
44758
|
try {
|
|
44490
|
-
const pid = Number.parseInt(
|
|
44759
|
+
const pid = Number.parseInt(readFileSync17(lockPath(profileDir2), "utf8").trim(), 10);
|
|
44491
44760
|
if (Number.isInteger(pid) && pid > 0 && pid !== process.pid && isAlive(pid)) return pid;
|
|
44492
44761
|
} catch {
|
|
44493
44762
|
}
|
|
@@ -44500,7 +44769,7 @@ function acquireInstanceLock(profileDir2) {
|
|
|
44500
44769
|
}
|
|
44501
44770
|
function releaseInstanceLock(profileDir2) {
|
|
44502
44771
|
try {
|
|
44503
|
-
const pid = Number.parseInt(
|
|
44772
|
+
const pid = Number.parseInt(readFileSync17(lockPath(profileDir2), "utf8").trim(), 10);
|
|
44504
44773
|
if (pid === process.pid) rmSync7(lockPath(profileDir2), { force: true });
|
|
44505
44774
|
} catch {
|
|
44506
44775
|
}
|
|
@@ -44796,11 +45065,12 @@ var SessionBus = class {
|
|
|
44796
45065
|
|
|
44797
45066
|
// src/core/control-server.ts
|
|
44798
45067
|
import { createServer } from "net";
|
|
44799
|
-
import { rmSync as rmSync8 } from "fs";
|
|
44800
|
-
import { join as
|
|
45068
|
+
import { chmodSync as chmodSync4, rmSync as rmSync8 } from "fs";
|
|
45069
|
+
import { join as join34 } from "path";
|
|
44801
45070
|
|
|
44802
45071
|
// src/core/control-protocol.ts
|
|
44803
45072
|
var CONTROL_SOCKET_FILE = "control.sock";
|
|
45073
|
+
var CONTROL_TOKEN_FILE = "control.token";
|
|
44804
45074
|
function encodeFrame(frame) {
|
|
44805
45075
|
return `${JSON.stringify(frame)}
|
|
44806
45076
|
`;
|
|
@@ -44824,9 +45094,41 @@ function createFrameParser() {
|
|
|
44824
45094
|
};
|
|
44825
45095
|
}
|
|
44826
45096
|
|
|
45097
|
+
// src/core/control-token.ts
|
|
45098
|
+
import { chmodSync as chmodSync3, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "fs";
|
|
45099
|
+
import { randomBytes as randomBytes3, timingSafeEqual } from "crypto";
|
|
45100
|
+
import { join as join33 } from "path";
|
|
45101
|
+
function controlTokenPath(profileDir2) {
|
|
45102
|
+
return join33(profileDir2, CONTROL_TOKEN_FILE);
|
|
45103
|
+
}
|
|
45104
|
+
function writeControlToken(profileDir2) {
|
|
45105
|
+
const token = randomBytes3(32).toString("hex");
|
|
45106
|
+
const path = controlTokenPath(profileDir2);
|
|
45107
|
+
writeFileSync10(path, token, { mode: 384 });
|
|
45108
|
+
try {
|
|
45109
|
+
chmodSync3(path, 384);
|
|
45110
|
+
} catch {
|
|
45111
|
+
}
|
|
45112
|
+
return token;
|
|
45113
|
+
}
|
|
45114
|
+
function readControlToken(profileDir2) {
|
|
45115
|
+
try {
|
|
45116
|
+
const v2 = readFileSync18(controlTokenPath(profileDir2), "utf8").trim();
|
|
45117
|
+
return v2.length > 0 ? v2 : null;
|
|
45118
|
+
} catch {
|
|
45119
|
+
return null;
|
|
45120
|
+
}
|
|
45121
|
+
}
|
|
45122
|
+
function tokenMatches(expected, given) {
|
|
45123
|
+
if (!given) return false;
|
|
45124
|
+
const a = Buffer.from(expected, "utf8");
|
|
45125
|
+
const b2 = Buffer.from(given, "utf8");
|
|
45126
|
+
return a.length === b2.length && timingSafeEqual(a, b2);
|
|
45127
|
+
}
|
|
45128
|
+
|
|
44827
45129
|
// src/core/control-server.ts
|
|
44828
45130
|
function startControlServer(bus, profileDir2, info) {
|
|
44829
|
-
const socketPath =
|
|
45131
|
+
const socketPath = join34(profileDir2, CONTROL_SOCKET_FILE);
|
|
44830
45132
|
try {
|
|
44831
45133
|
rmSync8(socketPath, { force: true });
|
|
44832
45134
|
} catch {
|
|
@@ -44858,25 +45160,37 @@ function startControlServer(bus, profileDir2, info) {
|
|
|
44858
45160
|
// Report an autonomous cycle's completion back to the `4pm auto-run` client (ADR-0319).
|
|
44859
45161
|
bus.onAutonomousDone((ok, note) => broadcast({ t: "autonomousDone", ok, note }))
|
|
44860
45162
|
];
|
|
45163
|
+
const token = writeControlToken(profileDir2);
|
|
45164
|
+
const sendSnapshot = (sock) => send(sock, {
|
|
45165
|
+
t: "snapshot",
|
|
45166
|
+
info,
|
|
45167
|
+
status: bus.status,
|
|
45168
|
+
busy: bus.busy,
|
|
45169
|
+
activeProfile: bus.activeProfile,
|
|
45170
|
+
scope: bus.scope,
|
|
45171
|
+
worker: bus.worker,
|
|
45172
|
+
project: bus.project,
|
|
45173
|
+
usage: bus.usage,
|
|
45174
|
+
tokens: bus.sessionTokens,
|
|
45175
|
+
transcript: bus.snapshot()
|
|
45176
|
+
});
|
|
44861
45177
|
const server = createServer((sock) => {
|
|
44862
|
-
clients.add(sock);
|
|
44863
|
-
send(sock, {
|
|
44864
|
-
t: "snapshot",
|
|
44865
|
-
info,
|
|
44866
|
-
status: bus.status,
|
|
44867
|
-
busy: bus.busy,
|
|
44868
|
-
activeProfile: bus.activeProfile,
|
|
44869
|
-
scope: bus.scope,
|
|
44870
|
-
worker: bus.worker,
|
|
44871
|
-
project: bus.project,
|
|
44872
|
-
usage: bus.usage,
|
|
44873
|
-
tokens: bus.sessionTokens,
|
|
44874
|
-
transcript: bus.snapshot()
|
|
44875
|
-
});
|
|
44876
|
-
const parse = createFrameParser();
|
|
44877
45178
|
sock.setEncoding("utf8");
|
|
45179
|
+
let authed = false;
|
|
45180
|
+
const parse = createFrameParser();
|
|
44878
45181
|
sock.on("data", (chunk) => {
|
|
44879
45182
|
for (const frame of parse(chunk)) {
|
|
45183
|
+
if (!authed) {
|
|
45184
|
+
if (frame.t === "auth" && tokenMatches(token, frame.token)) {
|
|
45185
|
+
authed = true;
|
|
45186
|
+
clients.add(sock);
|
|
45187
|
+
sendSnapshot(sock);
|
|
45188
|
+
} else {
|
|
45189
|
+
send(sock, { t: "authError" });
|
|
45190
|
+
sock.destroy();
|
|
45191
|
+
}
|
|
45192
|
+
continue;
|
|
45193
|
+
}
|
|
44880
45194
|
if (frame.t === "submit") bus.submitLocal(frame.input);
|
|
44881
45195
|
else if (frame.t === "reconnect") bus.requestReconnect();
|
|
44882
45196
|
else if (frame.t === "autonomousRun") bus.submitAutonomous();
|
|
@@ -44890,7 +45204,12 @@ function startControlServer(bus, profileDir2, info) {
|
|
|
44890
45204
|
});
|
|
44891
45205
|
server.on("error", () => {
|
|
44892
45206
|
});
|
|
44893
|
-
server.listen(socketPath)
|
|
45207
|
+
server.listen(socketPath, () => {
|
|
45208
|
+
try {
|
|
45209
|
+
chmodSync4(socketPath, 384);
|
|
45210
|
+
} catch {
|
|
45211
|
+
}
|
|
45212
|
+
});
|
|
44894
45213
|
return () => {
|
|
44895
45214
|
for (const un2 of unsubs) un2();
|
|
44896
45215
|
for (const sock of clients) sock.destroy();
|
|
@@ -44898,6 +45217,7 @@ function startControlServer(bus, profileDir2, info) {
|
|
|
44898
45217
|
server.close();
|
|
44899
45218
|
try {
|
|
44900
45219
|
rmSync8(socketPath, { force: true });
|
|
45220
|
+
rmSync8(controlTokenPath(profileDir2), { force: true });
|
|
44901
45221
|
} catch {
|
|
44902
45222
|
}
|
|
44903
45223
|
};
|
|
@@ -50560,17 +50880,17 @@ var import_react21 = __toESM(require_react(), 1);
|
|
|
50560
50880
|
var import_react26 = __toESM(require_react(), 1);
|
|
50561
50881
|
|
|
50562
50882
|
// src/core/input-history.ts
|
|
50563
|
-
import { existsSync as existsSync18, readFileSync as
|
|
50564
|
-
import { join as
|
|
50883
|
+
import { existsSync as existsSync18, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
|
|
50884
|
+
import { join as join35 } from "path";
|
|
50565
50885
|
var MAX_HISTORY = 200;
|
|
50566
50886
|
function historyPath(profileDir2) {
|
|
50567
|
-
return
|
|
50887
|
+
return join35(profileDir2, "input-history.json");
|
|
50568
50888
|
}
|
|
50569
50889
|
function loadInputHistory(profileDir2) {
|
|
50570
50890
|
const path = historyPath(profileDir2);
|
|
50571
50891
|
if (!existsSync18(path)) return [];
|
|
50572
50892
|
try {
|
|
50573
|
-
const data = JSON.parse(
|
|
50893
|
+
const data = JSON.parse(readFileSync20(path, "utf8"));
|
|
50574
50894
|
return Array.isArray(data) ? data.filter((x) => typeof x === "string") : [];
|
|
50575
50895
|
} catch {
|
|
50576
50896
|
return [];
|
|
@@ -50581,7 +50901,7 @@ function appendInputHistory(profileDir2, line, current2) {
|
|
|
50581
50901
|
if (!trimmed || current2[current2.length - 1] === trimmed) return current2;
|
|
50582
50902
|
const next = [...current2, trimmed].slice(-MAX_HISTORY);
|
|
50583
50903
|
try {
|
|
50584
|
-
|
|
50904
|
+
writeFileSync11(historyPath(profileDir2), JSON.stringify(next), "utf8");
|
|
50585
50905
|
} catch {
|
|
50586
50906
|
}
|
|
50587
50907
|
return next;
|
|
@@ -51178,7 +51498,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
51178
51498
|
return;
|
|
51179
51499
|
}
|
|
51180
51500
|
logger.configure({
|
|
51181
|
-
dir:
|
|
51501
|
+
dir: join36(profileDir2, "logs"),
|
|
51182
51502
|
level: resolveLogLevel(),
|
|
51183
51503
|
base: { version: CLI_VERSION, profile: profileName, scope: credential.scope }
|
|
51184
51504
|
});
|
|
@@ -51287,15 +51607,21 @@ async function runStart(profileDir2, profileName) {
|
|
|
51287
51607
|
}
|
|
51288
51608
|
|
|
51289
51609
|
// src/commands/attach.ts
|
|
51290
|
-
import { join as
|
|
51610
|
+
import { join as join37 } from "path";
|
|
51291
51611
|
|
|
51292
51612
|
// src/core/control-client.ts
|
|
51293
51613
|
import { connect as connect2 } from "net";
|
|
51294
|
-
function connectControl(socketPath, bus) {
|
|
51614
|
+
function connectControl(socketPath, bus, token) {
|
|
51295
51615
|
return new Promise((resolve10, reject) => {
|
|
51296
51616
|
const socket = connect2(socketPath);
|
|
51297
51617
|
socket.setEncoding("utf8");
|
|
51298
51618
|
let ready = false;
|
|
51619
|
+
socket.on("connect", () => {
|
|
51620
|
+
try {
|
|
51621
|
+
socket.write(encodeFrame({ t: "auth", token: token ?? "" }));
|
|
51622
|
+
} catch {
|
|
51623
|
+
}
|
|
51624
|
+
});
|
|
51299
51625
|
const forwardSubmit = bus.onLocalSubmit((input) => {
|
|
51300
51626
|
try {
|
|
51301
51627
|
socket.write(encodeFrame({ t: "submit", input }));
|
|
@@ -51374,6 +51700,9 @@ function connectControl(socketPath, bus) {
|
|
|
51374
51700
|
case "tokens":
|
|
51375
51701
|
bus.setTokens(frame.total);
|
|
51376
51702
|
break;
|
|
51703
|
+
case "authError":
|
|
51704
|
+
if (!ready) reject(new Error("control-channel auth failed"));
|
|
51705
|
+
break;
|
|
51377
51706
|
}
|
|
51378
51707
|
}
|
|
51379
51708
|
});
|
|
@@ -51387,11 +51716,11 @@ async function runAttach(profileDir2, profileName) {
|
|
|
51387
51716
|
process.exitCode = 1;
|
|
51388
51717
|
return;
|
|
51389
51718
|
}
|
|
51390
|
-
const socketPath =
|
|
51719
|
+
const socketPath = join37(profileDir2, CONTROL_SOCKET_FILE);
|
|
51391
51720
|
const bus = new SessionBus();
|
|
51392
51721
|
let conn;
|
|
51393
51722
|
try {
|
|
51394
|
-
conn = await connectControl(socketPath, bus);
|
|
51723
|
+
conn = await connectControl(socketPath, bus, readControlToken(profileDir2));
|
|
51395
51724
|
} catch {
|
|
51396
51725
|
console.error(t("attach.noDaemon", { profile: profileName }));
|
|
51397
51726
|
process.exitCode = 1;
|
|
@@ -51418,11 +51747,12 @@ async function runAttach(profileDir2, profileName) {
|
|
|
51418
51747
|
|
|
51419
51748
|
// src/commands/auto-run.ts
|
|
51420
51749
|
import { connect as connect3 } from "net";
|
|
51421
|
-
import { join as
|
|
51750
|
+
import { join as join38 } from "path";
|
|
51422
51751
|
var AUTO_RUN_TIMEOUT_MS = 3 * 60 * 60 * 1e3;
|
|
51423
51752
|
async function runAutoRun(profileDir2, profileName) {
|
|
51424
51753
|
initI18n(readProfileConfig(profileDir2).locale);
|
|
51425
|
-
const socketPath =
|
|
51754
|
+
const socketPath = join38(profileDir2, CONTROL_SOCKET_FILE);
|
|
51755
|
+
const token = readControlToken(profileDir2);
|
|
51426
51756
|
const code = await new Promise((resolve10) => {
|
|
51427
51757
|
let settled = false;
|
|
51428
51758
|
const done = (c) => {
|
|
@@ -51444,6 +51774,7 @@ async function runAutoRun(profileDir2, profileName) {
|
|
|
51444
51774
|
timer.unref?.();
|
|
51445
51775
|
socket.on("connect", () => {
|
|
51446
51776
|
try {
|
|
51777
|
+
socket.write(encodeFrame({ t: "auth", token: token ?? "" }));
|
|
51447
51778
|
socket.write(encodeFrame({ t: "autonomousRun" }));
|
|
51448
51779
|
} catch {
|
|
51449
51780
|
done(1);
|
|
@@ -51460,6 +51791,9 @@ async function runAutoRun(profileDir2, profileName) {
|
|
|
51460
51791
|
if (frame.ok) console.log(t("autoRun.done"));
|
|
51461
51792
|
else console.error(t("autoRun.failed", { note: frame.note ?? "" }));
|
|
51462
51793
|
done(frame.ok ? 0 : 1);
|
|
51794
|
+
} else if (frame.t === "authError") {
|
|
51795
|
+
console.error(t("autoRun.authFailed"));
|
|
51796
|
+
done(1);
|
|
51463
51797
|
}
|
|
51464
51798
|
}
|
|
51465
51799
|
});
|