@4pm/cli 1.4.2 → 1.4.3
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 +174 -102
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28550,7 +28550,7 @@ ${INSECURE_URL_WARNING}
|
|
|
28550
28550
|
|
|
28551
28551
|
// src/commands/start.ts
|
|
28552
28552
|
import { spawn as spawn9 } from "child_process";
|
|
28553
|
-
import { join as
|
|
28553
|
+
import { join as join29 } from "path";
|
|
28554
28554
|
|
|
28555
28555
|
// src/core/update.ts
|
|
28556
28556
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -28723,7 +28723,7 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
|
|
|
28723
28723
|
// src/core/ws-client.ts
|
|
28724
28724
|
init_wrapper();
|
|
28725
28725
|
import { randomUUID } from "crypto";
|
|
28726
|
-
import { basename as basename4, join as
|
|
28726
|
+
import { basename as basename4, join as join24 } from "path";
|
|
28727
28727
|
|
|
28728
28728
|
// vendor/@4pm/utils/helpers.ts
|
|
28729
28729
|
function backoffJitterMs(attempt, baseMs = 500, maxMs = 1e4) {
|
|
@@ -33087,6 +33087,19 @@ var createProjectRequestSchema = external_exports.object({
|
|
|
33087
33087
|
var copyProjectRequestSchema = external_exports.object({
|
|
33088
33088
|
name: external_exports.string().min(1).max(100)
|
|
33089
33089
|
});
|
|
33090
|
+
var PROJECT_ALERT_KINDS = ["budgetPercent", "absolute"];
|
|
33091
|
+
var projectAlertRuleSchema = external_exports.object({
|
|
33092
|
+
id: external_exports.string().min(1).max(64),
|
|
33093
|
+
kind: external_exports.enum(PROJECT_ALERT_KINDS),
|
|
33094
|
+
metric: external_exports.enum([UsageMetric.AI_TOKENS, UsageMetric.COMMANDS]),
|
|
33095
|
+
threshold: external_exports.number().int().min(1),
|
|
33096
|
+
email: external_exports.union([external_exports.string().email(), external_exports.literal("")]).optional(),
|
|
33097
|
+
notifyUserId: external_exports.union([external_exports.string().uuid(), external_exports.literal("")]).optional(),
|
|
33098
|
+
enabled: external_exports.boolean().default(true)
|
|
33099
|
+
});
|
|
33100
|
+
var projectAlertsSchema = external_exports.object({
|
|
33101
|
+
rules: external_exports.array(projectAlertRuleSchema).max(20).default([])
|
|
33102
|
+
});
|
|
33090
33103
|
var DEFAULT_OUTBOUND_RULES = {
|
|
33091
33104
|
secret: [
|
|
33092
33105
|
String.raw`/\b(api[_-]?key|secret|password|passwd|access[_-]?token|bearer)\b\s*[:=]\s*\S+/i`,
|
|
@@ -33729,7 +33742,7 @@ function decryptPayload(key, payloadBase64, nonceBase64) {
|
|
|
33729
33742
|
}
|
|
33730
33743
|
|
|
33731
33744
|
// src/core/ws-client.ts
|
|
33732
|
-
import { existsSync as
|
|
33745
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, rmSync as rmSync4 } from "fs";
|
|
33733
33746
|
import { randomUUID as randomCommandId } from "crypto";
|
|
33734
33747
|
|
|
33735
33748
|
// src/core/update-scheduler.ts
|
|
@@ -35492,28 +35505,87 @@ async function removePackage(root, slug) {
|
|
|
35492
35505
|
}
|
|
35493
35506
|
}
|
|
35494
35507
|
|
|
35495
|
-
// src/core/
|
|
35496
|
-
import {
|
|
35497
|
-
import {
|
|
35498
|
-
var
|
|
35499
|
-
|
|
35500
|
-
|
|
35508
|
+
// src/core/network-probe.ts
|
|
35509
|
+
import { existsSync as existsSync9, readFileSync as readFileSync14 } from "fs";
|
|
35510
|
+
import { connect } from "net";
|
|
35511
|
+
var CANARY_TARGETS = [
|
|
35512
|
+
{ host: "1.1.1.1", port: 443 },
|
|
35513
|
+
{ host: "8.8.8.8", port: 443 }
|
|
35514
|
+
];
|
|
35515
|
+
var PROBE_TIMEOUT_MS = 2500;
|
|
35516
|
+
function detectContainerized() {
|
|
35501
35517
|
try {
|
|
35502
|
-
if (
|
|
35503
|
-
|
|
35504
|
-
|
|
35505
|
-
|
|
35506
|
-
|
|
35507
|
-
|
|
35518
|
+
if (existsSync9("/.dockerenv")) return true;
|
|
35519
|
+
const cgroup = readFileSync14("/proc/1/cgroup", "utf8");
|
|
35520
|
+
return /docker|containerd|kubepods|libpod/.test(cgroup);
|
|
35521
|
+
} catch {
|
|
35522
|
+
return false;
|
|
35523
|
+
}
|
|
35524
|
+
}
|
|
35525
|
+
function canReach(host, port) {
|
|
35526
|
+
return new Promise((resolve8) => {
|
|
35527
|
+
const socket = connect({ host, port });
|
|
35528
|
+
let done = false;
|
|
35529
|
+
const finish = (ok) => {
|
|
35530
|
+
if (done) return;
|
|
35531
|
+
done = true;
|
|
35532
|
+
socket.destroy();
|
|
35533
|
+
resolve8(ok);
|
|
35534
|
+
};
|
|
35535
|
+
socket.setTimeout(PROBE_TIMEOUT_MS);
|
|
35536
|
+
socket.once("connect", () => finish(true));
|
|
35537
|
+
socket.once("timeout", () => finish(false));
|
|
35538
|
+
socket.once("error", () => finish(false));
|
|
35539
|
+
});
|
|
35540
|
+
}
|
|
35541
|
+
async function probeOutbound() {
|
|
35542
|
+
try {
|
|
35543
|
+
const results = await Promise.all(CANARY_TARGETS.map((t) => canReach(t.host, t.port)));
|
|
35544
|
+
return results.some(Boolean) ? "open" : "restricted";
|
|
35545
|
+
} catch {
|
|
35546
|
+
return "restricted";
|
|
35547
|
+
}
|
|
35548
|
+
}
|
|
35549
|
+
function probeInbound() {
|
|
35550
|
+
const isExposed = (path, loopbackHexPrefixes) => {
|
|
35551
|
+
let body;
|
|
35552
|
+
try {
|
|
35553
|
+
body = readFileSync14(path, "utf8");
|
|
35554
|
+
} catch {
|
|
35555
|
+
return false;
|
|
35556
|
+
}
|
|
35557
|
+
for (const line of body.split("\n").slice(1)) {
|
|
35558
|
+
const cols = line.trim().split(/\s+/);
|
|
35559
|
+
const local = cols[1];
|
|
35560
|
+
const st = cols[3];
|
|
35561
|
+
if (!local || st !== "0A") continue;
|
|
35562
|
+
const addrHex = local.split(":")[0]?.toUpperCase() ?? "";
|
|
35563
|
+
if (loopbackHexPrefixes.some((p) => addrHex === p)) continue;
|
|
35564
|
+
return true;
|
|
35508
35565
|
}
|
|
35566
|
+
return false;
|
|
35567
|
+
};
|
|
35568
|
+
try {
|
|
35569
|
+
const v4 = isExposed("/proc/net/tcp", ["0100007F"]);
|
|
35570
|
+
const v6 = isExposed("/proc/net/tcp6", ["00000000000000000000000001000000"]);
|
|
35571
|
+
return v4 || v6 ? "exposed" : "isolated";
|
|
35509
35572
|
} catch {
|
|
35573
|
+
return "isolated";
|
|
35510
35574
|
}
|
|
35511
35575
|
}
|
|
35576
|
+
async function probeNetwork() {
|
|
35577
|
+
return {
|
|
35578
|
+
outbound: await probeOutbound(),
|
|
35579
|
+
inbound: probeInbound(),
|
|
35580
|
+
containerized: detectContainerized(),
|
|
35581
|
+
checkedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
35582
|
+
};
|
|
35583
|
+
}
|
|
35512
35584
|
|
|
35513
35585
|
// src/core/git-auth.ts
|
|
35514
|
-
import { writeFileSync as
|
|
35586
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
35515
35587
|
import { homedir as homedir4 } from "os";
|
|
35516
|
-
import { join as
|
|
35588
|
+
import { join as join16 } from "path";
|
|
35517
35589
|
function tokenTarget(method) {
|
|
35518
35590
|
if (method === "gitlab-group-token") {
|
|
35519
35591
|
return { host: process.env.FOURPM_GIT_HOST || "gitlab.com", user: "oauth2" };
|
|
@@ -35531,7 +35603,7 @@ function applyGitAuth(method) {
|
|
|
35531
35603
|
try {
|
|
35532
35604
|
const line = `https://${target.user}:${token}@${target.host}
|
|
35533
35605
|
`;
|
|
35534
|
-
|
|
35606
|
+
writeFileSync6(join16(homedir4(), ".git-credentials"), line, { encoding: "utf8", mode: 384 });
|
|
35535
35607
|
return true;
|
|
35536
35608
|
} catch {
|
|
35537
35609
|
return false;
|
|
@@ -35540,7 +35612,7 @@ function applyGitAuth(method) {
|
|
|
35540
35612
|
|
|
35541
35613
|
// src/core/secrets.ts
|
|
35542
35614
|
import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
35543
|
-
import { join as
|
|
35615
|
+
import { join as join17 } from "path";
|
|
35544
35616
|
var SECURITY_REL = "AI_SECURITY.md";
|
|
35545
35617
|
var PLACEHOLDER_REL = "AI_PLACEHOLDER.md";
|
|
35546
35618
|
var SECRETS_REL = "project.secrets.json";
|
|
@@ -35553,7 +35625,7 @@ async function readText3(path, fallback = "") {
|
|
|
35553
35625
|
}
|
|
35554
35626
|
async function readSecretsObject(root) {
|
|
35555
35627
|
try {
|
|
35556
|
-
const v = JSON.parse(await readFile6(
|
|
35628
|
+
const v = JSON.parse(await readFile6(join17(root, SECRETS_REL), "utf8"));
|
|
35557
35629
|
return v && typeof v === "object" ? v : {};
|
|
35558
35630
|
} catch {
|
|
35559
35631
|
return {};
|
|
@@ -35572,8 +35644,8 @@ function placeholderKeys(text) {
|
|
|
35572
35644
|
}
|
|
35573
35645
|
async function readSecrets(root) {
|
|
35574
35646
|
const [security, placeholder, secrets] = await Promise.all([
|
|
35575
|
-
readText3(
|
|
35576
|
-
readText3(
|
|
35647
|
+
readText3(join17(root, SECURITY_REL)),
|
|
35648
|
+
readText3(join17(root, PLACEHOLDER_REL)),
|
|
35577
35649
|
readSecretsObject(root)
|
|
35578
35650
|
]);
|
|
35579
35651
|
const names = /* @__PURE__ */ new Set([...placeholderKeys(placeholder), ...Object.keys(secrets)]);
|
|
@@ -35586,14 +35658,14 @@ async function readSecrets(root) {
|
|
|
35586
35658
|
async function writeSecrets(root, req) {
|
|
35587
35659
|
try {
|
|
35588
35660
|
if (req.kind === "security") {
|
|
35589
|
-
await writeFile5(
|
|
35661
|
+
await writeFile5(join17(root, SECURITY_REL), req.content, "utf8");
|
|
35590
35662
|
} else if (req.kind === "placeholder") {
|
|
35591
|
-
await writeFile5(
|
|
35663
|
+
await writeFile5(join17(root, PLACEHOLDER_REL), req.content, "utf8");
|
|
35592
35664
|
} else {
|
|
35593
35665
|
const secrets = await readSecretsObject(root);
|
|
35594
35666
|
if (req.kind === "secret") secrets[req.key] = req.value;
|
|
35595
35667
|
else delete secrets[req.key];
|
|
35596
|
-
await writeFile5(
|
|
35668
|
+
await writeFile5(join17(root, SECRETS_REL), JSON.stringify(secrets, null, 2) + "\n", "utf8");
|
|
35597
35669
|
}
|
|
35598
35670
|
return { ok: true };
|
|
35599
35671
|
} catch (err) {
|
|
@@ -35603,13 +35675,13 @@ async function writeSecrets(root, req) {
|
|
|
35603
35675
|
|
|
35604
35676
|
// src/core/agent-tools.ts
|
|
35605
35677
|
import { readFile as readFile7, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
|
|
35606
|
-
import { dirname as dirname5, join as
|
|
35678
|
+
import { dirname as dirname5, join as join18 } from "path";
|
|
35607
35679
|
var SHARED_REL = ".claude/settings.json";
|
|
35608
35680
|
var LOCAL_REL = ".claude/settings.local.json";
|
|
35609
35681
|
var SECRETS_DENY = "Read(./project.secrets.json)";
|
|
35610
35682
|
var MODES = ["default", "acceptEdits", "plan", "bypassPermissions"];
|
|
35611
35683
|
function fileFor(root, scope) {
|
|
35612
|
-
return
|
|
35684
|
+
return join18(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
|
|
35613
35685
|
}
|
|
35614
35686
|
async function readSettingsObject(path) {
|
|
35615
35687
|
try {
|
|
@@ -35646,7 +35718,7 @@ async function writeAgentTools(root, scope, permissions) {
|
|
|
35646
35718
|
|
|
35647
35719
|
// src/core/graph.ts
|
|
35648
35720
|
import { readFile as readFile8, readdir as readdir5 } from "fs/promises";
|
|
35649
|
-
import { join as
|
|
35721
|
+
import { join as join19, relative as relative2, dirname as dirname6, resolve as resolve4, extname, basename as basename2 } from "path";
|
|
35650
35722
|
var MAX_FILES = 2e3;
|
|
35651
35723
|
var MAX_NODES = 3e3;
|
|
35652
35724
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "out", "coverage", ".turbo"]);
|
|
@@ -35666,7 +35738,7 @@ async function walk(root, accept) {
|
|
|
35666
35738
|
if (e.name.startsWith(".") && e.name !== ".claude") {
|
|
35667
35739
|
if (e.isDirectory()) continue;
|
|
35668
35740
|
}
|
|
35669
|
-
const full =
|
|
35741
|
+
const full = join19(dir2, e.name);
|
|
35670
35742
|
if (e.isDirectory()) {
|
|
35671
35743
|
if (!SKIP_DIRS.has(e.name)) stack.push(full);
|
|
35672
35744
|
} else if (accept(full)) {
|
|
@@ -35786,10 +35858,10 @@ async function buildGraph(root, mode) {
|
|
|
35786
35858
|
|
|
35787
35859
|
// src/core/rag.ts
|
|
35788
35860
|
import { readFile as readFile9, writeFile as writeFile7, statfs as statfs2, mkdir as mkdir6 } from "fs/promises";
|
|
35789
|
-
import { existsSync as
|
|
35861
|
+
import { existsSync as existsSync10 } from "fs";
|
|
35790
35862
|
import { execFile as execFile4, spawn as spawn7 } from "child_process";
|
|
35791
35863
|
import { totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
35792
|
-
import { join as
|
|
35864
|
+
import { join as join20 } from "path";
|
|
35793
35865
|
import { promisify as promisify4 } from "util";
|
|
35794
35866
|
var run3 = promisify4(execFile4);
|
|
35795
35867
|
var RAG_REL = ".claude/rag";
|
|
@@ -35810,7 +35882,7 @@ async function detectPython() {
|
|
|
35810
35882
|
}
|
|
35811
35883
|
async function installLog(root) {
|
|
35812
35884
|
try {
|
|
35813
|
-
const text = await readFile9(
|
|
35885
|
+
const text = await readFile9(join20(root, RAG_REL, "install.log"), "utf8");
|
|
35814
35886
|
return text.split("\n").filter((l) => l.length > 0).slice(-200);
|
|
35815
35887
|
} catch {
|
|
35816
35888
|
return [];
|
|
@@ -35872,14 +35944,14 @@ async function ragStatus(root) {
|
|
|
35872
35944
|
};
|
|
35873
35945
|
let modelName = "";
|
|
35874
35946
|
try {
|
|
35875
|
-
modelName = (await readFile9(
|
|
35947
|
+
modelName = (await readFile9(join20(root, RAG_REL, ".installed"), "utf8")).trim();
|
|
35876
35948
|
} catch {
|
|
35877
35949
|
modelName = "";
|
|
35878
35950
|
}
|
|
35879
35951
|
const modelPresent = modelName.length > 0;
|
|
35880
35952
|
return {
|
|
35881
35953
|
installed: fastembed && sqliteVec && modelPresent,
|
|
35882
|
-
installing:
|
|
35954
|
+
installing: existsSync10(join20(root, RAG_REL, ".installing")),
|
|
35883
35955
|
python: { found: py.found, version: py.version },
|
|
35884
35956
|
deps: { pip, fastembed, sqliteVec },
|
|
35885
35957
|
model: { present: modelPresent, name: modelName },
|
|
@@ -35890,9 +35962,9 @@ async function ragStatus(root) {
|
|
|
35890
35962
|
};
|
|
35891
35963
|
}
|
|
35892
35964
|
async function readIndexMeta(root) {
|
|
35893
|
-
const indexing =
|
|
35965
|
+
const indexing = existsSync10(join20(root, RAG_REL, ".indexing"));
|
|
35894
35966
|
try {
|
|
35895
|
-
const m = JSON.parse(await readFile9(
|
|
35967
|
+
const m = JSON.parse(await readFile9(join20(root, RAG_REL, ".index.json"), "utf8"));
|
|
35896
35968
|
return {
|
|
35897
35969
|
present: true,
|
|
35898
35970
|
indexing,
|
|
@@ -35959,14 +36031,14 @@ if __name__ == "__main__":
|
|
|
35959
36031
|
elif cmd == "query": query(sys.argv[2], int(sys.argv[3]) if len(sys.argv) > 3 else 8)
|
|
35960
36032
|
`;
|
|
35961
36033
|
async function ensureEngine(root) {
|
|
35962
|
-
await mkdir6(
|
|
35963
|
-
await writeFile7(
|
|
36034
|
+
await mkdir6(join20(root, RAG_REL), { recursive: true }).catch(() => void 0);
|
|
36035
|
+
await writeFile7(join20(root, RAG_REL, "rag_engine.py"), RAG_ENGINE_PY, "utf8");
|
|
35964
36036
|
}
|
|
35965
36037
|
async function ragReindex(root) {
|
|
35966
36038
|
const py = await detectPython();
|
|
35967
36039
|
if (!py.found) return { ok: false, started: false, error: "python3 not found" };
|
|
35968
|
-
if (!
|
|
35969
|
-
if (
|
|
36040
|
+
if (!existsSync10(join20(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
|
|
36041
|
+
if (existsSync10(join20(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
|
|
35970
36042
|
await ensureEngine(root);
|
|
35971
36043
|
const script = `
|
|
35972
36044
|
set +e
|
|
@@ -35986,12 +36058,12 @@ rm -f "${RAG_REL}/.indexing"
|
|
|
35986
36058
|
async function ragQuery(root, queryText, k = 8) {
|
|
35987
36059
|
const py = await detectPython();
|
|
35988
36060
|
if (!py.found) return { results: [], error: "python3 not found" };
|
|
35989
|
-
if (!
|
|
36061
|
+
if (!existsSync10(join20(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
|
|
35990
36062
|
await ensureEngine(root);
|
|
35991
36063
|
try {
|
|
35992
36064
|
const { stdout } = await run3(
|
|
35993
36065
|
py.bin,
|
|
35994
|
-
[
|
|
36066
|
+
[join20(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k, 1), 20))],
|
|
35995
36067
|
{ cwd: root, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
35996
36068
|
);
|
|
35997
36069
|
const parsed = JSON.parse(stdout.trim() || '{"results":[]}');
|
|
@@ -36003,9 +36075,9 @@ async function ragQuery(root, queryText, k = 8) {
|
|
|
36003
36075
|
async function ragInstall(root, model) {
|
|
36004
36076
|
const py = await detectPython();
|
|
36005
36077
|
if (!py.found) return { ok: false, started: false, error: "python3 not found on the worker" };
|
|
36006
|
-
if (
|
|
36078
|
+
if (existsSync10(join20(root, RAG_REL, ".installing"))) return { ok: true, started: true };
|
|
36007
36079
|
try {
|
|
36008
|
-
await mkdir6(
|
|
36080
|
+
await mkdir6(join20(root, RAG_REL), { recursive: true });
|
|
36009
36081
|
} catch {
|
|
36010
36082
|
}
|
|
36011
36083
|
const script = `
|
|
@@ -36093,12 +36165,12 @@ ${stderr}`;
|
|
|
36093
36165
|
// src/core/git-ssh-key.ts
|
|
36094
36166
|
import { execFile as execFile7 } from "child_process";
|
|
36095
36167
|
import { readFile as readFile11, rm as rm3, mkdir as mkdir7 } from "fs/promises";
|
|
36096
|
-
import { join as
|
|
36168
|
+
import { join as join21 } from "path";
|
|
36097
36169
|
import { promisify as promisify7 } from "util";
|
|
36098
36170
|
var run6 = promisify7(execFile7);
|
|
36099
36171
|
function keyPaths(profileDir2) {
|
|
36100
|
-
const dir2 =
|
|
36101
|
-
const key =
|
|
36172
|
+
const dir2 = join21(profileDir2, ".ssh");
|
|
36173
|
+
const key = join21(dir2, "id_4pm");
|
|
36102
36174
|
return { dir: dir2, key, pub: `${key}.pub` };
|
|
36103
36175
|
}
|
|
36104
36176
|
async function readPublicKey(pub) {
|
|
@@ -36290,7 +36362,7 @@ async function setCommitAuthor(cwd2, machineUsername) {
|
|
|
36290
36362
|
// src/core/scaffold.ts
|
|
36291
36363
|
import { cp, mkdir as mkdir8, writeFile as writeFile8 } from "fs/promises";
|
|
36292
36364
|
import { execFile as execFile10 } from "child_process";
|
|
36293
|
-
import { dirname as dirname8, join as
|
|
36365
|
+
import { dirname as dirname8, join as join22, resolve as resolve7 } from "path";
|
|
36294
36366
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
36295
36367
|
import { promisify as promisify10 } from "util";
|
|
36296
36368
|
var run9 = promisify10(execFile10);
|
|
@@ -36319,7 +36391,7 @@ async function provisionRepos(target, repos, emit, opts = {}) {
|
|
|
36319
36391
|
}
|
|
36320
36392
|
continue;
|
|
36321
36393
|
}
|
|
36322
|
-
const dir2 =
|
|
36394
|
+
const dir2 = join22(target, subDirName(repo));
|
|
36323
36395
|
if (repo.url) {
|
|
36324
36396
|
emit("git", `Cloning sub-repo ${repo.url}\u2026`);
|
|
36325
36397
|
await run9("git", ["clone", repo.url, dir2], { timeout: 12e4 });
|
|
@@ -36339,14 +36411,14 @@ async function scaffoldProject(payload, profileDir2, onProgress) {
|
|
|
36339
36411
|
const emit = (step, message) => onProgress?.({ projectId: payload.projectId, step, message });
|
|
36340
36412
|
try {
|
|
36341
36413
|
const root = process.env.SCAFFOLD_ROOT ? resolve7(process.env.SCAFFOLD_ROOT) : resolve7(profileDir2);
|
|
36342
|
-
const target =
|
|
36414
|
+
const target = join22(root, payload.projectName);
|
|
36343
36415
|
await mkdir8(target, { recursive: true });
|
|
36344
36416
|
emit("copy", "Copying the sample template\u2026");
|
|
36345
36417
|
await cp(sampleDir(), target, { recursive: true, force: false, errorOnExist: false });
|
|
36346
36418
|
if (payload.spec) {
|
|
36347
36419
|
emit("spec", "Writing project.spec.json\u2026");
|
|
36348
36420
|
await writeFile8(
|
|
36349
|
-
|
|
36421
|
+
join22(target, "project.spec.json"),
|
|
36350
36422
|
JSON.stringify(payload.spec, null, 2),
|
|
36351
36423
|
"utf8"
|
|
36352
36424
|
);
|
|
@@ -36364,7 +36436,7 @@ async function aiInit(target, spec, emit) {
|
|
|
36364
36436
|
emit("ai-init", "AI init: subagents, README, CLAUDE.md\u2026");
|
|
36365
36437
|
const subagents = Array.isArray(spec.subagents) ? spec.subagents : [];
|
|
36366
36438
|
if (subagents.length > 0) {
|
|
36367
|
-
await mkdir8(
|
|
36439
|
+
await mkdir8(join22(target, ".claude", "agents"), { recursive: true });
|
|
36368
36440
|
for (const sa of subagents) {
|
|
36369
36441
|
const name = (sa.name || "").trim().replace(/[^\w.-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
36370
36442
|
if (!name) continue;
|
|
@@ -36374,17 +36446,17 @@ name: ${name}
|
|
|
36374
36446
|
|
|
36375
36447
|
${(sa.description || "").trim()}
|
|
36376
36448
|
`;
|
|
36377
|
-
await writeFile8(
|
|
36449
|
+
await writeFile8(join22(target, ".claude", "agents", `${name}.md`), body, "utf8");
|
|
36378
36450
|
}
|
|
36379
36451
|
}
|
|
36380
36452
|
const specJson = JSON.stringify(spec);
|
|
36381
36453
|
await generateFile(
|
|
36382
|
-
|
|
36454
|
+
join22(target, "README.md"),
|
|
36383
36455
|
`Write a concise README.md (Markdown only, no preamble) for this project from its spec JSON:
|
|
36384
36456
|
${specJson}`
|
|
36385
36457
|
);
|
|
36386
36458
|
await generateFile(
|
|
36387
|
-
|
|
36459
|
+
join22(target, "CLAUDE.md"),
|
|
36388
36460
|
`Write a CLAUDE.md (Markdown only, no preamble) with guidance/conventions for AI agents working in this project, derived from its spec JSON:
|
|
36389
36461
|
${specJson}`
|
|
36390
36462
|
);
|
|
@@ -36400,7 +36472,7 @@ async function addProject(payload, profileDir2, onProgress) {
|
|
|
36400
36472
|
const emit = (step, message) => onProgress?.({ projectId: payload.projectId, step, message });
|
|
36401
36473
|
try {
|
|
36402
36474
|
const root = process.env.SCAFFOLD_ROOT ? resolve7(process.env.SCAFFOLD_ROOT) : resolve7(profileDir2);
|
|
36403
|
-
const target =
|
|
36475
|
+
const target = join22(root, payload.projectName);
|
|
36404
36476
|
await mkdir8(target, { recursive: true });
|
|
36405
36477
|
const repos = payload.repos ?? [];
|
|
36406
36478
|
if (repos.length > 0) await provisionRepos(target, repos, emit, { clonePrimary: true });
|
|
@@ -36412,14 +36484,14 @@ async function addProject(payload, profileDir2, onProgress) {
|
|
|
36412
36484
|
}
|
|
36413
36485
|
|
|
36414
36486
|
// src/core/config-sync.ts
|
|
36415
|
-
import { existsSync as
|
|
36416
|
-
import { join as
|
|
36487
|
+
import { existsSync as existsSync11, readFileSync as readFileSync15 } from "fs";
|
|
36488
|
+
import { join as join23 } from "path";
|
|
36417
36489
|
var SERVER_MANAGED_KEYS = ["physicPath", "sessionSwitchPct", "perPromptTokenLimit"];
|
|
36418
36490
|
function readConfigText(profileDir2) {
|
|
36419
|
-
const path =
|
|
36420
|
-
if (
|
|
36491
|
+
const path = join23(profileDir2, "config.json");
|
|
36492
|
+
if (existsSync11(path)) {
|
|
36421
36493
|
try {
|
|
36422
|
-
return
|
|
36494
|
+
return readFileSync15(path, "utf8");
|
|
36423
36495
|
} catch {
|
|
36424
36496
|
}
|
|
36425
36497
|
}
|
|
@@ -36844,7 +36916,6 @@ var WsClient = class {
|
|
|
36844
36916
|
});
|
|
36845
36917
|
this.outboundReviewEnabled = token.outboundReview?.enabled === true;
|
|
36846
36918
|
this.restrictToFolder = token.aiRestrictToFolder === true;
|
|
36847
|
-
writeEgressAllowlist(this.context.profileDir, token.aiSandbox ?? null);
|
|
36848
36919
|
applyGitAuth(token.gitAuth ?? null);
|
|
36849
36920
|
this.machineUsername = token.machineUsername ?? "";
|
|
36850
36921
|
if (token.scope && token.scope !== this.credential.scope) {
|
|
@@ -36992,8 +37063,8 @@ var WsClient = class {
|
|
|
36992
37063
|
const oldRoot = this.physicFolderPath(sync.oldName);
|
|
36993
37064
|
if (oldRoot) void uninstallCron(oldRoot).catch(() => void 0);
|
|
36994
37065
|
}
|
|
36995
|
-
if (sync.oldName)
|
|
36996
|
-
mkdirSync5(
|
|
37066
|
+
if (sync.oldName) rmSync4(join24(this.context.profileDir, sync.oldName), { recursive: true, force: true });
|
|
37067
|
+
mkdirSync5(join24(this.context.profileDir, sync.newName), { recursive: true });
|
|
36997
37068
|
this.physicRoot = this.physicFolderPath(sync.newName);
|
|
36998
37069
|
this.bus.setProject(sync.newName);
|
|
36999
37070
|
this.bus.log(`Physic project folder synced \u2192 ${sync.newName}`);
|
|
@@ -37004,7 +37075,7 @@ var WsClient = class {
|
|
|
37004
37075
|
if (del.name) {
|
|
37005
37076
|
const delRoot = this.physicFolderPath(del.name);
|
|
37006
37077
|
if (delRoot) void uninstallCron(delRoot).catch(() => void 0);
|
|
37007
|
-
|
|
37078
|
+
rmSync4(join24(this.context.profileDir, del.name), { recursive: true, force: true });
|
|
37008
37079
|
this.bus.log(`Physic project folder deleted (project removed) \u2192 ${del.name}`);
|
|
37009
37080
|
}
|
|
37010
37081
|
void manageSshKey("delete", this.context.profileDir).catch(() => void 0);
|
|
@@ -37376,7 +37447,7 @@ var WsClient = class {
|
|
|
37376
37447
|
}
|
|
37377
37448
|
case WsChannels.LOG_READ: {
|
|
37378
37449
|
const limit = Math.min(Math.max(payload.limit ?? 200, 1), 2e3);
|
|
37379
|
-
const lines = readRecentLogLines(
|
|
37450
|
+
const lines = readRecentLogLines(join24(this.context.profileDir, "logs"), limit);
|
|
37380
37451
|
this.send(WsChannels.LOG_READ, { lines }, message.id);
|
|
37381
37452
|
break;
|
|
37382
37453
|
}
|
|
@@ -37962,7 +38033,7 @@ var WsClient = class {
|
|
|
37962
38033
|
/** Create the physic folder at an absolute path if missing (best-effort). */
|
|
37963
38034
|
ensurePhysicFolderPath(folder) {
|
|
37964
38035
|
try {
|
|
37965
|
-
if (!
|
|
38036
|
+
if (!existsSync12(folder)) {
|
|
37966
38037
|
mkdirSync5(folder, { recursive: true });
|
|
37967
38038
|
this.bus.log(`Created physic project folder: ${folder}`);
|
|
37968
38039
|
}
|
|
@@ -37977,7 +38048,7 @@ var WsClient = class {
|
|
|
37977
38048
|
*/
|
|
37978
38049
|
physicFolderPath(projectName) {
|
|
37979
38050
|
const safe = projectName.replace(/[/\\]/g, "_").replace(/\.\./g, "_").trim();
|
|
37980
|
-
return safe ?
|
|
38051
|
+
return safe ? join24(this.context.profileDir, safe) : null;
|
|
37981
38052
|
}
|
|
37982
38053
|
/**
|
|
37983
38054
|
* Report machine.status: version + the state of the physic project being served
|
|
@@ -37991,7 +38062,7 @@ var WsClient = class {
|
|
|
37991
38062
|
projects: [],
|
|
37992
38063
|
version: CLI_VERSION,
|
|
37993
38064
|
physicPath,
|
|
37994
|
-
physicPathExists: physicPath ?
|
|
38065
|
+
physicPathExists: physicPath ? existsSync12(physicPath) : void 0,
|
|
37995
38066
|
// Real coarse state (ADR-0152): !paused and a recent tick (was hard-coded false).
|
|
37996
38067
|
autonomousRunning: physicPath ? isAutonomousRunning(physicPath) : false
|
|
37997
38068
|
});
|
|
@@ -38039,6 +38110,7 @@ var WsClient = class {
|
|
|
38039
38110
|
};
|
|
38040
38111
|
snapshot.aiCli = aiCli2;
|
|
38041
38112
|
snapshot.profile = profile;
|
|
38113
|
+
snapshot.network = await probeNetwork();
|
|
38042
38114
|
this.usageSnapshot = snapshot;
|
|
38043
38115
|
this.bus.setUsage?.(snapshot);
|
|
38044
38116
|
this.send(WsChannels.MACHINE_USAGE, snapshot);
|
|
@@ -38080,7 +38152,7 @@ var WsClient = class {
|
|
|
38080
38152
|
*/
|
|
38081
38153
|
uploadLog() {
|
|
38082
38154
|
try {
|
|
38083
|
-
const upload = readLogUpload(
|
|
38155
|
+
const upload = readLogUpload(join24(this.context.profileDir, "logs"));
|
|
38084
38156
|
if (!upload) return;
|
|
38085
38157
|
const sig = `${upload.fileName}:${upload.totalBytes}`;
|
|
38086
38158
|
if (sig === this.lastLogSig) return;
|
|
@@ -38108,10 +38180,10 @@ var WsClient = class {
|
|
|
38108
38180
|
};
|
|
38109
38181
|
|
|
38110
38182
|
// src/core/instance-lock.ts
|
|
38111
|
-
import { readFileSync as
|
|
38112
|
-
import { join as
|
|
38183
|
+
import { readFileSync as readFileSync16, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
38184
|
+
import { join as join25 } from "path";
|
|
38113
38185
|
function lockPath(profileDir2) {
|
|
38114
|
-
return
|
|
38186
|
+
return join25(profileDir2, "start.lock");
|
|
38115
38187
|
}
|
|
38116
38188
|
function isAlive(pid) {
|
|
38117
38189
|
try {
|
|
@@ -38123,7 +38195,7 @@ function isAlive(pid) {
|
|
|
38123
38195
|
}
|
|
38124
38196
|
function lockHolder(profileDir2) {
|
|
38125
38197
|
try {
|
|
38126
|
-
const pid = Number.parseInt(
|
|
38198
|
+
const pid = Number.parseInt(readFileSync16(lockPath(profileDir2), "utf8").trim(), 10);
|
|
38127
38199
|
if (Number.isInteger(pid) && pid > 0 && pid !== process.pid && isAlive(pid)) return pid;
|
|
38128
38200
|
} catch {
|
|
38129
38201
|
}
|
|
@@ -38131,13 +38203,13 @@ function lockHolder(profileDir2) {
|
|
|
38131
38203
|
}
|
|
38132
38204
|
function acquireInstanceLock(profileDir2) {
|
|
38133
38205
|
if (lockHolder(profileDir2) !== null) return false;
|
|
38134
|
-
|
|
38206
|
+
writeFileSync7(lockPath(profileDir2), String(process.pid), "utf8");
|
|
38135
38207
|
return true;
|
|
38136
38208
|
}
|
|
38137
38209
|
function releaseInstanceLock(profileDir2) {
|
|
38138
38210
|
try {
|
|
38139
|
-
const pid = Number.parseInt(
|
|
38140
|
-
if (pid === process.pid)
|
|
38211
|
+
const pid = Number.parseInt(readFileSync16(lockPath(profileDir2), "utf8").trim(), 10);
|
|
38212
|
+
if (pid === process.pid) rmSync5(lockPath(profileDir2), { force: true });
|
|
38141
38213
|
} catch {
|
|
38142
38214
|
}
|
|
38143
38215
|
}
|
|
@@ -38398,8 +38470,8 @@ var SessionBus = class {
|
|
|
38398
38470
|
|
|
38399
38471
|
// src/core/control-server.ts
|
|
38400
38472
|
import { createServer } from "net";
|
|
38401
|
-
import { rmSync as
|
|
38402
|
-
import { join as
|
|
38473
|
+
import { rmSync as rmSync6 } from "fs";
|
|
38474
|
+
import { join as join26 } from "path";
|
|
38403
38475
|
|
|
38404
38476
|
// src/core/control-protocol.ts
|
|
38405
38477
|
var CONTROL_SOCKET_FILE = "control.sock";
|
|
@@ -38428,9 +38500,9 @@ function createFrameParser() {
|
|
|
38428
38500
|
|
|
38429
38501
|
// src/core/control-server.ts
|
|
38430
38502
|
function startControlServer(bus, profileDir2, info) {
|
|
38431
|
-
const socketPath =
|
|
38503
|
+
const socketPath = join26(profileDir2, CONTROL_SOCKET_FILE);
|
|
38432
38504
|
try {
|
|
38433
|
-
|
|
38505
|
+
rmSync6(socketPath, { force: true });
|
|
38434
38506
|
} catch {
|
|
38435
38507
|
}
|
|
38436
38508
|
const clients = /* @__PURE__ */ new Set();
|
|
@@ -38496,7 +38568,7 @@ function startControlServer(bus, profileDir2, info) {
|
|
|
38496
38568
|
clients.clear();
|
|
38497
38569
|
server.close();
|
|
38498
38570
|
try {
|
|
38499
|
-
|
|
38571
|
+
rmSync6(socketPath, { force: true });
|
|
38500
38572
|
} catch {
|
|
38501
38573
|
}
|
|
38502
38574
|
};
|
|
@@ -40476,7 +40548,7 @@ var src_default = Yoga;
|
|
|
40476
40548
|
|
|
40477
40549
|
// ../../node_modules/.pnpm/ink@5.2.1_@types+react@18.3.31_react@18.3.1/node_modules/ink/build/reconciler.js
|
|
40478
40550
|
var import_react_reconciler = __toESM(require_react_reconciler(), 1);
|
|
40479
|
-
var
|
|
40551
|
+
var import_constants7 = __toESM(require_constants2(), 1);
|
|
40480
40552
|
import process3 from "process";
|
|
40481
40553
|
|
|
40482
40554
|
// ../../node_modules/.pnpm/ansi-regex@6.2.2/node_modules/ansi-regex/index.js
|
|
@@ -41718,7 +41790,7 @@ var reconciler_default = (0, import_react_reconciler.default)({
|
|
|
41718
41790
|
scheduleTimeout: setTimeout,
|
|
41719
41791
|
cancelTimeout: clearTimeout,
|
|
41720
41792
|
noTimeout: -1,
|
|
41721
|
-
getCurrentEventPriority: () =>
|
|
41793
|
+
getCurrentEventPriority: () => import_constants7.DefaultEventPriority,
|
|
41722
41794
|
beforeActiveInstanceBlur() {
|
|
41723
41795
|
},
|
|
41724
41796
|
afterActiveInstanceBlur() {
|
|
@@ -44160,17 +44232,17 @@ var import_react21 = __toESM(require_react(), 1);
|
|
|
44160
44232
|
var import_react26 = __toESM(require_react(), 1);
|
|
44161
44233
|
|
|
44162
44234
|
// src/core/input-history.ts
|
|
44163
|
-
import { existsSync as
|
|
44164
|
-
import { join as
|
|
44235
|
+
import { existsSync as existsSync14, readFileSync as readFileSync18, writeFileSync as writeFileSync8 } from "fs";
|
|
44236
|
+
import { join as join27 } from "path";
|
|
44165
44237
|
var MAX_HISTORY = 200;
|
|
44166
44238
|
function historyPath(profileDir2) {
|
|
44167
|
-
return
|
|
44239
|
+
return join27(profileDir2, "input-history.json");
|
|
44168
44240
|
}
|
|
44169
44241
|
function loadInputHistory(profileDir2) {
|
|
44170
44242
|
const path = historyPath(profileDir2);
|
|
44171
|
-
if (!
|
|
44243
|
+
if (!existsSync14(path)) return [];
|
|
44172
44244
|
try {
|
|
44173
|
-
const data = JSON.parse(
|
|
44245
|
+
const data = JSON.parse(readFileSync18(path, "utf8"));
|
|
44174
44246
|
return Array.isArray(data) ? data.filter((x) => typeof x === "string") : [];
|
|
44175
44247
|
} catch {
|
|
44176
44248
|
return [];
|
|
@@ -44181,7 +44253,7 @@ function appendInputHistory(profileDir2, line, current) {
|
|
|
44181
44253
|
if (!trimmed || current[current.length - 1] === trimmed) return current;
|
|
44182
44254
|
const next = [...current, trimmed].slice(-MAX_HISTORY);
|
|
44183
44255
|
try {
|
|
44184
|
-
|
|
44256
|
+
writeFileSync8(historyPath(profileDir2), JSON.stringify(next), "utf8");
|
|
44185
44257
|
} catch {
|
|
44186
44258
|
}
|
|
44187
44259
|
return next;
|
|
@@ -44430,8 +44502,8 @@ function TranscriptLine({ line }) {
|
|
|
44430
44502
|
var import_react24 = __toESM(require_react(), 1);
|
|
44431
44503
|
|
|
44432
44504
|
// src/ui/slash-commands.ts
|
|
44433
|
-
import { existsSync as
|
|
44434
|
-
import { join as
|
|
44505
|
+
import { existsSync as existsSync15 } from "fs";
|
|
44506
|
+
import { join as join28 } from "path";
|
|
44435
44507
|
var CONFIG_KEYS = [
|
|
44436
44508
|
"aiCli",
|
|
44437
44509
|
"claudeHome",
|
|
@@ -44443,7 +44515,7 @@ var CONFIG_KEYS = [
|
|
|
44443
44515
|
"aiEnv"
|
|
44444
44516
|
];
|
|
44445
44517
|
function configPath(profileDir2) {
|
|
44446
|
-
return
|
|
44518
|
+
return join28(profileDir2, "config.json");
|
|
44447
44519
|
}
|
|
44448
44520
|
function parseConfigAssignment(key, valueTokens) {
|
|
44449
44521
|
const value = valueTokens.join(" ").trim();
|
|
@@ -44493,7 +44565,7 @@ function runConfig(ctx) {
|
|
|
44493
44565
|
ctx.print(`\u2714 ${verb} default config: ${path}`);
|
|
44494
44566
|
ctx.print("add claude profiles: /config set claudeHome .claude .claude-1 (tried in order)");
|
|
44495
44567
|
};
|
|
44496
|
-
if (
|
|
44568
|
+
if (existsSync15(path)) {
|
|
44497
44569
|
ctx.confirm(
|
|
44498
44570
|
`config exists: ${path} \u2014 replace it with defaults? (y/N)`,
|
|
44499
44571
|
() => writeDefaults("replaced with")
|
|
@@ -44578,7 +44650,7 @@ function runOutput(ctx) {
|
|
|
44578
44650
|
}
|
|
44579
44651
|
function runLogs(ctx) {
|
|
44580
44652
|
const n = Math.max(1, Math.min(200, Number(ctx.args[0]) || 20));
|
|
44581
|
-
const dir2 =
|
|
44653
|
+
const dir2 = join28(ctx.info.profileDir, "logs");
|
|
44582
44654
|
const lines = readRecentLogLines(dir2, n);
|
|
44583
44655
|
if (lines.length === 0) {
|
|
44584
44656
|
ctx.print("No log lines yet.");
|
|
@@ -45099,7 +45171,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
45099
45171
|
return;
|
|
45100
45172
|
}
|
|
45101
45173
|
logger.configure({
|
|
45102
|
-
dir:
|
|
45174
|
+
dir: join29(profileDir2, "logs"),
|
|
45103
45175
|
level: resolveLogLevel(),
|
|
45104
45176
|
base: { version: CLI_VERSION, profile: profileName, scope: credential.scope }
|
|
45105
45177
|
});
|
|
@@ -45205,13 +45277,13 @@ async function runStart(profileDir2, profileName) {
|
|
|
45205
45277
|
}
|
|
45206
45278
|
|
|
45207
45279
|
// src/commands/attach.ts
|
|
45208
|
-
import { join as
|
|
45280
|
+
import { join as join30 } from "path";
|
|
45209
45281
|
|
|
45210
45282
|
// src/core/control-client.ts
|
|
45211
|
-
import { connect } from "net";
|
|
45283
|
+
import { connect as connect2 } from "net";
|
|
45212
45284
|
function connectControl(socketPath, bus) {
|
|
45213
45285
|
return new Promise((resolve8, reject) => {
|
|
45214
|
-
const socket =
|
|
45286
|
+
const socket = connect2(socketPath);
|
|
45215
45287
|
socket.setEncoding("utf8");
|
|
45216
45288
|
let ready = false;
|
|
45217
45289
|
const forwardSubmit = bus.onLocalSubmit((input) => {
|
|
@@ -45304,7 +45376,7 @@ async function runAttach(profileDir2, profileName) {
|
|
|
45304
45376
|
process.exitCode = 1;
|
|
45305
45377
|
return;
|
|
45306
45378
|
}
|
|
45307
|
-
const socketPath =
|
|
45379
|
+
const socketPath = join30(profileDir2, CONTROL_SOCKET_FILE);
|
|
45308
45380
|
const bus = new SessionBus();
|
|
45309
45381
|
let conn;
|
|
45310
45382
|
try {
|
|
@@ -45389,7 +45461,7 @@ Tip: for the credentials to survive \`docker rm\`/recreate, keep each profile pa
|
|
|
45389
45461
|
}
|
|
45390
45462
|
|
|
45391
45463
|
// src/commands/unlink.ts
|
|
45392
|
-
import { rmSync as
|
|
45464
|
+
import { rmSync as rmSync7 } from "fs";
|
|
45393
45465
|
async function runUnlink(profileDir2, profileName) {
|
|
45394
45466
|
const cred = readCredential(profileDir2, profileName);
|
|
45395
45467
|
if (!cred) {
|
|
@@ -45404,7 +45476,7 @@ async function runUnlink(profileDir2, profileName) {
|
|
|
45404
45476
|
`\u26A0 Could not reach the server to revoke \u2014 revoke it at web /machines. (${err.message})`
|
|
45405
45477
|
);
|
|
45406
45478
|
}
|
|
45407
|
-
|
|
45479
|
+
rmSync7(profileDir2, { recursive: true, force: true });
|
|
45408
45480
|
clearDefaultProfileIf(profileName);
|
|
45409
45481
|
console.log("\u2714 Deleted the profile and its physic project folder.");
|
|
45410
45482
|
}
|