@4pm/cli 1.25.0 → 1.28.0-b
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 +928 -477
- 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;
|
|
@@ -28557,11 +28557,12 @@ function fetchWhoami(serverUrl, hashcode3) {
|
|
|
28557
28557
|
function selfUnlink(serverUrl, hashcode3) {
|
|
28558
28558
|
return post(serverUrl, "/machine-links/self-unlink", { hashcode3 });
|
|
28559
28559
|
}
|
|
28560
|
-
async function fetchCliVersion(serverUrl) {
|
|
28560
|
+
async function fetchCliVersion(serverUrl, version) {
|
|
28561
28561
|
const ctrl = new AbortController();
|
|
28562
28562
|
const timer = setTimeout(() => ctrl.abort(), 2e4);
|
|
28563
28563
|
try {
|
|
28564
|
-
const
|
|
28564
|
+
const query = version ? `?version=${encodeURIComponent(version)}` : "";
|
|
28565
|
+
const res = await fetch(`${serverUrl}/meta/cli-version${query}`, { signal: ctrl.signal });
|
|
28565
28566
|
return unwrap(res);
|
|
28566
28567
|
} finally {
|
|
28567
28568
|
clearTimeout(timer);
|
|
@@ -28798,6 +28799,8 @@ var en_default = {
|
|
|
28798
28799
|
"update.updating": "Updating {from} \u2192 {to}\u2026",
|
|
28799
28800
|
"update.checkVersionFailed": "Could not check cli version: {error}",
|
|
28800
28801
|
"update.newVersionAutoUpdateOff": "A new version {latest} is available (running {current}) \u2014 autoUpdate is off.",
|
|
28802
|
+
"update.unsupportedSoon": "cli {current} stops being supported on {date} \u2014 update before then (4pm update).",
|
|
28803
|
+
"update.policyNote": "Note from 4PM: {note}",
|
|
28801
28804
|
"update.applying": "{kind} cli update {from} \u2192 {to}\u2026",
|
|
28802
28805
|
"update.kindMandatory": "Mandatory",
|
|
28803
28806
|
"update.kindAutomatic": "Automatic",
|
|
@@ -28981,6 +28984,8 @@ var vi_default = {
|
|
|
28981
28984
|
"update.updating": "\u0110ang c\u1EADp nh\u1EADt {from} \u2192 {to}\u2026",
|
|
28982
28985
|
"update.checkVersionFailed": "Kh\xF4ng th\u1EC3 ki\u1EC3m tra phi\xEAn b\u1EA3n cli: {error}",
|
|
28983
28986
|
"update.newVersionAutoUpdateOff": "C\xF3 phi\xEAn b\u1EA3n m\u1EDBi {latest} (\u0111ang ch\u1EA1y {current}) \u2014 autoUpdate \u0111ang t\u1EAFt.",
|
|
28987
|
+
"update.unsupportedSoon": "cli {current} s\u1EBD ng\u1EEBng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3 t\u1EEB {date} \u2014 h\xE3y c\u1EADp nh\u1EADt tr\u01B0\u1EDBc ng\xE0y \u0111\xF3 (4pm update).",
|
|
28988
|
+
"update.policyNote": "Ghi ch\xFA t\u1EEB 4PM: {note}",
|
|
28984
28989
|
"update.applying": "C\u1EADp nh\u1EADt cli {kind} {from} \u2192 {to}\u2026",
|
|
28985
28990
|
"update.kindMandatory": "b\u1EAFt bu\u1ED9c",
|
|
28986
28991
|
"update.kindAutomatic": "t\u1EF1 \u0111\u1ED9ng",
|
|
@@ -29164,6 +29169,8 @@ var ja_default = {
|
|
|
29164
29169
|
"update.updating": "\u66F4\u65B0\u4E2D {from} \u2192 {to}\u2026",
|
|
29165
29170
|
"update.checkVersionFailed": "cli \u306E\u30D0\u30FC\u30B8\u30E7\u30F3\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F: {error}",
|
|
29166
29171
|
"update.newVersionAutoUpdateOff": "\u65B0\u3057\u3044\u30D0\u30FC\u30B8\u30E7\u30F3 {latest} \u304C\u5229\u7528\u53EF\u80FD\u3067\u3059\uFF08\u5B9F\u884C\u4E2D {current}\uFF09\u2014 autoUpdate \u306F\u30AA\u30D5\u3067\u3059\u3002",
|
|
29172
|
+
"update.unsupportedSoon": "cli {current} \u306F {date} \u306B\u30B5\u30DD\u30FC\u30C8\u304C\u7D42\u4E86\u3057\u307E\u3059 \u2014 \u305D\u308C\u307E\u3067\u306B\u66F4\u65B0\u3057\u3066\u304F\u3060\u3055\u3044\uFF084pm update\uFF09\u3002",
|
|
29173
|
+
"update.policyNote": "4PM \u304B\u3089\u306E\u304A\u77E5\u3089\u305B: {note}",
|
|
29167
29174
|
"update.applying": "{kind} \u306E cli \u66F4\u65B0 {from} \u2192 {to}\u2026",
|
|
29168
29175
|
"update.kindMandatory": "\u5FC5\u9808",
|
|
29169
29176
|
"update.kindAutomatic": "\u81EA\u52D5",
|
|
@@ -29347,6 +29354,8 @@ var zh_default = {
|
|
|
29347
29354
|
"update.updating": "\u6B63\u5728\u66F4\u65B0 {from} \u2192 {to}\u2026",
|
|
29348
29355
|
"update.checkVersionFailed": "\u65E0\u6CD5\u68C0\u67E5 cli \u7248\u672C\uFF1A{error}",
|
|
29349
29356
|
"update.newVersionAutoUpdateOff": "\u6709\u65B0\u7248\u672C {latest} \u53EF\u7528\uFF08\u5F53\u524D\u8FD0\u884C {current}\uFF09\u2014 autoUpdate \u5DF2\u5173\u95ED\u3002",
|
|
29357
|
+
"update.unsupportedSoon": "cli {current} \u5C06\u4E8E {date} \u505C\u6B62\u652F\u6301 \u2014 \u8BF7\u5728\u6B64\u4E4B\u524D\u66F4\u65B0\uFF084pm update\uFF09\u3002",
|
|
29358
|
+
"update.policyNote": "\u6765\u81EA 4PM \u7684\u8BF4\u660E\uFF1A{note}",
|
|
29350
29359
|
"update.applying": "{kind} cli \u66F4\u65B0 {from} \u2192 {to}\u2026",
|
|
29351
29360
|
"update.kindMandatory": "\u5F3A\u5236",
|
|
29352
29361
|
"update.kindAutomatic": "\u81EA\u52A8",
|
|
@@ -29541,7 +29550,7 @@ async function runLink(serverUrl, explicitProfile, token = null) {
|
|
|
29541
29550
|
|
|
29542
29551
|
// src/commands/start.ts
|
|
29543
29552
|
import { spawn as spawn11 } from "child_process";
|
|
29544
|
-
import { join as
|
|
29553
|
+
import { join as join38 } from "path";
|
|
29545
29554
|
|
|
29546
29555
|
// src/core/update.ts
|
|
29547
29556
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -32701,12 +32710,18 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
|
|
|
32701
32710
|
if (CLI_VERSION.startsWith("0.0.0")) return { action: "none" };
|
|
32702
32711
|
let meta;
|
|
32703
32712
|
try {
|
|
32704
|
-
meta = await fetchCliVersion(serverUrl);
|
|
32713
|
+
meta = await fetchCliVersion(serverUrl, CLI_VERSION);
|
|
32705
32714
|
} catch (err) {
|
|
32706
32715
|
console.warn(t("update.checkVersionFailed", { error: String(err) }));
|
|
32707
32716
|
return { action: "none" };
|
|
32708
32717
|
}
|
|
32709
|
-
const mandatory = compareSemver(CLI_VERSION, meta.minSupported) < 0;
|
|
32718
|
+
const mandatory = compareSemver(CLI_VERSION, meta.minSupported) < 0 || meta.status === "unsupported";
|
|
32719
|
+
if (meta.status === "warning" && meta.unsupportedFrom) {
|
|
32720
|
+
console.warn(t("update.unsupportedSoon", { current: CLI_VERSION, date: meta.unsupportedFrom.slice(0, 10) }));
|
|
32721
|
+
if (meta.note) console.warn(t("update.policyNote", { note: meta.note }));
|
|
32722
|
+
} else if (meta.status === "unsupported" && meta.note) {
|
|
32723
|
+
console.warn(t("update.policyNote", { note: meta.note }));
|
|
32724
|
+
}
|
|
32710
32725
|
const outdated = shouldUpdateTo(CLI_VERSION, meta.latest);
|
|
32711
32726
|
if (!outdated) return { action: "none" };
|
|
32712
32727
|
if (!mandatory && !autoUpdate) {
|
|
@@ -32754,7 +32769,7 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
|
|
|
32754
32769
|
// src/core/ws-client.ts
|
|
32755
32770
|
init_wrapper();
|
|
32756
32771
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
32757
|
-
import { basename as basename7, join as
|
|
32772
|
+
import { basename as basename7, join as join33 } from "path";
|
|
32758
32773
|
import { existsSync as existsSync18, mkdirSync as mkdirSync9 } from "fs";
|
|
32759
32774
|
|
|
32760
32775
|
// vendor/@4pm/utils/helpers.ts
|
|
@@ -37049,6 +37064,17 @@ function githubHostKind(host) {
|
|
|
37049
37064
|
if (h.endsWith(".ghe.com")) return "ghe-cloud";
|
|
37050
37065
|
return "ghes";
|
|
37051
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
|
+
};
|
|
37052
37078
|
|
|
37053
37079
|
// vendor/@4pm/validation/schemas.ts
|
|
37054
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");
|
|
@@ -37255,6 +37281,67 @@ var agentToolsWriteRequestSchema = external_exports.object({
|
|
|
37255
37281
|
permissions: agentToolsPermissionsSchema
|
|
37256
37282
|
});
|
|
37257
37283
|
|
|
37284
|
+
// vendor/@4pm/dto/meta.ts
|
|
37285
|
+
var cliVersionString = external_exports.string().trim().regex(/^\d+\.\d+\.\d+([-+][0-9A-Za-z.-]+)?$/);
|
|
37286
|
+
var setCliMinVersionRequestSchema = external_exports.object({
|
|
37287
|
+
minSupported: cliVersionString.nullable(),
|
|
37288
|
+
disconnect: external_exports.boolean().optional()
|
|
37289
|
+
});
|
|
37290
|
+
var setCliVersionOverrideRequestSchema = external_exports.object({
|
|
37291
|
+
status: external_exports.enum(["supported", "warning", "unsupported"]),
|
|
37292
|
+
unsupportedFrom: external_exports.string().datetime({ offset: true }).nullable().optional(),
|
|
37293
|
+
note: external_exports.string().max(500).optional(),
|
|
37294
|
+
disconnect: external_exports.boolean().optional()
|
|
37295
|
+
});
|
|
37296
|
+
|
|
37297
|
+
// vendor/@4pm/dto/storage.ts
|
|
37298
|
+
var STORAGE_KINDS = [
|
|
37299
|
+
"messageText",
|
|
37300
|
+
"postText",
|
|
37301
|
+
"commentText",
|
|
37302
|
+
"memoText",
|
|
37303
|
+
"messageImages",
|
|
37304
|
+
"messageFiles",
|
|
37305
|
+
"postImages",
|
|
37306
|
+
"postFiles",
|
|
37307
|
+
"commentImages",
|
|
37308
|
+
"commentFiles",
|
|
37309
|
+
"memoImages",
|
|
37310
|
+
"memoFiles",
|
|
37311
|
+
"templates",
|
|
37312
|
+
"aiDocImages",
|
|
37313
|
+
"artifacts",
|
|
37314
|
+
"commandHistory"
|
|
37315
|
+
];
|
|
37316
|
+
var COMMUNITY_STORAGE_KINDS = [
|
|
37317
|
+
"messageText",
|
|
37318
|
+
"postText",
|
|
37319
|
+
"commentText",
|
|
37320
|
+
"messageImages",
|
|
37321
|
+
"messageFiles",
|
|
37322
|
+
"postImages",
|
|
37323
|
+
"postFiles",
|
|
37324
|
+
"commentImages",
|
|
37325
|
+
"commentFiles"
|
|
37326
|
+
];
|
|
37327
|
+
var SERVER_CLEARABLE_STORAGE_KINDS = [
|
|
37328
|
+
"memoText",
|
|
37329
|
+
"memoImages",
|
|
37330
|
+
"memoFiles",
|
|
37331
|
+
"templates",
|
|
37332
|
+
"commandHistory"
|
|
37333
|
+
];
|
|
37334
|
+
var CLEARABLE_STORAGE_KINDS = [
|
|
37335
|
+
...COMMUNITY_STORAGE_KINDS,
|
|
37336
|
+
...SERVER_CLEARABLE_STORAGE_KINDS
|
|
37337
|
+
];
|
|
37338
|
+
var storageClearRequestSchema = external_exports.object({
|
|
37339
|
+
kinds: external_exports.array(external_exports.enum(STORAGE_KINDS)).min(1).max(STORAGE_KINDS.length),
|
|
37340
|
+
before: external_exports.string().datetime({ offset: true }),
|
|
37341
|
+
/** org-0009 / community-0015 only — must equal the org name. */
|
|
37342
|
+
confirm: external_exports.string().max(200).optional()
|
|
37343
|
+
});
|
|
37344
|
+
|
|
37258
37345
|
// vendor/@4pm/dto/org.ts
|
|
37259
37346
|
var CLI_SETTINGS_BOUNDS = {
|
|
37260
37347
|
reconnectMaxBackoffSec: { min: 5, max: 60 },
|
|
@@ -37274,6 +37361,8 @@ var TEMPLATE_KINDS = ["report", "est", "wbs", "proposal", "feasibility"];
|
|
|
37274
37361
|
// vendor/@4pm/dto/project.ts
|
|
37275
37362
|
var listProjectsQuerySchema = baseRequestSchema.extend({
|
|
37276
37363
|
pinned: pinnedFilterSchema,
|
|
37364
|
+
/** Only projects this team is attached to (project-0001; the public v1 list — ADR-0364). */
|
|
37365
|
+
teamId: external_exports.string().uuid().optional(),
|
|
37277
37366
|
/** ADMIN-only Trash: list soft-deleted projects (ADR-0109). */
|
|
37278
37367
|
deleted: deletedFilterSchema
|
|
37279
37368
|
});
|
|
@@ -37562,7 +37651,7 @@ var userCardsQuerySchema = external_exports.object({
|
|
|
37562
37651
|
});
|
|
37563
37652
|
|
|
37564
37653
|
// src/core/scaffold.ts
|
|
37565
|
-
import { cp, mkdir, mkdtemp, rm, writeFile } from "fs/promises";
|
|
37654
|
+
import { cp, mkdir, mkdtemp, readFile, rm, writeFile } from "fs/promises";
|
|
37566
37655
|
import { existsSync as existsSync4 } from "fs";
|
|
37567
37656
|
import { execFile } from "child_process";
|
|
37568
37657
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -37671,45 +37760,98 @@ function providerSlug(url) {
|
|
|
37671
37760
|
const m2 = /(?:github|gitlab)\.com[/:]+([^/]+\/.+?)(?:\.git)?\/?$/i.exec(url);
|
|
37672
37761
|
return { provider, slug: m2?.[1] ?? "" };
|
|
37673
37762
|
}
|
|
37674
|
-
|
|
37675
|
-
|
|
37676
|
-
|
|
37677
|
-
|
|
37678
|
-
|
|
37679
|
-
|
|
37680
|
-
return;
|
|
37681
|
-
}
|
|
37682
|
-
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) {
|
|
37683
37769
|
try {
|
|
37684
|
-
await run("git",
|
|
37770
|
+
return (await run("git", args, { cwd: cwd2, timeout: 3e4 })).stdout.trim();
|
|
37685
37771
|
} catch {
|
|
37686
|
-
|
|
37772
|
+
return "";
|
|
37687
37773
|
}
|
|
37688
|
-
|
|
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;
|
|
37689
37806
|
emit("push", "Pushing the scaffold branch\u2026");
|
|
37690
37807
|
try {
|
|
37691
|
-
await run("git", ["push", "-u", "origin",
|
|
37808
|
+
await run("git", ["push", "-u", "origin", branch || "HEAD"], { cwd: root, timeout: 12e4 });
|
|
37809
|
+
result.pushed = true;
|
|
37692
37810
|
} catch (err) {
|
|
37693
|
-
const msg =
|
|
37694
|
-
emit("push-failed", `Scaffold committed locally but the push failed: ${msg} \u2014
|
|
37695
|
-
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);
|
|
37696
37814
|
}
|
|
37697
|
-
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);
|
|
37698
37822
|
if (!provider) {
|
|
37699
37823
|
emit("pr", "Unknown git host \u2014 skipping the pull request.");
|
|
37700
|
-
return;
|
|
37824
|
+
return result;
|
|
37701
37825
|
}
|
|
37702
37826
|
emit("pr", `Opening a pull request via ${provider}\u2026`);
|
|
37703
37827
|
try {
|
|
37704
|
-
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] : []];
|
|
37705
37829
|
const { stdout } = await run(provider, args, { cwd: root, timeout: 12e4 });
|
|
37706
|
-
const link2 = stdout.trim().split(/\s+/).find((
|
|
37830
|
+
const link2 = stdout.trim().split(/\s+/).find((x) => /^https?:\/\//.test(x)) ?? null;
|
|
37831
|
+
result.prUrl = link2;
|
|
37707
37832
|
emit("pr", `Pull request opened${link2 ? `: ${link2}` : "."}`);
|
|
37833
|
+
return result;
|
|
37708
37834
|
} catch (err) {
|
|
37709
|
-
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
|
+
}
|
|
37710
37842
|
emit("pr-failed", `Could not open a pull request (${provider}): ${msg}`);
|
|
37843
|
+
return fail("pr", msg);
|
|
37711
37844
|
}
|
|
37712
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
|
+
}
|
|
37713
37855
|
function cloneArgs(url, dest, branch) {
|
|
37714
37856
|
const b2 = (branch ?? "").trim();
|
|
37715
37857
|
return b2 ? ["clone", "-b", b2, url, dest] : ["clone", url, dest];
|
|
@@ -37802,7 +37944,7 @@ function sampleDir() {
|
|
|
37802
37944
|
}
|
|
37803
37945
|
return bundled;
|
|
37804
37946
|
}
|
|
37805
|
-
async function scaffoldProject(payload, profileDir2, onProgress, ai2) {
|
|
37947
|
+
async function scaffoldProject(payload, profileDir2, onProgress, ai2, applyGitAuth2) {
|
|
37806
37948
|
let lastStep = "start";
|
|
37807
37949
|
const emit = (step, message) => {
|
|
37808
37950
|
lastStep = step;
|
|
@@ -37814,15 +37956,16 @@ async function scaffoldProject(payload, profileDir2, onProgress, ai2) {
|
|
|
37814
37956
|
await mkdir(target, { recursive: true });
|
|
37815
37957
|
const repo = singleRepo(reposOf(payload.spec));
|
|
37816
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);
|
|
37817
37960
|
emit("git", "Cloning repository\u2026");
|
|
37818
37961
|
await provisionRepo(target, repo, emit, { createMissingBranch: true });
|
|
37819
37962
|
await scaffoldRepo(target, payload.projectName, payload.spec, emit, { resetClaude: true, ai: ai2 });
|
|
37820
37963
|
await attachSubmodules(target, submodulesOf(reposOf(payload.spec)), emit, true);
|
|
37821
37964
|
emit("version", "Writing .4pm/.4pm.json\u2026");
|
|
37822
37965
|
await writeTemplateMarker(target);
|
|
37823
|
-
await commitAndOpenPr(target, repo, emit);
|
|
37966
|
+
const publish = await commitAndOpenPr(target, repo, emit);
|
|
37824
37967
|
onProgress?.({ projectId: payload.projectId, step: "done", message: "Scaffold complete.", done: true });
|
|
37825
|
-
return { ok: true, path: target };
|
|
37968
|
+
return { ok: true, path: target, publish };
|
|
37826
37969
|
} catch (err) {
|
|
37827
37970
|
return { ok: false, error: err instanceof Error ? err.message : String(err), step: lastStep };
|
|
37828
37971
|
}
|
|
@@ -37956,7 +38099,7 @@ async function addProject(payload, profileDir2, onProgress, ai2) {
|
|
|
37956
38099
|
}
|
|
37957
38100
|
|
|
37958
38101
|
// src/core/worker-metrics.ts
|
|
37959
|
-
import { statfs, readFile } from "fs/promises";
|
|
38102
|
+
import { statfs, readFile as readFile2 } from "fs/promises";
|
|
37960
38103
|
import { existsSync as existsSync5 } from "fs";
|
|
37961
38104
|
import { totalmem, freemem, cpus, platform, arch } from "os";
|
|
37962
38105
|
var CGROUP = "/sys/fs/cgroup";
|
|
@@ -37964,7 +38107,7 @@ var MB = 1024 * 1024;
|
|
|
37964
38107
|
var GB = 1024 * 1024 * 1024;
|
|
37965
38108
|
async function readCgroup(file) {
|
|
37966
38109
|
try {
|
|
37967
|
-
return (await
|
|
38110
|
+
return (await readFile2(`${CGROUP}/${file}`, "utf8")).trim();
|
|
37968
38111
|
} catch {
|
|
37969
38112
|
return null;
|
|
37970
38113
|
}
|
|
@@ -38326,9 +38469,15 @@ var WsChannels = {
|
|
|
38326
38469
|
// cli → server (reply): a short-lived GitHub-App installation token for the served project's repo,
|
|
38327
38470
|
// per job scope; also the cli's "scope revoked" report (ADR-0356). The App key stays on the server.
|
|
38328
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",
|
|
38329
38475
|
PROJECT_CREATE: "project.create",
|
|
38330
38476
|
PROJECT_ADD: "project.add",
|
|
38331
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",
|
|
38332
38481
|
// server → cli: keep the physic folder name in sync with the project (folder =
|
|
38333
38482
|
// project name — ADR-0064) when the project is renamed.
|
|
38334
38483
|
PHYSIC_SYNC: "physic.sync",
|
|
@@ -38370,6 +38519,9 @@ var WsChannels = {
|
|
|
38370
38519
|
QUOTA_CHECK: "quota.check",
|
|
38371
38520
|
// cli → server (reply): per-org concurrent AI-run slot — acquire / renew / release (ADR-0359)
|
|
38372
38521
|
RUN_SLOT: "run.slot",
|
|
38522
|
+
// cli → server (reply): read / add the org's monthly autonomous-book counters — USER_TODO / USER_QA /
|
|
38523
|
+
// AI_TODO rows vs `autonomousRequestsPerMonth` (ADR-0365)
|
|
38524
|
+
AUTONOMOUS_BOOK_USAGE: "autonomous.bookUsage",
|
|
38373
38525
|
USAGE_REPORT: "usage.report",
|
|
38374
38526
|
// cli → server: Claude subscription usage snapshot (5h / weekly — ADR-0072)
|
|
38375
38527
|
MACHINE_USAGE: "machine.usage",
|
|
@@ -38590,8 +38742,8 @@ function resolveInstallTimeoutMs(sec) {
|
|
|
38590
38742
|
return sec && sec > 0 ? sec * 1e3 : TOOL_INSTALL_TIMEOUT_DEFAULT_MS;
|
|
38591
38743
|
}
|
|
38592
38744
|
function sleep(ms2) {
|
|
38593
|
-
return new Promise((
|
|
38594
|
-
const t2 = setTimeout(
|
|
38745
|
+
return new Promise((resolve13) => {
|
|
38746
|
+
const t2 = setTimeout(resolve13, ms2);
|
|
38595
38747
|
t2.unref?.();
|
|
38596
38748
|
});
|
|
38597
38749
|
}
|
|
@@ -38619,14 +38771,14 @@ function classifyToolFailure(code, out) {
|
|
|
38619
38771
|
return { reason: "other", retryable: false };
|
|
38620
38772
|
}
|
|
38621
38773
|
function run2(cmd, args, onLine, timeoutMs = 18e4) {
|
|
38622
|
-
return new Promise((
|
|
38774
|
+
return new Promise((resolve13) => {
|
|
38623
38775
|
let out = "";
|
|
38624
38776
|
let buf = "";
|
|
38625
38777
|
let child = null;
|
|
38626
38778
|
const done = (code) => {
|
|
38627
38779
|
clearTimeout(timer);
|
|
38628
38780
|
if (onLine && buf.trim()) onLine(buf.trim());
|
|
38629
|
-
|
|
38781
|
+
resolve13({ code, out });
|
|
38630
38782
|
};
|
|
38631
38783
|
const timer = setTimeout(() => {
|
|
38632
38784
|
child?.kill();
|
|
@@ -39009,11 +39161,11 @@ function getWorkingProfile(profileDir2, cmd) {
|
|
|
39009
39161
|
return readState(profileDir2)[cmd] ?? null;
|
|
39010
39162
|
}
|
|
39011
39163
|
function setWorkingProfile(profileDir2, cmd, dir2) {
|
|
39012
|
-
const
|
|
39013
|
-
if (
|
|
39014
|
-
|
|
39164
|
+
const state3 = readState(profileDir2);
|
|
39165
|
+
if (state3[cmd] === dir2) return;
|
|
39166
|
+
state3[cmd] = dir2;
|
|
39015
39167
|
try {
|
|
39016
|
-
writeFileSync4(statePath(profileDir2), JSON.stringify(
|
|
39168
|
+
writeFileSync4(statePath(profileDir2), JSON.stringify(state3, null, 2), "utf8");
|
|
39017
39169
|
} catch {
|
|
39018
39170
|
}
|
|
39019
39171
|
}
|
|
@@ -39022,11 +39174,11 @@ function getWorkingCredential(profileDir2) {
|
|
|
39022
39174
|
return readState(profileDir2)[WORKING_CREDENTIAL_KEY] ?? null;
|
|
39023
39175
|
}
|
|
39024
39176
|
function setWorkingCredential(profileDir2, key) {
|
|
39025
|
-
const
|
|
39026
|
-
if (
|
|
39027
|
-
|
|
39177
|
+
const state3 = readState(profileDir2);
|
|
39178
|
+
if (state3[WORKING_CREDENTIAL_KEY] === key) return;
|
|
39179
|
+
state3[WORKING_CREDENTIAL_KEY] = key;
|
|
39028
39180
|
try {
|
|
39029
|
-
writeFileSync4(statePath(profileDir2), JSON.stringify(
|
|
39181
|
+
writeFileSync4(statePath(profileDir2), JSON.stringify(state3, null, 2), "utf8");
|
|
39030
39182
|
} catch {
|
|
39031
39183
|
}
|
|
39032
39184
|
}
|
|
@@ -39035,20 +39187,20 @@ function getPinnedCredential(profileDir2) {
|
|
|
39035
39187
|
return readState(profileDir2)[PINNED_CREDENTIAL_KEY] ?? null;
|
|
39036
39188
|
}
|
|
39037
39189
|
function setPinnedCredential(profileDir2, key) {
|
|
39038
|
-
const
|
|
39039
|
-
if (
|
|
39040
|
-
|
|
39190
|
+
const state3 = readState(profileDir2);
|
|
39191
|
+
if (state3[PINNED_CREDENTIAL_KEY] === key) return;
|
|
39192
|
+
state3[PINNED_CREDENTIAL_KEY] = key;
|
|
39041
39193
|
try {
|
|
39042
|
-
writeFileSync4(statePath(profileDir2), JSON.stringify(
|
|
39194
|
+
writeFileSync4(statePath(profileDir2), JSON.stringify(state3, null, 2), "utf8");
|
|
39043
39195
|
} catch {
|
|
39044
39196
|
}
|
|
39045
39197
|
}
|
|
39046
39198
|
function clearPinnedCredential(profileDir2) {
|
|
39047
|
-
const
|
|
39048
|
-
if (!(PINNED_CREDENTIAL_KEY in
|
|
39049
|
-
delete
|
|
39199
|
+
const state3 = readState(profileDir2);
|
|
39200
|
+
if (!(PINNED_CREDENTIAL_KEY in state3)) return;
|
|
39201
|
+
delete state3[PINNED_CREDENTIAL_KEY];
|
|
39050
39202
|
try {
|
|
39051
|
-
writeFileSync4(statePath(profileDir2), JSON.stringify(
|
|
39203
|
+
writeFileSync4(statePath(profileDir2), JSON.stringify(state3, null, 2), "utf8");
|
|
39052
39204
|
} catch {
|
|
39053
39205
|
}
|
|
39054
39206
|
}
|
|
@@ -39433,16 +39585,16 @@ async function fetchUsage(token) {
|
|
|
39433
39585
|
}
|
|
39434
39586
|
}
|
|
39435
39587
|
function runUsageCli(dir2, aiCli2) {
|
|
39436
|
-
return new Promise((
|
|
39588
|
+
return new Promise((resolve13) => {
|
|
39437
39589
|
let out = "";
|
|
39438
39590
|
let child = null;
|
|
39439
39591
|
const finish = () => {
|
|
39440
39592
|
clearTimeout(timer);
|
|
39441
|
-
|
|
39593
|
+
resolve13(out);
|
|
39442
39594
|
};
|
|
39443
39595
|
const timer = setTimeout(() => {
|
|
39444
39596
|
child?.kill();
|
|
39445
|
-
|
|
39597
|
+
resolve13(out);
|
|
39446
39598
|
}, 3e4);
|
|
39447
39599
|
timer.unref?.();
|
|
39448
39600
|
try {
|
|
@@ -40138,7 +40290,7 @@ function parseModeratedAnswer(text) {
|
|
|
40138
40290
|
return { body: text.trim() };
|
|
40139
40291
|
}
|
|
40140
40292
|
function runClaudeOnce(cmd, profile, prompt, extraEnv, imageDir, denyDirs = [], timeoutMs = ANSWER_TIMEOUT_MS) {
|
|
40141
|
-
return new Promise((
|
|
40293
|
+
return new Promise((resolve13, reject) => {
|
|
40142
40294
|
const isClaude = cmd.includes("claude");
|
|
40143
40295
|
const args = [
|
|
40144
40296
|
"-p",
|
|
@@ -40182,7 +40334,7 @@ function runClaudeOnce(cmd, profile, prompt, extraEnv, imageDir, denyDirs = [],
|
|
|
40182
40334
|
clearTimeout(timer);
|
|
40183
40335
|
cleanup();
|
|
40184
40336
|
out += parser.flush();
|
|
40185
|
-
|
|
40337
|
+
resolve13({ code: code ?? -1, out, err, usage: parser.usage() });
|
|
40186
40338
|
});
|
|
40187
40339
|
child.stdin.write(prompt);
|
|
40188
40340
|
child.stdin.end();
|
|
@@ -40273,14 +40425,14 @@ function reportToolResult(tool, ok, message) {
|
|
|
40273
40425
|
}
|
|
40274
40426
|
|
|
40275
40427
|
// src/core/autonomous.ts
|
|
40276
|
-
import { readFile as
|
|
40428
|
+
import { readFile as readFile5, writeFile as writeFile4, chmod } from "fs/promises";
|
|
40277
40429
|
import { readFileSync as readFileSync14 } from "fs";
|
|
40278
40430
|
import { execFile as execFile3, spawn as spawn5 } from "child_process";
|
|
40279
40431
|
import { join as join17 } from "path";
|
|
40280
40432
|
import { promisify as promisify3 } from "util";
|
|
40281
40433
|
|
|
40282
40434
|
// src/core/autonomous-config.ts
|
|
40283
|
-
import { readFile as
|
|
40435
|
+
import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
40284
40436
|
import { readFileSync as readFileSync13 } from "fs";
|
|
40285
40437
|
import { join as join15 } from "path";
|
|
40286
40438
|
var DEFAULT_AUTONOMOUS_CONFIG = {
|
|
@@ -40316,7 +40468,7 @@ function coerce2(raw) {
|
|
|
40316
40468
|
}
|
|
40317
40469
|
async function readAutonomousConfig(profileDir2) {
|
|
40318
40470
|
try {
|
|
40319
|
-
return coerce2(JSON.parse(await
|
|
40471
|
+
return coerce2(JSON.parse(await readFile3(autonomousConfigPath(profileDir2), "utf8")));
|
|
40320
40472
|
} catch {
|
|
40321
40473
|
return { ...DEFAULT_AUTONOMOUS_CONFIG };
|
|
40322
40474
|
}
|
|
@@ -40330,7 +40482,7 @@ function readAutonomousConfigSync(profileDir2) {
|
|
|
40330
40482
|
}
|
|
40331
40483
|
async function readAutonomousConfigText(profileDir2) {
|
|
40332
40484
|
try {
|
|
40333
|
-
return await
|
|
40485
|
+
return await readFile3(autonomousConfigPath(profileDir2), "utf8");
|
|
40334
40486
|
} catch {
|
|
40335
40487
|
return JSON.stringify(DEFAULT_AUTONOMOUS_CONFIG, null, 2) + "\n";
|
|
40336
40488
|
}
|
|
@@ -40350,7 +40502,7 @@ function isInQuietHours(quietHours, now = /* @__PURE__ */ new Date()) {
|
|
|
40350
40502
|
}
|
|
40351
40503
|
|
|
40352
40504
|
// src/core/autonomous-history.ts
|
|
40353
|
-
import { readFile as
|
|
40505
|
+
import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
40354
40506
|
import { join as join16 } from "path";
|
|
40355
40507
|
var HISTORIES_REL = ".claude/.autonomous.histories.json";
|
|
40356
40508
|
var MAX_RECORDS = 50;
|
|
@@ -40360,7 +40512,7 @@ function historiesPath(root) {
|
|
|
40360
40512
|
}
|
|
40361
40513
|
async function readHistories(root) {
|
|
40362
40514
|
try {
|
|
40363
|
-
const d = JSON.parse(await
|
|
40515
|
+
const d = JSON.parse(await readFile4(historiesPath(root), "utf8"));
|
|
40364
40516
|
const ticks = d.ticks ?? {};
|
|
40365
40517
|
return {
|
|
40366
40518
|
cronApplied: typeof d.cronApplied === "string" ? d.cronApplied : typeof d.cron_applied === "string" ? d.cron_applied : "",
|
|
@@ -40402,7 +40554,7 @@ var BOOK_FILES = {
|
|
|
40402
40554
|
};
|
|
40403
40555
|
async function readText(path, fallback = "") {
|
|
40404
40556
|
try {
|
|
40405
|
-
return await
|
|
40557
|
+
return await readFile5(path, "utf8");
|
|
40406
40558
|
} catch {
|
|
40407
40559
|
return fallback;
|
|
40408
40560
|
}
|
|
@@ -40447,6 +40599,14 @@ function tableRowsById(md) {
|
|
|
40447
40599
|
}
|
|
40448
40600
|
return rows;
|
|
40449
40601
|
}
|
|
40602
|
+
var CAPPED_BOOK_FILES = { USER_TODO: "USER_TODO.md", USER_QA: "USER_QA.md", AI_TODO: "AI_TODO.md" };
|
|
40603
|
+
async function cappedBookIds(root) {
|
|
40604
|
+
const out = {};
|
|
40605
|
+
for (const [book, file] of Object.entries(CAPPED_BOOK_FILES)) {
|
|
40606
|
+
out[book] = new Set(tableRowsById(await readText(join17(root, file))).keys());
|
|
40607
|
+
}
|
|
40608
|
+
return out;
|
|
40609
|
+
}
|
|
40450
40610
|
function tickScript(root) {
|
|
40451
40611
|
return join17(root, TICK_REL);
|
|
40452
40612
|
}
|
|
@@ -40459,10 +40619,10 @@ async function crontabList() {
|
|
|
40459
40619
|
}
|
|
40460
40620
|
}
|
|
40461
40621
|
async function crontabSet(content) {
|
|
40462
|
-
return new Promise((
|
|
40622
|
+
return new Promise((resolve13) => {
|
|
40463
40623
|
const child = spawn5("crontab", ["-"], { stdio: ["pipe", "ignore", "ignore"] });
|
|
40464
|
-
child.on("error", () =>
|
|
40465
|
-
child.on("close", (code) =>
|
|
40624
|
+
child.on("error", () => resolve13(false));
|
|
40625
|
+
child.on("close", (code) => resolve13(code === 0));
|
|
40466
40626
|
child.stdin.end(content.endsWith("\n") ? content : content + "\n");
|
|
40467
40627
|
});
|
|
40468
40628
|
}
|
|
@@ -40538,6 +40698,7 @@ async function uninstallCron(root) {
|
|
|
40538
40698
|
await crontabSet(kept.join("\n"));
|
|
40539
40699
|
}
|
|
40540
40700
|
async function writeAutonomous(root, profileDir2, req, by) {
|
|
40701
|
+
let added;
|
|
40541
40702
|
try {
|
|
40542
40703
|
switch (req.kind) {
|
|
40543
40704
|
case "settings": {
|
|
@@ -40582,6 +40743,7 @@ async function writeAutonomous(root, profileDir2, req, by) {
|
|
|
40582
40743
|
const row = `| ${id} | | | ${tableCell(req.content)} |
|
|
40583
40744
|
`;
|
|
40584
40745
|
await writeFile4(join17(root, BOOK_FILES.userTodo), cur.replace(/\s*$/, "\n") + row, "utf8");
|
|
40746
|
+
added = 1;
|
|
40585
40747
|
const authors = await readJsonMap(join17(root, AUTHORS_REL));
|
|
40586
40748
|
authors[id] = { by, byLabel: req.byLabel ?? by, at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
40587
40749
|
await writeFile4(join17(root, AUTHORS_REL), JSON.stringify(authors, null, 2) + "\n", "utf8");
|
|
@@ -40594,8 +40756,10 @@ async function writeAutonomous(root, profileDir2, req, by) {
|
|
|
40594
40756
|
const authors = await readJsonMap(join17(root, AUTHORS_REL));
|
|
40595
40757
|
const at2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
40596
40758
|
const byLabel = req.byLabel ?? by;
|
|
40759
|
+
added = 0;
|
|
40597
40760
|
for (const [id, cells] of next) {
|
|
40598
40761
|
if (prev.get(id) !== cells) authors[id] = { by, byLabel, at: at2 };
|
|
40762
|
+
if (!prev.has(id)) added += 1;
|
|
40599
40763
|
}
|
|
40600
40764
|
await writeFile4(join17(root, file), req.content, "utf8");
|
|
40601
40765
|
await writeFile4(join17(root, AUTHORS_REL), JSON.stringify(authors, null, 2) + "\n", "utf8");
|
|
@@ -40607,7 +40771,7 @@ async function writeAutonomous(root, profileDir2, req, by) {
|
|
|
40607
40771
|
break;
|
|
40608
40772
|
}
|
|
40609
40773
|
}
|
|
40610
|
-
return { ok: true, status: await getAutonomousStatus(root, profileDir2) };
|
|
40774
|
+
return { ok: true, status: await getAutonomousStatus(root, profileDir2), ...added !== void 0 ? { added } : {} };
|
|
40611
40775
|
} catch (err) {
|
|
40612
40776
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
40613
40777
|
}
|
|
@@ -40631,14 +40795,14 @@ function detectContainerized() {
|
|
|
40631
40795
|
}
|
|
40632
40796
|
}
|
|
40633
40797
|
function canReach(host, port) {
|
|
40634
|
-
return new Promise((
|
|
40798
|
+
return new Promise((resolve13) => {
|
|
40635
40799
|
const socket = connect({ host, port });
|
|
40636
40800
|
let done = false;
|
|
40637
40801
|
const finish = (ok) => {
|
|
40638
40802
|
if (done) return;
|
|
40639
40803
|
done = true;
|
|
40640
40804
|
socket.destroy();
|
|
40641
|
-
|
|
40805
|
+
resolve13(ok);
|
|
40642
40806
|
};
|
|
40643
40807
|
socket.setTimeout(PROBE_TIMEOUT_MS);
|
|
40644
40808
|
socket.once("connect", () => finish(true));
|
|
@@ -40823,7 +40987,7 @@ async function issueGitToken(scope, host, path) {
|
|
|
40823
40987
|
}
|
|
40824
40988
|
}
|
|
40825
40989
|
function revokeAtGithub(t2) {
|
|
40826
|
-
return new Promise((
|
|
40990
|
+
return new Promise((resolve13) => {
|
|
40827
40991
|
try {
|
|
40828
40992
|
const req = httpsRequest(
|
|
40829
40993
|
new URL(`${t2.apiBase.replace(/\/+$/, "")}/installation/token`),
|
|
@@ -40834,14 +40998,14 @@ function revokeAtGithub(t2) {
|
|
|
40834
40998
|
},
|
|
40835
40999
|
(res) => {
|
|
40836
41000
|
res.resume();
|
|
40837
|
-
res.on("end", () =>
|
|
41001
|
+
res.on("end", () => resolve13());
|
|
40838
41002
|
}
|
|
40839
41003
|
);
|
|
40840
41004
|
req.on("timeout", () => req.destroy());
|
|
40841
|
-
req.on("error", () =>
|
|
41005
|
+
req.on("error", () => resolve13());
|
|
40842
41006
|
req.end();
|
|
40843
41007
|
} catch {
|
|
40844
|
-
|
|
41008
|
+
resolve13();
|
|
40845
41009
|
}
|
|
40846
41010
|
});
|
|
40847
41011
|
}
|
|
@@ -40863,26 +41027,113 @@ async function revokeAll() {
|
|
|
40863
41027
|
await Promise.all([...state.scopes.keys()].map((s3) => endGitScope(s3)));
|
|
40864
41028
|
}
|
|
40865
41029
|
|
|
40866
|
-
// src/core/
|
|
40867
|
-
|
|
40868
|
-
|
|
40869
|
-
|
|
40870
|
-
|
|
40871
|
-
|
|
40872
|
-
|
|
40873
|
-
|
|
40874
|
-
|
|
40875
|
-
|
|
40876
|
-
|
|
40877
|
-
|
|
40878
|
-
|
|
40879
|
-
|
|
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;
|
|
40880
41048
|
}
|
|
40881
|
-
function
|
|
40882
|
-
|
|
40883
|
-
|
|
40884
|
-
|
|
40885
|
-
|
|
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 };
|
|
40886
41137
|
}
|
|
40887
41138
|
|
|
40888
41139
|
// src/core/ws-client/command-dispatch.ts
|
|
@@ -40971,7 +41222,7 @@ async function runCommand(dispatch, emitRaw, opts) {
|
|
|
40971
41222
|
});
|
|
40972
41223
|
child.stdout?.on("data", (data) => batcher.push(data.toString("utf8")));
|
|
40973
41224
|
child.stderr?.on("data", (data) => batcher.push(data.toString("utf8")));
|
|
40974
|
-
await new Promise((
|
|
41225
|
+
await new Promise((resolve13) => {
|
|
40975
41226
|
child.on("close", (exitCode) => {
|
|
40976
41227
|
clearTimers();
|
|
40977
41228
|
batcher.flush();
|
|
@@ -40985,7 +41236,7 @@ async function runCommand(dispatch, emitRaw, opts) {
|
|
|
40985
41236
|
// note instead of spinning.
|
|
40986
41237
|
exitCode: cancelled ? COMMAND_CANCELLED_EXIT_CODE : timedOut ? 124 : exitCode ?? -1
|
|
40987
41238
|
});
|
|
40988
|
-
|
|
41239
|
+
resolve13();
|
|
40989
41240
|
});
|
|
40990
41241
|
child.on("error", (err) => {
|
|
40991
41242
|
clearTimers();
|
|
@@ -40997,7 +41248,7 @@ async function runCommand(dispatch, emitRaw, opts) {
|
|
|
40997
41248
|
done: true,
|
|
40998
41249
|
exitCode: -1
|
|
40999
41250
|
});
|
|
41000
|
-
|
|
41251
|
+
resolve13();
|
|
41001
41252
|
});
|
|
41002
41253
|
});
|
|
41003
41254
|
}
|
|
@@ -41020,15 +41271,15 @@ function isOutOfCredits(text) {
|
|
|
41020
41271
|
);
|
|
41021
41272
|
}
|
|
41022
41273
|
function runAttempt(commandId, cmd, args, stdin, cwd2, env3, onChunk, timeoutMs, signal) {
|
|
41023
|
-
return new Promise((
|
|
41274
|
+
return new Promise((resolve13) => {
|
|
41024
41275
|
runCommand(
|
|
41025
41276
|
{ commandId, cmd, args, path: cwd2, env: env3 },
|
|
41026
41277
|
(out) => {
|
|
41027
41278
|
if (out.chunk) onChunk(out.chunk);
|
|
41028
|
-
if (out.done)
|
|
41279
|
+
if (out.done) resolve13(out.exitCode ?? -1);
|
|
41029
41280
|
},
|
|
41030
41281
|
{ stdin, ...timeoutMs > 0 ? { timeoutMs } : {}, ...signal ? { signal } : {} }
|
|
41031
|
-
).catch(() =>
|
|
41282
|
+
).catch(() => resolve13(-1));
|
|
41032
41283
|
});
|
|
41033
41284
|
}
|
|
41034
41285
|
async function runAiFailover(plan, commandId, cwd2, handlers, timeoutMs = 0, signal) {
|
|
@@ -41249,8 +41500,8 @@ function runHistory(ctx) {
|
|
|
41249
41500
|
ctx.print(t("slash.history.header", { n: recent.length, total: all.length }));
|
|
41250
41501
|
recent.forEach((e, i) => {
|
|
41251
41502
|
const time = new Date(e.startedAt).toLocaleTimeString();
|
|
41252
|
-
const
|
|
41253
|
-
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}`);
|
|
41254
41505
|
});
|
|
41255
41506
|
}
|
|
41256
41507
|
function runOutput(ctx) {
|
|
@@ -41261,8 +41512,8 @@ function runOutput(ctx) {
|
|
|
41261
41512
|
return;
|
|
41262
41513
|
}
|
|
41263
41514
|
const entry = all.slice().reverse()[n - 1];
|
|
41264
|
-
const
|
|
41265
|
-
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 }));
|
|
41266
41517
|
const out = readCommandOutput(entry.commandId);
|
|
41267
41518
|
if (out == null) {
|
|
41268
41519
|
ctx.print(t("slash.output.none"), "warn");
|
|
@@ -41557,7 +41808,7 @@ function buildPrompt2(input) {
|
|
|
41557
41808
|
].join("\n");
|
|
41558
41809
|
}
|
|
41559
41810
|
function runOnce(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
41560
|
-
return new Promise((
|
|
41811
|
+
return new Promise((resolve13, reject) => {
|
|
41561
41812
|
const args = [
|
|
41562
41813
|
"-p",
|
|
41563
41814
|
"--output-format",
|
|
@@ -41592,7 +41843,7 @@ function runOnce(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
|
41592
41843
|
});
|
|
41593
41844
|
child.on("close", (code) => {
|
|
41594
41845
|
clearTimeout(timer);
|
|
41595
|
-
|
|
41846
|
+
resolve13({ code: code ?? -1, out, err });
|
|
41596
41847
|
});
|
|
41597
41848
|
child.stdin.write(prompt);
|
|
41598
41849
|
child.stdin.end();
|
|
@@ -41619,13 +41870,13 @@ async function runMemoryCompaction(ai2, cwd2, input) {
|
|
|
41619
41870
|
}
|
|
41620
41871
|
|
|
41621
41872
|
// src/core/git-commit-identity.ts
|
|
41622
|
-
import { execFile as
|
|
41623
|
-
import { promisify as
|
|
41624
|
-
var
|
|
41873
|
+
import { execFile as execFile5 } from "child_process";
|
|
41874
|
+
import { promisify as promisify5 } from "util";
|
|
41875
|
+
var run5 = promisify5(execFile5);
|
|
41625
41876
|
async function setCommitAuthor(cwd2, machineUsername) {
|
|
41626
41877
|
if (!machineUsername) return;
|
|
41627
41878
|
try {
|
|
41628
|
-
await
|
|
41879
|
+
await run5("git", ["config", "--local", "user.name", machineUsername], { cwd: cwd2, timeout: 8e3 });
|
|
41629
41880
|
} catch {
|
|
41630
41881
|
}
|
|
41631
41882
|
}
|
|
@@ -41633,13 +41884,13 @@ async function setCommitAuthor(cwd2, machineUsername) {
|
|
|
41633
41884
|
// src/core/ws-client/run-slot.ts
|
|
41634
41885
|
var NO_SLOT = { release: () => void 0 };
|
|
41635
41886
|
function sleep2(ms2, signal) {
|
|
41636
|
-
return new Promise((
|
|
41637
|
-
if (signal?.aborted) return
|
|
41887
|
+
return new Promise((resolve13) => {
|
|
41888
|
+
if (signal?.aborted) return resolve13();
|
|
41638
41889
|
const timer = setTimeout(done, ms2);
|
|
41639
41890
|
function done() {
|
|
41640
41891
|
clearTimeout(timer);
|
|
41641
41892
|
signal?.removeEventListener("abort", done);
|
|
41642
|
-
|
|
41893
|
+
resolve13();
|
|
41643
41894
|
}
|
|
41644
41895
|
signal?.addEventListener("abort", done, { once: true });
|
|
41645
41896
|
});
|
|
@@ -41682,6 +41933,7 @@ async function acquireRunSlot(ctx, onQueued, signal) {
|
|
|
41682
41933
|
}
|
|
41683
41934
|
return { kind: "granted", handle, queued: ticket !== void 0 };
|
|
41684
41935
|
}
|
|
41936
|
+
if ("denied" in reply) return { kind: "denied", reason: reply.denied };
|
|
41685
41937
|
ticket = reply.ticket;
|
|
41686
41938
|
limit = reply.limit;
|
|
41687
41939
|
if (reply.position !== lastPosition) {
|
|
@@ -41700,11 +41952,41 @@ function leaveQueue(ctx, ticket) {
|
|
|
41700
41952
|
// src/core/ws-client/command-dispatch.ts
|
|
41701
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:";
|
|
41702
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
|
+
}
|
|
41703
41985
|
function cancelActiveRun(commandId) {
|
|
41704
|
-
const
|
|
41705
|
-
if (!
|
|
41706
|
-
|
|
41707
|
-
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 };
|
|
41708
41990
|
}
|
|
41709
41991
|
function handleCommandChannels(ctx, message, payload) {
|
|
41710
41992
|
if (message.channel === WsChannels.COMMAND_CANCEL) {
|
|
@@ -41769,6 +42051,7 @@ function handleCommandChannels(ctx, message, payload) {
|
|
|
41769
42051
|
ctx.bus.endBusy(dispatch.cmd);
|
|
41770
42052
|
const code = out.exitCode ?? -1;
|
|
41771
42053
|
finishCommand(out.commandId, code);
|
|
42054
|
+
if (changesGitState(dispatch.cmd, dispatch.args)) requestGitSnapshot("git-write");
|
|
41772
42055
|
ctx.bus.push({
|
|
41773
42056
|
source: "server",
|
|
41774
42057
|
kind: "exit",
|
|
@@ -41800,15 +42083,16 @@ async function runLocalCommand(ctx, input) {
|
|
|
41800
42083
|
await runAiPrompt(ctx, prompt, commandId, "local");
|
|
41801
42084
|
}
|
|
41802
42085
|
async function runAiPrompt(ctx, prompt, commandId, origin, oneShot = false, images, aiConfig, readOnly = false, bypass = false) {
|
|
41803
|
-
const
|
|
41804
|
-
activeRuns.set(commandId,
|
|
42086
|
+
const run12 = { controller: new AbortController(), state: "queued" };
|
|
42087
|
+
activeRuns.set(commandId, run12);
|
|
41805
42088
|
try {
|
|
41806
|
-
await runAiPromptInner(ctx, prompt, commandId, origin,
|
|
42089
|
+
await runAiPromptInner(ctx, prompt, commandId, origin, run12, oneShot, images, aiConfig, readOnly, bypass);
|
|
41807
42090
|
} finally {
|
|
41808
42091
|
activeRuns.delete(commandId);
|
|
42092
|
+
if (!readOnly) requestGitSnapshot("ai-run");
|
|
41809
42093
|
}
|
|
41810
42094
|
}
|
|
41811
|
-
async function runAiPromptInner(ctx, prompt, commandId, origin,
|
|
42095
|
+
async function runAiPromptInner(ctx, prompt, commandId, origin, run12, oneShot = false, images, aiConfig, readOnly = false, bypass = false) {
|
|
41812
42096
|
const config = readProfileConfig(ctx.profileDir);
|
|
41813
42097
|
const projectTimeoutSec = config.projectAiRunTimeoutSec ?? 0;
|
|
41814
42098
|
const machineTimeoutSec = config.aiRunTimeoutSec ?? 0;
|
|
@@ -41989,13 +42273,20 @@ ${guardedPrompt}`;
|
|
|
41989
42273
|
queue: { position, limit, running: running2 }
|
|
41990
42274
|
});
|
|
41991
42275
|
},
|
|
41992
|
-
|
|
42276
|
+
run12.controller.signal
|
|
41993
42277
|
);
|
|
41994
42278
|
if (slot.kind === "timeout") {
|
|
41995
42279
|
neverStarted = true;
|
|
41996
42280
|
const text = `RUN_QUEUE_TIMEOUT: waited 30 min for a free run slot (plan limit ${slot.limit} parallel runs) \u2014 not run`;
|
|
41997
42281
|
ctx.bus.push({ source: origin, kind: "log", text, level: "warn" });
|
|
41998
42282
|
ctx.send(WsChannels.COMMAND_OUTPUT, { commandId, seq: seq++, chunk: `${text}
|
|
42283
|
+
`, log: true });
|
|
42284
|
+
result = { exitCode: 1, workedDir: null, workedKey: null, workedCmd: null, usage: { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 }, sessionId: "" };
|
|
42285
|
+
} else if (slot.kind === "denied") {
|
|
42286
|
+
neverStarted = true;
|
|
42287
|
+
const text = "STORAGE_QUOTA_EXCEEDED: the organization's hosted storage is full \u2014 clear storage in Settings \u203A Storage, then retry";
|
|
42288
|
+
ctx.bus.push({ source: origin, kind: "log", text, level: "warn" });
|
|
42289
|
+
ctx.send(WsChannels.COMMAND_OUTPUT, { commandId, seq: seq++, chunk: `${text}
|
|
41999
42290
|
`, log: true });
|
|
42000
42291
|
result = { exitCode: 1, workedDir: null, workedKey: null, workedCmd: null, usage: { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 }, sessionId: "" };
|
|
42001
42292
|
} else if (slot.kind === "cancelled") {
|
|
@@ -42003,9 +42294,9 @@ ${guardedPrompt}`;
|
|
|
42003
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 };
|
|
42004
42295
|
} else {
|
|
42005
42296
|
if (slot.queued) ctx.send(WsChannels.COMMAND_OUTPUT, { commandId, seq: seq++, chunk: "", log: true, queue: null });
|
|
42006
|
-
|
|
42297
|
+
run12.state = "running";
|
|
42007
42298
|
try {
|
|
42008
|
-
result = await runAiFailover(plan, commandId, cwd2, handlers, aiRunTimeoutMs,
|
|
42299
|
+
result = await runAiFailover(plan, commandId, cwd2, handlers, aiRunTimeoutMs, run12.controller.signal);
|
|
42009
42300
|
} finally {
|
|
42010
42301
|
slot.handle.release();
|
|
42011
42302
|
}
|
|
@@ -42316,16 +42607,211 @@ function rejectByReview(ctx, commandId, origin, prompt, reasons) {
|
|
|
42316
42607
|
});
|
|
42317
42608
|
}
|
|
42318
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
|
+
|
|
42319
42800
|
// src/core/autonomous-cycle.ts
|
|
42320
42801
|
import { randomUUID } from "crypto";
|
|
42321
|
-
import { appendFile as appendFile2, mkdir as mkdir3, readFile as
|
|
42322
|
-
import { join as
|
|
42323
|
-
function buildAutonomousCyclePrompt() {
|
|
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";
|
|
42804
|
+
function buildAutonomousCyclePrompt(opts = {}) {
|
|
42805
|
+
const capNote = opts.intakeBlocked ? `
|
|
42806
|
+
|
|
42807
|
+
**Step 2 is DISABLED this cycle** \u2014 the organization reached its monthly plan limit for new \`AI_TODO\` /
|
|
42808
|
+
\`USER_QA\` rows. Do NOT add any row to \`AI_TODO.md\` or \`USER_QA.md\`; skip Step 2 entirely and continue
|
|
42809
|
+
with the already-approved work.` : "";
|
|
42324
42810
|
return `You are running UNATTENDED as the 4PM autonomous cycle (headless, one tick). Do EXACTLY ONE small
|
|
42325
42811
|
work cycle with the steps below, then STOP. Keep each cycle to ONE small task so you never run out of
|
|
42326
42812
|
tokens mid-way. If a step fails, append a row to the **Incidents** table of \`AI_DONE.md\` and STOP \u2014
|
|
42327
42813
|
do not push on. Write all books/docs/code in the language the project's \`CLAUDE.md\` specifies (default
|
|
42328
|
-
English)
|
|
42814
|
+
English).${capNote}
|
|
42329
42815
|
|
|
42330
42816
|
## Books & approval (MUST read the approvals file)
|
|
42331
42817
|
The five "book" files at the project root \u2014 \`USER_TODO.md\`, \`USER_QA.md\`, \`AI_TODO.md\`,
|
|
@@ -42459,8 +42945,16 @@ async function runAutonomousCycle(ctx) {
|
|
|
42459
42945
|
await log(`[run] cycle (tick ${hist.ticks.count}/${today})`);
|
|
42460
42946
|
const commandId = randomUUID();
|
|
42461
42947
|
const override = cfg.model ? { model: cfg.model } : void 0;
|
|
42948
|
+
const usage = await readBookUsage(ctx);
|
|
42949
|
+
const intakeBlocked = ["AI_TODO", "USER_QA"].some((b2) => {
|
|
42950
|
+
const c = usage?.books[b2];
|
|
42951
|
+
return !!c && c.limit !== null && c.used >= c.limit;
|
|
42952
|
+
});
|
|
42953
|
+
if (intakeBlocked) await log("[cap] monthly AI_TODO/USER_QA limit reached \u2014 intake skipped this cycle");
|
|
42954
|
+
const booksBefore = await cappedBookIds(root);
|
|
42462
42955
|
try {
|
|
42463
|
-
await runAiPrompt(ctx, buildAutonomousCyclePrompt(), commandId, "local", false, void 0, override, false, true);
|
|
42956
|
+
await runAiPrompt(ctx, buildAutonomousCyclePrompt({ intakeBlocked }), commandId, "local", false, void 0, override, false, true);
|
|
42957
|
+
await reportNewBookRows(ctx, root, booksBefore);
|
|
42464
42958
|
hist = await readHistories(root);
|
|
42465
42959
|
hist.consecutiveFails = 0;
|
|
42466
42960
|
await recordRun(root, hist, "success", "cycle complete", today);
|
|
@@ -42468,6 +42962,7 @@ async function runAutonomousCycle(ctx) {
|
|
|
42468
42962
|
ctx.bus.autonomousDone(true);
|
|
42469
42963
|
} catch (err) {
|
|
42470
42964
|
const msg = err instanceof Error ? err.message : String(err);
|
|
42965
|
+
await reportNewBookRows(ctx, root, booksBefore);
|
|
42471
42966
|
hist = await readHistories(root);
|
|
42472
42967
|
hist.consecutiveFails += 1;
|
|
42473
42968
|
await recordRun(root, hist, "failure", msg, today);
|
|
@@ -42484,13 +42979,32 @@ async function runAutonomousCycle(ctx) {
|
|
|
42484
42979
|
running.delete(root);
|
|
42485
42980
|
}
|
|
42486
42981
|
}
|
|
42982
|
+
async function readBookUsage(ctx) {
|
|
42983
|
+
try {
|
|
42984
|
+
return await ctx.request(WsChannels.AUTONOMOUS_BOOK_USAGE, {});
|
|
42985
|
+
} catch {
|
|
42986
|
+
return null;
|
|
42987
|
+
}
|
|
42988
|
+
}
|
|
42989
|
+
async function reportNewBookRows(ctx, root, before) {
|
|
42990
|
+
try {
|
|
42991
|
+
const after = await cappedBookIds(root);
|
|
42992
|
+
const added = {};
|
|
42993
|
+
for (const book of Object.keys(after)) {
|
|
42994
|
+
const n = [...after[book]].filter((id) => !before[book].has(id)).length;
|
|
42995
|
+
if (n > 0) added[book] = n;
|
|
42996
|
+
}
|
|
42997
|
+
if (Object.keys(added).length > 0) await ctx.request(WsChannels.AUTONOMOUS_BOOK_USAGE, { added });
|
|
42998
|
+
} catch {
|
|
42999
|
+
}
|
|
43000
|
+
}
|
|
42487
43001
|
async function dayLog(root, line) {
|
|
42488
43002
|
try {
|
|
42489
|
-
const dir2 =
|
|
43003
|
+
const dir2 = join21(root, ".claude", "logs");
|
|
42490
43004
|
await mkdir3(dir2, { recursive: true });
|
|
42491
43005
|
const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
42492
43006
|
const ts2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
42493
|
-
await appendFile2(
|
|
43007
|
+
await appendFile2(join21(dir2, `autonomous-tick-${day}.log`), `${ts2} ${line}
|
|
42494
43008
|
`, "utf8");
|
|
42495
43009
|
} catch {
|
|
42496
43010
|
}
|
|
@@ -42498,12 +43012,12 @@ async function dayLog(root, line) {
|
|
|
42498
43012
|
async function pruneLogs(root, retentionDays) {
|
|
42499
43013
|
if (!(retentionDays > 0)) return;
|
|
42500
43014
|
try {
|
|
42501
|
-
const dir2 =
|
|
43015
|
+
const dir2 = join21(root, ".claude", "logs");
|
|
42502
43016
|
const cutoff = Date.now() - retentionDays * 864e5;
|
|
42503
43017
|
for (const f2 of await readdir2(dir2)) {
|
|
42504
43018
|
if (!/^autonomous-tick-.*\.log$/.test(f2)) continue;
|
|
42505
|
-
const st2 = await
|
|
42506
|
-
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);
|
|
42507
43021
|
}
|
|
42508
43022
|
} catch {
|
|
42509
43023
|
}
|
|
@@ -42515,15 +43029,15 @@ async function recordRun(root, hist, status, note, today) {
|
|
|
42515
43029
|
}
|
|
42516
43030
|
async function readJsonObject(path) {
|
|
42517
43031
|
try {
|
|
42518
|
-
return JSON.parse(await
|
|
43032
|
+
return JSON.parse(await readFile6(path, "utf8"));
|
|
42519
43033
|
} catch {
|
|
42520
43034
|
return {};
|
|
42521
43035
|
}
|
|
42522
43036
|
}
|
|
42523
43037
|
async function hasWork(root) {
|
|
42524
|
-
const approvals = await readJsonObject(
|
|
43038
|
+
const approvals = await readJsonObject(join21(root, ".claude/.autonomous.approvals.json"));
|
|
42525
43039
|
const approved = Object.entries(approvals).filter(([, v2]) => v2 && typeof v2 === "object" && v2.approved === true).map(([k2]) => k2);
|
|
42526
|
-
const read = (f2) =>
|
|
43040
|
+
const read = (f2) => readFile6(join21(root, f2), "utf8").catch(() => "");
|
|
42527
43041
|
const [userTodo, userQa, aiTodo, aiProgress] = await Promise.all([
|
|
42528
43042
|
read("USER_TODO.md"),
|
|
42529
43043
|
read("USER_QA.md"),
|
|
@@ -42561,12 +43075,12 @@ async function allAiProfilesExhausted(ctx, cfg) {
|
|
|
42561
43075
|
|
|
42562
43076
|
// src/core/fs-browse.ts
|
|
42563
43077
|
import { readdir as readdir3 } from "fs/promises";
|
|
42564
|
-
import { resolve as
|
|
43078
|
+
import { resolve as resolve6, sep as sep3 } from "path";
|
|
42565
43079
|
async function listDir(path, root) {
|
|
42566
43080
|
if (!root) return { path: "", entries: [] };
|
|
42567
|
-
const base =
|
|
42568
|
-
const requested =
|
|
42569
|
-
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;
|
|
42570
43084
|
try {
|
|
42571
43085
|
const dirents = await readdir3(target, { withFileTypes: true });
|
|
42572
43086
|
const entries2 = dirents.map((d) => ({
|
|
@@ -42583,14 +43097,14 @@ async function listDir(path, root) {
|
|
|
42583
43097
|
}
|
|
42584
43098
|
|
|
42585
43099
|
// src/core/fs-read.ts
|
|
42586
|
-
import { readFile as
|
|
42587
|
-
import { resolve as
|
|
43100
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
43101
|
+
import { resolve as resolve7, sep as sep4 } from "path";
|
|
42588
43102
|
var MAX_BYTES = 256 * 1024;
|
|
42589
43103
|
function resolveInRoot(root, relPath) {
|
|
42590
43104
|
if (!root) return null;
|
|
42591
|
-
const base =
|
|
42592
|
-
const target =
|
|
42593
|
-
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;
|
|
42594
43108
|
}
|
|
42595
43109
|
async function readWorkerFile(root, path) {
|
|
42596
43110
|
const target = resolveInRoot(root, path);
|
|
@@ -42598,7 +43112,7 @@ async function readWorkerFile(root, path) {
|
|
|
42598
43112
|
return { path, content: "", truncated: false };
|
|
42599
43113
|
}
|
|
42600
43114
|
try {
|
|
42601
|
-
const buf = await
|
|
43115
|
+
const buf = await readFile7(target);
|
|
42602
43116
|
const truncated = buf.byteLength > MAX_BYTES;
|
|
42603
43117
|
const content = buf.subarray(0, MAX_BYTES).toString("utf8");
|
|
42604
43118
|
return { path: target, content, truncated };
|
|
@@ -42609,13 +43123,13 @@ async function readWorkerFile(root, path) {
|
|
|
42609
43123
|
|
|
42610
43124
|
// src/core/fs-write.ts
|
|
42611
43125
|
import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
|
|
42612
|
-
import { dirname as dirname5, resolve as
|
|
43126
|
+
import { dirname as dirname5, resolve as resolve8, sep as sep5 } from "path";
|
|
42613
43127
|
var MAX_BYTES2 = 2 * 1024 * 1024;
|
|
42614
43128
|
function resolveInRoot2(root, relPath) {
|
|
42615
43129
|
if (!root) return null;
|
|
42616
|
-
const base =
|
|
42617
|
-
const target =
|
|
42618
|
-
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;
|
|
42619
43133
|
}
|
|
42620
43134
|
async function writeWorkerFile(root, relPath, content) {
|
|
42621
43135
|
const target = resolveInRoot2(root, relPath);
|
|
@@ -42636,24 +43150,24 @@ async function writeWorkerFile(root, relPath, content) {
|
|
|
42636
43150
|
}
|
|
42637
43151
|
|
|
42638
43152
|
// src/core/fs-mutate.ts
|
|
42639
|
-
import { mkdir as mkdir5, writeFile as writeFile6, rename, rm as rm3, stat as
|
|
42640
|
-
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";
|
|
42641
43155
|
var MAX_BYTES3 = 2 * 1024 * 1024;
|
|
42642
43156
|
function resolveInRoot3(root, rel) {
|
|
42643
|
-
const base =
|
|
42644
|
-
const target =
|
|
42645
|
-
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;
|
|
42646
43160
|
}
|
|
42647
43161
|
async function isDir(p2) {
|
|
42648
43162
|
try {
|
|
42649
|
-
return (await
|
|
43163
|
+
return (await stat3(p2)).isDirectory();
|
|
42650
43164
|
} catch {
|
|
42651
43165
|
return false;
|
|
42652
43166
|
}
|
|
42653
43167
|
}
|
|
42654
43168
|
async function exists(p2) {
|
|
42655
43169
|
try {
|
|
42656
|
-
await
|
|
43170
|
+
await stat3(p2);
|
|
42657
43171
|
return true;
|
|
42658
43172
|
} catch {
|
|
42659
43173
|
return false;
|
|
@@ -42666,13 +43180,13 @@ async function mutateFs(root, req) {
|
|
|
42666
43180
|
switch (req.op) {
|
|
42667
43181
|
case "mkdir": {
|
|
42668
43182
|
const target = resolveInRoot3(root, req.path ?? "");
|
|
42669
|
-
if (!target || target ===
|
|
43183
|
+
if (!target || target === resolve9(root)) return escaped(req.path ?? "");
|
|
42670
43184
|
await mkdir5(target, { recursive: true });
|
|
42671
43185
|
return { ok: true, path: target };
|
|
42672
43186
|
}
|
|
42673
43187
|
case "create": {
|
|
42674
43188
|
const target = resolveInRoot3(root, req.path ?? "");
|
|
42675
|
-
if (!target || target ===
|
|
43189
|
+
if (!target || target === resolve9(root)) return escaped(req.path ?? "");
|
|
42676
43190
|
if (await exists(target)) return { ok: false, path: target, error: "already exists" };
|
|
42677
43191
|
const content = req.content ?? "";
|
|
42678
43192
|
if (Buffer.byteLength(content, "utf8") > MAX_BYTES3) {
|
|
@@ -42686,9 +43200,9 @@ async function mutateFs(root, req) {
|
|
|
42686
43200
|
const from = resolveInRoot3(root, req.from ?? "");
|
|
42687
43201
|
let to2 = resolveInRoot3(root, req.to ?? "");
|
|
42688
43202
|
if (!from || !to2) return escaped(req.from ?? req.to ?? "");
|
|
42689
|
-
if (from ===
|
|
42690
|
-
if (await isDir(to2)) to2 =
|
|
42691
|
-
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 ?? "");
|
|
42692
43206
|
if (await exists(to2)) return { ok: false, path: to2, error: "destination already exists" };
|
|
42693
43207
|
await mkdir5(dirname6(to2), { recursive: true });
|
|
42694
43208
|
await rename(from, to2);
|
|
@@ -42697,7 +43211,7 @@ async function mutateFs(root, req) {
|
|
|
42697
43211
|
case "delete": {
|
|
42698
43212
|
const target = resolveInRoot3(root, req.path ?? "");
|
|
42699
43213
|
if (!target) return escaped(req.path ?? "");
|
|
42700
|
-
if (target ===
|
|
43214
|
+
if (target === resolve9(root)) return { ok: false, path: target, error: "cannot delete the project root" };
|
|
42701
43215
|
await rm3(target, { recursive: true, force: true });
|
|
42702
43216
|
return { ok: true, path: target };
|
|
42703
43217
|
}
|
|
@@ -42710,13 +43224,13 @@ async function mutateFs(root, req) {
|
|
|
42710
43224
|
}
|
|
42711
43225
|
|
|
42712
43226
|
// src/core/fs-transfer.ts
|
|
42713
|
-
import { mkdir as mkdir6, readFile as
|
|
42714
|
-
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";
|
|
42715
43229
|
function resolveInRoot4(root, relPath) {
|
|
42716
43230
|
if (!root) return null;
|
|
42717
|
-
const base =
|
|
42718
|
-
const target =
|
|
42719
|
-
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;
|
|
42720
43234
|
}
|
|
42721
43235
|
var MIME_BY_EXT = {
|
|
42722
43236
|
".png": "image/png",
|
|
@@ -42765,12 +43279,12 @@ async function downloadBinaryFile(root, relPath) {
|
|
|
42765
43279
|
const target = resolveInRoot4(root, relPath);
|
|
42766
43280
|
if (!target) return { error: "path escapes the project root" };
|
|
42767
43281
|
try {
|
|
42768
|
-
const info = await
|
|
43282
|
+
const info = await stat4(target);
|
|
42769
43283
|
if (!info.isFile()) return { error: "not a file" };
|
|
42770
43284
|
if (info.size > FS_TRANSFER_MAX_BYTES) {
|
|
42771
43285
|
return { error: `file too large (max ${FS_TRANSFER_MAX_BYTES} bytes)` };
|
|
42772
43286
|
}
|
|
42773
|
-
const buf = await
|
|
43287
|
+
const buf = await readFile8(target);
|
|
42774
43288
|
return {
|
|
42775
43289
|
contentBase64: buf.toString("base64"),
|
|
42776
43290
|
contentType: guessMime(target),
|
|
@@ -42829,16 +43343,16 @@ function handleFsChannels(ctx, message, payload) {
|
|
|
42829
43343
|
}
|
|
42830
43344
|
|
|
42831
43345
|
// src/core/git-diff.ts
|
|
42832
|
-
import { readFile as
|
|
42833
|
-
import { execFile as
|
|
42834
|
-
import { basename as basename5, dirname as dirname8, resolve as
|
|
42835
|
-
import { promisify as
|
|
42836
|
-
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);
|
|
42837
43351
|
function resolveInRoot5(root, relPath) {
|
|
42838
43352
|
if (!root) return null;
|
|
42839
|
-
const base =
|
|
42840
|
-
const target =
|
|
42841
|
-
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;
|
|
42842
43356
|
}
|
|
42843
43357
|
function looksBinary(buf) {
|
|
42844
43358
|
return buf.subarray(0, 8e3).includes(0);
|
|
@@ -42856,12 +43370,12 @@ async function gitDiff(root, path) {
|
|
|
42856
43370
|
const base = basename5(target);
|
|
42857
43371
|
let newBuf = null;
|
|
42858
43372
|
try {
|
|
42859
|
-
newBuf = await
|
|
43373
|
+
newBuf = await readFile9(target);
|
|
42860
43374
|
} catch {
|
|
42861
43375
|
}
|
|
42862
43376
|
let oldBuf = null;
|
|
42863
43377
|
try {
|
|
42864
|
-
const { stdout } = await
|
|
43378
|
+
const { stdout } = await run7("git", ["show", `HEAD:./${base}`], {
|
|
42865
43379
|
cwd: dir2,
|
|
42866
43380
|
timeout: 15e3,
|
|
42867
43381
|
maxBuffer: FS_TRANSFER_MAX_BYTES + 1024,
|
|
@@ -42891,12 +43405,12 @@ async function gitDiff(root, path) {
|
|
|
42891
43405
|
}
|
|
42892
43406
|
|
|
42893
43407
|
// src/core/git-env.ts
|
|
42894
|
-
import { execFile as
|
|
42895
|
-
import { promisify as
|
|
42896
|
-
var
|
|
43408
|
+
import { execFile as execFile8 } from "child_process";
|
|
43409
|
+
import { promisify as promisify8 } from "util";
|
|
43410
|
+
var run8 = promisify8(execFile8);
|
|
42897
43411
|
async function hasBinary(bin, args = ["--version"]) {
|
|
42898
43412
|
try {
|
|
42899
|
-
await
|
|
43413
|
+
await run8(bin, args, { timeout: 5e3 });
|
|
42900
43414
|
return true;
|
|
42901
43415
|
} catch {
|
|
42902
43416
|
return false;
|
|
@@ -42909,7 +43423,7 @@ async function checkGitEnv(provider) {
|
|
|
42909
43423
|
let account = null;
|
|
42910
43424
|
if (installed) {
|
|
42911
43425
|
try {
|
|
42912
|
-
const { stdout, stderr } = await
|
|
43426
|
+
const { stdout, stderr } = await run8(bin, ["auth", "status"], { timeout: 8e3 });
|
|
42913
43427
|
const out = `${stdout}
|
|
42914
43428
|
${stderr}`;
|
|
42915
43429
|
authenticated = /logged in|logged into|✓/i.test(out);
|
|
@@ -42922,33 +43436,33 @@ ${stderr}`;
|
|
|
42922
43436
|
}
|
|
42923
43437
|
|
|
42924
43438
|
// src/core/git-ssh-key.ts
|
|
42925
|
-
import { execFile as
|
|
42926
|
-
import { readFile as
|
|
42927
|
-
import { join as
|
|
42928
|
-
import { promisify as
|
|
42929
|
-
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);
|
|
42930
43444
|
function keyPaths(profileDir2) {
|
|
42931
|
-
const dir2 =
|
|
42932
|
-
const key =
|
|
43445
|
+
const dir2 = join22(profileDir2, ".ssh");
|
|
43446
|
+
const key = join22(dir2, "id_4pm");
|
|
42933
43447
|
return { dir: dir2, key, pub: `${key}.pub` };
|
|
42934
43448
|
}
|
|
42935
43449
|
async function readPublicKey(pub) {
|
|
42936
43450
|
try {
|
|
42937
|
-
return (await
|
|
43451
|
+
return (await readFile10(pub, "utf8")).trim();
|
|
42938
43452
|
} catch {
|
|
42939
43453
|
return null;
|
|
42940
43454
|
}
|
|
42941
43455
|
}
|
|
42942
43456
|
async function fingerprintOf(pub) {
|
|
42943
43457
|
try {
|
|
42944
|
-
const { stdout } = await
|
|
43458
|
+
const { stdout } = await run9("ssh-keygen", ["-lf", pub], { timeout: 5e3 });
|
|
42945
43459
|
return stdout.match(/SHA256:[A-Za-z0-9+/=]+/)?.[0] ?? null;
|
|
42946
43460
|
} catch {
|
|
42947
43461
|
return null;
|
|
42948
43462
|
}
|
|
42949
43463
|
}
|
|
42950
43464
|
async function configureGit(key) {
|
|
42951
|
-
await
|
|
43465
|
+
await run9(
|
|
42952
43466
|
"git",
|
|
42953
43467
|
["config", "--global", "core.sshCommand", `ssh -i ${key} -o IdentitiesOnly=yes`],
|
|
42954
43468
|
{ timeout: 5e3 }
|
|
@@ -42959,7 +43473,7 @@ async function generate(profileDir2) {
|
|
|
42959
43473
|
await mkdir7(dir2, { recursive: true });
|
|
42960
43474
|
await rm4(key, { force: true });
|
|
42961
43475
|
await rm4(pub, { force: true });
|
|
42962
|
-
await
|
|
43476
|
+
await run9("ssh-keygen", ["-t", "ed25519", "-f", key, "-N", "", "-C", "4pm-deploy-key"], {
|
|
42963
43477
|
timeout: 15e3
|
|
42964
43478
|
});
|
|
42965
43479
|
await configureGit(key);
|
|
@@ -42969,7 +43483,7 @@ async function remove(profileDir2) {
|
|
|
42969
43483
|
const { key, pub } = keyPaths(profileDir2);
|
|
42970
43484
|
await rm4(key, { force: true });
|
|
42971
43485
|
await rm4(pub, { force: true });
|
|
42972
|
-
await
|
|
43486
|
+
await run9("git", ["config", "--global", "--unset", "core.sshCommand"], { timeout: 5e3 }).catch(
|
|
42973
43487
|
() => void 0
|
|
42974
43488
|
);
|
|
42975
43489
|
return { publicKey: null, fingerprint: null };
|
|
@@ -42989,109 +43503,6 @@ async function manageSshKey(op, profileDir2) {
|
|
|
42989
43503
|
}
|
|
42990
43504
|
}
|
|
42991
43505
|
|
|
42992
|
-
// src/core/git-history.ts
|
|
42993
|
-
import { execFile as execFile8 } from "child_process";
|
|
42994
|
-
import { resolve as resolve10, sep as sep8 } from "path";
|
|
42995
|
-
import { promisify as promisify8 } from "util";
|
|
42996
|
-
var run8 = promisify8(execFile8);
|
|
42997
|
-
var FS = "";
|
|
42998
|
-
var RS = "";
|
|
42999
|
-
function resolveRepo(root, repo) {
|
|
43000
|
-
if (!root) return null;
|
|
43001
|
-
const base = resolve10(root);
|
|
43002
|
-
const requested = resolve10(base, repo && repo.trim() ? repo : ".");
|
|
43003
|
-
return requested === base || requested.startsWith(base + sep8) ? requested : base;
|
|
43004
|
-
}
|
|
43005
|
-
async function git(cwd2, args) {
|
|
43006
|
-
try {
|
|
43007
|
-
const { stdout } = await run8("git", args, {
|
|
43008
|
-
cwd: cwd2,
|
|
43009
|
-
timeout: 15e3,
|
|
43010
|
-
maxBuffer: 8 * 1024 * 1024
|
|
43011
|
-
});
|
|
43012
|
-
return stdout;
|
|
43013
|
-
} catch {
|
|
43014
|
-
return "";
|
|
43015
|
-
}
|
|
43016
|
-
}
|
|
43017
|
-
async function gitRepos(root) {
|
|
43018
|
-
if (!root) return { repos: [] };
|
|
43019
|
-
const base = resolve10(root);
|
|
43020
|
-
const top = (await git(base, ["rev-parse", "--show-toplevel"])).trim();
|
|
43021
|
-
if (!top || !(top === base || top.startsWith(base + sep8))) return { repos: [] };
|
|
43022
|
-
const repos = [];
|
|
43023
|
-
const rootRemote = (await git(base, ["remote", "get-url", "origin"])).trim();
|
|
43024
|
-
const rootBranch = (await git(base, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
43025
|
-
repos.push({ subdir: "", name: "", remote: rootRemote || null, branch: rootBranch || null });
|
|
43026
|
-
const subOut = await git(base, ["submodule", "status"]);
|
|
43027
|
-
for (const line of subOut.split("\n")) {
|
|
43028
|
-
const m2 = line.trim().match(/^[-+U ]?[0-9a-f]{7,40}\s+(\S+)/);
|
|
43029
|
-
const subPath = m2?.[1];
|
|
43030
|
-
if (!subPath) continue;
|
|
43031
|
-
const abs = resolve10(base, subPath);
|
|
43032
|
-
if (!(abs === base || abs.startsWith(base + sep8))) continue;
|
|
43033
|
-
const remote = (await git(abs, ["remote", "get-url", "origin"])).trim();
|
|
43034
|
-
const branch = (await git(abs, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
43035
|
-
repos.push({ subdir: subPath, name: subPath, remote: remote || null, branch: branch || null });
|
|
43036
|
-
}
|
|
43037
|
-
return { repos };
|
|
43038
|
-
}
|
|
43039
|
-
async function gitLog(root, repo, skip, limit) {
|
|
43040
|
-
const cwd2 = resolveRepo(root, repo);
|
|
43041
|
-
if (!cwd2) return { entries: [], hasMore: false };
|
|
43042
|
-
const capped = Math.min(Math.max(limit, 1), 200);
|
|
43043
|
-
const format = ["%H", "%h", "%an", "%aI", "%s"].join(FS) + RS;
|
|
43044
|
-
const stdout = await git(cwd2, [
|
|
43045
|
-
"log",
|
|
43046
|
-
`--skip=${Math.max(skip, 0)}`,
|
|
43047
|
-
`-n`,
|
|
43048
|
-
String(capped + 1),
|
|
43049
|
-
`--pretty=format:${format}`
|
|
43050
|
-
]);
|
|
43051
|
-
const rows = stdout.split(RS).map((r) => r.replace(/^\n/, "")).filter((r) => r.length > 0);
|
|
43052
|
-
const entries2 = rows.slice(0, capped).map((row) => {
|
|
43053
|
-
const [hash = "", shortHash = "", author = "", date = "", subject = ""] = row.split(FS);
|
|
43054
|
-
return { hash, shortHash, author, date, subject };
|
|
43055
|
-
});
|
|
43056
|
-
return { entries: entries2, hasMore: rows.length > capped };
|
|
43057
|
-
}
|
|
43058
|
-
async function gitCommit(root, repo, hash) {
|
|
43059
|
-
const cwd2 = resolveRepo(root, repo);
|
|
43060
|
-
const empty = { hash, author: "", date: "", subject: "", files: [] };
|
|
43061
|
-
if (!cwd2 || !hash) return empty;
|
|
43062
|
-
const format = ["%H", "%an", "%aI", "%s"].join(FS);
|
|
43063
|
-
const stdout = await git(cwd2, [
|
|
43064
|
-
"show",
|
|
43065
|
-
"--name-status",
|
|
43066
|
-
"-M",
|
|
43067
|
-
`--pretty=format:${format}`,
|
|
43068
|
-
hash
|
|
43069
|
-
]);
|
|
43070
|
-
if (!stdout) return empty;
|
|
43071
|
-
const lines = stdout.split("\n");
|
|
43072
|
-
const [full = hash, author = "", date = "", subject = ""] = (lines[0] ?? "").split(FS);
|
|
43073
|
-
const files = [];
|
|
43074
|
-
for (const line of lines.slice(1)) {
|
|
43075
|
-
if (!line.trim()) continue;
|
|
43076
|
-
const parts = line.split(" ");
|
|
43077
|
-
const status = (parts[0] ?? "").charAt(0);
|
|
43078
|
-
if (!status) continue;
|
|
43079
|
-
if (status === "R" || status === "C") {
|
|
43080
|
-
files.push({ path: parts[2] ?? "", status, oldPath: parts[1] ?? void 0 });
|
|
43081
|
-
} else {
|
|
43082
|
-
files.push({ path: parts[1] ?? "", status });
|
|
43083
|
-
}
|
|
43084
|
-
}
|
|
43085
|
-
return { hash: full, author, date, subject, files };
|
|
43086
|
-
}
|
|
43087
|
-
async function gitCommitDiff(root, repo, hash, path) {
|
|
43088
|
-
const cwd2 = resolveRepo(root, repo);
|
|
43089
|
-
if (!cwd2 || !hash || !path) return { path, oldContent: "", newContent: "" };
|
|
43090
|
-
const oldContent = await git(cwd2, ["show", `${hash}^:${path}`]);
|
|
43091
|
-
const newContent = await git(cwd2, ["show", `${hash}:${path}`]);
|
|
43092
|
-
return { path, oldContent, newContent };
|
|
43093
|
-
}
|
|
43094
|
-
|
|
43095
43506
|
// src/core/ws-client/handlers/git.ts
|
|
43096
43507
|
function handleGitChannels(ctx, message, payload) {
|
|
43097
43508
|
switch (message.channel) {
|
|
@@ -43144,12 +43555,12 @@ function handleGitChannels(ctx, message, payload) {
|
|
|
43144
43555
|
}
|
|
43145
43556
|
|
|
43146
43557
|
// src/core/ws-client/handlers/misc.ts
|
|
43147
|
-
import { join as
|
|
43558
|
+
import { join as join23 } from "path";
|
|
43148
43559
|
|
|
43149
43560
|
// src/core/ai-assist.ts
|
|
43150
|
-
import { execFile as
|
|
43151
|
-
import { promisify as
|
|
43152
|
-
var
|
|
43561
|
+
import { execFile as execFile10 } from "child_process";
|
|
43562
|
+
import { promisify as promisify10 } from "util";
|
|
43563
|
+
var run10 = promisify10(execFile10);
|
|
43153
43564
|
function aiCli() {
|
|
43154
43565
|
return process.env.AI_CLI || "claude";
|
|
43155
43566
|
}
|
|
@@ -43192,7 +43603,7 @@ function parseAi(cli, stdout, prompt) {
|
|
|
43192
43603
|
}
|
|
43193
43604
|
async function runAi(prompt) {
|
|
43194
43605
|
const cli = aiCli();
|
|
43195
|
-
const { stdout } = await
|
|
43606
|
+
const { stdout } = await run10(cli, aiArgs(cli, prompt), {
|
|
43196
43607
|
timeout: 12e4,
|
|
43197
43608
|
maxBuffer: 8 * 1024 * 1024
|
|
43198
43609
|
});
|
|
@@ -43294,7 +43705,7 @@ function handleMiscChannels(ctx, message, payload) {
|
|
|
43294
43705
|
}
|
|
43295
43706
|
case WsChannels.LOG_READ: {
|
|
43296
43707
|
const limit = Math.min(Math.max(payload.limit ?? 200, 1), 2e3);
|
|
43297
|
-
const lines = readRecentLogLines(
|
|
43708
|
+
const lines = readRecentLogLines(join23(ctx.profileDir, "logs"), limit);
|
|
43298
43709
|
ctx.send(WsChannels.LOG_READ, { lines }, message.id);
|
|
43299
43710
|
return true;
|
|
43300
43711
|
}
|
|
@@ -43326,7 +43737,7 @@ function handleMiscChannels(ctx, message, payload) {
|
|
|
43326
43737
|
|
|
43327
43738
|
// src/core/ws-client/handlers/project.ts
|
|
43328
43739
|
import { existsSync as existsSync14, mkdirSync as mkdirSync7, renameSync, rmSync as rmSync7 } from "fs";
|
|
43329
|
-
import { join as
|
|
43740
|
+
import { join as join24 } from "path";
|
|
43330
43741
|
|
|
43331
43742
|
// src/core/ai-task.ts
|
|
43332
43743
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -43398,8 +43809,8 @@ function handleProjectChannels(ctx, message, payload) {
|
|
|
43398
43809
|
case WsChannels.PHYSIC_SYNC: {
|
|
43399
43810
|
const sync = payload;
|
|
43400
43811
|
const oldName = sync.oldName;
|
|
43401
|
-
const oldPath = oldName ?
|
|
43402
|
-
const newPath =
|
|
43812
|
+
const oldPath = oldName ? join24(ctx.profileDir, oldName) : null;
|
|
43813
|
+
const newPath = join24(ctx.profileDir, sync.newName);
|
|
43403
43814
|
if (oldName) {
|
|
43404
43815
|
const oldRoot = ctx.physicFolderPath(oldName);
|
|
43405
43816
|
if (oldRoot) void uninstallCron(oldRoot).catch(() => void 0);
|
|
@@ -43411,6 +43822,7 @@ function handleProjectChannels(ctx, message, payload) {
|
|
|
43411
43822
|
mkdirSync7(newPath, { recursive: true });
|
|
43412
43823
|
}
|
|
43413
43824
|
ctx.physicRoot = ctx.physicFolderPath(sync.newName);
|
|
43825
|
+
requestGitSnapshot("physic-sync");
|
|
43414
43826
|
ctx.bus.setProject(sync.newName);
|
|
43415
43827
|
ctx.bus.log(t("project.folderSynced", { name: sync.newName }));
|
|
43416
43828
|
void ctx.cloneServingRepos().catch(() => void 0);
|
|
@@ -43421,7 +43833,7 @@ function handleProjectChannels(ctx, message, payload) {
|
|
|
43421
43833
|
if (del.name) {
|
|
43422
43834
|
const delRoot = ctx.physicFolderPath(del.name);
|
|
43423
43835
|
if (delRoot) void uninstallCron(delRoot).catch(() => void 0);
|
|
43424
|
-
rmSync7(
|
|
43836
|
+
rmSync7(join24(ctx.profileDir, del.name), { recursive: true, force: true });
|
|
43425
43837
|
ctx.bus.log(t("project.folderDeleted", { name: del.name }));
|
|
43426
43838
|
}
|
|
43427
43839
|
void manageSshKey("delete", ctx.profileDir).catch(() => void 0);
|
|
@@ -43440,6 +43852,7 @@ function handleProjectChannels(ctx, message, payload) {
|
|
|
43440
43852
|
projectAiMemoryBudgetChars: tokens.memory?.budgetChars ?? 0
|
|
43441
43853
|
});
|
|
43442
43854
|
ctx.setRestrictToFolder(tokens.restrictToFolder === true);
|
|
43855
|
+
if (tokens.gitAuth !== void 0) applyGitAuth(ctx, tokens.gitAuth, tokens.gitAuthHost ?? null);
|
|
43443
43856
|
logger.info("project.tokens.applied", {
|
|
43444
43857
|
aiRunTimeoutSec: tokens.aiRunTimeoutSec ?? 0,
|
|
43445
43858
|
autoClearIdleMinutes: tokens.autoClearIdleMinutes ?? 0
|
|
@@ -43452,9 +43865,28 @@ function handleProjectChannels(ctx, message, payload) {
|
|
|
43452
43865
|
ctx.profileDir,
|
|
43453
43866
|
(p2) => ctx.send(WsChannels.PROJECT_PROGRESS, p2),
|
|
43454
43867
|
// AI init on the standard AI path + one org run slot (ADR-0362).
|
|
43455
|
-
createAiTaskRunner(ctx)
|
|
43456
|
-
|
|
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
|
+
});
|
|
43457
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
|
+
);
|
|
43888
|
+
return true;
|
|
43889
|
+
}
|
|
43458
43890
|
case WsChannels.PROJECT_ADD:
|
|
43459
43891
|
void addProject(
|
|
43460
43892
|
payload,
|
|
@@ -43462,12 +43894,23 @@ function handleProjectChannels(ctx, message, payload) {
|
|
|
43462
43894
|
(p2) => ctx.send(WsChannels.PROJECT_PROGRESS, p2),
|
|
43463
43895
|
// An add-with-scaffold runs AI init too (ADR-0362).
|
|
43464
43896
|
createAiTaskRunner(ctx)
|
|
43465
|
-
).then((reply) =>
|
|
43897
|
+
).then((reply) => {
|
|
43898
|
+
ctx.send(WsChannels.PROJECT_ADD, reply, message.id);
|
|
43899
|
+
requestGitSnapshot("provision");
|
|
43900
|
+
});
|
|
43466
43901
|
return true;
|
|
43467
43902
|
default:
|
|
43468
43903
|
return false;
|
|
43469
43904
|
}
|
|
43470
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
|
+
}
|
|
43471
43914
|
|
|
43472
43915
|
// src/core/knowledge-compose.ts
|
|
43473
43916
|
import { spawn as spawn8 } from "child_process";
|
|
@@ -43489,7 +43932,7 @@ Extra focus from the author: ${req.prompt}` : "";
|
|
|
43489
43932
|
].join("\n");
|
|
43490
43933
|
}
|
|
43491
43934
|
function runOnce2(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
43492
|
-
return new Promise((
|
|
43935
|
+
return new Promise((resolve13, reject) => {
|
|
43493
43936
|
const args = ["-p", ...profile?.model ? ["--model", profile.model] : [], ...denySettingsArgs(cmd, [...denyDirs, profile?.dir])];
|
|
43494
43937
|
const env3 = {
|
|
43495
43938
|
...process.env,
|
|
@@ -43512,7 +43955,7 @@ function runOnce2(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
|
43512
43955
|
});
|
|
43513
43956
|
child.on("close", (code) => {
|
|
43514
43957
|
clearTimeout(timer);
|
|
43515
|
-
|
|
43958
|
+
resolve13({ code: code ?? -1, out, err });
|
|
43516
43959
|
});
|
|
43517
43960
|
child.stdin.write(prompt);
|
|
43518
43961
|
child.stdin.end();
|
|
@@ -43542,13 +43985,13 @@ async function runKnowledgeCompose(req, ai2, physicRoot) {
|
|
|
43542
43985
|
}
|
|
43543
43986
|
|
|
43544
43987
|
// src/core/faq-compose.ts
|
|
43545
|
-
import { spawn as spawn9, execFile as
|
|
43988
|
+
import { spawn as spawn9, execFile as execFile11 } from "child_process";
|
|
43546
43989
|
import { existsSync as existsSync15, mkdirSync as mkdirSync8, rmSync as rmSync8 } from "fs";
|
|
43547
43990
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
43548
|
-
import { join as
|
|
43991
|
+
import { join as join25 } from "path";
|
|
43549
43992
|
import { tmpdir as tmpdir4 } from "os";
|
|
43550
|
-
import { promisify as
|
|
43551
|
-
var execFileAsync2 =
|
|
43993
|
+
import { promisify as promisify11 } from "util";
|
|
43994
|
+
var execFileAsync2 = promisify11(execFile11);
|
|
43552
43995
|
var NO_USAGE2 = { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };
|
|
43553
43996
|
var COMPOSE_TIMEOUT_MS2 = 10 * 60 * 1e3;
|
|
43554
43997
|
var AGENT_DENIED_TOOLS = ["Bash", "WebFetch", "WebSearch", "NotebookEdit", "Task"];
|
|
@@ -43613,7 +44056,7 @@ function parsePerTicket(text) {
|
|
|
43613
44056
|
return [];
|
|
43614
44057
|
}
|
|
43615
44058
|
function runClaudeOnce2(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
43616
|
-
return new Promise((
|
|
44059
|
+
return new Promise((resolve13, reject) => {
|
|
43617
44060
|
const isClaude = cmd.includes("claude");
|
|
43618
44061
|
const args = [
|
|
43619
44062
|
"-p",
|
|
@@ -43646,7 +44089,7 @@ function runClaudeOnce2(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
|
43646
44089
|
child.on("close", (code) => {
|
|
43647
44090
|
clearTimeout(timer);
|
|
43648
44091
|
out += parser.flush();
|
|
43649
|
-
|
|
44092
|
+
resolve13({ code: code ?? -1, out, err, usage: parser.usage() });
|
|
43650
44093
|
});
|
|
43651
44094
|
child.stdin.write(prompt);
|
|
43652
44095
|
child.stdin.end();
|
|
@@ -43666,7 +44109,7 @@ async function runClaudeWithFailover2(prompt, cwd2, ai2) {
|
|
|
43666
44109
|
var MAX_OUTPUT_CHARS = 8e3;
|
|
43667
44110
|
async function runFaqCompose(req, ai2) {
|
|
43668
44111
|
if (!req.repo.token) return { prUrl: "", error: "no write token" };
|
|
43669
|
-
const workDir =
|
|
44112
|
+
const workDir = join25(tmpdir4(), "4pm-faq-sync", randomUUID3());
|
|
43670
44113
|
const gitEnv = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
|
|
43671
44114
|
const auth = gitAuthArgs("x-access-token", req.repo.token);
|
|
43672
44115
|
try {
|
|
@@ -43894,8 +44337,8 @@ function handleToolsChannels(ctx, message, payload) {
|
|
|
43894
44337
|
}
|
|
43895
44338
|
|
|
43896
44339
|
// src/core/agents.ts
|
|
43897
|
-
import { readFile as
|
|
43898
|
-
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";
|
|
43899
44342
|
var AGENTS_REL = ".claude/agents";
|
|
43900
44343
|
var SKILLS_REL = ".claude/skills";
|
|
43901
44344
|
var MEMORY_DIRS = /* @__PURE__ */ new Set(["short-memory", "long-memory"]);
|
|
@@ -43904,7 +44347,7 @@ function safeName(name) {
|
|
|
43904
44347
|
}
|
|
43905
44348
|
async function readText2(path, fallback = "") {
|
|
43906
44349
|
try {
|
|
43907
|
-
return await
|
|
44350
|
+
return await readFile11(path, "utf8");
|
|
43908
44351
|
} catch {
|
|
43909
44352
|
return fallback;
|
|
43910
44353
|
}
|
|
@@ -43919,17 +44362,17 @@ async function listAgents(root) {
|
|
|
43919
44362
|
const subagents = [];
|
|
43920
44363
|
const skills = [];
|
|
43921
44364
|
try {
|
|
43922
|
-
for (const e of await readdir4(
|
|
44365
|
+
for (const e of await readdir4(join26(root, AGENTS_REL), { withFileTypes: true })) {
|
|
43923
44366
|
if (!e.isFile() || !e.name.endsWith(".md") || MEMORY_DIRS.has(e.name)) continue;
|
|
43924
|
-
const fm = frontmatter(await readText2(
|
|
44367
|
+
const fm = frontmatter(await readText2(join26(root, AGENTS_REL, e.name)));
|
|
43925
44368
|
subagents.push({ name: e.name.replace(/\.md$/, ""), description: fm.description });
|
|
43926
44369
|
}
|
|
43927
44370
|
} catch {
|
|
43928
44371
|
}
|
|
43929
44372
|
try {
|
|
43930
|
-
for (const e of await readdir4(
|
|
44373
|
+
for (const e of await readdir4(join26(root, SKILLS_REL), { withFileTypes: true })) {
|
|
43931
44374
|
if (!e.isDirectory()) continue;
|
|
43932
|
-
const fm = frontmatter(await readText2(
|
|
44375
|
+
const fm = frontmatter(await readText2(join26(root, SKILLS_REL, e.name, "SKILL.md")));
|
|
43933
44376
|
skills.push({ name: e.name, description: fm.description });
|
|
43934
44377
|
}
|
|
43935
44378
|
} catch {
|
|
@@ -43940,15 +44383,15 @@ async function listAgents(root) {
|
|
|
43940
44383
|
}
|
|
43941
44384
|
function itemPath(root, kind, name) {
|
|
43942
44385
|
const n = safeName(name);
|
|
43943
|
-
return kind === "subagent" ?
|
|
44386
|
+
return kind === "subagent" ? join26(root, AGENTS_REL, `${n}.md`) : join26(root, SKILLS_REL, n, "SKILL.md");
|
|
43944
44387
|
}
|
|
43945
44388
|
async function readAgent(root, kind, name) {
|
|
43946
44389
|
const content = await readText2(itemPath(root, kind, name));
|
|
43947
44390
|
if (kind !== "subagent") return { content };
|
|
43948
44391
|
const n = safeName(name);
|
|
43949
44392
|
const [short, long] = await Promise.all([
|
|
43950
|
-
readText2(
|
|
43951
|
-
readText2(
|
|
44393
|
+
readText2(join26(root, AGENTS_REL, "short-memory", `${n}.md`)),
|
|
44394
|
+
readText2(join26(root, AGENTS_REL, "long-memory", `${n}.md`))
|
|
43952
44395
|
]);
|
|
43953
44396
|
return { content, memory: { short, long } };
|
|
43954
44397
|
}
|
|
@@ -43958,12 +44401,12 @@ async function writeAgent(root, req) {
|
|
|
43958
44401
|
try {
|
|
43959
44402
|
if (req.action === "delete") {
|
|
43960
44403
|
if (req.kind === "subagent") await rm5(itemPath(root, "subagent", n), { force: true });
|
|
43961
|
-
else await rm5(
|
|
44404
|
+
else await rm5(join26(root, SKILLS_REL, n), { recursive: true, force: true });
|
|
43962
44405
|
return { ok: true };
|
|
43963
44406
|
}
|
|
43964
44407
|
if (req.content === void 0) return { ok: false, error: "content is required" };
|
|
43965
44408
|
const path = itemPath(root, req.kind, n);
|
|
43966
|
-
await mkdir8(
|
|
44409
|
+
await mkdir8(join26(path, ".."), { recursive: true });
|
|
43967
44410
|
await writeFile8(path, req.content, "utf8");
|
|
43968
44411
|
return { ok: true };
|
|
43969
44412
|
} catch (err) {
|
|
@@ -43972,21 +44415,21 @@ async function writeAgent(root, req) {
|
|
|
43972
44415
|
}
|
|
43973
44416
|
|
|
43974
44417
|
// src/core/secrets.ts
|
|
43975
|
-
import { readFile as
|
|
43976
|
-
import { join as
|
|
44418
|
+
import { readFile as readFile12, writeFile as writeFile9 } from "fs/promises";
|
|
44419
|
+
import { join as join27 } from "path";
|
|
43977
44420
|
var SECURITY_REL = "AI_SECURITY.md";
|
|
43978
44421
|
var PLACEHOLDER_REL = "AI_PLACEHOLDER.md";
|
|
43979
44422
|
var SECRETS_REL = "project.secrets.json";
|
|
43980
44423
|
async function readText3(path, fallback = "") {
|
|
43981
44424
|
try {
|
|
43982
|
-
return await
|
|
44425
|
+
return await readFile12(path, "utf8");
|
|
43983
44426
|
} catch {
|
|
43984
44427
|
return fallback;
|
|
43985
44428
|
}
|
|
43986
44429
|
}
|
|
43987
44430
|
async function readSecretsObject(root) {
|
|
43988
44431
|
try {
|
|
43989
|
-
const v2 = JSON.parse(await
|
|
44432
|
+
const v2 = JSON.parse(await readFile12(join27(root, SECRETS_REL), "utf8"));
|
|
43990
44433
|
return v2 && typeof v2 === "object" ? v2 : {};
|
|
43991
44434
|
} catch {
|
|
43992
44435
|
return {};
|
|
@@ -44005,8 +44448,8 @@ function placeholderKeys(text) {
|
|
|
44005
44448
|
}
|
|
44006
44449
|
async function readSecrets(root) {
|
|
44007
44450
|
const [security, placeholder, secrets] = await Promise.all([
|
|
44008
|
-
readText3(
|
|
44009
|
-
readText3(
|
|
44451
|
+
readText3(join27(root, SECURITY_REL)),
|
|
44452
|
+
readText3(join27(root, PLACEHOLDER_REL)),
|
|
44010
44453
|
readSecretsObject(root)
|
|
44011
44454
|
]);
|
|
44012
44455
|
const names = /* @__PURE__ */ new Set([...placeholderKeys(placeholder), ...Object.keys(secrets)]);
|
|
@@ -44019,14 +44462,14 @@ async function readSecrets(root) {
|
|
|
44019
44462
|
async function writeSecrets(root, req) {
|
|
44020
44463
|
try {
|
|
44021
44464
|
if (req.kind === "security") {
|
|
44022
|
-
await writeFile9(
|
|
44465
|
+
await writeFile9(join27(root, SECURITY_REL), req.content, "utf8");
|
|
44023
44466
|
} else if (req.kind === "placeholder") {
|
|
44024
|
-
await writeFile9(
|
|
44467
|
+
await writeFile9(join27(root, PLACEHOLDER_REL), req.content, "utf8");
|
|
44025
44468
|
} else {
|
|
44026
44469
|
const secrets = await readSecretsObject(root);
|
|
44027
44470
|
if (req.kind === "secret") secrets[req.key] = req.value;
|
|
44028
44471
|
else delete secrets[req.key];
|
|
44029
|
-
await writeFile9(
|
|
44472
|
+
await writeFile9(join27(root, SECRETS_REL), JSON.stringify(secrets, null, 2) + "\n", "utf8");
|
|
44030
44473
|
}
|
|
44031
44474
|
return { ok: true };
|
|
44032
44475
|
} catch (err) {
|
|
@@ -44035,17 +44478,17 @@ async function writeSecrets(root, req) {
|
|
|
44035
44478
|
}
|
|
44036
44479
|
|
|
44037
44480
|
// src/core/agent-tools.ts
|
|
44038
|
-
import { readFile as
|
|
44039
|
-
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";
|
|
44040
44483
|
var SHARED_REL = ".claude/settings.json";
|
|
44041
44484
|
var LOCAL_REL = ".claude/settings.local.json";
|
|
44042
44485
|
var SECRETS_DENY = "Read(./project.secrets.json)";
|
|
44043
44486
|
function fileFor(root, scope) {
|
|
44044
|
-
return
|
|
44487
|
+
return join28(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
|
|
44045
44488
|
}
|
|
44046
44489
|
async function readSettingsObject(path) {
|
|
44047
44490
|
try {
|
|
44048
|
-
const v2 = JSON.parse(await
|
|
44491
|
+
const v2 = JSON.parse(await readFile13(path, "utf8"));
|
|
44049
44492
|
return v2 && typeof v2 === "object" ? v2 : {};
|
|
44050
44493
|
} catch {
|
|
44051
44494
|
return {};
|
|
@@ -44077,8 +44520,8 @@ async function writeAgentTools(root, scope, permissions) {
|
|
|
44077
44520
|
|
|
44078
44521
|
// src/core/packages.ts
|
|
44079
44522
|
import { createHash as createHash4 } from "crypto";
|
|
44080
|
-
import { readFile as
|
|
44081
|
-
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";
|
|
44082
44525
|
var AGENTS_REL2 = ".claude/agents";
|
|
44083
44526
|
var SKILLS_REL2 = ".claude/skills";
|
|
44084
44527
|
var MANIFEST_REL = ".claude/.4pm-packages.json";
|
|
@@ -44087,7 +44530,7 @@ function safeName2(name) {
|
|
|
44087
44530
|
}
|
|
44088
44531
|
async function readMaybe(path) {
|
|
44089
44532
|
try {
|
|
44090
|
-
return await
|
|
44533
|
+
return await readFile14(path);
|
|
44091
44534
|
} catch {
|
|
44092
44535
|
return null;
|
|
44093
44536
|
}
|
|
@@ -44102,7 +44545,7 @@ async function listFilesRel(dir2) {
|
|
|
44102
44545
|
return;
|
|
44103
44546
|
}
|
|
44104
44547
|
for (const e of entries2) {
|
|
44105
|
-
const full =
|
|
44548
|
+
const full = join29(d, e.name);
|
|
44106
44549
|
if (e.isDirectory()) await walk2(full);
|
|
44107
44550
|
else if (e.isFile()) out.push(relative(dir2, full));
|
|
44108
44551
|
}
|
|
@@ -44113,16 +44556,16 @@ async function listFilesRel(dir2) {
|
|
|
44113
44556
|
async function readArtifactFiles(root, kind, slug) {
|
|
44114
44557
|
const files = [];
|
|
44115
44558
|
if (kind === "subagent") {
|
|
44116
|
-
const agent = await readMaybe(
|
|
44559
|
+
const agent = await readMaybe(join29(root, AGENTS_REL2, `${slug}.md`));
|
|
44117
44560
|
if (agent) files.push({ path: "agent.md", contentBase64: agent.toString("base64") });
|
|
44118
|
-
const short = await readMaybe(
|
|
44561
|
+
const short = await readMaybe(join29(root, AGENTS_REL2, "short-memory", `${slug}.md`));
|
|
44119
44562
|
if (short) files.push({ path: "short-memory.md", contentBase64: short.toString("base64") });
|
|
44120
|
-
const long = await readMaybe(
|
|
44563
|
+
const long = await readMaybe(join29(root, AGENTS_REL2, "long-memory", `${slug}.md`));
|
|
44121
44564
|
if (long) files.push({ path: "long-memory.md", contentBase64: long.toString("base64") });
|
|
44122
44565
|
} else {
|
|
44123
|
-
const base =
|
|
44566
|
+
const base = join29(root, SKILLS_REL2, slug);
|
|
44124
44567
|
for (const rel of await listFilesRel(base)) {
|
|
44125
|
-
const buf = await readMaybe(
|
|
44568
|
+
const buf = await readMaybe(join29(base, rel));
|
|
44126
44569
|
if (buf) files.push({ path: rel.split("\\").join("/"), contentBase64: buf.toString("base64") });
|
|
44127
44570
|
}
|
|
44128
44571
|
}
|
|
@@ -44140,16 +44583,16 @@ async function artifactSha256(root, kind, slug) {
|
|
|
44140
44583
|
return h.digest("hex");
|
|
44141
44584
|
}
|
|
44142
44585
|
async function artifactExists(root, kind, slug) {
|
|
44143
|
-
const target = kind === "subagent" ?
|
|
44586
|
+
const target = kind === "subagent" ? join29(root, AGENTS_REL2, `${slug}.md`) : join29(root, SKILLS_REL2, slug);
|
|
44144
44587
|
try {
|
|
44145
|
-
await
|
|
44588
|
+
await stat5(target);
|
|
44146
44589
|
return true;
|
|
44147
44590
|
} catch {
|
|
44148
44591
|
return false;
|
|
44149
44592
|
}
|
|
44150
44593
|
}
|
|
44151
44594
|
async function readManifest(root) {
|
|
44152
|
-
const buf = await readMaybe(
|
|
44595
|
+
const buf = await readMaybe(join29(root, MANIFEST_REL));
|
|
44153
44596
|
if (!buf) return {};
|
|
44154
44597
|
try {
|
|
44155
44598
|
return JSON.parse(buf.toString("utf8"));
|
|
@@ -44158,7 +44601,7 @@ async function readManifest(root) {
|
|
|
44158
44601
|
}
|
|
44159
44602
|
}
|
|
44160
44603
|
async function writeManifest(root, manifest) {
|
|
44161
|
-
const path =
|
|
44604
|
+
const path = join29(root, MANIFEST_REL);
|
|
44162
44605
|
await mkdir10(dirname10(path), { recursive: true });
|
|
44163
44606
|
await writeFile11(path, JSON.stringify(manifest, null, 2), "utf8");
|
|
44164
44607
|
}
|
|
@@ -44167,24 +44610,24 @@ async function writeArtifactFiles(root, kind, slug, files) {
|
|
|
44167
44610
|
if (kind === "subagent") {
|
|
44168
44611
|
const agent = byPath.get("agent.md");
|
|
44169
44612
|
if (agent) {
|
|
44170
|
-
await mkdir10(
|
|
44171
|
-
await writeFile11(
|
|
44613
|
+
await mkdir10(join29(root, AGENTS_REL2), { recursive: true });
|
|
44614
|
+
await writeFile11(join29(root, AGENTS_REL2, `${slug}.md`), agent);
|
|
44172
44615
|
}
|
|
44173
44616
|
const short = byPath.get("short-memory.md");
|
|
44174
44617
|
if (short) {
|
|
44175
|
-
await mkdir10(
|
|
44176
|
-
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);
|
|
44177
44620
|
}
|
|
44178
44621
|
const long = byPath.get("long-memory.md");
|
|
44179
44622
|
if (long) {
|
|
44180
|
-
await mkdir10(
|
|
44181
|
-
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);
|
|
44182
44625
|
}
|
|
44183
44626
|
} else {
|
|
44184
|
-
const base =
|
|
44627
|
+
const base = join29(root, SKILLS_REL2, slug);
|
|
44185
44628
|
for (const [rel, buf] of byPath) {
|
|
44186
44629
|
if (rel.includes("..") || rel.startsWith("/")) continue;
|
|
44187
|
-
const dest =
|
|
44630
|
+
const dest = join29(base, rel);
|
|
44188
44631
|
await mkdir10(dirname10(dest), { recursive: true });
|
|
44189
44632
|
await writeFile11(dest, buf);
|
|
44190
44633
|
}
|
|
@@ -44250,11 +44693,11 @@ async function removePackage(root, slug) {
|
|
|
44250
44693
|
const entry = manifest[n];
|
|
44251
44694
|
if (!entry) return { ok: false, error: "not installed" };
|
|
44252
44695
|
if (entry.kind === "subagent") {
|
|
44253
|
-
await rm6(
|
|
44254
|
-
await rm6(
|
|
44255
|
-
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 });
|
|
44256
44699
|
} else {
|
|
44257
|
-
await rm6(
|
|
44700
|
+
await rm6(join29(root, SKILLS_REL2, n), { recursive: true, force: true });
|
|
44258
44701
|
}
|
|
44259
44702
|
delete manifest[n];
|
|
44260
44703
|
await writeManifest(root, manifest);
|
|
@@ -44265,8 +44708,8 @@ async function removePackage(root, slug) {
|
|
|
44265
44708
|
}
|
|
44266
44709
|
|
|
44267
44710
|
// src/core/graph.ts
|
|
44268
|
-
import { readFile as
|
|
44269
|
-
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";
|
|
44270
44713
|
var MAX_FILES = 2e3;
|
|
44271
44714
|
var MAX_NODES = 3e3;
|
|
44272
44715
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "out", "coverage", ".turbo"]);
|
|
@@ -44286,7 +44729,7 @@ async function walk(root, accept) {
|
|
|
44286
44729
|
if (e.name.startsWith(".") && e.name !== ".claude") {
|
|
44287
44730
|
if (e.isDirectory()) continue;
|
|
44288
44731
|
}
|
|
44289
|
-
const full =
|
|
44732
|
+
const full = join30(dir2, e.name);
|
|
44290
44733
|
if (e.isDirectory()) {
|
|
44291
44734
|
if (!SKIP_DIRS.has(e.name)) stack.push(full);
|
|
44292
44735
|
} else if (accept(full)) {
|
|
@@ -44314,14 +44757,14 @@ async function buildDocs(root) {
|
|
|
44314
44757
|
const rel = relative2(root, f2).split("\\").join("/");
|
|
44315
44758
|
let text = "";
|
|
44316
44759
|
try {
|
|
44317
|
-
text = await
|
|
44760
|
+
text = await readFile15(f2, "utf8");
|
|
44318
44761
|
} catch {
|
|
44319
44762
|
continue;
|
|
44320
44763
|
}
|
|
44321
44764
|
for (const m2 of text.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)) {
|
|
44322
44765
|
const href = (m2[1] ?? "").split("#")[0].trim();
|
|
44323
44766
|
if (!href || !href.endsWith(".md") || href.startsWith("http")) continue;
|
|
44324
|
-
const target = relative2(root,
|
|
44767
|
+
const target = relative2(root, resolve12(dirname11(f2), href)).split("\\").join("/");
|
|
44325
44768
|
if (ids.has(target) && target !== rel) {
|
|
44326
44769
|
const key = `${rel}->${target}`;
|
|
44327
44770
|
if (!seen.has(key)) {
|
|
@@ -44353,7 +44796,7 @@ async function buildCode(root) {
|
|
|
44353
44796
|
const rel = relative2(root, f2).split("\\").join("/");
|
|
44354
44797
|
let text = "";
|
|
44355
44798
|
try {
|
|
44356
|
-
text = await
|
|
44799
|
+
text = await readFile15(f2, "utf8");
|
|
44357
44800
|
} catch {
|
|
44358
44801
|
continue;
|
|
44359
44802
|
}
|
|
@@ -44405,17 +44848,17 @@ async function buildGraph(root, mode) {
|
|
|
44405
44848
|
}
|
|
44406
44849
|
|
|
44407
44850
|
// src/core/rag.ts
|
|
44408
|
-
import { readFile as
|
|
44851
|
+
import { readFile as readFile16, writeFile as writeFile12, statfs as statfs2, mkdir as mkdir11 } from "fs/promises";
|
|
44409
44852
|
import { existsSync as existsSync16 } from "fs";
|
|
44410
|
-
import { execFile as
|
|
44853
|
+
import { execFile as execFile12, spawn as spawn10 } from "child_process";
|
|
44411
44854
|
import { totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
44412
|
-
import { join as
|
|
44413
|
-
import { promisify as
|
|
44414
|
-
var
|
|
44855
|
+
import { join as join31 } from "path";
|
|
44856
|
+
import { promisify as promisify12 } from "util";
|
|
44857
|
+
var run11 = promisify12(execFile12);
|
|
44415
44858
|
var RAG_REL = ".claude/rag";
|
|
44416
44859
|
async function tryRun(cmd, args) {
|
|
44417
44860
|
try {
|
|
44418
|
-
const { stdout } = await
|
|
44861
|
+
const { stdout } = await run11(cmd, args, { timeout: 8e3 });
|
|
44419
44862
|
return { ok: true, out: stdout.trim() };
|
|
44420
44863
|
} catch {
|
|
44421
44864
|
return { ok: false, out: "" };
|
|
@@ -44430,7 +44873,7 @@ async function detectPython() {
|
|
|
44430
44873
|
}
|
|
44431
44874
|
async function installLog(root) {
|
|
44432
44875
|
try {
|
|
44433
|
-
const text = await
|
|
44876
|
+
const text = await readFile16(join31(root, RAG_REL, "install.log"), "utf8");
|
|
44434
44877
|
return text.split("\n").filter((l) => l.length > 0).slice(-200);
|
|
44435
44878
|
} catch {
|
|
44436
44879
|
return [];
|
|
@@ -44492,14 +44935,14 @@ async function ragStatus(root) {
|
|
|
44492
44935
|
};
|
|
44493
44936
|
let modelName = "";
|
|
44494
44937
|
try {
|
|
44495
|
-
modelName = (await
|
|
44938
|
+
modelName = (await readFile16(join31(root, RAG_REL, ".installed"), "utf8")).trim();
|
|
44496
44939
|
} catch {
|
|
44497
44940
|
modelName = "";
|
|
44498
44941
|
}
|
|
44499
44942
|
const modelPresent = modelName.length > 0;
|
|
44500
44943
|
return {
|
|
44501
44944
|
installed: fastembed && sqliteVec && modelPresent,
|
|
44502
|
-
installing: existsSync16(
|
|
44945
|
+
installing: existsSync16(join31(root, RAG_REL, ".installing")),
|
|
44503
44946
|
python: { found: py.found, version: py.version },
|
|
44504
44947
|
deps: { pip, fastembed, sqliteVec },
|
|
44505
44948
|
model: { present: modelPresent, name: modelName },
|
|
@@ -44510,9 +44953,9 @@ async function ragStatus(root) {
|
|
|
44510
44953
|
};
|
|
44511
44954
|
}
|
|
44512
44955
|
async function readIndexMeta(root) {
|
|
44513
|
-
const indexing = existsSync16(
|
|
44956
|
+
const indexing = existsSync16(join31(root, RAG_REL, ".indexing"));
|
|
44514
44957
|
try {
|
|
44515
|
-
const m2 = JSON.parse(await
|
|
44958
|
+
const m2 = JSON.parse(await readFile16(join31(root, RAG_REL, ".index.json"), "utf8"));
|
|
44516
44959
|
return {
|
|
44517
44960
|
present: true,
|
|
44518
44961
|
indexing,
|
|
@@ -44579,14 +45022,14 @@ if __name__ == "__main__":
|
|
|
44579
45022
|
elif cmd == "query": query(sys.argv[2], int(sys.argv[3]) if len(sys.argv) > 3 else 8)
|
|
44580
45023
|
`;
|
|
44581
45024
|
async function ensureEngine(root) {
|
|
44582
|
-
await mkdir11(
|
|
44583
|
-
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");
|
|
44584
45027
|
}
|
|
44585
45028
|
async function ragReindex(root) {
|
|
44586
45029
|
const py = await detectPython();
|
|
44587
45030
|
if (!py.found) return { ok: false, started: false, error: "python3 not found" };
|
|
44588
|
-
if (!existsSync16(
|
|
44589
|
-
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 };
|
|
44590
45033
|
await ensureEngine(root);
|
|
44591
45034
|
const script = `
|
|
44592
45035
|
set +e
|
|
@@ -44606,12 +45049,12 @@ rm -f "${RAG_REL}/.indexing"
|
|
|
44606
45049
|
async function ragQuery(root, queryText, k2 = 8) {
|
|
44607
45050
|
const py = await detectPython();
|
|
44608
45051
|
if (!py.found) return { results: [], error: "python3 not found" };
|
|
44609
|
-
if (!existsSync16(
|
|
45052
|
+
if (!existsSync16(join31(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
|
|
44610
45053
|
await ensureEngine(root);
|
|
44611
45054
|
try {
|
|
44612
|
-
const { stdout } = await
|
|
45055
|
+
const { stdout } = await run11(
|
|
44613
45056
|
py.bin,
|
|
44614
|
-
[
|
|
45057
|
+
[join31(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k2, 1), 20))],
|
|
44615
45058
|
{ cwd: root, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
44616
45059
|
);
|
|
44617
45060
|
const parsed = JSON.parse(stdout.trim() || '{"results":[]}');
|
|
@@ -44626,9 +45069,9 @@ async function ragInstall(root, model) {
|
|
|
44626
45069
|
}
|
|
44627
45070
|
const py = await detectPython();
|
|
44628
45071
|
if (!py.found) return { ok: false, started: false, error: "python3 not found on the worker" };
|
|
44629
|
-
if (existsSync16(
|
|
45072
|
+
if (existsSync16(join31(root, RAG_REL, ".installing"))) return { ok: true, started: true };
|
|
44630
45073
|
try {
|
|
44631
|
-
await mkdir11(
|
|
45074
|
+
await mkdir11(join31(root, RAG_REL), { recursive: true });
|
|
44632
45075
|
} catch {
|
|
44633
45076
|
}
|
|
44634
45077
|
const script = `
|
|
@@ -44656,7 +45099,7 @@ rm -f "${RAG_REL}/.installing"
|
|
|
44656
45099
|
|
|
44657
45100
|
// src/core/config-sync.ts
|
|
44658
45101
|
import { existsSync as existsSync17, readFileSync as readFileSync16 } from "fs";
|
|
44659
|
-
import { join as
|
|
45102
|
+
import { join as join32 } from "path";
|
|
44660
45103
|
var SERVER_MANAGED_KEYS = [
|
|
44661
45104
|
"physicPath",
|
|
44662
45105
|
"sessionSwitchPct",
|
|
@@ -44673,7 +45116,7 @@ var SERVER_MANAGED_KEYS = [
|
|
|
44673
45116
|
"projectAiMemoryBudgetChars"
|
|
44674
45117
|
];
|
|
44675
45118
|
function readConfigText(profileDir2) {
|
|
44676
|
-
const path =
|
|
45119
|
+
const path = join32(profileDir2, "config.json");
|
|
44677
45120
|
if (existsSync17(path)) {
|
|
44678
45121
|
try {
|
|
44679
45122
|
return readFileSync16(path, "utf8");
|
|
@@ -45160,14 +45603,14 @@ var WsClient = class {
|
|
|
45160
45603
|
*/
|
|
45161
45604
|
awaitConnected(timeoutMs) {
|
|
45162
45605
|
if (this.sessionKey && this.bus.status === "connected") return Promise.resolve(true);
|
|
45163
|
-
return new Promise((
|
|
45606
|
+
return new Promise((resolve13) => {
|
|
45164
45607
|
const waiter = () => {
|
|
45165
45608
|
clearTimeout(timer);
|
|
45166
|
-
|
|
45609
|
+
resolve13(true);
|
|
45167
45610
|
};
|
|
45168
45611
|
const timer = setTimeout(() => {
|
|
45169
45612
|
this.connectWaiters = this.connectWaiters.filter((w2) => w2 !== waiter);
|
|
45170
|
-
|
|
45613
|
+
resolve13(false);
|
|
45171
45614
|
}, timeoutMs);
|
|
45172
45615
|
this.connectWaiters.push(waiter);
|
|
45173
45616
|
});
|
|
@@ -45194,16 +45637,16 @@ var WsClient = class {
|
|
|
45194
45637
|
this.forceReconnect = false;
|
|
45195
45638
|
return Promise.resolve();
|
|
45196
45639
|
}
|
|
45197
|
-
return new Promise((
|
|
45640
|
+
return new Promise((resolve13) => {
|
|
45198
45641
|
const timer = setTimeout(() => {
|
|
45199
45642
|
this.wakeReconnect = null;
|
|
45200
|
-
|
|
45643
|
+
resolve13();
|
|
45201
45644
|
}, ms2);
|
|
45202
45645
|
this.wakeReconnect = () => {
|
|
45203
45646
|
clearTimeout(timer);
|
|
45204
45647
|
this.wakeReconnect = null;
|
|
45205
45648
|
this.forceReconnect = false;
|
|
45206
|
-
|
|
45649
|
+
resolve13();
|
|
45207
45650
|
};
|
|
45208
45651
|
});
|
|
45209
45652
|
}
|
|
@@ -45257,7 +45700,7 @@ var WsClient = class {
|
|
|
45257
45700
|
* One WS session: token → connect → handshake → handle messages until close.
|
|
45258
45701
|
*/
|
|
45259
45702
|
connectOnce() {
|
|
45260
|
-
return new Promise((
|
|
45703
|
+
return new Promise((resolve13, reject) => {
|
|
45261
45704
|
void (async () => {
|
|
45262
45705
|
this.sessionEstablished = false;
|
|
45263
45706
|
this.bus.setStatus("connecting");
|
|
@@ -45358,7 +45801,7 @@ var WsClient = class {
|
|
|
45358
45801
|
if (code === CLOSE_DRAINING) {
|
|
45359
45802
|
this.bus.log(t("error.serverDraining"));
|
|
45360
45803
|
}
|
|
45361
|
-
|
|
45804
|
+
resolve13();
|
|
45362
45805
|
});
|
|
45363
45806
|
socket.on("error", (err) => {
|
|
45364
45807
|
this.clearHeartbeat();
|
|
@@ -45396,6 +45839,14 @@ var WsClient = class {
|
|
|
45396
45839
|
this.startUsagePolling();
|
|
45397
45840
|
this.startLogUpload();
|
|
45398
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
|
+
});
|
|
45399
45850
|
setToolHealthSink((report) => this.send(WsChannels.TOOL_HEALTH, report));
|
|
45400
45851
|
this.connectWaiters.splice(0).forEach((fn) => fn());
|
|
45401
45852
|
return;
|
|
@@ -45484,7 +45935,7 @@ var WsClient = class {
|
|
|
45484
45935
|
* Used for quota.check before spawning an AI cli (ADR-0020).
|
|
45485
45936
|
*/
|
|
45486
45937
|
request(channel, data) {
|
|
45487
|
-
return new Promise((
|
|
45938
|
+
return new Promise((resolve13, reject) => {
|
|
45488
45939
|
if (!this.socket || !this.sessionKey) {
|
|
45489
45940
|
reject(new Error("Not connected to the server."));
|
|
45490
45941
|
return;
|
|
@@ -45504,7 +45955,7 @@ var WsClient = class {
|
|
|
45504
45955
|
}, 15e3);
|
|
45505
45956
|
timer.unref?.();
|
|
45506
45957
|
this.pending.set(envelope.id, {
|
|
45507
|
-
resolve: (value) =>
|
|
45958
|
+
resolve: (value) => resolve13(value),
|
|
45508
45959
|
timer
|
|
45509
45960
|
});
|
|
45510
45961
|
this.socket.send(JSON.stringify(envelope));
|
|
@@ -45573,7 +46024,7 @@ var WsClient = class {
|
|
|
45573
46024
|
*/
|
|
45574
46025
|
physicFolderPath(projectName) {
|
|
45575
46026
|
const safe = projectName.replace(/[/\\]/g, "_").replace(/\.\./g, "_").trim();
|
|
45576
|
-
return safe ?
|
|
46027
|
+
return safe ? join33(this.context.profileDir, safe) : null;
|
|
45577
46028
|
}
|
|
45578
46029
|
/**
|
|
45579
46030
|
* Report machine.status: version + the state of the physic project being served
|
|
@@ -45685,7 +46136,7 @@ var WsClient = class {
|
|
|
45685
46136
|
*/
|
|
45686
46137
|
uploadLog() {
|
|
45687
46138
|
try {
|
|
45688
|
-
const upload = readLogUpload(
|
|
46139
|
+
const upload = readLogUpload(join33(this.context.profileDir, "logs"));
|
|
45689
46140
|
if (!upload) return;
|
|
45690
46141
|
const sig = `${upload.fileName}:${upload.totalBytes}`;
|
|
45691
46142
|
if (sig === this.lastLogSig) return;
|
|
@@ -45848,9 +46299,9 @@ var WsClient = class {
|
|
|
45848
46299
|
|
|
45849
46300
|
// src/core/instance-lock.ts
|
|
45850
46301
|
import { readFileSync as readFileSync17, rmSync as rmSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
45851
|
-
import { join as
|
|
46302
|
+
import { join as join34 } from "path";
|
|
45852
46303
|
function lockPath(profileDir2) {
|
|
45853
|
-
return
|
|
46304
|
+
return join34(profileDir2, "start.lock");
|
|
45854
46305
|
}
|
|
45855
46306
|
function isAlive(pid) {
|
|
45856
46307
|
try {
|
|
@@ -46172,7 +46623,7 @@ var SessionBus = class {
|
|
|
46172
46623
|
// src/core/control-server.ts
|
|
46173
46624
|
import { createServer } from "net";
|
|
46174
46625
|
import { chmodSync as chmodSync5, rmSync as rmSync10 } from "fs";
|
|
46175
|
-
import { join as
|
|
46626
|
+
import { join as join36 } from "path";
|
|
46176
46627
|
|
|
46177
46628
|
// src/core/control-protocol.ts
|
|
46178
46629
|
var CONTROL_SOCKET_FILE = "control.sock";
|
|
@@ -46203,9 +46654,9 @@ function createFrameParser() {
|
|
|
46203
46654
|
// src/core/control-token.ts
|
|
46204
46655
|
import { chmodSync as chmodSync4, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "fs";
|
|
46205
46656
|
import { randomBytes as randomBytes3, timingSafeEqual } from "crypto";
|
|
46206
|
-
import { join as
|
|
46657
|
+
import { join as join35 } from "path";
|
|
46207
46658
|
function controlTokenPath(profileDir2) {
|
|
46208
|
-
return
|
|
46659
|
+
return join35(profileDir2, CONTROL_TOKEN_FILE);
|
|
46209
46660
|
}
|
|
46210
46661
|
function writeControlToken(profileDir2) {
|
|
46211
46662
|
const token = randomBytes3(32).toString("hex");
|
|
@@ -46234,7 +46685,7 @@ function tokenMatches(expected, given) {
|
|
|
46234
46685
|
|
|
46235
46686
|
// src/core/control-server.ts
|
|
46236
46687
|
function startControlServer(bus, profileDir2, info) {
|
|
46237
|
-
const socketPath =
|
|
46688
|
+
const socketPath = join36(profileDir2, CONTROL_SOCKET_FILE);
|
|
46238
46689
|
try {
|
|
46239
46690
|
rmSync10(socketPath, { force: true });
|
|
46240
46691
|
} catch {
|
|
@@ -51410,24 +51861,24 @@ var App = class extends import_react9.PureComponent {
|
|
|
51410
51861
|
})
|
|
51411
51862
|
}));
|
|
51412
51863
|
};
|
|
51413
|
-
findNextFocusable = (
|
|
51414
|
-
const activeIndex =
|
|
51415
|
-
return focusable.id ===
|
|
51864
|
+
findNextFocusable = (state3) => {
|
|
51865
|
+
const activeIndex = state3.focusables.findIndex((focusable) => {
|
|
51866
|
+
return focusable.id === state3.activeFocusId;
|
|
51416
51867
|
});
|
|
51417
|
-
for (let index = activeIndex + 1; index <
|
|
51418
|
-
const focusable =
|
|
51868
|
+
for (let index = activeIndex + 1; index < state3.focusables.length; index++) {
|
|
51869
|
+
const focusable = state3.focusables[index];
|
|
51419
51870
|
if (focusable?.isActive) {
|
|
51420
51871
|
return focusable.id;
|
|
51421
51872
|
}
|
|
51422
51873
|
}
|
|
51423
51874
|
return void 0;
|
|
51424
51875
|
};
|
|
51425
|
-
findPreviousFocusable = (
|
|
51426
|
-
const activeIndex =
|
|
51427
|
-
return focusable.id ===
|
|
51876
|
+
findPreviousFocusable = (state3) => {
|
|
51877
|
+
const activeIndex = state3.focusables.findIndex((focusable) => {
|
|
51878
|
+
return focusable.id === state3.activeFocusId;
|
|
51428
51879
|
});
|
|
51429
51880
|
for (let index = activeIndex - 1; index >= 0; index--) {
|
|
51430
|
-
const focusable =
|
|
51881
|
+
const focusable = state3.focusables[index];
|
|
51431
51882
|
if (focusable?.isActive) {
|
|
51432
51883
|
return focusable.id;
|
|
51433
51884
|
}
|
|
@@ -51623,8 +52074,8 @@ var Ink = class {
|
|
|
51623
52074
|
}
|
|
51624
52075
|
}
|
|
51625
52076
|
async waitUntilExit() {
|
|
51626
|
-
this.exitPromise ||= new Promise((
|
|
51627
|
-
this.resolveExitPromise =
|
|
52077
|
+
this.exitPromise ||= new Promise((resolve13, reject) => {
|
|
52078
|
+
this.resolveExitPromise = resolve13;
|
|
51628
52079
|
this.rejectExitPromise = reject;
|
|
51629
52080
|
});
|
|
51630
52081
|
return this.exitPromise;
|
|
@@ -51992,10 +52443,10 @@ var import_react26 = __toESM(require_react(), 1);
|
|
|
51992
52443
|
|
|
51993
52444
|
// src/core/input-history.ts
|
|
51994
52445
|
import { existsSync as existsSync20, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
|
|
51995
|
-
import { join as
|
|
52446
|
+
import { join as join37 } from "path";
|
|
51996
52447
|
var MAX_HISTORY = 200;
|
|
51997
52448
|
function historyPath(profileDir2) {
|
|
51998
|
-
return
|
|
52449
|
+
return join37(profileDir2, "input-history.json");
|
|
51999
52450
|
}
|
|
52000
52451
|
function loadInputHistory(profileDir2) {
|
|
52001
52452
|
const path = historyPath(profileDir2);
|
|
@@ -52609,7 +53060,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
52609
53060
|
return;
|
|
52610
53061
|
}
|
|
52611
53062
|
logger.configure({
|
|
52612
|
-
dir:
|
|
53063
|
+
dir: join38(profileDir2, "logs"),
|
|
52613
53064
|
level: resolveLogLevel(),
|
|
52614
53065
|
base: { version: CLI_VERSION, profile: profileName, scope: credential.scope }
|
|
52615
53066
|
});
|
|
@@ -52677,7 +53128,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
52677
53128
|
logger.error("ws.run.crash", { message: e.message, stack: e.stack });
|
|
52678
53129
|
if (client.isStopped) return;
|
|
52679
53130
|
bus.log(t("start.recovered", { message: e.message }), "error");
|
|
52680
|
-
await new Promise((
|
|
53131
|
+
await new Promise((resolve13) => setTimeout(resolve13, 1e3));
|
|
52681
53132
|
}
|
|
52682
53133
|
}
|
|
52683
53134
|
};
|
|
@@ -52719,12 +53170,12 @@ async function runStart(profileDir2, profileName) {
|
|
|
52719
53170
|
}
|
|
52720
53171
|
|
|
52721
53172
|
// src/commands/attach.ts
|
|
52722
|
-
import { join as
|
|
53173
|
+
import { join as join39 } from "path";
|
|
52723
53174
|
|
|
52724
53175
|
// src/core/control-client.ts
|
|
52725
53176
|
import { connect as connect2 } from "net";
|
|
52726
53177
|
function connectControl(socketPath, bus, token) {
|
|
52727
|
-
return new Promise((
|
|
53178
|
+
return new Promise((resolve13, reject) => {
|
|
52728
53179
|
const socket = connect2(socketPath);
|
|
52729
53180
|
socket.setEncoding("utf8");
|
|
52730
53181
|
let ready = false;
|
|
@@ -52777,7 +53228,7 @@ function connectControl(socketPath, bus, token) {
|
|
|
52777
53228
|
bus.setTokens(frame.tokens);
|
|
52778
53229
|
for (const entry of frame.transcript) bus.pushEntry(entry);
|
|
52779
53230
|
ready = true;
|
|
52780
|
-
|
|
53231
|
+
resolve13({ info: frame.info, close });
|
|
52781
53232
|
break;
|
|
52782
53233
|
case "transcript":
|
|
52783
53234
|
bus.pushEntry(frame.entry);
|
|
@@ -52828,7 +53279,7 @@ async function runAttach(profileDir2, profileName) {
|
|
|
52828
53279
|
process.exitCode = 1;
|
|
52829
53280
|
return;
|
|
52830
53281
|
}
|
|
52831
|
-
const socketPath =
|
|
53282
|
+
const socketPath = join39(profileDir2, CONTROL_SOCKET_FILE);
|
|
52832
53283
|
const bus = new SessionBus();
|
|
52833
53284
|
let conn;
|
|
52834
53285
|
try {
|
|
@@ -52859,13 +53310,13 @@ async function runAttach(profileDir2, profileName) {
|
|
|
52859
53310
|
|
|
52860
53311
|
// src/commands/auto-run.ts
|
|
52861
53312
|
import { connect as connect3 } from "net";
|
|
52862
|
-
import { join as
|
|
53313
|
+
import { join as join40 } from "path";
|
|
52863
53314
|
var AUTO_RUN_TIMEOUT_MS = 3 * 60 * 60 * 1e3;
|
|
52864
53315
|
async function runAutoRun(profileDir2, profileName) {
|
|
52865
53316
|
initI18n(readProfileConfig(profileDir2).locale);
|
|
52866
|
-
const socketPath =
|
|
53317
|
+
const socketPath = join40(profileDir2, CONTROL_SOCKET_FILE);
|
|
52867
53318
|
const token = readControlToken(profileDir2);
|
|
52868
|
-
const code = await new Promise((
|
|
53319
|
+
const code = await new Promise((resolve13) => {
|
|
52869
53320
|
let settled = false;
|
|
52870
53321
|
const done = (c) => {
|
|
52871
53322
|
if (settled) return;
|
|
@@ -52875,7 +53326,7 @@ async function runAutoRun(profileDir2, profileName) {
|
|
|
52875
53326
|
socket.destroy();
|
|
52876
53327
|
} catch {
|
|
52877
53328
|
}
|
|
52878
|
-
|
|
53329
|
+
resolve13(c);
|
|
52879
53330
|
};
|
|
52880
53331
|
const socket = connect3(socketPath);
|
|
52881
53332
|
socket.setEncoding("utf8");
|
|
@@ -52920,22 +53371,22 @@ async function runAutoRun(profileDir2, profileName) {
|
|
|
52920
53371
|
|
|
52921
53372
|
// src/commands/git-credential.ts
|
|
52922
53373
|
import { connect as connect4 } from "net";
|
|
52923
|
-
import { join as
|
|
53374
|
+
import { join as join41 } from "path";
|
|
52924
53375
|
var REQUEST_TIMEOUT_MS = 2e4;
|
|
52925
53376
|
function requestToken(host, path) {
|
|
52926
53377
|
const profileDir2 = process.env[PROFILE_DIR_ENV];
|
|
52927
53378
|
if (!profileDir2) return Promise.resolve(null);
|
|
52928
53379
|
const token = readControlToken(profileDir2);
|
|
52929
|
-
return new Promise((
|
|
53380
|
+
return new Promise((resolve13) => {
|
|
52930
53381
|
let done = false;
|
|
52931
53382
|
const finish = (value) => {
|
|
52932
53383
|
if (done) return;
|
|
52933
53384
|
done = true;
|
|
52934
53385
|
clearTimeout(timer);
|
|
52935
53386
|
socket.destroy();
|
|
52936
|
-
|
|
53387
|
+
resolve13(value);
|
|
52937
53388
|
};
|
|
52938
|
-
const socket = connect4(
|
|
53389
|
+
const socket = connect4(join41(profileDir2, CONTROL_SOCKET_FILE));
|
|
52939
53390
|
socket.setEncoding("utf8");
|
|
52940
53391
|
const timer = setTimeout(() => finish(null), REQUEST_TIMEOUT_MS);
|
|
52941
53392
|
socket.on("connect", () => {
|
|
@@ -52993,16 +53444,16 @@ import { spawn as spawn12 } from "child_process";
|
|
|
52993
53444
|
import { mkdirSync as mkdirSync10 } from "fs";
|
|
52994
53445
|
import { basename as basename8 } from "path";
|
|
52995
53446
|
function runProviderLogin(cmd, envVar, dir2) {
|
|
52996
|
-
return new Promise((
|
|
53447
|
+
return new Promise((resolve13) => {
|
|
52997
53448
|
const child = spawn12(cmd, [], {
|
|
52998
53449
|
stdio: "inherit",
|
|
52999
53450
|
env: { ...process.env, [envVar]: dir2 }
|
|
53000
53451
|
});
|
|
53001
53452
|
child.on("error", (err) => {
|
|
53002
53453
|
console.error(t("aiLogin.launchFailed", { cmd, message: err.message }));
|
|
53003
|
-
|
|
53454
|
+
resolve13(1);
|
|
53004
53455
|
});
|
|
53005
|
-
child.on("exit", (code) =>
|
|
53456
|
+
child.on("exit", (code) => resolve13(code ?? 0));
|
|
53006
53457
|
});
|
|
53007
53458
|
}
|
|
53008
53459
|
async function runAiLogin(profileDir2, profileName, aiFilter) {
|
|
@@ -53142,10 +53593,10 @@ function Picker({
|
|
|
53142
53593
|
] });
|
|
53143
53594
|
}
|
|
53144
53595
|
function selectFromList(items, title) {
|
|
53145
|
-
return new Promise((
|
|
53596
|
+
return new Promise((resolve13) => {
|
|
53146
53597
|
let chosen = null;
|
|
53147
53598
|
const app = render_default(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Picker, { items, title, onDone: (v2) => chosen = v2 }));
|
|
53148
|
-
void app.waitUntilExit().then(() =>
|
|
53599
|
+
void app.waitUntilExit().then(() => resolve13(chosen));
|
|
53149
53600
|
});
|
|
53150
53601
|
}
|
|
53151
53602
|
|