@primitive.ai/prim 0.1.0-alpha.92 → 0.1.0-alpha.93
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/README.md +34 -2
- package/dist/{chunk-67VNLLBN.js → chunk-2GP5UFV7.js} +1 -0
- package/dist/{chunk-ADUTW66W.js → chunk-36TWVNBE.js} +6 -6
- package/dist/{chunk-5HRR4AOR.js → chunk-C3VAA5CG.js} +1 -1
- package/dist/{chunk-LGGMJYY4.js → chunk-DCJN6LZH.js} +37 -5
- package/dist/{chunk-M6RIJVRE.js → chunk-DSWT6BCC.js} +78 -4
- package/dist/{chunk-FBAYCS3X.js → chunk-DXJQR4QI.js} +150 -38
- package/dist/{chunk-PCVGBE5B.js → chunk-G2VMA2QO.js} +2 -1
- package/dist/{chunk-NAHHFSLH.js → chunk-ICM2XCXS.js} +2 -2
- package/dist/{chunk-OAAP2MY6.js → chunk-K7D2EYBS.js} +2 -2
- package/dist/{chunk-5AFRMOPX.js → chunk-OH3EZXYX.js} +1 -1
- package/dist/{chunk-KFXF2V5G.js → chunk-OTHKG76J.js} +1 -1
- package/dist/chunk-OYHYCK4K.js +13 -0
- package/dist/{chunk-FKXFUUWJ.js → chunk-TYAHC25F.js} +1 -1
- package/dist/{chunk-5GYQ4J3D.js → chunk-UKUAAAXE.js} +2 -2
- package/dist/{chunk-26P57LAR.js → chunk-X64HTHUF.js} +3 -1
- package/dist/{chunk-WX52U7Z2.js → chunk-ZU563PQI.js} +58 -2
- package/dist/daemon/server.js +372 -123
- package/dist/hook-runtime/daemon/server.js +372 -123
- package/dist/hook-runtime/hooks/post-commit.js +57 -39
- package/dist/hook-runtime/hooks/post-rewrite.js +58 -40
- package/dist/hook-runtime/hooks/post-tool-use.js +95 -62
- package/dist/hook-runtime/hooks/pre-tool-use.js +32 -10
- package/dist/hook-runtime/hooks/prim-hook.js +142 -102
- package/dist/hook-runtime/hooks/session-start.js +37 -3
- package/dist/hook-runtime/index.js +1264 -523
- package/dist/hook-runtime/statusline-main.js +23 -1
- package/dist/hooks/post-commit.js +11 -8
- package/dist/hooks/post-rewrite.js +11 -8
- package/dist/hooks/post-tool-use.js +13 -15
- package/dist/hooks/pre-commit.js +4 -4
- package/dist/hooks/pre-tool-use.js +10 -12
- package/dist/hooks/prim-hook.js +17 -14
- package/dist/hooks/session-end.js +4 -4
- package/dist/hooks/session-start.js +12 -36
- package/dist/index.js +857 -290
- package/dist/statusline-main.js +23 -1
- package/package.json +1 -1
- package/dist/chunk-BTNNEH2W.js +0 -79
|
@@ -3035,7 +3035,7 @@ var require_commander = __commonJS({
|
|
|
3035
3035
|
var require_polyfills = __commonJS({
|
|
3036
3036
|
"node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/polyfills.js"(exports, module) {
|
|
3037
3037
|
"use strict";
|
|
3038
|
-
var
|
|
3038
|
+
var constants9 = __require("constants");
|
|
3039
3039
|
var origCwd = process.cwd;
|
|
3040
3040
|
var cwd = null;
|
|
3041
3041
|
var platform2 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
|
|
@@ -3059,7 +3059,7 @@ var require_polyfills = __commonJS({
|
|
|
3059
3059
|
var chdir;
|
|
3060
3060
|
module.exports = patch;
|
|
3061
3061
|
function patch(fs5) {
|
|
3062
|
-
if (
|
|
3062
|
+
if (constants9.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
|
|
3063
3063
|
patchLchmod(fs5);
|
|
3064
3064
|
}
|
|
3065
3065
|
if (!fs5.lutimes) {
|
|
@@ -3161,7 +3161,7 @@ var require_polyfills = __commonJS({
|
|
|
3161
3161
|
fs6.lchmod = function(path9, mode, callback) {
|
|
3162
3162
|
fs6.open(
|
|
3163
3163
|
path9,
|
|
3164
|
-
|
|
3164
|
+
constants9.O_WRONLY | constants9.O_SYMLINK,
|
|
3165
3165
|
mode,
|
|
3166
3166
|
function(err, fd) {
|
|
3167
3167
|
if (err) {
|
|
@@ -3177,7 +3177,7 @@ var require_polyfills = __commonJS({
|
|
|
3177
3177
|
);
|
|
3178
3178
|
};
|
|
3179
3179
|
fs6.lchmodSync = function(path9, mode) {
|
|
3180
|
-
var fd = fs6.openSync(path9,
|
|
3180
|
+
var fd = fs6.openSync(path9, constants9.O_WRONLY | constants9.O_SYMLINK, mode);
|
|
3181
3181
|
var threw = true;
|
|
3182
3182
|
var ret;
|
|
3183
3183
|
try {
|
|
@@ -3197,9 +3197,9 @@ var require_polyfills = __commonJS({
|
|
|
3197
3197
|
};
|
|
3198
3198
|
}
|
|
3199
3199
|
function patchLutimes(fs6) {
|
|
3200
|
-
if (
|
|
3200
|
+
if (constants9.hasOwnProperty("O_SYMLINK") && fs6.futimes) {
|
|
3201
3201
|
fs6.lutimes = function(path9, at, mt, cb) {
|
|
3202
|
-
fs6.open(path9,
|
|
3202
|
+
fs6.open(path9, constants9.O_SYMLINK, function(er, fd) {
|
|
3203
3203
|
if (er) {
|
|
3204
3204
|
if (cb) cb(er);
|
|
3205
3205
|
return;
|
|
@@ -3212,7 +3212,7 @@ var require_polyfills = __commonJS({
|
|
|
3212
3212
|
});
|
|
3213
3213
|
};
|
|
3214
3214
|
fs6.lutimesSync = function(path9, at, mt) {
|
|
3215
|
-
var fd = fs6.openSync(path9,
|
|
3215
|
+
var fd = fs6.openSync(path9, constants9.O_SYMLINK);
|
|
3216
3216
|
var ret;
|
|
3217
3217
|
var threw = true;
|
|
3218
3218
|
try {
|
|
@@ -3495,14 +3495,14 @@ var require_graceful_fs = __commonJS({
|
|
|
3495
3495
|
return close;
|
|
3496
3496
|
})(fs5.close);
|
|
3497
3497
|
fs5.closeSync = (function(fs$closeSync) {
|
|
3498
|
-
function
|
|
3498
|
+
function closeSync9(fd) {
|
|
3499
3499
|
fs$closeSync.apply(fs5, arguments);
|
|
3500
3500
|
resetQueue();
|
|
3501
3501
|
}
|
|
3502
|
-
Object.defineProperty(
|
|
3502
|
+
Object.defineProperty(closeSync9, previousSymbol, {
|
|
3503
3503
|
value: fs$closeSync
|
|
3504
3504
|
});
|
|
3505
|
-
return
|
|
3505
|
+
return closeSync9;
|
|
3506
3506
|
})(fs5.closeSync);
|
|
3507
3507
|
if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) {
|
|
3508
3508
|
process.on("exit", function() {
|
|
@@ -4963,7 +4963,7 @@ var require_rc = __commonJS({
|
|
|
4963
4963
|
"node_modules/.pnpm/rc@1.2.8/node_modules/rc/index.js"(exports, module) {
|
|
4964
4964
|
"use strict";
|
|
4965
4965
|
var cc = require_utils();
|
|
4966
|
-
var
|
|
4966
|
+
var join30 = __require("path").join;
|
|
4967
4967
|
var deepExtend = require_deep_extend();
|
|
4968
4968
|
var etc = "/etc";
|
|
4969
4969
|
var win = process.platform === "win32";
|
|
@@ -4988,15 +4988,15 @@ var require_rc = __commonJS({
|
|
|
4988
4988
|
}
|
|
4989
4989
|
if (!win)
|
|
4990
4990
|
[
|
|
4991
|
-
|
|
4992
|
-
|
|
4991
|
+
join30(etc, name, "config"),
|
|
4992
|
+
join30(etc, name + "rc")
|
|
4993
4993
|
].forEach(addConfigFile);
|
|
4994
4994
|
if (home)
|
|
4995
4995
|
[
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
4996
|
+
join30(home, ".config", name, "config"),
|
|
4997
|
+
join30(home, ".config", name),
|
|
4998
|
+
join30(home, "." + name, "config"),
|
|
4999
|
+
join30(home, "." + name + "rc")
|
|
5000
5000
|
].forEach(addConfigFile);
|
|
5001
5001
|
addConfigFile(cc.find("." + name + "rc"));
|
|
5002
5002
|
if (env4.config) addConfigFile(env4.config);
|
|
@@ -5014,7 +5014,7 @@ var require_rc = __commonJS({
|
|
|
5014
5014
|
var require_polyfills2 = __commonJS({
|
|
5015
5015
|
"node_modules/.pnpm/graceful-fs@4.2.10/node_modules/graceful-fs/polyfills.js"(exports, module) {
|
|
5016
5016
|
"use strict";
|
|
5017
|
-
var
|
|
5017
|
+
var constants9 = __require("constants");
|
|
5018
5018
|
var origCwd = process.cwd;
|
|
5019
5019
|
var cwd = null;
|
|
5020
5020
|
var platform2 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
|
|
@@ -5038,7 +5038,7 @@ var require_polyfills2 = __commonJS({
|
|
|
5038
5038
|
var chdir;
|
|
5039
5039
|
module.exports = patch;
|
|
5040
5040
|
function patch(fs5) {
|
|
5041
|
-
if (
|
|
5041
|
+
if (constants9.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
|
|
5042
5042
|
patchLchmod(fs5);
|
|
5043
5043
|
}
|
|
5044
5044
|
if (!fs5.lutimes) {
|
|
@@ -5140,7 +5140,7 @@ var require_polyfills2 = __commonJS({
|
|
|
5140
5140
|
fs6.lchmod = function(path9, mode, callback) {
|
|
5141
5141
|
fs6.open(
|
|
5142
5142
|
path9,
|
|
5143
|
-
|
|
5143
|
+
constants9.O_WRONLY | constants9.O_SYMLINK,
|
|
5144
5144
|
mode,
|
|
5145
5145
|
function(err, fd) {
|
|
5146
5146
|
if (err) {
|
|
@@ -5156,7 +5156,7 @@ var require_polyfills2 = __commonJS({
|
|
|
5156
5156
|
);
|
|
5157
5157
|
};
|
|
5158
5158
|
fs6.lchmodSync = function(path9, mode) {
|
|
5159
|
-
var fd = fs6.openSync(path9,
|
|
5159
|
+
var fd = fs6.openSync(path9, constants9.O_WRONLY | constants9.O_SYMLINK, mode);
|
|
5160
5160
|
var threw = true;
|
|
5161
5161
|
var ret;
|
|
5162
5162
|
try {
|
|
@@ -5176,9 +5176,9 @@ var require_polyfills2 = __commonJS({
|
|
|
5176
5176
|
};
|
|
5177
5177
|
}
|
|
5178
5178
|
function patchLutimes(fs6) {
|
|
5179
|
-
if (
|
|
5179
|
+
if (constants9.hasOwnProperty("O_SYMLINK") && fs6.futimes) {
|
|
5180
5180
|
fs6.lutimes = function(path9, at, mt, cb) {
|
|
5181
|
-
fs6.open(path9,
|
|
5181
|
+
fs6.open(path9, constants9.O_SYMLINK, function(er, fd) {
|
|
5182
5182
|
if (er) {
|
|
5183
5183
|
if (cb) cb(er);
|
|
5184
5184
|
return;
|
|
@@ -5191,7 +5191,7 @@ var require_polyfills2 = __commonJS({
|
|
|
5191
5191
|
});
|
|
5192
5192
|
};
|
|
5193
5193
|
fs6.lutimesSync = function(path9, at, mt) {
|
|
5194
|
-
var fd = fs6.openSync(path9,
|
|
5194
|
+
var fd = fs6.openSync(path9, constants9.O_SYMLINK);
|
|
5195
5195
|
var ret;
|
|
5196
5196
|
var threw = true;
|
|
5197
5197
|
try {
|
|
@@ -5474,14 +5474,14 @@ var require_graceful_fs2 = __commonJS({
|
|
|
5474
5474
|
return close;
|
|
5475
5475
|
})(fs5.close);
|
|
5476
5476
|
fs5.closeSync = (function(fs$closeSync) {
|
|
5477
|
-
function
|
|
5477
|
+
function closeSync9(fd) {
|
|
5478
5478
|
fs$closeSync.apply(fs5, arguments);
|
|
5479
5479
|
resetQueue();
|
|
5480
5480
|
}
|
|
5481
|
-
Object.defineProperty(
|
|
5481
|
+
Object.defineProperty(closeSync9, previousSymbol, {
|
|
5482
5482
|
value: fs$closeSync
|
|
5483
5483
|
});
|
|
5484
|
-
return
|
|
5484
|
+
return closeSync9;
|
|
5485
5485
|
})(fs5.closeSync);
|
|
5486
5486
|
if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) {
|
|
5487
5487
|
process.on("exit", function() {
|
|
@@ -6340,11 +6340,11 @@ var require_util = __commonJS({
|
|
|
6340
6340
|
if (files.includes("node_modules") || files.includes("package.json") || files.includes("package.json5") || files.includes("package.yaml") || files.includes("pnpm-workspace.yaml")) {
|
|
6341
6341
|
return name2;
|
|
6342
6342
|
}
|
|
6343
|
-
const
|
|
6344
|
-
if (
|
|
6343
|
+
const dirname21 = path9.dirname(name2);
|
|
6344
|
+
if (dirname21 === name2) {
|
|
6345
6345
|
return original;
|
|
6346
6346
|
}
|
|
6347
|
-
return find(
|
|
6347
|
+
return find(dirname21, original);
|
|
6348
6348
|
} catch (error) {
|
|
6349
6349
|
if (name2 === original) {
|
|
6350
6350
|
if (error.code === "ENOENT") {
|
|
@@ -8313,7 +8313,7 @@ var require_semver2 = __commonJS({
|
|
|
8313
8313
|
"node_modules/.pnpm/semver@7.7.4/node_modules/semver/index.js"(exports, module) {
|
|
8314
8314
|
"use strict";
|
|
8315
8315
|
var internalRe = require_re();
|
|
8316
|
-
var
|
|
8316
|
+
var constants9 = require_constants();
|
|
8317
8317
|
var SemVer = require_semver();
|
|
8318
8318
|
var identifiers = require_identifiers();
|
|
8319
8319
|
var parse2 = require_parse();
|
|
@@ -8395,8 +8395,8 @@ var require_semver2 = __commonJS({
|
|
|
8395
8395
|
re: internalRe.re,
|
|
8396
8396
|
src: internalRe.src,
|
|
8397
8397
|
tokens: internalRe.t,
|
|
8398
|
-
SEMVER_SPEC_VERSION:
|
|
8399
|
-
RELEASE_TYPES:
|
|
8398
|
+
SEMVER_SPEC_VERSION: constants9.SEMVER_SPEC_VERSION,
|
|
8399
|
+
RELEASE_TYPES: constants9.RELEASE_TYPES,
|
|
8400
8400
|
compareIdentifiers: identifiers.compareIdentifiers,
|
|
8401
8401
|
rcompareIdentifiers: identifiers.rcompareIdentifiers
|
|
8402
8402
|
};
|
|
@@ -8896,8 +8896,8 @@ var require_ucs2length = __commonJS({
|
|
|
8896
8896
|
});
|
|
8897
8897
|
|
|
8898
8898
|
// src/index.ts
|
|
8899
|
-
import { readFileSync as
|
|
8900
|
-
import { dirname as
|
|
8899
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
8900
|
+
import { dirname as dirname20, resolve as resolve10 } from "path";
|
|
8901
8901
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
8902
8902
|
|
|
8903
8903
|
// node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
|
|
@@ -9257,14 +9257,14 @@ var Temp = {
|
|
|
9257
9257
|
}
|
|
9258
9258
|
},
|
|
9259
9259
|
truncate: (filePath) => {
|
|
9260
|
-
const
|
|
9261
|
-
if (
|
|
9260
|
+
const basename5 = path2.basename(filePath);
|
|
9261
|
+
if (basename5.length <= LIMIT_BASENAME_LENGTH)
|
|
9262
9262
|
return filePath;
|
|
9263
|
-
const truncable = /^(\.?)(.*?)((?:\.[^.]+)?(?:\.tmp-\d{10}[a-f0-9]{6})?)$/.exec(
|
|
9263
|
+
const truncable = /^(\.?)(.*?)((?:\.[^.]+)?(?:\.tmp-\d{10}[a-f0-9]{6})?)$/.exec(basename5);
|
|
9264
9264
|
if (!truncable)
|
|
9265
9265
|
return filePath;
|
|
9266
|
-
const truncationLength =
|
|
9267
|
-
return `${filePath.slice(0, -
|
|
9266
|
+
const truncationLength = basename5.length - LIMIT_BASENAME_LENGTH;
|
|
9267
|
+
return `${filePath.slice(0, -basename5.length)}${truncable[1]}${truncable[2].slice(0, -truncationLength)}${truncable[3]}`;
|
|
9268
9268
|
}
|
|
9269
9269
|
};
|
|
9270
9270
|
node_default(Temp.purgeSyncAll);
|
|
@@ -12398,13 +12398,13 @@ function sameIssuerUrl(value, field, issuer) {
|
|
|
12398
12398
|
return parsed.toString();
|
|
12399
12399
|
}
|
|
12400
12400
|
function parseWorkosConnectConfiguration(value) {
|
|
12401
|
-
const
|
|
12401
|
+
const record3 = recordFrom(value, "Connect discovery");
|
|
12402
12402
|
const expectedKeys = ["protocol_version", "issuer", "client_id", "default_scopes"];
|
|
12403
|
-
if (
|
|
12403
|
+
if (record3.protocol_version !== 1 || Object.keys(record3).length !== expectedKeys.length || expectedKeys.some((key) => !Object.hasOwn(record3, key)) || !Array.isArray(record3.default_scopes) || record3.default_scopes.length !== WORKOS_CONNECT_DEFAULT_SCOPES.length || record3.default_scopes.some((scope, index) => scope !== WORKOS_CONNECT_DEFAULT_SCOPES[index])) {
|
|
12404
12404
|
throw new WorkosConnectProtocolError("invalid_response", "Connect discovery is invalid");
|
|
12405
12405
|
}
|
|
12406
|
-
const issuer = canonicalIssuer(
|
|
12407
|
-
const clientId = exactString(
|
|
12406
|
+
const issuer = canonicalIssuer(record3.issuer);
|
|
12407
|
+
const clientId = exactString(record3.client_id, "client_id", MAX_CLIENT_ID_LENGTH);
|
|
12408
12408
|
if (!CONNECT_CLIENT_ID_PATTERN.test(clientId)) {
|
|
12409
12409
|
throw new WorkosConnectProtocolError("invalid_response", "Connect discovery is invalid");
|
|
12410
12410
|
}
|
|
@@ -12415,14 +12415,14 @@ function parseWorkosConnectConfiguration(value) {
|
|
|
12415
12415
|
};
|
|
12416
12416
|
}
|
|
12417
12417
|
function parseWorkosConnectCredentialMetadata(value) {
|
|
12418
|
-
const
|
|
12419
|
-
if (
|
|
12418
|
+
const record3 = recordFrom(value, "credential metadata");
|
|
12419
|
+
if (record3.version !== 1 || record3.family !== "workos_connect") {
|
|
12420
12420
|
throw new WorkosConnectProtocolError("invalid_metadata", "credential metadata is invalid");
|
|
12421
12421
|
}
|
|
12422
|
-
const issuer = canonicalIssuer(
|
|
12423
|
-
const clientId = exactString(
|
|
12424
|
-
const accessTokenHash = exactString(
|
|
12425
|
-
const refreshTokenHash = exactString(
|
|
12422
|
+
const issuer = canonicalIssuer(record3.issuer);
|
|
12423
|
+
const clientId = exactString(record3.clientId, "clientId", MAX_CLIENT_ID_LENGTH);
|
|
12424
|
+
const accessTokenHash = exactString(record3.accessTokenHash, "accessTokenHash", 64);
|
|
12425
|
+
const refreshTokenHash = exactString(record3.refreshTokenHash, "refreshTokenHash", 64);
|
|
12426
12426
|
if (!CONNECT_CLIENT_ID_PATTERN.test(clientId)) {
|
|
12427
12427
|
throw new WorkosConnectProtocolError("invalid_metadata", "credential clientId is invalid");
|
|
12428
12428
|
}
|
|
@@ -12526,37 +12526,37 @@ async function requestWorkosDeviceAuthorization(configuration, fetchImpl = fetch
|
|
|
12526
12526
|
`Device authorization returned HTTP ${response.status}`
|
|
12527
12527
|
);
|
|
12528
12528
|
}
|
|
12529
|
-
const
|
|
12529
|
+
const record3 = recordFrom(value, "device authorization");
|
|
12530
12530
|
const verificationUri = sameIssuerUrl(
|
|
12531
|
-
|
|
12531
|
+
record3.verification_uri,
|
|
12532
12532
|
"verification_uri",
|
|
12533
12533
|
configuration.issuer
|
|
12534
12534
|
);
|
|
12535
12535
|
return {
|
|
12536
|
-
deviceCode: exactString(
|
|
12537
|
-
userCode: exactString(
|
|
12536
|
+
deviceCode: exactString(record3.device_code, "device_code", MAX_DEVICE_CODE_LENGTH),
|
|
12537
|
+
userCode: exactString(record3.user_code, "user_code", MAX_USER_CODE_LENGTH),
|
|
12538
12538
|
verificationUri,
|
|
12539
|
-
verificationUriComplete:
|
|
12540
|
-
|
|
12539
|
+
verificationUriComplete: record3.verification_uri_complete === void 0 ? void 0 : sameIssuerUrl(
|
|
12540
|
+
record3.verification_uri_complete,
|
|
12541
12541
|
"verification_uri_complete",
|
|
12542
12542
|
configuration.issuer
|
|
12543
12543
|
),
|
|
12544
|
-
expiresIn: safeInteger(
|
|
12545
|
-
interval: safeInteger(
|
|
12544
|
+
expiresIn: safeInteger(record3.expires_in, "expires_in", 1, MAX_DEVICE_LIFETIME_SECONDS),
|
|
12545
|
+
interval: safeInteger(record3.interval ?? 5, "interval", 1, MAX_INTERVAL_SECONDS)
|
|
12546
12546
|
};
|
|
12547
12547
|
}
|
|
12548
12548
|
function parseWorkosConnectTokens(value, options = {}) {
|
|
12549
|
-
const
|
|
12549
|
+
const record3 = recordFrom(value, "token response");
|
|
12550
12550
|
const accessToken = exactString(
|
|
12551
|
-
|
|
12551
|
+
record3.access_token,
|
|
12552
12552
|
"access_token",
|
|
12553
12553
|
WORKOS_CONNECT_RESPONSE_MAX_BYTES
|
|
12554
12554
|
);
|
|
12555
|
-
const refreshToken2 =
|
|
12555
|
+
const refreshToken2 = record3.refresh_token === void 0 && options.fallbackRefreshToken !== void 0 ? options.fallbackRefreshToken : exactString(record3.refresh_token, "refresh_token", WORKOS_CONNECT_RESPONSE_MAX_BYTES);
|
|
12556
12556
|
return {
|
|
12557
12557
|
accessToken,
|
|
12558
12558
|
refreshToken: refreshToken2,
|
|
12559
|
-
expiresIn:
|
|
12559
|
+
expiresIn: record3.expires_in === void 0 ? void 0 : safeInteger(record3.expires_in, "expires_in", 1, MAX_TOKEN_LIFETIME_SECONDS)
|
|
12560
12560
|
};
|
|
12561
12561
|
}
|
|
12562
12562
|
function providerError(value) {
|
|
@@ -12678,15 +12678,15 @@ function parseLegacyBrokerCredentialMetadata(value) {
|
|
|
12678
12678
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
12679
12679
|
throw new Error("credential family is invalid");
|
|
12680
12680
|
}
|
|
12681
|
-
const
|
|
12682
|
-
if (
|
|
12681
|
+
const record3 = value;
|
|
12682
|
+
if (record3.version !== 1 || record3.family !== "legacy_broker" || typeof record3.accessTokenHash !== "string" || typeof record3.refreshTokenHash !== "string" || !/^[a-f0-9]{64}$/u.test(record3.accessTokenHash) || !/^[a-f0-9]{64}$/u.test(record3.refreshTokenHash)) {
|
|
12683
12683
|
throw new Error("credential family is invalid");
|
|
12684
12684
|
}
|
|
12685
12685
|
return {
|
|
12686
12686
|
version: 1,
|
|
12687
12687
|
family: "legacy_broker",
|
|
12688
|
-
accessTokenHash:
|
|
12689
|
-
refreshTokenHash:
|
|
12688
|
+
accessTokenHash: record3.accessTokenHash,
|
|
12689
|
+
refreshTokenHash: record3.refreshTokenHash
|
|
12690
12690
|
};
|
|
12691
12691
|
}
|
|
12692
12692
|
function readBoundedCredentialJson(path9) {
|
|
@@ -12721,9 +12721,9 @@ function readCredentialFamily(path9) {
|
|
|
12721
12721
|
}
|
|
12722
12722
|
function isCredentialMigrationSentinel(value) {
|
|
12723
12723
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
12724
|
-
const
|
|
12725
|
-
const keys = Object.keys(
|
|
12726
|
-
return keys.length === 2 && keys[0] === "state" && keys[1] === "version" &&
|
|
12724
|
+
const record3 = value;
|
|
12725
|
+
const keys = Object.keys(record3).sort();
|
|
12726
|
+
return keys.length === 2 && keys[0] === "state" && keys[1] === "version" && record3.version === CREDENTIAL_MIGRATION_VERSION && record3.state === CREDENTIAL_MIGRATION_STATE;
|
|
12727
12727
|
}
|
|
12728
12728
|
function readCredentialMigrationState(path9 = CREDENTIAL_MIGRATION_PATH) {
|
|
12729
12729
|
const parsed = readBoundedCredentialJson(path9);
|
|
@@ -13837,10 +13837,10 @@ async function performTokenRefresh(options = {}) {
|
|
|
13837
13837
|
}
|
|
13838
13838
|
return void 0;
|
|
13839
13839
|
}
|
|
13840
|
-
const
|
|
13841
|
-
accessToken = typeof
|
|
13842
|
-
replacementRefreshToken = typeof
|
|
13843
|
-
expiresIn = typeof
|
|
13840
|
+
const record3 = typeof data === "object" && data !== null && !Array.isArray(data) ? data : void 0;
|
|
13841
|
+
accessToken = typeof record3?.access_token === "string" ? record3.access_token.trim() : "";
|
|
13842
|
+
replacementRefreshToken = typeof record3?.refresh_token === "string" ? record3.refresh_token.trim() : "";
|
|
13843
|
+
expiresIn = typeof record3?.expires_in === "number" && Number.isFinite(record3.expires_in) ? record3.expires_in : void 0;
|
|
13844
13844
|
if (!(accessToken && replacementRefreshToken)) {
|
|
13845
13845
|
if (readTrimmed2(REFRESH_TOKEN_PATH) === currentGeneration) {
|
|
13846
13846
|
writeTerminalFingerprint(currentGeneration);
|
|
@@ -31856,19 +31856,19 @@ var defaultDependencies = {
|
|
|
31856
31856
|
function isSafeVersion(value) {
|
|
31857
31857
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
31858
31858
|
}
|
|
31859
|
-
function optionalStringArray(
|
|
31860
|
-
const value =
|
|
31859
|
+
function optionalStringArray(record3, key) {
|
|
31860
|
+
const value = record3[key];
|
|
31861
31861
|
if (value === void 0) return void 0;
|
|
31862
31862
|
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) return void 0;
|
|
31863
31863
|
return [...value];
|
|
31864
31864
|
}
|
|
31865
|
-
function optionalSafeInteger(
|
|
31866
|
-
const value =
|
|
31865
|
+
function optionalSafeInteger(record3, key) {
|
|
31866
|
+
const value = record3[key];
|
|
31867
31867
|
if (value === void 0) return void 0;
|
|
31868
31868
|
return typeof value === "number" && Number.isSafeInteger(value) ? value : void 0;
|
|
31869
31869
|
}
|
|
31870
|
-
function optionalUserScopeMembers(
|
|
31871
|
-
const value =
|
|
31870
|
+
function optionalUserScopeMembers(record3) {
|
|
31871
|
+
const value = record3.users;
|
|
31872
31872
|
if (value === void 0) return void 0;
|
|
31873
31873
|
if (!Array.isArray(value)) return void 0;
|
|
31874
31874
|
const members = [];
|
|
@@ -31897,27 +31897,27 @@ function optionalUserScopeMembers(record2) {
|
|
|
31897
31897
|
}
|
|
31898
31898
|
function policyFromUnknown(value) {
|
|
31899
31899
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
31900
|
-
const
|
|
31901
|
-
if (!isSafeVersion(
|
|
31900
|
+
const record3 = value;
|
|
31901
|
+
if (!isSafeVersion(record3.updatedAt)) return void 0;
|
|
31902
31902
|
const fields = {
|
|
31903
|
-
repositories: optionalStringArray(
|
|
31904
|
-
directories: optionalStringArray(
|
|
31905
|
-
globs: optionalStringArray(
|
|
31906
|
-
branches: optionalStringArray(
|
|
31907
|
-
effectiveFrom: optionalSafeInteger(
|
|
31908
|
-
effectiveUntil: optionalSafeInteger(
|
|
31909
|
-
users: optionalUserScopeMembers(
|
|
31903
|
+
repositories: optionalStringArray(record3, "repositories"),
|
|
31904
|
+
directories: optionalStringArray(record3, "directories"),
|
|
31905
|
+
globs: optionalStringArray(record3, "globs"),
|
|
31906
|
+
branches: optionalStringArray(record3, "branches"),
|
|
31907
|
+
effectiveFrom: optionalSafeInteger(record3, "effectiveFrom"),
|
|
31908
|
+
effectiveUntil: optionalSafeInteger(record3, "effectiveUntil"),
|
|
31909
|
+
users: optionalUserScopeMembers(record3)
|
|
31910
31910
|
};
|
|
31911
31911
|
if (Object.entries(fields).some(
|
|
31912
|
-
([key, parsed]) =>
|
|
31912
|
+
([key, parsed]) => record3[key] !== void 0 && parsed === void 0
|
|
31913
31913
|
)) {
|
|
31914
31914
|
return void 0;
|
|
31915
31915
|
}
|
|
31916
|
-
if (
|
|
31916
|
+
if (record3.effectiveFrom !== void 0 && fields.effectiveFrom === void 0 || record3.effectiveUntil !== void 0 && fields.effectiveUntil === void 0 || fields.effectiveFrom !== void 0 && fields.effectiveUntil !== void 0 && fields.effectiveFrom >= fields.effectiveUntil) {
|
|
31917
31917
|
return void 0;
|
|
31918
31918
|
}
|
|
31919
31919
|
return {
|
|
31920
|
-
updatedAt:
|
|
31920
|
+
updatedAt: record3.updatedAt,
|
|
31921
31921
|
...fields.repositories === void 0 ? {} : { repositories: fields.repositories },
|
|
31922
31922
|
...fields.directories === void 0 ? {} : { directories: fields.directories },
|
|
31923
31923
|
...fields.globs === void 0 ? {} : { globs: fields.globs },
|
|
@@ -33697,18 +33697,18 @@ function parseAuthBrokerConfig(value) {
|
|
|
33697
33697
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
33698
33698
|
throw new Error("broker config must be an object");
|
|
33699
33699
|
}
|
|
33700
|
-
const
|
|
33701
|
-
if (typeof
|
|
33700
|
+
const record3 = value;
|
|
33701
|
+
if (typeof record3.authorization_server !== "string" || !record3.authorization_server.trim()) {
|
|
33702
33702
|
throw new Error("authorization_server is required");
|
|
33703
33703
|
}
|
|
33704
|
-
if (typeof
|
|
33704
|
+
if (typeof record3.client_id !== "string" || !record3.client_id.trim()) {
|
|
33705
33705
|
throw new Error("client_id is required");
|
|
33706
33706
|
}
|
|
33707
|
-
if (!Array.isArray(
|
|
33707
|
+
if (!Array.isArray(record3.default_scopes) || !record3.default_scopes.every((scope) => typeof scope === "string" && scope.length > 0)) {
|
|
33708
33708
|
throw new Error("default_scopes must be a string array");
|
|
33709
33709
|
}
|
|
33710
|
-
const authorizationServer = parseHttpUrl(
|
|
33711
|
-
const rawEndpoint =
|
|
33710
|
+
const authorizationServer = parseHttpUrl(record3.authorization_server, "authorization_server");
|
|
33711
|
+
const rawEndpoint = record3.authorization_endpoint === void 0 ? DEFAULT_WORKOS_AUTHORIZE_URL : record3.authorization_endpoint;
|
|
33712
33712
|
if (typeof rawEndpoint !== "string" || !rawEndpoint.trim()) {
|
|
33713
33713
|
throw new Error("authorization_endpoint must be a URL");
|
|
33714
33714
|
}
|
|
@@ -33719,8 +33719,8 @@ function parseAuthBrokerConfig(value) {
|
|
|
33719
33719
|
return {
|
|
33720
33720
|
authorization_server: authorizationServer.toString(),
|
|
33721
33721
|
authorization_endpoint: authorizationEndpoint.toString(),
|
|
33722
|
-
client_id:
|
|
33723
|
-
default_scopes: [...
|
|
33722
|
+
client_id: record3.client_id.trim(),
|
|
33723
|
+
default_scopes: [...record3.default_scopes]
|
|
33724
33724
|
};
|
|
33725
33725
|
}
|
|
33726
33726
|
async function fetchAuthBrokerConfig(siteUrl, fetchImpl = fetch) {
|
|
@@ -36386,7 +36386,7 @@ function registerCursorCommands(program3) {
|
|
|
36386
36386
|
|
|
36387
36387
|
// src/commands/daemon.ts
|
|
36388
36388
|
import { spawn as spawn3 } from "child_process";
|
|
36389
|
-
import { closeSync as
|
|
36389
|
+
import { closeSync as closeSync4, existsSync as existsSync12, mkdirSync as mkdirSync9, openSync as openSync4, readFileSync as readFileSync13, unlinkSync as unlinkSync3 } from "fs";
|
|
36390
36390
|
import { join as join16 } from "path";
|
|
36391
36391
|
|
|
36392
36392
|
// src/daemon/latest-bootstrap.ts
|
|
@@ -36458,12 +36458,12 @@ async function runLatestDaemonBootstrap(ensureCurrent, options = {}) {
|
|
|
36458
36458
|
import { spawnSync } from "child_process";
|
|
36459
36459
|
import { createHash as createHash8, randomBytes as randomBytes5 } from "crypto";
|
|
36460
36460
|
import {
|
|
36461
|
-
constants as
|
|
36461
|
+
constants as constants5,
|
|
36462
36462
|
accessSync as accessSync2,
|
|
36463
36463
|
chmodSync as chmodSync4,
|
|
36464
36464
|
copyFileSync as copyFileSync2,
|
|
36465
36465
|
existsSync as existsSync11,
|
|
36466
|
-
lstatSync as
|
|
36466
|
+
lstatSync as lstatSync6,
|
|
36467
36467
|
mkdirSync as mkdirSync8,
|
|
36468
36468
|
mkdtempSync as mkdtempSync2,
|
|
36469
36469
|
readFileSync as readFileSync12,
|
|
@@ -36472,16 +36472,53 @@ import {
|
|
|
36472
36472
|
realpathSync as realpathSync3,
|
|
36473
36473
|
renameSync as renameSync4,
|
|
36474
36474
|
rmSync as rmSync5,
|
|
36475
|
+
statSync as statSync3,
|
|
36475
36476
|
symlinkSync,
|
|
36476
36477
|
writeFileSync as writeFileSync7
|
|
36477
36478
|
} from "fs";
|
|
36478
36479
|
import { homedir as homedir8 } from "os";
|
|
36479
36480
|
import { basename as basename3, dirname as dirname10, isAbsolute as isAbsolute7, join as join15, resolve as resolve6 } from "path";
|
|
36480
36481
|
|
|
36482
|
+
// src/lib/bounded-file.ts
|
|
36483
|
+
import { constants as constants4, closeSync as closeSync3, fstatSync, lstatSync as lstatSync5, openSync as openSync3, readSync } from "fs";
|
|
36484
|
+
function readBoundedRegularFile(path9, maxBytes) {
|
|
36485
|
+
let fd;
|
|
36486
|
+
try {
|
|
36487
|
+
const linked = lstatSync5(path9);
|
|
36488
|
+
if (!linked.isFile() || linked.size > maxBytes) return void 0;
|
|
36489
|
+
fd = openSync3(path9, constants4.O_RDONLY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
|
|
36490
|
+
const opened = fstatSync(fd);
|
|
36491
|
+
if (!opened.isFile() || opened.dev !== linked.dev || opened.ino !== linked.ino || opened.size > maxBytes) {
|
|
36492
|
+
return void 0;
|
|
36493
|
+
}
|
|
36494
|
+
const buffer = Buffer.allocUnsafe(opened.size + 1);
|
|
36495
|
+
let length = 0;
|
|
36496
|
+
while (length < buffer.length) {
|
|
36497
|
+
const count = readSync(fd, buffer, length, buffer.length - length, length);
|
|
36498
|
+
if (count === 0) break;
|
|
36499
|
+
length += count;
|
|
36500
|
+
}
|
|
36501
|
+
if (length > opened.size) return void 0;
|
|
36502
|
+
return { text: buffer.subarray(0, length).toString("utf8"), ownerUid: opened.uid };
|
|
36503
|
+
} catch {
|
|
36504
|
+
return void 0;
|
|
36505
|
+
} finally {
|
|
36506
|
+
if (fd !== void 0) {
|
|
36507
|
+
try {
|
|
36508
|
+
closeSync3(fd);
|
|
36509
|
+
} catch {
|
|
36510
|
+
}
|
|
36511
|
+
}
|
|
36512
|
+
}
|
|
36513
|
+
}
|
|
36514
|
+
|
|
36481
36515
|
// src/daemon/env-binding.ts
|
|
36482
36516
|
function normalizeApiUrl(url) {
|
|
36483
36517
|
return url.trim().replace(/\/+$/, "");
|
|
36484
36518
|
}
|
|
36519
|
+
function apiUrlsMatch(a, b) {
|
|
36520
|
+
return normalizeApiUrl(a) === normalizeApiUrl(b);
|
|
36521
|
+
}
|
|
36485
36522
|
|
|
36486
36523
|
// src/daemon/launchd.ts
|
|
36487
36524
|
var LAUNCHD_LABEL = "ai.getprimitive.prim-daemon";
|
|
@@ -36500,6 +36537,11 @@ var SERVICE_NOT_FOUND = 113;
|
|
|
36500
36537
|
var TRANSITION_IN_PROGRESS = 5;
|
|
36501
36538
|
var OUTPUT_LIMIT = 4096;
|
|
36502
36539
|
var DAEMON_DISABLED_CONTENT = "disabled by `prim daemon stop`\n";
|
|
36540
|
+
var LAUNCHER_MAX_BYTES = 64 * 1024;
|
|
36541
|
+
var PLIST_MAX_BYTES = 64 * 1024;
|
|
36542
|
+
var DRIFT_HEAL_MARKER = "daemon-drift-heal.json";
|
|
36543
|
+
var DRIFT_HEAL_MARKER_TEMPORARY_RE = /^daemon-drift-heal\.json\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.tmp$/u;
|
|
36544
|
+
var DRIFT_HEAL_CLAIM_OWNER = "owner.json";
|
|
36503
36545
|
var LaunchctlError = class extends Error {
|
|
36504
36546
|
status;
|
|
36505
36547
|
timedOut;
|
|
@@ -36539,6 +36581,8 @@ function daemonControlPaths(options = {}) {
|
|
|
36539
36581
|
return {
|
|
36540
36582
|
launcher: join15(configDir, `prim-daemon-launcher-v${LAUNCHER_SCHEMA_VERSION}`),
|
|
36541
36583
|
disabledMarker: join15(configDir, "daemon.disabled"),
|
|
36584
|
+
driftHealMarker: join15(configDir, DRIFT_HEAL_MARKER),
|
|
36585
|
+
driftHealClaim: join15(configDir, "daemon-drift-heal.lock"),
|
|
36542
36586
|
lifecycleLockDir: join15(configDir, "daemon.lifecycle.lock"),
|
|
36543
36587
|
legacyDisabledMarker: legacy.disabledMarker,
|
|
36544
36588
|
legacyLifecycleLockDir: legacy.lifecycleLockDir
|
|
@@ -36599,7 +36643,7 @@ function readOwnedRuntimeManifest(releaseDir) {
|
|
|
36599
36643
|
}
|
|
36600
36644
|
const manifest = parsed;
|
|
36601
36645
|
if (manifest.schemaVersion !== RUNTIME_SCHEMA_VERSION || compareSemver(manifest.version, manifest.version) !== 0 || !isAbsolute7(manifest.nodePath) || /[\r\n]/u.test(manifest.nodePath) || manifest.daemonFile !== "prim-daemon-server" || !/^[0-9a-f]{64}$/u.test(manifest.daemonSha256) || new Date(manifest.stagedAt).toISOString() !== manifest.stagedAt || raw !== `${JSON.stringify(manifest, null, 2)}
|
|
36602
|
-
` || !
|
|
36646
|
+
` || !lstatSync6(daemonPath).isFile() || sha256File(daemonPath) !== manifest.daemonSha256) {
|
|
36603
36647
|
return null;
|
|
36604
36648
|
}
|
|
36605
36649
|
const entries = readdirSync3(releaseDir).sort();
|
|
@@ -36608,8 +36652,17 @@ function readOwnedRuntimeManifest(releaseDir) {
|
|
|
36608
36652
|
return null;
|
|
36609
36653
|
}
|
|
36610
36654
|
}
|
|
36655
|
+
function sameNode(recorded, desired) {
|
|
36656
|
+
if (recorded === desired) return true;
|
|
36657
|
+
try {
|
|
36658
|
+
const target = realpathSync3(desired);
|
|
36659
|
+
return recorded !== target && realpathSync3(recorded) === target;
|
|
36660
|
+
} catch {
|
|
36661
|
+
return false;
|
|
36662
|
+
}
|
|
36663
|
+
}
|
|
36611
36664
|
function sameRuntime(current, desired, daemonPath) {
|
|
36612
|
-
if (!current || current.version !== desired.version || current.nodePath
|
|
36665
|
+
if (!current || current.version !== desired.version || !sameNode(current.nodePath, desired.nodePath) || current.daemonSha256 !== desired.daemonSha256) {
|
|
36613
36666
|
return false;
|
|
36614
36667
|
}
|
|
36615
36668
|
try {
|
|
@@ -36637,7 +36690,7 @@ function stageRuntime(options = {}) {
|
|
|
36637
36690
|
if (!daemonSource || !existsSync11(daemonSource)) {
|
|
36638
36691
|
throw new Error("cannot stage runtime: prim-daemon-server bundle is unavailable");
|
|
36639
36692
|
}
|
|
36640
|
-
const nodePath = resolve6(options.nodePath ??
|
|
36693
|
+
const nodePath = resolve6(options.nodePath ?? stableNodePath());
|
|
36641
36694
|
const version = options.version ?? findPackageVersion(daemonSource);
|
|
36642
36695
|
const desired = {
|
|
36643
36696
|
schemaVersion: RUNTIME_SCHEMA_VERSION,
|
|
@@ -36736,13 +36789,30 @@ function assertRegularFile(path9, label) {
|
|
|
36736
36789
|
}
|
|
36737
36790
|
function lstatIfPresent3(path9) {
|
|
36738
36791
|
try {
|
|
36739
|
-
return
|
|
36792
|
+
return lstatSync6(path9);
|
|
36740
36793
|
} catch (error) {
|
|
36741
36794
|
const code = error.code;
|
|
36742
36795
|
if (code === "ENOENT" || code === "ENOTDIR") return void 0;
|
|
36743
36796
|
throw error;
|
|
36744
36797
|
}
|
|
36745
36798
|
}
|
|
36799
|
+
function driftHealMarkerTemporaries(configDir) {
|
|
36800
|
+
try {
|
|
36801
|
+
return readdirSync3(configDir).filter((entry) => DRIFT_HEAL_MARKER_TEMPORARY_RE.test(entry)).map((entry) => join15(configDir, entry));
|
|
36802
|
+
} catch (error) {
|
|
36803
|
+
const code = error.code;
|
|
36804
|
+
if (code === "ENOENT" || code === "ENOTDIR") return [];
|
|
36805
|
+
throw error;
|
|
36806
|
+
}
|
|
36807
|
+
}
|
|
36808
|
+
function assertDriftHealClaim(path9) {
|
|
36809
|
+
const claim = lstatIfPresent3(path9);
|
|
36810
|
+
if (!claim) return;
|
|
36811
|
+
if (!claim.isDirectory() || readdirSync3(path9).some((entry) => entry !== DRIFT_HEAL_CLAIM_OWNER)) {
|
|
36812
|
+
throw new Error(`refusing to remove unrecognized daemon drift-heal claim at ${path9}`);
|
|
36813
|
+
}
|
|
36814
|
+
assertRegularFile(join15(path9, DRIFT_HEAL_CLAIM_OWNER), "daemon drift-heal claim owner");
|
|
36815
|
+
}
|
|
36746
36816
|
function assertExactFile(path9, expected, label) {
|
|
36747
36817
|
if (!lstatIfPresent3(path9)) return;
|
|
36748
36818
|
assertRegularFile(path9, label);
|
|
@@ -36781,17 +36851,16 @@ function assertOwnedDaemonRuntime(options) {
|
|
|
36781
36851
|
);
|
|
36782
36852
|
}
|
|
36783
36853
|
assertRegularFile(logPath, "daemon log");
|
|
36854
|
+
assertRegularFile(control.driftHealMarker, "daemon drift-heal marker");
|
|
36855
|
+
for (const temporary of driftHealMarkerTemporaries(configDir)) {
|
|
36856
|
+
assertRegularFile(temporary, "daemon drift-heal marker temporary");
|
|
36857
|
+
}
|
|
36858
|
+
assertDriftHealClaim(control.driftHealClaim);
|
|
36784
36859
|
assertRegularFile(join15(configDir, "daemon-health.json"), "daemon health snapshot");
|
|
36785
36860
|
assertRegularFile(join15(configDir, "client_instance_id"), "daemon client instance id");
|
|
36786
36861
|
if (service && lstatIfPresent3(service.plistPath)) {
|
|
36787
36862
|
assertRegularFile(service.plistPath, "launchd property list");
|
|
36788
|
-
|
|
36789
|
-
launcherPath: control.launcher,
|
|
36790
|
-
logPath,
|
|
36791
|
-
workingDirectory: options.homeDir ?? homedir8(),
|
|
36792
|
-
label: options.label
|
|
36793
|
-
});
|
|
36794
|
-
if (readFileSync12(service.plistPath, "utf8") !== expected) {
|
|
36863
|
+
if (readFileSync12(service.plistPath, "utf8") !== ownedLaunchAgentPlist(options, logPath)) {
|
|
36795
36864
|
throw new Error(
|
|
36796
36865
|
`refusing to remove unrecognized launchd property list at ${service.plistPath}`
|
|
36797
36866
|
);
|
|
@@ -36868,6 +36937,9 @@ async function removeDaemonRuntime(options = {}) {
|
|
|
36868
36937
|
control.launcher,
|
|
36869
36938
|
control.disabledMarker,
|
|
36870
36939
|
control.legacyDisabledMarker,
|
|
36940
|
+
control.driftHealMarker,
|
|
36941
|
+
...driftHealMarkerTemporaries(configDir),
|
|
36942
|
+
control.driftHealClaim,
|
|
36871
36943
|
...service ? [service.plistPath] : [],
|
|
36872
36944
|
logPath,
|
|
36873
36945
|
join15(configDir, "daemon-health.json"),
|
|
@@ -36879,7 +36951,7 @@ async function removeDaemonRuntime(options = {}) {
|
|
|
36879
36951
|
return withDaemonLifecycleLock(async () => {
|
|
36880
36952
|
assertOwnedDaemonRuntime(options);
|
|
36881
36953
|
for (const path9 of ownedArtifacts.filter((candidate) => candidate !== runtime.runtimeDir)) {
|
|
36882
|
-
rmSync5(path9, { force: true });
|
|
36954
|
+
rmSync5(path9, { force: true, recursive: path9 === control.driftHealClaim });
|
|
36883
36955
|
}
|
|
36884
36956
|
if (lstatIfPresent3(runtime.runtimeDir)) {
|
|
36885
36957
|
const quarantined = `${runtime.runtimeDir}.uninstall-${String(process.pid)}-${randomBytes5(8).toString("hex")}`;
|
|
@@ -36913,9 +36985,22 @@ ${config.configDir ? `export PRIM_CONFIG_DIR=${shellQuote2(config.configDir)}
|
|
|
36913
36985
|
`
|
|
36914
36986
|
};
|
|
36915
36987
|
}
|
|
36988
|
+
function ownedLaunchAgentPlist(options, logPath) {
|
|
36989
|
+
return generateLaunchAgentPlist({
|
|
36990
|
+
launcherPath: daemonControlPaths(options).launcher,
|
|
36991
|
+
logPath,
|
|
36992
|
+
workingDirectory: options.homeDir ?? homedir8(),
|
|
36993
|
+
label: options.label
|
|
36994
|
+
});
|
|
36995
|
+
}
|
|
36916
36996
|
function readDaemonLauncher(path9) {
|
|
36997
|
+
return readDaemonLauncherFile(path9)?.config ?? null;
|
|
36998
|
+
}
|
|
36999
|
+
function readDaemonLauncherFile(path9) {
|
|
36917
37000
|
try {
|
|
36918
|
-
const
|
|
37001
|
+
const file = readBoundedRegularFile(path9, LAUNCHER_MAX_BYTES);
|
|
37002
|
+
if (!file) return null;
|
|
37003
|
+
const content = file.text;
|
|
36919
37004
|
const encoded = /^# prim-daemon-launcher: ([A-Za-z0-9_-]+)$/mu.exec(content)?.[1];
|
|
36920
37005
|
if (!encoded) return null;
|
|
36921
37006
|
const value = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
|
|
@@ -36930,7 +37015,7 @@ function readDaemonLauncher(path9) {
|
|
|
36930
37015
|
...typeof value.configDir === "string" ? { configDir: value.configDir } : {}
|
|
36931
37016
|
};
|
|
36932
37017
|
const expected = generateDaemonLauncher(config);
|
|
36933
|
-
return expected.revision === value.revision && expected.content === content ? config : null;
|
|
37018
|
+
return expected.revision === value.revision && expected.content === content ? { config, ownerUid: file.ownerUid } : null;
|
|
36934
37019
|
} catch {
|
|
36935
37020
|
return null;
|
|
36936
37021
|
}
|
|
@@ -36940,7 +37025,7 @@ function readUsableDaemonConfig(path9) {
|
|
|
36940
37025
|
if (!config) return null;
|
|
36941
37026
|
const manifest = readRuntimeManifest(join15(dirname10(config.daemonPath), "manifest.json"));
|
|
36942
37027
|
try {
|
|
36943
|
-
accessSync2(config.nodePath,
|
|
37028
|
+
accessSync2(config.nodePath, constants5.X_OK);
|
|
36944
37029
|
return manifest?.version === config.runtimeVersion && manifest.nodePath === config.nodePath && manifest.daemonSha256 === sha256File(config.daemonPath) ? config : null;
|
|
36945
37030
|
} catch {
|
|
36946
37031
|
return null;
|
|
@@ -37284,6 +37369,68 @@ function setDaemonExplicitlyDisabled(disabled, options = {}) {
|
|
|
37284
37369
|
}
|
|
37285
37370
|
atomicWrite3(paths.disabledMarker, content, RUNTIME_FILE_MODE);
|
|
37286
37371
|
}
|
|
37372
|
+
function launcherRunnable(config) {
|
|
37373
|
+
try {
|
|
37374
|
+
accessSync2(config.nodePath, constants5.X_OK);
|
|
37375
|
+
return statSync3(config.daemonPath).isFile();
|
|
37376
|
+
} catch {
|
|
37377
|
+
return false;
|
|
37378
|
+
}
|
|
37379
|
+
}
|
|
37380
|
+
function selectedDaemonLauncher(options = {}) {
|
|
37381
|
+
const file = readDaemonLauncherFile(daemonControlPaths(options).launcher);
|
|
37382
|
+
if (!file) return null;
|
|
37383
|
+
const { config, ownerUid } = file;
|
|
37384
|
+
return {
|
|
37385
|
+
runtimeVersion: config.runtimeVersion,
|
|
37386
|
+
...config.apiUrl ? { apiUrl: config.apiUrl } : {},
|
|
37387
|
+
ownerUid,
|
|
37388
|
+
runnable: launcherRunnable(config)
|
|
37389
|
+
};
|
|
37390
|
+
}
|
|
37391
|
+
var XML_ENTITIES = {
|
|
37392
|
+
"&": "&",
|
|
37393
|
+
"<": "<",
|
|
37394
|
+
">": ">",
|
|
37395
|
+
""": '"',
|
|
37396
|
+
"'": "'"
|
|
37397
|
+
};
|
|
37398
|
+
var XML_ENTITY_RE = /&(?:amp|lt|gt|quot|apos);/gu;
|
|
37399
|
+
var PLIST_LABEL_KEY = "<key>Label</key>";
|
|
37400
|
+
var PLIST_PROGRAM_KEY = "<key>ProgramArguments</key>";
|
|
37401
|
+
var PLIST_LABEL_RE = /<key>Label<\/key>\s*<string>([^<]*)<\/string>/u;
|
|
37402
|
+
var PLIST_PROGRAM_RE = /<key>ProgramArguments<\/key>\s*<array>\s*<string>([^<]*)<\/string>/u;
|
|
37403
|
+
function xmlUnescape(value) {
|
|
37404
|
+
if (value.replace(XML_ENTITY_RE, "").includes("&")) return void 0;
|
|
37405
|
+
return value.replace(XML_ENTITY_RE, (entity) => XML_ENTITIES[entity] ?? entity);
|
|
37406
|
+
}
|
|
37407
|
+
function launchAgentProgram(plist) {
|
|
37408
|
+
if (plist.split(PLIST_LABEL_KEY).length !== 2 || plist.split(PLIST_PROGRAM_KEY).length !== 2) {
|
|
37409
|
+
return void 0;
|
|
37410
|
+
}
|
|
37411
|
+
const label = PLIST_LABEL_RE.exec(plist)?.[1];
|
|
37412
|
+
const program3 = PLIST_PROGRAM_RE.exec(plist)?.[1];
|
|
37413
|
+
if (label === void 0 || program3 === void 0) return void 0;
|
|
37414
|
+
const decodedLabel = xmlUnescape(label);
|
|
37415
|
+
const decodedProgram = xmlUnescape(program3);
|
|
37416
|
+
return decodedLabel === void 0 || decodedProgram === void 0 ? void 0 : { label: decodedLabel, program: decodedProgram };
|
|
37417
|
+
}
|
|
37418
|
+
function launchAgentRunsConfigRoot(options = {}) {
|
|
37419
|
+
try {
|
|
37420
|
+
const service = launchdPaths(options);
|
|
37421
|
+
const plist = readBoundedRegularFile(service.plistPath, PLIST_MAX_BYTES);
|
|
37422
|
+
const agent = plist ? launchAgentProgram(plist.text) : void 0;
|
|
37423
|
+
return agent?.label === (options.label ?? LAUNCHD_LABEL) && agent.program === daemonControlPaths(options).launcher;
|
|
37424
|
+
} catch {
|
|
37425
|
+
return false;
|
|
37426
|
+
}
|
|
37427
|
+
}
|
|
37428
|
+
function daemonDriftHealMarkerPath(options = {}) {
|
|
37429
|
+
return daemonControlPaths(options).driftHealMarker;
|
|
37430
|
+
}
|
|
37431
|
+
function daemonDriftHealClaimPath(options = {}) {
|
|
37432
|
+
return daemonControlPaths(options).driftHealClaim;
|
|
37433
|
+
}
|
|
37287
37434
|
async function ensureMacDaemon(options = {}) {
|
|
37288
37435
|
return withDaemonLifecycleLock(() => ensureMacDaemonLocked(options), options);
|
|
37289
37436
|
}
|
|
@@ -37471,6 +37618,54 @@ async function bootoutMacDaemonLocked(options) {
|
|
|
37471
37618
|
return { wasLoaded, legacyStopped, service };
|
|
37472
37619
|
}
|
|
37473
37620
|
|
|
37621
|
+
// src/lib/delivery-backlog.ts
|
|
37622
|
+
var MS_PER_SECOND = 1e3;
|
|
37623
|
+
var MS_PER_MINUTE = 60 * MS_PER_SECOND;
|
|
37624
|
+
var MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
|
37625
|
+
var MS_PER_DAY = 24 * MS_PER_HOUR;
|
|
37626
|
+
function isNonNegativeInteger(value) {
|
|
37627
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
37628
|
+
}
|
|
37629
|
+
function isPositiveInteger(value) {
|
|
37630
|
+
return isNonNegativeInteger(value) && value > 0;
|
|
37631
|
+
}
|
|
37632
|
+
function deliveryBacklogState(ingestion) {
|
|
37633
|
+
if (ingestion?.healthy !== false) return void 0;
|
|
37634
|
+
const failures = ingestion.consecutiveFailures;
|
|
37635
|
+
const failedWithoutProgress = failures !== 0 && !(isPositiveInteger(failures) && isPositiveInteger(ingestion.lastFailedDrainAcknowledgedCount));
|
|
37636
|
+
if (failedWithoutProgress) return "failing";
|
|
37637
|
+
const retained = ingestion.lastRetainedBucketCount;
|
|
37638
|
+
if (!isNonNegativeInteger(retained)) return "failing";
|
|
37639
|
+
return retained > 0 ? "retained" : "draining";
|
|
37640
|
+
}
|
|
37641
|
+
function formatBacklogAge(ageMs) {
|
|
37642
|
+
if (ageMs >= MS_PER_DAY) return `${String(Math.floor(ageMs / MS_PER_DAY))}d`;
|
|
37643
|
+
if (ageMs >= MS_PER_HOUR) return `${String(Math.floor(ageMs / MS_PER_HOUR))}h`;
|
|
37644
|
+
if (ageMs >= MS_PER_MINUTE) return `${String(Math.floor(ageMs / MS_PER_MINUTE))}m`;
|
|
37645
|
+
return `${String(Math.max(0, Math.floor(ageMs / MS_PER_SECOND)))}s`;
|
|
37646
|
+
}
|
|
37647
|
+
function formatPendingBacklog(backlog, now = Date.now()) {
|
|
37648
|
+
const count = backlog.pendingCount;
|
|
37649
|
+
const moves = typeof count === "number" && count > 0 ? `${backlog.pendingSampled ? "at least " : ""}${String(count)} pending move${count === 1 ? "" : "s"}` : "an unknown number of pending moves";
|
|
37650
|
+
const age = backlog.oldestPendingAt === void 0 ? "" : ` (oldest ${backlog.pendingSampled ? "\u2265 " : ""}${formatBacklogAge(now - backlog.oldestPendingAt)})`;
|
|
37651
|
+
return `${moves}${age}`;
|
|
37652
|
+
}
|
|
37653
|
+
function deliveryBacklogSummary(ingestion, now = Date.now(), backlog = ingestion) {
|
|
37654
|
+
const state = deliveryBacklogState(ingestion);
|
|
37655
|
+
if (!(state && ingestion && backlog)) return void 0;
|
|
37656
|
+
const pending = formatPendingBacklog(backlog, now);
|
|
37657
|
+
if (state === "draining") return `draining ${pending} in the background`;
|
|
37658
|
+
if (state === "retained") {
|
|
37659
|
+
const buckets = ingestion.lastRetainedBucketCount ?? 0;
|
|
37660
|
+
const reasons = boundedHealthError(ingestion.lastRetainedReasons);
|
|
37661
|
+
return `delivery held back: ${String(buckets)} journal bucket${buckets === 1 ? "" : "s"} retained${reasons ? ` (${reasons})` : ""} \xB7 holding ${pending} \u2014 run \`prim doctor\``;
|
|
37662
|
+
}
|
|
37663
|
+
const failures = ingestion.consecutiveFailures;
|
|
37664
|
+
const count = isPositiveInteger(failures) ? ` (${String(failures)} consecutive failure${failures === 1 ? "" : "s"})` : "";
|
|
37665
|
+
const error = boundedHealthError(ingestion.lastError);
|
|
37666
|
+
return `delivery failing${count}${error ? `: ${error}` : ""} \xB7 retrying ${pending} in the background`;
|
|
37667
|
+
}
|
|
37668
|
+
|
|
37474
37669
|
// src/lib/presence.ts
|
|
37475
37670
|
var DECISION_ORIGIN = "https://app.getprimitive.ai";
|
|
37476
37671
|
function validDecisionUrl(value) {
|
|
@@ -37545,7 +37740,8 @@ function formatStatusline(version, snapshot, resolveIngestionStatus, options = {
|
|
|
37545
37740
|
if (snapshot.ingestion?.healthy === false) {
|
|
37546
37741
|
const pending = snapshot.ingestion.pendingCount;
|
|
37547
37742
|
const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
|
|
37548
|
-
|
|
37743
|
+
const delivery = deliveryBacklogState(snapshot.ingestion) === "draining" && snapshot.heartbeat?.healthy === true ? "draining" : "stalled";
|
|
37744
|
+
return `primitive ${version} (daemon: degraded \xB7 delivery: ${delivery}${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""}${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
|
|
37549
37745
|
}
|
|
37550
37746
|
if (snapshot.heartbeat?.healthy === false) {
|
|
37551
37747
|
return `primitive ${version} (daemon: degraded \xB7 presence: unavailable${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
|
|
@@ -37588,16 +37784,20 @@ var STATUS_PROBE_TIMEOUT_MS = 500;
|
|
|
37588
37784
|
var READY_TIMEOUT_MS = 5e3;
|
|
37589
37785
|
var READY_POLL_MS2 = 100;
|
|
37590
37786
|
var READY_PROBE_TIMEOUT_MS2 = 250;
|
|
37591
|
-
var
|
|
37592
|
-
var
|
|
37787
|
+
var READY_SNAPSHOT_TIMEOUT_MS = 3e4;
|
|
37788
|
+
var READY_SNAPSHOT_POLL_MS = 250;
|
|
37593
37789
|
var EXIT_OK3 = 0;
|
|
37594
37790
|
var EXIT_NOT_RUNNING = 2;
|
|
37595
37791
|
var EXIT_BOOTING = 3;
|
|
37596
|
-
|
|
37597
|
-
|
|
37792
|
+
var VERIFIED_PREFIX = "[prim] \u2713 ";
|
|
37793
|
+
function formatDaemonLifecycleMessage(message, decisionIngestion, snapshot, now = Date.now()) {
|
|
37794
|
+
const backlog = deliveryBacklogSummary(snapshot?.ingestion, now);
|
|
37795
|
+
const state = deliveryBacklogState(snapshot?.ingestion);
|
|
37796
|
+
const verdict = (state === "failing" || state === "retained") && message.startsWith(VERIFIED_PREFIX) ? `[prim] \u26A0 ${message.slice(VERIFIED_PREFIX.length)}` : message;
|
|
37797
|
+
return `${verdict} \xB7 Decision ingestion ${decisionIngestion}${backlog ? ` \xB7 ${backlog}` : ""}`;
|
|
37598
37798
|
}
|
|
37599
|
-
function formatCurrentDaemonLifecycleMessage(message) {
|
|
37600
|
-
return formatDaemonLifecycleMessage(message, decisionIngestionStatus(process.cwd()));
|
|
37799
|
+
function formatCurrentDaemonLifecycleMessage(message, snapshot) {
|
|
37800
|
+
return formatDaemonLifecycleMessage(message, decisionIngestionStatus(process.cwd()), snapshot);
|
|
37601
37801
|
}
|
|
37602
37802
|
function daemonDegradedReason(snapshot) {
|
|
37603
37803
|
if (!snapshot || snapshot.healthy !== false) return void 0;
|
|
@@ -37648,7 +37848,7 @@ function openDaemonLog(configDir = CONFIG_DIR) {
|
|
|
37648
37848
|
if (!existsSync12(configDir)) {
|
|
37649
37849
|
mkdirSync9(configDir, { recursive: true, mode: CONFIG_DIR_MODE2 });
|
|
37650
37850
|
}
|
|
37651
|
-
return
|
|
37851
|
+
return openSync4(join16(configDir, "daemon.log"), "a", LOG_FILE_MODE);
|
|
37652
37852
|
}
|
|
37653
37853
|
async function waitForReady() {
|
|
37654
37854
|
const deadline = Date.now() + READY_TIMEOUT_MS;
|
|
@@ -37660,17 +37860,23 @@ async function waitForReady() {
|
|
|
37660
37860
|
}
|
|
37661
37861
|
return daemonIsLive(READY_PROBE_TIMEOUT_MS2);
|
|
37662
37862
|
}
|
|
37663
|
-
|
|
37664
|
-
|
|
37863
|
+
function requestStatusSnapshot() {
|
|
37864
|
+
return daemonRequest(
|
|
37865
|
+
"status_snapshot",
|
|
37866
|
+
{},
|
|
37867
|
+
{ timeoutMs: STATUS_PROBE_TIMEOUT_MS }
|
|
37868
|
+
);
|
|
37869
|
+
}
|
|
37870
|
+
async function waitForReadySnapshot(expectedVersion, options = {}) {
|
|
37871
|
+
const request2 = options.requestSnapshot ?? requestStatusSnapshot;
|
|
37872
|
+
const wait = options.sleep ?? sleep;
|
|
37873
|
+
const nowMs = options.nowMs ?? Date.now;
|
|
37874
|
+
const deadline = nowMs() + READY_SNAPSHOT_TIMEOUT_MS;
|
|
37665
37875
|
let snapshot = null;
|
|
37666
|
-
while (
|
|
37667
|
-
snapshot = await
|
|
37668
|
-
|
|
37669
|
-
|
|
37670
|
-
{ timeoutMs: STATUS_PROBE_TIMEOUT_MS }
|
|
37671
|
-
);
|
|
37672
|
-
if (snapshot?.healthy === true && snapshot.version === expectedVersion) return snapshot;
|
|
37673
|
-
await sleep(HEALTHY_POLL_MS);
|
|
37876
|
+
while (nowMs() < deadline) {
|
|
37877
|
+
snapshot = await request2();
|
|
37878
|
+
if (daemonStartIsReady(true, snapshot, expectedVersion)) return snapshot;
|
|
37879
|
+
await wait(READY_SNAPSHOT_POLL_MS);
|
|
37674
37880
|
}
|
|
37675
37881
|
return snapshot;
|
|
37676
37882
|
}
|
|
@@ -37683,11 +37889,20 @@ async function verifiedPid(existing) {
|
|
|
37683
37889
|
);
|
|
37684
37890
|
return snapshot?.pid === existing.pid ? snapshot : null;
|
|
37685
37891
|
}
|
|
37686
|
-
function
|
|
37687
|
-
|
|
37892
|
+
function daemonStartIsReady(serviceReady, snapshot, expectedVersion) {
|
|
37893
|
+
if (!serviceReady || !snapshot || expectedVersion === void 0) return false;
|
|
37894
|
+
if (snapshot.version !== expectedVersion || snapshot.needsReauth === true) return false;
|
|
37895
|
+
if (snapshot.healthy === true) return true;
|
|
37896
|
+
return snapshot.heartbeat?.healthy === true && snapshot.ingestion?.healthy === false;
|
|
37688
37897
|
}
|
|
37689
|
-
function
|
|
37690
|
-
|
|
37898
|
+
function daemonBacklogFields(snapshot) {
|
|
37899
|
+
const state = deliveryBacklogState(snapshot?.ingestion);
|
|
37900
|
+
if (!state) return {};
|
|
37901
|
+
if (state === "draining") return { draining: true, ingestion: snapshot?.ingestion };
|
|
37902
|
+
return state === "retained" ? { draining: false, deliveryRetained: true, ingestion: snapshot?.ingestion } : { draining: false, deliveryFailing: true, ingestion: snapshot?.ingestion };
|
|
37903
|
+
}
|
|
37904
|
+
function daemonStartHealthFields(ready, snapshot) {
|
|
37905
|
+
if (ready) return daemonBacklogFields(snapshot);
|
|
37691
37906
|
return {
|
|
37692
37907
|
state: "degraded",
|
|
37693
37908
|
needsReauth: snapshot?.needsReauth === true,
|
|
@@ -37695,16 +37910,28 @@ function daemonStartHealthFields(healthy, snapshot) {
|
|
|
37695
37910
|
ingestion: snapshot?.ingestion ?? null
|
|
37696
37911
|
};
|
|
37697
37912
|
}
|
|
37913
|
+
function daemonStartFailureReason(snapshot, expectedVersion) {
|
|
37914
|
+
if (snapshot && !snapshot.needsReauth && expectedVersion !== void 0 && snapshot.version !== expectedVersion) {
|
|
37915
|
+
return `daemon reports ${snapshot.version ? `v${snapshot.version}` : "no version"}, expected v${expectedVersion}`;
|
|
37916
|
+
}
|
|
37917
|
+
return daemonDegradedReason(snapshot);
|
|
37918
|
+
}
|
|
37698
37919
|
async function detachedDaemonStart(opts) {
|
|
37699
37920
|
const existing = readPidfile();
|
|
37700
37921
|
if (existing?.alive) {
|
|
37701
37922
|
const snapshot = await verifiedPid(existing);
|
|
37702
37923
|
if (snapshot) {
|
|
37703
37924
|
process.stderr.write(
|
|
37704
|
-
`${formatCurrentDaemonLifecycleMessage(`[prim] daemon already running (pid=${existing.pid})
|
|
37925
|
+
`${formatCurrentDaemonLifecycleMessage(`[prim] daemon already running (pid=${existing.pid})`, snapshot)}
|
|
37705
37926
|
`
|
|
37706
37927
|
);
|
|
37707
|
-
console.log(
|
|
37928
|
+
console.log(
|
|
37929
|
+
JSON.stringify(
|
|
37930
|
+
{ started: false, pid: existing.pid, ...daemonBacklogFields(snapshot) },
|
|
37931
|
+
null,
|
|
37932
|
+
2
|
|
37933
|
+
)
|
|
37934
|
+
);
|
|
37708
37935
|
return;
|
|
37709
37936
|
}
|
|
37710
37937
|
process.stderr.write(
|
|
@@ -37753,16 +37980,19 @@ async function detachedDaemonStart(opts) {
|
|
|
37753
37980
|
});
|
|
37754
37981
|
child.unref();
|
|
37755
37982
|
if (logFd !== void 0) {
|
|
37756
|
-
|
|
37983
|
+
closeSync4(logFd);
|
|
37757
37984
|
}
|
|
37758
37985
|
const live = await waitForReady();
|
|
37759
37986
|
if (live) {
|
|
37760
37987
|
const after = readPidfile();
|
|
37988
|
+
const snapshot = await requestStatusSnapshot();
|
|
37761
37989
|
process.stderr.write(
|
|
37762
|
-
`${formatCurrentDaemonLifecycleMessage(`[prim] \u2713 daemon started (pid=${after?.pid ?? "?"}, socket=${SOCK_PATH2})
|
|
37990
|
+
`${formatCurrentDaemonLifecycleMessage(`[prim] \u2713 daemon started (pid=${after?.pid ?? "?"}, socket=${SOCK_PATH2})`, snapshot)}
|
|
37763
37991
|
`
|
|
37764
37992
|
);
|
|
37765
|
-
console.log(
|
|
37993
|
+
console.log(
|
|
37994
|
+
JSON.stringify({ started: true, pid: after?.pid, ...daemonBacklogFields(snapshot) }, null, 2)
|
|
37995
|
+
);
|
|
37766
37996
|
return;
|
|
37767
37997
|
}
|
|
37768
37998
|
process.stderr.write(
|
|
@@ -37885,19 +38115,25 @@ async function macDaemonStart(forceRestart = false) {
|
|
|
37885
38115
|
const result = await ensureMacDaemon({ explicitlyStarted: true, forceRestart });
|
|
37886
38116
|
const serviceReady = result.state === "running";
|
|
37887
38117
|
const expectedVersion = result.runtime?.manifest.version;
|
|
37888
|
-
const snapshot = serviceReady && expectedVersion ? await
|
|
37889
|
-
const
|
|
37890
|
-
if (
|
|
38118
|
+
const snapshot = serviceReady && expectedVersion ? await waitForReadySnapshot(expectedVersion) : null;
|
|
38119
|
+
const ready = daemonStartIsReady(serviceReady, snapshot, expectedVersion);
|
|
38120
|
+
if (ready) {
|
|
37891
38121
|
const verb = result.action === "none" ? "already running" : "started";
|
|
37892
38122
|
const servicePid = result.service.loaded ? result.service.pid : void 0;
|
|
37893
38123
|
process.stderr.write(
|
|
37894
|
-
`${formatCurrentDaemonLifecycleMessage(`[prim] \u2713 daemon ${verb} under launchd (pid=${snapshot?.pid ?? servicePid ?? "?"})
|
|
38124
|
+
`${formatCurrentDaemonLifecycleMessage(`[prim] \u2713 daemon ${verb} under launchd (pid=${snapshot?.pid ?? servicePid ?? "?"})`, snapshot)}
|
|
37895
38125
|
`
|
|
37896
38126
|
);
|
|
38127
|
+
} else if (!(serviceReady && expectedVersion)) {
|
|
38128
|
+
process.stderr.write(
|
|
38129
|
+
`[prim] \u2717 launchd did not converge on the desired daemon (${result.action}) (see ${LOG_PATH})
|
|
38130
|
+
`
|
|
38131
|
+
);
|
|
38132
|
+
if (!process.exitCode) process.exitCode = EXIT_NOT_RUNNING;
|
|
37897
38133
|
} else {
|
|
37898
|
-
const reason =
|
|
38134
|
+
const reason = daemonStartFailureReason(snapshot, expectedVersion);
|
|
37899
38135
|
process.stderr.write(
|
|
37900
|
-
`[prim] \u2717 launchd daemon did not reach healthy heartbeat
|
|
38136
|
+
`[prim] \u2717 launchd daemon did not reach a healthy heartbeat on the expected version${reason ? ` \xB7 ${reason}` : ""} (see ${LOG_PATH})
|
|
37901
38137
|
`
|
|
37902
38138
|
);
|
|
37903
38139
|
if (!process.exitCode) process.exitCode = EXIT_NOT_RUNNING;
|
|
@@ -37905,14 +38141,20 @@ async function macDaemonStart(forceRestart = false) {
|
|
|
37905
38141
|
console.log(
|
|
37906
38142
|
JSON.stringify(
|
|
37907
38143
|
{
|
|
37908
|
-
started:
|
|
38144
|
+
started: ready,
|
|
37909
38145
|
supervised: true,
|
|
37910
38146
|
action: result.action,
|
|
37911
38147
|
pid: snapshot?.pid ?? (result.service.loaded ? result.service.pid : void 0),
|
|
37912
38148
|
loaded: result.service.loaded,
|
|
37913
38149
|
responding: result.responding,
|
|
37914
|
-
healthy
|
|
37915
|
-
|
|
38150
|
+
// `healthy` keeps meaning "start succeeded" (always equal to `started`
|
|
38151
|
+
// and the exit code) so existing consumers stay correct. It is not the
|
|
38152
|
+
// daemon's own health: `daemon status --json` stays degraded while a
|
|
38153
|
+
// backlog drains, is held back, or fails to deliver, which start
|
|
38154
|
+
// reports additively via `draining`, `deliveryRetained`,
|
|
38155
|
+
// `deliveryFailing`, and `ingestion`.
|
|
38156
|
+
healthy: ready,
|
|
38157
|
+
...daemonStartHealthFields(ready, snapshot),
|
|
37916
38158
|
version: snapshot?.version,
|
|
37917
38159
|
expectedVersion
|
|
37918
38160
|
},
|
|
@@ -38216,11 +38458,12 @@ async function daemonEnsure() {
|
|
|
38216
38458
|
return { disabled };
|
|
38217
38459
|
}
|
|
38218
38460
|
const result = await ensureMacDaemon();
|
|
38461
|
+
const snapshot = result.state === "running" ? await requestStatusSnapshot() : null;
|
|
38219
38462
|
if (result.state === "disabled") {
|
|
38220
38463
|
process.stderr.write("[prim] daemon remains explicitly disabled\n");
|
|
38221
38464
|
} else if (result.state === "running") {
|
|
38222
38465
|
process.stderr.write(
|
|
38223
|
-
`${formatCurrentDaemonLifecycleMessage(`[prim] \u2713 daemon ensured under launchd (${result.action})
|
|
38466
|
+
`${formatCurrentDaemonLifecycleMessage(`[prim] \u2713 daemon ensured under launchd (${result.action})`, snapshot)}
|
|
38224
38467
|
`
|
|
38225
38468
|
);
|
|
38226
38469
|
} else {
|
|
@@ -38234,7 +38477,8 @@ async function daemonEnsure() {
|
|
|
38234
38477
|
ensured: result.state === "running",
|
|
38235
38478
|
disabled: result.state === "disabled",
|
|
38236
38479
|
supervised: true,
|
|
38237
|
-
action: result.action
|
|
38480
|
+
action: result.action,
|
|
38481
|
+
...daemonBacklogFields(snapshot)
|
|
38238
38482
|
},
|
|
38239
38483
|
null,
|
|
38240
38484
|
2
|
|
@@ -39290,11 +39534,11 @@ function asRecord(value, path9) {
|
|
|
39290
39534
|
}
|
|
39291
39535
|
return value;
|
|
39292
39536
|
}
|
|
39293
|
-
function assertKeys(
|
|
39537
|
+
function assertKeys(record3, path9, required, optional = []) {
|
|
39294
39538
|
const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
|
|
39295
|
-
const missing = required.find((key) => !Object.hasOwn(
|
|
39539
|
+
const missing = required.find((key) => !Object.hasOwn(record3, key));
|
|
39296
39540
|
if (missing !== void 0) contractError(path9, `is missing ${missing}`);
|
|
39297
|
-
const extra = Object.keys(
|
|
39541
|
+
const extra = Object.keys(record3).find((key) => !allowed.has(key));
|
|
39298
39542
|
if (extra !== void 0) contractError(path9, `contains unexpected field ${extra}`);
|
|
39299
39543
|
}
|
|
39300
39544
|
function asString(value, path9, options) {
|
|
@@ -39324,97 +39568,97 @@ function asSha(value, path9) {
|
|
|
39324
39568
|
if (!SHA_RE.test(sha)) contractError(path9, "must be a canonical lowercase Git commit SHA");
|
|
39325
39569
|
return sha;
|
|
39326
39570
|
}
|
|
39327
|
-
function optionalString(
|
|
39328
|
-
if (!Object.hasOwn(
|
|
39329
|
-
return asString(
|
|
39571
|
+
function optionalString(record3, key, path9, max) {
|
|
39572
|
+
if (!Object.hasOwn(record3, key)) return void 0;
|
|
39573
|
+
return asString(record3[key], `${path9}.${key}`, { max, nonempty: true });
|
|
39330
39574
|
}
|
|
39331
39575
|
function parseProposal(value, path9) {
|
|
39332
|
-
const
|
|
39576
|
+
const record3 = asRecord(value, path9);
|
|
39333
39577
|
assertKeys(
|
|
39334
|
-
|
|
39578
|
+
record3,
|
|
39335
39579
|
path9,
|
|
39336
39580
|
["_id", "repoFullName", "deadCommitSha", "status", "signals", "lastEvaluatedAt"],
|
|
39337
39581
|
["proposedSha", "proposedTier", "nextEvaluateAt"]
|
|
39338
39582
|
);
|
|
39339
|
-
const repoFullName = asString(
|
|
39583
|
+
const repoFullName = asString(record3.repoFullName, `${path9}.repoFullName`, {
|
|
39340
39584
|
max: 200,
|
|
39341
39585
|
nonempty: true
|
|
39342
39586
|
});
|
|
39343
39587
|
if (!REPO_FULL_NAME_RE.test(repoFullName)) {
|
|
39344
39588
|
contractError(`${path9}.repoFullName`, "must be canonical owner/repository syntax");
|
|
39345
39589
|
}
|
|
39346
|
-
const status =
|
|
39590
|
+
const status = record3.status;
|
|
39347
39591
|
if (status !== "proposed" && status !== "confirmed" && status !== "verification_failed") {
|
|
39348
39592
|
contractError(`${path9}.status`, "is not a review-visible proposal status");
|
|
39349
39593
|
}
|
|
39350
|
-
if (!Array.isArray(
|
|
39594
|
+
if (!Array.isArray(record3.signals) || record3.signals.length > MAX_SIGNALS) {
|
|
39351
39595
|
contractError(`${path9}.signals`, "must be a bounded array");
|
|
39352
39596
|
}
|
|
39353
|
-
const signals =
|
|
39597
|
+
const signals = record3.signals.map(
|
|
39354
39598
|
(signal, index) => asString(signal, `${path9}.signals[${String(index)}]`, {
|
|
39355
39599
|
max: MAX_SIGNAL_LENGTH,
|
|
39356
39600
|
nonempty: true
|
|
39357
39601
|
})
|
|
39358
39602
|
);
|
|
39359
|
-
const proposedSha = Object.hasOwn(
|
|
39603
|
+
const proposedSha = Object.hasOwn(record3, "proposedSha") ? asSha(record3.proposedSha, `${path9}.proposedSha`) : void 0;
|
|
39360
39604
|
let proposedTier;
|
|
39361
|
-
if (Object.hasOwn(
|
|
39362
|
-
if (
|
|
39605
|
+
if (Object.hasOwn(record3, "proposedTier")) {
|
|
39606
|
+
if (record3.proposedTier !== "exact_patch_body" && record3.proposedTier !== "exact_file_set") {
|
|
39363
39607
|
contractError(`${path9}.proposedTier`, "is not a supported match tier");
|
|
39364
39608
|
}
|
|
39365
|
-
proposedTier =
|
|
39609
|
+
proposedTier = record3.proposedTier;
|
|
39366
39610
|
}
|
|
39367
|
-
const nextEvaluateAt = Object.hasOwn(
|
|
39611
|
+
const nextEvaluateAt = Object.hasOwn(record3, "nextEvaluateAt") ? asTimestamp(record3.nextEvaluateAt, `${path9}.nextEvaluateAt`) : void 0;
|
|
39368
39612
|
return {
|
|
39369
|
-
_id: asString(
|
|
39613
|
+
_id: asString(record3._id, `${path9}._id`, { max: 256, nonempty: true }),
|
|
39370
39614
|
repoFullName,
|
|
39371
|
-
deadCommitSha: asSha(
|
|
39615
|
+
deadCommitSha: asSha(record3.deadCommitSha, `${path9}.deadCommitSha`),
|
|
39372
39616
|
status,
|
|
39373
39617
|
proposedSha,
|
|
39374
39618
|
proposedTier,
|
|
39375
39619
|
signals,
|
|
39376
|
-
lastEvaluatedAt: asTimestamp(
|
|
39620
|
+
lastEvaluatedAt: asTimestamp(record3.lastEvaluatedAt, `${path9}.lastEvaluatedAt`),
|
|
39377
39621
|
nextEvaluateAt
|
|
39378
39622
|
};
|
|
39379
39623
|
}
|
|
39380
39624
|
function parseDecision(value, path9) {
|
|
39381
|
-
const
|
|
39382
|
-
assertKeys(
|
|
39383
|
-
const status =
|
|
39625
|
+
const record3 = asRecord(value, path9);
|
|
39626
|
+
assertKeys(record3, path9, ["_id", "intent", "status", "intentTruncated"], ["shortId"]);
|
|
39627
|
+
const status = record3.status;
|
|
39384
39628
|
if (status !== "provisional" && status !== "abandoned") {
|
|
39385
39629
|
contractError(`${path9}.status`, "is not a repairable decision status");
|
|
39386
39630
|
}
|
|
39387
39631
|
return {
|
|
39388
|
-
_id: asString(
|
|
39389
|
-
shortId: optionalString(
|
|
39390
|
-
intent: asString(
|
|
39632
|
+
_id: asString(record3._id, `${path9}._id`, { max: 256, nonempty: true }),
|
|
39633
|
+
shortId: optionalString(record3, "shortId", path9, 128),
|
|
39634
|
+
intent: asString(record3.intent, `${path9}.intent`, { max: MAX_INTENT_LENGTH }),
|
|
39391
39635
|
status,
|
|
39392
|
-
intentTruncated: asBoolean(
|
|
39636
|
+
intentTruncated: asBoolean(record3.intentTruncated, `${path9}.intentTruncated`)
|
|
39393
39637
|
};
|
|
39394
39638
|
}
|
|
39395
39639
|
function parseListItem(value, path9) {
|
|
39396
|
-
const
|
|
39397
|
-
assertKeys(
|
|
39640
|
+
const record3 = asRecord(value, path9);
|
|
39641
|
+
assertKeys(record3, path9, [
|
|
39398
39642
|
"proposal",
|
|
39399
39643
|
"decisions",
|
|
39400
39644
|
"affectedDecisionCount",
|
|
39401
39645
|
"decisionsTruncated",
|
|
39402
39646
|
"reviewToken"
|
|
39403
39647
|
]);
|
|
39404
|
-
if (!Array.isArray(
|
|
39648
|
+
if (!Array.isArray(record3.decisions) || record3.decisions.length > MAX_REPAIR_DECISIONS_FOR_CONFIRM) {
|
|
39405
39649
|
contractError(`${path9}.decisions`, "must be a bounded array");
|
|
39406
39650
|
}
|
|
39407
|
-
const decisions =
|
|
39651
|
+
const decisions = record3.decisions.map(
|
|
39408
39652
|
(decision, index) => parseDecision(decision, `${path9}.decisions[${String(index)}]`)
|
|
39409
39653
|
);
|
|
39410
|
-
const proposal = parseProposal(
|
|
39411
|
-
const decisionsTruncated = asBoolean(
|
|
39412
|
-
const affectedDecisionCount =
|
|
39654
|
+
const proposal = parseProposal(record3.proposal, `${path9}.proposal`);
|
|
39655
|
+
const decisionsTruncated = asBoolean(record3.decisionsTruncated, `${path9}.decisionsTruncated`);
|
|
39656
|
+
const affectedDecisionCount = record3.affectedDecisionCount === null ? null : asCount(record3.affectedDecisionCount, `${path9}.affectedDecisionCount`);
|
|
39413
39657
|
let reviewToken;
|
|
39414
|
-
if (
|
|
39658
|
+
if (record3.reviewToken === null) {
|
|
39415
39659
|
reviewToken = null;
|
|
39416
39660
|
} else {
|
|
39417
|
-
reviewToken = asString(
|
|
39661
|
+
reviewToken = asString(record3.reviewToken, `${path9}.reviewToken`, {
|
|
39418
39662
|
max: 64,
|
|
39419
39663
|
nonempty: true
|
|
39420
39664
|
});
|
|
@@ -39447,19 +39691,19 @@ function parseListItem(value, path9) {
|
|
|
39447
39691
|
}
|
|
39448
39692
|
function parseListPage(value, pageNumber) {
|
|
39449
39693
|
const path9 = `page ${String(pageNumber)}`;
|
|
39450
|
-
const
|
|
39451
|
-
assertKeys(
|
|
39452
|
-
if (!Array.isArray(
|
|
39694
|
+
const record3 = asRecord(value, path9);
|
|
39695
|
+
assertKeys(record3, path9, ["repairs", "isDone", "nextCursor", "truncated"]);
|
|
39696
|
+
if (!Array.isArray(record3.repairs) || record3.repairs.length > MAX_REPAIRS_PER_PAGE) {
|
|
39453
39697
|
contractError(`${path9}.repairs`, "must be a bounded array");
|
|
39454
39698
|
}
|
|
39455
|
-
const isDone = asBoolean(
|
|
39456
|
-
const truncated = asBoolean(
|
|
39699
|
+
const isDone = asBoolean(record3.isDone, `${path9}.isDone`);
|
|
39700
|
+
const truncated = asBoolean(record3.truncated, `${path9}.truncated`);
|
|
39457
39701
|
if (truncated === isDone) contractError(path9, "has inconsistent isDone/truncated flags");
|
|
39458
39702
|
let nextCursor;
|
|
39459
|
-
if (
|
|
39703
|
+
if (record3.nextCursor === null) {
|
|
39460
39704
|
nextCursor = null;
|
|
39461
39705
|
} else {
|
|
39462
|
-
nextCursor = asString(
|
|
39706
|
+
nextCursor = asString(record3.nextCursor, `${path9}.nextCursor`, {
|
|
39463
39707
|
max: MAX_CURSOR_LENGTH,
|
|
39464
39708
|
nonempty: true
|
|
39465
39709
|
});
|
|
@@ -39467,7 +39711,7 @@ function parseListPage(value, pageNumber) {
|
|
|
39467
39711
|
if (isDone && nextCursor !== null) contractError(path9, "is done but includes a next cursor");
|
|
39468
39712
|
if (!isDone && nextCursor === null) contractError(path9, "is incomplete without a next cursor");
|
|
39469
39713
|
return {
|
|
39470
|
-
repairs:
|
|
39714
|
+
repairs: record3.repairs.map(
|
|
39471
39715
|
(item, index) => parseListItem(item, `${path9}.repairs[${String(index)}]`)
|
|
39472
39716
|
),
|
|
39473
39717
|
isDone,
|
|
@@ -39545,23 +39789,23 @@ function normalizedResolutionInput(proposalId, expectedProposedSha) {
|
|
|
39545
39789
|
return { proposalId, expectedProposedSha: expectedProposedSha.toLowerCase() };
|
|
39546
39790
|
}
|
|
39547
39791
|
function parseResolutionOutcome(value, action) {
|
|
39548
|
-
const
|
|
39792
|
+
const record3 = asRecord(value, "resolution response");
|
|
39549
39793
|
const allowed = action === "confirm" ? ["confirmed", "already_applied", "disabled", "no_op", "stale"] : ["rejected", "already_applied", "no_op", "stale"];
|
|
39550
|
-
if (
|
|
39551
|
-
assertKeys(
|
|
39794
|
+
if (record3.status === "backoff") {
|
|
39795
|
+
assertKeys(record3, "resolution response", ["status", "retryAt"]);
|
|
39552
39796
|
if (action !== "confirm") {
|
|
39553
39797
|
contractError("resolution response.status", "backoff is impossible for reject");
|
|
39554
39798
|
}
|
|
39555
39799
|
return {
|
|
39556
39800
|
status: "backoff",
|
|
39557
|
-
retryAt: asTimestamp(
|
|
39801
|
+
retryAt: asTimestamp(record3.retryAt, "resolution response.retryAt")
|
|
39558
39802
|
};
|
|
39559
39803
|
}
|
|
39560
|
-
assertKeys(
|
|
39561
|
-
if (typeof
|
|
39804
|
+
assertKeys(record3, "resolution response", ["status"]);
|
|
39805
|
+
if (typeof record3.status !== "string" || !allowed.includes(record3.status)) {
|
|
39562
39806
|
contractError("resolution response.status", "is not a supported outcome");
|
|
39563
39807
|
}
|
|
39564
|
-
return { status:
|
|
39808
|
+
return { status: record3.status };
|
|
39565
39809
|
}
|
|
39566
39810
|
function normalizedReviewToken(value) {
|
|
39567
39811
|
if (value === void 0 || !REVIEW_TOKEN_INPUT_RE.test(value)) {
|
|
@@ -39572,18 +39816,18 @@ function normalizedReviewToken(value) {
|
|
|
39572
39816
|
return value.toLowerCase();
|
|
39573
39817
|
}
|
|
39574
39818
|
function parseConflictOutcome(value, action) {
|
|
39575
|
-
const
|
|
39576
|
-
assertKeys(
|
|
39819
|
+
const record3 = asRecord(value, "resolution conflict response");
|
|
39820
|
+
assertKeys(record3, "resolution conflict response", ["status"]);
|
|
39577
39821
|
if (action !== "confirm") {
|
|
39578
39822
|
contractError(
|
|
39579
39823
|
"resolution conflict response.status",
|
|
39580
39824
|
"a review conflict is impossible for reject"
|
|
39581
39825
|
);
|
|
39582
39826
|
}
|
|
39583
|
-
if (
|
|
39827
|
+
if (record3.status !== "review_too_large" && record3.status !== "stale_review") {
|
|
39584
39828
|
contractError("resolution conflict response.status", "is not a supported 409 outcome");
|
|
39585
39829
|
}
|
|
39586
|
-
return { status:
|
|
39830
|
+
return { status: record3.status };
|
|
39587
39831
|
}
|
|
39588
39832
|
async function resolveRepair(proposalId, expectedProposedSha, action, expectedReviewToken, deps = defaultDeps6) {
|
|
39589
39833
|
const normalized = normalizedResolutionInput(proposalId, expectedProposedSha);
|
|
@@ -40725,7 +40969,7 @@ function registerDecisionsCommands(program3) {
|
|
|
40725
40969
|
}
|
|
40726
40970
|
|
|
40727
40971
|
// src/commands/doctor.ts
|
|
40728
|
-
import { existsSync as
|
|
40972
|
+
import { existsSync as existsSync16 } from "fs";
|
|
40729
40973
|
|
|
40730
40974
|
// src/decisions/feedback.ts
|
|
40731
40975
|
var FEEDBACK_PROTOCOL_VERSION = 2;
|
|
@@ -40774,28 +41018,35 @@ function parseOrganizationBinding(value) {
|
|
|
40774
41018
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
40775
41019
|
return { state: "server_contract_unavailable" };
|
|
40776
41020
|
}
|
|
40777
|
-
const
|
|
40778
|
-
if (
|
|
41021
|
+
const record3 = value;
|
|
41022
|
+
if (record3.authenticated !== true || record3.organizationBindingVersion !== ORGANIZATION_BINDING_VERSION || !isCaptureAuthorityKind(record3.captureAuthorityKind)) {
|
|
40779
41023
|
return { state: "server_contract_unavailable" };
|
|
40780
41024
|
}
|
|
40781
|
-
if (
|
|
41025
|
+
if (record3.organizationId === null && record3.workosOrganizationId === null) {
|
|
40782
41026
|
return { state: "no_current_organization" };
|
|
40783
41027
|
}
|
|
40784
|
-
if (safeOrganizationId(
|
|
41028
|
+
if (safeOrganizationId(record3.organizationId) && record3.workosOrganizationId === null) {
|
|
40785
41029
|
return { state: "organization_identity_unreconciled" };
|
|
40786
41030
|
}
|
|
40787
|
-
if (!safeOrganizationId(
|
|
41031
|
+
if (!safeOrganizationId(record3.organizationId) || !safeOrganizationId(record3.workosOrganizationId)) {
|
|
40788
41032
|
return { state: "server_contract_unavailable" };
|
|
40789
41033
|
}
|
|
40790
41034
|
return {
|
|
40791
41035
|
state: "current",
|
|
40792
41036
|
binding: {
|
|
40793
|
-
captureAuthorityKind:
|
|
40794
|
-
organizationId:
|
|
40795
|
-
workosOrganizationId:
|
|
41037
|
+
captureAuthorityKind: record3.captureAuthorityKind,
|
|
41038
|
+
organizationId: record3.organizationId,
|
|
41039
|
+
workosOrganizationId: record3.workosOrganizationId
|
|
40796
41040
|
}
|
|
40797
41041
|
};
|
|
40798
41042
|
}
|
|
41043
|
+
function summarizeRetainedBuckets(retained) {
|
|
41044
|
+
const counts = /* @__PURE__ */ new Map();
|
|
41045
|
+
for (const item of retained) {
|
|
41046
|
+
counts.set(item.reason, (counts.get(item.reason) ?? 0) + 1);
|
|
41047
|
+
}
|
|
41048
|
+
return [...counts].sort(([left], [right]) => left.localeCompare(right)).map(([reason, count]) => `${reason}:${String(count)}`).join(", ");
|
|
41049
|
+
}
|
|
40799
41050
|
function classifyJournalBuckets(buckets, binding) {
|
|
40800
41051
|
const deliverableBuckets = /* @__PURE__ */ new Set();
|
|
40801
41052
|
const retainedBuckets = [];
|
|
@@ -40869,18 +41120,109 @@ async function inspectJournalDelivery(buckets, options = {}) {
|
|
|
40869
41120
|
// src/journal.ts
|
|
40870
41121
|
import {
|
|
40871
41122
|
appendFileSync,
|
|
40872
|
-
closeSync as
|
|
40873
|
-
existsSync as
|
|
40874
|
-
fstatSync,
|
|
41123
|
+
closeSync as closeSync5,
|
|
41124
|
+
existsSync as existsSync14,
|
|
41125
|
+
fstatSync as fstatSync2,
|
|
40875
41126
|
mkdirSync as mkdirSync10,
|
|
40876
|
-
openSync as
|
|
41127
|
+
openSync as openSync5,
|
|
40877
41128
|
opendirSync,
|
|
41129
|
+
readFileSync as readFileSync15,
|
|
41130
|
+
readSync as readSync2,
|
|
41131
|
+
readdirSync as readdirSync5
|
|
41132
|
+
} from "fs";
|
|
41133
|
+
import { dirname as dirname12, join as join18 } from "path";
|
|
41134
|
+
|
|
41135
|
+
// src/drain-progress.ts
|
|
41136
|
+
import {
|
|
41137
|
+
chmodSync as chmodSync5,
|
|
41138
|
+
existsSync as existsSync13,
|
|
40878
41139
|
readFileSync as readFileSync14,
|
|
40879
|
-
|
|
40880
|
-
|
|
41140
|
+
readdirSync as readdirSync4,
|
|
41141
|
+
rmSync as rmSync6,
|
|
41142
|
+
rmdirSync
|
|
40881
41143
|
} from "fs";
|
|
40882
|
-
import { dirname as dirname11, join as join17 } from "path";
|
|
40883
|
-
var
|
|
41144
|
+
import { basename as basename4, dirname as dirname11, join as join17 } from "path";
|
|
41145
|
+
var DIRECTORY_MODE3 = 448;
|
|
41146
|
+
var FILE_MODE2 = 384;
|
|
41147
|
+
var DRAIN_PROGRESS_DIRNAME = "drain-progress";
|
|
41148
|
+
var CHECKPOINT_NAME = /^(.+)\.json(?:\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.tmp)?$/;
|
|
41149
|
+
function rotationIdentity(stat) {
|
|
41150
|
+
return { size: stat.size, ino: stat.ino, dev: stat.dev };
|
|
41151
|
+
}
|
|
41152
|
+
function drainProgressDirectory(flushingPath) {
|
|
41153
|
+
return join17(dirname11(flushingPath), DRAIN_PROGRESS_DIRNAME);
|
|
41154
|
+
}
|
|
41155
|
+
function drainProgressPath(flushingPath) {
|
|
41156
|
+
return join17(drainProgressDirectory(flushingPath), `${basename4(flushingPath)}.json`);
|
|
41157
|
+
}
|
|
41158
|
+
function isByteCount(value) {
|
|
41159
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
41160
|
+
}
|
|
41161
|
+
function drainResumeOffset(flushingPath, rotation) {
|
|
41162
|
+
let parsed;
|
|
41163
|
+
try {
|
|
41164
|
+
parsed = JSON.parse(readFileSync14(drainProgressPath(flushingPath), "utf8"));
|
|
41165
|
+
} catch {
|
|
41166
|
+
return 0;
|
|
41167
|
+
}
|
|
41168
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
41169
|
+
return 0;
|
|
41170
|
+
}
|
|
41171
|
+
const { v, offset, size, ino, dev } = parsed;
|
|
41172
|
+
if (v !== 2 || ino !== rotation.ino || dev !== rotation.dev || size !== rotation.size || !isByteCount(offset) || offset > rotation.size) {
|
|
41173
|
+
return 0;
|
|
41174
|
+
}
|
|
41175
|
+
return offset;
|
|
41176
|
+
}
|
|
41177
|
+
function writeDrainCheckpoint(flushingPath, checkpoint) {
|
|
41178
|
+
const directory = drainProgressDirectory(flushingPath);
|
|
41179
|
+
ensureDurableDirectory(directory, DIRECTORY_MODE3);
|
|
41180
|
+
chmodSync5(directory, DIRECTORY_MODE3);
|
|
41181
|
+
atomicWriteFile(drainProgressPath(flushingPath), `${JSON.stringify(checkpoint)}
|
|
41182
|
+
`, {
|
|
41183
|
+
mode: FILE_MODE2
|
|
41184
|
+
});
|
|
41185
|
+
}
|
|
41186
|
+
function removeEmptyDirectory(directory) {
|
|
41187
|
+
try {
|
|
41188
|
+
rmdirSync(directory);
|
|
41189
|
+
} catch (error) {
|
|
41190
|
+
const code = error.code;
|
|
41191
|
+
if (code !== "ENOENT" && code !== "ENOTEMPTY" && code !== "EEXIST") {
|
|
41192
|
+
throw error;
|
|
41193
|
+
}
|
|
41194
|
+
}
|
|
41195
|
+
}
|
|
41196
|
+
function removeDrainCheckpoint(flushingPath) {
|
|
41197
|
+
rmSync6(drainProgressPath(flushingPath), { force: true });
|
|
41198
|
+
removeEmptyDirectory(drainProgressDirectory(flushingPath));
|
|
41199
|
+
}
|
|
41200
|
+
function sweepOrphanedDrainCheckpoints(bucketDirectory) {
|
|
41201
|
+
const directory = join17(bucketDirectory, DRAIN_PROGRESS_DIRNAME);
|
|
41202
|
+
let names;
|
|
41203
|
+
try {
|
|
41204
|
+
names = readdirSync4(directory);
|
|
41205
|
+
} catch {
|
|
41206
|
+
return;
|
|
41207
|
+
}
|
|
41208
|
+
for (const name of names) {
|
|
41209
|
+
const rotation = CHECKPOINT_NAME.exec(name)?.[1];
|
|
41210
|
+
if (rotation === void 0 || existsSync13(join17(bucketDirectory, rotation))) {
|
|
41211
|
+
continue;
|
|
41212
|
+
}
|
|
41213
|
+
try {
|
|
41214
|
+
rmSync6(join17(directory, name), { force: true });
|
|
41215
|
+
} catch {
|
|
41216
|
+
}
|
|
41217
|
+
}
|
|
41218
|
+
try {
|
|
41219
|
+
removeEmptyDirectory(directory);
|
|
41220
|
+
} catch {
|
|
41221
|
+
}
|
|
41222
|
+
}
|
|
41223
|
+
|
|
41224
|
+
// src/journal.ts
|
|
41225
|
+
var JOURNAL_DIR = join18(primConfigDirectory(), "moves");
|
|
40884
41226
|
var JOURNAL_BASENAME = "journal.ndjson";
|
|
40885
41227
|
var JOURNAL_STATS_SAMPLE_BYTES = 64 * 1024;
|
|
40886
41228
|
var PENDING_STATS_MAX_FILES = 128;
|
|
@@ -40892,7 +41234,7 @@ function envSlug(apiUrl) {
|
|
|
40892
41234
|
return slug;
|
|
40893
41235
|
}
|
|
40894
41236
|
function currentEnvDir() {
|
|
40895
|
-
return
|
|
41237
|
+
return join18(JOURNAL_DIR, envSlug(getSiteUrl()));
|
|
40896
41238
|
}
|
|
40897
41239
|
function parseMoves(content) {
|
|
40898
41240
|
const moves = [];
|
|
@@ -40908,23 +41250,23 @@ function parseMoves(content) {
|
|
|
40908
41250
|
return moves;
|
|
40909
41251
|
}
|
|
40910
41252
|
function readMovesFromPath(path9) {
|
|
40911
|
-
if (!
|
|
41253
|
+
if (!existsSync14(path9)) {
|
|
40912
41254
|
return [];
|
|
40913
41255
|
}
|
|
40914
|
-
return parseMoves(
|
|
41256
|
+
return parseMoves(readFileSync15(path9, "utf-8"));
|
|
40915
41257
|
}
|
|
40916
41258
|
function listBuckets() {
|
|
40917
41259
|
const out = [];
|
|
40918
41260
|
const envDir = currentEnvDir();
|
|
40919
|
-
if (!
|
|
41261
|
+
if (!existsSync14(envDir)) {
|
|
40920
41262
|
return out;
|
|
40921
41263
|
}
|
|
40922
|
-
for (const entry of
|
|
41264
|
+
for (const entry of readdirSync5(envDir, { withFileTypes: true })) {
|
|
40923
41265
|
if (!entry.isDirectory()) {
|
|
40924
41266
|
continue;
|
|
40925
41267
|
}
|
|
40926
|
-
const path9 =
|
|
40927
|
-
if (
|
|
41268
|
+
const path9 = join18(envDir, entry.name, JOURNAL_BASENAME);
|
|
41269
|
+
if (existsSync14(path9)) {
|
|
40928
41270
|
out.push({ bucket: entry.name, path: path9 });
|
|
40929
41271
|
}
|
|
40930
41272
|
}
|
|
@@ -40941,22 +41283,23 @@ function oldestCapturedAt(moves) {
|
|
|
40941
41283
|
}
|
|
40942
41284
|
return oldest;
|
|
40943
41285
|
}
|
|
40944
|
-
function sampleJournalFile(path9, maxBytes = JOURNAL_STATS_SAMPLE_BYTES) {
|
|
40945
|
-
const fd =
|
|
41286
|
+
function sampleJournalFile(path9, maxBytes = JOURNAL_STATS_SAMPLE_BYTES, options = {}) {
|
|
41287
|
+
const fd = openSync5(path9, "r");
|
|
40946
41288
|
try {
|
|
40947
|
-
const initial =
|
|
40948
|
-
const
|
|
41289
|
+
const initial = fstatSync2(fd);
|
|
41290
|
+
const start = options.fromDrainCheckpoint ? drainResumeOffset(path9, rotationIdentity(initial)) : 0;
|
|
41291
|
+
const target = Math.min(initial.size - start, Math.max(0, maxBytes));
|
|
40949
41292
|
const buffer = Buffer.allocUnsafe(target);
|
|
40950
41293
|
let sampledBytes = 0;
|
|
40951
41294
|
while (sampledBytes < target) {
|
|
40952
|
-
const count =
|
|
41295
|
+
const count = readSync2(fd, buffer, sampledBytes, target - sampledBytes, start + sampledBytes);
|
|
40953
41296
|
if (count === 0) {
|
|
40954
41297
|
break;
|
|
40955
41298
|
}
|
|
40956
41299
|
sampledBytes += count;
|
|
40957
41300
|
}
|
|
40958
|
-
const stat =
|
|
40959
|
-
const sampled = stat.size > sampledBytes;
|
|
41301
|
+
const stat = fstatSync2(fd);
|
|
41302
|
+
const sampled = stat.size - start > sampledBytes;
|
|
40960
41303
|
let content = buffer.subarray(0, sampledBytes).toString("utf-8");
|
|
40961
41304
|
if (sampled) {
|
|
40962
41305
|
const lastNewline = content.lastIndexOf("\n");
|
|
@@ -40966,6 +41309,7 @@ function sampleJournalFile(path9, maxBytes = JOURNAL_STATS_SAMPLE_BYTES) {
|
|
|
40966
41309
|
const moves = parseMoves(content);
|
|
40967
41310
|
return {
|
|
40968
41311
|
sizeBytes: stat.size,
|
|
41312
|
+
pendingBytes: Math.max(0, stat.size - start),
|
|
40969
41313
|
mtimeMs: stat.mtimeMs,
|
|
40970
41314
|
lineCount: sampled && stat.size > 0 ? Math.max(1, lines.length) : lines.length,
|
|
40971
41315
|
oldestCapturedAt: oldestCapturedAt(moves),
|
|
@@ -40973,7 +41317,7 @@ function sampleJournalFile(path9, maxBytes = JOURNAL_STATS_SAMPLE_BYTES) {
|
|
|
40973
41317
|
sampledBytes
|
|
40974
41318
|
};
|
|
40975
41319
|
} finally {
|
|
40976
|
-
|
|
41320
|
+
closeSync5(fd);
|
|
40977
41321
|
}
|
|
40978
41322
|
}
|
|
40979
41323
|
function parseFlushingPid(name) {
|
|
@@ -40984,7 +41328,7 @@ function parseFlushingPid(name) {
|
|
|
40984
41328
|
return Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
40985
41329
|
}
|
|
40986
41330
|
function listFlushingInDir(dir, bucket, options = {}) {
|
|
40987
|
-
if (!
|
|
41331
|
+
if (!existsSync14(dir)) {
|
|
40988
41332
|
return [];
|
|
40989
41333
|
}
|
|
40990
41334
|
const out = [];
|
|
@@ -40998,10 +41342,10 @@ function listFlushingInDir(dir, bucket, options = {}) {
|
|
|
40998
41342
|
entry = entries.readSync();
|
|
40999
41343
|
continue;
|
|
41000
41344
|
}
|
|
41001
|
-
const path9 =
|
|
41345
|
+
const path9 = join18(dir, entry.name);
|
|
41002
41346
|
let sample;
|
|
41003
41347
|
try {
|
|
41004
|
-
sample = sampleJournalFile(path9, remainingBytes);
|
|
41348
|
+
sample = sampleJournalFile(path9, remainingBytes, { fromDrainCheckpoint: true });
|
|
41005
41349
|
} catch (err) {
|
|
41006
41350
|
if (err.code === "ENOENT") {
|
|
41007
41351
|
entry = entries.readSync();
|
|
@@ -41025,18 +41369,18 @@ function listFlushingInDir(dir, bucket, options = {}) {
|
|
|
41025
41369
|
}
|
|
41026
41370
|
function listFlushing(options = {}) {
|
|
41027
41371
|
const envDir = currentEnvDir();
|
|
41028
|
-
if (!
|
|
41372
|
+
if (!existsSync14(envDir)) {
|
|
41029
41373
|
return [];
|
|
41030
41374
|
}
|
|
41031
41375
|
const out = [];
|
|
41032
41376
|
let remainingBytes = options.sampleBytes ?? JOURNAL_STATS_SAMPLE_BYTES;
|
|
41033
41377
|
const maxFiles = options.maxFiles ?? Number.POSITIVE_INFINITY;
|
|
41034
|
-
for (const entry of
|
|
41378
|
+
for (const entry of readdirSync5(envDir, { withFileTypes: true })) {
|
|
41035
41379
|
if (out.length >= maxFiles) {
|
|
41036
41380
|
break;
|
|
41037
41381
|
}
|
|
41038
41382
|
if (entry.isDirectory()) {
|
|
41039
|
-
const found = listFlushingInDir(
|
|
41383
|
+
const found = listFlushingInDir(join18(envDir, entry.name), entry.name, {
|
|
41040
41384
|
sampleBytes: remainingBytes,
|
|
41041
41385
|
maxFiles: maxFiles - out.length
|
|
41042
41386
|
});
|
|
@@ -41049,6 +41393,17 @@ function listFlushing(options = {}) {
|
|
|
41049
41393
|
}
|
|
41050
41394
|
return out;
|
|
41051
41395
|
}
|
|
41396
|
+
function sweepOrphanedDrainProgress() {
|
|
41397
|
+
const envDir = currentEnvDir();
|
|
41398
|
+
if (!existsSync14(envDir)) {
|
|
41399
|
+
return;
|
|
41400
|
+
}
|
|
41401
|
+
for (const entry of readdirSync5(envDir, { withFileTypes: true })) {
|
|
41402
|
+
if (entry.isDirectory()) {
|
|
41403
|
+
sweepOrphanedDrainCheckpoints(join18(envDir, entry.name));
|
|
41404
|
+
}
|
|
41405
|
+
}
|
|
41406
|
+
}
|
|
41052
41407
|
function bucketStats(options = {}) {
|
|
41053
41408
|
const out = [];
|
|
41054
41409
|
let remainingBytes = options.sampleBytes ?? JOURNAL_STATS_SAMPLE_BYTES;
|
|
@@ -41094,9 +41449,9 @@ function pendingJournalStats() {
|
|
|
41094
41449
|
}
|
|
41095
41450
|
|
|
41096
41451
|
// src/commands/hermes-install.ts
|
|
41097
|
-
import { existsSync as
|
|
41452
|
+
import { existsSync as existsSync15, readFileSync as readFileSync16, rmSync as rmSync7, statSync as statSync4 } from "fs";
|
|
41098
41453
|
import { homedir as homedir9 } from "os";
|
|
41099
|
-
import { join as
|
|
41454
|
+
import { join as join19 } from "path";
|
|
41100
41455
|
|
|
41101
41456
|
// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/browser/dist/nodes/identity.js
|
|
41102
41457
|
var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias");
|
|
@@ -47375,13 +47730,13 @@ var JSON_INDENT3 = 2;
|
|
|
47375
47730
|
var CONFIG_MODE = 384;
|
|
47376
47731
|
var PRIM_BINS4 = [CAPTURE_BIN4, GATE_BIN4, POST_TOOL_USE_BIN3, SESSION_START_BIN4, SESSION_END_BIN3];
|
|
47377
47732
|
function hermesHome() {
|
|
47378
|
-
return process.env.HERMES_HOME ??
|
|
47733
|
+
return process.env.HERMES_HOME ?? join19(homedir9(), ".hermes");
|
|
47379
47734
|
}
|
|
47380
47735
|
function configPath() {
|
|
47381
|
-
return
|
|
47736
|
+
return join19(hermesHome(), "config.yaml");
|
|
47382
47737
|
}
|
|
47383
47738
|
function legacyShimPath() {
|
|
47384
|
-
return
|
|
47739
|
+
return join19(hermesHome(), "agent-hooks", "prim-shim.sh");
|
|
47385
47740
|
}
|
|
47386
47741
|
function commandFor2(bin) {
|
|
47387
47742
|
return stableHookCommand(bin, HERMES_ARGS);
|
|
@@ -47495,8 +47850,8 @@ function hookRuntimeResolutions3(hooks) {
|
|
|
47495
47850
|
}
|
|
47496
47851
|
function inspectHookRuntimeResolutions4() {
|
|
47497
47852
|
const path9 = configPath();
|
|
47498
|
-
if (!
|
|
47499
|
-
return hookRuntimeResolutions3(readHooks(parseHermesDocument(
|
|
47853
|
+
if (!existsSync15(path9)) return [];
|
|
47854
|
+
return hookRuntimeResolutions3(readHooks(parseHermesDocument(readFileSync16(path9, "utf8"))));
|
|
47500
47855
|
}
|
|
47501
47856
|
function entryMatchesRegistration3(owned, registration) {
|
|
47502
47857
|
const expected = entryFor(registration);
|
|
@@ -47765,11 +48120,11 @@ function assertMergeValid(before, after, expectedHooks) {
|
|
|
47765
48120
|
}
|
|
47766
48121
|
}
|
|
47767
48122
|
function readConfigSnapshot(path9) {
|
|
47768
|
-
if (!
|
|
48123
|
+
if (!existsSync15(path9)) return { exists: false, mode: CONFIG_MODE, raw: "" };
|
|
47769
48124
|
return {
|
|
47770
48125
|
exists: true,
|
|
47771
|
-
mode:
|
|
47772
|
-
raw:
|
|
48126
|
+
mode: statSync4(path9).mode & 511,
|
|
48127
|
+
raw: readFileSync16(path9, "utf8")
|
|
47773
48128
|
};
|
|
47774
48129
|
}
|
|
47775
48130
|
var CONCURRENT_CONFIG_MESSAGE = "Hermes config changed during update; concurrent changes were preserved, retry the command";
|
|
@@ -47778,7 +48133,7 @@ function writeHermesConfigSnapshot(path9, before, after, expectedHooks) {
|
|
|
47778
48133
|
ensureParent: true,
|
|
47779
48134
|
mode: before.mode,
|
|
47780
48135
|
validate(temporaryPath) {
|
|
47781
|
-
const flushed =
|
|
48136
|
+
const flushed = readFileSync16(temporaryPath, "utf8");
|
|
47782
48137
|
if (!mergePreservesHermesSemantics(before.raw, flushed, expectedHooks)) {
|
|
47783
48138
|
throw new Error(INVALID_MERGE_MESSAGE);
|
|
47784
48139
|
}
|
|
@@ -47790,7 +48145,7 @@ function writeHermesConfigSnapshot(path9, before, after, expectedHooks) {
|
|
|
47790
48145
|
});
|
|
47791
48146
|
}
|
|
47792
48147
|
function removeLegacyShim() {
|
|
47793
|
-
|
|
48148
|
+
rmSync7(legacyShimPath(), { force: true });
|
|
47794
48149
|
}
|
|
47795
48150
|
function autoAcceptOf(raw) {
|
|
47796
48151
|
if (raw.trim().length === 0) {
|
|
@@ -47831,7 +48186,7 @@ function performInstall4(opts) {
|
|
|
47831
48186
|
}
|
|
47832
48187
|
function performUninstall4() {
|
|
47833
48188
|
const path9 = configPath();
|
|
47834
|
-
if (!
|
|
48189
|
+
if (!existsSync15(path9)) {
|
|
47835
48190
|
removeLegacyShim();
|
|
47836
48191
|
return { path: path9, gate: false, capture: false, autoAccept: false, changed: false };
|
|
47837
48192
|
}
|
|
@@ -47856,7 +48211,7 @@ function performUninstall4() {
|
|
|
47856
48211
|
}
|
|
47857
48212
|
function performStatus4() {
|
|
47858
48213
|
const path9 = configPath();
|
|
47859
|
-
const raw =
|
|
48214
|
+
const raw = existsSync15(path9) ? readFileSync16(path9, "utf8") : "";
|
|
47860
48215
|
const hooks = raw ? readHooks(parseHermesDocument(raw)) : {};
|
|
47861
48216
|
return {
|
|
47862
48217
|
path: path9,
|
|
@@ -47910,7 +48265,6 @@ function registerHermesCommands(program3) {
|
|
|
47910
48265
|
// src/commands/doctor.ts
|
|
47911
48266
|
var DAEMON_PROBE_TIMEOUT_MS2 = 500;
|
|
47912
48267
|
var CONNECTIVITY_TIMEOUT_MS = 3e3;
|
|
47913
|
-
var MS_PER_SECOND = 1e3;
|
|
47914
48268
|
var STALE_PENDING_MS = 3e4;
|
|
47915
48269
|
var EXIT_UNHEALTHY = 1;
|
|
47916
48270
|
function classifyDoctor(checks) {
|
|
@@ -47946,7 +48300,7 @@ function checkAuth() {
|
|
|
47946
48300
|
return classifyAuthCredential(
|
|
47947
48301
|
credential,
|
|
47948
48302
|
storedCredential ? getTokenExpiresAt() : void 0,
|
|
47949
|
-
storedCredential &&
|
|
48303
|
+
storedCredential && existsSync16(REFRESH_TOKEN_PATH)
|
|
47950
48304
|
);
|
|
47951
48305
|
}
|
|
47952
48306
|
function classifyDaemonHealth(snapshot, options = {}) {
|
|
@@ -48032,8 +48386,19 @@ function classifyDaemonHealth(snapshot, options = {}) {
|
|
|
48032
48386
|
};
|
|
48033
48387
|
}
|
|
48034
48388
|
if (!snapshot.ingestion?.healthy) {
|
|
48035
|
-
|
|
48036
|
-
|
|
48389
|
+
if (options.backlogExpected && deliveryBacklogState(snapshot.ingestion) === "draining") {
|
|
48390
|
+
const ingestionStatus2 = options.ingestionStatus ?? decisionIngestionStatus(process.cwd());
|
|
48391
|
+
const live = options.backlog;
|
|
48392
|
+
const progress = live && live.pendingCount === 0 && live.pendingSampled !== true ? "backlog drained; daemon health refreshes on its next sweep" : deliveryBacklogSummary(snapshot.ingestion, options.now, live);
|
|
48393
|
+
return {
|
|
48394
|
+
name: "daemon",
|
|
48395
|
+
status: "warn",
|
|
48396
|
+
detail: `supervised and live${snapshot.version ? ` \xB7 v${snapshot.version}` : ""} \xB7 Decision ingestion ${ingestionStatus2} \xB7 ${progress}`
|
|
48397
|
+
};
|
|
48398
|
+
}
|
|
48399
|
+
const counts = options.backlog ?? snapshot.ingestion;
|
|
48400
|
+
const pending = counts?.pendingCount ?? 0;
|
|
48401
|
+
const pendingLabel = counts?.pendingSampled ? `at least ${String(pending)}` : String(pending);
|
|
48037
48402
|
return {
|
|
48038
48403
|
name: "daemon",
|
|
48039
48404
|
status: "fail",
|
|
@@ -48050,17 +48415,23 @@ function classifyDaemonHealth(snapshot, options = {}) {
|
|
|
48050
48415
|
detail: `supervised and healthy${snapshot.version ? ` \xB7 v${snapshot.version}` : ""} \xB7 Decision ingestion ${ingestionStatus}`
|
|
48051
48416
|
};
|
|
48052
48417
|
}
|
|
48053
|
-
async function
|
|
48418
|
+
async function probeDelivery() {
|
|
48419
|
+
const stats = pendingJournalStats();
|
|
48420
|
+
const now = Date.now();
|
|
48421
|
+
const disabled = daemonExplicitlyDisabled();
|
|
48422
|
+
const expectedVersion = packageVersion();
|
|
48054
48423
|
let service;
|
|
48055
48424
|
if (process.platform === "darwin") {
|
|
48056
48425
|
try {
|
|
48057
48426
|
service = getLaunchdService();
|
|
48058
48427
|
} catch (error) {
|
|
48059
|
-
const detail = boundedHealthError(error instanceof Error ? error.message : String(error));
|
|
48060
48428
|
return {
|
|
48061
|
-
|
|
48062
|
-
|
|
48063
|
-
|
|
48429
|
+
snapshot: null,
|
|
48430
|
+
launchdError: error instanceof Error ? error.message : String(error),
|
|
48431
|
+
disabled,
|
|
48432
|
+
expectedVersion,
|
|
48433
|
+
stats,
|
|
48434
|
+
now
|
|
48064
48435
|
};
|
|
48065
48436
|
}
|
|
48066
48437
|
}
|
|
@@ -48069,53 +48440,74 @@ async function checkDaemon() {
|
|
|
48069
48440
|
{ callerEnv: getSiteUrl() },
|
|
48070
48441
|
{ timeoutMs: DAEMON_PROBE_TIMEOUT_MS2 }
|
|
48071
48442
|
);
|
|
48072
|
-
return
|
|
48073
|
-
|
|
48074
|
-
|
|
48075
|
-
|
|
48443
|
+
return { snapshot, service, disabled, expectedVersion, stats, now };
|
|
48444
|
+
}
|
|
48445
|
+
function daemonCheckVouchesForDrain(check) {
|
|
48446
|
+
return check.name === "daemon" && check.status !== "fail";
|
|
48447
|
+
}
|
|
48448
|
+
function classifyDelivery(probe, options = {}) {
|
|
48449
|
+
const { stats, now } = probe;
|
|
48450
|
+
const launchdDetail = probe.launchdError === void 0 ? void 0 : boundedHealthError(probe.launchdError);
|
|
48451
|
+
const daemon = probe.launchdError === void 0 ? classifyDaemonHealth(probe.snapshot, {
|
|
48452
|
+
disabled: probe.disabled,
|
|
48453
|
+
service: probe.service,
|
|
48454
|
+
ingestionStatus: probe.ingestionStatus,
|
|
48455
|
+
expectedVersion: probe.expectedVersion,
|
|
48456
|
+
backlogExpected: options.backlogExpected,
|
|
48457
|
+
backlog: {
|
|
48458
|
+
pendingCount: stats.pendingCount,
|
|
48459
|
+
pendingSampled: stats.sampled,
|
|
48460
|
+
oldestPendingAt: stats.oldestPendingAt
|
|
48461
|
+
},
|
|
48462
|
+
now
|
|
48463
|
+
}) : {
|
|
48464
|
+
name: "daemon",
|
|
48465
|
+
status: "fail",
|
|
48466
|
+
detail: `launchd status unavailable${launchdDetail ? `: ${launchdDetail}` : ""}`
|
|
48467
|
+
};
|
|
48468
|
+
const journal = classifyJournal(stats, now, {
|
|
48469
|
+
backlogExpected: options.backlogExpected === true && daemonCheckVouchesForDrain(daemon)
|
|
48076
48470
|
});
|
|
48471
|
+
return [daemon, journal, classifyStranded(stats)];
|
|
48077
48472
|
}
|
|
48078
|
-
function
|
|
48079
|
-
const stats = pendingJournalStats();
|
|
48473
|
+
function classifyJournal(stats, now, options = {}) {
|
|
48080
48474
|
const pending = stats.pendingCount;
|
|
48081
48475
|
const pendingLabel = stats.sampled ? `at least ${String(pending)}` : String(pending);
|
|
48476
|
+
const backlog = (detail) => options.backlogExpected ? {
|
|
48477
|
+
name: "journal",
|
|
48478
|
+
status: "warn",
|
|
48479
|
+
detail: `${formatPendingBacklog(
|
|
48480
|
+
{
|
|
48481
|
+
pendingCount: pending,
|
|
48482
|
+
pendingSampled: stats.sampled,
|
|
48483
|
+
oldestPendingAt: stats.oldestPendingAt
|
|
48484
|
+
},
|
|
48485
|
+
now
|
|
48486
|
+
)} \u2014 draining in the background`
|
|
48487
|
+
} : { name: "journal", status: "fail", detail };
|
|
48082
48488
|
if (pending === 0) {
|
|
48083
48489
|
if (stats.sampled) {
|
|
48084
|
-
return
|
|
48085
|
-
name: "journal",
|
|
48086
|
-
status: "fail",
|
|
48087
|
-
detail: "bounded journal sample could not prove the queue is empty"
|
|
48088
|
-
};
|
|
48490
|
+
return backlog("bounded journal sample could not prove the queue is empty");
|
|
48089
48491
|
}
|
|
48090
48492
|
return { name: "journal", status: "ok", detail: "no pending moves" };
|
|
48091
48493
|
}
|
|
48092
48494
|
if (stats.oldestPendingAt === void 0) {
|
|
48093
|
-
|
|
48094
|
-
|
|
48095
|
-
status: "fail",
|
|
48096
|
-
detail: `${pendingLabel} pending with no readable capture timestamp`
|
|
48097
|
-
};
|
|
48495
|
+
const detail = `${pendingLabel} pending with no readable capture timestamp`;
|
|
48496
|
+
return stats.sampled ? backlog(detail) : { name: "journal", status: "fail", detail };
|
|
48098
48497
|
}
|
|
48099
|
-
const oldestMs =
|
|
48498
|
+
const oldestMs = now - stats.oldestPendingAt;
|
|
48100
48499
|
const oldestS = Math.round(oldestMs / MS_PER_SECOND);
|
|
48101
48500
|
if (oldestMs > STALE_PENDING_MS) {
|
|
48102
|
-
return
|
|
48103
|
-
|
|
48104
|
-
|
|
48105
|
-
detail: `${pendingLabel} pending, oldest observed ${String(oldestS)}s \u2014 30s delivery SLA missed`
|
|
48106
|
-
};
|
|
48501
|
+
return backlog(
|
|
48502
|
+
`${pendingLabel} pending, oldest observed ${String(oldestS)}s \u2014 30s delivery SLA missed`
|
|
48503
|
+
);
|
|
48107
48504
|
}
|
|
48108
48505
|
if (stats.sampled) {
|
|
48109
|
-
return {
|
|
48110
|
-
name: "journal",
|
|
48111
|
-
status: "fail",
|
|
48112
|
-
detail: `${pendingLabel} pending in bounded sample; 30s delivery SLA cannot be proven`
|
|
48113
|
-
};
|
|
48506
|
+
return backlog(`${pendingLabel} pending in bounded sample; 30s delivery SLA cannot be proven`);
|
|
48114
48507
|
}
|
|
48115
48508
|
return { name: "journal", status: "ok", detail: `${String(pending)} pending, draining` };
|
|
48116
48509
|
}
|
|
48117
|
-
function
|
|
48118
|
-
const stats = pendingJournalStats();
|
|
48510
|
+
function classifyStranded(stats) {
|
|
48119
48511
|
if (stats.strandedFileCount === 0 && !stats.strandedSampled) {
|
|
48120
48512
|
return { name: "stranded", status: "ok", detail: "none" };
|
|
48121
48513
|
}
|
|
@@ -48141,15 +48533,10 @@ function classifyJournalOrganization(bucketCount, retainedBuckets) {
|
|
|
48141
48533
|
detail: "all pending buckets match the active credential"
|
|
48142
48534
|
};
|
|
48143
48535
|
}
|
|
48144
|
-
const reasonCounts = /* @__PURE__ */ new Map();
|
|
48145
|
-
for (const item of retainedBuckets) {
|
|
48146
|
-
reasonCounts.set(item.reason, (reasonCounts.get(item.reason) ?? 0) + 1);
|
|
48147
|
-
}
|
|
48148
|
-
const reasons = [...reasonCounts].sort(([left], [right]) => left.localeCompare(right)).map(([reason, count]) => `${reason}:${String(count)}`).join(", ");
|
|
48149
48536
|
return {
|
|
48150
48537
|
name: "journal-org",
|
|
48151
48538
|
status: "fail",
|
|
48152
|
-
detail: `${String(retainedBuckets.length)} bucket(s) retained (${
|
|
48539
|
+
detail: `${String(retainedBuckets.length)} bucket(s) retained (${summarizeRetainedBuckets(retainedBuckets)})`
|
|
48153
48540
|
};
|
|
48154
48541
|
}
|
|
48155
48542
|
async function checkJournalOrganization() {
|
|
@@ -48560,30 +48947,37 @@ function refreshOwnedGlobalHooksForHealth() {
|
|
|
48560
48947
|
} catch {
|
|
48561
48948
|
}
|
|
48562
48949
|
}
|
|
48563
|
-
async function
|
|
48950
|
+
async function independentChecks() {
|
|
48564
48951
|
refreshOwnedGlobalHooksForHealth();
|
|
48565
48952
|
const backend = await checkBackend();
|
|
48566
|
-
return
|
|
48567
|
-
checkAuth(),
|
|
48568
|
-
|
|
48569
|
-
|
|
48570
|
-
|
|
48571
|
-
|
|
48572
|
-
|
|
48573
|
-
|
|
48574
|
-
|
|
48575
|
-
|
|
48576
|
-
|
|
48577
|
-
|
|
48578
|
-
|
|
48579
|
-
|
|
48580
|
-
|
|
48953
|
+
return {
|
|
48954
|
+
before: [checkAuth()],
|
|
48955
|
+
after: [
|
|
48956
|
+
await checkJournalOrganization(),
|
|
48957
|
+
checkFeedbackHooks(),
|
|
48958
|
+
...checkAgentHooks(),
|
|
48959
|
+
checkHookRuntime(),
|
|
48960
|
+
await checkRepositoryBinding(),
|
|
48961
|
+
checkManagedHook("post-commit", inspectEffectivePostCommitHook),
|
|
48962
|
+
checkManagedHook("post-rewrite", inspectEffectivePostRewriteHook),
|
|
48963
|
+
...backend,
|
|
48964
|
+
await checkFeedbackCapability()
|
|
48965
|
+
]
|
|
48966
|
+
};
|
|
48967
|
+
}
|
|
48968
|
+
var DEFAULT_DOCTOR_PROBES = {
|
|
48969
|
+
delivery: probeDelivery,
|
|
48970
|
+
independent: independentChecks
|
|
48971
|
+
};
|
|
48972
|
+
async function collectChecks(options, probes) {
|
|
48973
|
+
const { before, after } = await probes.independent();
|
|
48974
|
+
return [...before, ...classifyDelivery(await probes.delivery(), options), ...after];
|
|
48581
48975
|
}
|
|
48582
48976
|
function icon(status) {
|
|
48583
48977
|
return status === "ok" ? "\u2713" : status === "warn" ? "\u26A0" : "\u2717";
|
|
48584
48978
|
}
|
|
48585
|
-
async function runDoctor() {
|
|
48586
|
-
const checks = await collectChecks();
|
|
48979
|
+
async function runDoctor(options, probes) {
|
|
48980
|
+
const checks = await collectChecks(options, probes);
|
|
48587
48981
|
const { json, exitCode } = classifyDoctor(checks);
|
|
48588
48982
|
const headline = json.status === "ok" ? "\u2713 healthy" : json.status === "warn" ? "\u26A0 degraded" : "\u2717 unhealthy";
|
|
48589
48983
|
process.stderr.write(`[prim] doctor: ${headline}
|
|
@@ -48597,101 +48991,104 @@ async function runDoctor() {
|
|
|
48597
48991
|
process.exitCode = exitCode;
|
|
48598
48992
|
}
|
|
48599
48993
|
}
|
|
48600
|
-
function registerDoctorCommands(program3) {
|
|
48994
|
+
function registerDoctorCommands(program3, dependencies = {}) {
|
|
48601
48995
|
program3.command("doctor").description(
|
|
48602
48996
|
"Check capture and feedback health end to end (auth, supervisor, delivery, server)"
|
|
48603
|
-
).action(async () => {
|
|
48604
|
-
await runDoctor(
|
|
48997
|
+
).addOption(new Option("--expect-backlog").hideHelp()).action(async (opts) => {
|
|
48998
|
+
await runDoctor(
|
|
48999
|
+
{ backlogExpected: opts.expectBacklog === true },
|
|
49000
|
+
dependencies.probes ?? DEFAULT_DOCTOR_PROBES
|
|
49001
|
+
);
|
|
48605
49002
|
});
|
|
48606
49003
|
}
|
|
48607
49004
|
|
|
48608
49005
|
// src/commands/moves.ts
|
|
48609
|
-
import { existsSync as
|
|
48610
|
-
import { join as
|
|
49006
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync12, unlinkSync as unlinkSync5, writeFileSync as writeFileSync9 } from "fs";
|
|
49007
|
+
import { join as join22 } from "path";
|
|
48611
49008
|
|
|
48612
49009
|
// src/flusher.ts
|
|
48613
|
-
import { createReadStream, renameSync as renameSync6, unlinkSync as unlinkSync4 } from "fs";
|
|
48614
|
-
import { dirname as
|
|
49010
|
+
import { createReadStream, renameSync as renameSync6, statSync as statSync5, unlinkSync as unlinkSync4 } from "fs";
|
|
49011
|
+
import { dirname as dirname14, join as join21 } from "path";
|
|
48615
49012
|
|
|
48616
49013
|
// src/dead-letter.ts
|
|
48617
49014
|
import { createHash as createHash9, randomBytes as randomBytes6 } from "crypto";
|
|
48618
49015
|
import {
|
|
48619
|
-
constants as
|
|
48620
|
-
chmodSync as
|
|
48621
|
-
closeSync as
|
|
48622
|
-
existsSync as
|
|
49016
|
+
constants as constants6,
|
|
49017
|
+
chmodSync as chmodSync6,
|
|
49018
|
+
closeSync as closeSync6,
|
|
49019
|
+
existsSync as existsSync17,
|
|
48623
49020
|
fchmodSync as fchmodSync3,
|
|
48624
49021
|
fsyncSync as fsyncSync3,
|
|
48625
49022
|
mkdirSync as mkdirSync11,
|
|
48626
|
-
openSync as
|
|
49023
|
+
openSync as openSync6,
|
|
48627
49024
|
renameSync as renameSync5,
|
|
48628
|
-
rmSync as
|
|
49025
|
+
rmSync as rmSync8,
|
|
48629
49026
|
writeFileSync as writeFileSync8
|
|
48630
49027
|
} from "fs";
|
|
48631
|
-
import { dirname as
|
|
48632
|
-
var
|
|
48633
|
-
var
|
|
49028
|
+
import { dirname as dirname13, join as join20 } from "path";
|
|
49029
|
+
var DIRECTORY_MODE4 = 448;
|
|
49030
|
+
var FILE_MODE3 = 384;
|
|
48634
49031
|
var DEAD_LETTER_DIRNAME = "dead-letter";
|
|
48635
49032
|
function deadLetterDirectoryForRotation(flushingPath) {
|
|
48636
|
-
return
|
|
49033
|
+
return join20(dirname13(flushingPath), DEAD_LETTER_DIRNAME);
|
|
48637
49034
|
}
|
|
48638
49035
|
function deadLetterPathForRawLine(flushingPath, rawLine) {
|
|
48639
49036
|
const quarantineId = createHash9("sha256").update(rawLine).digest("hex");
|
|
48640
|
-
return
|
|
49037
|
+
return join20(deadLetterDirectoryForRotation(flushingPath), `raw-${quarantineId}.json`);
|
|
48641
49038
|
}
|
|
48642
49039
|
function ensureDeadLetterDirectory(flushingPath, persistDirectory) {
|
|
48643
49040
|
const directory = deadLetterDirectoryForRotation(flushingPath);
|
|
48644
|
-
mkdirSync11(directory, { recursive: true, mode:
|
|
48645
|
-
|
|
48646
|
-
persistDirectory(
|
|
49041
|
+
mkdirSync11(directory, { recursive: true, mode: DIRECTORY_MODE4 });
|
|
49042
|
+
chmodSync6(directory, DIRECTORY_MODE4);
|
|
49043
|
+
persistDirectory(dirname13(directory));
|
|
48647
49044
|
return directory;
|
|
48648
49045
|
}
|
|
48649
|
-
function persistDeadLetter(directory, path9, quarantineId,
|
|
48650
|
-
if (
|
|
49046
|
+
function persistDeadLetter(directory, path9, quarantineId, record3, persistDirectory) {
|
|
49047
|
+
if (existsSync17(path9)) {
|
|
48651
49048
|
persistDirectory(directory);
|
|
48652
49049
|
return;
|
|
48653
49050
|
}
|
|
48654
|
-
const temporaryPath =
|
|
49051
|
+
const temporaryPath = join20(
|
|
48655
49052
|
directory,
|
|
48656
49053
|
`.${quarantineId}.${String(process.pid)}.${randomBytes6(8).toString("hex")}.tmp`
|
|
48657
49054
|
);
|
|
48658
|
-
const flags =
|
|
49055
|
+
const flags = constants6.O_CREAT | constants6.O_EXCL | constants6.O_WRONLY | constants6.O_NOFOLLOW;
|
|
48659
49056
|
try {
|
|
48660
|
-
const fd =
|
|
49057
|
+
const fd = openSync6(temporaryPath, flags, FILE_MODE3);
|
|
48661
49058
|
try {
|
|
48662
|
-
fchmodSync3(fd,
|
|
48663
|
-
writeFileSync8(fd, `${JSON.stringify(
|
|
49059
|
+
fchmodSync3(fd, FILE_MODE3);
|
|
49060
|
+
writeFileSync8(fd, `${JSON.stringify(record3)}
|
|
48664
49061
|
`, "utf8");
|
|
48665
49062
|
fsyncSync3(fd);
|
|
48666
49063
|
} finally {
|
|
48667
|
-
|
|
49064
|
+
closeSync6(fd);
|
|
48668
49065
|
}
|
|
48669
49066
|
renameSync5(temporaryPath, path9);
|
|
48670
49067
|
persistDirectory(directory);
|
|
48671
49068
|
} finally {
|
|
48672
|
-
|
|
49069
|
+
rmSync8(temporaryPath, { force: true });
|
|
48673
49070
|
}
|
|
48674
49071
|
}
|
|
48675
49072
|
function quarantineMove(flushingPath, move, reason, now = Date.now, options = {}) {
|
|
48676
49073
|
const persistDirectory = options.syncDirectory ?? syncDirectory;
|
|
48677
49074
|
const directory = ensureDeadLetterDirectory(flushingPath, persistDirectory);
|
|
48678
49075
|
const serializedMove = JSON.stringify(move);
|
|
48679
|
-
const
|
|
49076
|
+
const record3 = {
|
|
48680
49077
|
version: 1,
|
|
48681
49078
|
quarantineId: createHash9("sha256").update(serializedMove).digest("hex"),
|
|
48682
49079
|
quarantinedAt: now(),
|
|
48683
49080
|
reason,
|
|
48684
49081
|
move
|
|
48685
49082
|
};
|
|
48686
|
-
const path9 =
|
|
48687
|
-
persistDeadLetter(directory, path9,
|
|
48688
|
-
return
|
|
49083
|
+
const path9 = join20(directory, `${record3.quarantineId}.json`);
|
|
49084
|
+
persistDeadLetter(directory, path9, record3.quarantineId, record3, persistDirectory);
|
|
49085
|
+
return record3;
|
|
48689
49086
|
}
|
|
48690
49087
|
function quarantineRawLine(flushingPath, rawLine, now = Date.now, options = {}) {
|
|
48691
49088
|
const persistDirectory = options.syncDirectory ?? syncDirectory;
|
|
48692
49089
|
const directory = ensureDeadLetterDirectory(flushingPath, persistDirectory);
|
|
48693
49090
|
const bytes = Buffer.from(rawLine);
|
|
48694
|
-
const
|
|
49091
|
+
const record3 = {
|
|
48695
49092
|
version: 1,
|
|
48696
49093
|
recordKind: "raw_line_v1",
|
|
48697
49094
|
quarantineId: createHash9("sha256").update(bytes).digest("hex"),
|
|
@@ -48704,11 +49101,11 @@ function quarantineRawLine(flushingPath, rawLine, now = Date.now, options = {})
|
|
|
48704
49101
|
persistDeadLetter(
|
|
48705
49102
|
directory,
|
|
48706
49103
|
deadLetterPathForRawLine(flushingPath, bytes),
|
|
48707
|
-
|
|
48708
|
-
|
|
49104
|
+
record3.quarantineId,
|
|
49105
|
+
record3,
|
|
48709
49106
|
persistDirectory
|
|
48710
49107
|
);
|
|
48711
|
-
return
|
|
49108
|
+
return record3;
|
|
48712
49109
|
}
|
|
48713
49110
|
|
|
48714
49111
|
// src/ingest-response.ts
|
|
@@ -48742,9 +49139,13 @@ function requireDurableIngestAcknowledgement(value, expected, cwd = process.cwd(
|
|
|
48742
49139
|
|
|
48743
49140
|
// src/flusher.ts
|
|
48744
49141
|
var BATCH_SIZE = 500;
|
|
49142
|
+
var BATCH_MAX_BYTES = 1048576;
|
|
48745
49143
|
var HTTP_TIMEOUT_MS = 1e4;
|
|
48746
49144
|
var OPPORTUNISTIC_FLUSH_AFTER_MS = 6e4;
|
|
48747
49145
|
var ORPHAN_QUARANTINE_MS = 6e4;
|
|
49146
|
+
function exceedsBatchBytes(count, bytes, nextBytes, maxBytes) {
|
|
49147
|
+
return count > 0 && bytes + nextBytes > maxBytes;
|
|
49148
|
+
}
|
|
48748
49149
|
function addDrainCounts(left, right) {
|
|
48749
49150
|
return {
|
|
48750
49151
|
flushed: left.flushed + right.flushed,
|
|
@@ -48755,8 +49156,8 @@ function collectionScopeRoots(batch) {
|
|
|
48755
49156
|
return batch.flatMap((move) => {
|
|
48756
49157
|
const env4 = move.env;
|
|
48757
49158
|
if (typeof env4 !== "object" || env4 === null || Array.isArray(env4)) return [];
|
|
48758
|
-
const
|
|
48759
|
-
const root = typeof
|
|
49159
|
+
const record3 = env4;
|
|
49160
|
+
const root = typeof record3.gitRoot === "string" ? record3.gitRoot : record3.cwd;
|
|
48760
49161
|
return typeof root === "string" && root.length > 0 ? [root] : [];
|
|
48761
49162
|
});
|
|
48762
49163
|
}
|
|
@@ -48764,8 +49165,8 @@ function isVersionedInvalidMoveDisposition(body) {
|
|
|
48764
49165
|
if (typeof body !== "object" || body === null || Array.isArray(body)) {
|
|
48765
49166
|
return false;
|
|
48766
49167
|
}
|
|
48767
|
-
const
|
|
48768
|
-
return Object.keys(
|
|
49168
|
+
const record3 = body;
|
|
49169
|
+
return Object.keys(record3).length === 2 && record3.error === "invalid_move" && record3.errorVersion === 1;
|
|
48769
49170
|
}
|
|
48770
49171
|
function deadLetterReason(error) {
|
|
48771
49172
|
if (error instanceof InvalidJournalEnvelopeError) {
|
|
@@ -48794,23 +49195,25 @@ function mayBeBatchSizeFailure(error) {
|
|
|
48794
49195
|
}
|
|
48795
49196
|
return error instanceof Error && error.name === "TimeoutError";
|
|
48796
49197
|
}
|
|
48797
|
-
async function* rawJournalLines(input) {
|
|
49198
|
+
async function* rawJournalLines(input, offset) {
|
|
48798
49199
|
let pending = [];
|
|
48799
49200
|
let pendingBytes = 0;
|
|
49201
|
+
let end = offset;
|
|
48800
49202
|
for await (const value of input) {
|
|
48801
49203
|
const chunk2 = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
48802
49204
|
let start = 0;
|
|
48803
49205
|
let newline = chunk2.indexOf(10, start);
|
|
48804
49206
|
while (newline !== -1) {
|
|
48805
49207
|
const tail = chunk2.subarray(start, newline + 1);
|
|
48806
|
-
|
|
48807
|
-
|
|
48808
|
-
} else {
|
|
49208
|
+
let line = tail;
|
|
49209
|
+
if (pending.length > 0) {
|
|
48809
49210
|
pending.push(tail);
|
|
48810
|
-
|
|
49211
|
+
line = Buffer.concat(pending, pendingBytes + tail.length);
|
|
48811
49212
|
pending = [];
|
|
48812
49213
|
pendingBytes = 0;
|
|
48813
49214
|
}
|
|
49215
|
+
end += line.length;
|
|
49216
|
+
yield { bytes: line, end };
|
|
48814
49217
|
start = newline + 1;
|
|
48815
49218
|
newline = chunk2.indexOf(10, start);
|
|
48816
49219
|
}
|
|
@@ -48821,18 +49224,43 @@ async function* rawJournalLines(input) {
|
|
|
48821
49224
|
}
|
|
48822
49225
|
}
|
|
48823
49226
|
if (pendingBytes > 0) {
|
|
48824
|
-
|
|
49227
|
+
const bytes = pending.length === 1 ? pending[0] : Buffer.concat(pending, pendingBytes);
|
|
49228
|
+
yield { bytes, end: end + pendingBytes };
|
|
48825
49229
|
}
|
|
48826
49230
|
}
|
|
49231
|
+
var drainProgressWarned = false;
|
|
48827
49232
|
async function drainFlushingPath(flushingPath, client, binding, options = {}) {
|
|
49233
|
+
const rotation = rotationIdentity(statSync5(flushingPath));
|
|
49234
|
+
const resumeAt = drainResumeOffset(flushingPath, rotation);
|
|
49235
|
+
const recordProgress = (end) => {
|
|
49236
|
+
if (end === void 0 || end >= rotation.size) {
|
|
49237
|
+
return;
|
|
49238
|
+
}
|
|
49239
|
+
try {
|
|
49240
|
+
writeDrainCheckpoint(flushingPath, { v: 2, offset: end, ...rotation });
|
|
49241
|
+
drainProgressWarned = false;
|
|
49242
|
+
} catch (error) {
|
|
49243
|
+
if (!drainProgressWarned) {
|
|
49244
|
+
drainProgressWarned = true;
|
|
49245
|
+
process.stderr.write(
|
|
49246
|
+
`[prim] could not record drain progress: ${error instanceof Error ? error.message : String(error)}
|
|
49247
|
+
`
|
|
49248
|
+
);
|
|
49249
|
+
}
|
|
49250
|
+
}
|
|
49251
|
+
};
|
|
49252
|
+
const reportProgress = (delta) => {
|
|
49253
|
+
options.onProgress?.(delta);
|
|
49254
|
+
return delta;
|
|
49255
|
+
};
|
|
48828
49256
|
const postBatch = async (batch2) => {
|
|
49257
|
+
const moves = batch2.map((entry) => entry.move);
|
|
48829
49258
|
try {
|
|
48830
|
-
const request2 = buildOrganizationBoundMoveRequest(
|
|
49259
|
+
const request2 = buildOrganizationBoundMoveRequest(moves, binding);
|
|
48831
49260
|
const response = await client.post("/api/cli/moves/ingest", request2, {
|
|
48832
49261
|
signal: AbortSignal.timeout(HTTP_TIMEOUT_MS)
|
|
48833
49262
|
});
|
|
48834
|
-
requireDurableIngestAcknowledgement(response,
|
|
48835
|
-
return { flushed: batch2.length, quarantined: 0 };
|
|
49263
|
+
requireDurableIngestAcknowledgement(response, moves.length, collectionScopeRoots(moves));
|
|
48836
49264
|
} catch (error) {
|
|
48837
49265
|
const reason = deadLetterReason(error);
|
|
48838
49266
|
if (!(reason || mayBeBatchSizeFailure(error))) {
|
|
@@ -48847,33 +49275,43 @@ async function drainFlushingPath(flushingPath, client, binding, options = {}) {
|
|
|
48847
49275
|
if (!reason) {
|
|
48848
49276
|
throw error;
|
|
48849
49277
|
}
|
|
48850
|
-
const [
|
|
49278
|
+
const [entry] = batch2;
|
|
48851
49279
|
const quarantined = quarantineMove(
|
|
48852
49280
|
flushingPath,
|
|
48853
|
-
move,
|
|
49281
|
+
entry.move,
|
|
48854
49282
|
reason,
|
|
48855
49283
|
Date.now,
|
|
48856
49284
|
options.deadLetterPersistence
|
|
48857
49285
|
);
|
|
49286
|
+
recordProgress(entry.end);
|
|
48858
49287
|
process.stderr.write(
|
|
48859
49288
|
`[prim] quarantined rejected move ${quarantined.quarantineId.slice(0, 12)} (${reason})
|
|
48860
49289
|
`
|
|
48861
49290
|
);
|
|
48862
|
-
return { flushed: 0, quarantined: 1 };
|
|
49291
|
+
return reportProgress({ flushed: 0, quarantined: 1 });
|
|
48863
49292
|
}
|
|
49293
|
+
recordProgress(batch2.at(-1)?.end);
|
|
49294
|
+
return reportProgress({ flushed: batch2.length, quarantined: 0 });
|
|
48864
49295
|
};
|
|
48865
|
-
const input = createReadStream(flushingPath);
|
|
49296
|
+
const input = createReadStream(flushingPath, { start: resumeAt });
|
|
48866
49297
|
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
48867
49298
|
let batch = [];
|
|
49299
|
+
let batchBytes = 0;
|
|
48868
49300
|
let counts = { flushed: 0, quarantined: 0 };
|
|
49301
|
+
const postPending = async () => {
|
|
49302
|
+
counts = addDrainCounts(counts, await postBatch(batch));
|
|
49303
|
+
batch = [];
|
|
49304
|
+
batchBytes = 0;
|
|
49305
|
+
};
|
|
48869
49306
|
try {
|
|
48870
|
-
for await (const rawLine of rawJournalLines(input)) {
|
|
49307
|
+
for await (const { bytes: rawLine, end } of rawJournalLines(input, resumeAt)) {
|
|
48871
49308
|
if (rawLine.length === 1 && rawLine[0] === 10 || rawLine.length === 2 && rawLine[0] === 13 && rawLine[1] === 10) {
|
|
48872
49309
|
continue;
|
|
48873
49310
|
}
|
|
49311
|
+
let move;
|
|
48874
49312
|
try {
|
|
48875
49313
|
const line = decoder.decode(rawLine);
|
|
48876
|
-
|
|
49314
|
+
move = JSON.parse(line);
|
|
48877
49315
|
} catch {
|
|
48878
49316
|
const quarantined = quarantineRawLine(
|
|
48879
49317
|
flushingPath,
|
|
@@ -48885,24 +49323,35 @@ async function drainFlushingPath(flushingPath, client, binding, options = {}) {
|
|
|
48885
49323
|
`[prim] quarantined invalid journal line ${quarantined.quarantineId.slice(0, 12)} (invalid_move)
|
|
48886
49324
|
`
|
|
48887
49325
|
);
|
|
48888
|
-
counts
|
|
49326
|
+
counts = addDrainCounts(counts, reportProgress({ flushed: 0, quarantined: 1 }));
|
|
49327
|
+
if (batch.length === 0) {
|
|
49328
|
+
recordProgress(end);
|
|
49329
|
+
}
|
|
48889
49330
|
continue;
|
|
48890
49331
|
}
|
|
48891
|
-
if (batch.length
|
|
48892
|
-
|
|
48893
|
-
|
|
49332
|
+
if (exceedsBatchBytes(batch.length, batchBytes, rawLine.length, BATCH_MAX_BYTES)) {
|
|
49333
|
+
await postPending();
|
|
49334
|
+
}
|
|
49335
|
+
batch.push({ move, end });
|
|
49336
|
+
batchBytes += rawLine.length;
|
|
49337
|
+
if (batch.length >= BATCH_SIZE) {
|
|
49338
|
+
await postPending();
|
|
48894
49339
|
}
|
|
48895
49340
|
}
|
|
48896
49341
|
if (batch.length > 0) {
|
|
48897
|
-
|
|
49342
|
+
await postPending();
|
|
48898
49343
|
}
|
|
48899
49344
|
} finally {
|
|
48900
49345
|
input.destroy();
|
|
48901
49346
|
}
|
|
48902
49347
|
unlinkSync4(flushingPath);
|
|
49348
|
+
try {
|
|
49349
|
+
removeDrainCheckpoint(flushingPath);
|
|
49350
|
+
} catch {
|
|
49351
|
+
}
|
|
48903
49352
|
return counts;
|
|
48904
49353
|
}
|
|
48905
|
-
async function drainPath(path9, client, binding) {
|
|
49354
|
+
async function drainPath(path9, client, binding, onProgress) {
|
|
48906
49355
|
const tmpPath = `${path9}.flushing.${String(Date.now())}.${String(process.pid)}`;
|
|
48907
49356
|
try {
|
|
48908
49357
|
renameSync6(path9, tmpPath);
|
|
@@ -48912,7 +49361,19 @@ async function drainPath(path9, client, binding) {
|
|
|
48912
49361
|
}
|
|
48913
49362
|
throw err;
|
|
48914
49363
|
}
|
|
48915
|
-
return drainFlushingPath(tmpPath, client, binding);
|
|
49364
|
+
return drainFlushingPath(tmpPath, client, binding, { onProgress });
|
|
49365
|
+
}
|
|
49366
|
+
async function settleDrain(drain) {
|
|
49367
|
+
let progress = { flushed: 0, quarantined: 0 };
|
|
49368
|
+
try {
|
|
49369
|
+
return {
|
|
49370
|
+
counts: await drain((delta) => {
|
|
49371
|
+
progress = addDrainCounts(progress, delta);
|
|
49372
|
+
})
|
|
49373
|
+
};
|
|
49374
|
+
} catch (error) {
|
|
49375
|
+
return { counts: progress, failure: { error } };
|
|
49376
|
+
}
|
|
48916
49377
|
}
|
|
48917
49378
|
function selectRecoverable(files, now, opts = {}) {
|
|
48918
49379
|
const quarantineMs = opts.quarantineMs ?? ORPHAN_QUARANTINE_MS;
|
|
@@ -48929,7 +49390,8 @@ async function recoverOrphans(candidates, options) {
|
|
|
48929
49390
|
flushed: 0,
|
|
48930
49391
|
quarantined: 0,
|
|
48931
49392
|
errors: [],
|
|
48932
|
-
failedBuckets: /* @__PURE__ */ new Set()
|
|
49393
|
+
failedBuckets: /* @__PURE__ */ new Set(),
|
|
49394
|
+
failedDrainFlushed: 0
|
|
48933
49395
|
};
|
|
48934
49396
|
const recoverable = selectRecoverable(candidates, options.now ?? Date.now(), {
|
|
48935
49397
|
ownerPid: options.ownerPid ?? process.pid,
|
|
@@ -48940,28 +49402,41 @@ async function recoverOrphans(candidates, options) {
|
|
|
48940
49402
|
if (summary.failedBuckets.has(file.bucket)) {
|
|
48941
49403
|
continue;
|
|
48942
49404
|
}
|
|
48943
|
-
|
|
48944
|
-
|
|
48945
|
-
|
|
48946
|
-
|
|
48947
|
-
|
|
48948
|
-
summary.errors.push(
|
|
49405
|
+
const { counts, failure: failure4 } = await settleDrain((onProgress) => drain(file.path, onProgress));
|
|
49406
|
+
summary.flushed += counts.flushed;
|
|
49407
|
+
summary.quarantined += counts.quarantined;
|
|
49408
|
+
if (failure4) {
|
|
49409
|
+
summary.failedDrainFlushed += counts.flushed;
|
|
49410
|
+
summary.errors.push(failure4.error);
|
|
48949
49411
|
summary.failedBuckets.add(file.bucket);
|
|
48950
49412
|
}
|
|
48951
49413
|
}
|
|
48952
49414
|
return summary;
|
|
48953
49415
|
}
|
|
48954
49416
|
var FlushError = class extends Error {
|
|
49417
|
+
/** Moves the whole sweep acknowledged, across every drain it ran. */
|
|
48955
49418
|
flushed;
|
|
48956
49419
|
quarantined;
|
|
48957
|
-
|
|
49420
|
+
/** Buckets the failed sweep held back without sending, as on success. */
|
|
49421
|
+
retained;
|
|
49422
|
+
/**
|
|
49423
|
+
* Of `flushed`, the Moves acknowledged by the drains that failed, before
|
|
49424
|
+
* they failed. Only this shows the failure itself is advancing: `flushed`
|
|
49425
|
+
* also counts other buckets' drains, so a bucket whose drain fails every
|
|
49426
|
+
* time would read as progress while a sibling bucket kept delivering.
|
|
49427
|
+
*/
|
|
49428
|
+
failedDrainFlushed;
|
|
49429
|
+
constructor(cause, flushed, quarantined, retained = [], failedDrainFlushed = 0) {
|
|
48958
49430
|
super(cause instanceof Error ? cause.message : String(cause), { cause });
|
|
48959
49431
|
this.name = "FlushError";
|
|
48960
49432
|
this.flushed = flushed;
|
|
48961
49433
|
this.quarantined = quarantined;
|
|
49434
|
+
this.retained = retained;
|
|
49435
|
+
this.failedDrainFlushed = failedDrainFlushed;
|
|
48962
49436
|
}
|
|
48963
49437
|
};
|
|
48964
49438
|
async function flushOnce() {
|
|
49439
|
+
sweepOrphanedDrainProgress();
|
|
48965
49440
|
const orphanCandidates = listFlushing({ sampleBytes: 0 });
|
|
48966
49441
|
const liveBuckets = listBuckets();
|
|
48967
49442
|
if (orphanCandidates.length === 0 && liveBuckets.length === 0) {
|
|
@@ -48978,10 +49453,11 @@ async function flushOnce() {
|
|
|
48978
49453
|
const binding = inspection.binding;
|
|
48979
49454
|
const recovered = await recoverOrphans(
|
|
48980
49455
|
orphanCandidates.filter((file) => inspection.deliverableBuckets.has(file.bucket)),
|
|
48981
|
-
{ drain: (path9) => drainFlushingPath(path9, client, binding) }
|
|
49456
|
+
{ drain: (path9, onProgress) => drainFlushingPath(path9, client, binding, { onProgress }) }
|
|
48982
49457
|
);
|
|
48983
49458
|
let total = recovered.flushed;
|
|
48984
49459
|
let quarantined = recovered.quarantined;
|
|
49460
|
+
let failedDrainFlushed = recovered.failedDrainFlushed;
|
|
48985
49461
|
const errors = recovered.errors;
|
|
48986
49462
|
for (const { bucket, path: path9 } of liveBuckets) {
|
|
48987
49463
|
if (!inspection.deliverableBuckets.has(bucket)) {
|
|
@@ -48990,21 +49466,29 @@ async function flushOnce() {
|
|
|
48990
49466
|
if (recovered.failedBuckets.has(bucket)) {
|
|
48991
49467
|
continue;
|
|
48992
49468
|
}
|
|
48993
|
-
|
|
48994
|
-
|
|
48995
|
-
|
|
48996
|
-
|
|
48997
|
-
|
|
48998
|
-
|
|
49469
|
+
const { counts, failure: failure4 } = await settleDrain(
|
|
49470
|
+
(onProgress) => drainPath(path9, client, binding, onProgress)
|
|
49471
|
+
);
|
|
49472
|
+
total += counts.flushed;
|
|
49473
|
+
quarantined += counts.quarantined;
|
|
49474
|
+
if (failure4) {
|
|
49475
|
+
failedDrainFlushed += counts.flushed;
|
|
49476
|
+
errors.push(failure4.error);
|
|
48999
49477
|
}
|
|
49000
49478
|
}
|
|
49001
49479
|
if (errors.length > 0) {
|
|
49002
|
-
throw new FlushError(
|
|
49480
|
+
throw new FlushError(
|
|
49481
|
+
errors[0],
|
|
49482
|
+
total,
|
|
49483
|
+
quarantined,
|
|
49484
|
+
inspection.retainedBuckets,
|
|
49485
|
+
failedDrainFlushed
|
|
49486
|
+
);
|
|
49003
49487
|
}
|
|
49004
49488
|
return inspection.retainedBuckets.length > 0 ? { flushed: total, quarantined, retained: inspection.retainedBuckets } : { flushed: total, quarantined };
|
|
49005
49489
|
}
|
|
49006
49490
|
var flushInFlight;
|
|
49007
|
-
var FLUSH_LOCK_PATH =
|
|
49491
|
+
var FLUSH_LOCK_PATH = join21(dirname14(JOURNAL_DIR), ".flush.lock");
|
|
49008
49492
|
var FLUSH_LOCK_TIMEOUT_MS = 250;
|
|
49009
49493
|
function isFlushLockContended(error) {
|
|
49010
49494
|
return error instanceof Error && error.message.startsWith("timed out waiting for file lock");
|
|
@@ -49029,18 +49513,16 @@ function flush() {
|
|
|
49029
49513
|
return attempt;
|
|
49030
49514
|
}
|
|
49031
49515
|
function shouldFlushPending(stats, now, thresholdMs = OPPORTUNISTIC_FLUSH_AFTER_MS) {
|
|
49032
|
-
if (stats.sampled) {
|
|
49516
|
+
if (stats.sampled || stats.strandedFileCount > 0 && stats.strandedCount === 0) {
|
|
49033
49517
|
return true;
|
|
49034
49518
|
}
|
|
49035
49519
|
return stats.pendingCount > 0 && (stats.oldestPendingAt === void 0 || now - stats.oldestPendingAt > thresholdMs);
|
|
49036
49520
|
}
|
|
49037
|
-
|
|
49521
|
+
function journalNeedsFlush(now = Date.now()) {
|
|
49038
49522
|
try {
|
|
49039
|
-
|
|
49040
|
-
if (shouldFlushPending(stats, Date.now())) {
|
|
49041
|
-
await flush();
|
|
49042
|
-
}
|
|
49523
|
+
return shouldFlushPending(pendingJournalStats(), now);
|
|
49043
49524
|
} catch {
|
|
49525
|
+
return false;
|
|
49044
49526
|
}
|
|
49045
49527
|
}
|
|
49046
49528
|
|
|
@@ -49053,7 +49535,7 @@ var EVENT_COL_WIDTH = 20;
|
|
|
49053
49535
|
var BUCKET_COL_WIDTH = 20;
|
|
49054
49536
|
var TAIL_BUCKET_COL_WIDTH = 12;
|
|
49055
49537
|
var DIR_MODE = 448;
|
|
49056
|
-
var
|
|
49538
|
+
var FILE_MODE4 = 384;
|
|
49057
49539
|
var WORKSPACE_FILE = ".prim/workspace.json";
|
|
49058
49540
|
function registerMovesCommands(program3) {
|
|
49059
49541
|
const moves = program3.command("moves").description("Decision Event Pipeline \u2014 local journal");
|
|
@@ -49099,7 +49581,7 @@ function registerMovesCommands(program3) {
|
|
|
49099
49581
|
}
|
|
49100
49582
|
if (stranded.length > 0) {
|
|
49101
49583
|
const moveCount = stranded.reduce((n, f) => n + f.lineCount, 0);
|
|
49102
|
-
const byteCount = stranded.reduce((n, f) => n + f.
|
|
49584
|
+
const byteCount = stranded.reduce((n, f) => n + f.pendingBytes, 0);
|
|
49103
49585
|
const sampled = stranded.some((file) => file.sampled);
|
|
49104
49586
|
const qualifier = sampled ? "at least " : "";
|
|
49105
49587
|
console.log(
|
|
@@ -49110,7 +49592,7 @@ function registerMovesCommands(program3) {
|
|
|
49110
49592
|
const owner = f.pid === void 0 ? "no pid" : `pid ${String(f.pid)}`;
|
|
49111
49593
|
const count = `${String(f.lineCount)}${f.sampled ? "+" : ""}`;
|
|
49112
49594
|
console.log(
|
|
49113
|
-
` ${f.bucket.padEnd(BUCKET_COL_WIDTH)} ${count.padStart(5)} stranded, ${String(f.
|
|
49595
|
+
` ${f.bucket.padEnd(BUCKET_COL_WIDTH)} ${count.padStart(5)} stranded, ${String(f.pendingBytes).padStart(8)} bytes, ${String(ageS)}s ago (${owner})`
|
|
49114
49596
|
);
|
|
49115
49597
|
}
|
|
49116
49598
|
}
|
|
@@ -49138,19 +49620,19 @@ function registerMovesCommands(program3) {
|
|
|
49138
49620
|
}
|
|
49139
49621
|
});
|
|
49140
49622
|
moves.command("bind").description("Pin the current directory to an org via .prim/workspace.json").requiredOption("--orgId <orgId>", "Convex organization id").action((opts) => {
|
|
49141
|
-
const dir =
|
|
49142
|
-
if (!
|
|
49623
|
+
const dir = join22(process.cwd(), ".prim");
|
|
49624
|
+
if (!existsSync18(dir)) {
|
|
49143
49625
|
mkdirSync12(dir, { recursive: true, mode: DIR_MODE });
|
|
49144
49626
|
}
|
|
49145
|
-
const file =
|
|
49627
|
+
const file = join22(process.cwd(), WORKSPACE_FILE);
|
|
49146
49628
|
writeFileSync9(file, JSON.stringify({ orgId: opts.orgId, boundAt: Date.now() }, null, 2), {
|
|
49147
|
-
mode:
|
|
49629
|
+
mode: FILE_MODE4
|
|
49148
49630
|
});
|
|
49149
49631
|
console.log(`[prim] bound ${process.cwd()} to org ${opts.orgId}`);
|
|
49150
49632
|
});
|
|
49151
49633
|
moves.command("drop").description("Remove the .prim/workspace.json binding from the cwd").action(() => {
|
|
49152
|
-
const file =
|
|
49153
|
-
if (!
|
|
49634
|
+
const file = join22(process.cwd(), WORKSPACE_FILE);
|
|
49635
|
+
if (!existsSync18(file)) {
|
|
49154
49636
|
console.log("[prim] no workspace binding in cwd");
|
|
49155
49637
|
return;
|
|
49156
49638
|
}
|
|
@@ -49241,31 +49723,31 @@ function registerReconcileCommands(program3) {
|
|
|
49241
49723
|
|
|
49242
49724
|
// src/commands/session.ts
|
|
49243
49725
|
import {
|
|
49244
|
-
existsSync as
|
|
49726
|
+
existsSync as existsSync20,
|
|
49245
49727
|
mkdirSync as mkdirSync13,
|
|
49246
|
-
readFileSync as
|
|
49247
|
-
readdirSync as
|
|
49728
|
+
readFileSync as readFileSync18,
|
|
49729
|
+
readdirSync as readdirSync6,
|
|
49248
49730
|
unlinkSync as unlinkSync6,
|
|
49249
49731
|
writeFileSync as writeFileSync10
|
|
49250
49732
|
} from "fs";
|
|
49251
|
-
import { join as
|
|
49733
|
+
import { join as join24 } from "path";
|
|
49252
49734
|
|
|
49253
49735
|
// src/binding.ts
|
|
49254
|
-
import { existsSync as
|
|
49255
|
-
import { dirname as
|
|
49736
|
+
import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
|
|
49737
|
+
import { dirname as dirname15, join as join23 } from "path";
|
|
49256
49738
|
var PRIM_CONFIG_DIR = primConfigDirectory();
|
|
49257
|
-
var SESSIONS_DIR =
|
|
49739
|
+
var SESSIONS_DIR = join23(PRIM_CONFIG_DIR, "sessions");
|
|
49258
49740
|
|
|
49259
49741
|
// src/commands/session.ts
|
|
49260
49742
|
var DIR_MODE2 = 448;
|
|
49261
|
-
var
|
|
49743
|
+
var FILE_MODE5 = 384;
|
|
49262
49744
|
function ensureDir() {
|
|
49263
|
-
if (!
|
|
49745
|
+
if (!existsSync20(SESSIONS_DIR)) {
|
|
49264
49746
|
mkdirSync13(SESSIONS_DIR, { recursive: true, mode: DIR_MODE2 });
|
|
49265
49747
|
}
|
|
49266
49748
|
}
|
|
49267
49749
|
function markerPath(sessionId) {
|
|
49268
|
-
return
|
|
49750
|
+
return join24(SESSIONS_DIR, `${sessionId}.json`);
|
|
49269
49751
|
}
|
|
49270
49752
|
function registerSessionCommands(program3) {
|
|
49271
49753
|
const session = program3.command("session").description("Decision Event Pipeline \u2014 session binding markers");
|
|
@@ -49276,16 +49758,16 @@ function registerSessionCommands(program3) {
|
|
|
49276
49758
|
startedAt: Date.now()
|
|
49277
49759
|
};
|
|
49278
49760
|
writeFileSync10(markerPath(sessionId), JSON.stringify(marker, null, 2), {
|
|
49279
|
-
mode:
|
|
49761
|
+
mode: FILE_MODE5
|
|
49280
49762
|
});
|
|
49281
49763
|
console.log(`[prim] session ${sessionId} bound to org ${opts.orgId}`);
|
|
49282
49764
|
});
|
|
49283
49765
|
session.command("list").description("List active session markers").action(() => {
|
|
49284
|
-
if (!
|
|
49766
|
+
if (!existsSync20(SESSIONS_DIR)) {
|
|
49285
49767
|
console.log("[prim] no session markers");
|
|
49286
49768
|
return;
|
|
49287
49769
|
}
|
|
49288
|
-
const files =
|
|
49770
|
+
const files = readdirSync6(SESSIONS_DIR).filter((f) => f.endsWith(".json"));
|
|
49289
49771
|
if (files.length === 0) {
|
|
49290
49772
|
console.log("[prim] no session markers");
|
|
49291
49773
|
return;
|
|
@@ -49293,7 +49775,7 @@ function registerSessionCommands(program3) {
|
|
|
49293
49775
|
for (const f of files) {
|
|
49294
49776
|
const sessionId = f.replace(/\.json$/, "");
|
|
49295
49777
|
try {
|
|
49296
|
-
const m = JSON.parse(
|
|
49778
|
+
const m = JSON.parse(readFileSync18(join24(SESSIONS_DIR, f), "utf-8"));
|
|
49297
49779
|
console.log(`${sessionId} org=${m.orgId}`);
|
|
49298
49780
|
} catch {
|
|
49299
49781
|
}
|
|
@@ -49301,7 +49783,7 @@ function registerSessionCommands(program3) {
|
|
|
49301
49783
|
});
|
|
49302
49784
|
session.command("drop <sessionId>").description("Remove a session marker").action((sessionId) => {
|
|
49303
49785
|
const p = markerPath(sessionId);
|
|
49304
|
-
if (!
|
|
49786
|
+
if (!existsSync20(p)) {
|
|
49305
49787
|
console.log(`[prim] no marker for session ${sessionId}`);
|
|
49306
49788
|
return;
|
|
49307
49789
|
}
|
|
@@ -49312,10 +49794,24 @@ function registerSessionCommands(program3) {
|
|
|
49312
49794
|
|
|
49313
49795
|
// src/commands/setup.ts
|
|
49314
49796
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
49315
|
-
import { existsSync as
|
|
49316
|
-
import { join as
|
|
49797
|
+
import { existsSync as existsSync21, readFileSync as readFileSync19 } from "fs";
|
|
49798
|
+
import { join as join25 } from "path";
|
|
49317
49799
|
var EXIT_INCOMPLETE = 1;
|
|
49318
49800
|
var EXIT_USAGE3 = 2;
|
|
49801
|
+
var SETUP_ORCHESTRATOR_ENV = "PRIM_SETUP_ORCHESTRATOR";
|
|
49802
|
+
var SETUP_DAEMON_DRAINS_ENV = "PRIM_SETUP_DAEMON_DRAINS";
|
|
49803
|
+
function setupStepSpawnOptions(capture, env4 = process.env, options = {}) {
|
|
49804
|
+
return {
|
|
49805
|
+
env: {
|
|
49806
|
+
...env4,
|
|
49807
|
+
[SETUP_ORCHESTRATOR_ENV]: "1",
|
|
49808
|
+
// Unset rather than inherited when this setup starts no daemon.
|
|
49809
|
+
[SETUP_DAEMON_DRAINS_ENV]: options.startsDaemon === true ? "1" : void 0
|
|
49810
|
+
},
|
|
49811
|
+
stdio: capture ? ["inherit", "pipe", "ignore"] : "inherit",
|
|
49812
|
+
encoding: "utf-8"
|
|
49813
|
+
};
|
|
49814
|
+
}
|
|
49319
49815
|
var SESSION_LABELS = {
|
|
49320
49816
|
claude: "Claude Code integration",
|
|
49321
49817
|
codex: "Codex integration",
|
|
@@ -49366,7 +49862,7 @@ function planSetupSteps(opts) {
|
|
|
49366
49862
|
steps.push({
|
|
49367
49863
|
key: "health",
|
|
49368
49864
|
label: "Capture health",
|
|
49369
|
-
args: ["doctor"],
|
|
49865
|
+
args: ["doctor", "--expect-backlog"],
|
|
49370
49866
|
required: true
|
|
49371
49867
|
});
|
|
49372
49868
|
}
|
|
@@ -49395,8 +49891,8 @@ function detectProjectConflicts(agent, run) {
|
|
|
49395
49891
|
}
|
|
49396
49892
|
try {
|
|
49397
49893
|
const root = gitToplevel();
|
|
49398
|
-
const preCommit = root &&
|
|
49399
|
-
if (preCommit &&
|
|
49894
|
+
const preCommit = root && join25(root, ".git", "hooks", "pre-commit");
|
|
49895
|
+
if (preCommit && existsSync21(preCommit) && readFileSync19(preCommit, "utf-8").includes("prim-pre-commit")) {
|
|
49400
49896
|
conflicts.push(CONFLICT_HOOKS);
|
|
49401
49897
|
}
|
|
49402
49898
|
} catch {
|
|
@@ -49466,10 +49962,11 @@ function registerSetupCommand(program3, dependencies = {}) {
|
|
|
49466
49962
|
const self = process.argv[1];
|
|
49467
49963
|
const globals = program3.optsWithGlobals();
|
|
49468
49964
|
const run = dependencies.run ?? ((args, capture = false) => {
|
|
49469
|
-
const r = spawnSync2(
|
|
49470
|
-
|
|
49471
|
-
|
|
49472
|
-
|
|
49965
|
+
const r = spawnSync2(
|
|
49966
|
+
process.execPath,
|
|
49967
|
+
[self, ...args],
|
|
49968
|
+
setupStepSpawnOptions(capture, process.env, { startsDaemon: opts.daemon })
|
|
49969
|
+
);
|
|
49473
49970
|
return { code: r.status ?? 1, stdout: capture ? r.stdout ?? "" : "" };
|
|
49474
49971
|
});
|
|
49475
49972
|
const results = {};
|
|
@@ -49562,20 +50059,20 @@ function registerSetupCommand(program3, dependencies = {}) {
|
|
|
49562
50059
|
// src/commands/skill.ts
|
|
49563
50060
|
import { createHash as createHash11 } from "crypto";
|
|
49564
50061
|
import {
|
|
49565
|
-
constants as
|
|
49566
|
-
closeSync as
|
|
49567
|
-
existsSync as
|
|
49568
|
-
fstatSync as
|
|
49569
|
-
lstatSync as
|
|
49570
|
-
openSync as
|
|
49571
|
-
readFileSync as
|
|
49572
|
-
readSync as
|
|
49573
|
-
readdirSync as
|
|
49574
|
-
rmdirSync as
|
|
50062
|
+
constants as constants8,
|
|
50063
|
+
closeSync as closeSync8,
|
|
50064
|
+
existsSync as existsSync23,
|
|
50065
|
+
fstatSync as fstatSync4,
|
|
50066
|
+
lstatSync as lstatSync8,
|
|
50067
|
+
openSync as openSync8,
|
|
50068
|
+
readFileSync as readFileSync21,
|
|
50069
|
+
readSync as readSync4,
|
|
50070
|
+
readdirSync as readdirSync8,
|
|
50071
|
+
rmdirSync as rmdirSync3,
|
|
49575
50072
|
unlinkSync as unlinkSync7
|
|
49576
50073
|
} from "fs";
|
|
49577
50074
|
import { homedir as homedir11 } from "os";
|
|
49578
|
-
import { dirname as
|
|
50075
|
+
import { dirname as dirname17, join as join28, resolve as resolve8 } from "path";
|
|
49579
50076
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
49580
50077
|
|
|
49581
50078
|
// node_modules/.pnpm/diff@5.2.2/node_modules/diff/lib/index.mjs
|
|
@@ -49745,7 +50242,7 @@ Diff.prototype = {
|
|
|
49745
50242
|
tokenize: function tokenize(value) {
|
|
49746
50243
|
return value.split("");
|
|
49747
50244
|
},
|
|
49748
|
-
join: function
|
|
50245
|
+
join: function join26(chars) {
|
|
49749
50246
|
return chars.join("");
|
|
49750
50247
|
}
|
|
49751
50248
|
};
|
|
@@ -50083,30 +50580,30 @@ function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) {
|
|
|
50083
50580
|
// src/commands/claude-plugin.ts
|
|
50084
50581
|
import { createHash as createHash10 } from "crypto";
|
|
50085
50582
|
import {
|
|
50086
|
-
constants as
|
|
50087
|
-
closeSync as
|
|
50088
|
-
existsSync as
|
|
50089
|
-
fstatSync as
|
|
50090
|
-
lstatSync as
|
|
50583
|
+
constants as constants7,
|
|
50584
|
+
closeSync as closeSync7,
|
|
50585
|
+
existsSync as existsSync22,
|
|
50586
|
+
fstatSync as fstatSync3,
|
|
50587
|
+
lstatSync as lstatSync7,
|
|
50091
50588
|
mkdirSync as mkdirSync14,
|
|
50092
|
-
openSync as
|
|
50093
|
-
readFileSync as
|
|
50094
|
-
readSync as
|
|
50095
|
-
readdirSync as
|
|
50589
|
+
openSync as openSync7,
|
|
50590
|
+
readFileSync as readFileSync20,
|
|
50591
|
+
readSync as readSync3,
|
|
50592
|
+
readdirSync as readdirSync7,
|
|
50096
50593
|
realpathSync as realpathSync4,
|
|
50097
|
-
rmSync as
|
|
50098
|
-
rmdirSync
|
|
50594
|
+
rmSync as rmSync9,
|
|
50595
|
+
rmdirSync as rmdirSync2
|
|
50099
50596
|
} from "fs";
|
|
50100
50597
|
import { homedir as homedir10 } from "os";
|
|
50101
|
-
import { dirname as
|
|
50598
|
+
import { dirname as dirname16, isAbsolute as isAbsolute8, join as join27, relative as relative2, resolve as resolve7, sep as sep2 } from "path";
|
|
50102
50599
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
50103
|
-
var __dirname4 =
|
|
50600
|
+
var __dirname4 = dirname16(fileURLToPath4(import.meta.url));
|
|
50104
50601
|
var PLUGIN_DESCRIPTION = "Primitive decision-graph guidance for the prim CLI \u2014 a model-invoked skill installed by prim skill install.";
|
|
50105
50602
|
var MAX_MANIFEST_BYTES2 = 64 * 1024;
|
|
50106
50603
|
var MAX_SKILL_BYTES = 1024 * 1024;
|
|
50107
50604
|
function pluginDirFor(cwd, scope) {
|
|
50108
|
-
const base = scope === "user" ?
|
|
50109
|
-
return
|
|
50605
|
+
const base = scope === "user" ? join27(homedir10(), ".claude") : join27(gitToplevel(cwd) ?? cwd, ".claude");
|
|
50606
|
+
return join27(base, "skills", "prim");
|
|
50110
50607
|
}
|
|
50111
50608
|
function resolvePluginDir(cwd, scope) {
|
|
50112
50609
|
if (scope && scope !== "user" && scope !== "project") {
|
|
@@ -50117,10 +50614,10 @@ function resolvePluginDir(cwd, scope) {
|
|
|
50117
50614
|
}
|
|
50118
50615
|
function packageVersion2() {
|
|
50119
50616
|
let dir = __dirname4;
|
|
50120
|
-
while (dir !==
|
|
50617
|
+
while (dir !== dirname16(dir)) {
|
|
50121
50618
|
const p = resolve7(dir, "package.json");
|
|
50122
|
-
if (
|
|
50123
|
-
dir =
|
|
50619
|
+
if (existsSync22(p)) return JSON.parse(readFileSync20(p, "utf-8")).version;
|
|
50620
|
+
dir = dirname16(dir);
|
|
50124
50621
|
}
|
|
50125
50622
|
return "0.0.0";
|
|
50126
50623
|
}
|
|
@@ -50134,12 +50631,12 @@ function renderClaudePlugin() {
|
|
|
50134
50631
|
return { manifest: pluginManifest(version), skill: loadSkill(), version };
|
|
50135
50632
|
}
|
|
50136
50633
|
function removeDirIfEmpty(dir) {
|
|
50137
|
-
if (
|
|
50634
|
+
if (existsSync22(dir) && readdirSync7(dir).length === 0) rmdirSync2(dir);
|
|
50138
50635
|
}
|
|
50139
50636
|
function pluginPaths(dir) {
|
|
50140
50637
|
return {
|
|
50141
|
-
manifestPath:
|
|
50142
|
-
skillPath:
|
|
50638
|
+
manifestPath: join27(dir, ".claude-plugin", "plugin.json"),
|
|
50639
|
+
skillPath: join27(dir, "SKILL.md")
|
|
50143
50640
|
};
|
|
50144
50641
|
}
|
|
50145
50642
|
function installClaudePlugin(cwd, opts) {
|
|
@@ -50147,8 +50644,8 @@ function installClaudePlugin(cwd, opts) {
|
|
|
50147
50644
|
if (dir === null) return 1;
|
|
50148
50645
|
const { manifestPath, skillPath } = pluginPaths(dir);
|
|
50149
50646
|
const { manifest, skill } = renderClaudePlugin();
|
|
50150
|
-
const manifestCurrent =
|
|
50151
|
-
const skillCurrent =
|
|
50647
|
+
const manifestCurrent = existsSync22(manifestPath) ? readFileSync20(manifestPath, "utf-8") : null;
|
|
50648
|
+
const skillCurrent = existsSync22(skillPath) ? readFileSync20(skillPath, "utf-8") : null;
|
|
50152
50649
|
if (manifestCurrent === manifest && skillCurrent === skill) {
|
|
50153
50650
|
console.log("No changes \u2014 prim skill plugin already up to date.");
|
|
50154
50651
|
return 0;
|
|
@@ -50157,7 +50654,7 @@ function installClaudePlugin(cwd, opts) {
|
|
|
50157
50654
|
console.log(`Would write plugin to ${dir} (.claude-plugin/plugin.json + SKILL.md)`);
|
|
50158
50655
|
return 0;
|
|
50159
50656
|
}
|
|
50160
|
-
mkdirSync14(
|
|
50657
|
+
mkdirSync14(join27(dir, ".claude-plugin"), { recursive: true });
|
|
50161
50658
|
atomicWrite4(manifestPath, manifest);
|
|
50162
50659
|
atomicWrite4(skillPath, skill);
|
|
50163
50660
|
console.log(`Installed prim skill plugin at ${dir}`);
|
|
@@ -50171,13 +50668,13 @@ function uninstallClaudePlugin(cwd, opts) {
|
|
|
50171
50668
|
const dir = resolvePluginDir(cwd, opts.scope);
|
|
50172
50669
|
if (dir === null) return 1;
|
|
50173
50670
|
const { manifestPath, skillPath } = pluginPaths(dir);
|
|
50174
|
-
if (!
|
|
50671
|
+
if (!existsSync22(manifestPath) && !existsSync22(skillPath)) {
|
|
50175
50672
|
console.log(`prim skill plugin not present at ${dir}`);
|
|
50176
50673
|
return 0;
|
|
50177
50674
|
}
|
|
50178
|
-
|
|
50179
|
-
|
|
50180
|
-
removeDirIfEmpty(
|
|
50675
|
+
rmSync9(manifestPath, { force: true });
|
|
50676
|
+
rmSync9(skillPath, { force: true });
|
|
50677
|
+
removeDirIfEmpty(join27(dir, ".claude-plugin"));
|
|
50181
50678
|
removeDirIfEmpty(dir);
|
|
50182
50679
|
console.log(`Removed prim skill plugin from ${dir}`);
|
|
50183
50680
|
return 0;
|
|
@@ -50186,7 +50683,7 @@ function statusClaudePlugin(cwd, opts) {
|
|
|
50186
50683
|
const dir = resolvePluginDir(cwd, opts.scope);
|
|
50187
50684
|
if (dir === null) return 1;
|
|
50188
50685
|
const { skillPath } = pluginPaths(dir);
|
|
50189
|
-
const installed =
|
|
50686
|
+
const installed = existsSync22(skillPath);
|
|
50190
50687
|
if (opts.json) {
|
|
50191
50688
|
printJson({ installed, target: dir });
|
|
50192
50689
|
return installed ? 0 : 1;
|
|
@@ -50200,7 +50697,7 @@ function statusClaudePlugin(cwd, opts) {
|
|
|
50200
50697
|
}
|
|
50201
50698
|
|
|
50202
50699
|
// src/commands/skill.ts
|
|
50203
|
-
var __dirname5 =
|
|
50700
|
+
var __dirname5 = dirname17(fileURLToPath5(import.meta.url));
|
|
50204
50701
|
var SKILL_BEGIN = "<!-- BEGIN PRIM SKILL v1 -->";
|
|
50205
50702
|
var SKILL_END = "<!-- END PRIM SKILL v1 -->";
|
|
50206
50703
|
var CODEX_GUIDANCE_LIMIT_BYTES = 1024 * 1024;
|
|
@@ -50228,25 +50725,25 @@ function sha2563(content) {
|
|
|
50228
50725
|
}
|
|
50229
50726
|
function cursorSkillDirectory(cwd, scope) {
|
|
50230
50727
|
const base = scope === "user" ? cursorConfigDirectory() : gitToplevel(cwd) ?? cwd;
|
|
50231
|
-
return
|
|
50728
|
+
return join28(base, scope === "user" ? "skills" : ".cursor/skills", "prim");
|
|
50232
50729
|
}
|
|
50233
50730
|
function cursorSkillPaths(cwd, scope) {
|
|
50234
50731
|
const directory = cursorSkillDirectory(cwd, scope);
|
|
50235
50732
|
return {
|
|
50236
50733
|
directory,
|
|
50237
|
-
skill:
|
|
50238
|
-
manifest:
|
|
50734
|
+
skill: join28(directory, "SKILL.md"),
|
|
50735
|
+
manifest: join28(directory, CURSOR_SKILL_MANIFEST)
|
|
50239
50736
|
};
|
|
50240
50737
|
}
|
|
50241
50738
|
function readCursorSkillManifest(path9) {
|
|
50242
50739
|
try {
|
|
50243
|
-
const value = JSON.parse(
|
|
50740
|
+
const value = JSON.parse(readFileSync21(path9, "utf8"));
|
|
50244
50741
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
50245
|
-
const
|
|
50246
|
-
if (
|
|
50742
|
+
const record3 = value;
|
|
50743
|
+
if (record3.version !== 1 || record3.owner !== CURSOR_SKILL_OWNER || typeof record3.contentSha256 !== "string" || !/^[0-9a-f]{64}$/u.test(record3.contentSha256)) {
|
|
50247
50744
|
return;
|
|
50248
50745
|
}
|
|
50249
|
-
return
|
|
50746
|
+
return record3;
|
|
50250
50747
|
} catch {
|
|
50251
50748
|
return;
|
|
50252
50749
|
}
|
|
@@ -50254,7 +50751,7 @@ function readCursorSkillManifest(path9) {
|
|
|
50254
50751
|
function cursorSkillStatus(cwd, scope) {
|
|
50255
50752
|
const paths = cursorSkillPaths(cwd, scope);
|
|
50256
50753
|
const manifest = readCursorSkillManifest(paths.manifest);
|
|
50257
|
-
const content =
|
|
50754
|
+
const content = existsSync23(paths.skill) ? readFileSync21(paths.skill, "utf8") : void 0;
|
|
50258
50755
|
const owned = manifest !== void 0;
|
|
50259
50756
|
const installed = owned && content !== void 0 && sha2563(content) === manifest.contentSha256;
|
|
50260
50757
|
return {
|
|
@@ -50272,8 +50769,8 @@ function validCursorSkillScope(scope) {
|
|
|
50272
50769
|
function installCursorSkill(cwd, opts) {
|
|
50273
50770
|
const paths = cursorSkillPaths(cwd, opts.scope);
|
|
50274
50771
|
const status = cursorSkillStatus(cwd, opts.scope);
|
|
50275
|
-
const manifestExists =
|
|
50276
|
-
const skillExists =
|
|
50772
|
+
const manifestExists = existsSync23(paths.manifest);
|
|
50773
|
+
const skillExists = existsSync23(paths.skill);
|
|
50277
50774
|
if (manifestExists && !status.owned || !manifestExists && skillExists || status.edited) {
|
|
50278
50775
|
console.error(`Refusing to overwrite a foreign or edited Cursor skill at ${paths.skill}`);
|
|
50279
50776
|
return 1;
|
|
@@ -50284,12 +50781,12 @@ function installCursorSkill(cwd, opts) {
|
|
|
50284
50781
|
owner: CURSOR_SKILL_OWNER,
|
|
50285
50782
|
contentSha256: sha2563(content)
|
|
50286
50783
|
};
|
|
50287
|
-
if (status.installed &&
|
|
50784
|
+
if (status.installed && readFileSync21(paths.skill, "utf8") === content) {
|
|
50288
50785
|
console.log(`No changes \u2014 Cursor skill already up to date at ${paths.skill}`);
|
|
50289
50786
|
return 0;
|
|
50290
50787
|
}
|
|
50291
50788
|
if (opts.dryRun) {
|
|
50292
|
-
const existing = skillExists ?
|
|
50789
|
+
const existing = skillExists ? readFileSync21(paths.skill, "utf8") : "";
|
|
50293
50790
|
process.stdout.write(createPatch(paths.skill, existing, content, "current", "proposed"));
|
|
50294
50791
|
return 0;
|
|
50295
50792
|
}
|
|
@@ -50310,34 +50807,34 @@ function uninstallCursorSkill(cwd, scope) {
|
|
|
50310
50807
|
console.error(`Refusing to remove an edited Cursor skill at ${paths.skill}`);
|
|
50311
50808
|
return 1;
|
|
50312
50809
|
}
|
|
50313
|
-
if (
|
|
50314
|
-
if (
|
|
50810
|
+
if (existsSync23(paths.skill)) unlinkSync7(paths.skill);
|
|
50811
|
+
if (existsSync23(paths.manifest)) unlinkSync7(paths.manifest);
|
|
50315
50812
|
try {
|
|
50316
|
-
if (
|
|
50813
|
+
if (readdirSync8(paths.directory).length === 0) rmdirSync3(paths.directory);
|
|
50317
50814
|
} catch {
|
|
50318
50815
|
}
|
|
50319
50816
|
console.log(`Removed Primitive-owned Cursor skill from ${paths.skill}`);
|
|
50320
50817
|
return 0;
|
|
50321
50818
|
}
|
|
50322
50819
|
function userTargetFor(agent) {
|
|
50323
|
-
if (agent === "claude") return
|
|
50324
|
-
if (agent === "codex") return
|
|
50820
|
+
if (agent === "claude") return join28(homedir11(), ".claude", "CLAUDE.md");
|
|
50821
|
+
if (agent === "codex") return join28(homedir11(), ".codex", "AGENTS.md");
|
|
50325
50822
|
if (agent === "hermes") {
|
|
50326
|
-
return
|
|
50823
|
+
return join28(process.env.HERMES_HOME ?? join28(homedir11(), ".hermes"), ".hermes.md");
|
|
50327
50824
|
}
|
|
50328
50825
|
return null;
|
|
50329
50826
|
}
|
|
50330
50827
|
function loadSkill() {
|
|
50331
50828
|
let dir = __dirname5;
|
|
50332
|
-
while (dir !==
|
|
50829
|
+
while (dir !== dirname17(dir)) {
|
|
50333
50830
|
const p = resolve8(dir, "SKILL.md");
|
|
50334
|
-
if (
|
|
50335
|
-
dir =
|
|
50831
|
+
if (existsSync23(p)) return readFileSync21(p, "utf-8");
|
|
50832
|
+
dir = dirname17(dir);
|
|
50336
50833
|
}
|
|
50337
50834
|
throw new Error("SKILL.md not found in package");
|
|
50338
50835
|
}
|
|
50339
50836
|
function detectTargets(cwd) {
|
|
50340
|
-
return TARGET_CANDIDATES.filter((p) =>
|
|
50837
|
+
return TARGET_CANDIDATES.filter((p) => existsSync23(resolve8(cwd, p)));
|
|
50341
50838
|
}
|
|
50342
50839
|
function detectNewline(content) {
|
|
50343
50840
|
return content.includes("\r\n") ? "\r\n" : "\n";
|
|
@@ -50403,7 +50900,7 @@ function runInstall(cwd, opts) {
|
|
|
50403
50900
|
}
|
|
50404
50901
|
const target = resolveTarget(cwd, opts);
|
|
50405
50902
|
if (target === null) return 1;
|
|
50406
|
-
const existing =
|
|
50903
|
+
const existing = existsSync23(target) ? readFileSync21(target, "utf-8") : "";
|
|
50407
50904
|
const eol = existing ? detectNewline(existing) : "\n";
|
|
50408
50905
|
const block = composeBlock(loadSkill(), eol);
|
|
50409
50906
|
const next = applyBlock(existing, block, eol);
|
|
@@ -50427,11 +50924,11 @@ function runUninstall(cwd, opts) {
|
|
|
50427
50924
|
}
|
|
50428
50925
|
const target = resolveTarget(cwd, opts);
|
|
50429
50926
|
if (target === null) return 1;
|
|
50430
|
-
if (!
|
|
50927
|
+
if (!existsSync23(target)) {
|
|
50431
50928
|
console.log(`Skill block not present at ${target}`);
|
|
50432
50929
|
return 0;
|
|
50433
50930
|
}
|
|
50434
|
-
const existing =
|
|
50931
|
+
const existing = readFileSync21(target, "utf-8");
|
|
50435
50932
|
const next = removeBlock(existing);
|
|
50436
50933
|
if (next === null) {
|
|
50437
50934
|
console.log(`Skill block not present at ${target}`);
|
|
@@ -50455,10 +50952,10 @@ function runStatus(cwd, opts) {
|
|
|
50455
50952
|
}
|
|
50456
50953
|
const target = resolveTarget(cwd, opts);
|
|
50457
50954
|
if (target === null) return 1;
|
|
50458
|
-
const fileExists =
|
|
50955
|
+
const fileExists = existsSync23(target);
|
|
50459
50956
|
let installed = false;
|
|
50460
50957
|
if (fileExists) {
|
|
50461
|
-
const content =
|
|
50958
|
+
const content = readFileSync21(target, "utf-8");
|
|
50462
50959
|
installed = content.includes(SKILL_BEGIN) && content.includes(SKILL_END);
|
|
50463
50960
|
}
|
|
50464
50961
|
if (opts.json) {
|
|
@@ -50495,13 +50992,13 @@ function registerSkillCommands(program3) {
|
|
|
50495
50992
|
}
|
|
50496
50993
|
|
|
50497
50994
|
// src/commands/statusline.ts
|
|
50498
|
-
import { readFileSync as
|
|
50499
|
-
import { dirname as
|
|
50995
|
+
import { readFileSync as readFileSync22 } from "fs";
|
|
50996
|
+
import { dirname as dirname18, resolve as resolve9 } from "path";
|
|
50500
50997
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
50501
50998
|
var STATUSLINE_TIMEOUT_MS = 200;
|
|
50502
50999
|
function readPackageVersion() {
|
|
50503
51000
|
try {
|
|
50504
|
-
const here =
|
|
51001
|
+
const here = dirname18(fileURLToPath6(import.meta.url));
|
|
50505
51002
|
const candidates = [
|
|
50506
51003
|
// Schema-v2 runtimes staged a manifest beside this standalone bundle.
|
|
50507
51004
|
// Keep resolving it while those legacy launchers transition to schema v3.
|
|
@@ -50511,7 +51008,7 @@ function readPackageVersion() {
|
|
|
50511
51008
|
];
|
|
50512
51009
|
for (const path9 of candidates) {
|
|
50513
51010
|
try {
|
|
50514
|
-
const pkg2 = JSON.parse(
|
|
51011
|
+
const pkg2 = JSON.parse(readFileSync22(path9, "utf-8"));
|
|
50515
51012
|
if (pkg2.version) {
|
|
50516
51013
|
return pkg2.version;
|
|
50517
51014
|
}
|
|
@@ -50925,28 +51422,272 @@ function registerWelcomeCommand(program3, deps = { getClient }) {
|
|
|
50925
51422
|
});
|
|
50926
51423
|
}
|
|
50927
51424
|
|
|
51425
|
+
// src/daemon/self-heal.ts
|
|
51426
|
+
import { spawn as spawn4 } from "child_process";
|
|
51427
|
+
|
|
51428
|
+
// src/lib/unattended.ts
|
|
51429
|
+
var UNATTENDED_ENV = "PRIM_UNATTENDED";
|
|
51430
|
+
function unattendedEnv(env4 = process.env) {
|
|
51431
|
+
return { ...env4, [UNATTENDED_ENV]: "1" };
|
|
51432
|
+
}
|
|
51433
|
+
function isUnattended(env4 = process.env) {
|
|
51434
|
+
return env4[UNATTENDED_ENV] === "1";
|
|
51435
|
+
}
|
|
51436
|
+
|
|
51437
|
+
// src/daemon/self-heal.ts
|
|
51438
|
+
function kickDaemonEnsure(options = {}) {
|
|
51439
|
+
const primEntry = options.primEntry === void 0 ? binFile("prim") : options.primEntry;
|
|
51440
|
+
if (!primEntry) {
|
|
51441
|
+
return false;
|
|
51442
|
+
}
|
|
51443
|
+
try {
|
|
51444
|
+
const args = [primEntry, "daemon", "ensure"];
|
|
51445
|
+
if (options.latestBootstrap ?? (options.platform ?? process.platform) === "darwin") {
|
|
51446
|
+
args.push("--latest-bootstrap");
|
|
51447
|
+
}
|
|
51448
|
+
const child = (options.spawnProcess ?? spawn4)(
|
|
51449
|
+
options.nodeEntry ?? process.execPath,
|
|
51450
|
+
args,
|
|
51451
|
+
// Unattended: neither SessionStart's repair nor a drift heal is a person
|
|
51452
|
+
// running prim, so the child (and its descendants) never heal in turn.
|
|
51453
|
+
{ detached: true, stdio: "ignore", env: unattendedEnv() }
|
|
51454
|
+
);
|
|
51455
|
+
child.once("error", () => {
|
|
51456
|
+
});
|
|
51457
|
+
child.unref();
|
|
51458
|
+
return true;
|
|
51459
|
+
} catch {
|
|
51460
|
+
return false;
|
|
51461
|
+
}
|
|
51462
|
+
}
|
|
51463
|
+
|
|
51464
|
+
// src/daemon/drift-heal.ts
|
|
51465
|
+
var ATTEMPT_MARKER_MODE = 384;
|
|
51466
|
+
var ATTEMPT_MARKER_MAX_BYTES = 1024;
|
|
51467
|
+
var PATH_SEPARATORS = /[\\/]/u;
|
|
51468
|
+
var DAEMON_DRIFT_HEAL_ENV = "PRIM_DAEMON_DRIFT_HEAL";
|
|
51469
|
+
var DAEMON_DRIFT_HEAL_RETRY_MS = 60 * 60 * 1e3;
|
|
51470
|
+
function readAttempt(path9) {
|
|
51471
|
+
const file = readBoundedRegularFile(path9, ATTEMPT_MARKER_MAX_BYTES);
|
|
51472
|
+
if (!file) return null;
|
|
51473
|
+
try {
|
|
51474
|
+
const value = JSON.parse(file.text);
|
|
51475
|
+
if (typeof value !== "object" || value === null) return null;
|
|
51476
|
+
const attempt = value;
|
|
51477
|
+
return typeof attempt.attemptedAt === "number" && Number.isFinite(attempt.attemptedAt) && typeof attempt.fromVersion === "string" && typeof attempt.toVersion === "string" ? attempt : null;
|
|
51478
|
+
} catch {
|
|
51479
|
+
return null;
|
|
51480
|
+
}
|
|
51481
|
+
}
|
|
51482
|
+
function claimHeld() {
|
|
51483
|
+
throw new Error("another command holds the daemon drift-heal claim");
|
|
51484
|
+
}
|
|
51485
|
+
function claimAttempt(markerPath2, attempt) {
|
|
51486
|
+
const previous = readAttempt(markerPath2);
|
|
51487
|
+
const elapsed = previous ? attempt.attemptedAt - previous.attemptedAt : Number.POSITIVE_INFINITY;
|
|
51488
|
+
if (elapsed >= 0 && elapsed < DAEMON_DRIFT_HEAL_RETRY_MS) return false;
|
|
51489
|
+
atomicWriteFile(markerPath2, `${JSON.stringify(attempt)}
|
|
51490
|
+
`, { mode: ATTEMPT_MARKER_MODE });
|
|
51491
|
+
return true;
|
|
51492
|
+
}
|
|
51493
|
+
function isInstalledPackage(root) {
|
|
51494
|
+
return root?.split(PATH_SEPARATORS).includes("node_modules") === true;
|
|
51495
|
+
}
|
|
51496
|
+
function needsHeal(launcher, toVersion) {
|
|
51497
|
+
const order = compareSemver(launcher.runtimeVersion, toVersion);
|
|
51498
|
+
return order === -1 || order === 0 && !launcher.runnable;
|
|
51499
|
+
}
|
|
51500
|
+
function startDriftHeal(options) {
|
|
51501
|
+
if ((options.platform ?? process.platform) !== "darwin") return false;
|
|
51502
|
+
const env4 = options.env ?? process.env;
|
|
51503
|
+
if (env4[DAEMON_DRIFT_HEAL_ENV] === "0") return false;
|
|
51504
|
+
const root = options.packageRoot === void 0 ? packageRoot() : options.packageRoot;
|
|
51505
|
+
if (!isInstalledPackage(root)) return false;
|
|
51506
|
+
const euid = options.euid ?? process.geteuid?.();
|
|
51507
|
+
if (euid === void 0 || euid === 0) return false;
|
|
51508
|
+
const paths = { env: env4, homeDir: options.homeDir };
|
|
51509
|
+
if (daemonExplicitlyDisabled(paths)) return false;
|
|
51510
|
+
const launcher = (options.selectedLauncher ?? (() => selectedDaemonLauncher(paths)))();
|
|
51511
|
+
if (!launcher || launcher.ownerUid !== euid) return false;
|
|
51512
|
+
const toVersion = options.cliVersion === void 0 ? packageVersion() : options.cliVersion;
|
|
51513
|
+
if (!(toVersion && needsHeal(launcher, toVersion))) return false;
|
|
51514
|
+
if (!apiUrlsMatch(
|
|
51515
|
+
getSiteUrlForEnvironment(launcher.apiUrl),
|
|
51516
|
+
getSiteUrlForEnvironment(env4.PRIM_API_URL)
|
|
51517
|
+
)) {
|
|
51518
|
+
return false;
|
|
51519
|
+
}
|
|
51520
|
+
const runsConfigRoot = options.launchAgentRunsConfigRoot ?? (() => launchAgentRunsConfigRoot(paths));
|
|
51521
|
+
if (!runsConfigRoot()) return false;
|
|
51522
|
+
const primEntry = options.primEntry === void 0 ? binFile("prim") : options.primEntry;
|
|
51523
|
+
if (!primEntry) return false;
|
|
51524
|
+
const attempt = {
|
|
51525
|
+
attemptedAt: (options.nowMs ?? Date.now)(),
|
|
51526
|
+
fromVersion: launcher.runtimeVersion,
|
|
51527
|
+
toVersion
|
|
51528
|
+
};
|
|
51529
|
+
const claimed = withFileLockSync(
|
|
51530
|
+
daemonDriftHealClaimPath(paths),
|
|
51531
|
+
() => claimAttempt(daemonDriftHealMarkerPath(paths), attempt),
|
|
51532
|
+
{ timeoutMs: 0, sleep: claimHeld }
|
|
51533
|
+
);
|
|
51534
|
+
if (!claimed) return false;
|
|
51535
|
+
return kickDaemonEnsure({
|
|
51536
|
+
primEntry,
|
|
51537
|
+
nodeEntry: options.nodeEntry,
|
|
51538
|
+
spawnProcess: options.spawnProcess,
|
|
51539
|
+
latestBootstrap: false
|
|
51540
|
+
});
|
|
51541
|
+
}
|
|
51542
|
+
function healDaemonDrift(options = {}) {
|
|
51543
|
+
try {
|
|
51544
|
+
return startDriftHeal(options);
|
|
51545
|
+
} catch {
|
|
51546
|
+
return false;
|
|
51547
|
+
}
|
|
51548
|
+
}
|
|
51549
|
+
|
|
51550
|
+
// src/lib/background-flush.ts
|
|
51551
|
+
import { spawn as spawn5 } from "child_process";
|
|
51552
|
+
import { readFileSync as readFileSync23 } from "fs";
|
|
51553
|
+
|
|
51554
|
+
// src/daemon/health.ts
|
|
51555
|
+
import { chmodSync as chmodSync7, existsSync as existsSync24, mkdirSync as mkdirSync15, renameSync as renameSync7, writeFileSync as writeFileSync11 } from "fs";
|
|
51556
|
+
import { dirname as dirname19, join as join29 } from "path";
|
|
51557
|
+
var HEARTBEAT_FRESH_MS = 9e4;
|
|
51558
|
+
var DAEMON_HEALTH_PATH = join29(primConfigDirectory(), "daemon-health.json");
|
|
51559
|
+
|
|
51560
|
+
// src/lib/background-flush.ts
|
|
51561
|
+
function record2(value) {
|
|
51562
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
51563
|
+
}
|
|
51564
|
+
function daemonOwnsDrain(options = {}) {
|
|
51565
|
+
try {
|
|
51566
|
+
const expectedVersion = options.expectedVersion === void 0 ? packageVersion() : options.expectedVersion;
|
|
51567
|
+
if (!expectedVersion) return false;
|
|
51568
|
+
const health = record2(
|
|
51569
|
+
JSON.parse(readFileSync23(options.healthPath ?? DAEMON_HEALTH_PATH, "utf8"))
|
|
51570
|
+
);
|
|
51571
|
+
const heartbeat = record2(health?.heartbeat);
|
|
51572
|
+
const ingestion = record2(health?.ingestion);
|
|
51573
|
+
if (!(health && heartbeat && ingestion)) return false;
|
|
51574
|
+
if (health.schemaVersion !== 1 || health.version !== expectedVersion) return false;
|
|
51575
|
+
const daemonSiteUrl = health.siteUrl;
|
|
51576
|
+
if (typeof daemonSiteUrl !== "string" || envSlug(daemonSiteUrl) !== envSlug(options.siteUrl ?? getSiteUrl())) {
|
|
51577
|
+
return false;
|
|
51578
|
+
}
|
|
51579
|
+
if (health.needsReauth === true) return false;
|
|
51580
|
+
const pid = health.pid;
|
|
51581
|
+
if (typeof pid !== "number" || !(options.isAlive ?? processIsAlive)(pid)) return false;
|
|
51582
|
+
const lastSuccessAt = heartbeat.lastSuccessAt;
|
|
51583
|
+
const now = options.now ?? Date.now();
|
|
51584
|
+
return heartbeat.consecutiveFailures === 0 && typeof lastSuccessAt === "number" && lastSuccessAt <= now && now - lastSuccessAt < HEARTBEAT_FRESH_MS && ingestion.consecutiveFailures === 0;
|
|
51585
|
+
} catch {
|
|
51586
|
+
return false;
|
|
51587
|
+
}
|
|
51588
|
+
}
|
|
51589
|
+
function startBackgroundFlush(options = {}) {
|
|
51590
|
+
try {
|
|
51591
|
+
if ((options.daemonOwnsDrain ?? daemonOwnsDrain)()) return false;
|
|
51592
|
+
if (!(options.needsFlush ?? journalNeedsFlush)()) return false;
|
|
51593
|
+
const primEntry = options.primEntry === void 0 ? binFile("prim") : options.primEntry;
|
|
51594
|
+
if (!primEntry) return false;
|
|
51595
|
+
const child = (options.spawnProcess ?? spawn5)(
|
|
51596
|
+
options.nodeEntry ?? process.execPath,
|
|
51597
|
+
[primEntry, "moves", "flush"],
|
|
51598
|
+
// Ignored stdio matters as much as detaching: a child holding inherited
|
|
51599
|
+
// pipes would keep a caller that captures them (spawnSync) waiting.
|
|
51600
|
+
// windowsHide keeps the detached child from opening a console window.
|
|
51601
|
+
// The child is unattended even when its parent is not: `doctor` or
|
|
51602
|
+
// `statusline` must not reach the daemon drift heal through its drain.
|
|
51603
|
+
{ detached: true, stdio: "ignore", windowsHide: true, env: unattendedEnv() }
|
|
51604
|
+
);
|
|
51605
|
+
child.once("error", () => {
|
|
51606
|
+
});
|
|
51607
|
+
child.unref();
|
|
51608
|
+
return true;
|
|
51609
|
+
} catch {
|
|
51610
|
+
return false;
|
|
51611
|
+
}
|
|
51612
|
+
}
|
|
51613
|
+
|
|
50928
51614
|
// src/lib/startup-background.ts
|
|
50929
51615
|
var ROOT_OPTIONS = /* @__PURE__ */ new Set(["-y", "--yes", "--non-interactive"]);
|
|
51616
|
+
var HELP_OPTIONS = /* @__PURE__ */ new Set(["-h", "--help"]);
|
|
51617
|
+
var DAEMON_HEAL_COMMANDS = /* @__PURE__ */ new Map([
|
|
51618
|
+
// Credentials; `auth login` is how a person returns to a daemon held for re-auth.
|
|
51619
|
+
["auth", /* @__PURE__ */ new Set(["api-keys", "login", "set-token", "status"])],
|
|
51620
|
+
// Reading and curating the decision graph: the everyday attended surface.
|
|
51621
|
+
[
|
|
51622
|
+
"decisions",
|
|
51623
|
+
/* @__PURE__ */ new Set([
|
|
51624
|
+
"cascade",
|
|
51625
|
+
"check",
|
|
51626
|
+
"confirm",
|
|
51627
|
+
"create",
|
|
51628
|
+
"demote",
|
|
51629
|
+
"link",
|
|
51630
|
+
"promote",
|
|
51631
|
+
"publish",
|
|
51632
|
+
"ratify",
|
|
51633
|
+
"recent",
|
|
51634
|
+
"repairs",
|
|
51635
|
+
"rescope",
|
|
51636
|
+
"restore",
|
|
51637
|
+
"show",
|
|
51638
|
+
"supersede",
|
|
51639
|
+
"unlink",
|
|
51640
|
+
"withdraw"
|
|
51641
|
+
])
|
|
51642
|
+
],
|
|
51643
|
+
["reconcile", "any"],
|
|
51644
|
+
// Repository connection, activation, and journal plumbing a person steers.
|
|
51645
|
+
// Hook-side `moves flush` drains are unattended and never reach this table.
|
|
51646
|
+
["github", /* @__PURE__ */ new Set(["connect"])],
|
|
51647
|
+
["enable", "any"],
|
|
51648
|
+
["moves", /* @__PURE__ */ new Set(["bind", "drop", "flush", "status", "tail"])],
|
|
51649
|
+
["session", /* @__PURE__ */ new Set(["drop", "list", "start"])],
|
|
51650
|
+
// Installing or inspecting an integration, never removing one.
|
|
51651
|
+
["claude", /* @__PURE__ */ new Set(["install", "preauth", "status"])],
|
|
51652
|
+
["codex", /* @__PURE__ */ new Set(["install", "status"])],
|
|
51653
|
+
["cursor", /* @__PURE__ */ new Set(["install", "status"])],
|
|
51654
|
+
["hermes", /* @__PURE__ */ new Set(["install", "status"])],
|
|
51655
|
+
["hooks", /* @__PURE__ */ new Set(["install"])],
|
|
51656
|
+
["skill", /* @__PURE__ */ new Set(["install", "status"])],
|
|
51657
|
+
["welcome", "any"]
|
|
51658
|
+
]);
|
|
50930
51659
|
function commandArguments(argv) {
|
|
50931
51660
|
let index = 0;
|
|
50932
51661
|
while (ROOT_OPTIONS.has(argv[index] ?? "")) index += 1;
|
|
50933
51662
|
return argv.slice(index);
|
|
50934
51663
|
}
|
|
51664
|
+
function mayHealDaemon(argv, env4) {
|
|
51665
|
+
if (isUnattended(env4) || env4[SETUP_ORCHESTRATOR_ENV] === "1") return false;
|
|
51666
|
+
const args = commandArguments(argv);
|
|
51667
|
+
if (args.some((arg) => HELP_OPTIONS.has(arg))) return false;
|
|
51668
|
+
const [command, subcommand] = args;
|
|
51669
|
+
const allowed = command === void 0 ? void 0 : DAEMON_HEAL_COMMANDS.get(command);
|
|
51670
|
+
if (allowed === void 0) return false;
|
|
51671
|
+
return allowed === "any" || subcommand !== void 0 && allowed.has(subcommand);
|
|
51672
|
+
}
|
|
50935
51673
|
function runStartupBackgroundWork(argv, env4, work) {
|
|
50936
|
-
const
|
|
51674
|
+
const args = commandArguments(argv);
|
|
51675
|
+
const [command, subcommand] = args;
|
|
50937
51676
|
const uninstall = command === "uninstall" || env4[UNINSTALL_ORCHESTRATOR_ENV] === "1";
|
|
50938
51677
|
if (uninstall) return;
|
|
50939
51678
|
work.notify();
|
|
50940
|
-
|
|
50941
|
-
|
|
51679
|
+
const setupDaemonDrains = command === "setup" && !args.includes("--no-daemon") || env4[SETUP_DAEMON_DRAINS_ENV] === "1";
|
|
51680
|
+
if (!setupDaemonDrains && (command !== "moves" || subcommand !== "flush")) work.flush();
|
|
51681
|
+
if (mayHealDaemon(argv, env4)) work.healDaemon();
|
|
50942
51682
|
}
|
|
50943
51683
|
|
|
50944
51684
|
// src/index.ts
|
|
50945
|
-
var __dirname6 =
|
|
50946
|
-
var pkg = JSON.parse(
|
|
51685
|
+
var __dirname6 = dirname20(fileURLToPath7(import.meta.url));
|
|
51686
|
+
var pkg = JSON.parse(readFileSync24(resolve10(__dirname6, "../package.json"), "utf-8"));
|
|
50947
51687
|
runStartupBackgroundWork(process.argv.slice(2), process.env, {
|
|
50948
51688
|
notify: () => updateNotifier({ pkg }).notify(),
|
|
50949
|
-
flush: () =>
|
|
51689
|
+
flush: () => startBackgroundFlush(),
|
|
51690
|
+
healDaemon: () => healDaemonDrift()
|
|
50950
51691
|
});
|
|
50951
51692
|
var program2 = new Command();
|
|
50952
51693
|
program2.name("prim").description("CLI for Primitive's decision graph").version(pkg.version).option("-y, --yes", "auto-confirm prompts").option(
|