@4pm/cli 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +253 -61
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -32954,7 +32954,11 @@ var issueProvisioningTokenRequestSchema = external_exports.object({
|
|
|
32954
32954
|
ttlSec: external_exports.number().int().min(0).max(2592e3).optional()
|
|
32955
32955
|
});
|
|
32956
32956
|
var listWorkersQuerySchema = baseRequestSchema.extend({
|
|
32957
|
-
status: external_exports.enum(["online", "offline"]).optional()
|
|
32957
|
+
status: external_exports.enum(["online", "offline"]).optional(),
|
|
32958
|
+
/** Only workers whose live CPU/memory/disk usage % ≥ the given threshold (0–100). */
|
|
32959
|
+
minCpuPct: external_exports.coerce.number().min(0).max(100).optional(),
|
|
32960
|
+
minMemPct: external_exports.coerce.number().min(0).max(100).optional(),
|
|
32961
|
+
minDiskPct: external_exports.coerce.number().min(0).max(100).optional()
|
|
32958
32962
|
});
|
|
32959
32963
|
var updateWorkerRequestSchema = external_exports.object({
|
|
32960
32964
|
name: external_exports.string().min(1).max(100).optional()
|
|
@@ -33290,6 +33294,118 @@ var activityHeartbeatSchema = external_exports.object({
|
|
|
33290
33294
|
projectId: external_exports.string().uuid().optional()
|
|
33291
33295
|
});
|
|
33292
33296
|
|
|
33297
|
+
// src/core/worker-metrics.ts
|
|
33298
|
+
import { statfs, readFile } from "fs/promises";
|
|
33299
|
+
import { existsSync as existsSync4 } from "fs";
|
|
33300
|
+
import { totalmem, freemem, cpus, platform, arch } from "os";
|
|
33301
|
+
var CGROUP = "/sys/fs/cgroup";
|
|
33302
|
+
var MB = 1024 * 1024;
|
|
33303
|
+
var GB = 1024 * 1024 * 1024;
|
|
33304
|
+
async function readCgroup(file) {
|
|
33305
|
+
try {
|
|
33306
|
+
return (await readFile(`${CGROUP}/${file}`, "utf8")).trim();
|
|
33307
|
+
} catch {
|
|
33308
|
+
return null;
|
|
33309
|
+
}
|
|
33310
|
+
}
|
|
33311
|
+
function createWorkerMetricsSampler(diskPath) {
|
|
33312
|
+
const cgroupV2 = existsSync4(`${CGROUP}/cpu.stat`) && existsSync4(`${CGROUP}/memory.current`);
|
|
33313
|
+
let prev = null;
|
|
33314
|
+
async function cgroupCpuCount() {
|
|
33315
|
+
const raw = await readCgroup("cpu.max");
|
|
33316
|
+
if (raw) {
|
|
33317
|
+
const [quota, period] = raw.split(/\s+/);
|
|
33318
|
+
if (quota && quota !== "max" && period) {
|
|
33319
|
+
const n = Number(quota) / Number(period);
|
|
33320
|
+
if (n > 0) return Math.max(1, Math.round(n * 10) / 10);
|
|
33321
|
+
}
|
|
33322
|
+
}
|
|
33323
|
+
return cpus().length;
|
|
33324
|
+
}
|
|
33325
|
+
async function cgroupCpuPct(cpuCount) {
|
|
33326
|
+
const stat2 = await readCgroup("cpu.stat");
|
|
33327
|
+
const m = stat2?.match(/usage_usec\s+(\d+)/);
|
|
33328
|
+
const usageUsec = m ? Number(m[1]) : NaN;
|
|
33329
|
+
const now = Date.now();
|
|
33330
|
+
if (!Number.isFinite(usageUsec)) return 0;
|
|
33331
|
+
if (!prev) {
|
|
33332
|
+
prev = { busy: usageUsec, ref: now };
|
|
33333
|
+
return 0;
|
|
33334
|
+
}
|
|
33335
|
+
const dUsage = usageUsec - prev.busy;
|
|
33336
|
+
const dWall = (now - prev.ref) * 1e3;
|
|
33337
|
+
prev = { busy: usageUsec, ref: now };
|
|
33338
|
+
if (dWall <= 0) return 0;
|
|
33339
|
+
return clampPct(dUsage / (dWall * cpuCount) * 100);
|
|
33340
|
+
}
|
|
33341
|
+
function osCpuPct() {
|
|
33342
|
+
let busy = 0;
|
|
33343
|
+
let total = 0;
|
|
33344
|
+
for (const c of cpus()) {
|
|
33345
|
+
const t = c.times;
|
|
33346
|
+
busy += t.user + t.nice + t.sys + t.irq;
|
|
33347
|
+
total += t.user + t.nice + t.sys + t.irq + t.idle;
|
|
33348
|
+
}
|
|
33349
|
+
if (!prev) {
|
|
33350
|
+
prev = { busy, ref: total };
|
|
33351
|
+
return 0;
|
|
33352
|
+
}
|
|
33353
|
+
const dBusy = busy - prev.busy;
|
|
33354
|
+
const dTotal = total - prev.ref;
|
|
33355
|
+
prev = { busy, ref: total };
|
|
33356
|
+
if (dTotal <= 0) return 0;
|
|
33357
|
+
return clampPct(dBusy / dTotal * 100);
|
|
33358
|
+
}
|
|
33359
|
+
async function disk() {
|
|
33360
|
+
try {
|
|
33361
|
+
const fs2 = await statfs(diskPath);
|
|
33362
|
+
const total = fs2.blocks * fs2.bsize;
|
|
33363
|
+
const free = fs2.bavail * fs2.bsize;
|
|
33364
|
+
return { usedGb: round1((total - free) / GB), totalGb: round1(total / GB) };
|
|
33365
|
+
} catch {
|
|
33366
|
+
return { usedGb: 0, totalGb: 0 };
|
|
33367
|
+
}
|
|
33368
|
+
}
|
|
33369
|
+
return {
|
|
33370
|
+
async sample() {
|
|
33371
|
+
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
33372
|
+
const { usedGb: diskUsedGb, totalGb: diskTotalGb } = await disk();
|
|
33373
|
+
const base = { platform: platform(), arch: arch(), diskUsedGb, diskTotalGb, at };
|
|
33374
|
+
if (cgroupV2) {
|
|
33375
|
+
const memMaxRaw = await readCgroup("memory.max");
|
|
33376
|
+
const memCurRaw = await readCgroup("memory.current");
|
|
33377
|
+
const memTotalBytes = memMaxRaw && memMaxRaw !== "max" ? Number(memMaxRaw) : totalmem();
|
|
33378
|
+
const memUsedBytes = memCurRaw ? Number(memCurRaw) : totalmem() - freemem();
|
|
33379
|
+
const cpuCount = await cgroupCpuCount();
|
|
33380
|
+
const cpuPct = await cgroupCpuPct(cpuCount);
|
|
33381
|
+
return {
|
|
33382
|
+
...base,
|
|
33383
|
+
source: "cgroup",
|
|
33384
|
+
cpuCount,
|
|
33385
|
+
cpuPct,
|
|
33386
|
+
memUsedMb: Math.round(memUsedBytes / MB),
|
|
33387
|
+
memTotalMb: Math.round(memTotalBytes / MB)
|
|
33388
|
+
};
|
|
33389
|
+
}
|
|
33390
|
+
const total = totalmem();
|
|
33391
|
+
return {
|
|
33392
|
+
...base,
|
|
33393
|
+
source: "os",
|
|
33394
|
+
cpuCount: cpus().length,
|
|
33395
|
+
cpuPct: osCpuPct(),
|
|
33396
|
+
memUsedMb: Math.round((total - freemem()) / MB),
|
|
33397
|
+
memTotalMb: Math.round(total / MB)
|
|
33398
|
+
};
|
|
33399
|
+
}
|
|
33400
|
+
};
|
|
33401
|
+
}
|
|
33402
|
+
function clampPct(n) {
|
|
33403
|
+
return round1(Math.min(100, Math.max(0, n)));
|
|
33404
|
+
}
|
|
33405
|
+
function round1(n) {
|
|
33406
|
+
return Math.round(n * 10) / 10;
|
|
33407
|
+
}
|
|
33408
|
+
|
|
33293
33409
|
// vendor/@4pm/ws/batcher.ts
|
|
33294
33410
|
var OutputBatcher = class {
|
|
33295
33411
|
opts;
|
|
@@ -33432,6 +33548,12 @@ var WsChannels = {
|
|
|
33432
33548
|
// whether a web viewer is attached) so an unwatched cli syncs nothing.
|
|
33433
33549
|
CONSOLE_SYNC: "console.sync",
|
|
33434
33550
|
CONSOLE_WATCH: "console.watch",
|
|
33551
|
+
// worker resource live-metrics (ADR-0214): the cli streams live CPU/RAM/disk (cgroup-accurate)
|
|
33552
|
+
// only while `metrics.watch` (server → cli: whether a web viewer has the Workers tab open) is on,
|
|
33553
|
+
// so an unwatched cli samples nothing. Buffered ephemerally in Redis (never the DB), separate from
|
|
33554
|
+
// the low-frequency, DB-authoritative `machine.status`.
|
|
33555
|
+
METRICS_WATCH: "metrics.watch",
|
|
33556
|
+
MACHINE_METRICS: "machine.metrics",
|
|
33435
33557
|
// AI spec-assist (suggest/review/compose) moved off dedicated ai.* channels to
|
|
33436
33558
|
// command.dispatch({ ai:true }) → the profile-failover AI path (ADR-0100).
|
|
33437
33559
|
// quota & usage (ADR-0020)
|
|
@@ -33449,6 +33571,10 @@ var WsChannels = {
|
|
|
33449
33571
|
// server → cli (reply): read a finished command's captured output on demand (ADR-0115) —
|
|
33450
33572
|
// cli stores command-output/{commandId}.log; returns null when pruned/unavailable.
|
|
33451
33573
|
COMMAND_OUTPUT_READ: "command.output-read",
|
|
33574
|
+
// server → cli (reply): flush pending data before a rented machine is scrubbed (ADR-0210) —
|
|
33575
|
+
// the worker uploads its pending log tail (+ any queued history) then acks, so the release
|
|
33576
|
+
// sweep can finalise without waiting the full 5-minute timeout when the worker is online.
|
|
33577
|
+
RENTAL_FLUSH: "rental.flush",
|
|
33452
33578
|
// outbound review (ADR-0082): machine→server request, server→outbound evaluate, result back
|
|
33453
33579
|
REVIEW_REQUEST: "review.request",
|
|
33454
33580
|
REVIEW_EVALUATE: "review.evaluate",
|
|
@@ -33514,7 +33640,7 @@ function decryptPayload(key, payloadBase64, nonceBase64) {
|
|
|
33514
33640
|
}
|
|
33515
33641
|
|
|
33516
33642
|
// src/core/ws-client.ts
|
|
33517
|
-
import { existsSync as
|
|
33643
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, rmSync as rmSync5 } from "fs";
|
|
33518
33644
|
import { randomUUID as randomCommandId } from "crypto";
|
|
33519
33645
|
|
|
33520
33646
|
// src/core/update-scheduler.ts
|
|
@@ -33920,14 +34046,14 @@ async function runAiFailover(plan, commandId, cwd2, handlers) {
|
|
|
33920
34046
|
}
|
|
33921
34047
|
|
|
33922
34048
|
// src/core/ai-profile-state.ts
|
|
33923
|
-
import { existsSync as
|
|
34049
|
+
import { existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
33924
34050
|
import { join as join7 } from "path";
|
|
33925
34051
|
function statePath(profileDir2) {
|
|
33926
34052
|
return join7(profileDir2, "ai-profiles.json");
|
|
33927
34053
|
}
|
|
33928
34054
|
function readState(profileDir2) {
|
|
33929
34055
|
const path = statePath(profileDir2);
|
|
33930
|
-
if (!
|
|
34056
|
+
if (!existsSync5(path)) return {};
|
|
33931
34057
|
try {
|
|
33932
34058
|
return JSON.parse(readFileSync7(path, "utf8"));
|
|
33933
34059
|
} catch {
|
|
@@ -34393,7 +34519,7 @@ async function evaluateReview(payload) {
|
|
|
34393
34519
|
}
|
|
34394
34520
|
|
|
34395
34521
|
// src/core/command-history.ts
|
|
34396
|
-
import { existsSync as
|
|
34522
|
+
import { existsSync as existsSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
34397
34523
|
import { join as join10 } from "path";
|
|
34398
34524
|
var MAX_ENTRIES = 1e3;
|
|
34399
34525
|
var filePath = null;
|
|
@@ -34457,7 +34583,7 @@ async function uploadHistory(cfg) {
|
|
|
34457
34583
|
}
|
|
34458
34584
|
function initCommandHistory(profileDir2, profileName, uploadMinutes = 10) {
|
|
34459
34585
|
filePath = join10(profileDir2, "command-history.json");
|
|
34460
|
-
if (
|
|
34586
|
+
if (existsSync6(filePath)) {
|
|
34461
34587
|
try {
|
|
34462
34588
|
entries = JSON.parse(readFileSync10(filePath, "utf8"));
|
|
34463
34589
|
} catch {
|
|
@@ -34515,7 +34641,7 @@ function finishCommand(commandId, exitCode, usage) {
|
|
|
34515
34641
|
// src/core/command-output-store.ts
|
|
34516
34642
|
import {
|
|
34517
34643
|
appendFileSync,
|
|
34518
|
-
existsSync as
|
|
34644
|
+
existsSync as existsSync7,
|
|
34519
34645
|
mkdirSync as mkdirSync3,
|
|
34520
34646
|
readFileSync as readFileSync11,
|
|
34521
34647
|
readdirSync as readdirSync3,
|
|
@@ -34543,7 +34669,7 @@ function appendCommandOutput(commandId, text) {
|
|
|
34543
34669
|
function readCommandOutput(commandId) {
|
|
34544
34670
|
if (!dir) return null;
|
|
34545
34671
|
const path = join11(dir, `${commandId}.log`);
|
|
34546
|
-
if (!
|
|
34672
|
+
if (!existsSync7(path)) return null;
|
|
34547
34673
|
try {
|
|
34548
34674
|
return readFileSync11(path, "utf8");
|
|
34549
34675
|
} catch {
|
|
@@ -34594,13 +34720,13 @@ async function listDir(path, root) {
|
|
|
34594
34720
|
}
|
|
34595
34721
|
|
|
34596
34722
|
// src/core/fs-read.ts
|
|
34597
|
-
import { readFile } from "fs/promises";
|
|
34723
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
34598
34724
|
import { resolve as resolve2 } from "path";
|
|
34599
34725
|
var MAX_BYTES = 256 * 1024;
|
|
34600
34726
|
async function readWorkerFile(path) {
|
|
34601
34727
|
const target = resolve2(path);
|
|
34602
34728
|
try {
|
|
34603
|
-
const buf = await
|
|
34729
|
+
const buf = await readFile2(target);
|
|
34604
34730
|
const truncated = buf.byteLength > MAX_BYTES;
|
|
34605
34731
|
const content = buf.subarray(0, MAX_BYTES).toString("utf8");
|
|
34606
34732
|
return { path: target, content, truncated };
|
|
@@ -34641,7 +34767,7 @@ async function writeWorkerFile(root, relPath, content) {
|
|
|
34641
34767
|
import { spawn as spawn4 } from "child_process";
|
|
34642
34768
|
import { execFile as execFile2 } from "child_process";
|
|
34643
34769
|
import { createHash as createHash3 } from "crypto";
|
|
34644
|
-
import { existsSync as
|
|
34770
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync12, statSync as statSync3 } from "fs";
|
|
34645
34771
|
import { join as join12 } from "path";
|
|
34646
34772
|
import { promisify as promisify2 } from "util";
|
|
34647
34773
|
var execFileAsync = promisify2(execFile2);
|
|
@@ -34662,7 +34788,7 @@ function authedUrl(url, token) {
|
|
|
34662
34788
|
}
|
|
34663
34789
|
async function ensureRepo(profileDir2, repo) {
|
|
34664
34790
|
const dir2 = repoDir(profileDir2, repo.url, repo.branch);
|
|
34665
|
-
if (
|
|
34791
|
+
if (existsSync8(join12(dir2, ".git"))) return dir2;
|
|
34666
34792
|
const url = authedUrl(repo.url, repo.token);
|
|
34667
34793
|
const gitEnv = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
|
|
34668
34794
|
mkdirSync4(kbRoot(profileDir2), { recursive: true });
|
|
@@ -34673,11 +34799,11 @@ async function ensureRepo(profileDir2, repo) {
|
|
|
34673
34799
|
}
|
|
34674
34800
|
async function refreshSupportKb(profileDir2) {
|
|
34675
34801
|
const root = kbRoot(profileDir2);
|
|
34676
|
-
if (!
|
|
34802
|
+
if (!existsSync8(root)) return;
|
|
34677
34803
|
const gitEnv = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
|
|
34678
34804
|
for (const entry of readdirSync4(root)) {
|
|
34679
34805
|
const dir2 = join12(root, entry);
|
|
34680
|
-
if (!
|
|
34806
|
+
if (!existsSync8(join12(dir2, ".git"))) continue;
|
|
34681
34807
|
try {
|
|
34682
34808
|
await execFileAsync("git", ["-C", dir2, "pull", "--ff-only"], { env: gitEnv });
|
|
34683
34809
|
} catch (err) {
|
|
@@ -34853,7 +34979,7 @@ async function runKnowledgeCompose(req, ai, physicRoot) {
|
|
|
34853
34979
|
}
|
|
34854
34980
|
|
|
34855
34981
|
// src/core/autonomous.ts
|
|
34856
|
-
import { readFile as
|
|
34982
|
+
import { readFile as readFile3, writeFile as writeFile2, chmod } from "fs/promises";
|
|
34857
34983
|
import { readFileSync as readFileSync13 } from "fs";
|
|
34858
34984
|
import { execFile as execFile3, spawn as spawn6 } from "child_process";
|
|
34859
34985
|
import { join as join13 } from "path";
|
|
@@ -34874,7 +35000,7 @@ var BOOK_FILES = {
|
|
|
34874
35000
|
var DEFAULT_CRON = "*/10 * * * *";
|
|
34875
35001
|
async function readText(path, fallback = "") {
|
|
34876
35002
|
try {
|
|
34877
|
-
return await
|
|
35003
|
+
return await readFile3(path, "utf8");
|
|
34878
35004
|
} catch {
|
|
34879
35005
|
return fallback;
|
|
34880
35006
|
}
|
|
@@ -35011,7 +35137,7 @@ ${req.content.trim()}
|
|
|
35011
35137
|
}
|
|
35012
35138
|
|
|
35013
35139
|
// src/core/agents.ts
|
|
35014
|
-
import { readFile as
|
|
35140
|
+
import { readFile as readFile4, writeFile as writeFile3, mkdir as mkdir3, readdir as readdir3, rm } from "fs/promises";
|
|
35015
35141
|
import { join as join14 } from "path";
|
|
35016
35142
|
var AGENTS_REL = ".claude/agents";
|
|
35017
35143
|
var SKILLS_REL = ".claude/skills";
|
|
@@ -35021,7 +35147,7 @@ function safeName(name) {
|
|
|
35021
35147
|
}
|
|
35022
35148
|
async function readText2(path, fallback = "") {
|
|
35023
35149
|
try {
|
|
35024
|
-
return await
|
|
35150
|
+
return await readFile4(path, "utf8");
|
|
35025
35151
|
} catch {
|
|
35026
35152
|
return fallback;
|
|
35027
35153
|
}
|
|
@@ -35090,7 +35216,7 @@ async function writeAgent(root, req) {
|
|
|
35090
35216
|
|
|
35091
35217
|
// src/core/packages.ts
|
|
35092
35218
|
import { createHash as createHash4 } from "crypto";
|
|
35093
|
-
import { readFile as
|
|
35219
|
+
import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4, readdir as readdir4, rm as rm2, stat } from "fs/promises";
|
|
35094
35220
|
import { dirname as dirname4, join as join15, relative } from "path";
|
|
35095
35221
|
var AGENTS_REL2 = ".claude/agents";
|
|
35096
35222
|
var SKILLS_REL2 = ".claude/skills";
|
|
@@ -35100,7 +35226,7 @@ function safeName2(name) {
|
|
|
35100
35226
|
}
|
|
35101
35227
|
async function readMaybe(path) {
|
|
35102
35228
|
try {
|
|
35103
|
-
return await
|
|
35229
|
+
return await readFile5(path);
|
|
35104
35230
|
} catch {
|
|
35105
35231
|
return null;
|
|
35106
35232
|
}
|
|
@@ -35324,21 +35450,21 @@ function applyGitAuth(method) {
|
|
|
35324
35450
|
}
|
|
35325
35451
|
|
|
35326
35452
|
// src/core/secrets.ts
|
|
35327
|
-
import { readFile as
|
|
35453
|
+
import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
35328
35454
|
import { join as join18 } from "path";
|
|
35329
35455
|
var SECURITY_REL = "AI_SECURITY.md";
|
|
35330
35456
|
var PLACEHOLDER_REL = "AI_PLACEHOLDER.md";
|
|
35331
35457
|
var SECRETS_REL = "project.secrets.json";
|
|
35332
35458
|
async function readText3(path, fallback = "") {
|
|
35333
35459
|
try {
|
|
35334
|
-
return await
|
|
35460
|
+
return await readFile6(path, "utf8");
|
|
35335
35461
|
} catch {
|
|
35336
35462
|
return fallback;
|
|
35337
35463
|
}
|
|
35338
35464
|
}
|
|
35339
35465
|
async function readSecretsObject(root) {
|
|
35340
35466
|
try {
|
|
35341
|
-
const v = JSON.parse(await
|
|
35467
|
+
const v = JSON.parse(await readFile6(join18(root, SECRETS_REL), "utf8"));
|
|
35342
35468
|
return v && typeof v === "object" ? v : {};
|
|
35343
35469
|
} catch {
|
|
35344
35470
|
return {};
|
|
@@ -35387,7 +35513,7 @@ async function writeSecrets(root, req) {
|
|
|
35387
35513
|
}
|
|
35388
35514
|
|
|
35389
35515
|
// src/core/agent-tools.ts
|
|
35390
|
-
import { readFile as
|
|
35516
|
+
import { readFile as readFile7, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
|
|
35391
35517
|
import { dirname as dirname5, join as join19 } from "path";
|
|
35392
35518
|
var SHARED_REL = ".claude/settings.json";
|
|
35393
35519
|
var LOCAL_REL = ".claude/settings.local.json";
|
|
@@ -35398,7 +35524,7 @@ function fileFor(root, scope) {
|
|
|
35398
35524
|
}
|
|
35399
35525
|
async function readSettingsObject(path) {
|
|
35400
35526
|
try {
|
|
35401
|
-
const v = JSON.parse(await
|
|
35527
|
+
const v = JSON.parse(await readFile7(path, "utf8"));
|
|
35402
35528
|
return v && typeof v === "object" ? v : {};
|
|
35403
35529
|
} catch {
|
|
35404
35530
|
return {};
|
|
@@ -35430,7 +35556,7 @@ async function writeAgentTools(root, scope, permissions) {
|
|
|
35430
35556
|
}
|
|
35431
35557
|
|
|
35432
35558
|
// src/core/graph.ts
|
|
35433
|
-
import { readFile as
|
|
35559
|
+
import { readFile as readFile8, readdir as readdir5 } from "fs/promises";
|
|
35434
35560
|
import { join as join20, relative as relative2, dirname as dirname6, resolve as resolve4, extname, basename as basename2 } from "path";
|
|
35435
35561
|
var MAX_FILES = 2e3;
|
|
35436
35562
|
var MAX_NODES = 3e3;
|
|
@@ -35479,7 +35605,7 @@ async function buildDocs(root) {
|
|
|
35479
35605
|
const rel = relative2(root, f).split("\\").join("/");
|
|
35480
35606
|
let text = "";
|
|
35481
35607
|
try {
|
|
35482
|
-
text = await
|
|
35608
|
+
text = await readFile8(f, "utf8");
|
|
35483
35609
|
} catch {
|
|
35484
35610
|
continue;
|
|
35485
35611
|
}
|
|
@@ -35518,7 +35644,7 @@ async function buildCode(root) {
|
|
|
35518
35644
|
const rel = relative2(root, f).split("\\").join("/");
|
|
35519
35645
|
let text = "";
|
|
35520
35646
|
try {
|
|
35521
|
-
text = await
|
|
35647
|
+
text = await readFile8(f, "utf8");
|
|
35522
35648
|
} catch {
|
|
35523
35649
|
continue;
|
|
35524
35650
|
}
|
|
@@ -35570,10 +35696,10 @@ async function buildGraph(root, mode) {
|
|
|
35570
35696
|
}
|
|
35571
35697
|
|
|
35572
35698
|
// src/core/rag.ts
|
|
35573
|
-
import { readFile as
|
|
35574
|
-
import { existsSync as
|
|
35699
|
+
import { readFile as readFile9, writeFile as writeFile7, statfs as statfs2, mkdir as mkdir6 } from "fs/promises";
|
|
35700
|
+
import { existsSync as existsSync9 } from "fs";
|
|
35575
35701
|
import { execFile as execFile4, spawn as spawn7 } from "child_process";
|
|
35576
|
-
import { totalmem, cpus } from "os";
|
|
35702
|
+
import { totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
35577
35703
|
import { join as join21 } from "path";
|
|
35578
35704
|
import { promisify as promisify4 } from "util";
|
|
35579
35705
|
var run3 = promisify4(execFile4);
|
|
@@ -35595,7 +35721,7 @@ async function detectPython() {
|
|
|
35595
35721
|
}
|
|
35596
35722
|
async function installLog(root) {
|
|
35597
35723
|
try {
|
|
35598
|
-
const text = await
|
|
35724
|
+
const text = await readFile9(join21(root, RAG_REL, "install.log"), "utf8");
|
|
35599
35725
|
return text.split("\n").filter((l) => l.length > 0).slice(-200);
|
|
35600
35726
|
} catch {
|
|
35601
35727
|
return [];
|
|
@@ -35643,28 +35769,28 @@ async function ragStatus(root) {
|
|
|
35643
35769
|
]) : [false, false, false];
|
|
35644
35770
|
let diskFreeGB = 0;
|
|
35645
35771
|
try {
|
|
35646
|
-
const s = await
|
|
35772
|
+
const s = await statfs2(root);
|
|
35647
35773
|
diskFreeGB = Math.round(s.bavail * s.bsize / 1e9 * 10) / 10;
|
|
35648
35774
|
} catch {
|
|
35649
35775
|
diskFreeGB = 0;
|
|
35650
35776
|
}
|
|
35651
35777
|
const gpu = (await tryRun("nvidia-smi", ["-L"])).ok;
|
|
35652
35778
|
const machine = {
|
|
35653
|
-
ramGB: Math.round(
|
|
35654
|
-
cpus:
|
|
35779
|
+
ramGB: Math.round(totalmem2() / 1e9 * 10) / 10,
|
|
35780
|
+
cpus: cpus2().length,
|
|
35655
35781
|
diskFreeGB,
|
|
35656
35782
|
gpu
|
|
35657
35783
|
};
|
|
35658
35784
|
let modelName = "";
|
|
35659
35785
|
try {
|
|
35660
|
-
modelName = (await
|
|
35786
|
+
modelName = (await readFile9(join21(root, RAG_REL, ".installed"), "utf8")).trim();
|
|
35661
35787
|
} catch {
|
|
35662
35788
|
modelName = "";
|
|
35663
35789
|
}
|
|
35664
35790
|
const modelPresent = modelName.length > 0;
|
|
35665
35791
|
return {
|
|
35666
35792
|
installed: fastembed && sqliteVec && modelPresent,
|
|
35667
|
-
installing:
|
|
35793
|
+
installing: existsSync9(join21(root, RAG_REL, ".installing")),
|
|
35668
35794
|
python: { found: py.found, version: py.version },
|
|
35669
35795
|
deps: { pip, fastembed, sqliteVec },
|
|
35670
35796
|
model: { present: modelPresent, name: modelName },
|
|
@@ -35675,9 +35801,9 @@ async function ragStatus(root) {
|
|
|
35675
35801
|
};
|
|
35676
35802
|
}
|
|
35677
35803
|
async function readIndexMeta(root) {
|
|
35678
|
-
const indexing =
|
|
35804
|
+
const indexing = existsSync9(join21(root, RAG_REL, ".indexing"));
|
|
35679
35805
|
try {
|
|
35680
|
-
const m = JSON.parse(await
|
|
35806
|
+
const m = JSON.parse(await readFile9(join21(root, RAG_REL, ".index.json"), "utf8"));
|
|
35681
35807
|
return {
|
|
35682
35808
|
present: true,
|
|
35683
35809
|
indexing,
|
|
@@ -35750,8 +35876,8 @@ async function ensureEngine(root) {
|
|
|
35750
35876
|
async function ragReindex(root) {
|
|
35751
35877
|
const py = await detectPython();
|
|
35752
35878
|
if (!py.found) return { ok: false, started: false, error: "python3 not found" };
|
|
35753
|
-
if (!
|
|
35754
|
-
if (
|
|
35879
|
+
if (!existsSync9(join21(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
|
|
35880
|
+
if (existsSync9(join21(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
|
|
35755
35881
|
await ensureEngine(root);
|
|
35756
35882
|
const script = `
|
|
35757
35883
|
set +e
|
|
@@ -35771,7 +35897,7 @@ rm -f "${RAG_REL}/.indexing"
|
|
|
35771
35897
|
async function ragQuery(root, queryText, k = 8) {
|
|
35772
35898
|
const py = await detectPython();
|
|
35773
35899
|
if (!py.found) return { results: [], error: "python3 not found" };
|
|
35774
|
-
if (!
|
|
35900
|
+
if (!existsSync9(join21(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
|
|
35775
35901
|
await ensureEngine(root);
|
|
35776
35902
|
try {
|
|
35777
35903
|
const { stdout } = await run3(
|
|
@@ -35788,7 +35914,7 @@ async function ragQuery(root, queryText, k = 8) {
|
|
|
35788
35914
|
async function ragInstall(root, model) {
|
|
35789
35915
|
const py = await detectPython();
|
|
35790
35916
|
if (!py.found) return { ok: false, started: false, error: "python3 not found on the worker" };
|
|
35791
|
-
if (
|
|
35917
|
+
if (existsSync9(join21(root, RAG_REL, ".installing"))) return { ok: true, started: true };
|
|
35792
35918
|
try {
|
|
35793
35919
|
await mkdir6(join21(root, RAG_REL), { recursive: true });
|
|
35794
35920
|
} catch {
|
|
@@ -35817,7 +35943,7 @@ rm -f "${RAG_REL}/.installing"
|
|
|
35817
35943
|
}
|
|
35818
35944
|
|
|
35819
35945
|
// src/core/git-diff.ts
|
|
35820
|
-
import { readFile as
|
|
35946
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
35821
35947
|
import { execFile as execFile5 } from "child_process";
|
|
35822
35948
|
import { basename as basename3, dirname as dirname7, resolve as resolve5 } from "path";
|
|
35823
35949
|
import { promisify as promisify5 } from "util";
|
|
@@ -35828,7 +35954,7 @@ async function gitDiff(path) {
|
|
|
35828
35954
|
const base = basename3(target);
|
|
35829
35955
|
let newContent = "";
|
|
35830
35956
|
try {
|
|
35831
|
-
newContent = await
|
|
35957
|
+
newContent = await readFile10(target, "utf8");
|
|
35832
35958
|
} catch {
|
|
35833
35959
|
}
|
|
35834
35960
|
let oldContent = "";
|
|
@@ -35877,7 +36003,7 @@ ${stderr}`;
|
|
|
35877
36003
|
|
|
35878
36004
|
// src/core/git-ssh-key.ts
|
|
35879
36005
|
import { execFile as execFile7 } from "child_process";
|
|
35880
|
-
import { readFile as
|
|
36006
|
+
import { readFile as readFile11, rm as rm3, mkdir as mkdir7 } from "fs/promises";
|
|
35881
36007
|
import { join as join22 } from "path";
|
|
35882
36008
|
import { promisify as promisify7 } from "util";
|
|
35883
36009
|
var run6 = promisify7(execFile7);
|
|
@@ -35888,7 +36014,7 @@ function keyPaths(profileDir2) {
|
|
|
35888
36014
|
}
|
|
35889
36015
|
async function readPublicKey(pub) {
|
|
35890
36016
|
try {
|
|
35891
|
-
return (await
|
|
36017
|
+
return (await readFile11(pub, "utf8")).trim();
|
|
35892
36018
|
} catch {
|
|
35893
36019
|
return null;
|
|
35894
36020
|
}
|
|
@@ -36197,12 +36323,12 @@ async function addProject(payload, profileDir2, onProgress) {
|
|
|
36197
36323
|
}
|
|
36198
36324
|
|
|
36199
36325
|
// src/core/config-sync.ts
|
|
36200
|
-
import { existsSync as
|
|
36326
|
+
import { existsSync as existsSync10, readFileSync as readFileSync14 } from "fs";
|
|
36201
36327
|
import { join as join24 } from "path";
|
|
36202
36328
|
var SERVER_MANAGED_KEYS = ["physicPath", "sessionSwitchPct", "perPromptTokenLimit"];
|
|
36203
36329
|
function readConfigText(profileDir2) {
|
|
36204
36330
|
const path = join24(profileDir2, "config.json");
|
|
36205
|
-
if (
|
|
36331
|
+
if (existsSync10(path)) {
|
|
36206
36332
|
try {
|
|
36207
36333
|
return readFileSync14(path, "utf8");
|
|
36208
36334
|
} catch {
|
|
@@ -36363,6 +36489,8 @@ var CLOSE_DRAINING = 4001;
|
|
|
36363
36489
|
var OFFLINE_BUFFER_MAX_BYTES = 512 * 1024;
|
|
36364
36490
|
var CONSOLE_SNAPSHOT_INTERVAL_MS = 3e4;
|
|
36365
36491
|
var CONSOLE_WATCH_LEASE_MS = 45e3;
|
|
36492
|
+
var METRICS_SAMPLE_INTERVAL_MS = 5e3;
|
|
36493
|
+
var METRICS_WATCH_LEASE_MS = 45e3;
|
|
36366
36494
|
function toDtoEntry(e) {
|
|
36367
36495
|
return { id: e.id, source: e.source, kind: e.kind, text: e.text, level: e.level, resultKind: e.resultKind };
|
|
36368
36496
|
}
|
|
@@ -36455,6 +36583,16 @@ var WsClient = class {
|
|
|
36455
36583
|
consoleSnapshotTimer = null;
|
|
36456
36584
|
/** Lease expiry: stop syncing if the server does not renew `console.watch` in time. */
|
|
36457
36585
|
consoleWatchExpiry = null;
|
|
36586
|
+
/**
|
|
36587
|
+
* Worker-metrics state (ADR-0214). `metricsWatching` is set by `metrics.watch` (a viewer has the
|
|
36588
|
+
* Workers tab open); only then does the cli sample + emit `machine.metrics`. The sampler holds the
|
|
36589
|
+
* CPU-delta baseline; both timers are lease-driven like console-sync and reset on reconnect.
|
|
36590
|
+
*/
|
|
36591
|
+
metricsWatching = false;
|
|
36592
|
+
metricsTimer = null;
|
|
36593
|
+
metricsWatchExpiry = null;
|
|
36594
|
+
/** Lazily created on the first `metrics.watch` (keeps the CPU-delta baseline across samples). */
|
|
36595
|
+
metricsSampler = null;
|
|
36458
36596
|
/** cli → server requests awaiting a reply (quota.check — ADR-0020). */
|
|
36459
36597
|
pending = /* @__PURE__ */ new Map();
|
|
36460
36598
|
/** Daily scheduled cli auto-update (ADR-0074); policy fed from each ws_token. */
|
|
@@ -36690,6 +36828,7 @@ var WsClient = class {
|
|
|
36690
36828
|
}
|
|
36691
36829
|
logger.info("ws.connected", { version: CLI_VERSION });
|
|
36692
36830
|
this.stopConsoleSync();
|
|
36831
|
+
this.stopMetricsSync();
|
|
36693
36832
|
this.bus.setStatus("connected");
|
|
36694
36833
|
this.finishReconnect();
|
|
36695
36834
|
this.sendMachineStatus();
|
|
@@ -37152,6 +37291,11 @@ var WsClient = class {
|
|
|
37152
37291
|
this.send(WsChannels.COMMAND_OUTPUT_READ, { output }, message.id);
|
|
37153
37292
|
break;
|
|
37154
37293
|
}
|
|
37294
|
+
case WsChannels.RENTAL_FLUSH: {
|
|
37295
|
+
this.uploadLog();
|
|
37296
|
+
this.send(WsChannels.RENTAL_FLUSH, { ok: true }, message.id);
|
|
37297
|
+
break;
|
|
37298
|
+
}
|
|
37155
37299
|
case WsChannels.PROJECT_CREATE:
|
|
37156
37300
|
void scaffoldProject(
|
|
37157
37301
|
payload,
|
|
@@ -37176,6 +37320,9 @@ var WsClient = class {
|
|
|
37176
37320
|
case WsChannels.CONSOLE_WATCH:
|
|
37177
37321
|
this.setConsoleWatching(payload.on);
|
|
37178
37322
|
break;
|
|
37323
|
+
case WsChannels.METRICS_WATCH:
|
|
37324
|
+
this.setMetricsWatching(payload.on);
|
|
37325
|
+
break;
|
|
37179
37326
|
default:
|
|
37180
37327
|
this.bus.log(`Unsupported channel: ${message.channel}`, "warn");
|
|
37181
37328
|
}
|
|
@@ -37605,6 +37752,50 @@ var WsClient = class {
|
|
|
37605
37752
|
this.consoleWatchExpiry = null;
|
|
37606
37753
|
}
|
|
37607
37754
|
}
|
|
37755
|
+
/**
|
|
37756
|
+
* Handle `metrics.watch` (ADR-0214) — a renewable lease mirroring `console.watch`. `on` renews it:
|
|
37757
|
+
* on the first assertion the cli samples immediately + starts the ~5s sampler; later renewals just
|
|
37758
|
+
* extend the lease. No renewal within `METRICS_WATCH_LEASE_MS` (or an explicit `on:false`) ⇒ stop.
|
|
37759
|
+
*/
|
|
37760
|
+
setMetricsWatching(on) {
|
|
37761
|
+
if (!on) {
|
|
37762
|
+
this.stopMetricsSync();
|
|
37763
|
+
return;
|
|
37764
|
+
}
|
|
37765
|
+
if (this.metricsWatchExpiry) clearTimeout(this.metricsWatchExpiry);
|
|
37766
|
+
this.metricsWatchExpiry = setTimeout(() => this.stopMetricsSync(), METRICS_WATCH_LEASE_MS);
|
|
37767
|
+
this.metricsWatchExpiry.unref?.();
|
|
37768
|
+
if (this.metricsWatching) return;
|
|
37769
|
+
this.metricsWatching = true;
|
|
37770
|
+
void this.sendMachineMetrics();
|
|
37771
|
+
this.metricsTimer = setInterval(() => void this.sendMachineMetrics(), METRICS_SAMPLE_INTERVAL_MS);
|
|
37772
|
+
this.metricsTimer.unref?.();
|
|
37773
|
+
}
|
|
37774
|
+
/** Stop sampling worker resources (lease expired / viewer left / reconnect) — clears both timers. */
|
|
37775
|
+
stopMetricsSync() {
|
|
37776
|
+
this.metricsWatching = false;
|
|
37777
|
+
if (this.metricsTimer) {
|
|
37778
|
+
clearInterval(this.metricsTimer);
|
|
37779
|
+
this.metricsTimer = null;
|
|
37780
|
+
}
|
|
37781
|
+
if (this.metricsWatchExpiry) {
|
|
37782
|
+
clearTimeout(this.metricsWatchExpiry);
|
|
37783
|
+
this.metricsWatchExpiry = null;
|
|
37784
|
+
}
|
|
37785
|
+
}
|
|
37786
|
+
/** Sample the worker's live CPU/RAM/disk and push a `machine.metrics` frame (ADR-0214). */
|
|
37787
|
+
async sendMachineMetrics() {
|
|
37788
|
+
if (!this.sessionKey) return;
|
|
37789
|
+
try {
|
|
37790
|
+
if (!this.metricsSampler) this.metricsSampler = createWorkerMetricsSampler(this.context.profileDir);
|
|
37791
|
+
const resources = await this.metricsSampler.sample();
|
|
37792
|
+
this.send(WsChannels.MACHINE_METRICS, {
|
|
37793
|
+
fingerprint: this.context.machine.fingerprint,
|
|
37794
|
+
resources
|
|
37795
|
+
});
|
|
37796
|
+
} catch {
|
|
37797
|
+
}
|
|
37798
|
+
}
|
|
37608
37799
|
/**
|
|
37609
37800
|
* Send a cli → server request and await the reply by envelope id (15s timeout).
|
|
37610
37801
|
* Used for quota.check before spawning an AI cli (ADR-0020).
|
|
@@ -37677,7 +37868,7 @@ var WsClient = class {
|
|
|
37677
37868
|
/** Create the physic folder at an absolute path if missing (best-effort). */
|
|
37678
37869
|
ensurePhysicFolderPath(folder) {
|
|
37679
37870
|
try {
|
|
37680
|
-
if (!
|
|
37871
|
+
if (!existsSync11(folder)) {
|
|
37681
37872
|
mkdirSync5(folder, { recursive: true });
|
|
37682
37873
|
this.bus.log(`Created physic project folder: ${folder}`);
|
|
37683
37874
|
}
|
|
@@ -37706,7 +37897,7 @@ var WsClient = class {
|
|
|
37706
37897
|
projects: [],
|
|
37707
37898
|
version: CLI_VERSION,
|
|
37708
37899
|
physicPath,
|
|
37709
|
-
physicPathExists: physicPath ?
|
|
37900
|
+
physicPathExists: physicPath ? existsSync11(physicPath) : void 0,
|
|
37710
37901
|
// Real coarse state (ADR-0152): !paused and a recent tick (was hard-coded false).
|
|
37711
37902
|
autonomousRunning: physicPath ? isAutonomousRunning(physicPath) : false
|
|
37712
37903
|
});
|
|
@@ -37729,6 +37920,7 @@ var WsClient = class {
|
|
|
37729
37920
|
this.logUploadTimer = null;
|
|
37730
37921
|
if (this.kbRefreshTimer) clearInterval(this.kbRefreshTimer);
|
|
37731
37922
|
this.kbRefreshTimer = null;
|
|
37923
|
+
this.stopMetricsSync();
|
|
37732
37924
|
}
|
|
37733
37925
|
/**
|
|
37734
37926
|
* Poll the Claude subscription usage API and push a `machine.usage` snapshot (ADR-0072).
|
|
@@ -38415,12 +38607,12 @@ var isWebWorker = typeof WorkerGlobalScope !== "undefined" && globalThis instanc
|
|
|
38415
38607
|
var isDedicatedWorker = typeof DedicatedWorkerGlobalScope !== "undefined" && globalThis instanceof DedicatedWorkerGlobalScope;
|
|
38416
38608
|
var isSharedWorker = typeof SharedWorkerGlobalScope !== "undefined" && globalThis instanceof SharedWorkerGlobalScope;
|
|
38417
38609
|
var isServiceWorker = typeof ServiceWorkerGlobalScope !== "undefined" && globalThis instanceof ServiceWorkerGlobalScope;
|
|
38418
|
-
var
|
|
38419
|
-
var isMacOs =
|
|
38420
|
-
var isWindows =
|
|
38421
|
-
var isLinux =
|
|
38422
|
-
var isIos =
|
|
38423
|
-
var isAndroid =
|
|
38610
|
+
var platform2 = globalThis.navigator?.userAgentData?.platform;
|
|
38611
|
+
var isMacOs = platform2 === "macOS" || globalThis.navigator?.platform === "MacIntel" || globalThis.navigator?.userAgent?.includes(" Mac ") === true || globalThis.process?.platform === "darwin";
|
|
38612
|
+
var isWindows = platform2 === "Windows" || globalThis.navigator?.platform === "Win32" || globalThis.process?.platform === "win32";
|
|
38613
|
+
var isLinux = platform2 === "Linux" || globalThis.navigator?.platform?.startsWith("Linux") === true || globalThis.navigator?.userAgent?.includes(" Linux ") === true || globalThis.process?.platform === "linux";
|
|
38614
|
+
var isIos = platform2 === "iOS" || globalThis.navigator?.platform === "MacIntel" && globalThis.navigator?.maxTouchPoints > 1 || /iPad|iPhone|iPod/.test(globalThis.navigator?.platform);
|
|
38615
|
+
var isAndroid = platform2 === "Android" || globalThis.navigator?.platform === "Android" || globalThis.navigator?.userAgent?.includes(" Android ") === true || globalThis.process?.platform === "android";
|
|
38424
38616
|
|
|
38425
38617
|
// ../../node_modules/.pnpm/ansi-escapes@7.3.0/node_modules/ansi-escapes/base.js
|
|
38426
38618
|
var ESC = "\x1B[";
|
|
@@ -43874,7 +44066,7 @@ var import_react21 = __toESM(require_react(), 1);
|
|
|
43874
44066
|
var import_react26 = __toESM(require_react(), 1);
|
|
43875
44067
|
|
|
43876
44068
|
// src/core/input-history.ts
|
|
43877
|
-
import { existsSync as
|
|
44069
|
+
import { existsSync as existsSync13, readFileSync as readFileSync17, writeFileSync as writeFileSync9 } from "fs";
|
|
43878
44070
|
import { join as join28 } from "path";
|
|
43879
44071
|
var MAX_HISTORY = 200;
|
|
43880
44072
|
function historyPath(profileDir2) {
|
|
@@ -43882,7 +44074,7 @@ function historyPath(profileDir2) {
|
|
|
43882
44074
|
}
|
|
43883
44075
|
function loadInputHistory(profileDir2) {
|
|
43884
44076
|
const path = historyPath(profileDir2);
|
|
43885
|
-
if (!
|
|
44077
|
+
if (!existsSync13(path)) return [];
|
|
43886
44078
|
try {
|
|
43887
44079
|
const data = JSON.parse(readFileSync17(path, "utf8"));
|
|
43888
44080
|
return Array.isArray(data) ? data.filter((x) => typeof x === "string") : [];
|
|
@@ -44144,7 +44336,7 @@ function TranscriptLine({ line }) {
|
|
|
44144
44336
|
var import_react24 = __toESM(require_react(), 1);
|
|
44145
44337
|
|
|
44146
44338
|
// src/ui/slash-commands.ts
|
|
44147
|
-
import { existsSync as
|
|
44339
|
+
import { existsSync as existsSync14 } from "fs";
|
|
44148
44340
|
import { join as join29 } from "path";
|
|
44149
44341
|
var CONFIG_KEYS = [
|
|
44150
44342
|
"aiCli",
|
|
@@ -44207,7 +44399,7 @@ function runConfig(ctx) {
|
|
|
44207
44399
|
ctx.print(`\u2714 ${verb} default config: ${path}`);
|
|
44208
44400
|
ctx.print("add claude profiles: /config set claudeHome .claude .claude-1 (tried in order)");
|
|
44209
44401
|
};
|
|
44210
|
-
if (
|
|
44402
|
+
if (existsSync14(path)) {
|
|
44211
44403
|
ctx.confirm(
|
|
44212
44404
|
`config exists: ${path} \u2014 replace it with defaults? (y/N)`,
|
|
44213
44405
|
() => writeDefaults("replaced with")
|