@4pm/cli 1.18.0 → 1.19.2
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 +595 -247
- 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
|
|
@@ -38022,6 +38032,9 @@ var OutputBatcher = class {
|
|
|
38022
38032
|
flushIntervalMs: 200,
|
|
38023
38033
|
flushSizeKb: 64,
|
|
38024
38034
|
maxBufferKb: 512,
|
|
38035
|
+
// Well under the cli-server WS maxPayload (8 MiB — ADR-0322) even after the enveloped frame's
|
|
38036
|
+
// AES-256-GCM + base64 + JSON inflation (~1.37×), and small enough to keep SSE replay chunks light.
|
|
38037
|
+
maxFrameKb: 256,
|
|
38025
38038
|
overflow: "backpressure",
|
|
38026
38039
|
...options
|
|
38027
38040
|
};
|
|
@@ -38052,7 +38065,9 @@ var OutputBatcher = class {
|
|
|
38052
38065
|
}
|
|
38053
38066
|
}
|
|
38054
38067
|
/**
|
|
38055
|
-
* Flush the
|
|
38068
|
+
* Flush the current buffer immediately, splitting it into frames no larger than `maxFrameKb`
|
|
38069
|
+
* (ADR-0322) so a single large burst never produces one over-`maxPayload` WS frame. The
|
|
38070
|
+
* `truncated` flag is carried on the last frame only.
|
|
38056
38071
|
*/
|
|
38057
38072
|
flush() {
|
|
38058
38073
|
if (this.timer) {
|
|
@@ -38060,7 +38075,7 @@ var OutputBatcher = class {
|
|
|
38060
38075
|
this.timer = null;
|
|
38061
38076
|
}
|
|
38062
38077
|
if (this.buffer.length === 0 && !this.truncated) return;
|
|
38063
|
-
const
|
|
38078
|
+
const buf = this.buffer;
|
|
38064
38079
|
const truncated = this.truncated;
|
|
38065
38080
|
this.buffer = "";
|
|
38066
38081
|
this.truncated = false;
|
|
@@ -38068,7 +38083,16 @@ var OutputBatcher = class {
|
|
|
38068
38083
|
this.paused = false;
|
|
38069
38084
|
this.opts.onPressure?.(false);
|
|
38070
38085
|
}
|
|
38071
|
-
this.opts.
|
|
38086
|
+
const max = this.opts.maxFrameKb * 1024;
|
|
38087
|
+
if (buf.length <= max) {
|
|
38088
|
+
this.opts.onFlush(buf, truncated);
|
|
38089
|
+
return;
|
|
38090
|
+
}
|
|
38091
|
+
for (let i = 0; i < buf.length; i += max) {
|
|
38092
|
+
const slice = buf.slice(i, i + max);
|
|
38093
|
+
const isLast = i + max >= buf.length;
|
|
38094
|
+
this.opts.onFlush(slice, isLast && truncated);
|
|
38095
|
+
}
|
|
38072
38096
|
}
|
|
38073
38097
|
};
|
|
38074
38098
|
|
|
@@ -39887,14 +39911,122 @@ function reportToolResult(tool, ok, message) {
|
|
|
39887
39911
|
}
|
|
39888
39912
|
|
|
39889
39913
|
// src/core/autonomous.ts
|
|
39890
|
-
import { readFile as
|
|
39891
|
-
import { readFileSync as
|
|
39914
|
+
import { readFile as readFile4, writeFile as writeFile4, chmod } from "fs/promises";
|
|
39915
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
39892
39916
|
import { execFile as execFile4, spawn as spawn5 } from "child_process";
|
|
39893
|
-
import { join as
|
|
39917
|
+
import { join as join17 } from "path";
|
|
39894
39918
|
import { promisify as promisify4 } from "util";
|
|
39895
|
-
|
|
39896
|
-
|
|
39919
|
+
|
|
39920
|
+
// src/core/autonomous-config.ts
|
|
39921
|
+
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
39922
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
39923
|
+
import { join as join15 } from "path";
|
|
39924
|
+
var DEFAULT_AUTONOMOUS_CONFIG = {
|
|
39925
|
+
paused: true,
|
|
39926
|
+
cronSchedule: "*/10 * * * *",
|
|
39927
|
+
quietHours: "",
|
|
39928
|
+
maxTicksPerDay: -1,
|
|
39929
|
+
stopOnConsecutiveFailures: 3,
|
|
39930
|
+
logRetentionDays: 14,
|
|
39931
|
+
model: "",
|
|
39932
|
+
maxSessionPct: 80,
|
|
39933
|
+
maxWeeklyPct: 90
|
|
39934
|
+
};
|
|
39935
|
+
function autonomousConfigPath(profileDir2) {
|
|
39936
|
+
return join15(profileDir2, "autonomous.config.json");
|
|
39937
|
+
}
|
|
39938
|
+
function coerce2(raw) {
|
|
39939
|
+
const o = raw && typeof raw === "object" ? raw : {};
|
|
39940
|
+
const d = DEFAULT_AUTONOMOUS_CONFIG;
|
|
39941
|
+
const num = (v2, def) => typeof v2 === "number" && Number.isFinite(v2) ? v2 : def;
|
|
39942
|
+
const str = (v2, def) => typeof v2 === "string" ? v2 : def;
|
|
39943
|
+
return {
|
|
39944
|
+
paused: typeof o.paused === "boolean" ? o.paused : d.paused,
|
|
39945
|
+
cronSchedule: str(o.cronSchedule, d.cronSchedule),
|
|
39946
|
+
quietHours: str(o.quietHours, d.quietHours),
|
|
39947
|
+
maxTicksPerDay: num(o.maxTicksPerDay, d.maxTicksPerDay),
|
|
39948
|
+
stopOnConsecutiveFailures: num(o.stopOnConsecutiveFailures, d.stopOnConsecutiveFailures),
|
|
39949
|
+
logRetentionDays: num(o.logRetentionDays, d.logRetentionDays),
|
|
39950
|
+
model: str(o.model, d.model),
|
|
39951
|
+
maxSessionPct: num(o.maxSessionPct, d.maxSessionPct),
|
|
39952
|
+
maxWeeklyPct: num(o.maxWeeklyPct, d.maxWeeklyPct)
|
|
39953
|
+
};
|
|
39954
|
+
}
|
|
39955
|
+
async function readAutonomousConfig(profileDir2) {
|
|
39956
|
+
try {
|
|
39957
|
+
return coerce2(JSON.parse(await readFile2(autonomousConfigPath(profileDir2), "utf8")));
|
|
39958
|
+
} catch {
|
|
39959
|
+
return { ...DEFAULT_AUTONOMOUS_CONFIG };
|
|
39960
|
+
}
|
|
39961
|
+
}
|
|
39962
|
+
function readAutonomousConfigSync(profileDir2) {
|
|
39963
|
+
try {
|
|
39964
|
+
return coerce2(JSON.parse(readFileSync13(autonomousConfigPath(profileDir2), "utf8")));
|
|
39965
|
+
} catch {
|
|
39966
|
+
return { ...DEFAULT_AUTONOMOUS_CONFIG };
|
|
39967
|
+
}
|
|
39968
|
+
}
|
|
39969
|
+
async function readAutonomousConfigText(profileDir2) {
|
|
39970
|
+
try {
|
|
39971
|
+
return await readFile2(autonomousConfigPath(profileDir2), "utf8");
|
|
39972
|
+
} catch {
|
|
39973
|
+
return JSON.stringify(DEFAULT_AUTONOMOUS_CONFIG, null, 2) + "\n";
|
|
39974
|
+
}
|
|
39975
|
+
}
|
|
39976
|
+
async function writeAutonomousConfig(profileDir2, raw) {
|
|
39977
|
+
const cfg = coerce2(raw);
|
|
39978
|
+
await writeFile2(autonomousConfigPath(profileDir2), JSON.stringify(cfg, null, 2) + "\n", "utf8");
|
|
39979
|
+
}
|
|
39980
|
+
function isInQuietHours(quietHours, now = /* @__PURE__ */ new Date()) {
|
|
39981
|
+
const m2 = /^(\d{1,2}):(\d{2})-(\d{1,2}):(\d{2})$/.exec(quietHours.trim());
|
|
39982
|
+
if (!m2) return false;
|
|
39983
|
+
const toMin = (h, mi2) => Number(h) * 60 + Number(mi2);
|
|
39984
|
+
const start = toMin(m2[1], m2[2]);
|
|
39985
|
+
const end = toMin(m2[3], m2[4]);
|
|
39986
|
+
const cur = now.getHours() * 60 + now.getMinutes();
|
|
39987
|
+
return start <= end ? cur >= start && cur < end : cur >= start || cur < end;
|
|
39988
|
+
}
|
|
39989
|
+
|
|
39990
|
+
// src/core/autonomous-history.ts
|
|
39991
|
+
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
39992
|
+
import { join as join16 } from "path";
|
|
39897
39993
|
var HISTORIES_REL = ".claude/.autonomous.histories.json";
|
|
39994
|
+
var MAX_RECORDS = 50;
|
|
39995
|
+
var SKELETON = { cronApplied: "", consecutiveFails: 0, ticks: { day: "", count: 0 }, records: [] };
|
|
39996
|
+
function historiesPath(root) {
|
|
39997
|
+
return join16(root, HISTORIES_REL);
|
|
39998
|
+
}
|
|
39999
|
+
async function readHistories(root) {
|
|
40000
|
+
try {
|
|
40001
|
+
const d = JSON.parse(await readFile3(historiesPath(root), "utf8"));
|
|
40002
|
+
const ticks = d.ticks ?? {};
|
|
40003
|
+
return {
|
|
40004
|
+
cronApplied: typeof d.cronApplied === "string" ? d.cronApplied : typeof d.cron_applied === "string" ? d.cron_applied : "",
|
|
40005
|
+
consecutiveFails: typeof d.consecutiveFails === "number" ? d.consecutiveFails : typeof d.consecutive_fails === "number" ? d.consecutive_fails : 0,
|
|
40006
|
+
ticks: { day: typeof ticks.day === "string" ? ticks.day : "", count: typeof ticks.count === "number" ? ticks.count : 0 },
|
|
40007
|
+
records: Array.isArray(d.records) ? d.records : []
|
|
40008
|
+
};
|
|
40009
|
+
} catch {
|
|
40010
|
+
return { ...SKELETON, ticks: { ...SKELETON.ticks }, records: [] };
|
|
40011
|
+
}
|
|
40012
|
+
}
|
|
40013
|
+
async function writeHistories(root, h) {
|
|
40014
|
+
try {
|
|
40015
|
+
await writeFile3(historiesPath(root), JSON.stringify(h, null, 2) + "\n", "utf8");
|
|
40016
|
+
} catch {
|
|
40017
|
+
}
|
|
40018
|
+
}
|
|
40019
|
+
function todayTickCount(h, today) {
|
|
40020
|
+
return h.ticks.day === today ? h.ticks.count : 0;
|
|
40021
|
+
}
|
|
40022
|
+
function pushRecord(h, rec) {
|
|
40023
|
+
h.records.push(rec);
|
|
40024
|
+
if (h.records.length > MAX_RECORDS) h.records = h.records.slice(-MAX_RECORDS);
|
|
40025
|
+
return h;
|
|
40026
|
+
}
|
|
40027
|
+
|
|
40028
|
+
// src/core/autonomous.ts
|
|
40029
|
+
var run4 = promisify4(execFile4);
|
|
39898
40030
|
var APPROVALS_REL = ".claude/.autonomous.approvals.json";
|
|
39899
40031
|
var AUTHORS_REL = ".claude/.autonomous.authors.json";
|
|
39900
40032
|
var TICK_REL = ".claude/hooks/autonomous-tick.sh";
|
|
@@ -39906,10 +40038,9 @@ var BOOK_FILES = {
|
|
|
39906
40038
|
aiDone: "AI_DONE.md",
|
|
39907
40039
|
userQa: "USER_QA.md"
|
|
39908
40040
|
};
|
|
39909
|
-
var DEFAULT_CRON = "*/10 * * * *";
|
|
39910
40041
|
async function readText(path, fallback = "") {
|
|
39911
40042
|
try {
|
|
39912
|
-
return await
|
|
40043
|
+
return await readFile4(path, "utf8");
|
|
39913
40044
|
} catch {
|
|
39914
40045
|
return fallback;
|
|
39915
40046
|
}
|
|
@@ -39955,7 +40086,7 @@ function tableRowsById(md) {
|
|
|
39955
40086
|
return rows;
|
|
39956
40087
|
}
|
|
39957
40088
|
function tickScript(root) {
|
|
39958
|
-
return
|
|
40089
|
+
return join17(root, TICK_REL);
|
|
39959
40090
|
}
|
|
39960
40091
|
async function crontabList() {
|
|
39961
40092
|
try {
|
|
@@ -39977,28 +40108,28 @@ async function cronInstalled(root) {
|
|
|
39977
40108
|
const script = tickScript(root);
|
|
39978
40109
|
return (await crontabList()).split("\n").some((l) => l.includes(script) && !l.trim().startsWith("#"));
|
|
39979
40110
|
}
|
|
39980
|
-
async function getAutonomousStatus(root) {
|
|
39981
|
-
const
|
|
39982
|
-
|
|
39983
|
-
|
|
39984
|
-
|
|
39985
|
-
|
|
40111
|
+
async function getAutonomousStatus(root, profileDir2) {
|
|
40112
|
+
const [cfg, hist, installed] = await Promise.all([
|
|
40113
|
+
readAutonomousConfig(profileDir2),
|
|
40114
|
+
readHistories(root),
|
|
40115
|
+
cronInstalled(root)
|
|
40116
|
+
]);
|
|
40117
|
+
const last = hist.records[hist.records.length - 1];
|
|
39986
40118
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
39987
40119
|
return {
|
|
39988
|
-
installed
|
|
39989
|
-
paused:
|
|
39990
|
-
cronSchedule:
|
|
39991
|
-
lastTickAt: last
|
|
39992
|
-
lastResult: last
|
|
39993
|
-
consecutiveFails:
|
|
39994
|
-
todayTicks: ticks.day === today
|
|
40120
|
+
installed,
|
|
40121
|
+
paused: cfg.paused,
|
|
40122
|
+
cronSchedule: cfg.cronSchedule,
|
|
40123
|
+
lastTickAt: last?.ts ?? null,
|
|
40124
|
+
lastResult: last?.status ?? null,
|
|
40125
|
+
consecutiveFails: hist.consecutiveFails,
|
|
40126
|
+
todayTicks: hist.ticks.day === today ? hist.ticks.count : 0
|
|
39995
40127
|
};
|
|
39996
40128
|
}
|
|
39997
|
-
function isAutonomousRunning(root) {
|
|
40129
|
+
function isAutonomousRunning(root, profileDir2) {
|
|
39998
40130
|
try {
|
|
39999
|
-
|
|
40000
|
-
|
|
40001
|
-
const h = JSON.parse(readFileSync13(join15(root, HISTORIES_REL), "utf8"));
|
|
40131
|
+
if (readAutonomousConfigSync(profileDir2).paused) return false;
|
|
40132
|
+
const h = JSON.parse(readFileSync14(join17(root, ".claude/.autonomous.histories.json"), "utf8"));
|
|
40002
40133
|
const ts2 = h.records?.[h.records.length - 1]?.ts;
|
|
40003
40134
|
if (!ts2) return false;
|
|
40004
40135
|
const t2 = Date.parse(ts2.replace(" ", "T"));
|
|
@@ -40007,13 +40138,13 @@ function isAutonomousRunning(root) {
|
|
|
40007
40138
|
return false;
|
|
40008
40139
|
}
|
|
40009
40140
|
}
|
|
40010
|
-
async function readAutonomous(root) {
|
|
40141
|
+
async function readAutonomous(root, profileDir2) {
|
|
40011
40142
|
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(
|
|
40143
|
+
readAutonomousConfigText(profileDir2),
|
|
40144
|
+
readText(join17(root, APPROVALS_REL), "{}"),
|
|
40145
|
+
readText(join17(root, AUTHORS_REL), "{}"),
|
|
40146
|
+
getAutonomousStatus(root, profileDir2),
|
|
40147
|
+
...Object.values(BOOK_FILES).map((f2) => readText(join17(root, f2)))
|
|
40017
40148
|
]);
|
|
40018
40149
|
const keys = Object.keys(BOOK_FILES);
|
|
40019
40150
|
const bookMap = {};
|
|
@@ -40022,18 +40153,21 @@ async function readAutonomous(root) {
|
|
|
40022
40153
|
}
|
|
40023
40154
|
async function readAutonomousLogs(root, date) {
|
|
40024
40155
|
const day = date && /^\d{4}-\d{2}-\d{2}$/.test(date) ? date : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
40025
|
-
const text = await readText(
|
|
40156
|
+
const text = await readText(join17(root, LOG_DIR_REL, `autonomous-tick-${day}.log`));
|
|
40026
40157
|
const lines = text ? text.split("\n").filter((l) => l.length > 0).slice(-500) : [];
|
|
40027
40158
|
return { date: day, lines };
|
|
40028
40159
|
}
|
|
40029
|
-
async function installCron(root) {
|
|
40160
|
+
async function installCron(root, profileDir2) {
|
|
40030
40161
|
const script = tickScript(root);
|
|
40031
|
-
const
|
|
40162
|
+
const cfg = await readAutonomousConfig(profileDir2);
|
|
40032
40163
|
await chmod(script, 493).catch(() => void 0);
|
|
40033
40164
|
const kept = (await crontabList()).split("\n").filter((l) => l.trim() && !l.includes(script));
|
|
40034
|
-
kept.push(`${
|
|
40165
|
+
kept.push(`${cfg.cronSchedule} ${script}`);
|
|
40035
40166
|
await crontabSet(kept.join("\n"));
|
|
40036
40167
|
}
|
|
40168
|
+
async function syncCron(root, profileDir2) {
|
|
40169
|
+
if (await cronInstalled(root)) await installCron(root, profileDir2);
|
|
40170
|
+
}
|
|
40037
40171
|
async function uninstallCron(root) {
|
|
40038
40172
|
const script = tickScript(root);
|
|
40039
40173
|
const cur = await crontabList();
|
|
@@ -40041,82 +40175,84 @@ async function uninstallCron(root) {
|
|
|
40041
40175
|
const kept = cur.split("\n").filter((l) => l.trim() && !l.includes(script));
|
|
40042
40176
|
await crontabSet(kept.join("\n"));
|
|
40043
40177
|
}
|
|
40044
|
-
async function writeAutonomous(root, req, by) {
|
|
40178
|
+
async function writeAutonomous(root, profileDir2, req, by) {
|
|
40045
40179
|
try {
|
|
40046
40180
|
switch (req.kind) {
|
|
40047
40181
|
case "settings": {
|
|
40048
|
-
|
|
40049
|
-
|
|
40182
|
+
const parsed = parseJson(req.settings);
|
|
40183
|
+
if (!parsed) return { ok: false, error: "settings is not valid JSON" };
|
|
40184
|
+
await writeAutonomousConfig(profileDir2, parsed);
|
|
40185
|
+
await syncCron(root, profileDir2);
|
|
40050
40186
|
break;
|
|
40051
40187
|
}
|
|
40052
40188
|
case "approvals": {
|
|
40053
40189
|
if (req.approved && !req.byIsAdmin) {
|
|
40054
|
-
const authors = await readJsonMap(
|
|
40190
|
+
const authors = await readJsonMap(join17(root, AUTHORS_REL));
|
|
40055
40191
|
if (entryBy(authors, req.taskId) === by) {
|
|
40056
40192
|
return { ok: false, code: "APPROVAL_SELF", failedId: req.taskId, error: "self-approval blocked" };
|
|
40057
40193
|
}
|
|
40058
40194
|
}
|
|
40059
|
-
const map = parseJson(await readText(
|
|
40195
|
+
const map = parseJson(await readText(join17(root, APPROVALS_REL), "{}")) ?? {};
|
|
40060
40196
|
if (req.approved) map[req.taskId] = { approved: true, by, byLabel: req.byLabel ?? by, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
40061
40197
|
else delete map[req.taskId];
|
|
40062
|
-
await
|
|
40198
|
+
await writeFile4(join17(root, APPROVALS_REL), JSON.stringify(map, null, 2) + "\n", "utf8");
|
|
40063
40199
|
break;
|
|
40064
40200
|
}
|
|
40065
40201
|
case "approvalsBatch": {
|
|
40066
40202
|
if (!req.byIsAdmin) {
|
|
40067
|
-
const authors = await readJsonMap(
|
|
40203
|
+
const authors = await readJsonMap(join17(root, AUTHORS_REL));
|
|
40068
40204
|
const selfId = req.approve.find((id) => entryBy(authors, id) === by);
|
|
40069
40205
|
if (selfId) return { ok: false, code: "APPROVAL_SELF", failedId: selfId, error: "self-approval blocked" };
|
|
40070
40206
|
}
|
|
40071
|
-
const map = parseJson(await readText(
|
|
40207
|
+
const map = parseJson(await readText(join17(root, APPROVALS_REL), "{}")) ?? {};
|
|
40072
40208
|
const at2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
40073
40209
|
const byLabel = req.byLabel ?? by;
|
|
40074
40210
|
for (const taskId of req.approve) map[taskId] = { approved: true, by, byLabel, at: at2 };
|
|
40075
40211
|
for (const taskId of req.unapprove) delete map[taskId];
|
|
40076
|
-
await
|
|
40212
|
+
await writeFile4(join17(root, APPROVALS_REL), JSON.stringify(map, null, 2) + "\n", "utf8");
|
|
40077
40213
|
break;
|
|
40078
40214
|
}
|
|
40079
40215
|
case "userTodo": {
|
|
40080
|
-
const cur = await readText(
|
|
40216
|
+
const cur = await readText(join17(root, BOOK_FILES.userTodo));
|
|
40081
40217
|
const ids = [...cur.matchAll(/REQ-(\d{4})-(\d{4})/g)];
|
|
40082
40218
|
const maxGroup = ids.reduce((m2, g2) => Math.max(m2, Number(g2[1])), 0);
|
|
40083
40219
|
const id = `REQ-${String(maxGroup + 1).padStart(4, "0")}-0001`;
|
|
40084
40220
|
const row = `| ${id} | | | ${tableCell(req.content)} |
|
|
40085
40221
|
`;
|
|
40086
|
-
await
|
|
40087
|
-
const authors = await readJsonMap(
|
|
40222
|
+
await writeFile4(join17(root, BOOK_FILES.userTodo), cur.replace(/\s*$/, "\n") + row, "utf8");
|
|
40223
|
+
const authors = await readJsonMap(join17(root, AUTHORS_REL));
|
|
40088
40224
|
authors[id] = { by, byLabel: req.byLabel ?? by, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
40089
|
-
await
|
|
40225
|
+
await writeFile4(join17(root, AUTHORS_REL), JSON.stringify(authors, null, 2) + "\n", "utf8");
|
|
40090
40226
|
break;
|
|
40091
40227
|
}
|
|
40092
40228
|
case "bookSave": {
|
|
40093
40229
|
const file = BOOK_FILES[req.book];
|
|
40094
|
-
const prev = tableRowsById(await readText(
|
|
40230
|
+
const prev = tableRowsById(await readText(join17(root, file)));
|
|
40095
40231
|
const next = tableRowsById(req.content);
|
|
40096
|
-
const authors = await readJsonMap(
|
|
40232
|
+
const authors = await readJsonMap(join17(root, AUTHORS_REL));
|
|
40097
40233
|
const at2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
40098
40234
|
const byLabel = req.byLabel ?? by;
|
|
40099
40235
|
for (const [id, cells] of next) {
|
|
40100
40236
|
if (prev.get(id) !== cells) authors[id] = { by, byLabel, at: at2 };
|
|
40101
40237
|
}
|
|
40102
|
-
await
|
|
40103
|
-
await
|
|
40238
|
+
await writeFile4(join17(root, file), req.content, "utf8");
|
|
40239
|
+
await writeFile4(join17(root, AUTHORS_REL), JSON.stringify(authors, null, 2) + "\n", "utf8");
|
|
40104
40240
|
break;
|
|
40105
40241
|
}
|
|
40106
40242
|
case "cron": {
|
|
40107
|
-
if (req.action === "install") await installCron(root);
|
|
40243
|
+
if (req.action === "install") await installCron(root, profileDir2);
|
|
40108
40244
|
else await uninstallCron(root);
|
|
40109
40245
|
break;
|
|
40110
40246
|
}
|
|
40111
40247
|
}
|
|
40112
|
-
return { ok: true, status: await getAutonomousStatus(root) };
|
|
40248
|
+
return { ok: true, status: await getAutonomousStatus(root, profileDir2) };
|
|
40113
40249
|
} catch (err) {
|
|
40114
40250
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
40115
40251
|
}
|
|
40116
40252
|
}
|
|
40117
40253
|
|
|
40118
40254
|
// src/core/network-probe.ts
|
|
40119
|
-
import { existsSync as existsSync11, readFileSync as
|
|
40255
|
+
import { existsSync as existsSync11, readFileSync as readFileSync15 } from "fs";
|
|
40120
40256
|
import { connect } from "net";
|
|
40121
40257
|
var CANARY_TARGETS = [
|
|
40122
40258
|
{ host: "1.1.1.1", port: 443 },
|
|
@@ -40126,7 +40262,7 @@ var PROBE_TIMEOUT_MS = 2500;
|
|
|
40126
40262
|
function detectContainerized() {
|
|
40127
40263
|
try {
|
|
40128
40264
|
if (existsSync11("/.dockerenv")) return true;
|
|
40129
|
-
const cgroup =
|
|
40265
|
+
const cgroup = readFileSync15("/proc/1/cgroup", "utf8");
|
|
40130
40266
|
return /docker|containerd|kubepods|libpod/.test(cgroup);
|
|
40131
40267
|
} catch {
|
|
40132
40268
|
return false;
|
|
@@ -40160,7 +40296,7 @@ function probeInbound() {
|
|
|
40160
40296
|
const isExposed = (path, loopbackHexPrefixes) => {
|
|
40161
40297
|
let body;
|
|
40162
40298
|
try {
|
|
40163
|
-
body =
|
|
40299
|
+
body = readFileSync15(path, "utf8");
|
|
40164
40300
|
} catch {
|
|
40165
40301
|
return false;
|
|
40166
40302
|
}
|
|
@@ -40195,7 +40331,7 @@ async function probeNetwork() {
|
|
|
40195
40331
|
// src/core/git-auth.ts
|
|
40196
40332
|
import { writeFileSync as writeFileSync8 } from "fs";
|
|
40197
40333
|
import { homedir as homedir4 } from "os";
|
|
40198
|
-
import { join as
|
|
40334
|
+
import { join as join18 } from "path";
|
|
40199
40335
|
function tokenTarget(method) {
|
|
40200
40336
|
if (method === "gitlab-group-token") {
|
|
40201
40337
|
return { host: process.env.FOURPM_GIT_HOST || "gitlab.com", user: "oauth2" };
|
|
@@ -40213,7 +40349,7 @@ function applyGitAuth(method) {
|
|
|
40213
40349
|
try {
|
|
40214
40350
|
const line = `https://${target.user}:${token}@${target.host}
|
|
40215
40351
|
`;
|
|
40216
|
-
writeFileSync8(
|
|
40352
|
+
writeFileSync8(join18(homedir4(), ".git-credentials"), line, { encoding: "utf8", mode: 384 });
|
|
40217
40353
|
return true;
|
|
40218
40354
|
} catch {
|
|
40219
40355
|
return false;
|
|
@@ -40451,7 +40587,7 @@ function formatTimestampInZone(date, timeZone) {
|
|
|
40451
40587
|
|
|
40452
40588
|
// src/ui/slash-commands.ts
|
|
40453
40589
|
import { existsSync as existsSync12 } from "fs";
|
|
40454
|
-
import { join as
|
|
40590
|
+
import { join as join19 } from "path";
|
|
40455
40591
|
var CONFIG_KEYS = [
|
|
40456
40592
|
"aiCli",
|
|
40457
40593
|
"claudeHome",
|
|
@@ -40463,7 +40599,7 @@ var CONFIG_KEYS = [
|
|
|
40463
40599
|
"aiEnv"
|
|
40464
40600
|
];
|
|
40465
40601
|
function configPath(profileDir2) {
|
|
40466
|
-
return
|
|
40602
|
+
return join19(profileDir2, "config.json");
|
|
40467
40603
|
}
|
|
40468
40604
|
function parseConfigAssignment(key, valueTokens) {
|
|
40469
40605
|
const value = valueTokens.join(" ").trim();
|
|
@@ -40598,7 +40734,7 @@ function runOutput(ctx) {
|
|
|
40598
40734
|
}
|
|
40599
40735
|
function runLogs(ctx) {
|
|
40600
40736
|
const n = Math.max(1, Math.min(200, Number(ctx.args[0]) || 20));
|
|
40601
|
-
const dir2 =
|
|
40737
|
+
const dir2 = join19(ctx.info.profileDir, "logs");
|
|
40602
40738
|
const lines = readRecentLogLines(dir2, n);
|
|
40603
40739
|
if (lines.length === 0) {
|
|
40604
40740
|
ctx.print(t("slash.logs.none"));
|
|
@@ -41438,6 +41574,8 @@ function rejectByReview(ctx, commandId, origin, prompt, reasons) {
|
|
|
41438
41574
|
|
|
41439
41575
|
// src/core/autonomous-cycle.ts
|
|
41440
41576
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
41577
|
+
import { appendFile as appendFile2, mkdir as mkdir3, readFile as readFile5, readdir as readdir2, rm as rm2, stat } from "fs/promises";
|
|
41578
|
+
import { join as join20 } from "path";
|
|
41441
41579
|
function buildAutonomousCyclePrompt() {
|
|
41442
41580
|
return `You are running UNATTENDED as the 4PM autonomous cycle (headless, one tick). Do EXACTLY ONE small
|
|
41443
41581
|
work cycle with the steps below, then STOP. Keep each cycle to ONE small task so you never run out of
|
|
@@ -41520,20 +41658,165 @@ non-fast-forward \u21D2 \`git pull --rebase origin "$BASE"\` then push again).
|
|
|
41520
41658
|
|
|
41521
41659
|
## Step 8 \u2014 Stop
|
|
41522
41660
|
Print a one-line summary (task id + PR link or incident). STOP \u2014 the next cron tick runs the next cycle.
|
|
41523
|
-
The
|
|
41661
|
+
The 4PM cli serializes cycles (one at a time); you do NOT manage any run lock.`;
|
|
41524
41662
|
}
|
|
41663
|
+
var running = /* @__PURE__ */ new Set();
|
|
41525
41664
|
async function runAutonomousCycle(ctx) {
|
|
41526
|
-
const
|
|
41665
|
+
const root = ctx.physicRoot;
|
|
41666
|
+
const profileDir2 = ctx.profileDir;
|
|
41667
|
+
if (!root) {
|
|
41668
|
+
ctx.bus.autonomousDone(false, "no served project");
|
|
41669
|
+
return;
|
|
41670
|
+
}
|
|
41671
|
+
if (running.has(root)) {
|
|
41672
|
+
ctx.bus.autonomousDone(false, "a cycle is already running");
|
|
41673
|
+
return;
|
|
41674
|
+
}
|
|
41675
|
+
running.add(root);
|
|
41676
|
+
const log = (line) => dayLog(root, line);
|
|
41527
41677
|
try {
|
|
41528
|
-
|
|
41529
|
-
|
|
41678
|
+
const cfg = await readAutonomousConfig(profileDir2);
|
|
41679
|
+
await pruneLogs(root, cfg.logRetentionDays);
|
|
41680
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
41681
|
+
if (!ctx.isReady) {
|
|
41682
|
+
await log("[skip] cli offline (no WS connection)");
|
|
41683
|
+
ctx.bus.autonomousDone(false, "cli offline");
|
|
41684
|
+
return;
|
|
41685
|
+
}
|
|
41686
|
+
if (cfg.paused) {
|
|
41687
|
+
await log("[skip] paused");
|
|
41688
|
+
ctx.bus.autonomousDone(false, "paused");
|
|
41689
|
+
return;
|
|
41690
|
+
}
|
|
41691
|
+
if (isInQuietHours(cfg.quietHours)) {
|
|
41692
|
+
await log("[skip] within quiet hours");
|
|
41693
|
+
ctx.bus.autonomousDone(false, "quiet hours");
|
|
41694
|
+
return;
|
|
41695
|
+
}
|
|
41696
|
+
let hist = await readHistories(root);
|
|
41697
|
+
if (cfg.maxTicksPerDay > 0 && todayTickCount(hist, today) >= cfg.maxTicksPerDay) {
|
|
41698
|
+
await log(`[skip] reached max ticks/day (${cfg.maxTicksPerDay})`);
|
|
41699
|
+
ctx.bus.autonomousDone(false, "max ticks reached");
|
|
41700
|
+
return;
|
|
41701
|
+
}
|
|
41702
|
+
if (!await hasWork(root)) {
|
|
41703
|
+
await log("[skip] no approved USER_TODO/USER_QA/AI_TODO work, AI_PROGRESS empty");
|
|
41704
|
+
ctx.bus.autonomousDone(false, "no work");
|
|
41705
|
+
return;
|
|
41706
|
+
}
|
|
41707
|
+
if (await allAiProfilesExhausted(ctx, cfg)) {
|
|
41708
|
+
await log(`[skip] all AI profiles over usage limit (\u2265 ${cfg.maxSessionPct}%/${cfg.maxWeeklyPct}%)`);
|
|
41709
|
+
await recordRun(root, hist, "skip", "all AI profiles over usage limit", today);
|
|
41710
|
+
ctx.bus.autonomousDone(false, "usage limit");
|
|
41711
|
+
return;
|
|
41712
|
+
}
|
|
41713
|
+
hist.ticks = { day: today, count: todayTickCount(hist, today) + 1 };
|
|
41714
|
+
await writeHistories(root, hist);
|
|
41715
|
+
await log(`[run] cycle (tick ${hist.ticks.count}/${today})`);
|
|
41716
|
+
const commandId = randomUUID2();
|
|
41717
|
+
const override = cfg.model ? { model: cfg.model } : void 0;
|
|
41718
|
+
try {
|
|
41719
|
+
await runAiPrompt(ctx, buildAutonomousCyclePrompt(), commandId, "local", false, void 0, override, false, true);
|
|
41720
|
+
hist = await readHistories(root);
|
|
41721
|
+
hist.consecutiveFails = 0;
|
|
41722
|
+
await recordRun(root, hist, "success", "cycle complete", today);
|
|
41723
|
+
await log("[done] cycle complete");
|
|
41724
|
+
ctx.bus.autonomousDone(true);
|
|
41725
|
+
} catch (err) {
|
|
41726
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
41727
|
+
hist = await readHistories(root);
|
|
41728
|
+
hist.consecutiveFails += 1;
|
|
41729
|
+
await recordRun(root, hist, "failure", msg, today);
|
|
41730
|
+
await log(`[warn] cycle failed: ${msg}`);
|
|
41731
|
+
if (cfg.stopOnConsecutiveFailures > 0 && hist.consecutiveFails >= cfg.stopOnConsecutiveFailures) {
|
|
41732
|
+
await writeAutonomousConfig(profileDir2, { ...cfg, paused: true });
|
|
41733
|
+
await log(`[stop] ${hist.consecutiveFails} consecutive failures \u2192 paused`);
|
|
41734
|
+
}
|
|
41735
|
+
ctx.bus.autonomousDone(false, msg);
|
|
41736
|
+
}
|
|
41530
41737
|
} catch (err) {
|
|
41531
41738
|
ctx.bus.autonomousDone(false, err instanceof Error ? err.message : String(err));
|
|
41739
|
+
} finally {
|
|
41740
|
+
running.delete(root);
|
|
41741
|
+
}
|
|
41742
|
+
}
|
|
41743
|
+
async function dayLog(root, line) {
|
|
41744
|
+
try {
|
|
41745
|
+
const dir2 = join20(root, ".claude", "logs");
|
|
41746
|
+
await mkdir3(dir2, { recursive: true });
|
|
41747
|
+
const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
41748
|
+
const ts2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
41749
|
+
await appendFile2(join20(dir2, `autonomous-tick-${day}.log`), `${ts2} ${line}
|
|
41750
|
+
`, "utf8");
|
|
41751
|
+
} catch {
|
|
41752
|
+
}
|
|
41753
|
+
}
|
|
41754
|
+
async function pruneLogs(root, retentionDays) {
|
|
41755
|
+
if (!(retentionDays > 0)) return;
|
|
41756
|
+
try {
|
|
41757
|
+
const dir2 = join20(root, ".claude", "logs");
|
|
41758
|
+
const cutoff = Date.now() - retentionDays * 864e5;
|
|
41759
|
+
for (const f2 of await readdir2(dir2)) {
|
|
41760
|
+
if (!/^autonomous-tick-.*\.log$/.test(f2)) continue;
|
|
41761
|
+
const st2 = await stat(join20(dir2, f2)).catch(() => null);
|
|
41762
|
+
if (st2 && st2.mtimeMs < cutoff) await rm2(join20(dir2, f2), { force: true }).catch(() => void 0);
|
|
41763
|
+
}
|
|
41764
|
+
} catch {
|
|
41765
|
+
}
|
|
41766
|
+
}
|
|
41767
|
+
async function recordRun(root, hist, status, note, today) {
|
|
41768
|
+
const ts2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
41769
|
+
pushRecord(hist, { ts: ts2, status, note, tick: `${today} #${hist.ticks.count}` });
|
|
41770
|
+
await writeHistories(root, hist);
|
|
41771
|
+
}
|
|
41772
|
+
async function readJsonObject(path) {
|
|
41773
|
+
try {
|
|
41774
|
+
return JSON.parse(await readFile5(path, "utf8"));
|
|
41775
|
+
} catch {
|
|
41776
|
+
return {};
|
|
41532
41777
|
}
|
|
41533
41778
|
}
|
|
41779
|
+
async function hasWork(root) {
|
|
41780
|
+
const approvals = await readJsonObject(join20(root, ".claude/.autonomous.approvals.json"));
|
|
41781
|
+
const approved = Object.entries(approvals).filter(([, v2]) => v2 && typeof v2 === "object" && v2.approved === true).map(([k2]) => k2);
|
|
41782
|
+
const read = (f2) => readFile5(join20(root, f2), "utf8").catch(() => "");
|
|
41783
|
+
const [userTodo, userQa, aiTodo, aiProgress] = await Promise.all([
|
|
41784
|
+
read("USER_TODO.md"),
|
|
41785
|
+
read("USER_QA.md"),
|
|
41786
|
+
read("AI_TODO.md"),
|
|
41787
|
+
read("AI_PROGRESS.md")
|
|
41788
|
+
]);
|
|
41789
|
+
const anyApproved = (prefix, text) => approved.some((id) => id.startsWith(prefix) && text.includes(id));
|
|
41790
|
+
return anyApproved("REQ-", userTodo) || anyApproved("QA-", userQa) || anyApproved("TSK-", aiTodo) || /TSK-\d{4}-\d{4}/.test(aiProgress);
|
|
41791
|
+
}
|
|
41792
|
+
function hasUsableCodex(config) {
|
|
41793
|
+
const mixed = config.aiProfiles?.some(
|
|
41794
|
+
(c) => c.provider === "codex" && c.enabled !== false && Boolean(c.profile?.trim())
|
|
41795
|
+
);
|
|
41796
|
+
const legacy = (config.codexHome ?? []).some((p2) => p2.enabled !== false && Boolean(p2.profile?.trim()));
|
|
41797
|
+
return Boolean(mixed || legacy);
|
|
41798
|
+
}
|
|
41799
|
+
async function allAiProfilesExhausted(ctx, cfg) {
|
|
41800
|
+
const config = readProfileConfig(ctx.profileDir);
|
|
41801
|
+
const active = activeProvider(config);
|
|
41802
|
+
if (active === "codex" || active === "antigravity") return false;
|
|
41803
|
+
if (active === null && hasUsableCodex(config)) return false;
|
|
41804
|
+
const dirs = claudeHomeDirs(config);
|
|
41805
|
+
if (dirs.length === 0) return false;
|
|
41806
|
+
const usages = await Promise.all(dirs.map((d) => checkClaudeUsage([d]).catch(() => null)));
|
|
41807
|
+
let readAny = false;
|
|
41808
|
+
for (const u2 of usages) {
|
|
41809
|
+
if (!u2) continue;
|
|
41810
|
+
readAny = true;
|
|
41811
|
+
if (u2.session.utilizationPct < cfg.maxSessionPct && u2.weekly.utilizationPct < cfg.maxWeeklyPct) {
|
|
41812
|
+
return false;
|
|
41813
|
+
}
|
|
41814
|
+
}
|
|
41815
|
+
return readAny;
|
|
41816
|
+
}
|
|
41534
41817
|
|
|
41535
41818
|
// src/core/fs-browse.ts
|
|
41536
|
-
import { readdir as
|
|
41819
|
+
import { readdir as readdir3 } from "fs/promises";
|
|
41537
41820
|
import { resolve as resolve2, sep as sep2 } from "path";
|
|
41538
41821
|
async function listDir(path, root) {
|
|
41539
41822
|
if (!root) return { path: "", entries: [] };
|
|
@@ -41541,7 +41824,7 @@ async function listDir(path, root) {
|
|
|
41541
41824
|
const requested = resolve2(base, path && path.trim() ? path : ".");
|
|
41542
41825
|
const target = requested === base || requested.startsWith(base + sep2) ? requested : base;
|
|
41543
41826
|
try {
|
|
41544
|
-
const dirents = await
|
|
41827
|
+
const dirents = await readdir3(target, { withFileTypes: true });
|
|
41545
41828
|
const entries2 = dirents.map((d) => ({
|
|
41546
41829
|
name: d.name,
|
|
41547
41830
|
type: d.isDirectory() ? "dir" : "file"
|
|
@@ -41556,7 +41839,7 @@ async function listDir(path, root) {
|
|
|
41556
41839
|
}
|
|
41557
41840
|
|
|
41558
41841
|
// src/core/fs-read.ts
|
|
41559
|
-
import { readFile as
|
|
41842
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
41560
41843
|
import { resolve as resolve3, sep as sep3 } from "path";
|
|
41561
41844
|
var MAX_BYTES = 256 * 1024;
|
|
41562
41845
|
function resolveInRoot(root, relPath) {
|
|
@@ -41571,7 +41854,7 @@ async function readWorkerFile(root, path) {
|
|
|
41571
41854
|
return { path, content: "", truncated: false };
|
|
41572
41855
|
}
|
|
41573
41856
|
try {
|
|
41574
|
-
const buf = await
|
|
41857
|
+
const buf = await readFile6(target);
|
|
41575
41858
|
const truncated = buf.byteLength > MAX_BYTES;
|
|
41576
41859
|
const content = buf.subarray(0, MAX_BYTES).toString("utf8");
|
|
41577
41860
|
return { path: target, content, truncated };
|
|
@@ -41581,7 +41864,7 @@ async function readWorkerFile(root, path) {
|
|
|
41581
41864
|
}
|
|
41582
41865
|
|
|
41583
41866
|
// src/core/fs-write.ts
|
|
41584
|
-
import { mkdir as
|
|
41867
|
+
import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
|
|
41585
41868
|
import { dirname as dirname4, resolve as resolve4, sep as sep4 } from "path";
|
|
41586
41869
|
var MAX_BYTES2 = 2 * 1024 * 1024;
|
|
41587
41870
|
function resolveInRoot2(root, relPath) {
|
|
@@ -41600,8 +41883,8 @@ async function writeWorkerFile(root, relPath, content) {
|
|
|
41600
41883
|
return { ok: false, path: target, bytes: 0, error: `content too large (max ${MAX_BYTES2} bytes)` };
|
|
41601
41884
|
}
|
|
41602
41885
|
try {
|
|
41603
|
-
await
|
|
41604
|
-
await
|
|
41886
|
+
await mkdir4(dirname4(target), { recursive: true });
|
|
41887
|
+
await writeFile5(target, content, "utf8");
|
|
41605
41888
|
return { ok: true, path: target, bytes };
|
|
41606
41889
|
} catch (err) {
|
|
41607
41890
|
return { ok: false, path: target, bytes: 0, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -41609,7 +41892,7 @@ async function writeWorkerFile(root, relPath, content) {
|
|
|
41609
41892
|
}
|
|
41610
41893
|
|
|
41611
41894
|
// src/core/fs-mutate.ts
|
|
41612
|
-
import { mkdir as
|
|
41895
|
+
import { mkdir as mkdir5, writeFile as writeFile6, rename, rm as rm3, stat as stat2 } from "fs/promises";
|
|
41613
41896
|
import { basename as basename3, dirname as dirname5, resolve as resolve5, sep as sep5 } from "path";
|
|
41614
41897
|
var MAX_BYTES3 = 2 * 1024 * 1024;
|
|
41615
41898
|
function resolveInRoot3(root, rel) {
|
|
@@ -41619,14 +41902,14 @@ function resolveInRoot3(root, rel) {
|
|
|
41619
41902
|
}
|
|
41620
41903
|
async function isDir(p2) {
|
|
41621
41904
|
try {
|
|
41622
|
-
return (await
|
|
41905
|
+
return (await stat2(p2)).isDirectory();
|
|
41623
41906
|
} catch {
|
|
41624
41907
|
return false;
|
|
41625
41908
|
}
|
|
41626
41909
|
}
|
|
41627
41910
|
async function exists(p2) {
|
|
41628
41911
|
try {
|
|
41629
|
-
await
|
|
41912
|
+
await stat2(p2);
|
|
41630
41913
|
return true;
|
|
41631
41914
|
} catch {
|
|
41632
41915
|
return false;
|
|
@@ -41640,7 +41923,7 @@ async function mutateFs(root, req) {
|
|
|
41640
41923
|
case "mkdir": {
|
|
41641
41924
|
const target = resolveInRoot3(root, req.path ?? "");
|
|
41642
41925
|
if (!target || target === resolve5(root)) return escaped(req.path ?? "");
|
|
41643
|
-
await
|
|
41926
|
+
await mkdir5(target, { recursive: true });
|
|
41644
41927
|
return { ok: true, path: target };
|
|
41645
41928
|
}
|
|
41646
41929
|
case "create": {
|
|
@@ -41651,8 +41934,8 @@ async function mutateFs(root, req) {
|
|
|
41651
41934
|
if (Buffer.byteLength(content, "utf8") > MAX_BYTES3) {
|
|
41652
41935
|
return { ok: false, path: target, error: `content too large (max ${MAX_BYTES3} bytes)` };
|
|
41653
41936
|
}
|
|
41654
|
-
await
|
|
41655
|
-
await
|
|
41937
|
+
await mkdir5(dirname5(target), { recursive: true });
|
|
41938
|
+
await writeFile6(target, content, "utf8");
|
|
41656
41939
|
return { ok: true, path: target };
|
|
41657
41940
|
}
|
|
41658
41941
|
case "move": {
|
|
@@ -41663,7 +41946,7 @@ async function mutateFs(root, req) {
|
|
|
41663
41946
|
if (await isDir(to2)) to2 = resolve5(to2, basename3(from));
|
|
41664
41947
|
if (to2 !== resolve5(root) && !to2.startsWith(resolve5(root) + sep5)) return escaped(req.to ?? "");
|
|
41665
41948
|
if (await exists(to2)) return { ok: false, path: to2, error: "destination already exists" };
|
|
41666
|
-
await
|
|
41949
|
+
await mkdir5(dirname5(to2), { recursive: true });
|
|
41667
41950
|
await rename(from, to2);
|
|
41668
41951
|
return { ok: true, path: to2 };
|
|
41669
41952
|
}
|
|
@@ -41671,7 +41954,7 @@ async function mutateFs(root, req) {
|
|
|
41671
41954
|
const target = resolveInRoot3(root, req.path ?? "");
|
|
41672
41955
|
if (!target) return escaped(req.path ?? "");
|
|
41673
41956
|
if (target === resolve5(root)) return { ok: false, path: target, error: "cannot delete the project root" };
|
|
41674
|
-
await
|
|
41957
|
+
await rm3(target, { recursive: true, force: true });
|
|
41675
41958
|
return { ok: true, path: target };
|
|
41676
41959
|
}
|
|
41677
41960
|
default:
|
|
@@ -41683,7 +41966,7 @@ async function mutateFs(root, req) {
|
|
|
41683
41966
|
}
|
|
41684
41967
|
|
|
41685
41968
|
// src/core/fs-transfer.ts
|
|
41686
|
-
import { mkdir as
|
|
41969
|
+
import { mkdir as mkdir6, readFile as readFile7, stat as stat3, writeFile as writeFile7 } from "fs/promises";
|
|
41687
41970
|
import { basename as basename4, dirname as dirname6, extname, resolve as resolve6, sep as sep6 } from "path";
|
|
41688
41971
|
function resolveInRoot4(root, relPath) {
|
|
41689
41972
|
if (!root) return null;
|
|
@@ -41727,8 +42010,8 @@ async function uploadBinaryFile(root, relPath, contentBase64) {
|
|
|
41727
42010
|
return { ok: false, path: target, bytes: 0, error: `file too large (max ${FS_TRANSFER_MAX_BYTES} bytes)` };
|
|
41728
42011
|
}
|
|
41729
42012
|
try {
|
|
41730
|
-
await
|
|
41731
|
-
await
|
|
42013
|
+
await mkdir6(dirname6(target), { recursive: true });
|
|
42014
|
+
await writeFile7(target, buf);
|
|
41732
42015
|
return { ok: true, path: target, bytes: buf.byteLength };
|
|
41733
42016
|
} catch (err) {
|
|
41734
42017
|
return { ok: false, path: target, bytes: 0, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -41738,12 +42021,12 @@ async function downloadBinaryFile(root, relPath) {
|
|
|
41738
42021
|
const target = resolveInRoot4(root, relPath);
|
|
41739
42022
|
if (!target) return { error: "path escapes the project root" };
|
|
41740
42023
|
try {
|
|
41741
|
-
const info = await
|
|
42024
|
+
const info = await stat3(target);
|
|
41742
42025
|
if (!info.isFile()) return { error: "not a file" };
|
|
41743
42026
|
if (info.size > FS_TRANSFER_MAX_BYTES) {
|
|
41744
42027
|
return { error: `file too large (max ${FS_TRANSFER_MAX_BYTES} bytes)` };
|
|
41745
42028
|
}
|
|
41746
|
-
const buf = await
|
|
42029
|
+
const buf = await readFile7(target);
|
|
41747
42030
|
return {
|
|
41748
42031
|
contentBase64: buf.toString("base64"),
|
|
41749
42032
|
contentType: guessMime(target),
|
|
@@ -41802,7 +42085,7 @@ function handleFsChannels(ctx, message, payload) {
|
|
|
41802
42085
|
}
|
|
41803
42086
|
|
|
41804
42087
|
// src/core/git-diff.ts
|
|
41805
|
-
import { readFile as
|
|
42088
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
41806
42089
|
import { execFile as execFile6 } from "child_process";
|
|
41807
42090
|
import { basename as basename5, dirname as dirname7, resolve as resolve7, sep as sep7 } from "path";
|
|
41808
42091
|
import { promisify as promisify6 } from "util";
|
|
@@ -41829,7 +42112,7 @@ async function gitDiff(root, path) {
|
|
|
41829
42112
|
const base = basename5(target);
|
|
41830
42113
|
let newBuf = null;
|
|
41831
42114
|
try {
|
|
41832
|
-
newBuf = await
|
|
42115
|
+
newBuf = await readFile8(target);
|
|
41833
42116
|
} catch {
|
|
41834
42117
|
}
|
|
41835
42118
|
let oldBuf = null;
|
|
@@ -41896,18 +42179,18 @@ ${stderr}`;
|
|
|
41896
42179
|
|
|
41897
42180
|
// src/core/git-ssh-key.ts
|
|
41898
42181
|
import { execFile as execFile8 } from "child_process";
|
|
41899
|
-
import { readFile as
|
|
41900
|
-
import { join as
|
|
42182
|
+
import { readFile as readFile9, rm as rm4, mkdir as mkdir7 } from "fs/promises";
|
|
42183
|
+
import { join as join21 } from "path";
|
|
41901
42184
|
import { promisify as promisify8 } from "util";
|
|
41902
42185
|
var run8 = promisify8(execFile8);
|
|
41903
42186
|
function keyPaths(profileDir2) {
|
|
41904
|
-
const dir2 =
|
|
41905
|
-
const key =
|
|
42187
|
+
const dir2 = join21(profileDir2, ".ssh");
|
|
42188
|
+
const key = join21(dir2, "id_4pm");
|
|
41906
42189
|
return { dir: dir2, key, pub: `${key}.pub` };
|
|
41907
42190
|
}
|
|
41908
42191
|
async function readPublicKey(pub) {
|
|
41909
42192
|
try {
|
|
41910
|
-
return (await
|
|
42193
|
+
return (await readFile9(pub, "utf8")).trim();
|
|
41911
42194
|
} catch {
|
|
41912
42195
|
return null;
|
|
41913
42196
|
}
|
|
@@ -41929,9 +42212,9 @@ async function configureGit(key) {
|
|
|
41929
42212
|
}
|
|
41930
42213
|
async function generate(profileDir2) {
|
|
41931
42214
|
const { dir: dir2, key, pub } = keyPaths(profileDir2);
|
|
41932
|
-
await
|
|
41933
|
-
await
|
|
41934
|
-
await
|
|
42215
|
+
await mkdir7(dir2, { recursive: true });
|
|
42216
|
+
await rm4(key, { force: true });
|
|
42217
|
+
await rm4(pub, { force: true });
|
|
41935
42218
|
await run8("ssh-keygen", ["-t", "ed25519", "-f", key, "-N", "", "-C", "4pm-deploy-key"], {
|
|
41936
42219
|
timeout: 15e3
|
|
41937
42220
|
});
|
|
@@ -41940,8 +42223,8 @@ async function generate(profileDir2) {
|
|
|
41940
42223
|
}
|
|
41941
42224
|
async function remove(profileDir2) {
|
|
41942
42225
|
const { key, pub } = keyPaths(profileDir2);
|
|
41943
|
-
await
|
|
41944
|
-
await
|
|
42226
|
+
await rm4(key, { force: true });
|
|
42227
|
+
await rm4(pub, { force: true });
|
|
41945
42228
|
await run8("git", ["config", "--global", "--unset", "core.sshCommand"], { timeout: 5e3 }).catch(
|
|
41946
42229
|
() => void 0
|
|
41947
42230
|
);
|
|
@@ -42115,7 +42398,7 @@ function handleGitChannels(ctx, message, payload) {
|
|
|
42115
42398
|
}
|
|
42116
42399
|
|
|
42117
42400
|
// src/core/ws-client/handlers/misc.ts
|
|
42118
|
-
import { join as
|
|
42401
|
+
import { join as join22 } from "path";
|
|
42119
42402
|
|
|
42120
42403
|
// src/core/outbound-review.ts
|
|
42121
42404
|
function compilePatterns(sources, fallback) {
|
|
@@ -42197,7 +42480,7 @@ function handleMiscChannels(ctx, message, payload) {
|
|
|
42197
42480
|
}
|
|
42198
42481
|
case WsChannels.LOG_READ: {
|
|
42199
42482
|
const limit = Math.min(Math.max(payload.limit ?? 200, 1), 2e3);
|
|
42200
|
-
const lines = readRecentLogLines(
|
|
42483
|
+
const lines = readRecentLogLines(join22(ctx.profileDir, "logs"), limit);
|
|
42201
42484
|
ctx.send(WsChannels.LOG_READ, { lines }, message.id);
|
|
42202
42485
|
return true;
|
|
42203
42486
|
}
|
|
@@ -42229,14 +42512,14 @@ function handleMiscChannels(ctx, message, payload) {
|
|
|
42229
42512
|
|
|
42230
42513
|
// src/core/ws-client/handlers/project.ts
|
|
42231
42514
|
import { existsSync as existsSync13, mkdirSync as mkdirSync6, renameSync, rmSync as rmSync6 } from "fs";
|
|
42232
|
-
import { join as
|
|
42515
|
+
import { join as join23 } from "path";
|
|
42233
42516
|
function handleProjectChannels(ctx, message, payload) {
|
|
42234
42517
|
switch (message.channel) {
|
|
42235
42518
|
case WsChannels.PHYSIC_SYNC: {
|
|
42236
42519
|
const sync = payload;
|
|
42237
42520
|
const oldName = sync.oldName;
|
|
42238
|
-
const oldPath = oldName ?
|
|
42239
|
-
const newPath =
|
|
42521
|
+
const oldPath = oldName ? join23(ctx.profileDir, oldName) : null;
|
|
42522
|
+
const newPath = join23(ctx.profileDir, sync.newName);
|
|
42240
42523
|
if (oldName) {
|
|
42241
42524
|
const oldRoot = ctx.physicFolderPath(oldName);
|
|
42242
42525
|
if (oldRoot) void uninstallCron(oldRoot).catch(() => void 0);
|
|
@@ -42258,7 +42541,7 @@ function handleProjectChannels(ctx, message, payload) {
|
|
|
42258
42541
|
if (del.name) {
|
|
42259
42542
|
const delRoot = ctx.physicFolderPath(del.name);
|
|
42260
42543
|
if (delRoot) void uninstallCron(delRoot).catch(() => void 0);
|
|
42261
|
-
rmSync6(
|
|
42544
|
+
rmSync6(join23(ctx.profileDir, del.name), { recursive: true, force: true });
|
|
42262
42545
|
ctx.bus.log(t("project.folderDeleted", { name: del.name }));
|
|
42263
42546
|
}
|
|
42264
42547
|
void manageSshKey("delete", ctx.profileDir).catch(() => void 0);
|
|
@@ -42527,8 +42810,8 @@ function handleToolsChannels(ctx, message, payload) {
|
|
|
42527
42810
|
}
|
|
42528
42811
|
|
|
42529
42812
|
// src/core/agents.ts
|
|
42530
|
-
import { readFile as
|
|
42531
|
-
import { join as
|
|
42813
|
+
import { readFile as readFile10, writeFile as writeFile8, mkdir as mkdir8, readdir as readdir4, rm as rm5 } from "fs/promises";
|
|
42814
|
+
import { join as join24 } from "path";
|
|
42532
42815
|
var AGENTS_REL = ".claude/agents";
|
|
42533
42816
|
var SKILLS_REL = ".claude/skills";
|
|
42534
42817
|
var MEMORY_DIRS = /* @__PURE__ */ new Set(["short-memory", "long-memory"]);
|
|
@@ -42537,7 +42820,7 @@ function safeName(name) {
|
|
|
42537
42820
|
}
|
|
42538
42821
|
async function readText2(path, fallback = "") {
|
|
42539
42822
|
try {
|
|
42540
|
-
return await
|
|
42823
|
+
return await readFile10(path, "utf8");
|
|
42541
42824
|
} catch {
|
|
42542
42825
|
return fallback;
|
|
42543
42826
|
}
|
|
@@ -42552,17 +42835,17 @@ async function listAgents(root) {
|
|
|
42552
42835
|
const subagents = [];
|
|
42553
42836
|
const skills = [];
|
|
42554
42837
|
try {
|
|
42555
|
-
for (const e of await
|
|
42838
|
+
for (const e of await readdir4(join24(root, AGENTS_REL), { withFileTypes: true })) {
|
|
42556
42839
|
if (!e.isFile() || !e.name.endsWith(".md") || MEMORY_DIRS.has(e.name)) continue;
|
|
42557
|
-
const fm = frontmatter(await readText2(
|
|
42840
|
+
const fm = frontmatter(await readText2(join24(root, AGENTS_REL, e.name)));
|
|
42558
42841
|
subagents.push({ name: e.name.replace(/\.md$/, ""), description: fm.description });
|
|
42559
42842
|
}
|
|
42560
42843
|
} catch {
|
|
42561
42844
|
}
|
|
42562
42845
|
try {
|
|
42563
|
-
for (const e of await
|
|
42846
|
+
for (const e of await readdir4(join24(root, SKILLS_REL), { withFileTypes: true })) {
|
|
42564
42847
|
if (!e.isDirectory()) continue;
|
|
42565
|
-
const fm = frontmatter(await readText2(
|
|
42848
|
+
const fm = frontmatter(await readText2(join24(root, SKILLS_REL, e.name, "SKILL.md")));
|
|
42566
42849
|
skills.push({ name: e.name, description: fm.description });
|
|
42567
42850
|
}
|
|
42568
42851
|
} catch {
|
|
@@ -42573,15 +42856,15 @@ async function listAgents(root) {
|
|
|
42573
42856
|
}
|
|
42574
42857
|
function itemPath(root, kind, name) {
|
|
42575
42858
|
const n = safeName(name);
|
|
42576
|
-
return kind === "subagent" ?
|
|
42859
|
+
return kind === "subagent" ? join24(root, AGENTS_REL, `${n}.md`) : join24(root, SKILLS_REL, n, "SKILL.md");
|
|
42577
42860
|
}
|
|
42578
42861
|
async function readAgent(root, kind, name) {
|
|
42579
42862
|
const content = await readText2(itemPath(root, kind, name));
|
|
42580
42863
|
if (kind !== "subagent") return { content };
|
|
42581
42864
|
const n = safeName(name);
|
|
42582
42865
|
const [short, long] = await Promise.all([
|
|
42583
|
-
readText2(
|
|
42584
|
-
readText2(
|
|
42866
|
+
readText2(join24(root, AGENTS_REL, "short-memory", `${n}.md`)),
|
|
42867
|
+
readText2(join24(root, AGENTS_REL, "long-memory", `${n}.md`))
|
|
42585
42868
|
]);
|
|
42586
42869
|
return { content, memory: { short, long } };
|
|
42587
42870
|
}
|
|
@@ -42590,14 +42873,14 @@ async function writeAgent(root, req) {
|
|
|
42590
42873
|
if (!n) return { ok: false, error: "invalid name" };
|
|
42591
42874
|
try {
|
|
42592
42875
|
if (req.action === "delete") {
|
|
42593
|
-
if (req.kind === "subagent") await
|
|
42594
|
-
else await
|
|
42876
|
+
if (req.kind === "subagent") await rm5(itemPath(root, "subagent", n), { force: true });
|
|
42877
|
+
else await rm5(join24(root, SKILLS_REL, n), { recursive: true, force: true });
|
|
42595
42878
|
return { ok: true };
|
|
42596
42879
|
}
|
|
42597
42880
|
if (req.content === void 0) return { ok: false, error: "content is required" };
|
|
42598
42881
|
const path = itemPath(root, req.kind, n);
|
|
42599
|
-
await
|
|
42600
|
-
await
|
|
42882
|
+
await mkdir8(join24(path, ".."), { recursive: true });
|
|
42883
|
+
await writeFile8(path, req.content, "utf8");
|
|
42601
42884
|
return { ok: true };
|
|
42602
42885
|
} catch (err) {
|
|
42603
42886
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -42605,21 +42888,21 @@ async function writeAgent(root, req) {
|
|
|
42605
42888
|
}
|
|
42606
42889
|
|
|
42607
42890
|
// src/core/secrets.ts
|
|
42608
|
-
import { readFile as
|
|
42609
|
-
import { join as
|
|
42891
|
+
import { readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
|
|
42892
|
+
import { join as join25 } from "path";
|
|
42610
42893
|
var SECURITY_REL = "AI_SECURITY.md";
|
|
42611
42894
|
var PLACEHOLDER_REL = "AI_PLACEHOLDER.md";
|
|
42612
42895
|
var SECRETS_REL = "project.secrets.json";
|
|
42613
42896
|
async function readText3(path, fallback = "") {
|
|
42614
42897
|
try {
|
|
42615
|
-
return await
|
|
42898
|
+
return await readFile11(path, "utf8");
|
|
42616
42899
|
} catch {
|
|
42617
42900
|
return fallback;
|
|
42618
42901
|
}
|
|
42619
42902
|
}
|
|
42620
42903
|
async function readSecretsObject(root) {
|
|
42621
42904
|
try {
|
|
42622
|
-
const v2 = JSON.parse(await
|
|
42905
|
+
const v2 = JSON.parse(await readFile11(join25(root, SECRETS_REL), "utf8"));
|
|
42623
42906
|
return v2 && typeof v2 === "object" ? v2 : {};
|
|
42624
42907
|
} catch {
|
|
42625
42908
|
return {};
|
|
@@ -42638,8 +42921,8 @@ function placeholderKeys(text) {
|
|
|
42638
42921
|
}
|
|
42639
42922
|
async function readSecrets(root) {
|
|
42640
42923
|
const [security, placeholder, secrets] = await Promise.all([
|
|
42641
|
-
readText3(
|
|
42642
|
-
readText3(
|
|
42924
|
+
readText3(join25(root, SECURITY_REL)),
|
|
42925
|
+
readText3(join25(root, PLACEHOLDER_REL)),
|
|
42643
42926
|
readSecretsObject(root)
|
|
42644
42927
|
]);
|
|
42645
42928
|
const names = /* @__PURE__ */ new Set([...placeholderKeys(placeholder), ...Object.keys(secrets)]);
|
|
@@ -42652,14 +42935,14 @@ async function readSecrets(root) {
|
|
|
42652
42935
|
async function writeSecrets(root, req) {
|
|
42653
42936
|
try {
|
|
42654
42937
|
if (req.kind === "security") {
|
|
42655
|
-
await
|
|
42938
|
+
await writeFile9(join25(root, SECURITY_REL), req.content, "utf8");
|
|
42656
42939
|
} else if (req.kind === "placeholder") {
|
|
42657
|
-
await
|
|
42940
|
+
await writeFile9(join25(root, PLACEHOLDER_REL), req.content, "utf8");
|
|
42658
42941
|
} else {
|
|
42659
42942
|
const secrets = await readSecretsObject(root);
|
|
42660
42943
|
if (req.kind === "secret") secrets[req.key] = req.value;
|
|
42661
42944
|
else delete secrets[req.key];
|
|
42662
|
-
await
|
|
42945
|
+
await writeFile9(join25(root, SECRETS_REL), JSON.stringify(secrets, null, 2) + "\n", "utf8");
|
|
42663
42946
|
}
|
|
42664
42947
|
return { ok: true };
|
|
42665
42948
|
} catch (err) {
|
|
@@ -42668,18 +42951,18 @@ async function writeSecrets(root, req) {
|
|
|
42668
42951
|
}
|
|
42669
42952
|
|
|
42670
42953
|
// src/core/agent-tools.ts
|
|
42671
|
-
import { readFile as
|
|
42672
|
-
import { dirname as dirname8, join as
|
|
42954
|
+
import { readFile as readFile12, writeFile as writeFile10, mkdir as mkdir9 } from "fs/promises";
|
|
42955
|
+
import { dirname as dirname8, join as join26 } from "path";
|
|
42673
42956
|
var SHARED_REL = ".claude/settings.json";
|
|
42674
42957
|
var LOCAL_REL = ".claude/settings.local.json";
|
|
42675
42958
|
var SECRETS_DENY = "Read(./project.secrets.json)";
|
|
42676
42959
|
var MODES = ["default", "acceptEdits", "plan", "bypassPermissions"];
|
|
42677
42960
|
function fileFor(root, scope) {
|
|
42678
|
-
return
|
|
42961
|
+
return join26(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
|
|
42679
42962
|
}
|
|
42680
42963
|
async function readSettingsObject(path) {
|
|
42681
42964
|
try {
|
|
42682
|
-
const v2 = JSON.parse(await
|
|
42965
|
+
const v2 = JSON.parse(await readFile12(path, "utf8"));
|
|
42683
42966
|
return v2 && typeof v2 === "object" ? v2 : {};
|
|
42684
42967
|
} catch {
|
|
42685
42968
|
return {};
|
|
@@ -42702,8 +42985,8 @@ async function writeAgentTools(root, scope, permissions) {
|
|
|
42702
42985
|
const next = normalizePermissions(permissions);
|
|
42703
42986
|
if (scope === "shared" && !next.deny.includes(SECRETS_DENY)) next.deny = [...next.deny, SECRETS_DENY];
|
|
42704
42987
|
obj.permissions = next;
|
|
42705
|
-
await
|
|
42706
|
-
await
|
|
42988
|
+
await mkdir9(dirname8(path), { recursive: true });
|
|
42989
|
+
await writeFile10(path, JSON.stringify(obj, null, 2) + "\n", "utf8");
|
|
42707
42990
|
return { ok: true };
|
|
42708
42991
|
} catch (err) {
|
|
42709
42992
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -42712,8 +42995,8 @@ async function writeAgentTools(root, scope, permissions) {
|
|
|
42712
42995
|
|
|
42713
42996
|
// src/core/packages.ts
|
|
42714
42997
|
import { createHash as createHash4 } from "crypto";
|
|
42715
|
-
import { readFile as
|
|
42716
|
-
import { dirname as dirname9, join as
|
|
42998
|
+
import { readFile as readFile13, writeFile as writeFile11, mkdir as mkdir10, readdir as readdir5, rm as rm6, stat as stat4 } from "fs/promises";
|
|
42999
|
+
import { dirname as dirname9, join as join27, relative } from "path";
|
|
42717
43000
|
var AGENTS_REL2 = ".claude/agents";
|
|
42718
43001
|
var SKILLS_REL2 = ".claude/skills";
|
|
42719
43002
|
var MANIFEST_REL = ".claude/.4pm-packages.json";
|
|
@@ -42722,7 +43005,7 @@ function safeName2(name) {
|
|
|
42722
43005
|
}
|
|
42723
43006
|
async function readMaybe(path) {
|
|
42724
43007
|
try {
|
|
42725
|
-
return await
|
|
43008
|
+
return await readFile13(path);
|
|
42726
43009
|
} catch {
|
|
42727
43010
|
return null;
|
|
42728
43011
|
}
|
|
@@ -42732,12 +43015,12 @@ async function listFilesRel(dir2) {
|
|
|
42732
43015
|
async function walk2(d) {
|
|
42733
43016
|
let entries2;
|
|
42734
43017
|
try {
|
|
42735
|
-
entries2 = await
|
|
43018
|
+
entries2 = await readdir5(d, { withFileTypes: true });
|
|
42736
43019
|
} catch {
|
|
42737
43020
|
return;
|
|
42738
43021
|
}
|
|
42739
43022
|
for (const e of entries2) {
|
|
42740
|
-
const full =
|
|
43023
|
+
const full = join27(d, e.name);
|
|
42741
43024
|
if (e.isDirectory()) await walk2(full);
|
|
42742
43025
|
else if (e.isFile()) out.push(relative(dir2, full));
|
|
42743
43026
|
}
|
|
@@ -42748,16 +43031,16 @@ async function listFilesRel(dir2) {
|
|
|
42748
43031
|
async function readArtifactFiles(root, kind, slug) {
|
|
42749
43032
|
const files = [];
|
|
42750
43033
|
if (kind === "subagent") {
|
|
42751
|
-
const agent = await readMaybe(
|
|
43034
|
+
const agent = await readMaybe(join27(root, AGENTS_REL2, `${slug}.md`));
|
|
42752
43035
|
if (agent) files.push({ path: "agent.md", contentBase64: agent.toString("base64") });
|
|
42753
|
-
const short = await readMaybe(
|
|
43036
|
+
const short = await readMaybe(join27(root, AGENTS_REL2, "short-memory", `${slug}.md`));
|
|
42754
43037
|
if (short) files.push({ path: "short-memory.md", contentBase64: short.toString("base64") });
|
|
42755
|
-
const long = await readMaybe(
|
|
43038
|
+
const long = await readMaybe(join27(root, AGENTS_REL2, "long-memory", `${slug}.md`));
|
|
42756
43039
|
if (long) files.push({ path: "long-memory.md", contentBase64: long.toString("base64") });
|
|
42757
43040
|
} else {
|
|
42758
|
-
const base =
|
|
43041
|
+
const base = join27(root, SKILLS_REL2, slug);
|
|
42759
43042
|
for (const rel of await listFilesRel(base)) {
|
|
42760
|
-
const buf = await readMaybe(
|
|
43043
|
+
const buf = await readMaybe(join27(base, rel));
|
|
42761
43044
|
if (buf) files.push({ path: rel.split("\\").join("/"), contentBase64: buf.toString("base64") });
|
|
42762
43045
|
}
|
|
42763
43046
|
}
|
|
@@ -42775,16 +43058,16 @@ async function artifactSha256(root, kind, slug) {
|
|
|
42775
43058
|
return h.digest("hex");
|
|
42776
43059
|
}
|
|
42777
43060
|
async function artifactExists(root, kind, slug) {
|
|
42778
|
-
const target = kind === "subagent" ?
|
|
43061
|
+
const target = kind === "subagent" ? join27(root, AGENTS_REL2, `${slug}.md`) : join27(root, SKILLS_REL2, slug);
|
|
42779
43062
|
try {
|
|
42780
|
-
await
|
|
43063
|
+
await stat4(target);
|
|
42781
43064
|
return true;
|
|
42782
43065
|
} catch {
|
|
42783
43066
|
return false;
|
|
42784
43067
|
}
|
|
42785
43068
|
}
|
|
42786
43069
|
async function readManifest(root) {
|
|
42787
|
-
const buf = await readMaybe(
|
|
43070
|
+
const buf = await readMaybe(join27(root, MANIFEST_REL));
|
|
42788
43071
|
if (!buf) return {};
|
|
42789
43072
|
try {
|
|
42790
43073
|
return JSON.parse(buf.toString("utf8"));
|
|
@@ -42793,35 +43076,35 @@ async function readManifest(root) {
|
|
|
42793
43076
|
}
|
|
42794
43077
|
}
|
|
42795
43078
|
async function writeManifest(root, manifest) {
|
|
42796
|
-
const path =
|
|
42797
|
-
await
|
|
42798
|
-
await
|
|
43079
|
+
const path = join27(root, MANIFEST_REL);
|
|
43080
|
+
await mkdir10(dirname9(path), { recursive: true });
|
|
43081
|
+
await writeFile11(path, JSON.stringify(manifest, null, 2), "utf8");
|
|
42799
43082
|
}
|
|
42800
43083
|
async function writeArtifactFiles(root, kind, slug, files) {
|
|
42801
43084
|
const byPath = new Map(files.map((f2) => [f2.path, Buffer.from(f2.contentBase64, "base64")]));
|
|
42802
43085
|
if (kind === "subagent") {
|
|
42803
43086
|
const agent = byPath.get("agent.md");
|
|
42804
43087
|
if (agent) {
|
|
42805
|
-
await
|
|
42806
|
-
await
|
|
43088
|
+
await mkdir10(join27(root, AGENTS_REL2), { recursive: true });
|
|
43089
|
+
await writeFile11(join27(root, AGENTS_REL2, `${slug}.md`), agent);
|
|
42807
43090
|
}
|
|
42808
43091
|
const short = byPath.get("short-memory.md");
|
|
42809
43092
|
if (short) {
|
|
42810
|
-
await
|
|
42811
|
-
await
|
|
43093
|
+
await mkdir10(join27(root, AGENTS_REL2, "short-memory"), { recursive: true });
|
|
43094
|
+
await writeFile11(join27(root, AGENTS_REL2, "short-memory", `${slug}.md`), short);
|
|
42812
43095
|
}
|
|
42813
43096
|
const long = byPath.get("long-memory.md");
|
|
42814
43097
|
if (long) {
|
|
42815
|
-
await
|
|
42816
|
-
await
|
|
43098
|
+
await mkdir10(join27(root, AGENTS_REL2, "long-memory"), { recursive: true });
|
|
43099
|
+
await writeFile11(join27(root, AGENTS_REL2, "long-memory", `${slug}.md`), long);
|
|
42817
43100
|
}
|
|
42818
43101
|
} else {
|
|
42819
|
-
const base =
|
|
43102
|
+
const base = join27(root, SKILLS_REL2, slug);
|
|
42820
43103
|
for (const [rel, buf] of byPath) {
|
|
42821
43104
|
if (rel.includes("..") || rel.startsWith("/")) continue;
|
|
42822
|
-
const dest =
|
|
42823
|
-
await
|
|
42824
|
-
await
|
|
43105
|
+
const dest = join27(base, rel);
|
|
43106
|
+
await mkdir10(dirname9(dest), { recursive: true });
|
|
43107
|
+
await writeFile11(dest, buf);
|
|
42825
43108
|
}
|
|
42826
43109
|
}
|
|
42827
43110
|
}
|
|
@@ -42885,11 +43168,11 @@ async function removePackage(root, slug) {
|
|
|
42885
43168
|
const entry = manifest[n];
|
|
42886
43169
|
if (!entry) return { ok: false, error: "not installed" };
|
|
42887
43170
|
if (entry.kind === "subagent") {
|
|
42888
|
-
await
|
|
42889
|
-
await
|
|
42890
|
-
await
|
|
43171
|
+
await rm6(join27(root, AGENTS_REL2, `${n}.md`), { force: true });
|
|
43172
|
+
await rm6(join27(root, AGENTS_REL2, "short-memory", `${n}.md`), { force: true });
|
|
43173
|
+
await rm6(join27(root, AGENTS_REL2, "long-memory", `${n}.md`), { force: true });
|
|
42891
43174
|
} else {
|
|
42892
|
-
await
|
|
43175
|
+
await rm6(join27(root, SKILLS_REL2, n), { recursive: true, force: true });
|
|
42893
43176
|
}
|
|
42894
43177
|
delete manifest[n];
|
|
42895
43178
|
await writeManifest(root, manifest);
|
|
@@ -42900,8 +43183,8 @@ async function removePackage(root, slug) {
|
|
|
42900
43183
|
}
|
|
42901
43184
|
|
|
42902
43185
|
// src/core/graph.ts
|
|
42903
|
-
import { readFile as
|
|
42904
|
-
import { join as
|
|
43186
|
+
import { readFile as readFile14, readdir as readdir6 } from "fs/promises";
|
|
43187
|
+
import { join as join28, relative as relative2, dirname as dirname10, resolve as resolve9, extname as extname2, basename as basename6 } from "path";
|
|
42905
43188
|
var MAX_FILES = 2e3;
|
|
42906
43189
|
var MAX_NODES = 3e3;
|
|
42907
43190
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "out", "coverage", ".turbo"]);
|
|
@@ -42913,7 +43196,7 @@ async function walk(root, accept) {
|
|
|
42913
43196
|
const dir2 = stack.pop();
|
|
42914
43197
|
let entries2;
|
|
42915
43198
|
try {
|
|
42916
|
-
entries2 = await
|
|
43199
|
+
entries2 = await readdir6(dir2, { withFileTypes: true });
|
|
42917
43200
|
} catch {
|
|
42918
43201
|
continue;
|
|
42919
43202
|
}
|
|
@@ -42921,7 +43204,7 @@ async function walk(root, accept) {
|
|
|
42921
43204
|
if (e.name.startsWith(".") && e.name !== ".claude") {
|
|
42922
43205
|
if (e.isDirectory()) continue;
|
|
42923
43206
|
}
|
|
42924
|
-
const full =
|
|
43207
|
+
const full = join28(dir2, e.name);
|
|
42925
43208
|
if (e.isDirectory()) {
|
|
42926
43209
|
if (!SKIP_DIRS.has(e.name)) stack.push(full);
|
|
42927
43210
|
} else if (accept(full)) {
|
|
@@ -42949,7 +43232,7 @@ async function buildDocs(root) {
|
|
|
42949
43232
|
const rel = relative2(root, f2).split("\\").join("/");
|
|
42950
43233
|
let text = "";
|
|
42951
43234
|
try {
|
|
42952
|
-
text = await
|
|
43235
|
+
text = await readFile14(f2, "utf8");
|
|
42953
43236
|
} catch {
|
|
42954
43237
|
continue;
|
|
42955
43238
|
}
|
|
@@ -42988,7 +43271,7 @@ async function buildCode(root) {
|
|
|
42988
43271
|
const rel = relative2(root, f2).split("\\").join("/");
|
|
42989
43272
|
let text = "";
|
|
42990
43273
|
try {
|
|
42991
|
-
text = await
|
|
43274
|
+
text = await readFile14(f2, "utf8");
|
|
42992
43275
|
} catch {
|
|
42993
43276
|
continue;
|
|
42994
43277
|
}
|
|
@@ -43040,11 +43323,11 @@ async function buildGraph(root, mode) {
|
|
|
43040
43323
|
}
|
|
43041
43324
|
|
|
43042
43325
|
// src/core/rag.ts
|
|
43043
|
-
import { readFile as
|
|
43326
|
+
import { readFile as readFile15, writeFile as writeFile12, statfs as statfs2, mkdir as mkdir11 } from "fs/promises";
|
|
43044
43327
|
import { existsSync as existsSync14 } from "fs";
|
|
43045
43328
|
import { execFile as execFile10, spawn as spawn9 } from "child_process";
|
|
43046
43329
|
import { totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
43047
|
-
import { join as
|
|
43330
|
+
import { join as join29 } from "path";
|
|
43048
43331
|
import { promisify as promisify10 } from "util";
|
|
43049
43332
|
var run10 = promisify10(execFile10);
|
|
43050
43333
|
var RAG_REL = ".claude/rag";
|
|
@@ -43065,7 +43348,7 @@ async function detectPython() {
|
|
|
43065
43348
|
}
|
|
43066
43349
|
async function installLog(root) {
|
|
43067
43350
|
try {
|
|
43068
|
-
const text = await
|
|
43351
|
+
const text = await readFile15(join29(root, RAG_REL, "install.log"), "utf8");
|
|
43069
43352
|
return text.split("\n").filter((l) => l.length > 0).slice(-200);
|
|
43070
43353
|
} catch {
|
|
43071
43354
|
return [];
|
|
@@ -43127,14 +43410,14 @@ async function ragStatus(root) {
|
|
|
43127
43410
|
};
|
|
43128
43411
|
let modelName = "";
|
|
43129
43412
|
try {
|
|
43130
|
-
modelName = (await
|
|
43413
|
+
modelName = (await readFile15(join29(root, RAG_REL, ".installed"), "utf8")).trim();
|
|
43131
43414
|
} catch {
|
|
43132
43415
|
modelName = "";
|
|
43133
43416
|
}
|
|
43134
43417
|
const modelPresent = modelName.length > 0;
|
|
43135
43418
|
return {
|
|
43136
43419
|
installed: fastembed && sqliteVec && modelPresent,
|
|
43137
|
-
installing: existsSync14(
|
|
43420
|
+
installing: existsSync14(join29(root, RAG_REL, ".installing")),
|
|
43138
43421
|
python: { found: py.found, version: py.version },
|
|
43139
43422
|
deps: { pip, fastembed, sqliteVec },
|
|
43140
43423
|
model: { present: modelPresent, name: modelName },
|
|
@@ -43145,9 +43428,9 @@ async function ragStatus(root) {
|
|
|
43145
43428
|
};
|
|
43146
43429
|
}
|
|
43147
43430
|
async function readIndexMeta(root) {
|
|
43148
|
-
const indexing = existsSync14(
|
|
43431
|
+
const indexing = existsSync14(join29(root, RAG_REL, ".indexing"));
|
|
43149
43432
|
try {
|
|
43150
|
-
const m2 = JSON.parse(await
|
|
43433
|
+
const m2 = JSON.parse(await readFile15(join29(root, RAG_REL, ".index.json"), "utf8"));
|
|
43151
43434
|
return {
|
|
43152
43435
|
present: true,
|
|
43153
43436
|
indexing,
|
|
@@ -43214,14 +43497,14 @@ if __name__ == "__main__":
|
|
|
43214
43497
|
elif cmd == "query": query(sys.argv[2], int(sys.argv[3]) if len(sys.argv) > 3 else 8)
|
|
43215
43498
|
`;
|
|
43216
43499
|
async function ensureEngine(root) {
|
|
43217
|
-
await
|
|
43218
|
-
await
|
|
43500
|
+
await mkdir11(join29(root, RAG_REL), { recursive: true }).catch(() => void 0);
|
|
43501
|
+
await writeFile12(join29(root, RAG_REL, "rag_engine.py"), RAG_ENGINE_PY, "utf8");
|
|
43219
43502
|
}
|
|
43220
43503
|
async function ragReindex(root) {
|
|
43221
43504
|
const py = await detectPython();
|
|
43222
43505
|
if (!py.found) return { ok: false, started: false, error: "python3 not found" };
|
|
43223
|
-
if (!existsSync14(
|
|
43224
|
-
if (existsSync14(
|
|
43506
|
+
if (!existsSync14(join29(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
|
|
43507
|
+
if (existsSync14(join29(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
|
|
43225
43508
|
await ensureEngine(root);
|
|
43226
43509
|
const script = `
|
|
43227
43510
|
set +e
|
|
@@ -43241,12 +43524,12 @@ rm -f "${RAG_REL}/.indexing"
|
|
|
43241
43524
|
async function ragQuery(root, queryText, k2 = 8) {
|
|
43242
43525
|
const py = await detectPython();
|
|
43243
43526
|
if (!py.found) return { results: [], error: "python3 not found" };
|
|
43244
|
-
if (!existsSync14(
|
|
43527
|
+
if (!existsSync14(join29(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
|
|
43245
43528
|
await ensureEngine(root);
|
|
43246
43529
|
try {
|
|
43247
43530
|
const { stdout } = await run10(
|
|
43248
43531
|
py.bin,
|
|
43249
|
-
[
|
|
43532
|
+
[join29(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k2, 1), 20))],
|
|
43250
43533
|
{ cwd: root, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
43251
43534
|
);
|
|
43252
43535
|
const parsed = JSON.parse(stdout.trim() || '{"results":[]}');
|
|
@@ -43261,9 +43544,9 @@ async function ragInstall(root, model) {
|
|
|
43261
43544
|
}
|
|
43262
43545
|
const py = await detectPython();
|
|
43263
43546
|
if (!py.found) return { ok: false, started: false, error: "python3 not found on the worker" };
|
|
43264
|
-
if (existsSync14(
|
|
43547
|
+
if (existsSync14(join29(root, RAG_REL, ".installing"))) return { ok: true, started: true };
|
|
43265
43548
|
try {
|
|
43266
|
-
await
|
|
43549
|
+
await mkdir11(join29(root, RAG_REL), { recursive: true });
|
|
43267
43550
|
} catch {
|
|
43268
43551
|
}
|
|
43269
43552
|
const script = `
|
|
@@ -43290,8 +43573,8 @@ rm -f "${RAG_REL}/.installing"
|
|
|
43290
43573
|
}
|
|
43291
43574
|
|
|
43292
43575
|
// src/core/config-sync.ts
|
|
43293
|
-
import { existsSync as existsSync15, readFileSync as
|
|
43294
|
-
import { join as
|
|
43576
|
+
import { existsSync as existsSync15, readFileSync as readFileSync16 } from "fs";
|
|
43577
|
+
import { join as join30 } from "path";
|
|
43295
43578
|
var SERVER_MANAGED_KEYS = [
|
|
43296
43579
|
"physicPath",
|
|
43297
43580
|
"sessionSwitchPct",
|
|
@@ -43308,10 +43591,10 @@ var SERVER_MANAGED_KEYS = [
|
|
|
43308
43591
|
"projectAiMemoryBudgetChars"
|
|
43309
43592
|
];
|
|
43310
43593
|
function readConfigText(profileDir2) {
|
|
43311
|
-
const path =
|
|
43594
|
+
const path = join30(profileDir2, "config.json");
|
|
43312
43595
|
if (existsSync15(path)) {
|
|
43313
43596
|
try {
|
|
43314
|
-
return
|
|
43597
|
+
return readFileSync16(path, "utf8");
|
|
43315
43598
|
} catch {
|
|
43316
43599
|
}
|
|
43317
43600
|
}
|
|
@@ -43342,7 +43625,7 @@ function handleWorkerChannels(ctx, message, payload) {
|
|
|
43342
43625
|
switch (message.channel) {
|
|
43343
43626
|
case WsChannels.AUTONOMOUS_READ:
|
|
43344
43627
|
if (ctx.physicRoot) {
|
|
43345
|
-
void readAutonomous(ctx.physicRoot).then(
|
|
43628
|
+
void readAutonomous(ctx.physicRoot, ctx.profileDir).then(
|
|
43346
43629
|
(reply) => ctx.send(WsChannels.AUTONOMOUS_READ, reply, message.id)
|
|
43347
43630
|
);
|
|
43348
43631
|
}
|
|
@@ -43359,7 +43642,7 @@ function handleWorkerChannels(ctx, message, payload) {
|
|
|
43359
43642
|
case WsChannels.AUTONOMOUS_WRITE: {
|
|
43360
43643
|
const req = payload;
|
|
43361
43644
|
if (ctx.physicRoot) {
|
|
43362
|
-
void writeAutonomous(ctx.physicRoot, req, req.by ?? "unknown").then(
|
|
43645
|
+
void writeAutonomous(ctx.physicRoot, ctx.profileDir, req, req.by ?? "unknown").then(
|
|
43363
43646
|
(reply) => ctx.send(WsChannels.AUTONOMOUS_WRITE, reply, message.id)
|
|
43364
43647
|
);
|
|
43365
43648
|
}
|
|
@@ -44201,7 +44484,7 @@ var WsClient = class {
|
|
|
44201
44484
|
*/
|
|
44202
44485
|
physicFolderPath(projectName) {
|
|
44203
44486
|
const safe = projectName.replace(/[/\\]/g, "_").replace(/\.\./g, "_").trim();
|
|
44204
|
-
return safe ?
|
|
44487
|
+
return safe ? join31(this.context.profileDir, safe) : null;
|
|
44205
44488
|
}
|
|
44206
44489
|
/**
|
|
44207
44490
|
* Report machine.status: version + the state of the physic project being served
|
|
@@ -44218,7 +44501,7 @@ var WsClient = class {
|
|
|
44218
44501
|
physicPath,
|
|
44219
44502
|
physicPathExists: physicPath ? existsSync16(physicPath) : void 0,
|
|
44220
44503
|
// Real coarse state (ADR-0152): !paused and a recent tick (was hard-coded false).
|
|
44221
|
-
autonomousRunning: physicPath ? isAutonomousRunning(physicPath) : false,
|
|
44504
|
+
autonomousRunning: physicPath ? isAutonomousRunning(physicPath, this.context.profileDir) : false,
|
|
44222
44505
|
// The machine-user AI-run limit (0 = unlimited) + profile count so a dispatch can resolve the
|
|
44223
44506
|
// effective timeout and derive the SSE reply windows server-side (ADR-0256).
|
|
44224
44507
|
aiRunTimeoutSec: config.aiRunTimeoutSec ?? 0,
|
|
@@ -44310,7 +44593,7 @@ var WsClient = class {
|
|
|
44310
44593
|
*/
|
|
44311
44594
|
uploadLog() {
|
|
44312
44595
|
try {
|
|
44313
|
-
const upload = readLogUpload(
|
|
44596
|
+
const upload = readLogUpload(join31(this.context.profileDir, "logs"));
|
|
44314
44597
|
if (!upload) return;
|
|
44315
44598
|
const sig = `${upload.fileName}:${upload.totalBytes}`;
|
|
44316
44599
|
if (sig === this.lastLogSig) return;
|
|
@@ -44472,10 +44755,10 @@ var WsClient = class {
|
|
|
44472
44755
|
};
|
|
44473
44756
|
|
|
44474
44757
|
// src/core/instance-lock.ts
|
|
44475
|
-
import { readFileSync as
|
|
44476
|
-
import { join as
|
|
44758
|
+
import { readFileSync as readFileSync17, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "fs";
|
|
44759
|
+
import { join as join32 } from "path";
|
|
44477
44760
|
function lockPath(profileDir2) {
|
|
44478
|
-
return
|
|
44761
|
+
return join32(profileDir2, "start.lock");
|
|
44479
44762
|
}
|
|
44480
44763
|
function isAlive(pid) {
|
|
44481
44764
|
try {
|
|
@@ -44487,7 +44770,7 @@ function isAlive(pid) {
|
|
|
44487
44770
|
}
|
|
44488
44771
|
function lockHolder(profileDir2) {
|
|
44489
44772
|
try {
|
|
44490
|
-
const pid = Number.parseInt(
|
|
44773
|
+
const pid = Number.parseInt(readFileSync17(lockPath(profileDir2), "utf8").trim(), 10);
|
|
44491
44774
|
if (Number.isInteger(pid) && pid > 0 && pid !== process.pid && isAlive(pid)) return pid;
|
|
44492
44775
|
} catch {
|
|
44493
44776
|
}
|
|
@@ -44500,7 +44783,7 @@ function acquireInstanceLock(profileDir2) {
|
|
|
44500
44783
|
}
|
|
44501
44784
|
function releaseInstanceLock(profileDir2) {
|
|
44502
44785
|
try {
|
|
44503
|
-
const pid = Number.parseInt(
|
|
44786
|
+
const pid = Number.parseInt(readFileSync17(lockPath(profileDir2), "utf8").trim(), 10);
|
|
44504
44787
|
if (pid === process.pid) rmSync7(lockPath(profileDir2), { force: true });
|
|
44505
44788
|
} catch {
|
|
44506
44789
|
}
|
|
@@ -44796,11 +45079,12 @@ var SessionBus = class {
|
|
|
44796
45079
|
|
|
44797
45080
|
// src/core/control-server.ts
|
|
44798
45081
|
import { createServer } from "net";
|
|
44799
|
-
import { rmSync as rmSync8 } from "fs";
|
|
44800
|
-
import { join as
|
|
45082
|
+
import { chmodSync as chmodSync4, rmSync as rmSync8 } from "fs";
|
|
45083
|
+
import { join as join34 } from "path";
|
|
44801
45084
|
|
|
44802
45085
|
// src/core/control-protocol.ts
|
|
44803
45086
|
var CONTROL_SOCKET_FILE = "control.sock";
|
|
45087
|
+
var CONTROL_TOKEN_FILE = "control.token";
|
|
44804
45088
|
function encodeFrame(frame) {
|
|
44805
45089
|
return `${JSON.stringify(frame)}
|
|
44806
45090
|
`;
|
|
@@ -44824,9 +45108,41 @@ function createFrameParser() {
|
|
|
44824
45108
|
};
|
|
44825
45109
|
}
|
|
44826
45110
|
|
|
45111
|
+
// src/core/control-token.ts
|
|
45112
|
+
import { chmodSync as chmodSync3, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "fs";
|
|
45113
|
+
import { randomBytes as randomBytes3, timingSafeEqual } from "crypto";
|
|
45114
|
+
import { join as join33 } from "path";
|
|
45115
|
+
function controlTokenPath(profileDir2) {
|
|
45116
|
+
return join33(profileDir2, CONTROL_TOKEN_FILE);
|
|
45117
|
+
}
|
|
45118
|
+
function writeControlToken(profileDir2) {
|
|
45119
|
+
const token = randomBytes3(32).toString("hex");
|
|
45120
|
+
const path = controlTokenPath(profileDir2);
|
|
45121
|
+
writeFileSync10(path, token, { mode: 384 });
|
|
45122
|
+
try {
|
|
45123
|
+
chmodSync3(path, 384);
|
|
45124
|
+
} catch {
|
|
45125
|
+
}
|
|
45126
|
+
return token;
|
|
45127
|
+
}
|
|
45128
|
+
function readControlToken(profileDir2) {
|
|
45129
|
+
try {
|
|
45130
|
+
const v2 = readFileSync18(controlTokenPath(profileDir2), "utf8").trim();
|
|
45131
|
+
return v2.length > 0 ? v2 : null;
|
|
45132
|
+
} catch {
|
|
45133
|
+
return null;
|
|
45134
|
+
}
|
|
45135
|
+
}
|
|
45136
|
+
function tokenMatches(expected, given) {
|
|
45137
|
+
if (!given) return false;
|
|
45138
|
+
const a = Buffer.from(expected, "utf8");
|
|
45139
|
+
const b2 = Buffer.from(given, "utf8");
|
|
45140
|
+
return a.length === b2.length && timingSafeEqual(a, b2);
|
|
45141
|
+
}
|
|
45142
|
+
|
|
44827
45143
|
// src/core/control-server.ts
|
|
44828
45144
|
function startControlServer(bus, profileDir2, info) {
|
|
44829
|
-
const socketPath =
|
|
45145
|
+
const socketPath = join34(profileDir2, CONTROL_SOCKET_FILE);
|
|
44830
45146
|
try {
|
|
44831
45147
|
rmSync8(socketPath, { force: true });
|
|
44832
45148
|
} catch {
|
|
@@ -44858,25 +45174,37 @@ function startControlServer(bus, profileDir2, info) {
|
|
|
44858
45174
|
// Report an autonomous cycle's completion back to the `4pm auto-run` client (ADR-0319).
|
|
44859
45175
|
bus.onAutonomousDone((ok, note) => broadcast({ t: "autonomousDone", ok, note }))
|
|
44860
45176
|
];
|
|
45177
|
+
const token = writeControlToken(profileDir2);
|
|
45178
|
+
const sendSnapshot = (sock) => send(sock, {
|
|
45179
|
+
t: "snapshot",
|
|
45180
|
+
info,
|
|
45181
|
+
status: bus.status,
|
|
45182
|
+
busy: bus.busy,
|
|
45183
|
+
activeProfile: bus.activeProfile,
|
|
45184
|
+
scope: bus.scope,
|
|
45185
|
+
worker: bus.worker,
|
|
45186
|
+
project: bus.project,
|
|
45187
|
+
usage: bus.usage,
|
|
45188
|
+
tokens: bus.sessionTokens,
|
|
45189
|
+
transcript: bus.snapshot()
|
|
45190
|
+
});
|
|
44861
45191
|
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
45192
|
sock.setEncoding("utf8");
|
|
45193
|
+
let authed = false;
|
|
45194
|
+
const parse = createFrameParser();
|
|
44878
45195
|
sock.on("data", (chunk) => {
|
|
44879
45196
|
for (const frame of parse(chunk)) {
|
|
45197
|
+
if (!authed) {
|
|
45198
|
+
if (frame.t === "auth" && tokenMatches(token, frame.token)) {
|
|
45199
|
+
authed = true;
|
|
45200
|
+
clients.add(sock);
|
|
45201
|
+
sendSnapshot(sock);
|
|
45202
|
+
} else {
|
|
45203
|
+
send(sock, { t: "authError" });
|
|
45204
|
+
sock.destroy();
|
|
45205
|
+
}
|
|
45206
|
+
continue;
|
|
45207
|
+
}
|
|
44880
45208
|
if (frame.t === "submit") bus.submitLocal(frame.input);
|
|
44881
45209
|
else if (frame.t === "reconnect") bus.requestReconnect();
|
|
44882
45210
|
else if (frame.t === "autonomousRun") bus.submitAutonomous();
|
|
@@ -44890,7 +45218,12 @@ function startControlServer(bus, profileDir2, info) {
|
|
|
44890
45218
|
});
|
|
44891
45219
|
server.on("error", () => {
|
|
44892
45220
|
});
|
|
44893
|
-
server.listen(socketPath)
|
|
45221
|
+
server.listen(socketPath, () => {
|
|
45222
|
+
try {
|
|
45223
|
+
chmodSync4(socketPath, 384);
|
|
45224
|
+
} catch {
|
|
45225
|
+
}
|
|
45226
|
+
});
|
|
44894
45227
|
return () => {
|
|
44895
45228
|
for (const un2 of unsubs) un2();
|
|
44896
45229
|
for (const sock of clients) sock.destroy();
|
|
@@ -44898,6 +45231,7 @@ function startControlServer(bus, profileDir2, info) {
|
|
|
44898
45231
|
server.close();
|
|
44899
45232
|
try {
|
|
44900
45233
|
rmSync8(socketPath, { force: true });
|
|
45234
|
+
rmSync8(controlTokenPath(profileDir2), { force: true });
|
|
44901
45235
|
} catch {
|
|
44902
45236
|
}
|
|
44903
45237
|
};
|
|
@@ -50560,17 +50894,17 @@ var import_react21 = __toESM(require_react(), 1);
|
|
|
50560
50894
|
var import_react26 = __toESM(require_react(), 1);
|
|
50561
50895
|
|
|
50562
50896
|
// src/core/input-history.ts
|
|
50563
|
-
import { existsSync as existsSync18, readFileSync as
|
|
50564
|
-
import { join as
|
|
50897
|
+
import { existsSync as existsSync18, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
|
|
50898
|
+
import { join as join35 } from "path";
|
|
50565
50899
|
var MAX_HISTORY = 200;
|
|
50566
50900
|
function historyPath(profileDir2) {
|
|
50567
|
-
return
|
|
50901
|
+
return join35(profileDir2, "input-history.json");
|
|
50568
50902
|
}
|
|
50569
50903
|
function loadInputHistory(profileDir2) {
|
|
50570
50904
|
const path = historyPath(profileDir2);
|
|
50571
50905
|
if (!existsSync18(path)) return [];
|
|
50572
50906
|
try {
|
|
50573
|
-
const data = JSON.parse(
|
|
50907
|
+
const data = JSON.parse(readFileSync20(path, "utf8"));
|
|
50574
50908
|
return Array.isArray(data) ? data.filter((x) => typeof x === "string") : [];
|
|
50575
50909
|
} catch {
|
|
50576
50910
|
return [];
|
|
@@ -50581,7 +50915,7 @@ function appendInputHistory(profileDir2, line, current2) {
|
|
|
50581
50915
|
if (!trimmed || current2[current2.length - 1] === trimmed) return current2;
|
|
50582
50916
|
const next = [...current2, trimmed].slice(-MAX_HISTORY);
|
|
50583
50917
|
try {
|
|
50584
|
-
|
|
50918
|
+
writeFileSync11(historyPath(profileDir2), JSON.stringify(next), "utf8");
|
|
50585
50919
|
} catch {
|
|
50586
50920
|
}
|
|
50587
50921
|
return next;
|
|
@@ -51178,7 +51512,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
51178
51512
|
return;
|
|
51179
51513
|
}
|
|
51180
51514
|
logger.configure({
|
|
51181
|
-
dir:
|
|
51515
|
+
dir: join36(profileDir2, "logs"),
|
|
51182
51516
|
level: resolveLogLevel(),
|
|
51183
51517
|
base: { version: CLI_VERSION, profile: profileName, scope: credential.scope }
|
|
51184
51518
|
});
|
|
@@ -51287,15 +51621,21 @@ async function runStart(profileDir2, profileName) {
|
|
|
51287
51621
|
}
|
|
51288
51622
|
|
|
51289
51623
|
// src/commands/attach.ts
|
|
51290
|
-
import { join as
|
|
51624
|
+
import { join as join37 } from "path";
|
|
51291
51625
|
|
|
51292
51626
|
// src/core/control-client.ts
|
|
51293
51627
|
import { connect as connect2 } from "net";
|
|
51294
|
-
function connectControl(socketPath, bus) {
|
|
51628
|
+
function connectControl(socketPath, bus, token) {
|
|
51295
51629
|
return new Promise((resolve10, reject) => {
|
|
51296
51630
|
const socket = connect2(socketPath);
|
|
51297
51631
|
socket.setEncoding("utf8");
|
|
51298
51632
|
let ready = false;
|
|
51633
|
+
socket.on("connect", () => {
|
|
51634
|
+
try {
|
|
51635
|
+
socket.write(encodeFrame({ t: "auth", token: token ?? "" }));
|
|
51636
|
+
} catch {
|
|
51637
|
+
}
|
|
51638
|
+
});
|
|
51299
51639
|
const forwardSubmit = bus.onLocalSubmit((input) => {
|
|
51300
51640
|
try {
|
|
51301
51641
|
socket.write(encodeFrame({ t: "submit", input }));
|
|
@@ -51374,6 +51714,9 @@ function connectControl(socketPath, bus) {
|
|
|
51374
51714
|
case "tokens":
|
|
51375
51715
|
bus.setTokens(frame.total);
|
|
51376
51716
|
break;
|
|
51717
|
+
case "authError":
|
|
51718
|
+
if (!ready) reject(new Error("control-channel auth failed"));
|
|
51719
|
+
break;
|
|
51377
51720
|
}
|
|
51378
51721
|
}
|
|
51379
51722
|
});
|
|
@@ -51387,11 +51730,11 @@ async function runAttach(profileDir2, profileName) {
|
|
|
51387
51730
|
process.exitCode = 1;
|
|
51388
51731
|
return;
|
|
51389
51732
|
}
|
|
51390
|
-
const socketPath =
|
|
51733
|
+
const socketPath = join37(profileDir2, CONTROL_SOCKET_FILE);
|
|
51391
51734
|
const bus = new SessionBus();
|
|
51392
51735
|
let conn;
|
|
51393
51736
|
try {
|
|
51394
|
-
conn = await connectControl(socketPath, bus);
|
|
51737
|
+
conn = await connectControl(socketPath, bus, readControlToken(profileDir2));
|
|
51395
51738
|
} catch {
|
|
51396
51739
|
console.error(t("attach.noDaemon", { profile: profileName }));
|
|
51397
51740
|
process.exitCode = 1;
|
|
@@ -51418,11 +51761,12 @@ async function runAttach(profileDir2, profileName) {
|
|
|
51418
51761
|
|
|
51419
51762
|
// src/commands/auto-run.ts
|
|
51420
51763
|
import { connect as connect3 } from "net";
|
|
51421
|
-
import { join as
|
|
51764
|
+
import { join as join38 } from "path";
|
|
51422
51765
|
var AUTO_RUN_TIMEOUT_MS = 3 * 60 * 60 * 1e3;
|
|
51423
51766
|
async function runAutoRun(profileDir2, profileName) {
|
|
51424
51767
|
initI18n(readProfileConfig(profileDir2).locale);
|
|
51425
|
-
const socketPath =
|
|
51768
|
+
const socketPath = join38(profileDir2, CONTROL_SOCKET_FILE);
|
|
51769
|
+
const token = readControlToken(profileDir2);
|
|
51426
51770
|
const code = await new Promise((resolve10) => {
|
|
51427
51771
|
let settled = false;
|
|
51428
51772
|
const done = (c) => {
|
|
@@ -51444,6 +51788,7 @@ async function runAutoRun(profileDir2, profileName) {
|
|
|
51444
51788
|
timer.unref?.();
|
|
51445
51789
|
socket.on("connect", () => {
|
|
51446
51790
|
try {
|
|
51791
|
+
socket.write(encodeFrame({ t: "auth", token: token ?? "" }));
|
|
51447
51792
|
socket.write(encodeFrame({ t: "autonomousRun" }));
|
|
51448
51793
|
} catch {
|
|
51449
51794
|
done(1);
|
|
@@ -51460,6 +51805,9 @@ async function runAutoRun(profileDir2, profileName) {
|
|
|
51460
51805
|
if (frame.ok) console.log(t("autoRun.done"));
|
|
51461
51806
|
else console.error(t("autoRun.failed", { note: frame.note ?? "" }));
|
|
51462
51807
|
done(frame.ok ? 0 : 1);
|
|
51808
|
+
} else if (frame.t === "authError") {
|
|
51809
|
+
console.error(t("autoRun.authFailed"));
|
|
51810
|
+
done(1);
|
|
51463
51811
|
}
|
|
51464
51812
|
}
|
|
51465
51813
|
});
|