@4pm/cli 1.27.0 → 1.28.0-c
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 +786 -469
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5713,14 +5713,14 @@ var require_react_development = __commonJS({
|
|
|
5713
5713
|
var thenableResult = result;
|
|
5714
5714
|
var wasAwaited = false;
|
|
5715
5715
|
var thenable = {
|
|
5716
|
-
then: function(
|
|
5716
|
+
then: function(resolve13, reject) {
|
|
5717
5717
|
wasAwaited = true;
|
|
5718
5718
|
thenableResult.then(function(returnValue2) {
|
|
5719
5719
|
popActScope(prevActScopeDepth);
|
|
5720
5720
|
if (actScopeDepth === 0) {
|
|
5721
|
-
recursivelyFlushAsyncActWork(returnValue2,
|
|
5721
|
+
recursivelyFlushAsyncActWork(returnValue2, resolve13, reject);
|
|
5722
5722
|
} else {
|
|
5723
|
-
|
|
5723
|
+
resolve13(returnValue2);
|
|
5724
5724
|
}
|
|
5725
5725
|
}, function(error2) {
|
|
5726
5726
|
popActScope(prevActScopeDepth);
|
|
@@ -5750,20 +5750,20 @@ var require_react_development = __commonJS({
|
|
|
5750
5750
|
ReactCurrentActQueue.current = null;
|
|
5751
5751
|
}
|
|
5752
5752
|
var _thenable = {
|
|
5753
|
-
then: function(
|
|
5753
|
+
then: function(resolve13, reject) {
|
|
5754
5754
|
if (ReactCurrentActQueue.current === null) {
|
|
5755
5755
|
ReactCurrentActQueue.current = [];
|
|
5756
|
-
recursivelyFlushAsyncActWork(returnValue,
|
|
5756
|
+
recursivelyFlushAsyncActWork(returnValue, resolve13, reject);
|
|
5757
5757
|
} else {
|
|
5758
|
-
|
|
5758
|
+
resolve13(returnValue);
|
|
5759
5759
|
}
|
|
5760
5760
|
}
|
|
5761
5761
|
};
|
|
5762
5762
|
return _thenable;
|
|
5763
5763
|
} else {
|
|
5764
5764
|
var _thenable2 = {
|
|
5765
|
-
then: function(
|
|
5766
|
-
|
|
5765
|
+
then: function(resolve13, reject) {
|
|
5766
|
+
resolve13(returnValue);
|
|
5767
5767
|
}
|
|
5768
5768
|
};
|
|
5769
5769
|
return _thenable2;
|
|
@@ -5779,7 +5779,7 @@ var require_react_development = __commonJS({
|
|
|
5779
5779
|
actScopeDepth = prevActScopeDepth;
|
|
5780
5780
|
}
|
|
5781
5781
|
}
|
|
5782
|
-
function recursivelyFlushAsyncActWork(returnValue,
|
|
5782
|
+
function recursivelyFlushAsyncActWork(returnValue, resolve13, reject) {
|
|
5783
5783
|
{
|
|
5784
5784
|
var queue = ReactCurrentActQueue.current;
|
|
5785
5785
|
if (queue !== null) {
|
|
@@ -5788,16 +5788,16 @@ var require_react_development = __commonJS({
|
|
|
5788
5788
|
enqueueTask(function() {
|
|
5789
5789
|
if (queue.length === 0) {
|
|
5790
5790
|
ReactCurrentActQueue.current = null;
|
|
5791
|
-
|
|
5791
|
+
resolve13(returnValue);
|
|
5792
5792
|
} else {
|
|
5793
|
-
recursivelyFlushAsyncActWork(returnValue,
|
|
5793
|
+
recursivelyFlushAsyncActWork(returnValue, resolve13, reject);
|
|
5794
5794
|
}
|
|
5795
5795
|
});
|
|
5796
5796
|
} catch (error2) {
|
|
5797
5797
|
reject(error2);
|
|
5798
5798
|
}
|
|
5799
5799
|
} else {
|
|
5800
|
-
|
|
5800
|
+
resolve13(returnValue);
|
|
5801
5801
|
}
|
|
5802
5802
|
}
|
|
5803
5803
|
}
|
|
@@ -16203,9 +16203,9 @@ var require_react_reconciler_development = __commonJS({
|
|
|
16203
16203
|
var node = row;
|
|
16204
16204
|
while (node !== null) {
|
|
16205
16205
|
if (node.tag === SuspenseComponent) {
|
|
16206
|
-
var
|
|
16207
|
-
if (
|
|
16208
|
-
var dehydrated =
|
|
16206
|
+
var state3 = node.memoizedState;
|
|
16207
|
+
if (state3 !== null) {
|
|
16208
|
+
var dehydrated = state3.dehydrated;
|
|
16209
16209
|
if (dehydrated === null || isSuspenseInstancePending(dehydrated) || isSuspenseInstanceFallback(dehydrated)) {
|
|
16210
16210
|
return node;
|
|
16211
16211
|
}
|
|
@@ -16549,8 +16549,8 @@ var require_react_reconciler_development = __commonJS({
|
|
|
16549
16549
|
stores: null
|
|
16550
16550
|
};
|
|
16551
16551
|
}
|
|
16552
|
-
function basicStateReducer(
|
|
16553
|
-
return typeof action === "function" ? action(
|
|
16552
|
+
function basicStateReducer(state3, action) {
|
|
16553
|
+
return typeof action === "function" ? action(state3) : action;
|
|
16554
16554
|
}
|
|
16555
16555
|
function mountReducer(reducer, initialArg, init) {
|
|
16556
16556
|
var hook = mountWorkInProgressHook();
|
|
@@ -18537,10 +18537,10 @@ var require_react_reconciler_development = __commonJS({
|
|
|
18537
18537
|
}
|
|
18538
18538
|
}
|
|
18539
18539
|
}
|
|
18540
|
-
var
|
|
18540
|
+
var state3 = workInProgress2.memoizedState = instance.state !== null && instance.state !== void 0 ? instance.state : null;
|
|
18541
18541
|
adoptClassInstance(workInProgress2, instance);
|
|
18542
18542
|
{
|
|
18543
|
-
if (typeof ctor.getDerivedStateFromProps === "function" &&
|
|
18543
|
+
if (typeof ctor.getDerivedStateFromProps === "function" && state3 === null) {
|
|
18544
18544
|
var componentName = getComponentNameFromType(ctor) || "Component";
|
|
18545
18545
|
if (!didWarnAboutUninitializedState.has(componentName)) {
|
|
18546
18546
|
didWarnAboutUninitializedState.add(componentName);
|
|
@@ -19463,8 +19463,8 @@ var require_react_reconciler_development = __commonJS({
|
|
|
19463
19463
|
var _instance = workInProgress2.stateNode;
|
|
19464
19464
|
var ctor = workInProgress2.type;
|
|
19465
19465
|
var tempInstance = new ctor(workInProgress2.memoizedProps, _instance.context);
|
|
19466
|
-
var
|
|
19467
|
-
_instance.updater.enqueueSetState(_instance,
|
|
19466
|
+
var state3 = tempInstance.state;
|
|
19467
|
+
_instance.updater.enqueueSetState(_instance, state3, null);
|
|
19468
19468
|
break;
|
|
19469
19469
|
}
|
|
19470
19470
|
case true: {
|
|
@@ -20239,8 +20239,8 @@ var require_react_reconciler_development = __commonJS({
|
|
|
20239
20239
|
var node = firstChild;
|
|
20240
20240
|
while (node !== null) {
|
|
20241
20241
|
if (node.tag === SuspenseComponent) {
|
|
20242
|
-
var
|
|
20243
|
-
if (
|
|
20242
|
+
var state3 = node.memoizedState;
|
|
20243
|
+
if (state3 !== null) {
|
|
20244
20244
|
scheduleSuspenseWorkOnFiber(node, renderLanes2, workInProgress2);
|
|
20245
20245
|
}
|
|
20246
20246
|
} else if (node.tag === SuspenseListComponent) {
|
|
@@ -20669,9 +20669,9 @@ var require_react_reconciler_development = __commonJS({
|
|
|
20669
20669
|
}
|
|
20670
20670
|
break;
|
|
20671
20671
|
case SuspenseComponent: {
|
|
20672
|
-
var
|
|
20673
|
-
if (
|
|
20674
|
-
if (
|
|
20672
|
+
var state3 = workInProgress2.memoizedState;
|
|
20673
|
+
if (state3 !== null) {
|
|
20674
|
+
if (state3.dehydrated !== null) {
|
|
20675
20675
|
pushSuspenseContext(workInProgress2, setDefaultShallowSuspenseContext(suspenseStackCursor.current));
|
|
20676
20676
|
workInProgress2.flags |= DidCapture;
|
|
20677
20677
|
return null;
|
|
@@ -29550,7 +29550,7 @@ async function runLink(serverUrl, explicitProfile, token = null) {
|
|
|
29550
29550
|
|
|
29551
29551
|
// src/commands/start.ts
|
|
29552
29552
|
import { spawn as spawn11 } from "child_process";
|
|
29553
|
-
import { join as
|
|
29553
|
+
import { join as join38 } from "path";
|
|
29554
29554
|
|
|
29555
29555
|
// src/core/update.ts
|
|
29556
29556
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -32769,7 +32769,7 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
|
|
|
32769
32769
|
// src/core/ws-client.ts
|
|
32770
32770
|
init_wrapper();
|
|
32771
32771
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
32772
|
-
import { basename as basename7, join as
|
|
32772
|
+
import { basename as basename7, join as join33 } from "path";
|
|
32773
32773
|
import { existsSync as existsSync18, mkdirSync as mkdirSync9 } from "fs";
|
|
32774
32774
|
|
|
32775
32775
|
// vendor/@4pm/utils/helpers.ts
|
|
@@ -37064,6 +37064,17 @@ function githubHostKind(host) {
|
|
|
37064
37064
|
if (h.endsWith(".ghe.com")) return "ghe-cloud";
|
|
37065
37065
|
return "ghes";
|
|
37066
37066
|
}
|
|
37067
|
+
var scaffoldPublishRequestSchema = external_exports.object({
|
|
37068
|
+
machineLinkId: external_exports.string().uuid().optional()
|
|
37069
|
+
});
|
|
37070
|
+
|
|
37071
|
+
// vendor/@4pm/dto/git-overview.ts
|
|
37072
|
+
var GIT_SNAPSHOT_LIMITS = {
|
|
37073
|
+
localCommits: 50,
|
|
37074
|
+
filesPerCommit: 100,
|
|
37075
|
+
dirtyFiles: 200,
|
|
37076
|
+
remoteCommits: 200
|
|
37077
|
+
};
|
|
37067
37078
|
|
|
37068
37079
|
// vendor/@4pm/validation/schemas.ts
|
|
37069
37080
|
var passwordSchema = external_exports.string().min(8, "password: at least 8 characters").max(32, "password: at most 32 characters").regex(/[a-zA-Z]/, "password: needs at least 1 letter").regex(/[0-9]/, "password: needs at least 1 digit");
|
|
@@ -37640,7 +37651,7 @@ var userCardsQuerySchema = external_exports.object({
|
|
|
37640
37651
|
});
|
|
37641
37652
|
|
|
37642
37653
|
// src/core/scaffold.ts
|
|
37643
|
-
import { cp, mkdir, mkdtemp, rm, writeFile } from "fs/promises";
|
|
37654
|
+
import { cp, mkdir, mkdtemp, readFile, rm, writeFile } from "fs/promises";
|
|
37644
37655
|
import { existsSync as existsSync4 } from "fs";
|
|
37645
37656
|
import { execFile } from "child_process";
|
|
37646
37657
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -37749,45 +37760,98 @@ function providerSlug(url) {
|
|
|
37749
37760
|
const m2 = /(?:github|gitlab)\.com[/:]+([^/]+\/.+?)(?:\.git)?\/?$/i.exec(url);
|
|
37750
37761
|
return { provider, slug: m2?.[1] ?? "" };
|
|
37751
37762
|
}
|
|
37752
|
-
|
|
37753
|
-
|
|
37754
|
-
|
|
37755
|
-
|
|
37756
|
-
|
|
37757
|
-
|
|
37758
|
-
return;
|
|
37759
|
-
}
|
|
37760
|
-
const commitArgs = ["commit", "-m", "chore: scaffold project (4PM)"];
|
|
37763
|
+
function errText(err) {
|
|
37764
|
+
const e = err;
|
|
37765
|
+
const raw = (e?.stderr || e?.message || String(err)).trim();
|
|
37766
|
+
return raw.split("\n").filter(Boolean).slice(-3).join(" ").slice(0, 500);
|
|
37767
|
+
}
|
|
37768
|
+
async function gitOut(cwd2, args) {
|
|
37761
37769
|
try {
|
|
37762
|
-
await run("git",
|
|
37770
|
+
return (await run("git", args, { cwd: cwd2, timeout: 3e4 })).stdout.trim();
|
|
37763
37771
|
} catch {
|
|
37764
|
-
|
|
37772
|
+
return "";
|
|
37765
37773
|
}
|
|
37766
|
-
|
|
37774
|
+
}
|
|
37775
|
+
async function commitAndOpenPr(root, repo, emit) {
|
|
37776
|
+
const result = { committed: false, pushed: false, branch: null, prUrl: null, step: null, error: null };
|
|
37777
|
+
const fail = (step, error) => {
|
|
37778
|
+
result.step = step;
|
|
37779
|
+
result.error = error;
|
|
37780
|
+
return result;
|
|
37781
|
+
};
|
|
37782
|
+
emit("commit", "Committing the scaffolded project\u2026");
|
|
37783
|
+
try {
|
|
37784
|
+
await run("git", ["add", "-A"], { cwd: root, timeout: 6e4 });
|
|
37785
|
+
const staged = (await run("git", ["diff", "--cached", "--name-only"], { cwd: root, timeout: 3e4 })).stdout.trim();
|
|
37786
|
+
if (staged) {
|
|
37787
|
+
const commitArgs = ["commit", "-m", "chore: scaffold project (4PM)"];
|
|
37788
|
+
try {
|
|
37789
|
+
await run("git", commitArgs, { cwd: root, timeout: 6e4 });
|
|
37790
|
+
} catch {
|
|
37791
|
+
await run("git", ["-c", "user.name=4PM", "-c", "user.email=noreply@4pm.app", ...commitArgs], { cwd: root, timeout: 6e4 });
|
|
37792
|
+
}
|
|
37793
|
+
} else {
|
|
37794
|
+
emit("commit", "Nothing new to commit.");
|
|
37795
|
+
}
|
|
37796
|
+
} catch (err) {
|
|
37797
|
+
const msg = errText(err);
|
|
37798
|
+
emit("commit-failed", `Could not commit the scaffold: ${msg}`);
|
|
37799
|
+
return fail("commit", msg);
|
|
37800
|
+
}
|
|
37801
|
+
result.committed = !!await gitOut(root, ["rev-parse", "--verify", "HEAD"]);
|
|
37802
|
+
const declared = (repo.branch ?? "").trim();
|
|
37803
|
+
const head = await gitOut(root, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
37804
|
+
const branch = declared || (head && head !== "HEAD" ? head : "");
|
|
37805
|
+
result.branch = branch || null;
|
|
37767
37806
|
emit("push", "Pushing the scaffold branch\u2026");
|
|
37768
37807
|
try {
|
|
37769
|
-
await run("git", ["push", "-u", "origin",
|
|
37808
|
+
await run("git", ["push", "-u", "origin", branch || "HEAD"], { cwd: root, timeout: 12e4 });
|
|
37809
|
+
result.pushed = true;
|
|
37770
37810
|
} catch (err) {
|
|
37771
|
-
const msg =
|
|
37772
|
-
emit("push-failed", `Scaffold committed locally but the push failed: ${msg} \u2014
|
|
37773
|
-
return;
|
|
37811
|
+
const msg = errText(err);
|
|
37812
|
+
emit("push-failed", `Scaffold committed locally but the push failed: ${msg} \u2014 retry from the project page when credentials are available.`);
|
|
37813
|
+
return fail("push", msg);
|
|
37774
37814
|
}
|
|
37775
|
-
const
|
|
37815
|
+
const remoteHead = (await gitOut(root, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"])).replace(/^origin\//, "");
|
|
37816
|
+
if (branch && remoteHead && branch === remoteHead) {
|
|
37817
|
+
emit("pr", `Pushed to the default branch (${branch}) \u2014 no pull request needed.`);
|
|
37818
|
+
return result;
|
|
37819
|
+
}
|
|
37820
|
+
const url = (repo.url ?? "").trim() || await gitOut(root, ["remote", "get-url", "origin"]);
|
|
37821
|
+
const { provider, slug } = providerSlug(url);
|
|
37776
37822
|
if (!provider) {
|
|
37777
37823
|
emit("pr", "Unknown git host \u2014 skipping the pull request.");
|
|
37778
|
-
return;
|
|
37824
|
+
return result;
|
|
37779
37825
|
}
|
|
37780
37826
|
emit("pr", `Opening a pull request via ${provider}\u2026`);
|
|
37781
37827
|
try {
|
|
37782
|
-
const args = provider === "glab" ? ["mr", "create", "--fill", ...slug ? ["-R", slug] : []] : ["pr", "create", "--fill", ...slug ? ["-R", slug] : []];
|
|
37828
|
+
const args = provider === "glab" ? ["mr", "create", "--fill", "--yes", ...branch ? ["--source-branch", branch] : [], ...slug ? ["-R", slug] : []] : ["pr", "create", "--fill", ...branch ? ["--head", branch] : [], ...slug ? ["-R", slug] : []];
|
|
37783
37829
|
const { stdout } = await run(provider, args, { cwd: root, timeout: 12e4 });
|
|
37784
|
-
const link2 = stdout.trim().split(/\s+/).find((
|
|
37830
|
+
const link2 = stdout.trim().split(/\s+/).find((x) => /^https?:\/\//.test(x)) ?? null;
|
|
37831
|
+
result.prUrl = link2;
|
|
37785
37832
|
emit("pr", `Pull request opened${link2 ? `: ${link2}` : "."}`);
|
|
37833
|
+
return result;
|
|
37786
37834
|
} catch (err) {
|
|
37787
|
-
const msg =
|
|
37835
|
+
const msg = errText(err);
|
|
37836
|
+
const existing = /already exists/i.test(msg) ? msg.match(/https?:\/\/\S+/)?.[0] ?? null : null;
|
|
37837
|
+
if (existing) {
|
|
37838
|
+
result.prUrl = existing;
|
|
37839
|
+
emit("pr", `A pull request is already open: ${existing}`);
|
|
37840
|
+
return result;
|
|
37841
|
+
}
|
|
37788
37842
|
emit("pr-failed", `Could not open a pull request (${provider}): ${msg}`);
|
|
37843
|
+
return fail("pr", msg);
|
|
37789
37844
|
}
|
|
37790
37845
|
}
|
|
37846
|
+
async function publishScaffold(root, emit) {
|
|
37847
|
+
let repo = {};
|
|
37848
|
+
try {
|
|
37849
|
+
const spec = JSON.parse(await readFile(join6(root, "project.spec.json"), "utf8"));
|
|
37850
|
+
repo = singleRepo(reposOf(spec)) ?? {};
|
|
37851
|
+
} catch {
|
|
37852
|
+
}
|
|
37853
|
+
return commitAndOpenPr(root, repo, emit);
|
|
37854
|
+
}
|
|
37791
37855
|
function cloneArgs(url, dest, branch) {
|
|
37792
37856
|
const b2 = (branch ?? "").trim();
|
|
37793
37857
|
return b2 ? ["clone", "-b", b2, url, dest] : ["clone", url, dest];
|
|
@@ -37880,7 +37944,7 @@ function sampleDir() {
|
|
|
37880
37944
|
}
|
|
37881
37945
|
return bundled;
|
|
37882
37946
|
}
|
|
37883
|
-
async function scaffoldProject(payload, profileDir2, onProgress, ai2) {
|
|
37947
|
+
async function scaffoldProject(payload, profileDir2, onProgress, ai2, applyGitAuth2) {
|
|
37884
37948
|
let lastStep = "start";
|
|
37885
37949
|
const emit = (step, message) => {
|
|
37886
37950
|
lastStep = step;
|
|
@@ -37892,15 +37956,16 @@ async function scaffoldProject(payload, profileDir2, onProgress, ai2) {
|
|
|
37892
37956
|
await mkdir(target, { recursive: true });
|
|
37893
37957
|
const repo = singleRepo(reposOf(payload.spec));
|
|
37894
37958
|
if (!repo) throw new Error("A project must declare one repo (ADR-0314).");
|
|
37959
|
+
if (payload.gitAuth !== void 0) applyGitAuth2?.(payload.gitAuth ?? null, payload.gitAuthHost ?? null);
|
|
37895
37960
|
emit("git", "Cloning repository\u2026");
|
|
37896
37961
|
await provisionRepo(target, repo, emit, { createMissingBranch: true });
|
|
37897
37962
|
await scaffoldRepo(target, payload.projectName, payload.spec, emit, { resetClaude: true, ai: ai2 });
|
|
37898
37963
|
await attachSubmodules(target, submodulesOf(reposOf(payload.spec)), emit, true);
|
|
37899
37964
|
emit("version", "Writing .4pm/.4pm.json\u2026");
|
|
37900
37965
|
await writeTemplateMarker(target);
|
|
37901
|
-
await commitAndOpenPr(target, repo, emit);
|
|
37966
|
+
const publish = await commitAndOpenPr(target, repo, emit);
|
|
37902
37967
|
onProgress?.({ projectId: payload.projectId, step: "done", message: "Scaffold complete.", done: true });
|
|
37903
|
-
return { ok: true, path: target };
|
|
37968
|
+
return { ok: true, path: target, publish };
|
|
37904
37969
|
} catch (err) {
|
|
37905
37970
|
return { ok: false, error: err instanceof Error ? err.message : String(err), step: lastStep };
|
|
37906
37971
|
}
|
|
@@ -38034,7 +38099,7 @@ async function addProject(payload, profileDir2, onProgress, ai2) {
|
|
|
38034
38099
|
}
|
|
38035
38100
|
|
|
38036
38101
|
// src/core/worker-metrics.ts
|
|
38037
|
-
import { statfs, readFile } from "fs/promises";
|
|
38102
|
+
import { statfs, readFile as readFile2 } from "fs/promises";
|
|
38038
38103
|
import { existsSync as existsSync5 } from "fs";
|
|
38039
38104
|
import { totalmem, freemem, cpus, platform, arch } from "os";
|
|
38040
38105
|
var CGROUP = "/sys/fs/cgroup";
|
|
@@ -38042,7 +38107,7 @@ var MB = 1024 * 1024;
|
|
|
38042
38107
|
var GB = 1024 * 1024 * 1024;
|
|
38043
38108
|
async function readCgroup(file) {
|
|
38044
38109
|
try {
|
|
38045
|
-
return (await
|
|
38110
|
+
return (await readFile2(`${CGROUP}/${file}`, "utf8")).trim();
|
|
38046
38111
|
} catch {
|
|
38047
38112
|
return null;
|
|
38048
38113
|
}
|
|
@@ -38404,9 +38469,15 @@ var WsChannels = {
|
|
|
38404
38469
|
// cli → server (reply): a short-lived GitHub-App installation token for the served project's repo,
|
|
38405
38470
|
// per job scope; also the cli's "scope revoked" report (ADR-0356). The App key stays on the server.
|
|
38406
38471
|
GIT_TOKEN: "git.token",
|
|
38472
|
+
// cli → server (one-way): the worker's per-repo git state of the served project (ADR-0369) — head,
|
|
38473
|
+
// branches + tracking, remote, dirty, worker-only commits, origin window. Latest kept per physic × repo.
|
|
38474
|
+
GIT_SNAPSHOT: "git.snapshot",
|
|
38407
38475
|
PROJECT_CREATE: "project.create",
|
|
38408
38476
|
PROJECT_ADD: "project.add",
|
|
38409
38477
|
PROJECT_PROGRESS: "project.progress",
|
|
38478
|
+
// server → cli (reply): retry the scaffold commit → push → PR in the project folder (ADR-0368,
|
|
38479
|
+
// project-0074); the reply carries the new outcome, stored on `projects.scaffold_publish`.
|
|
38480
|
+
PROJECT_PUBLISH: "project.publish",
|
|
38410
38481
|
// server → cli: keep the physic folder name in sync with the project (folder =
|
|
38411
38482
|
// project name — ADR-0064) when the project is renamed.
|
|
38412
38483
|
PHYSIC_SYNC: "physic.sync",
|
|
@@ -38671,8 +38742,8 @@ function resolveInstallTimeoutMs(sec) {
|
|
|
38671
38742
|
return sec && sec > 0 ? sec * 1e3 : TOOL_INSTALL_TIMEOUT_DEFAULT_MS;
|
|
38672
38743
|
}
|
|
38673
38744
|
function sleep(ms2) {
|
|
38674
|
-
return new Promise((
|
|
38675
|
-
const t2 = setTimeout(
|
|
38745
|
+
return new Promise((resolve13) => {
|
|
38746
|
+
const t2 = setTimeout(resolve13, ms2);
|
|
38676
38747
|
t2.unref?.();
|
|
38677
38748
|
});
|
|
38678
38749
|
}
|
|
@@ -38700,14 +38771,14 @@ function classifyToolFailure(code, out) {
|
|
|
38700
38771
|
return { reason: "other", retryable: false };
|
|
38701
38772
|
}
|
|
38702
38773
|
function run2(cmd, args, onLine, timeoutMs = 18e4) {
|
|
38703
|
-
return new Promise((
|
|
38774
|
+
return new Promise((resolve13) => {
|
|
38704
38775
|
let out = "";
|
|
38705
38776
|
let buf = "";
|
|
38706
38777
|
let child = null;
|
|
38707
38778
|
const done = (code) => {
|
|
38708
38779
|
clearTimeout(timer);
|
|
38709
38780
|
if (onLine && buf.trim()) onLine(buf.trim());
|
|
38710
|
-
|
|
38781
|
+
resolve13({ code, out });
|
|
38711
38782
|
};
|
|
38712
38783
|
const timer = setTimeout(() => {
|
|
38713
38784
|
child?.kill();
|
|
@@ -39090,11 +39161,11 @@ function getWorkingProfile(profileDir2, cmd) {
|
|
|
39090
39161
|
return readState(profileDir2)[cmd] ?? null;
|
|
39091
39162
|
}
|
|
39092
39163
|
function setWorkingProfile(profileDir2, cmd, dir2) {
|
|
39093
|
-
const
|
|
39094
|
-
if (
|
|
39095
|
-
|
|
39164
|
+
const state3 = readState(profileDir2);
|
|
39165
|
+
if (state3[cmd] === dir2) return;
|
|
39166
|
+
state3[cmd] = dir2;
|
|
39096
39167
|
try {
|
|
39097
|
-
writeFileSync4(statePath(profileDir2), JSON.stringify(
|
|
39168
|
+
writeFileSync4(statePath(profileDir2), JSON.stringify(state3, null, 2), "utf8");
|
|
39098
39169
|
} catch {
|
|
39099
39170
|
}
|
|
39100
39171
|
}
|
|
@@ -39103,11 +39174,11 @@ function getWorkingCredential(profileDir2) {
|
|
|
39103
39174
|
return readState(profileDir2)[WORKING_CREDENTIAL_KEY] ?? null;
|
|
39104
39175
|
}
|
|
39105
39176
|
function setWorkingCredential(profileDir2, key) {
|
|
39106
|
-
const
|
|
39107
|
-
if (
|
|
39108
|
-
|
|
39177
|
+
const state3 = readState(profileDir2);
|
|
39178
|
+
if (state3[WORKING_CREDENTIAL_KEY] === key) return;
|
|
39179
|
+
state3[WORKING_CREDENTIAL_KEY] = key;
|
|
39109
39180
|
try {
|
|
39110
|
-
writeFileSync4(statePath(profileDir2), JSON.stringify(
|
|
39181
|
+
writeFileSync4(statePath(profileDir2), JSON.stringify(state3, null, 2), "utf8");
|
|
39111
39182
|
} catch {
|
|
39112
39183
|
}
|
|
39113
39184
|
}
|
|
@@ -39116,20 +39187,20 @@ function getPinnedCredential(profileDir2) {
|
|
|
39116
39187
|
return readState(profileDir2)[PINNED_CREDENTIAL_KEY] ?? null;
|
|
39117
39188
|
}
|
|
39118
39189
|
function setPinnedCredential(profileDir2, key) {
|
|
39119
|
-
const
|
|
39120
|
-
if (
|
|
39121
|
-
|
|
39190
|
+
const state3 = readState(profileDir2);
|
|
39191
|
+
if (state3[PINNED_CREDENTIAL_KEY] === key) return;
|
|
39192
|
+
state3[PINNED_CREDENTIAL_KEY] = key;
|
|
39122
39193
|
try {
|
|
39123
|
-
writeFileSync4(statePath(profileDir2), JSON.stringify(
|
|
39194
|
+
writeFileSync4(statePath(profileDir2), JSON.stringify(state3, null, 2), "utf8");
|
|
39124
39195
|
} catch {
|
|
39125
39196
|
}
|
|
39126
39197
|
}
|
|
39127
39198
|
function clearPinnedCredential(profileDir2) {
|
|
39128
|
-
const
|
|
39129
|
-
if (!(PINNED_CREDENTIAL_KEY in
|
|
39130
|
-
delete
|
|
39199
|
+
const state3 = readState(profileDir2);
|
|
39200
|
+
if (!(PINNED_CREDENTIAL_KEY in state3)) return;
|
|
39201
|
+
delete state3[PINNED_CREDENTIAL_KEY];
|
|
39131
39202
|
try {
|
|
39132
|
-
writeFileSync4(statePath(profileDir2), JSON.stringify(
|
|
39203
|
+
writeFileSync4(statePath(profileDir2), JSON.stringify(state3, null, 2), "utf8");
|
|
39133
39204
|
} catch {
|
|
39134
39205
|
}
|
|
39135
39206
|
}
|
|
@@ -39514,16 +39585,16 @@ async function fetchUsage(token) {
|
|
|
39514
39585
|
}
|
|
39515
39586
|
}
|
|
39516
39587
|
function runUsageCli(dir2, aiCli2) {
|
|
39517
|
-
return new Promise((
|
|
39588
|
+
return new Promise((resolve13) => {
|
|
39518
39589
|
let out = "";
|
|
39519
39590
|
let child = null;
|
|
39520
39591
|
const finish = () => {
|
|
39521
39592
|
clearTimeout(timer);
|
|
39522
|
-
|
|
39593
|
+
resolve13(out);
|
|
39523
39594
|
};
|
|
39524
39595
|
const timer = setTimeout(() => {
|
|
39525
39596
|
child?.kill();
|
|
39526
|
-
|
|
39597
|
+
resolve13(out);
|
|
39527
39598
|
}, 3e4);
|
|
39528
39599
|
timer.unref?.();
|
|
39529
39600
|
try {
|
|
@@ -40219,7 +40290,7 @@ function parseModeratedAnswer(text) {
|
|
|
40219
40290
|
return { body: text.trim() };
|
|
40220
40291
|
}
|
|
40221
40292
|
function runClaudeOnce(cmd, profile, prompt, extraEnv, imageDir, denyDirs = [], timeoutMs = ANSWER_TIMEOUT_MS) {
|
|
40222
|
-
return new Promise((
|
|
40293
|
+
return new Promise((resolve13, reject) => {
|
|
40223
40294
|
const isClaude = cmd.includes("claude");
|
|
40224
40295
|
const args = [
|
|
40225
40296
|
"-p",
|
|
@@ -40263,7 +40334,7 @@ function runClaudeOnce(cmd, profile, prompt, extraEnv, imageDir, denyDirs = [],
|
|
|
40263
40334
|
clearTimeout(timer);
|
|
40264
40335
|
cleanup();
|
|
40265
40336
|
out += parser.flush();
|
|
40266
|
-
|
|
40337
|
+
resolve13({ code: code ?? -1, out, err, usage: parser.usage() });
|
|
40267
40338
|
});
|
|
40268
40339
|
child.stdin.write(prompt);
|
|
40269
40340
|
child.stdin.end();
|
|
@@ -40354,14 +40425,14 @@ function reportToolResult(tool, ok, message) {
|
|
|
40354
40425
|
}
|
|
40355
40426
|
|
|
40356
40427
|
// src/core/autonomous.ts
|
|
40357
|
-
import { readFile as
|
|
40428
|
+
import { readFile as readFile5, writeFile as writeFile4, chmod } from "fs/promises";
|
|
40358
40429
|
import { readFileSync as readFileSync14 } from "fs";
|
|
40359
40430
|
import { execFile as execFile3, spawn as spawn5 } from "child_process";
|
|
40360
40431
|
import { join as join17 } from "path";
|
|
40361
40432
|
import { promisify as promisify3 } from "util";
|
|
40362
40433
|
|
|
40363
40434
|
// src/core/autonomous-config.ts
|
|
40364
|
-
import { readFile as
|
|
40435
|
+
import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
40365
40436
|
import { readFileSync as readFileSync13 } from "fs";
|
|
40366
40437
|
import { join as join15 } from "path";
|
|
40367
40438
|
var DEFAULT_AUTONOMOUS_CONFIG = {
|
|
@@ -40397,7 +40468,7 @@ function coerce2(raw) {
|
|
|
40397
40468
|
}
|
|
40398
40469
|
async function readAutonomousConfig(profileDir2) {
|
|
40399
40470
|
try {
|
|
40400
|
-
return coerce2(JSON.parse(await
|
|
40471
|
+
return coerce2(JSON.parse(await readFile3(autonomousConfigPath(profileDir2), "utf8")));
|
|
40401
40472
|
} catch {
|
|
40402
40473
|
return { ...DEFAULT_AUTONOMOUS_CONFIG };
|
|
40403
40474
|
}
|
|
@@ -40411,7 +40482,7 @@ function readAutonomousConfigSync(profileDir2) {
|
|
|
40411
40482
|
}
|
|
40412
40483
|
async function readAutonomousConfigText(profileDir2) {
|
|
40413
40484
|
try {
|
|
40414
|
-
return await
|
|
40485
|
+
return await readFile3(autonomousConfigPath(profileDir2), "utf8");
|
|
40415
40486
|
} catch {
|
|
40416
40487
|
return JSON.stringify(DEFAULT_AUTONOMOUS_CONFIG, null, 2) + "\n";
|
|
40417
40488
|
}
|
|
@@ -40431,7 +40502,7 @@ function isInQuietHours(quietHours, now = /* @__PURE__ */ new Date()) {
|
|
|
40431
40502
|
}
|
|
40432
40503
|
|
|
40433
40504
|
// src/core/autonomous-history.ts
|
|
40434
|
-
import { readFile as
|
|
40505
|
+
import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
40435
40506
|
import { join as join16 } from "path";
|
|
40436
40507
|
var HISTORIES_REL = ".claude/.autonomous.histories.json";
|
|
40437
40508
|
var MAX_RECORDS = 50;
|
|
@@ -40441,7 +40512,7 @@ function historiesPath(root) {
|
|
|
40441
40512
|
}
|
|
40442
40513
|
async function readHistories(root) {
|
|
40443
40514
|
try {
|
|
40444
|
-
const d = JSON.parse(await
|
|
40515
|
+
const d = JSON.parse(await readFile4(historiesPath(root), "utf8"));
|
|
40445
40516
|
const ticks = d.ticks ?? {};
|
|
40446
40517
|
return {
|
|
40447
40518
|
cronApplied: typeof d.cronApplied === "string" ? d.cronApplied : typeof d.cron_applied === "string" ? d.cron_applied : "",
|
|
@@ -40483,7 +40554,7 @@ var BOOK_FILES = {
|
|
|
40483
40554
|
};
|
|
40484
40555
|
async function readText(path, fallback = "") {
|
|
40485
40556
|
try {
|
|
40486
|
-
return await
|
|
40557
|
+
return await readFile5(path, "utf8");
|
|
40487
40558
|
} catch {
|
|
40488
40559
|
return fallback;
|
|
40489
40560
|
}
|
|
@@ -40548,10 +40619,10 @@ async function crontabList() {
|
|
|
40548
40619
|
}
|
|
40549
40620
|
}
|
|
40550
40621
|
async function crontabSet(content) {
|
|
40551
|
-
return new Promise((
|
|
40622
|
+
return new Promise((resolve13) => {
|
|
40552
40623
|
const child = spawn5("crontab", ["-"], { stdio: ["pipe", "ignore", "ignore"] });
|
|
40553
|
-
child.on("error", () =>
|
|
40554
|
-
child.on("close", (code) =>
|
|
40624
|
+
child.on("error", () => resolve13(false));
|
|
40625
|
+
child.on("close", (code) => resolve13(code === 0));
|
|
40555
40626
|
child.stdin.end(content.endsWith("\n") ? content : content + "\n");
|
|
40556
40627
|
});
|
|
40557
40628
|
}
|
|
@@ -40724,14 +40795,14 @@ function detectContainerized() {
|
|
|
40724
40795
|
}
|
|
40725
40796
|
}
|
|
40726
40797
|
function canReach(host, port) {
|
|
40727
|
-
return new Promise((
|
|
40798
|
+
return new Promise((resolve13) => {
|
|
40728
40799
|
const socket = connect({ host, port });
|
|
40729
40800
|
let done = false;
|
|
40730
40801
|
const finish = (ok) => {
|
|
40731
40802
|
if (done) return;
|
|
40732
40803
|
done = true;
|
|
40733
40804
|
socket.destroy();
|
|
40734
|
-
|
|
40805
|
+
resolve13(ok);
|
|
40735
40806
|
};
|
|
40736
40807
|
socket.setTimeout(PROBE_TIMEOUT_MS);
|
|
40737
40808
|
socket.once("connect", () => finish(true));
|
|
@@ -40916,7 +40987,7 @@ async function issueGitToken(scope, host, path) {
|
|
|
40916
40987
|
}
|
|
40917
40988
|
}
|
|
40918
40989
|
function revokeAtGithub(t2) {
|
|
40919
|
-
return new Promise((
|
|
40990
|
+
return new Promise((resolve13) => {
|
|
40920
40991
|
try {
|
|
40921
40992
|
const req = httpsRequest(
|
|
40922
40993
|
new URL(`${t2.apiBase.replace(/\/+$/, "")}/installation/token`),
|
|
@@ -40927,14 +40998,14 @@ function revokeAtGithub(t2) {
|
|
|
40927
40998
|
},
|
|
40928
40999
|
(res) => {
|
|
40929
41000
|
res.resume();
|
|
40930
|
-
res.on("end", () =>
|
|
41001
|
+
res.on("end", () => resolve13());
|
|
40931
41002
|
}
|
|
40932
41003
|
);
|
|
40933
41004
|
req.on("timeout", () => req.destroy());
|
|
40934
|
-
req.on("error", () =>
|
|
41005
|
+
req.on("error", () => resolve13());
|
|
40935
41006
|
req.end();
|
|
40936
41007
|
} catch {
|
|
40937
|
-
|
|
41008
|
+
resolve13();
|
|
40938
41009
|
}
|
|
40939
41010
|
});
|
|
40940
41011
|
}
|
|
@@ -40956,26 +41027,113 @@ async function revokeAll() {
|
|
|
40956
41027
|
await Promise.all([...state.scopes.keys()].map((s3) => endGitScope(s3)));
|
|
40957
41028
|
}
|
|
40958
41029
|
|
|
40959
|
-
// src/core/
|
|
40960
|
-
|
|
40961
|
-
|
|
40962
|
-
|
|
40963
|
-
|
|
40964
|
-
|
|
40965
|
-
|
|
40966
|
-
|
|
40967
|
-
|
|
40968
|
-
|
|
40969
|
-
|
|
40970
|
-
|
|
40971
|
-
|
|
40972
|
-
|
|
41030
|
+
// src/core/git-snapshot.ts
|
|
41031
|
+
import { execFile as execFile6 } from "child_process";
|
|
41032
|
+
import { stat } from "fs/promises";
|
|
41033
|
+
import { join as join20, resolve as resolve5 } from "path";
|
|
41034
|
+
import { promisify as promisify6 } from "util";
|
|
41035
|
+
|
|
41036
|
+
// src/core/git-history.ts
|
|
41037
|
+
import { execFile as execFile4 } from "child_process";
|
|
41038
|
+
import { resolve as resolve4, sep as sep2 } from "path";
|
|
41039
|
+
import { promisify as promisify4 } from "util";
|
|
41040
|
+
var run4 = promisify4(execFile4);
|
|
41041
|
+
var FS = "";
|
|
41042
|
+
var RS = "";
|
|
41043
|
+
function resolveRepo(root, repo) {
|
|
41044
|
+
if (!root) return null;
|
|
41045
|
+
const base = resolve4(root);
|
|
41046
|
+
const requested = resolve4(base, repo && repo.trim() ? repo : ".");
|
|
41047
|
+
return requested === base || requested.startsWith(base + sep2) ? requested : base;
|
|
40973
41048
|
}
|
|
40974
|
-
function
|
|
40975
|
-
|
|
40976
|
-
|
|
40977
|
-
|
|
40978
|
-
|
|
41049
|
+
async function git(cwd2, args) {
|
|
41050
|
+
try {
|
|
41051
|
+
const { stdout } = await run4("git", args, {
|
|
41052
|
+
cwd: cwd2,
|
|
41053
|
+
timeout: 15e3,
|
|
41054
|
+
maxBuffer: 8 * 1024 * 1024
|
|
41055
|
+
});
|
|
41056
|
+
return stdout;
|
|
41057
|
+
} catch {
|
|
41058
|
+
return "";
|
|
41059
|
+
}
|
|
41060
|
+
}
|
|
41061
|
+
async function gitRepos(root) {
|
|
41062
|
+
if (!root) return { repos: [] };
|
|
41063
|
+
const base = resolve4(root);
|
|
41064
|
+
const top = (await git(base, ["rev-parse", "--show-toplevel"])).trim();
|
|
41065
|
+
if (!top || !(top === base || top.startsWith(base + sep2))) return { repos: [] };
|
|
41066
|
+
const repos = [];
|
|
41067
|
+
const rootRemote = (await git(base, ["remote", "get-url", "origin"])).trim();
|
|
41068
|
+
const rootBranch = (await git(base, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
41069
|
+
repos.push({ subdir: "", name: "", remote: rootRemote || null, branch: rootBranch || null });
|
|
41070
|
+
const subOut = await git(base, ["submodule", "status"]);
|
|
41071
|
+
for (const line of subOut.split("\n")) {
|
|
41072
|
+
const m2 = line.trim().match(/^[-+U ]?[0-9a-f]{7,40}\s+(\S+)/);
|
|
41073
|
+
const subPath = m2?.[1];
|
|
41074
|
+
if (!subPath) continue;
|
|
41075
|
+
const abs = resolve4(base, subPath);
|
|
41076
|
+
if (!(abs === base || abs.startsWith(base + sep2))) continue;
|
|
41077
|
+
const remote = (await git(abs, ["remote", "get-url", "origin"])).trim();
|
|
41078
|
+
const branch = (await git(abs, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
41079
|
+
repos.push({ subdir: subPath, name: subPath, remote: remote || null, branch: branch || null });
|
|
41080
|
+
}
|
|
41081
|
+
return { repos };
|
|
41082
|
+
}
|
|
41083
|
+
async function gitLog(root, repo, skip, limit) {
|
|
41084
|
+
const cwd2 = resolveRepo(root, repo);
|
|
41085
|
+
if (!cwd2) return { entries: [], hasMore: false };
|
|
41086
|
+
const capped = Math.min(Math.max(limit, 1), 200);
|
|
41087
|
+
const format = ["%H", "%h", "%an", "%aI", "%s"].join(FS) + RS;
|
|
41088
|
+
const stdout = await git(cwd2, [
|
|
41089
|
+
"log",
|
|
41090
|
+
`--skip=${Math.max(skip, 0)}`,
|
|
41091
|
+
`-n`,
|
|
41092
|
+
String(capped + 1),
|
|
41093
|
+
`--pretty=format:${format}`
|
|
41094
|
+
]);
|
|
41095
|
+
const rows = stdout.split(RS).map((r) => r.replace(/^\n/, "")).filter((r) => r.length > 0);
|
|
41096
|
+
const entries2 = rows.slice(0, capped).map((row) => {
|
|
41097
|
+
const [hash = "", shortHash = "", author = "", date = "", subject = ""] = row.split(FS);
|
|
41098
|
+
return { hash, shortHash, author, date, subject };
|
|
41099
|
+
});
|
|
41100
|
+
return { entries: entries2, hasMore: rows.length > capped };
|
|
41101
|
+
}
|
|
41102
|
+
async function gitCommit(root, repo, hash) {
|
|
41103
|
+
const cwd2 = resolveRepo(root, repo);
|
|
41104
|
+
const empty = { hash, author: "", date: "", subject: "", files: [] };
|
|
41105
|
+
if (!cwd2 || !hash) return empty;
|
|
41106
|
+
const format = ["%H", "%an", "%aI", "%s"].join(FS);
|
|
41107
|
+
const stdout = await git(cwd2, [
|
|
41108
|
+
"show",
|
|
41109
|
+
"--name-status",
|
|
41110
|
+
"-M",
|
|
41111
|
+
`--pretty=format:${format}`,
|
|
41112
|
+
hash
|
|
41113
|
+
]);
|
|
41114
|
+
if (!stdout) return empty;
|
|
41115
|
+
const lines = stdout.split("\n");
|
|
41116
|
+
const [full = hash, author = "", date = "", subject = ""] = (lines[0] ?? "").split(FS);
|
|
41117
|
+
const files = [];
|
|
41118
|
+
for (const line of lines.slice(1)) {
|
|
41119
|
+
if (!line.trim()) continue;
|
|
41120
|
+
const parts = line.split(" ");
|
|
41121
|
+
const status = (parts[0] ?? "").charAt(0);
|
|
41122
|
+
if (!status) continue;
|
|
41123
|
+
if (status === "R" || status === "C") {
|
|
41124
|
+
files.push({ path: parts[2] ?? "", status, oldPath: parts[1] ?? void 0 });
|
|
41125
|
+
} else {
|
|
41126
|
+
files.push({ path: parts[1] ?? "", status });
|
|
41127
|
+
}
|
|
41128
|
+
}
|
|
41129
|
+
return { hash: full, author, date, subject, files };
|
|
41130
|
+
}
|
|
41131
|
+
async function gitCommitDiff(root, repo, hash, path) {
|
|
41132
|
+
const cwd2 = resolveRepo(root, repo);
|
|
41133
|
+
if (!cwd2 || !hash || !path) return { path, oldContent: "", newContent: "" };
|
|
41134
|
+
const oldContent = await git(cwd2, ["show", `${hash}^:${path}`]);
|
|
41135
|
+
const newContent = await git(cwd2, ["show", `${hash}:${path}`]);
|
|
41136
|
+
return { path, oldContent, newContent };
|
|
40979
41137
|
}
|
|
40980
41138
|
|
|
40981
41139
|
// src/core/ws-client/command-dispatch.ts
|
|
@@ -41064,7 +41222,7 @@ async function runCommand(dispatch, emitRaw, opts) {
|
|
|
41064
41222
|
});
|
|
41065
41223
|
child.stdout?.on("data", (data) => batcher.push(data.toString("utf8")));
|
|
41066
41224
|
child.stderr?.on("data", (data) => batcher.push(data.toString("utf8")));
|
|
41067
|
-
await new Promise((
|
|
41225
|
+
await new Promise((resolve13) => {
|
|
41068
41226
|
child.on("close", (exitCode) => {
|
|
41069
41227
|
clearTimers();
|
|
41070
41228
|
batcher.flush();
|
|
@@ -41078,7 +41236,7 @@ async function runCommand(dispatch, emitRaw, opts) {
|
|
|
41078
41236
|
// note instead of spinning.
|
|
41079
41237
|
exitCode: cancelled ? COMMAND_CANCELLED_EXIT_CODE : timedOut ? 124 : exitCode ?? -1
|
|
41080
41238
|
});
|
|
41081
|
-
|
|
41239
|
+
resolve13();
|
|
41082
41240
|
});
|
|
41083
41241
|
child.on("error", (err) => {
|
|
41084
41242
|
clearTimers();
|
|
@@ -41090,7 +41248,7 @@ async function runCommand(dispatch, emitRaw, opts) {
|
|
|
41090
41248
|
done: true,
|
|
41091
41249
|
exitCode: -1
|
|
41092
41250
|
});
|
|
41093
|
-
|
|
41251
|
+
resolve13();
|
|
41094
41252
|
});
|
|
41095
41253
|
});
|
|
41096
41254
|
}
|
|
@@ -41113,15 +41271,15 @@ function isOutOfCredits(text) {
|
|
|
41113
41271
|
);
|
|
41114
41272
|
}
|
|
41115
41273
|
function runAttempt(commandId, cmd, args, stdin, cwd2, env3, onChunk, timeoutMs, signal) {
|
|
41116
|
-
return new Promise((
|
|
41274
|
+
return new Promise((resolve13) => {
|
|
41117
41275
|
runCommand(
|
|
41118
41276
|
{ commandId, cmd, args, path: cwd2, env: env3 },
|
|
41119
41277
|
(out) => {
|
|
41120
41278
|
if (out.chunk) onChunk(out.chunk);
|
|
41121
|
-
if (out.done)
|
|
41279
|
+
if (out.done) resolve13(out.exitCode ?? -1);
|
|
41122
41280
|
},
|
|
41123
41281
|
{ stdin, ...timeoutMs > 0 ? { timeoutMs } : {}, ...signal ? { signal } : {} }
|
|
41124
|
-
).catch(() =>
|
|
41282
|
+
).catch(() => resolve13(-1));
|
|
41125
41283
|
});
|
|
41126
41284
|
}
|
|
41127
41285
|
async function runAiFailover(plan, commandId, cwd2, handlers, timeoutMs = 0, signal) {
|
|
@@ -41342,8 +41500,8 @@ function runHistory(ctx) {
|
|
|
41342
41500
|
ctx.print(t("slash.history.header", { n: recent.length, total: all.length }));
|
|
41343
41501
|
recent.forEach((e, i) => {
|
|
41344
41502
|
const time = new Date(e.startedAt).toLocaleTimeString();
|
|
41345
|
-
const
|
|
41346
|
-
ctx.print(` [${i + 1}] ${time} ${commandLine(e.cmd, e.args)} \xB7 ${
|
|
41503
|
+
const state3 = e.status === "done" ? "\u2713" : e.status === "failed" ? `\u2717 exit ${e.exitCode}` : t("slash.history.running");
|
|
41504
|
+
ctx.print(` [${i + 1}] ${time} ${commandLine(e.cmd, e.args)} \xB7 ${state3}`);
|
|
41347
41505
|
});
|
|
41348
41506
|
}
|
|
41349
41507
|
function runOutput(ctx) {
|
|
@@ -41354,8 +41512,8 @@ function runOutput(ctx) {
|
|
|
41354
41512
|
return;
|
|
41355
41513
|
}
|
|
41356
41514
|
const entry = all.slice().reverse()[n - 1];
|
|
41357
|
-
const
|
|
41358
|
-
ctx.print(t("slash.output.header", { n, cmd: commandLine(entry.cmd, entry.args), status: entry.status, state:
|
|
41515
|
+
const state3 = entry.exitCode != null ? ` (exit ${entry.exitCode})` : "";
|
|
41516
|
+
ctx.print(t("slash.output.header", { n, cmd: commandLine(entry.cmd, entry.args), status: entry.status, state: state3 }));
|
|
41359
41517
|
const out = readCommandOutput(entry.commandId);
|
|
41360
41518
|
if (out == null) {
|
|
41361
41519
|
ctx.print(t("slash.output.none"), "warn");
|
|
@@ -41650,7 +41808,7 @@ function buildPrompt2(input) {
|
|
|
41650
41808
|
].join("\n");
|
|
41651
41809
|
}
|
|
41652
41810
|
function runOnce(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
41653
|
-
return new Promise((
|
|
41811
|
+
return new Promise((resolve13, reject) => {
|
|
41654
41812
|
const args = [
|
|
41655
41813
|
"-p",
|
|
41656
41814
|
"--output-format",
|
|
@@ -41685,7 +41843,7 @@ function runOnce(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
|
41685
41843
|
});
|
|
41686
41844
|
child.on("close", (code) => {
|
|
41687
41845
|
clearTimeout(timer);
|
|
41688
|
-
|
|
41846
|
+
resolve13({ code: code ?? -1, out, err });
|
|
41689
41847
|
});
|
|
41690
41848
|
child.stdin.write(prompt);
|
|
41691
41849
|
child.stdin.end();
|
|
@@ -41712,13 +41870,13 @@ async function runMemoryCompaction(ai2, cwd2, input) {
|
|
|
41712
41870
|
}
|
|
41713
41871
|
|
|
41714
41872
|
// src/core/git-commit-identity.ts
|
|
41715
|
-
import { execFile as
|
|
41716
|
-
import { promisify as
|
|
41717
|
-
var
|
|
41873
|
+
import { execFile as execFile5 } from "child_process";
|
|
41874
|
+
import { promisify as promisify5 } from "util";
|
|
41875
|
+
var run5 = promisify5(execFile5);
|
|
41718
41876
|
async function setCommitAuthor(cwd2, machineUsername) {
|
|
41719
41877
|
if (!machineUsername) return;
|
|
41720
41878
|
try {
|
|
41721
|
-
await
|
|
41879
|
+
await run5("git", ["config", "--local", "user.name", machineUsername], { cwd: cwd2, timeout: 8e3 });
|
|
41722
41880
|
} catch {
|
|
41723
41881
|
}
|
|
41724
41882
|
}
|
|
@@ -41726,13 +41884,13 @@ async function setCommitAuthor(cwd2, machineUsername) {
|
|
|
41726
41884
|
// src/core/ws-client/run-slot.ts
|
|
41727
41885
|
var NO_SLOT = { release: () => void 0 };
|
|
41728
41886
|
function sleep2(ms2, signal) {
|
|
41729
|
-
return new Promise((
|
|
41730
|
-
if (signal?.aborted) return
|
|
41887
|
+
return new Promise((resolve13) => {
|
|
41888
|
+
if (signal?.aborted) return resolve13();
|
|
41731
41889
|
const timer = setTimeout(done, ms2);
|
|
41732
41890
|
function done() {
|
|
41733
41891
|
clearTimeout(timer);
|
|
41734
41892
|
signal?.removeEventListener("abort", done);
|
|
41735
|
-
|
|
41893
|
+
resolve13();
|
|
41736
41894
|
}
|
|
41737
41895
|
signal?.addEventListener("abort", done, { once: true });
|
|
41738
41896
|
});
|
|
@@ -41794,11 +41952,41 @@ function leaveQueue(ctx, ticket) {
|
|
|
41794
41952
|
// src/core/ws-client/command-dispatch.ts
|
|
41795
41953
|
var MEMORY_SEED_HEADER = "CONTEXT MEMORY from earlier in this conversation (may span prior sessions/accounts). Use it as background; do not repeat it back unless relevant:";
|
|
41796
41954
|
var activeRuns = /* @__PURE__ */ new Map();
|
|
41955
|
+
var GIT_STATE_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
41956
|
+
"add",
|
|
41957
|
+
"branch",
|
|
41958
|
+
"checkout",
|
|
41959
|
+
"cherry-pick",
|
|
41960
|
+
"commit",
|
|
41961
|
+
"fetch",
|
|
41962
|
+
"merge",
|
|
41963
|
+
"pull",
|
|
41964
|
+
"push",
|
|
41965
|
+
"rebase",
|
|
41966
|
+
"reset",
|
|
41967
|
+
"restore",
|
|
41968
|
+
"revert",
|
|
41969
|
+
"rm",
|
|
41970
|
+
"stash",
|
|
41971
|
+
"switch",
|
|
41972
|
+
"tag",
|
|
41973
|
+
"mv"
|
|
41974
|
+
]);
|
|
41975
|
+
function changesGitState(cmd, args) {
|
|
41976
|
+
if (cmd === "gh" || cmd === "glab") return args.includes("create") || args.includes("merge");
|
|
41977
|
+
if (cmd !== "git") return false;
|
|
41978
|
+
let i = 0;
|
|
41979
|
+
while (args[i] === "-C" || args[i] === "-c") i += 2;
|
|
41980
|
+
return GIT_STATE_SUBCOMMANDS.has(args[i] ?? "");
|
|
41981
|
+
}
|
|
41982
|
+
function activeAiRunCount() {
|
|
41983
|
+
return activeRuns.size;
|
|
41984
|
+
}
|
|
41797
41985
|
function cancelActiveRun(commandId) {
|
|
41798
|
-
const
|
|
41799
|
-
if (!
|
|
41800
|
-
|
|
41801
|
-
return { ok: true, state:
|
|
41986
|
+
const run12 = activeRuns.get(commandId);
|
|
41987
|
+
if (!run12) return { ok: false, state: "unknown" };
|
|
41988
|
+
run12.controller.abort();
|
|
41989
|
+
return { ok: true, state: run12.state };
|
|
41802
41990
|
}
|
|
41803
41991
|
function handleCommandChannels(ctx, message, payload) {
|
|
41804
41992
|
if (message.channel === WsChannels.COMMAND_CANCEL) {
|
|
@@ -41863,6 +42051,7 @@ function handleCommandChannels(ctx, message, payload) {
|
|
|
41863
42051
|
ctx.bus.endBusy(dispatch.cmd);
|
|
41864
42052
|
const code = out.exitCode ?? -1;
|
|
41865
42053
|
finishCommand(out.commandId, code);
|
|
42054
|
+
if (changesGitState(dispatch.cmd, dispatch.args)) requestGitSnapshot("git-write");
|
|
41866
42055
|
ctx.bus.push({
|
|
41867
42056
|
source: "server",
|
|
41868
42057
|
kind: "exit",
|
|
@@ -41894,15 +42083,16 @@ async function runLocalCommand(ctx, input) {
|
|
|
41894
42083
|
await runAiPrompt(ctx, prompt, commandId, "local");
|
|
41895
42084
|
}
|
|
41896
42085
|
async function runAiPrompt(ctx, prompt, commandId, origin, oneShot = false, images, aiConfig, readOnly = false, bypass = false) {
|
|
41897
|
-
const
|
|
41898
|
-
activeRuns.set(commandId,
|
|
42086
|
+
const run12 = { controller: new AbortController(), state: "queued" };
|
|
42087
|
+
activeRuns.set(commandId, run12);
|
|
41899
42088
|
try {
|
|
41900
|
-
await runAiPromptInner(ctx, prompt, commandId, origin,
|
|
42089
|
+
await runAiPromptInner(ctx, prompt, commandId, origin, run12, oneShot, images, aiConfig, readOnly, bypass);
|
|
41901
42090
|
} finally {
|
|
41902
42091
|
activeRuns.delete(commandId);
|
|
42092
|
+
if (!readOnly) requestGitSnapshot("ai-run");
|
|
41903
42093
|
}
|
|
41904
42094
|
}
|
|
41905
|
-
async function runAiPromptInner(ctx, prompt, commandId, origin,
|
|
42095
|
+
async function runAiPromptInner(ctx, prompt, commandId, origin, run12, oneShot = false, images, aiConfig, readOnly = false, bypass = false) {
|
|
41906
42096
|
const config = readProfileConfig(ctx.profileDir);
|
|
41907
42097
|
const projectTimeoutSec = config.projectAiRunTimeoutSec ?? 0;
|
|
41908
42098
|
const machineTimeoutSec = config.aiRunTimeoutSec ?? 0;
|
|
@@ -42083,7 +42273,7 @@ ${guardedPrompt}`;
|
|
|
42083
42273
|
queue: { position, limit, running: running2 }
|
|
42084
42274
|
});
|
|
42085
42275
|
},
|
|
42086
|
-
|
|
42276
|
+
run12.controller.signal
|
|
42087
42277
|
);
|
|
42088
42278
|
if (slot.kind === "timeout") {
|
|
42089
42279
|
neverStarted = true;
|
|
@@ -42104,9 +42294,9 @@ ${guardedPrompt}`;
|
|
|
42104
42294
|
result = { exitCode: COMMAND_CANCELLED_EXIT_CODE, workedDir: null, workedKey: null, workedCmd: null, usage: { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 }, sessionId: "", cancelled: true };
|
|
42105
42295
|
} else {
|
|
42106
42296
|
if (slot.queued) ctx.send(WsChannels.COMMAND_OUTPUT, { commandId, seq: seq++, chunk: "", log: true, queue: null });
|
|
42107
|
-
|
|
42297
|
+
run12.state = "running";
|
|
42108
42298
|
try {
|
|
42109
|
-
result = await runAiFailover(plan, commandId, cwd2, handlers, aiRunTimeoutMs,
|
|
42299
|
+
result = await runAiFailover(plan, commandId, cwd2, handlers, aiRunTimeoutMs, run12.controller.signal);
|
|
42110
42300
|
} finally {
|
|
42111
42301
|
slot.handle.release();
|
|
42112
42302
|
}
|
|
@@ -42417,10 +42607,200 @@ function rejectByReview(ctx, commandId, origin, prompt, reasons) {
|
|
|
42417
42607
|
});
|
|
42418
42608
|
}
|
|
42419
42609
|
|
|
42610
|
+
// src/core/git-snapshot.ts
|
|
42611
|
+
var run6 = promisify6(execFile6);
|
|
42612
|
+
var US = "";
|
|
42613
|
+
var RS2 = "";
|
|
42614
|
+
var MIN_SPACING_MS = 15e3;
|
|
42615
|
+
var DEFAULT_INTERVAL_MIN = 10;
|
|
42616
|
+
async function git2(cwd2, args, timeout = 2e4) {
|
|
42617
|
+
try {
|
|
42618
|
+
const { stdout } = await run6("git", args, { cwd: cwd2, timeout, maxBuffer: 16 * 1024 * 1024 });
|
|
42619
|
+
return stdout;
|
|
42620
|
+
} catch {
|
|
42621
|
+
return "";
|
|
42622
|
+
}
|
|
42623
|
+
}
|
|
42624
|
+
function parseTrack(track) {
|
|
42625
|
+
const ahead = /ahead (\d+)/.exec(track)?.[1];
|
|
42626
|
+
const behind = /behind (\d+)/.exec(track)?.[1];
|
|
42627
|
+
return { ahead: ahead ? Number(ahead) : 0, behind: behind ? Number(behind) : 0 };
|
|
42628
|
+
}
|
|
42629
|
+
function parseStatus(out) {
|
|
42630
|
+
const dirty = { staged: 0, unstaged: 0, untracked: 0, conflicted: 0 };
|
|
42631
|
+
const files = [];
|
|
42632
|
+
for (const line of out.split("\n")) {
|
|
42633
|
+
if (!line || line.startsWith("#")) continue;
|
|
42634
|
+
const kind = line[0];
|
|
42635
|
+
if (kind === "?") {
|
|
42636
|
+
dirty.untracked++;
|
|
42637
|
+
files.push({ status: "?", path: line.slice(2) });
|
|
42638
|
+
} else if (kind === "u") {
|
|
42639
|
+
dirty.conflicted++;
|
|
42640
|
+
files.push({ status: "U", path: line.split(" ").slice(10).join(" ") });
|
|
42641
|
+
} else if (kind === "1" || kind === "2") {
|
|
42642
|
+
const xy = line.slice(2, 4);
|
|
42643
|
+
const x = xy[0] ?? ".";
|
|
42644
|
+
const y = xy[1] ?? ".";
|
|
42645
|
+
if (x !== ".") dirty.staged++;
|
|
42646
|
+
if (y !== ".") dirty.unstaged++;
|
|
42647
|
+
const parts = line.split(" ");
|
|
42648
|
+
const path = (kind === "1" ? parts.slice(8) : parts.slice(9)).join(" ").split(" ")[0] ?? "";
|
|
42649
|
+
files.push({ status: y !== "." ? y : x, path });
|
|
42650
|
+
}
|
|
42651
|
+
}
|
|
42652
|
+
return { dirty, files };
|
|
42653
|
+
}
|
|
42654
|
+
function parseLog(out, withFiles) {
|
|
42655
|
+
const commits = [];
|
|
42656
|
+
for (const rec of out.split(RS2)) {
|
|
42657
|
+
if (!rec.trim()) continue;
|
|
42658
|
+
const [header = "", ...rest] = rec.split("\n");
|
|
42659
|
+
const [sha = "", parents = "", author = "", date = "", subject = ""] = header.split(US);
|
|
42660
|
+
if (!sha) continue;
|
|
42661
|
+
const files = withFiles ? rest.map((l) => l.trim()).filter(Boolean).map((l) => {
|
|
42662
|
+
const cols = l.split(" ");
|
|
42663
|
+
return { status: (cols[0] ?? "M").charAt(0), path: cols[cols.length - 1] ?? "" };
|
|
42664
|
+
}) : [];
|
|
42665
|
+
commits.push({ sha, parents: parents.split(" ").filter(Boolean), author, date, subject, files });
|
|
42666
|
+
}
|
|
42667
|
+
return commits;
|
|
42668
|
+
}
|
|
42669
|
+
async function buildRepoSnapshot(dir2, subdir) {
|
|
42670
|
+
const L2 = GIT_SNAPSHOT_LIMITS;
|
|
42671
|
+
const fmt = `--format=${RS2}%H${US}%P${US}%an${US}%aI${US}%s`;
|
|
42672
|
+
const [abbrev, headSha, heads, remotes, remoteHead, status, gitDir] = await Promise.all([
|
|
42673
|
+
git2(dir2, ["rev-parse", "--abbrev-ref", "HEAD"]),
|
|
42674
|
+
git2(dir2, ["rev-parse", "--verify", "HEAD"]),
|
|
42675
|
+
git2(dir2, ["for-each-ref", "refs/heads", `--format=%(refname:short)${US}%(objectname)${US}%(upstream:short)${US}%(upstream:track,nobracket)`]),
|
|
42676
|
+
git2(dir2, ["for-each-ref", "refs/remotes/origin", `--format=%(refname:short)${US}%(objectname)${US}%(symref)`]),
|
|
42677
|
+
git2(dir2, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"]),
|
|
42678
|
+
git2(dir2, ["status", "--porcelain=v2", "--untracked-files=all"]),
|
|
42679
|
+
git2(dir2, ["rev-parse", "--absolute-git-dir"])
|
|
42680
|
+
]);
|
|
42681
|
+
const branchName = abbrev.trim();
|
|
42682
|
+
const detached = branchName === "HEAD";
|
|
42683
|
+
const branches = heads.split("\n").filter(Boolean).map((l) => {
|
|
42684
|
+
const [name = "", sha = "", upstream2 = "", track = ""] = l.split(US);
|
|
42685
|
+
return { name, sha, upstream: upstream2 || null, ...parseTrack(track) };
|
|
42686
|
+
});
|
|
42687
|
+
const remoteBranches = remotes.split("\n").filter(Boolean).map((l) => l.split(US)).filter(([, , symref]) => !symref).map(([name = "", sha = ""]) => ({ name: name.replace(/^origin\//, ""), sha })).filter((b2) => b2.name && b2.name !== "origin");
|
|
42688
|
+
const { dirty, files } = parseStatus(status);
|
|
42689
|
+
const [localOut, remoteOut] = await Promise.all([
|
|
42690
|
+
git2(dir2, ["log", "--branches", "--not", "--remotes", "-n", String(L2.localCommits + 1), "--name-status", fmt], 3e4),
|
|
42691
|
+
git2(dir2, ["log", "--remotes", "--date-order", "-n", String(L2.remoteCommits), fmt], 3e4)
|
|
42692
|
+
]);
|
|
42693
|
+
const localAll = parseLog(localOut, true);
|
|
42694
|
+
const localCommits = localAll.slice(0, L2.localCommits).map((c) => ({
|
|
42695
|
+
...c,
|
|
42696
|
+
branches: branches.filter((b2) => b2.sha === c.sha).map((b2) => b2.name),
|
|
42697
|
+
files: c.files.slice(0, L2.filesPerCommit),
|
|
42698
|
+
filesTruncated: c.files.length > L2.filesPerCommit
|
|
42699
|
+
}));
|
|
42700
|
+
const remoteCommits = parseLog(remoteOut, false).map(({ files: _f, ...c }) => c);
|
|
42701
|
+
const defaultBranch = remoteHead.trim().replace(/^origin\//, "") || null;
|
|
42702
|
+
const upstream = branches.find((b2) => b2.name === branchName)?.upstream;
|
|
42703
|
+
const baseRef = upstream ?? (defaultBranch ? `origin/${defaultBranch}` : null);
|
|
42704
|
+
const baseSha = headSha.trim() && baseRef ? (await git2(dir2, ["merge-base", "HEAD", baseRef])).trim() || null : null;
|
|
42705
|
+
let lastFetchAt = null;
|
|
42706
|
+
try {
|
|
42707
|
+
if (gitDir.trim()) lastFetchAt = (await stat(join20(gitDir.trim(), "FETCH_HEAD"))).mtime.toISOString();
|
|
42708
|
+
} catch {
|
|
42709
|
+
lastFetchAt = null;
|
|
42710
|
+
}
|
|
42711
|
+
return {
|
|
42712
|
+
subdir,
|
|
42713
|
+
head: { branch: detached || !branchName ? null : branchName, sha: headSha.trim() || null, detached },
|
|
42714
|
+
branches,
|
|
42715
|
+
remote: { name: "origin", defaultBranch, branches: remoteBranches },
|
|
42716
|
+
dirty,
|
|
42717
|
+
dirtyFiles: files.slice(0, L2.dirtyFiles),
|
|
42718
|
+
dirtyTruncated: files.length > L2.dirtyFiles,
|
|
42719
|
+
localCommits,
|
|
42720
|
+
localTruncated: localAll.length > L2.localCommits,
|
|
42721
|
+
remoteCommits,
|
|
42722
|
+
baseSha,
|
|
42723
|
+
lastFetchAt
|
|
42724
|
+
};
|
|
42725
|
+
}
|
|
42726
|
+
async function buildGitSnapshot(root) {
|
|
42727
|
+
const base = resolve5(root);
|
|
42728
|
+
const { repos } = await gitRepos(base);
|
|
42729
|
+
const snaps = await Promise.all(repos.map((r) => buildRepoSnapshot(r.subdir ? resolve5(base, r.subdir) : base, r.subdir)));
|
|
42730
|
+
return { takenAt: (/* @__PURE__ */ new Date()).toISOString(), repos: snaps };
|
|
42731
|
+
}
|
|
42732
|
+
async function fetchAll(root) {
|
|
42733
|
+
const base = resolve5(root);
|
|
42734
|
+
const { repos } = await gitRepos(base);
|
|
42735
|
+
for (const r of repos) await git2(r.subdir ? resolve5(base, r.subdir) : base, ["fetch", "--prune", "--quiet", "origin"], 6e4);
|
|
42736
|
+
}
|
|
42737
|
+
var state2 = { host: null, lastSentAt: 0, pending: null, periodic: null, running: false };
|
|
42738
|
+
async function reportNow(fetchFirst) {
|
|
42739
|
+
const host = state2.host;
|
|
42740
|
+
const root = host?.physicRoot;
|
|
42741
|
+
if (!host || !root || state2.running || activeAiRunCount() > 0) return;
|
|
42742
|
+
state2.running = true;
|
|
42743
|
+
try {
|
|
42744
|
+
if (fetchFirst) await fetchAll(root);
|
|
42745
|
+
const payload = await buildGitSnapshot(root);
|
|
42746
|
+
if (payload.repos.length === 0) return;
|
|
42747
|
+
host.send(WsChannels.GIT_SNAPSHOT, payload);
|
|
42748
|
+
state2.lastSentAt = Date.now();
|
|
42749
|
+
} catch (err) {
|
|
42750
|
+
logger.warn("git.snapshot.failed", { error: String(err) });
|
|
42751
|
+
} finally {
|
|
42752
|
+
state2.running = false;
|
|
42753
|
+
}
|
|
42754
|
+
}
|
|
42755
|
+
function requestGitSnapshot(reason) {
|
|
42756
|
+
if (!state2.host || state2.pending) return;
|
|
42757
|
+
const wait = Math.max(0, state2.lastSentAt + MIN_SPACING_MS - Date.now());
|
|
42758
|
+
state2.pending = setTimeout(() => {
|
|
42759
|
+
state2.pending = null;
|
|
42760
|
+
logger.debug("git.snapshot.request", { reason });
|
|
42761
|
+
void reportNow(false);
|
|
42762
|
+
}, wait || 1e3);
|
|
42763
|
+
state2.pending.unref?.();
|
|
42764
|
+
}
|
|
42765
|
+
function startGitSnapshots(host) {
|
|
42766
|
+
state2.host = host;
|
|
42767
|
+
if (state2.periodic) clearInterval(state2.periodic);
|
|
42768
|
+
state2.periodic = null;
|
|
42769
|
+
const raw = readProfileConfig(host.profileDir).gitSnapshotIntervalMin;
|
|
42770
|
+
const minutes = typeof raw === "number" && Number.isFinite(raw) && raw >= 0 ? raw : DEFAULT_INTERVAL_MIN;
|
|
42771
|
+
if (minutes > 0) {
|
|
42772
|
+
state2.periodic = setInterval(() => void reportNow(true), minutes * 6e4);
|
|
42773
|
+
state2.periodic.unref?.();
|
|
42774
|
+
}
|
|
42775
|
+
requestGitSnapshot("connect");
|
|
42776
|
+
}
|
|
42777
|
+
|
|
42778
|
+
// src/core/ws-client/transcript.ts
|
|
42779
|
+
function toDtoEntry(e) {
|
|
42780
|
+
return {
|
|
42781
|
+
id: e.id,
|
|
42782
|
+
source: e.source,
|
|
42783
|
+
kind: e.kind,
|
|
42784
|
+
text: e.text,
|
|
42785
|
+
level: e.level,
|
|
42786
|
+
resultKind: e.resultKind,
|
|
42787
|
+
// Processing time on a terminal `exit` entry (ADR-0249) — the web renders it inline.
|
|
42788
|
+
...e.durationMs != null ? { durationMs: e.durationMs } : {},
|
|
42789
|
+
// AI-run metadata on an `aireq` marker — powers the web's clickable CLI name → details modal.
|
|
42790
|
+
...e.aiMeta ? { aiMeta: e.aiMeta } : {}
|
|
42791
|
+
};
|
|
42792
|
+
}
|
|
42793
|
+
function envReconnectMaxSec() {
|
|
42794
|
+
const raw = Number(process.env.FOURPM_RECONNECT_MAX_BACKOFF_SEC);
|
|
42795
|
+
if (!Number.isFinite(raw)) return null;
|
|
42796
|
+
const { min, max } = CLI_SETTINGS_BOUNDS.reconnectMaxBackoffSec;
|
|
42797
|
+
return Math.min(Math.max(Math.floor(raw), min), max);
|
|
42798
|
+
}
|
|
42799
|
+
|
|
42420
42800
|
// src/core/autonomous-cycle.ts
|
|
42421
42801
|
import { randomUUID } from "crypto";
|
|
42422
|
-
import { appendFile as appendFile2, mkdir as mkdir3, readFile as
|
|
42423
|
-
import { join as
|
|
42802
|
+
import { appendFile as appendFile2, mkdir as mkdir3, readFile as readFile6, readdir as readdir2, rm as rm2, stat as stat2 } from "fs/promises";
|
|
42803
|
+
import { join as join21 } from "path";
|
|
42424
42804
|
function buildAutonomousCyclePrompt(opts = {}) {
|
|
42425
42805
|
const capNote = opts.intakeBlocked ? `
|
|
42426
42806
|
|
|
@@ -42620,11 +43000,11 @@ async function reportNewBookRows(ctx, root, before) {
|
|
|
42620
43000
|
}
|
|
42621
43001
|
async function dayLog(root, line) {
|
|
42622
43002
|
try {
|
|
42623
|
-
const dir2 =
|
|
43003
|
+
const dir2 = join21(root, ".claude", "logs");
|
|
42624
43004
|
await mkdir3(dir2, { recursive: true });
|
|
42625
43005
|
const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
42626
43006
|
const ts2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
42627
|
-
await appendFile2(
|
|
43007
|
+
await appendFile2(join21(dir2, `autonomous-tick-${day}.log`), `${ts2} ${line}
|
|
42628
43008
|
`, "utf8");
|
|
42629
43009
|
} catch {
|
|
42630
43010
|
}
|
|
@@ -42632,12 +43012,12 @@ async function dayLog(root, line) {
|
|
|
42632
43012
|
async function pruneLogs(root, retentionDays) {
|
|
42633
43013
|
if (!(retentionDays > 0)) return;
|
|
42634
43014
|
try {
|
|
42635
|
-
const dir2 =
|
|
43015
|
+
const dir2 = join21(root, ".claude", "logs");
|
|
42636
43016
|
const cutoff = Date.now() - retentionDays * 864e5;
|
|
42637
43017
|
for (const f2 of await readdir2(dir2)) {
|
|
42638
43018
|
if (!/^autonomous-tick-.*\.log$/.test(f2)) continue;
|
|
42639
|
-
const st2 = await
|
|
42640
|
-
if (st2 && st2.mtimeMs < cutoff) await rm2(
|
|
43019
|
+
const st2 = await stat2(join21(dir2, f2)).catch(() => null);
|
|
43020
|
+
if (st2 && st2.mtimeMs < cutoff) await rm2(join21(dir2, f2), { force: true }).catch(() => void 0);
|
|
42641
43021
|
}
|
|
42642
43022
|
} catch {
|
|
42643
43023
|
}
|
|
@@ -42649,15 +43029,15 @@ async function recordRun(root, hist, status, note, today) {
|
|
|
42649
43029
|
}
|
|
42650
43030
|
async function readJsonObject(path) {
|
|
42651
43031
|
try {
|
|
42652
|
-
return JSON.parse(await
|
|
43032
|
+
return JSON.parse(await readFile6(path, "utf8"));
|
|
42653
43033
|
} catch {
|
|
42654
43034
|
return {};
|
|
42655
43035
|
}
|
|
42656
43036
|
}
|
|
42657
43037
|
async function hasWork(root) {
|
|
42658
|
-
const approvals = await readJsonObject(
|
|
43038
|
+
const approvals = await readJsonObject(join21(root, ".claude/.autonomous.approvals.json"));
|
|
42659
43039
|
const approved = Object.entries(approvals).filter(([, v2]) => v2 && typeof v2 === "object" && v2.approved === true).map(([k2]) => k2);
|
|
42660
|
-
const read = (f2) =>
|
|
43040
|
+
const read = (f2) => readFile6(join21(root, f2), "utf8").catch(() => "");
|
|
42661
43041
|
const [userTodo, userQa, aiTodo, aiProgress] = await Promise.all([
|
|
42662
43042
|
read("USER_TODO.md"),
|
|
42663
43043
|
read("USER_QA.md"),
|
|
@@ -42695,12 +43075,12 @@ async function allAiProfilesExhausted(ctx, cfg) {
|
|
|
42695
43075
|
|
|
42696
43076
|
// src/core/fs-browse.ts
|
|
42697
43077
|
import { readdir as readdir3 } from "fs/promises";
|
|
42698
|
-
import { resolve as
|
|
43078
|
+
import { resolve as resolve6, sep as sep3 } from "path";
|
|
42699
43079
|
async function listDir(path, root) {
|
|
42700
43080
|
if (!root) return { path: "", entries: [] };
|
|
42701
|
-
const base =
|
|
42702
|
-
const requested =
|
|
42703
|
-
const target = requested === base || requested.startsWith(base +
|
|
43081
|
+
const base = resolve6(root);
|
|
43082
|
+
const requested = resolve6(base, path && path.trim() ? path : ".");
|
|
43083
|
+
const target = requested === base || requested.startsWith(base + sep3) ? requested : base;
|
|
42704
43084
|
try {
|
|
42705
43085
|
const dirents = await readdir3(target, { withFileTypes: true });
|
|
42706
43086
|
const entries2 = dirents.map((d) => ({
|
|
@@ -42717,14 +43097,14 @@ async function listDir(path, root) {
|
|
|
42717
43097
|
}
|
|
42718
43098
|
|
|
42719
43099
|
// src/core/fs-read.ts
|
|
42720
|
-
import { readFile as
|
|
42721
|
-
import { resolve as
|
|
43100
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
43101
|
+
import { resolve as resolve7, sep as sep4 } from "path";
|
|
42722
43102
|
var MAX_BYTES = 256 * 1024;
|
|
42723
43103
|
function resolveInRoot(root, relPath) {
|
|
42724
43104
|
if (!root) return null;
|
|
42725
|
-
const base =
|
|
42726
|
-
const target =
|
|
42727
|
-
return target === base || target.startsWith(base +
|
|
43105
|
+
const base = resolve7(root);
|
|
43106
|
+
const target = resolve7(base, relPath || ".");
|
|
43107
|
+
return target === base || target.startsWith(base + sep4) ? target : null;
|
|
42728
43108
|
}
|
|
42729
43109
|
async function readWorkerFile(root, path) {
|
|
42730
43110
|
const target = resolveInRoot(root, path);
|
|
@@ -42732,7 +43112,7 @@ async function readWorkerFile(root, path) {
|
|
|
42732
43112
|
return { path, content: "", truncated: false };
|
|
42733
43113
|
}
|
|
42734
43114
|
try {
|
|
42735
|
-
const buf = await
|
|
43115
|
+
const buf = await readFile7(target);
|
|
42736
43116
|
const truncated = buf.byteLength > MAX_BYTES;
|
|
42737
43117
|
const content = buf.subarray(0, MAX_BYTES).toString("utf8");
|
|
42738
43118
|
return { path: target, content, truncated };
|
|
@@ -42743,13 +43123,13 @@ async function readWorkerFile(root, path) {
|
|
|
42743
43123
|
|
|
42744
43124
|
// src/core/fs-write.ts
|
|
42745
43125
|
import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
|
|
42746
|
-
import { dirname as dirname5, resolve as
|
|
43126
|
+
import { dirname as dirname5, resolve as resolve8, sep as sep5 } from "path";
|
|
42747
43127
|
var MAX_BYTES2 = 2 * 1024 * 1024;
|
|
42748
43128
|
function resolveInRoot2(root, relPath) {
|
|
42749
43129
|
if (!root) return null;
|
|
42750
|
-
const base =
|
|
42751
|
-
const target =
|
|
42752
|
-
return target === base || target.startsWith(base +
|
|
43130
|
+
const base = resolve8(root);
|
|
43131
|
+
const target = resolve8(base, relPath || ".");
|
|
43132
|
+
return target === base || target.startsWith(base + sep5) ? target : null;
|
|
42753
43133
|
}
|
|
42754
43134
|
async function writeWorkerFile(root, relPath, content) {
|
|
42755
43135
|
const target = resolveInRoot2(root, relPath);
|
|
@@ -42770,24 +43150,24 @@ async function writeWorkerFile(root, relPath, content) {
|
|
|
42770
43150
|
}
|
|
42771
43151
|
|
|
42772
43152
|
// src/core/fs-mutate.ts
|
|
42773
|
-
import { mkdir as mkdir5, writeFile as writeFile6, rename, rm as rm3, stat as
|
|
42774
|
-
import { basename as basename3, dirname as dirname6, resolve as
|
|
43153
|
+
import { mkdir as mkdir5, writeFile as writeFile6, rename, rm as rm3, stat as stat3 } from "fs/promises";
|
|
43154
|
+
import { basename as basename3, dirname as dirname6, resolve as resolve9, sep as sep6 } from "path";
|
|
42775
43155
|
var MAX_BYTES3 = 2 * 1024 * 1024;
|
|
42776
43156
|
function resolveInRoot3(root, rel) {
|
|
42777
|
-
const base =
|
|
42778
|
-
const target =
|
|
42779
|
-
return target === base || target.startsWith(base +
|
|
43157
|
+
const base = resolve9(root);
|
|
43158
|
+
const target = resolve9(base, rel || ".");
|
|
43159
|
+
return target === base || target.startsWith(base + sep6) ? target : null;
|
|
42780
43160
|
}
|
|
42781
43161
|
async function isDir(p2) {
|
|
42782
43162
|
try {
|
|
42783
|
-
return (await
|
|
43163
|
+
return (await stat3(p2)).isDirectory();
|
|
42784
43164
|
} catch {
|
|
42785
43165
|
return false;
|
|
42786
43166
|
}
|
|
42787
43167
|
}
|
|
42788
43168
|
async function exists(p2) {
|
|
42789
43169
|
try {
|
|
42790
|
-
await
|
|
43170
|
+
await stat3(p2);
|
|
42791
43171
|
return true;
|
|
42792
43172
|
} catch {
|
|
42793
43173
|
return false;
|
|
@@ -42800,13 +43180,13 @@ async function mutateFs(root, req) {
|
|
|
42800
43180
|
switch (req.op) {
|
|
42801
43181
|
case "mkdir": {
|
|
42802
43182
|
const target = resolveInRoot3(root, req.path ?? "");
|
|
42803
|
-
if (!target || target ===
|
|
43183
|
+
if (!target || target === resolve9(root)) return escaped(req.path ?? "");
|
|
42804
43184
|
await mkdir5(target, { recursive: true });
|
|
42805
43185
|
return { ok: true, path: target };
|
|
42806
43186
|
}
|
|
42807
43187
|
case "create": {
|
|
42808
43188
|
const target = resolveInRoot3(root, req.path ?? "");
|
|
42809
|
-
if (!target || target ===
|
|
43189
|
+
if (!target || target === resolve9(root)) return escaped(req.path ?? "");
|
|
42810
43190
|
if (await exists(target)) return { ok: false, path: target, error: "already exists" };
|
|
42811
43191
|
const content = req.content ?? "";
|
|
42812
43192
|
if (Buffer.byteLength(content, "utf8") > MAX_BYTES3) {
|
|
@@ -42820,9 +43200,9 @@ async function mutateFs(root, req) {
|
|
|
42820
43200
|
const from = resolveInRoot3(root, req.from ?? "");
|
|
42821
43201
|
let to2 = resolveInRoot3(root, req.to ?? "");
|
|
42822
43202
|
if (!from || !to2) return escaped(req.from ?? req.to ?? "");
|
|
42823
|
-
if (from ===
|
|
42824
|
-
if (await isDir(to2)) to2 =
|
|
42825
|
-
if (to2 !==
|
|
43203
|
+
if (from === resolve9(root)) return { ok: false, path: from, error: "cannot move the project root" };
|
|
43204
|
+
if (await isDir(to2)) to2 = resolve9(to2, basename3(from));
|
|
43205
|
+
if (to2 !== resolve9(root) && !to2.startsWith(resolve9(root) + sep6)) return escaped(req.to ?? "");
|
|
42826
43206
|
if (await exists(to2)) return { ok: false, path: to2, error: "destination already exists" };
|
|
42827
43207
|
await mkdir5(dirname6(to2), { recursive: true });
|
|
42828
43208
|
await rename(from, to2);
|
|
@@ -42831,7 +43211,7 @@ async function mutateFs(root, req) {
|
|
|
42831
43211
|
case "delete": {
|
|
42832
43212
|
const target = resolveInRoot3(root, req.path ?? "");
|
|
42833
43213
|
if (!target) return escaped(req.path ?? "");
|
|
42834
|
-
if (target ===
|
|
43214
|
+
if (target === resolve9(root)) return { ok: false, path: target, error: "cannot delete the project root" };
|
|
42835
43215
|
await rm3(target, { recursive: true, force: true });
|
|
42836
43216
|
return { ok: true, path: target };
|
|
42837
43217
|
}
|
|
@@ -42844,13 +43224,13 @@ async function mutateFs(root, req) {
|
|
|
42844
43224
|
}
|
|
42845
43225
|
|
|
42846
43226
|
// src/core/fs-transfer.ts
|
|
42847
|
-
import { mkdir as mkdir6, readFile as
|
|
42848
|
-
import { basename as basename4, dirname as dirname7, extname, resolve as
|
|
43227
|
+
import { mkdir as mkdir6, readFile as readFile8, stat as stat4, writeFile as writeFile7 } from "fs/promises";
|
|
43228
|
+
import { basename as basename4, dirname as dirname7, extname, resolve as resolve10, sep as sep7 } from "path";
|
|
42849
43229
|
function resolveInRoot4(root, relPath) {
|
|
42850
43230
|
if (!root) return null;
|
|
42851
|
-
const base =
|
|
42852
|
-
const target =
|
|
42853
|
-
return target === base || target.startsWith(base +
|
|
43231
|
+
const base = resolve10(root);
|
|
43232
|
+
const target = resolve10(base, relPath || ".");
|
|
43233
|
+
return target === base || target.startsWith(base + sep7) ? target : null;
|
|
42854
43234
|
}
|
|
42855
43235
|
var MIME_BY_EXT = {
|
|
42856
43236
|
".png": "image/png",
|
|
@@ -42899,12 +43279,12 @@ async function downloadBinaryFile(root, relPath) {
|
|
|
42899
43279
|
const target = resolveInRoot4(root, relPath);
|
|
42900
43280
|
if (!target) return { error: "path escapes the project root" };
|
|
42901
43281
|
try {
|
|
42902
|
-
const info = await
|
|
43282
|
+
const info = await stat4(target);
|
|
42903
43283
|
if (!info.isFile()) return { error: "not a file" };
|
|
42904
43284
|
if (info.size > FS_TRANSFER_MAX_BYTES) {
|
|
42905
43285
|
return { error: `file too large (max ${FS_TRANSFER_MAX_BYTES} bytes)` };
|
|
42906
43286
|
}
|
|
42907
|
-
const buf = await
|
|
43287
|
+
const buf = await readFile8(target);
|
|
42908
43288
|
return {
|
|
42909
43289
|
contentBase64: buf.toString("base64"),
|
|
42910
43290
|
contentType: guessMime(target),
|
|
@@ -42963,16 +43343,16 @@ function handleFsChannels(ctx, message, payload) {
|
|
|
42963
43343
|
}
|
|
42964
43344
|
|
|
42965
43345
|
// src/core/git-diff.ts
|
|
42966
|
-
import { readFile as
|
|
42967
|
-
import { execFile as
|
|
42968
|
-
import { basename as basename5, dirname as dirname8, resolve as
|
|
42969
|
-
import { promisify as
|
|
42970
|
-
var
|
|
43346
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
43347
|
+
import { execFile as execFile7 } from "child_process";
|
|
43348
|
+
import { basename as basename5, dirname as dirname8, resolve as resolve11, sep as sep8 } from "path";
|
|
43349
|
+
import { promisify as promisify7 } from "util";
|
|
43350
|
+
var run7 = promisify7(execFile7);
|
|
42971
43351
|
function resolveInRoot5(root, relPath) {
|
|
42972
43352
|
if (!root) return null;
|
|
42973
|
-
const base =
|
|
42974
|
-
const target =
|
|
42975
|
-
return target === base || target.startsWith(base +
|
|
43353
|
+
const base = resolve11(root);
|
|
43354
|
+
const target = resolve11(base, relPath || ".");
|
|
43355
|
+
return target === base || target.startsWith(base + sep8) ? target : null;
|
|
42976
43356
|
}
|
|
42977
43357
|
function looksBinary(buf) {
|
|
42978
43358
|
return buf.subarray(0, 8e3).includes(0);
|
|
@@ -42990,12 +43370,12 @@ async function gitDiff(root, path) {
|
|
|
42990
43370
|
const base = basename5(target);
|
|
42991
43371
|
let newBuf = null;
|
|
42992
43372
|
try {
|
|
42993
|
-
newBuf = await
|
|
43373
|
+
newBuf = await readFile9(target);
|
|
42994
43374
|
} catch {
|
|
42995
43375
|
}
|
|
42996
43376
|
let oldBuf = null;
|
|
42997
43377
|
try {
|
|
42998
|
-
const { stdout } = await
|
|
43378
|
+
const { stdout } = await run7("git", ["show", `HEAD:./${base}`], {
|
|
42999
43379
|
cwd: dir2,
|
|
43000
43380
|
timeout: 15e3,
|
|
43001
43381
|
maxBuffer: FS_TRANSFER_MAX_BYTES + 1024,
|
|
@@ -43025,12 +43405,12 @@ async function gitDiff(root, path) {
|
|
|
43025
43405
|
}
|
|
43026
43406
|
|
|
43027
43407
|
// src/core/git-env.ts
|
|
43028
|
-
import { execFile as
|
|
43029
|
-
import { promisify as
|
|
43030
|
-
var
|
|
43408
|
+
import { execFile as execFile8 } from "child_process";
|
|
43409
|
+
import { promisify as promisify8 } from "util";
|
|
43410
|
+
var run8 = promisify8(execFile8);
|
|
43031
43411
|
async function hasBinary(bin, args = ["--version"]) {
|
|
43032
43412
|
try {
|
|
43033
|
-
await
|
|
43413
|
+
await run8(bin, args, { timeout: 5e3 });
|
|
43034
43414
|
return true;
|
|
43035
43415
|
} catch {
|
|
43036
43416
|
return false;
|
|
@@ -43043,7 +43423,7 @@ async function checkGitEnv(provider) {
|
|
|
43043
43423
|
let account = null;
|
|
43044
43424
|
if (installed) {
|
|
43045
43425
|
try {
|
|
43046
|
-
const { stdout, stderr } = await
|
|
43426
|
+
const { stdout, stderr } = await run8(bin, ["auth", "status"], { timeout: 8e3 });
|
|
43047
43427
|
const out = `${stdout}
|
|
43048
43428
|
${stderr}`;
|
|
43049
43429
|
authenticated = /logged in|logged into|✓/i.test(out);
|
|
@@ -43056,33 +43436,33 @@ ${stderr}`;
|
|
|
43056
43436
|
}
|
|
43057
43437
|
|
|
43058
43438
|
// src/core/git-ssh-key.ts
|
|
43059
|
-
import { execFile as
|
|
43060
|
-
import { readFile as
|
|
43061
|
-
import { join as
|
|
43062
|
-
import { promisify as
|
|
43063
|
-
var
|
|
43439
|
+
import { execFile as execFile9 } from "child_process";
|
|
43440
|
+
import { readFile as readFile10, rm as rm4, mkdir as mkdir7 } from "fs/promises";
|
|
43441
|
+
import { join as join22 } from "path";
|
|
43442
|
+
import { promisify as promisify9 } from "util";
|
|
43443
|
+
var run9 = promisify9(execFile9);
|
|
43064
43444
|
function keyPaths(profileDir2) {
|
|
43065
|
-
const dir2 =
|
|
43066
|
-
const key =
|
|
43445
|
+
const dir2 = join22(profileDir2, ".ssh");
|
|
43446
|
+
const key = join22(dir2, "id_4pm");
|
|
43067
43447
|
return { dir: dir2, key, pub: `${key}.pub` };
|
|
43068
43448
|
}
|
|
43069
43449
|
async function readPublicKey(pub) {
|
|
43070
43450
|
try {
|
|
43071
|
-
return (await
|
|
43451
|
+
return (await readFile10(pub, "utf8")).trim();
|
|
43072
43452
|
} catch {
|
|
43073
43453
|
return null;
|
|
43074
43454
|
}
|
|
43075
43455
|
}
|
|
43076
43456
|
async function fingerprintOf(pub) {
|
|
43077
43457
|
try {
|
|
43078
|
-
const { stdout } = await
|
|
43458
|
+
const { stdout } = await run9("ssh-keygen", ["-lf", pub], { timeout: 5e3 });
|
|
43079
43459
|
return stdout.match(/SHA256:[A-Za-z0-9+/=]+/)?.[0] ?? null;
|
|
43080
43460
|
} catch {
|
|
43081
43461
|
return null;
|
|
43082
43462
|
}
|
|
43083
43463
|
}
|
|
43084
43464
|
async function configureGit(key) {
|
|
43085
|
-
await
|
|
43465
|
+
await run9(
|
|
43086
43466
|
"git",
|
|
43087
43467
|
["config", "--global", "core.sshCommand", `ssh -i ${key} -o IdentitiesOnly=yes`],
|
|
43088
43468
|
{ timeout: 5e3 }
|
|
@@ -43093,7 +43473,7 @@ async function generate(profileDir2) {
|
|
|
43093
43473
|
await mkdir7(dir2, { recursive: true });
|
|
43094
43474
|
await rm4(key, { force: true });
|
|
43095
43475
|
await rm4(pub, { force: true });
|
|
43096
|
-
await
|
|
43476
|
+
await run9("ssh-keygen", ["-t", "ed25519", "-f", key, "-N", "", "-C", "4pm-deploy-key"], {
|
|
43097
43477
|
timeout: 15e3
|
|
43098
43478
|
});
|
|
43099
43479
|
await configureGit(key);
|
|
@@ -43103,7 +43483,7 @@ async function remove(profileDir2) {
|
|
|
43103
43483
|
const { key, pub } = keyPaths(profileDir2);
|
|
43104
43484
|
await rm4(key, { force: true });
|
|
43105
43485
|
await rm4(pub, { force: true });
|
|
43106
|
-
await
|
|
43486
|
+
await run9("git", ["config", "--global", "--unset", "core.sshCommand"], { timeout: 5e3 }).catch(
|
|
43107
43487
|
() => void 0
|
|
43108
43488
|
);
|
|
43109
43489
|
return { publicKey: null, fingerprint: null };
|
|
@@ -43123,109 +43503,6 @@ async function manageSshKey(op, profileDir2) {
|
|
|
43123
43503
|
}
|
|
43124
43504
|
}
|
|
43125
43505
|
|
|
43126
|
-
// src/core/git-history.ts
|
|
43127
|
-
import { execFile as execFile8 } from "child_process";
|
|
43128
|
-
import { resolve as resolve10, sep as sep8 } from "path";
|
|
43129
|
-
import { promisify as promisify8 } from "util";
|
|
43130
|
-
var run8 = promisify8(execFile8);
|
|
43131
|
-
var FS = "";
|
|
43132
|
-
var RS = "";
|
|
43133
|
-
function resolveRepo(root, repo) {
|
|
43134
|
-
if (!root) return null;
|
|
43135
|
-
const base = resolve10(root);
|
|
43136
|
-
const requested = resolve10(base, repo && repo.trim() ? repo : ".");
|
|
43137
|
-
return requested === base || requested.startsWith(base + sep8) ? requested : base;
|
|
43138
|
-
}
|
|
43139
|
-
async function git(cwd2, args) {
|
|
43140
|
-
try {
|
|
43141
|
-
const { stdout } = await run8("git", args, {
|
|
43142
|
-
cwd: cwd2,
|
|
43143
|
-
timeout: 15e3,
|
|
43144
|
-
maxBuffer: 8 * 1024 * 1024
|
|
43145
|
-
});
|
|
43146
|
-
return stdout;
|
|
43147
|
-
} catch {
|
|
43148
|
-
return "";
|
|
43149
|
-
}
|
|
43150
|
-
}
|
|
43151
|
-
async function gitRepos(root) {
|
|
43152
|
-
if (!root) return { repos: [] };
|
|
43153
|
-
const base = resolve10(root);
|
|
43154
|
-
const top = (await git(base, ["rev-parse", "--show-toplevel"])).trim();
|
|
43155
|
-
if (!top || !(top === base || top.startsWith(base + sep8))) return { repos: [] };
|
|
43156
|
-
const repos = [];
|
|
43157
|
-
const rootRemote = (await git(base, ["remote", "get-url", "origin"])).trim();
|
|
43158
|
-
const rootBranch = (await git(base, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
43159
|
-
repos.push({ subdir: "", name: "", remote: rootRemote || null, branch: rootBranch || null });
|
|
43160
|
-
const subOut = await git(base, ["submodule", "status"]);
|
|
43161
|
-
for (const line of subOut.split("\n")) {
|
|
43162
|
-
const m2 = line.trim().match(/^[-+U ]?[0-9a-f]{7,40}\s+(\S+)/);
|
|
43163
|
-
const subPath = m2?.[1];
|
|
43164
|
-
if (!subPath) continue;
|
|
43165
|
-
const abs = resolve10(base, subPath);
|
|
43166
|
-
if (!(abs === base || abs.startsWith(base + sep8))) continue;
|
|
43167
|
-
const remote = (await git(abs, ["remote", "get-url", "origin"])).trim();
|
|
43168
|
-
const branch = (await git(abs, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
43169
|
-
repos.push({ subdir: subPath, name: subPath, remote: remote || null, branch: branch || null });
|
|
43170
|
-
}
|
|
43171
|
-
return { repos };
|
|
43172
|
-
}
|
|
43173
|
-
async function gitLog(root, repo, skip, limit) {
|
|
43174
|
-
const cwd2 = resolveRepo(root, repo);
|
|
43175
|
-
if (!cwd2) return { entries: [], hasMore: false };
|
|
43176
|
-
const capped = Math.min(Math.max(limit, 1), 200);
|
|
43177
|
-
const format = ["%H", "%h", "%an", "%aI", "%s"].join(FS) + RS;
|
|
43178
|
-
const stdout = await git(cwd2, [
|
|
43179
|
-
"log",
|
|
43180
|
-
`--skip=${Math.max(skip, 0)}`,
|
|
43181
|
-
`-n`,
|
|
43182
|
-
String(capped + 1),
|
|
43183
|
-
`--pretty=format:${format}`
|
|
43184
|
-
]);
|
|
43185
|
-
const rows = stdout.split(RS).map((r) => r.replace(/^\n/, "")).filter((r) => r.length > 0);
|
|
43186
|
-
const entries2 = rows.slice(0, capped).map((row) => {
|
|
43187
|
-
const [hash = "", shortHash = "", author = "", date = "", subject = ""] = row.split(FS);
|
|
43188
|
-
return { hash, shortHash, author, date, subject };
|
|
43189
|
-
});
|
|
43190
|
-
return { entries: entries2, hasMore: rows.length > capped };
|
|
43191
|
-
}
|
|
43192
|
-
async function gitCommit(root, repo, hash) {
|
|
43193
|
-
const cwd2 = resolveRepo(root, repo);
|
|
43194
|
-
const empty = { hash, author: "", date: "", subject: "", files: [] };
|
|
43195
|
-
if (!cwd2 || !hash) return empty;
|
|
43196
|
-
const format = ["%H", "%an", "%aI", "%s"].join(FS);
|
|
43197
|
-
const stdout = await git(cwd2, [
|
|
43198
|
-
"show",
|
|
43199
|
-
"--name-status",
|
|
43200
|
-
"-M",
|
|
43201
|
-
`--pretty=format:${format}`,
|
|
43202
|
-
hash
|
|
43203
|
-
]);
|
|
43204
|
-
if (!stdout) return empty;
|
|
43205
|
-
const lines = stdout.split("\n");
|
|
43206
|
-
const [full = hash, author = "", date = "", subject = ""] = (lines[0] ?? "").split(FS);
|
|
43207
|
-
const files = [];
|
|
43208
|
-
for (const line of lines.slice(1)) {
|
|
43209
|
-
if (!line.trim()) continue;
|
|
43210
|
-
const parts = line.split(" ");
|
|
43211
|
-
const status = (parts[0] ?? "").charAt(0);
|
|
43212
|
-
if (!status) continue;
|
|
43213
|
-
if (status === "R" || status === "C") {
|
|
43214
|
-
files.push({ path: parts[2] ?? "", status, oldPath: parts[1] ?? void 0 });
|
|
43215
|
-
} else {
|
|
43216
|
-
files.push({ path: parts[1] ?? "", status });
|
|
43217
|
-
}
|
|
43218
|
-
}
|
|
43219
|
-
return { hash: full, author, date, subject, files };
|
|
43220
|
-
}
|
|
43221
|
-
async function gitCommitDiff(root, repo, hash, path) {
|
|
43222
|
-
const cwd2 = resolveRepo(root, repo);
|
|
43223
|
-
if (!cwd2 || !hash || !path) return { path, oldContent: "", newContent: "" };
|
|
43224
|
-
const oldContent = await git(cwd2, ["show", `${hash}^:${path}`]);
|
|
43225
|
-
const newContent = await git(cwd2, ["show", `${hash}:${path}`]);
|
|
43226
|
-
return { path, oldContent, newContent };
|
|
43227
|
-
}
|
|
43228
|
-
|
|
43229
43506
|
// src/core/ws-client/handlers/git.ts
|
|
43230
43507
|
function handleGitChannels(ctx, message, payload) {
|
|
43231
43508
|
switch (message.channel) {
|
|
@@ -43278,12 +43555,12 @@ function handleGitChannels(ctx, message, payload) {
|
|
|
43278
43555
|
}
|
|
43279
43556
|
|
|
43280
43557
|
// src/core/ws-client/handlers/misc.ts
|
|
43281
|
-
import { join as
|
|
43558
|
+
import { join as join23 } from "path";
|
|
43282
43559
|
|
|
43283
43560
|
// src/core/ai-assist.ts
|
|
43284
|
-
import { execFile as
|
|
43285
|
-
import { promisify as
|
|
43286
|
-
var
|
|
43561
|
+
import { execFile as execFile10 } from "child_process";
|
|
43562
|
+
import { promisify as promisify10 } from "util";
|
|
43563
|
+
var run10 = promisify10(execFile10);
|
|
43287
43564
|
function aiCli() {
|
|
43288
43565
|
return process.env.AI_CLI || "claude";
|
|
43289
43566
|
}
|
|
@@ -43326,7 +43603,7 @@ function parseAi(cli, stdout, prompt) {
|
|
|
43326
43603
|
}
|
|
43327
43604
|
async function runAi(prompt) {
|
|
43328
43605
|
const cli = aiCli();
|
|
43329
|
-
const { stdout } = await
|
|
43606
|
+
const { stdout } = await run10(cli, aiArgs(cli, prompt), {
|
|
43330
43607
|
timeout: 12e4,
|
|
43331
43608
|
maxBuffer: 8 * 1024 * 1024
|
|
43332
43609
|
});
|
|
@@ -43428,7 +43705,7 @@ function handleMiscChannels(ctx, message, payload) {
|
|
|
43428
43705
|
}
|
|
43429
43706
|
case WsChannels.LOG_READ: {
|
|
43430
43707
|
const limit = Math.min(Math.max(payload.limit ?? 200, 1), 2e3);
|
|
43431
|
-
const lines = readRecentLogLines(
|
|
43708
|
+
const lines = readRecentLogLines(join23(ctx.profileDir, "logs"), limit);
|
|
43432
43709
|
ctx.send(WsChannels.LOG_READ, { lines }, message.id);
|
|
43433
43710
|
return true;
|
|
43434
43711
|
}
|
|
@@ -43460,7 +43737,7 @@ function handleMiscChannels(ctx, message, payload) {
|
|
|
43460
43737
|
|
|
43461
43738
|
// src/core/ws-client/handlers/project.ts
|
|
43462
43739
|
import { existsSync as existsSync14, mkdirSync as mkdirSync7, renameSync, rmSync as rmSync7 } from "fs";
|
|
43463
|
-
import { join as
|
|
43740
|
+
import { join as join24 } from "path";
|
|
43464
43741
|
|
|
43465
43742
|
// src/core/ai-task.ts
|
|
43466
43743
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -43532,8 +43809,8 @@ function handleProjectChannels(ctx, message, payload) {
|
|
|
43532
43809
|
case WsChannels.PHYSIC_SYNC: {
|
|
43533
43810
|
const sync = payload;
|
|
43534
43811
|
const oldName = sync.oldName;
|
|
43535
|
-
const oldPath = oldName ?
|
|
43536
|
-
const newPath =
|
|
43812
|
+
const oldPath = oldName ? join24(ctx.profileDir, oldName) : null;
|
|
43813
|
+
const newPath = join24(ctx.profileDir, sync.newName);
|
|
43537
43814
|
if (oldName) {
|
|
43538
43815
|
const oldRoot = ctx.physicFolderPath(oldName);
|
|
43539
43816
|
if (oldRoot) void uninstallCron(oldRoot).catch(() => void 0);
|
|
@@ -43545,6 +43822,7 @@ function handleProjectChannels(ctx, message, payload) {
|
|
|
43545
43822
|
mkdirSync7(newPath, { recursive: true });
|
|
43546
43823
|
}
|
|
43547
43824
|
ctx.physicRoot = ctx.physicFolderPath(sync.newName);
|
|
43825
|
+
requestGitSnapshot("physic-sync");
|
|
43548
43826
|
ctx.bus.setProject(sync.newName);
|
|
43549
43827
|
ctx.bus.log(t("project.folderSynced", { name: sync.newName }));
|
|
43550
43828
|
void ctx.cloneServingRepos().catch(() => void 0);
|
|
@@ -43555,7 +43833,7 @@ function handleProjectChannels(ctx, message, payload) {
|
|
|
43555
43833
|
if (del.name) {
|
|
43556
43834
|
const delRoot = ctx.physicFolderPath(del.name);
|
|
43557
43835
|
if (delRoot) void uninstallCron(delRoot).catch(() => void 0);
|
|
43558
|
-
rmSync7(
|
|
43836
|
+
rmSync7(join24(ctx.profileDir, del.name), { recursive: true, force: true });
|
|
43559
43837
|
ctx.bus.log(t("project.folderDeleted", { name: del.name }));
|
|
43560
43838
|
}
|
|
43561
43839
|
void manageSshKey("delete", ctx.profileDir).catch(() => void 0);
|
|
@@ -43574,6 +43852,7 @@ function handleProjectChannels(ctx, message, payload) {
|
|
|
43574
43852
|
projectAiMemoryBudgetChars: tokens.memory?.budgetChars ?? 0
|
|
43575
43853
|
});
|
|
43576
43854
|
ctx.setRestrictToFolder(tokens.restrictToFolder === true);
|
|
43855
|
+
if (tokens.gitAuth !== void 0) applyGitAuth(ctx, tokens.gitAuth, tokens.gitAuthHost ?? null);
|
|
43577
43856
|
logger.info("project.tokens.applied", {
|
|
43578
43857
|
aiRunTimeoutSec: tokens.aiRunTimeoutSec ?? 0,
|
|
43579
43858
|
autoClearIdleMinutes: tokens.autoClearIdleMinutes ?? 0
|
|
@@ -43586,9 +43865,28 @@ function handleProjectChannels(ctx, message, payload) {
|
|
|
43586
43865
|
ctx.profileDir,
|
|
43587
43866
|
(p2) => ctx.send(WsChannels.PROJECT_PROGRESS, p2),
|
|
43588
43867
|
// AI init on the standard AI path + one org run slot (ADR-0362).
|
|
43589
|
-
createAiTaskRunner(ctx)
|
|
43590
|
-
|
|
43868
|
+
createAiTaskRunner(ctx),
|
|
43869
|
+
(method, host) => applyGitAuth(ctx, method, host)
|
|
43870
|
+
).then((reply) => {
|
|
43871
|
+
ctx.send(WsChannels.PROJECT_CREATE, reply, message.id);
|
|
43872
|
+
requestGitSnapshot("scaffold");
|
|
43873
|
+
});
|
|
43874
|
+
return true;
|
|
43875
|
+
case WsChannels.PROJECT_PUBLISH: {
|
|
43876
|
+
const root = ctx.physicRoot;
|
|
43877
|
+
if (!root) {
|
|
43878
|
+
ctx.send(WsChannels.PROJECT_PUBLISH, { publish: null, error: "This worker has no project folder." }, message.id);
|
|
43879
|
+
return true;
|
|
43880
|
+
}
|
|
43881
|
+
const projectId = payload.projectId;
|
|
43882
|
+
void publishScaffold(root, (step, msg) => ctx.send(WsChannels.PROJECT_PROGRESS, { projectId, step, message: msg })).then((publish) => {
|
|
43883
|
+
ctx.send(WsChannels.PROJECT_PUBLISH, { publish }, message.id);
|
|
43884
|
+
requestGitSnapshot("publish");
|
|
43885
|
+
}).catch(
|
|
43886
|
+
(err) => ctx.send(WsChannels.PROJECT_PUBLISH, { publish: null, error: String(err) }, message.id)
|
|
43887
|
+
);
|
|
43591
43888
|
return true;
|
|
43889
|
+
}
|
|
43592
43890
|
case WsChannels.PROJECT_ADD:
|
|
43593
43891
|
void addProject(
|
|
43594
43892
|
payload,
|
|
@@ -43596,12 +43894,23 @@ function handleProjectChannels(ctx, message, payload) {
|
|
|
43596
43894
|
(p2) => ctx.send(WsChannels.PROJECT_PROGRESS, p2),
|
|
43597
43895
|
// An add-with-scaffold runs AI init too (ADR-0362).
|
|
43598
43896
|
createAiTaskRunner(ctx)
|
|
43599
|
-
).then((reply) =>
|
|
43897
|
+
).then((reply) => {
|
|
43898
|
+
ctx.send(WsChannels.PROJECT_ADD, reply, message.id);
|
|
43899
|
+
requestGitSnapshot("provision");
|
|
43900
|
+
});
|
|
43600
43901
|
return true;
|
|
43601
43902
|
default:
|
|
43602
43903
|
return false;
|
|
43603
43904
|
}
|
|
43604
43905
|
}
|
|
43906
|
+
function applyGitAuth(ctx, method, host) {
|
|
43907
|
+
configureGitAuth(
|
|
43908
|
+
method ?? null,
|
|
43909
|
+
host,
|
|
43910
|
+
ctx.profileDir,
|
|
43911
|
+
(req) => ctx.request(WsChannels.GIT_TOKEN, req)
|
|
43912
|
+
);
|
|
43913
|
+
}
|
|
43605
43914
|
|
|
43606
43915
|
// src/core/knowledge-compose.ts
|
|
43607
43916
|
import { spawn as spawn8 } from "child_process";
|
|
@@ -43623,7 +43932,7 @@ Extra focus from the author: ${req.prompt}` : "";
|
|
|
43623
43932
|
].join("\n");
|
|
43624
43933
|
}
|
|
43625
43934
|
function runOnce2(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
43626
|
-
return new Promise((
|
|
43935
|
+
return new Promise((resolve13, reject) => {
|
|
43627
43936
|
const args = ["-p", ...profile?.model ? ["--model", profile.model] : [], ...denySettingsArgs(cmd, [...denyDirs, profile?.dir])];
|
|
43628
43937
|
const env3 = {
|
|
43629
43938
|
...process.env,
|
|
@@ -43646,7 +43955,7 @@ function runOnce2(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
|
43646
43955
|
});
|
|
43647
43956
|
child.on("close", (code) => {
|
|
43648
43957
|
clearTimeout(timer);
|
|
43649
|
-
|
|
43958
|
+
resolve13({ code: code ?? -1, out, err });
|
|
43650
43959
|
});
|
|
43651
43960
|
child.stdin.write(prompt);
|
|
43652
43961
|
child.stdin.end();
|
|
@@ -43676,13 +43985,13 @@ async function runKnowledgeCompose(req, ai2, physicRoot) {
|
|
|
43676
43985
|
}
|
|
43677
43986
|
|
|
43678
43987
|
// src/core/faq-compose.ts
|
|
43679
|
-
import { spawn as spawn9, execFile as
|
|
43988
|
+
import { spawn as spawn9, execFile as execFile11 } from "child_process";
|
|
43680
43989
|
import { existsSync as existsSync15, mkdirSync as mkdirSync8, rmSync as rmSync8 } from "fs";
|
|
43681
43990
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
43682
|
-
import { join as
|
|
43991
|
+
import { join as join25 } from "path";
|
|
43683
43992
|
import { tmpdir as tmpdir4 } from "os";
|
|
43684
|
-
import { promisify as
|
|
43685
|
-
var execFileAsync2 =
|
|
43993
|
+
import { promisify as promisify11 } from "util";
|
|
43994
|
+
var execFileAsync2 = promisify11(execFile11);
|
|
43686
43995
|
var NO_USAGE2 = { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };
|
|
43687
43996
|
var COMPOSE_TIMEOUT_MS2 = 10 * 60 * 1e3;
|
|
43688
43997
|
var AGENT_DENIED_TOOLS = ["Bash", "WebFetch", "WebSearch", "NotebookEdit", "Task"];
|
|
@@ -43747,7 +44056,7 @@ function parsePerTicket(text) {
|
|
|
43747
44056
|
return [];
|
|
43748
44057
|
}
|
|
43749
44058
|
function runClaudeOnce2(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
43750
|
-
return new Promise((
|
|
44059
|
+
return new Promise((resolve13, reject) => {
|
|
43751
44060
|
const isClaude = cmd.includes("claude");
|
|
43752
44061
|
const args = [
|
|
43753
44062
|
"-p",
|
|
@@ -43780,7 +44089,7 @@ function runClaudeOnce2(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
|
43780
44089
|
child.on("close", (code) => {
|
|
43781
44090
|
clearTimeout(timer);
|
|
43782
44091
|
out += parser.flush();
|
|
43783
|
-
|
|
44092
|
+
resolve13({ code: code ?? -1, out, err, usage: parser.usage() });
|
|
43784
44093
|
});
|
|
43785
44094
|
child.stdin.write(prompt);
|
|
43786
44095
|
child.stdin.end();
|
|
@@ -43800,7 +44109,7 @@ async function runClaudeWithFailover2(prompt, cwd2, ai2) {
|
|
|
43800
44109
|
var MAX_OUTPUT_CHARS = 8e3;
|
|
43801
44110
|
async function runFaqCompose(req, ai2) {
|
|
43802
44111
|
if (!req.repo.token) return { prUrl: "", error: "no write token" };
|
|
43803
|
-
const workDir =
|
|
44112
|
+
const workDir = join25(tmpdir4(), "4pm-faq-sync", randomUUID3());
|
|
43804
44113
|
const gitEnv = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
|
|
43805
44114
|
const auth = gitAuthArgs("x-access-token", req.repo.token);
|
|
43806
44115
|
try {
|
|
@@ -44028,8 +44337,8 @@ function handleToolsChannels(ctx, message, payload) {
|
|
|
44028
44337
|
}
|
|
44029
44338
|
|
|
44030
44339
|
// src/core/agents.ts
|
|
44031
|
-
import { readFile as
|
|
44032
|
-
import { join as
|
|
44340
|
+
import { readFile as readFile11, writeFile as writeFile8, mkdir as mkdir8, readdir as readdir4, rm as rm5 } from "fs/promises";
|
|
44341
|
+
import { join as join26 } from "path";
|
|
44033
44342
|
var AGENTS_REL = ".claude/agents";
|
|
44034
44343
|
var SKILLS_REL = ".claude/skills";
|
|
44035
44344
|
var MEMORY_DIRS = /* @__PURE__ */ new Set(["short-memory", "long-memory"]);
|
|
@@ -44038,7 +44347,7 @@ function safeName(name) {
|
|
|
44038
44347
|
}
|
|
44039
44348
|
async function readText2(path, fallback = "") {
|
|
44040
44349
|
try {
|
|
44041
|
-
return await
|
|
44350
|
+
return await readFile11(path, "utf8");
|
|
44042
44351
|
} catch {
|
|
44043
44352
|
return fallback;
|
|
44044
44353
|
}
|
|
@@ -44053,17 +44362,17 @@ async function listAgents(root) {
|
|
|
44053
44362
|
const subagents = [];
|
|
44054
44363
|
const skills = [];
|
|
44055
44364
|
try {
|
|
44056
|
-
for (const e of await readdir4(
|
|
44365
|
+
for (const e of await readdir4(join26(root, AGENTS_REL), { withFileTypes: true })) {
|
|
44057
44366
|
if (!e.isFile() || !e.name.endsWith(".md") || MEMORY_DIRS.has(e.name)) continue;
|
|
44058
|
-
const fm = frontmatter(await readText2(
|
|
44367
|
+
const fm = frontmatter(await readText2(join26(root, AGENTS_REL, e.name)));
|
|
44059
44368
|
subagents.push({ name: e.name.replace(/\.md$/, ""), description: fm.description });
|
|
44060
44369
|
}
|
|
44061
44370
|
} catch {
|
|
44062
44371
|
}
|
|
44063
44372
|
try {
|
|
44064
|
-
for (const e of await readdir4(
|
|
44373
|
+
for (const e of await readdir4(join26(root, SKILLS_REL), { withFileTypes: true })) {
|
|
44065
44374
|
if (!e.isDirectory()) continue;
|
|
44066
|
-
const fm = frontmatter(await readText2(
|
|
44375
|
+
const fm = frontmatter(await readText2(join26(root, SKILLS_REL, e.name, "SKILL.md")));
|
|
44067
44376
|
skills.push({ name: e.name, description: fm.description });
|
|
44068
44377
|
}
|
|
44069
44378
|
} catch {
|
|
@@ -44074,15 +44383,15 @@ async function listAgents(root) {
|
|
|
44074
44383
|
}
|
|
44075
44384
|
function itemPath(root, kind, name) {
|
|
44076
44385
|
const n = safeName(name);
|
|
44077
|
-
return kind === "subagent" ?
|
|
44386
|
+
return kind === "subagent" ? join26(root, AGENTS_REL, `${n}.md`) : join26(root, SKILLS_REL, n, "SKILL.md");
|
|
44078
44387
|
}
|
|
44079
44388
|
async function readAgent(root, kind, name) {
|
|
44080
44389
|
const content = await readText2(itemPath(root, kind, name));
|
|
44081
44390
|
if (kind !== "subagent") return { content };
|
|
44082
44391
|
const n = safeName(name);
|
|
44083
44392
|
const [short, long] = await Promise.all([
|
|
44084
|
-
readText2(
|
|
44085
|
-
readText2(
|
|
44393
|
+
readText2(join26(root, AGENTS_REL, "short-memory", `${n}.md`)),
|
|
44394
|
+
readText2(join26(root, AGENTS_REL, "long-memory", `${n}.md`))
|
|
44086
44395
|
]);
|
|
44087
44396
|
return { content, memory: { short, long } };
|
|
44088
44397
|
}
|
|
@@ -44092,12 +44401,12 @@ async function writeAgent(root, req) {
|
|
|
44092
44401
|
try {
|
|
44093
44402
|
if (req.action === "delete") {
|
|
44094
44403
|
if (req.kind === "subagent") await rm5(itemPath(root, "subagent", n), { force: true });
|
|
44095
|
-
else await rm5(
|
|
44404
|
+
else await rm5(join26(root, SKILLS_REL, n), { recursive: true, force: true });
|
|
44096
44405
|
return { ok: true };
|
|
44097
44406
|
}
|
|
44098
44407
|
if (req.content === void 0) return { ok: false, error: "content is required" };
|
|
44099
44408
|
const path = itemPath(root, req.kind, n);
|
|
44100
|
-
await mkdir8(
|
|
44409
|
+
await mkdir8(join26(path, ".."), { recursive: true });
|
|
44101
44410
|
await writeFile8(path, req.content, "utf8");
|
|
44102
44411
|
return { ok: true };
|
|
44103
44412
|
} catch (err) {
|
|
@@ -44106,21 +44415,21 @@ async function writeAgent(root, req) {
|
|
|
44106
44415
|
}
|
|
44107
44416
|
|
|
44108
44417
|
// src/core/secrets.ts
|
|
44109
|
-
import { readFile as
|
|
44110
|
-
import { join as
|
|
44418
|
+
import { readFile as readFile12, writeFile as writeFile9 } from "fs/promises";
|
|
44419
|
+
import { join as join27 } from "path";
|
|
44111
44420
|
var SECURITY_REL = "AI_SECURITY.md";
|
|
44112
44421
|
var PLACEHOLDER_REL = "AI_PLACEHOLDER.md";
|
|
44113
44422
|
var SECRETS_REL = "project.secrets.json";
|
|
44114
44423
|
async function readText3(path, fallback = "") {
|
|
44115
44424
|
try {
|
|
44116
|
-
return await
|
|
44425
|
+
return await readFile12(path, "utf8");
|
|
44117
44426
|
} catch {
|
|
44118
44427
|
return fallback;
|
|
44119
44428
|
}
|
|
44120
44429
|
}
|
|
44121
44430
|
async function readSecretsObject(root) {
|
|
44122
44431
|
try {
|
|
44123
|
-
const v2 = JSON.parse(await
|
|
44432
|
+
const v2 = JSON.parse(await readFile12(join27(root, SECRETS_REL), "utf8"));
|
|
44124
44433
|
return v2 && typeof v2 === "object" ? v2 : {};
|
|
44125
44434
|
} catch {
|
|
44126
44435
|
return {};
|
|
@@ -44139,8 +44448,8 @@ function placeholderKeys(text) {
|
|
|
44139
44448
|
}
|
|
44140
44449
|
async function readSecrets(root) {
|
|
44141
44450
|
const [security, placeholder, secrets] = await Promise.all([
|
|
44142
|
-
readText3(
|
|
44143
|
-
readText3(
|
|
44451
|
+
readText3(join27(root, SECURITY_REL)),
|
|
44452
|
+
readText3(join27(root, PLACEHOLDER_REL)),
|
|
44144
44453
|
readSecretsObject(root)
|
|
44145
44454
|
]);
|
|
44146
44455
|
const names = /* @__PURE__ */ new Set([...placeholderKeys(placeholder), ...Object.keys(secrets)]);
|
|
@@ -44153,14 +44462,14 @@ async function readSecrets(root) {
|
|
|
44153
44462
|
async function writeSecrets(root, req) {
|
|
44154
44463
|
try {
|
|
44155
44464
|
if (req.kind === "security") {
|
|
44156
|
-
await writeFile9(
|
|
44465
|
+
await writeFile9(join27(root, SECURITY_REL), req.content, "utf8");
|
|
44157
44466
|
} else if (req.kind === "placeholder") {
|
|
44158
|
-
await writeFile9(
|
|
44467
|
+
await writeFile9(join27(root, PLACEHOLDER_REL), req.content, "utf8");
|
|
44159
44468
|
} else {
|
|
44160
44469
|
const secrets = await readSecretsObject(root);
|
|
44161
44470
|
if (req.kind === "secret") secrets[req.key] = req.value;
|
|
44162
44471
|
else delete secrets[req.key];
|
|
44163
|
-
await writeFile9(
|
|
44472
|
+
await writeFile9(join27(root, SECRETS_REL), JSON.stringify(secrets, null, 2) + "\n", "utf8");
|
|
44164
44473
|
}
|
|
44165
44474
|
return { ok: true };
|
|
44166
44475
|
} catch (err) {
|
|
@@ -44169,17 +44478,17 @@ async function writeSecrets(root, req) {
|
|
|
44169
44478
|
}
|
|
44170
44479
|
|
|
44171
44480
|
// src/core/agent-tools.ts
|
|
44172
|
-
import { readFile as
|
|
44173
|
-
import { dirname as dirname9, join as
|
|
44481
|
+
import { readFile as readFile13, writeFile as writeFile10, mkdir as mkdir9 } from "fs/promises";
|
|
44482
|
+
import { dirname as dirname9, join as join28 } from "path";
|
|
44174
44483
|
var SHARED_REL = ".claude/settings.json";
|
|
44175
44484
|
var LOCAL_REL = ".claude/settings.local.json";
|
|
44176
44485
|
var SECRETS_DENY = "Read(./project.secrets.json)";
|
|
44177
44486
|
function fileFor(root, scope) {
|
|
44178
|
-
return
|
|
44487
|
+
return join28(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
|
|
44179
44488
|
}
|
|
44180
44489
|
async function readSettingsObject(path) {
|
|
44181
44490
|
try {
|
|
44182
|
-
const v2 = JSON.parse(await
|
|
44491
|
+
const v2 = JSON.parse(await readFile13(path, "utf8"));
|
|
44183
44492
|
return v2 && typeof v2 === "object" ? v2 : {};
|
|
44184
44493
|
} catch {
|
|
44185
44494
|
return {};
|
|
@@ -44211,8 +44520,8 @@ async function writeAgentTools(root, scope, permissions) {
|
|
|
44211
44520
|
|
|
44212
44521
|
// src/core/packages.ts
|
|
44213
44522
|
import { createHash as createHash4 } from "crypto";
|
|
44214
|
-
import { readFile as
|
|
44215
|
-
import { dirname as dirname10, join as
|
|
44523
|
+
import { readFile as readFile14, writeFile as writeFile11, mkdir as mkdir10, readdir as readdir5, rm as rm6, stat as stat5 } from "fs/promises";
|
|
44524
|
+
import { dirname as dirname10, join as join29, relative } from "path";
|
|
44216
44525
|
var AGENTS_REL2 = ".claude/agents";
|
|
44217
44526
|
var SKILLS_REL2 = ".claude/skills";
|
|
44218
44527
|
var MANIFEST_REL = ".claude/.4pm-packages.json";
|
|
@@ -44221,7 +44530,7 @@ function safeName2(name) {
|
|
|
44221
44530
|
}
|
|
44222
44531
|
async function readMaybe(path) {
|
|
44223
44532
|
try {
|
|
44224
|
-
return await
|
|
44533
|
+
return await readFile14(path);
|
|
44225
44534
|
} catch {
|
|
44226
44535
|
return null;
|
|
44227
44536
|
}
|
|
@@ -44236,7 +44545,7 @@ async function listFilesRel(dir2) {
|
|
|
44236
44545
|
return;
|
|
44237
44546
|
}
|
|
44238
44547
|
for (const e of entries2) {
|
|
44239
|
-
const full =
|
|
44548
|
+
const full = join29(d, e.name);
|
|
44240
44549
|
if (e.isDirectory()) await walk2(full);
|
|
44241
44550
|
else if (e.isFile()) out.push(relative(dir2, full));
|
|
44242
44551
|
}
|
|
@@ -44247,16 +44556,16 @@ async function listFilesRel(dir2) {
|
|
|
44247
44556
|
async function readArtifactFiles(root, kind, slug) {
|
|
44248
44557
|
const files = [];
|
|
44249
44558
|
if (kind === "subagent") {
|
|
44250
|
-
const agent = await readMaybe(
|
|
44559
|
+
const agent = await readMaybe(join29(root, AGENTS_REL2, `${slug}.md`));
|
|
44251
44560
|
if (agent) files.push({ path: "agent.md", contentBase64: agent.toString("base64") });
|
|
44252
|
-
const short = await readMaybe(
|
|
44561
|
+
const short = await readMaybe(join29(root, AGENTS_REL2, "short-memory", `${slug}.md`));
|
|
44253
44562
|
if (short) files.push({ path: "short-memory.md", contentBase64: short.toString("base64") });
|
|
44254
|
-
const long = await readMaybe(
|
|
44563
|
+
const long = await readMaybe(join29(root, AGENTS_REL2, "long-memory", `${slug}.md`));
|
|
44255
44564
|
if (long) files.push({ path: "long-memory.md", contentBase64: long.toString("base64") });
|
|
44256
44565
|
} else {
|
|
44257
|
-
const base =
|
|
44566
|
+
const base = join29(root, SKILLS_REL2, slug);
|
|
44258
44567
|
for (const rel of await listFilesRel(base)) {
|
|
44259
|
-
const buf = await readMaybe(
|
|
44568
|
+
const buf = await readMaybe(join29(base, rel));
|
|
44260
44569
|
if (buf) files.push({ path: rel.split("\\").join("/"), contentBase64: buf.toString("base64") });
|
|
44261
44570
|
}
|
|
44262
44571
|
}
|
|
@@ -44274,16 +44583,16 @@ async function artifactSha256(root, kind, slug) {
|
|
|
44274
44583
|
return h.digest("hex");
|
|
44275
44584
|
}
|
|
44276
44585
|
async function artifactExists(root, kind, slug) {
|
|
44277
|
-
const target = kind === "subagent" ?
|
|
44586
|
+
const target = kind === "subagent" ? join29(root, AGENTS_REL2, `${slug}.md`) : join29(root, SKILLS_REL2, slug);
|
|
44278
44587
|
try {
|
|
44279
|
-
await
|
|
44588
|
+
await stat5(target);
|
|
44280
44589
|
return true;
|
|
44281
44590
|
} catch {
|
|
44282
44591
|
return false;
|
|
44283
44592
|
}
|
|
44284
44593
|
}
|
|
44285
44594
|
async function readManifest(root) {
|
|
44286
|
-
const buf = await readMaybe(
|
|
44595
|
+
const buf = await readMaybe(join29(root, MANIFEST_REL));
|
|
44287
44596
|
if (!buf) return {};
|
|
44288
44597
|
try {
|
|
44289
44598
|
return JSON.parse(buf.toString("utf8"));
|
|
@@ -44292,7 +44601,7 @@ async function readManifest(root) {
|
|
|
44292
44601
|
}
|
|
44293
44602
|
}
|
|
44294
44603
|
async function writeManifest(root, manifest) {
|
|
44295
|
-
const path =
|
|
44604
|
+
const path = join29(root, MANIFEST_REL);
|
|
44296
44605
|
await mkdir10(dirname10(path), { recursive: true });
|
|
44297
44606
|
await writeFile11(path, JSON.stringify(manifest, null, 2), "utf8");
|
|
44298
44607
|
}
|
|
@@ -44301,24 +44610,24 @@ async function writeArtifactFiles(root, kind, slug, files) {
|
|
|
44301
44610
|
if (kind === "subagent") {
|
|
44302
44611
|
const agent = byPath.get("agent.md");
|
|
44303
44612
|
if (agent) {
|
|
44304
|
-
await mkdir10(
|
|
44305
|
-
await writeFile11(
|
|
44613
|
+
await mkdir10(join29(root, AGENTS_REL2), { recursive: true });
|
|
44614
|
+
await writeFile11(join29(root, AGENTS_REL2, `${slug}.md`), agent);
|
|
44306
44615
|
}
|
|
44307
44616
|
const short = byPath.get("short-memory.md");
|
|
44308
44617
|
if (short) {
|
|
44309
|
-
await mkdir10(
|
|
44310
|
-
await writeFile11(
|
|
44618
|
+
await mkdir10(join29(root, AGENTS_REL2, "short-memory"), { recursive: true });
|
|
44619
|
+
await writeFile11(join29(root, AGENTS_REL2, "short-memory", `${slug}.md`), short);
|
|
44311
44620
|
}
|
|
44312
44621
|
const long = byPath.get("long-memory.md");
|
|
44313
44622
|
if (long) {
|
|
44314
|
-
await mkdir10(
|
|
44315
|
-
await writeFile11(
|
|
44623
|
+
await mkdir10(join29(root, AGENTS_REL2, "long-memory"), { recursive: true });
|
|
44624
|
+
await writeFile11(join29(root, AGENTS_REL2, "long-memory", `${slug}.md`), long);
|
|
44316
44625
|
}
|
|
44317
44626
|
} else {
|
|
44318
|
-
const base =
|
|
44627
|
+
const base = join29(root, SKILLS_REL2, slug);
|
|
44319
44628
|
for (const [rel, buf] of byPath) {
|
|
44320
44629
|
if (rel.includes("..") || rel.startsWith("/")) continue;
|
|
44321
|
-
const dest =
|
|
44630
|
+
const dest = join29(base, rel);
|
|
44322
44631
|
await mkdir10(dirname10(dest), { recursive: true });
|
|
44323
44632
|
await writeFile11(dest, buf);
|
|
44324
44633
|
}
|
|
@@ -44384,11 +44693,11 @@ async function removePackage(root, slug) {
|
|
|
44384
44693
|
const entry = manifest[n];
|
|
44385
44694
|
if (!entry) return { ok: false, error: "not installed" };
|
|
44386
44695
|
if (entry.kind === "subagent") {
|
|
44387
|
-
await rm6(
|
|
44388
|
-
await rm6(
|
|
44389
|
-
await rm6(
|
|
44696
|
+
await rm6(join29(root, AGENTS_REL2, `${n}.md`), { force: true });
|
|
44697
|
+
await rm6(join29(root, AGENTS_REL2, "short-memory", `${n}.md`), { force: true });
|
|
44698
|
+
await rm6(join29(root, AGENTS_REL2, "long-memory", `${n}.md`), { force: true });
|
|
44390
44699
|
} else {
|
|
44391
|
-
await rm6(
|
|
44700
|
+
await rm6(join29(root, SKILLS_REL2, n), { recursive: true, force: true });
|
|
44392
44701
|
}
|
|
44393
44702
|
delete manifest[n];
|
|
44394
44703
|
await writeManifest(root, manifest);
|
|
@@ -44399,8 +44708,8 @@ async function removePackage(root, slug) {
|
|
|
44399
44708
|
}
|
|
44400
44709
|
|
|
44401
44710
|
// src/core/graph.ts
|
|
44402
|
-
import { readFile as
|
|
44403
|
-
import { join as
|
|
44711
|
+
import { readFile as readFile15, readdir as readdir6 } from "fs/promises";
|
|
44712
|
+
import { join as join30, relative as relative2, dirname as dirname11, resolve as resolve12, extname as extname2, basename as basename6 } from "path";
|
|
44404
44713
|
var MAX_FILES = 2e3;
|
|
44405
44714
|
var MAX_NODES = 3e3;
|
|
44406
44715
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "out", "coverage", ".turbo"]);
|
|
@@ -44420,7 +44729,7 @@ async function walk(root, accept) {
|
|
|
44420
44729
|
if (e.name.startsWith(".") && e.name !== ".claude") {
|
|
44421
44730
|
if (e.isDirectory()) continue;
|
|
44422
44731
|
}
|
|
44423
|
-
const full =
|
|
44732
|
+
const full = join30(dir2, e.name);
|
|
44424
44733
|
if (e.isDirectory()) {
|
|
44425
44734
|
if (!SKIP_DIRS.has(e.name)) stack.push(full);
|
|
44426
44735
|
} else if (accept(full)) {
|
|
@@ -44448,14 +44757,14 @@ async function buildDocs(root) {
|
|
|
44448
44757
|
const rel = relative2(root, f2).split("\\").join("/");
|
|
44449
44758
|
let text = "";
|
|
44450
44759
|
try {
|
|
44451
|
-
text = await
|
|
44760
|
+
text = await readFile15(f2, "utf8");
|
|
44452
44761
|
} catch {
|
|
44453
44762
|
continue;
|
|
44454
44763
|
}
|
|
44455
44764
|
for (const m2 of text.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)) {
|
|
44456
44765
|
const href = (m2[1] ?? "").split("#")[0].trim();
|
|
44457
44766
|
if (!href || !href.endsWith(".md") || href.startsWith("http")) continue;
|
|
44458
|
-
const target = relative2(root,
|
|
44767
|
+
const target = relative2(root, resolve12(dirname11(f2), href)).split("\\").join("/");
|
|
44459
44768
|
if (ids.has(target) && target !== rel) {
|
|
44460
44769
|
const key = `${rel}->${target}`;
|
|
44461
44770
|
if (!seen.has(key)) {
|
|
@@ -44487,7 +44796,7 @@ async function buildCode(root) {
|
|
|
44487
44796
|
const rel = relative2(root, f2).split("\\").join("/");
|
|
44488
44797
|
let text = "";
|
|
44489
44798
|
try {
|
|
44490
|
-
text = await
|
|
44799
|
+
text = await readFile15(f2, "utf8");
|
|
44491
44800
|
} catch {
|
|
44492
44801
|
continue;
|
|
44493
44802
|
}
|
|
@@ -44539,17 +44848,17 @@ async function buildGraph(root, mode) {
|
|
|
44539
44848
|
}
|
|
44540
44849
|
|
|
44541
44850
|
// src/core/rag.ts
|
|
44542
|
-
import { readFile as
|
|
44851
|
+
import { readFile as readFile16, writeFile as writeFile12, statfs as statfs2, mkdir as mkdir11 } from "fs/promises";
|
|
44543
44852
|
import { existsSync as existsSync16 } from "fs";
|
|
44544
|
-
import { execFile as
|
|
44853
|
+
import { execFile as execFile12, spawn as spawn10 } from "child_process";
|
|
44545
44854
|
import { totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
44546
|
-
import { join as
|
|
44547
|
-
import { promisify as
|
|
44548
|
-
var
|
|
44855
|
+
import { join as join31 } from "path";
|
|
44856
|
+
import { promisify as promisify12 } from "util";
|
|
44857
|
+
var run11 = promisify12(execFile12);
|
|
44549
44858
|
var RAG_REL = ".claude/rag";
|
|
44550
44859
|
async function tryRun(cmd, args) {
|
|
44551
44860
|
try {
|
|
44552
|
-
const { stdout } = await
|
|
44861
|
+
const { stdout } = await run11(cmd, args, { timeout: 8e3 });
|
|
44553
44862
|
return { ok: true, out: stdout.trim() };
|
|
44554
44863
|
} catch {
|
|
44555
44864
|
return { ok: false, out: "" };
|
|
@@ -44564,7 +44873,7 @@ async function detectPython() {
|
|
|
44564
44873
|
}
|
|
44565
44874
|
async function installLog(root) {
|
|
44566
44875
|
try {
|
|
44567
|
-
const text = await
|
|
44876
|
+
const text = await readFile16(join31(root, RAG_REL, "install.log"), "utf8");
|
|
44568
44877
|
return text.split("\n").filter((l) => l.length > 0).slice(-200);
|
|
44569
44878
|
} catch {
|
|
44570
44879
|
return [];
|
|
@@ -44626,14 +44935,14 @@ async function ragStatus(root) {
|
|
|
44626
44935
|
};
|
|
44627
44936
|
let modelName = "";
|
|
44628
44937
|
try {
|
|
44629
|
-
modelName = (await
|
|
44938
|
+
modelName = (await readFile16(join31(root, RAG_REL, ".installed"), "utf8")).trim();
|
|
44630
44939
|
} catch {
|
|
44631
44940
|
modelName = "";
|
|
44632
44941
|
}
|
|
44633
44942
|
const modelPresent = modelName.length > 0;
|
|
44634
44943
|
return {
|
|
44635
44944
|
installed: fastembed && sqliteVec && modelPresent,
|
|
44636
|
-
installing: existsSync16(
|
|
44945
|
+
installing: existsSync16(join31(root, RAG_REL, ".installing")),
|
|
44637
44946
|
python: { found: py.found, version: py.version },
|
|
44638
44947
|
deps: { pip, fastembed, sqliteVec },
|
|
44639
44948
|
model: { present: modelPresent, name: modelName },
|
|
@@ -44644,9 +44953,9 @@ async function ragStatus(root) {
|
|
|
44644
44953
|
};
|
|
44645
44954
|
}
|
|
44646
44955
|
async function readIndexMeta(root) {
|
|
44647
|
-
const indexing = existsSync16(
|
|
44956
|
+
const indexing = existsSync16(join31(root, RAG_REL, ".indexing"));
|
|
44648
44957
|
try {
|
|
44649
|
-
const m2 = JSON.parse(await
|
|
44958
|
+
const m2 = JSON.parse(await readFile16(join31(root, RAG_REL, ".index.json"), "utf8"));
|
|
44650
44959
|
return {
|
|
44651
44960
|
present: true,
|
|
44652
44961
|
indexing,
|
|
@@ -44713,14 +45022,14 @@ if __name__ == "__main__":
|
|
|
44713
45022
|
elif cmd == "query": query(sys.argv[2], int(sys.argv[3]) if len(sys.argv) > 3 else 8)
|
|
44714
45023
|
`;
|
|
44715
45024
|
async function ensureEngine(root) {
|
|
44716
|
-
await mkdir11(
|
|
44717
|
-
await writeFile12(
|
|
45025
|
+
await mkdir11(join31(root, RAG_REL), { recursive: true }).catch(() => void 0);
|
|
45026
|
+
await writeFile12(join31(root, RAG_REL, "rag_engine.py"), RAG_ENGINE_PY, "utf8");
|
|
44718
45027
|
}
|
|
44719
45028
|
async function ragReindex(root) {
|
|
44720
45029
|
const py = await detectPython();
|
|
44721
45030
|
if (!py.found) return { ok: false, started: false, error: "python3 not found" };
|
|
44722
|
-
if (!existsSync16(
|
|
44723
|
-
if (existsSync16(
|
|
45031
|
+
if (!existsSync16(join31(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
|
|
45032
|
+
if (existsSync16(join31(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
|
|
44724
45033
|
await ensureEngine(root);
|
|
44725
45034
|
const script = `
|
|
44726
45035
|
set +e
|
|
@@ -44740,12 +45049,12 @@ rm -f "${RAG_REL}/.indexing"
|
|
|
44740
45049
|
async function ragQuery(root, queryText, k2 = 8) {
|
|
44741
45050
|
const py = await detectPython();
|
|
44742
45051
|
if (!py.found) return { results: [], error: "python3 not found" };
|
|
44743
|
-
if (!existsSync16(
|
|
45052
|
+
if (!existsSync16(join31(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
|
|
44744
45053
|
await ensureEngine(root);
|
|
44745
45054
|
try {
|
|
44746
|
-
const { stdout } = await
|
|
45055
|
+
const { stdout } = await run11(
|
|
44747
45056
|
py.bin,
|
|
44748
|
-
[
|
|
45057
|
+
[join31(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k2, 1), 20))],
|
|
44749
45058
|
{ cwd: root, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
44750
45059
|
);
|
|
44751
45060
|
const parsed = JSON.parse(stdout.trim() || '{"results":[]}');
|
|
@@ -44760,9 +45069,9 @@ async function ragInstall(root, model) {
|
|
|
44760
45069
|
}
|
|
44761
45070
|
const py = await detectPython();
|
|
44762
45071
|
if (!py.found) return { ok: false, started: false, error: "python3 not found on the worker" };
|
|
44763
|
-
if (existsSync16(
|
|
45072
|
+
if (existsSync16(join31(root, RAG_REL, ".installing"))) return { ok: true, started: true };
|
|
44764
45073
|
try {
|
|
44765
|
-
await mkdir11(
|
|
45074
|
+
await mkdir11(join31(root, RAG_REL), { recursive: true });
|
|
44766
45075
|
} catch {
|
|
44767
45076
|
}
|
|
44768
45077
|
const script = `
|
|
@@ -44790,7 +45099,7 @@ rm -f "${RAG_REL}/.installing"
|
|
|
44790
45099
|
|
|
44791
45100
|
// src/core/config-sync.ts
|
|
44792
45101
|
import { existsSync as existsSync17, readFileSync as readFileSync16 } from "fs";
|
|
44793
|
-
import { join as
|
|
45102
|
+
import { join as join32 } from "path";
|
|
44794
45103
|
var SERVER_MANAGED_KEYS = [
|
|
44795
45104
|
"physicPath",
|
|
44796
45105
|
"sessionSwitchPct",
|
|
@@ -44807,7 +45116,7 @@ var SERVER_MANAGED_KEYS = [
|
|
|
44807
45116
|
"projectAiMemoryBudgetChars"
|
|
44808
45117
|
];
|
|
44809
45118
|
function readConfigText(profileDir2) {
|
|
44810
|
-
const path =
|
|
45119
|
+
const path = join32(profileDir2, "config.json");
|
|
44811
45120
|
if (existsSync17(path)) {
|
|
44812
45121
|
try {
|
|
44813
45122
|
return readFileSync16(path, "utf8");
|
|
@@ -45294,14 +45603,14 @@ var WsClient = class {
|
|
|
45294
45603
|
*/
|
|
45295
45604
|
awaitConnected(timeoutMs) {
|
|
45296
45605
|
if (this.sessionKey && this.bus.status === "connected") return Promise.resolve(true);
|
|
45297
|
-
return new Promise((
|
|
45606
|
+
return new Promise((resolve13) => {
|
|
45298
45607
|
const waiter = () => {
|
|
45299
45608
|
clearTimeout(timer);
|
|
45300
|
-
|
|
45609
|
+
resolve13(true);
|
|
45301
45610
|
};
|
|
45302
45611
|
const timer = setTimeout(() => {
|
|
45303
45612
|
this.connectWaiters = this.connectWaiters.filter((w2) => w2 !== waiter);
|
|
45304
|
-
|
|
45613
|
+
resolve13(false);
|
|
45305
45614
|
}, timeoutMs);
|
|
45306
45615
|
this.connectWaiters.push(waiter);
|
|
45307
45616
|
});
|
|
@@ -45328,16 +45637,16 @@ var WsClient = class {
|
|
|
45328
45637
|
this.forceReconnect = false;
|
|
45329
45638
|
return Promise.resolve();
|
|
45330
45639
|
}
|
|
45331
|
-
return new Promise((
|
|
45640
|
+
return new Promise((resolve13) => {
|
|
45332
45641
|
const timer = setTimeout(() => {
|
|
45333
45642
|
this.wakeReconnect = null;
|
|
45334
|
-
|
|
45643
|
+
resolve13();
|
|
45335
45644
|
}, ms2);
|
|
45336
45645
|
this.wakeReconnect = () => {
|
|
45337
45646
|
clearTimeout(timer);
|
|
45338
45647
|
this.wakeReconnect = null;
|
|
45339
45648
|
this.forceReconnect = false;
|
|
45340
|
-
|
|
45649
|
+
resolve13();
|
|
45341
45650
|
};
|
|
45342
45651
|
});
|
|
45343
45652
|
}
|
|
@@ -45391,7 +45700,7 @@ var WsClient = class {
|
|
|
45391
45700
|
* One WS session: token → connect → handshake → handle messages until close.
|
|
45392
45701
|
*/
|
|
45393
45702
|
connectOnce() {
|
|
45394
|
-
return new Promise((
|
|
45703
|
+
return new Promise((resolve13, reject) => {
|
|
45395
45704
|
void (async () => {
|
|
45396
45705
|
this.sessionEstablished = false;
|
|
45397
45706
|
this.bus.setStatus("connecting");
|
|
@@ -45492,7 +45801,7 @@ var WsClient = class {
|
|
|
45492
45801
|
if (code === CLOSE_DRAINING) {
|
|
45493
45802
|
this.bus.log(t("error.serverDraining"));
|
|
45494
45803
|
}
|
|
45495
|
-
|
|
45804
|
+
resolve13();
|
|
45496
45805
|
});
|
|
45497
45806
|
socket.on("error", (err) => {
|
|
45498
45807
|
this.clearHeartbeat();
|
|
@@ -45530,6 +45839,14 @@ var WsClient = class {
|
|
|
45530
45839
|
this.startUsagePolling();
|
|
45531
45840
|
this.startLogUpload();
|
|
45532
45841
|
this.startKbRefresh();
|
|
45842
|
+
const servedRoot = () => this.physicRoot;
|
|
45843
|
+
startGitSnapshots({
|
|
45844
|
+
get physicRoot() {
|
|
45845
|
+
return servedRoot();
|
|
45846
|
+
},
|
|
45847
|
+
profileDir: this.context.profileDir,
|
|
45848
|
+
send: (channel, data) => this.send(channel, data, null)
|
|
45849
|
+
});
|
|
45533
45850
|
setToolHealthSink((report) => this.send(WsChannels.TOOL_HEALTH, report));
|
|
45534
45851
|
this.connectWaiters.splice(0).forEach((fn) => fn());
|
|
45535
45852
|
return;
|
|
@@ -45618,7 +45935,7 @@ var WsClient = class {
|
|
|
45618
45935
|
* Used for quota.check before spawning an AI cli (ADR-0020).
|
|
45619
45936
|
*/
|
|
45620
45937
|
request(channel, data) {
|
|
45621
|
-
return new Promise((
|
|
45938
|
+
return new Promise((resolve13, reject) => {
|
|
45622
45939
|
if (!this.socket || !this.sessionKey) {
|
|
45623
45940
|
reject(new Error("Not connected to the server."));
|
|
45624
45941
|
return;
|
|
@@ -45638,7 +45955,7 @@ var WsClient = class {
|
|
|
45638
45955
|
}, 15e3);
|
|
45639
45956
|
timer.unref?.();
|
|
45640
45957
|
this.pending.set(envelope.id, {
|
|
45641
|
-
resolve: (value) =>
|
|
45958
|
+
resolve: (value) => resolve13(value),
|
|
45642
45959
|
timer
|
|
45643
45960
|
});
|
|
45644
45961
|
this.socket.send(JSON.stringify(envelope));
|
|
@@ -45707,7 +46024,7 @@ var WsClient = class {
|
|
|
45707
46024
|
*/
|
|
45708
46025
|
physicFolderPath(projectName) {
|
|
45709
46026
|
const safe = projectName.replace(/[/\\]/g, "_").replace(/\.\./g, "_").trim();
|
|
45710
|
-
return safe ?
|
|
46027
|
+
return safe ? join33(this.context.profileDir, safe) : null;
|
|
45711
46028
|
}
|
|
45712
46029
|
/**
|
|
45713
46030
|
* Report machine.status: version + the state of the physic project being served
|
|
@@ -45819,7 +46136,7 @@ var WsClient = class {
|
|
|
45819
46136
|
*/
|
|
45820
46137
|
uploadLog() {
|
|
45821
46138
|
try {
|
|
45822
|
-
const upload = readLogUpload(
|
|
46139
|
+
const upload = readLogUpload(join33(this.context.profileDir, "logs"));
|
|
45823
46140
|
if (!upload) return;
|
|
45824
46141
|
const sig = `${upload.fileName}:${upload.totalBytes}`;
|
|
45825
46142
|
if (sig === this.lastLogSig) return;
|
|
@@ -45982,9 +46299,9 @@ var WsClient = class {
|
|
|
45982
46299
|
|
|
45983
46300
|
// src/core/instance-lock.ts
|
|
45984
46301
|
import { readFileSync as readFileSync17, rmSync as rmSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
45985
|
-
import { join as
|
|
46302
|
+
import { join as join34 } from "path";
|
|
45986
46303
|
function lockPath(profileDir2) {
|
|
45987
|
-
return
|
|
46304
|
+
return join34(profileDir2, "start.lock");
|
|
45988
46305
|
}
|
|
45989
46306
|
function isAlive(pid) {
|
|
45990
46307
|
try {
|
|
@@ -46306,7 +46623,7 @@ var SessionBus = class {
|
|
|
46306
46623
|
// src/core/control-server.ts
|
|
46307
46624
|
import { createServer } from "net";
|
|
46308
46625
|
import { chmodSync as chmodSync5, rmSync as rmSync10 } from "fs";
|
|
46309
|
-
import { join as
|
|
46626
|
+
import { join as join36 } from "path";
|
|
46310
46627
|
|
|
46311
46628
|
// src/core/control-protocol.ts
|
|
46312
46629
|
var CONTROL_SOCKET_FILE = "control.sock";
|
|
@@ -46337,9 +46654,9 @@ function createFrameParser() {
|
|
|
46337
46654
|
// src/core/control-token.ts
|
|
46338
46655
|
import { chmodSync as chmodSync4, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "fs";
|
|
46339
46656
|
import { randomBytes as randomBytes3, timingSafeEqual } from "crypto";
|
|
46340
|
-
import { join as
|
|
46657
|
+
import { join as join35 } from "path";
|
|
46341
46658
|
function controlTokenPath(profileDir2) {
|
|
46342
|
-
return
|
|
46659
|
+
return join35(profileDir2, CONTROL_TOKEN_FILE);
|
|
46343
46660
|
}
|
|
46344
46661
|
function writeControlToken(profileDir2) {
|
|
46345
46662
|
const token = randomBytes3(32).toString("hex");
|
|
@@ -46368,7 +46685,7 @@ function tokenMatches(expected, given) {
|
|
|
46368
46685
|
|
|
46369
46686
|
// src/core/control-server.ts
|
|
46370
46687
|
function startControlServer(bus, profileDir2, info) {
|
|
46371
|
-
const socketPath =
|
|
46688
|
+
const socketPath = join36(profileDir2, CONTROL_SOCKET_FILE);
|
|
46372
46689
|
try {
|
|
46373
46690
|
rmSync10(socketPath, { force: true });
|
|
46374
46691
|
} catch {
|
|
@@ -51544,24 +51861,24 @@ var App = class extends import_react9.PureComponent {
|
|
|
51544
51861
|
})
|
|
51545
51862
|
}));
|
|
51546
51863
|
};
|
|
51547
|
-
findNextFocusable = (
|
|
51548
|
-
const activeIndex =
|
|
51549
|
-
return focusable.id ===
|
|
51864
|
+
findNextFocusable = (state3) => {
|
|
51865
|
+
const activeIndex = state3.focusables.findIndex((focusable) => {
|
|
51866
|
+
return focusable.id === state3.activeFocusId;
|
|
51550
51867
|
});
|
|
51551
|
-
for (let index = activeIndex + 1; index <
|
|
51552
|
-
const focusable =
|
|
51868
|
+
for (let index = activeIndex + 1; index < state3.focusables.length; index++) {
|
|
51869
|
+
const focusable = state3.focusables[index];
|
|
51553
51870
|
if (focusable?.isActive) {
|
|
51554
51871
|
return focusable.id;
|
|
51555
51872
|
}
|
|
51556
51873
|
}
|
|
51557
51874
|
return void 0;
|
|
51558
51875
|
};
|
|
51559
|
-
findPreviousFocusable = (
|
|
51560
|
-
const activeIndex =
|
|
51561
|
-
return focusable.id ===
|
|
51876
|
+
findPreviousFocusable = (state3) => {
|
|
51877
|
+
const activeIndex = state3.focusables.findIndex((focusable) => {
|
|
51878
|
+
return focusable.id === state3.activeFocusId;
|
|
51562
51879
|
});
|
|
51563
51880
|
for (let index = activeIndex - 1; index >= 0; index--) {
|
|
51564
|
-
const focusable =
|
|
51881
|
+
const focusable = state3.focusables[index];
|
|
51565
51882
|
if (focusable?.isActive) {
|
|
51566
51883
|
return focusable.id;
|
|
51567
51884
|
}
|
|
@@ -51757,8 +52074,8 @@ var Ink = class {
|
|
|
51757
52074
|
}
|
|
51758
52075
|
}
|
|
51759
52076
|
async waitUntilExit() {
|
|
51760
|
-
this.exitPromise ||= new Promise((
|
|
51761
|
-
this.resolveExitPromise =
|
|
52077
|
+
this.exitPromise ||= new Promise((resolve13, reject) => {
|
|
52078
|
+
this.resolveExitPromise = resolve13;
|
|
51762
52079
|
this.rejectExitPromise = reject;
|
|
51763
52080
|
});
|
|
51764
52081
|
return this.exitPromise;
|
|
@@ -52126,10 +52443,10 @@ var import_react26 = __toESM(require_react(), 1);
|
|
|
52126
52443
|
|
|
52127
52444
|
// src/core/input-history.ts
|
|
52128
52445
|
import { existsSync as existsSync20, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
|
|
52129
|
-
import { join as
|
|
52446
|
+
import { join as join37 } from "path";
|
|
52130
52447
|
var MAX_HISTORY = 200;
|
|
52131
52448
|
function historyPath(profileDir2) {
|
|
52132
|
-
return
|
|
52449
|
+
return join37(profileDir2, "input-history.json");
|
|
52133
52450
|
}
|
|
52134
52451
|
function loadInputHistory(profileDir2) {
|
|
52135
52452
|
const path = historyPath(profileDir2);
|
|
@@ -52743,7 +53060,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
52743
53060
|
return;
|
|
52744
53061
|
}
|
|
52745
53062
|
logger.configure({
|
|
52746
|
-
dir:
|
|
53063
|
+
dir: join38(profileDir2, "logs"),
|
|
52747
53064
|
level: resolveLogLevel(),
|
|
52748
53065
|
base: { version: CLI_VERSION, profile: profileName, scope: credential.scope }
|
|
52749
53066
|
});
|
|
@@ -52811,7 +53128,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
52811
53128
|
logger.error("ws.run.crash", { message: e.message, stack: e.stack });
|
|
52812
53129
|
if (client.isStopped) return;
|
|
52813
53130
|
bus.log(t("start.recovered", { message: e.message }), "error");
|
|
52814
|
-
await new Promise((
|
|
53131
|
+
await new Promise((resolve13) => setTimeout(resolve13, 1e3));
|
|
52815
53132
|
}
|
|
52816
53133
|
}
|
|
52817
53134
|
};
|
|
@@ -52853,12 +53170,12 @@ async function runStart(profileDir2, profileName) {
|
|
|
52853
53170
|
}
|
|
52854
53171
|
|
|
52855
53172
|
// src/commands/attach.ts
|
|
52856
|
-
import { join as
|
|
53173
|
+
import { join as join39 } from "path";
|
|
52857
53174
|
|
|
52858
53175
|
// src/core/control-client.ts
|
|
52859
53176
|
import { connect as connect2 } from "net";
|
|
52860
53177
|
function connectControl(socketPath, bus, token) {
|
|
52861
|
-
return new Promise((
|
|
53178
|
+
return new Promise((resolve13, reject) => {
|
|
52862
53179
|
const socket = connect2(socketPath);
|
|
52863
53180
|
socket.setEncoding("utf8");
|
|
52864
53181
|
let ready = false;
|
|
@@ -52911,7 +53228,7 @@ function connectControl(socketPath, bus, token) {
|
|
|
52911
53228
|
bus.setTokens(frame.tokens);
|
|
52912
53229
|
for (const entry of frame.transcript) bus.pushEntry(entry);
|
|
52913
53230
|
ready = true;
|
|
52914
|
-
|
|
53231
|
+
resolve13({ info: frame.info, close });
|
|
52915
53232
|
break;
|
|
52916
53233
|
case "transcript":
|
|
52917
53234
|
bus.pushEntry(frame.entry);
|
|
@@ -52962,7 +53279,7 @@ async function runAttach(profileDir2, profileName) {
|
|
|
52962
53279
|
process.exitCode = 1;
|
|
52963
53280
|
return;
|
|
52964
53281
|
}
|
|
52965
|
-
const socketPath =
|
|
53282
|
+
const socketPath = join39(profileDir2, CONTROL_SOCKET_FILE);
|
|
52966
53283
|
const bus = new SessionBus();
|
|
52967
53284
|
let conn;
|
|
52968
53285
|
try {
|
|
@@ -52993,13 +53310,13 @@ async function runAttach(profileDir2, profileName) {
|
|
|
52993
53310
|
|
|
52994
53311
|
// src/commands/auto-run.ts
|
|
52995
53312
|
import { connect as connect3 } from "net";
|
|
52996
|
-
import { join as
|
|
53313
|
+
import { join as join40 } from "path";
|
|
52997
53314
|
var AUTO_RUN_TIMEOUT_MS = 3 * 60 * 60 * 1e3;
|
|
52998
53315
|
async function runAutoRun(profileDir2, profileName) {
|
|
52999
53316
|
initI18n(readProfileConfig(profileDir2).locale);
|
|
53000
|
-
const socketPath =
|
|
53317
|
+
const socketPath = join40(profileDir2, CONTROL_SOCKET_FILE);
|
|
53001
53318
|
const token = readControlToken(profileDir2);
|
|
53002
|
-
const code = await new Promise((
|
|
53319
|
+
const code = await new Promise((resolve13) => {
|
|
53003
53320
|
let settled = false;
|
|
53004
53321
|
const done = (c) => {
|
|
53005
53322
|
if (settled) return;
|
|
@@ -53009,7 +53326,7 @@ async function runAutoRun(profileDir2, profileName) {
|
|
|
53009
53326
|
socket.destroy();
|
|
53010
53327
|
} catch {
|
|
53011
53328
|
}
|
|
53012
|
-
|
|
53329
|
+
resolve13(c);
|
|
53013
53330
|
};
|
|
53014
53331
|
const socket = connect3(socketPath);
|
|
53015
53332
|
socket.setEncoding("utf8");
|
|
@@ -53054,22 +53371,22 @@ async function runAutoRun(profileDir2, profileName) {
|
|
|
53054
53371
|
|
|
53055
53372
|
// src/commands/git-credential.ts
|
|
53056
53373
|
import { connect as connect4 } from "net";
|
|
53057
|
-
import { join as
|
|
53374
|
+
import { join as join41 } from "path";
|
|
53058
53375
|
var REQUEST_TIMEOUT_MS = 2e4;
|
|
53059
53376
|
function requestToken(host, path) {
|
|
53060
53377
|
const profileDir2 = process.env[PROFILE_DIR_ENV];
|
|
53061
53378
|
if (!profileDir2) return Promise.resolve(null);
|
|
53062
53379
|
const token = readControlToken(profileDir2);
|
|
53063
|
-
return new Promise((
|
|
53380
|
+
return new Promise((resolve13) => {
|
|
53064
53381
|
let done = false;
|
|
53065
53382
|
const finish = (value) => {
|
|
53066
53383
|
if (done) return;
|
|
53067
53384
|
done = true;
|
|
53068
53385
|
clearTimeout(timer);
|
|
53069
53386
|
socket.destroy();
|
|
53070
|
-
|
|
53387
|
+
resolve13(value);
|
|
53071
53388
|
};
|
|
53072
|
-
const socket = connect4(
|
|
53389
|
+
const socket = connect4(join41(profileDir2, CONTROL_SOCKET_FILE));
|
|
53073
53390
|
socket.setEncoding("utf8");
|
|
53074
53391
|
const timer = setTimeout(() => finish(null), REQUEST_TIMEOUT_MS);
|
|
53075
53392
|
socket.on("connect", () => {
|
|
@@ -53127,16 +53444,16 @@ import { spawn as spawn12 } from "child_process";
|
|
|
53127
53444
|
import { mkdirSync as mkdirSync10 } from "fs";
|
|
53128
53445
|
import { basename as basename8 } from "path";
|
|
53129
53446
|
function runProviderLogin(cmd, envVar, dir2) {
|
|
53130
|
-
return new Promise((
|
|
53447
|
+
return new Promise((resolve13) => {
|
|
53131
53448
|
const child = spawn12(cmd, [], {
|
|
53132
53449
|
stdio: "inherit",
|
|
53133
53450
|
env: { ...process.env, [envVar]: dir2 }
|
|
53134
53451
|
});
|
|
53135
53452
|
child.on("error", (err) => {
|
|
53136
53453
|
console.error(t("aiLogin.launchFailed", { cmd, message: err.message }));
|
|
53137
|
-
|
|
53454
|
+
resolve13(1);
|
|
53138
53455
|
});
|
|
53139
|
-
child.on("exit", (code) =>
|
|
53456
|
+
child.on("exit", (code) => resolve13(code ?? 0));
|
|
53140
53457
|
});
|
|
53141
53458
|
}
|
|
53142
53459
|
async function runAiLogin(profileDir2, profileName, aiFilter) {
|
|
@@ -53276,10 +53593,10 @@ function Picker({
|
|
|
53276
53593
|
] });
|
|
53277
53594
|
}
|
|
53278
53595
|
function selectFromList(items, title) {
|
|
53279
|
-
return new Promise((
|
|
53596
|
+
return new Promise((resolve13) => {
|
|
53280
53597
|
let chosen = null;
|
|
53281
53598
|
const app = render_default(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Picker, { items, title, onDone: (v2) => chosen = v2 }));
|
|
53282
|
-
void app.waitUntilExit().then(() =>
|
|
53599
|
+
void app.waitUntilExit().then(() => resolve13(chosen));
|
|
53283
53600
|
});
|
|
53284
53601
|
}
|
|
53285
53602
|
|