@primitive.ai/prim 0.1.0-alpha.91 → 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-VW3FDYRT.js → chunk-36TWVNBE.js} +6 -6
- package/dist/{chunk-HU4PW6I6.js → chunk-C3VAA5CG.js} +1 -1
- package/dist/{chunk-FOLKHXLJ.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-EWZMDD44.js → chunk-X64HTHUF.js} +22 -3
- package/dist/{chunk-WX52U7Z2.js → chunk-ZU563PQI.js} +58 -2
- package/dist/daemon/server.js +382 -124
- package/dist/hook-runtime/daemon/server.js +382 -124
- 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 +111 -78
- package/dist/hook-runtime/hooks/pre-tool-use.js +48 -26
- package/dist/hook-runtime/hooks/prim-hook.js +157 -117
- package/dist/hook-runtime/hooks/session-end.js +1 -1
- package/dist/hook-runtime/hooks/session-start.js +38 -4
- package/dist/hook-runtime/index.js +1293 -526
- 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 +867 -291
- package/dist/statusline-main.js +23 -1
- package/package.json +1 -1
- package/dist/chunk-BTNNEH2W.js +0 -79
package/dist/daemon/server.js
CHANGED
|
@@ -52,9 +52,9 @@ var require_ucs2length = __commonJS({
|
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
// src/daemon/server.ts
|
|
55
|
-
import { chmodSync as
|
|
55
|
+
import { chmodSync as chmodSync7, existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync9, unlinkSync as unlinkSync3 } from "fs";
|
|
56
56
|
import { createServer } from "net";
|
|
57
|
-
import { join as
|
|
57
|
+
import { join as join14 } from "path";
|
|
58
58
|
import { fileURLToPath } from "url";
|
|
59
59
|
|
|
60
60
|
// src/client.ts
|
|
@@ -1112,8 +1112,8 @@ async function getPinnedClient(options = {}) {
|
|
|
1112
1112
|
}
|
|
1113
1113
|
|
|
1114
1114
|
// src/flusher.ts
|
|
1115
|
-
import { createReadStream, renameSync as renameSync3, unlinkSync } from "fs";
|
|
1116
|
-
import { dirname as
|
|
1115
|
+
import { createReadStream, renameSync as renameSync3, statSync as statSync2, unlinkSync } from "fs";
|
|
1116
|
+
import { dirname as dirname8, join as join9 } from "path";
|
|
1117
1117
|
|
|
1118
1118
|
// src/dead-letter.ts
|
|
1119
1119
|
import { createHash as createHash3, randomBytes as randomBytes2 } from "crypto";
|
|
@@ -1213,6 +1213,95 @@ function quarantineRawLine(flushingPath, rawLine, now = Date.now, options = {})
|
|
|
1213
1213
|
return record;
|
|
1214
1214
|
}
|
|
1215
1215
|
|
|
1216
|
+
// src/drain-progress.ts
|
|
1217
|
+
import {
|
|
1218
|
+
chmodSync as chmodSync3,
|
|
1219
|
+
existsSync as existsSync4,
|
|
1220
|
+
readFileSync as readFileSync4,
|
|
1221
|
+
readdirSync,
|
|
1222
|
+
rmSync as rmSync5,
|
|
1223
|
+
rmdirSync
|
|
1224
|
+
} from "fs";
|
|
1225
|
+
import { basename, dirname as dirname5, join as join6 } from "path";
|
|
1226
|
+
var DIRECTORY_MODE3 = 448;
|
|
1227
|
+
var FILE_MODE3 = 384;
|
|
1228
|
+
var DRAIN_PROGRESS_DIRNAME = "drain-progress";
|
|
1229
|
+
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)?$/;
|
|
1230
|
+
function rotationIdentity(stat) {
|
|
1231
|
+
return { size: stat.size, ino: stat.ino, dev: stat.dev };
|
|
1232
|
+
}
|
|
1233
|
+
function drainProgressDirectory(flushingPath) {
|
|
1234
|
+
return join6(dirname5(flushingPath), DRAIN_PROGRESS_DIRNAME);
|
|
1235
|
+
}
|
|
1236
|
+
function drainProgressPath(flushingPath) {
|
|
1237
|
+
return join6(drainProgressDirectory(flushingPath), `${basename(flushingPath)}.json`);
|
|
1238
|
+
}
|
|
1239
|
+
function isByteCount(value) {
|
|
1240
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
1241
|
+
}
|
|
1242
|
+
function drainResumeOffset(flushingPath, rotation) {
|
|
1243
|
+
let parsed;
|
|
1244
|
+
try {
|
|
1245
|
+
parsed = JSON.parse(readFileSync4(drainProgressPath(flushingPath), "utf8"));
|
|
1246
|
+
} catch {
|
|
1247
|
+
return 0;
|
|
1248
|
+
}
|
|
1249
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1250
|
+
return 0;
|
|
1251
|
+
}
|
|
1252
|
+
const { v, offset, size, ino, dev } = parsed;
|
|
1253
|
+
if (v !== 2 || ino !== rotation.ino || dev !== rotation.dev || size !== rotation.size || !isByteCount(offset) || offset > rotation.size) {
|
|
1254
|
+
return 0;
|
|
1255
|
+
}
|
|
1256
|
+
return offset;
|
|
1257
|
+
}
|
|
1258
|
+
function writeDrainCheckpoint(flushingPath, checkpoint) {
|
|
1259
|
+
const directory = drainProgressDirectory(flushingPath);
|
|
1260
|
+
ensureDurableDirectory(directory, DIRECTORY_MODE3);
|
|
1261
|
+
chmodSync3(directory, DIRECTORY_MODE3);
|
|
1262
|
+
atomicWriteFile(drainProgressPath(flushingPath), `${JSON.stringify(checkpoint)}
|
|
1263
|
+
`, {
|
|
1264
|
+
mode: FILE_MODE3
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1267
|
+
function removeEmptyDirectory(directory) {
|
|
1268
|
+
try {
|
|
1269
|
+
rmdirSync(directory);
|
|
1270
|
+
} catch (error) {
|
|
1271
|
+
const code = error.code;
|
|
1272
|
+
if (code !== "ENOENT" && code !== "ENOTEMPTY" && code !== "EEXIST") {
|
|
1273
|
+
throw error;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
function removeDrainCheckpoint(flushingPath) {
|
|
1278
|
+
rmSync5(drainProgressPath(flushingPath), { force: true });
|
|
1279
|
+
removeEmptyDirectory(drainProgressDirectory(flushingPath));
|
|
1280
|
+
}
|
|
1281
|
+
function sweepOrphanedDrainCheckpoints(bucketDirectory) {
|
|
1282
|
+
const directory = join6(bucketDirectory, DRAIN_PROGRESS_DIRNAME);
|
|
1283
|
+
let names;
|
|
1284
|
+
try {
|
|
1285
|
+
names = readdirSync(directory);
|
|
1286
|
+
} catch {
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
for (const name of names) {
|
|
1290
|
+
const rotation = CHECKPOINT_NAME.exec(name)?.[1];
|
|
1291
|
+
if (rotation === void 0 || existsSync4(join6(bucketDirectory, rotation))) {
|
|
1292
|
+
continue;
|
|
1293
|
+
}
|
|
1294
|
+
try {
|
|
1295
|
+
rmSync5(join6(directory, name), { force: true });
|
|
1296
|
+
} catch {
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
try {
|
|
1300
|
+
removeEmptyDirectory(directory);
|
|
1301
|
+
} catch {
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1216
1305
|
// src/generated/cli-http-v1.validators.js
|
|
1217
1306
|
var import_ucs2length = __toESM(require_ucs2length(), 1);
|
|
1218
1307
|
var schema33 = { "type": "object", "properties": { "authenticated": { "type": "boolean", "const": true }, "organizationBindingVersion": { "type": "number", "const": 1 }, "captureAuthorityKind": { "type": "string", "enum": ["workos", "service_token"] }, "organizationId": { "anyOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] }, "workosOrganizationId": { "anyOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] } }, "required": ["authenticated", "organizationBindingVersion", "captureAuthorityKind", "organizationId", "workosOrganizationId"], "additionalProperties": false };
|
|
@@ -19003,15 +19092,15 @@ validate68.evaluated = { "props": true, "dynamicProps": false, "dynamicItems": f
|
|
|
19003
19092
|
|
|
19004
19093
|
// src/lib/activation.ts
|
|
19005
19094
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
19006
|
-
import { existsSync as
|
|
19095
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
19007
19096
|
import { homedir as homedir2 } from "os";
|
|
19008
|
-
import { join as
|
|
19097
|
+
import { join as join7 } from "path";
|
|
19009
19098
|
|
|
19010
19099
|
// src/lib/git.ts
|
|
19011
19100
|
import { execFileSync } from "child_process";
|
|
19012
19101
|
import { createHash as createHash4 } from "crypto";
|
|
19013
19102
|
import { lstatSync, realpathSync } from "fs";
|
|
19014
|
-
import { basename, dirname as
|
|
19103
|
+
import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute2, relative, resolve as resolve2, sep } from "path";
|
|
19015
19104
|
var GIT_TIMEOUT_MS = 1e3;
|
|
19016
19105
|
var GITHUB_OWNER_PATTERN = /^(?![A-Za-z0-9-]*--)[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/u;
|
|
19017
19106
|
var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]{1,100}$/u;
|
|
@@ -19121,10 +19210,10 @@ function hasProjectPrimInstall(cwd) {
|
|
|
19121
19210
|
const root = gitToplevel(cwd);
|
|
19122
19211
|
if (root === null || root === homedir2()) return false;
|
|
19123
19212
|
for (const rel of PROJECT_INSTALL_FILES) {
|
|
19124
|
-
const path =
|
|
19125
|
-
if (
|
|
19213
|
+
const path = join7(root, rel);
|
|
19214
|
+
if (existsSync5(path)) {
|
|
19126
19215
|
try {
|
|
19127
|
-
const content =
|
|
19216
|
+
const content = readFileSync5(path, "utf-8");
|
|
19128
19217
|
if (PRIM_HOOK_BINS.some((bin) => content.includes(bin))) return true;
|
|
19129
19218
|
} catch {
|
|
19130
19219
|
}
|
|
@@ -19393,6 +19482,13 @@ function parseOrganizationBinding(value) {
|
|
|
19393
19482
|
}
|
|
19394
19483
|
};
|
|
19395
19484
|
}
|
|
19485
|
+
function summarizeRetainedBuckets(retained) {
|
|
19486
|
+
const counts = /* @__PURE__ */ new Map();
|
|
19487
|
+
for (const item of retained) {
|
|
19488
|
+
counts.set(item.reason, (counts.get(item.reason) ?? 0) + 1);
|
|
19489
|
+
}
|
|
19490
|
+
return [...counts].sort(([left], [right]) => left.localeCompare(right)).map(([reason, count]) => `${reason}:${String(count)}`).join(", ");
|
|
19491
|
+
}
|
|
19396
19492
|
function classifyJournalBuckets(buckets, binding) {
|
|
19397
19493
|
const deliverableBuckets = /* @__PURE__ */ new Set();
|
|
19398
19494
|
const retainedBuckets = [];
|
|
@@ -19467,17 +19563,17 @@ async function inspectJournalDelivery(buckets, options = {}) {
|
|
|
19467
19563
|
import {
|
|
19468
19564
|
appendFileSync,
|
|
19469
19565
|
closeSync as closeSync3,
|
|
19470
|
-
existsSync as
|
|
19566
|
+
existsSync as existsSync6,
|
|
19471
19567
|
fstatSync,
|
|
19472
19568
|
mkdirSync as mkdirSync5,
|
|
19473
19569
|
openSync as openSync3,
|
|
19474
19570
|
opendirSync,
|
|
19475
|
-
readFileSync as
|
|
19571
|
+
readFileSync as readFileSync6,
|
|
19476
19572
|
readSync,
|
|
19477
|
-
readdirSync
|
|
19573
|
+
readdirSync as readdirSync2
|
|
19478
19574
|
} from "fs";
|
|
19479
|
-
import { dirname as
|
|
19480
|
-
var JOURNAL_DIR =
|
|
19575
|
+
import { dirname as dirname7, join as join8 } from "path";
|
|
19576
|
+
var JOURNAL_DIR = join8(primConfigDirectory(), "moves");
|
|
19481
19577
|
var JOURNAL_BASENAME = "journal.ndjson";
|
|
19482
19578
|
var JOURNAL_STATS_SAMPLE_BYTES = 64 * 1024;
|
|
19483
19579
|
var PENDING_STATS_MAX_FILES = 128;
|
|
@@ -19489,7 +19585,7 @@ function envSlug(apiUrl) {
|
|
|
19489
19585
|
return slug;
|
|
19490
19586
|
}
|
|
19491
19587
|
function currentEnvDir() {
|
|
19492
|
-
return
|
|
19588
|
+
return join8(JOURNAL_DIR, envSlug(getSiteUrl()));
|
|
19493
19589
|
}
|
|
19494
19590
|
function parseMoves(content) {
|
|
19495
19591
|
const moves = [];
|
|
@@ -19507,15 +19603,15 @@ function parseMoves(content) {
|
|
|
19507
19603
|
function listBuckets() {
|
|
19508
19604
|
const out = [];
|
|
19509
19605
|
const envDir = currentEnvDir();
|
|
19510
|
-
if (!
|
|
19606
|
+
if (!existsSync6(envDir)) {
|
|
19511
19607
|
return out;
|
|
19512
19608
|
}
|
|
19513
|
-
for (const entry of
|
|
19609
|
+
for (const entry of readdirSync2(envDir, { withFileTypes: true })) {
|
|
19514
19610
|
if (!entry.isDirectory()) {
|
|
19515
19611
|
continue;
|
|
19516
19612
|
}
|
|
19517
|
-
const path =
|
|
19518
|
-
if (
|
|
19613
|
+
const path = join8(envDir, entry.name, JOURNAL_BASENAME);
|
|
19614
|
+
if (existsSync6(path)) {
|
|
19519
19615
|
out.push({ bucket: entry.name, path });
|
|
19520
19616
|
}
|
|
19521
19617
|
}
|
|
@@ -19532,22 +19628,23 @@ function oldestCapturedAt(moves) {
|
|
|
19532
19628
|
}
|
|
19533
19629
|
return oldest;
|
|
19534
19630
|
}
|
|
19535
|
-
function sampleJournalFile(path, maxBytes = JOURNAL_STATS_SAMPLE_BYTES) {
|
|
19631
|
+
function sampleJournalFile(path, maxBytes = JOURNAL_STATS_SAMPLE_BYTES, options = {}) {
|
|
19536
19632
|
const fd = openSync3(path, "r");
|
|
19537
19633
|
try {
|
|
19538
19634
|
const initial = fstatSync(fd);
|
|
19539
|
-
const
|
|
19635
|
+
const start = options.fromDrainCheckpoint ? drainResumeOffset(path, rotationIdentity(initial)) : 0;
|
|
19636
|
+
const target = Math.min(initial.size - start, Math.max(0, maxBytes));
|
|
19540
19637
|
const buffer = Buffer.allocUnsafe(target);
|
|
19541
19638
|
let sampledBytes = 0;
|
|
19542
19639
|
while (sampledBytes < target) {
|
|
19543
|
-
const count = readSync(fd, buffer, sampledBytes, target - sampledBytes, sampledBytes);
|
|
19640
|
+
const count = readSync(fd, buffer, sampledBytes, target - sampledBytes, start + sampledBytes);
|
|
19544
19641
|
if (count === 0) {
|
|
19545
19642
|
break;
|
|
19546
19643
|
}
|
|
19547
19644
|
sampledBytes += count;
|
|
19548
19645
|
}
|
|
19549
19646
|
const stat = fstatSync(fd);
|
|
19550
|
-
const sampled = stat.size > sampledBytes;
|
|
19647
|
+
const sampled = stat.size - start > sampledBytes;
|
|
19551
19648
|
let content = buffer.subarray(0, sampledBytes).toString("utf-8");
|
|
19552
19649
|
if (sampled) {
|
|
19553
19650
|
const lastNewline = content.lastIndexOf("\n");
|
|
@@ -19557,6 +19654,7 @@ function sampleJournalFile(path, maxBytes = JOURNAL_STATS_SAMPLE_BYTES) {
|
|
|
19557
19654
|
const moves = parseMoves(content);
|
|
19558
19655
|
return {
|
|
19559
19656
|
sizeBytes: stat.size,
|
|
19657
|
+
pendingBytes: Math.max(0, stat.size - start),
|
|
19560
19658
|
mtimeMs: stat.mtimeMs,
|
|
19561
19659
|
lineCount: sampled && stat.size > 0 ? Math.max(1, lines.length) : lines.length,
|
|
19562
19660
|
oldestCapturedAt: oldestCapturedAt(moves),
|
|
@@ -19575,7 +19673,7 @@ function parseFlushingPid(name) {
|
|
|
19575
19673
|
return Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
19576
19674
|
}
|
|
19577
19675
|
function listFlushingInDir(dir, bucket, options = {}) {
|
|
19578
|
-
if (!
|
|
19676
|
+
if (!existsSync6(dir)) {
|
|
19579
19677
|
return [];
|
|
19580
19678
|
}
|
|
19581
19679
|
const out = [];
|
|
@@ -19589,10 +19687,10 @@ function listFlushingInDir(dir, bucket, options = {}) {
|
|
|
19589
19687
|
entry = entries.readSync();
|
|
19590
19688
|
continue;
|
|
19591
19689
|
}
|
|
19592
|
-
const path =
|
|
19690
|
+
const path = join8(dir, entry.name);
|
|
19593
19691
|
let sample;
|
|
19594
19692
|
try {
|
|
19595
|
-
sample = sampleJournalFile(path, remainingBytes);
|
|
19693
|
+
sample = sampleJournalFile(path, remainingBytes, { fromDrainCheckpoint: true });
|
|
19596
19694
|
} catch (err) {
|
|
19597
19695
|
if (err.code === "ENOENT") {
|
|
19598
19696
|
entry = entries.readSync();
|
|
@@ -19616,18 +19714,18 @@ function listFlushingInDir(dir, bucket, options = {}) {
|
|
|
19616
19714
|
}
|
|
19617
19715
|
function listFlushing(options = {}) {
|
|
19618
19716
|
const envDir = currentEnvDir();
|
|
19619
|
-
if (!
|
|
19717
|
+
if (!existsSync6(envDir)) {
|
|
19620
19718
|
return [];
|
|
19621
19719
|
}
|
|
19622
19720
|
const out = [];
|
|
19623
19721
|
let remainingBytes = options.sampleBytes ?? JOURNAL_STATS_SAMPLE_BYTES;
|
|
19624
19722
|
const maxFiles = options.maxFiles ?? Number.POSITIVE_INFINITY;
|
|
19625
|
-
for (const entry of
|
|
19723
|
+
for (const entry of readdirSync2(envDir, { withFileTypes: true })) {
|
|
19626
19724
|
if (out.length >= maxFiles) {
|
|
19627
19725
|
break;
|
|
19628
19726
|
}
|
|
19629
19727
|
if (entry.isDirectory()) {
|
|
19630
|
-
const found = listFlushingInDir(
|
|
19728
|
+
const found = listFlushingInDir(join8(envDir, entry.name), entry.name, {
|
|
19631
19729
|
sampleBytes: remainingBytes,
|
|
19632
19730
|
maxFiles: maxFiles - out.length
|
|
19633
19731
|
});
|
|
@@ -19640,6 +19738,17 @@ function listFlushing(options = {}) {
|
|
|
19640
19738
|
}
|
|
19641
19739
|
return out;
|
|
19642
19740
|
}
|
|
19741
|
+
function sweepOrphanedDrainProgress() {
|
|
19742
|
+
const envDir = currentEnvDir();
|
|
19743
|
+
if (!existsSync6(envDir)) {
|
|
19744
|
+
return;
|
|
19745
|
+
}
|
|
19746
|
+
for (const entry of readdirSync2(envDir, { withFileTypes: true })) {
|
|
19747
|
+
if (entry.isDirectory()) {
|
|
19748
|
+
sweepOrphanedDrainCheckpoints(join8(envDir, entry.name));
|
|
19749
|
+
}
|
|
19750
|
+
}
|
|
19751
|
+
}
|
|
19643
19752
|
function bucketStats(options = {}) {
|
|
19644
19753
|
const out = [];
|
|
19645
19754
|
let remainingBytes = options.sampleBytes ?? JOURNAL_STATS_SAMPLE_BYTES;
|
|
@@ -19686,8 +19795,12 @@ function pendingJournalStats() {
|
|
|
19686
19795
|
|
|
19687
19796
|
// src/flusher.ts
|
|
19688
19797
|
var BATCH_SIZE = 500;
|
|
19798
|
+
var BATCH_MAX_BYTES = 1048576;
|
|
19689
19799
|
var HTTP_TIMEOUT_MS = 1e4;
|
|
19690
19800
|
var ORPHAN_QUARANTINE_MS = 6e4;
|
|
19801
|
+
function exceedsBatchBytes(count, bytes, nextBytes, maxBytes) {
|
|
19802
|
+
return count > 0 && bytes + nextBytes > maxBytes;
|
|
19803
|
+
}
|
|
19691
19804
|
function addDrainCounts(left, right) {
|
|
19692
19805
|
return {
|
|
19693
19806
|
flushed: left.flushed + right.flushed,
|
|
@@ -19731,23 +19844,31 @@ function deadLetterReason(error) {
|
|
|
19731
19844
|
}
|
|
19732
19845
|
return void 0;
|
|
19733
19846
|
}
|
|
19734
|
-
|
|
19847
|
+
function mayBeBatchSizeFailure(error) {
|
|
19848
|
+
if (error instanceof HttpError) {
|
|
19849
|
+
return error.status >= 500 && error.status !== 503;
|
|
19850
|
+
}
|
|
19851
|
+
return error instanceof Error && error.name === "TimeoutError";
|
|
19852
|
+
}
|
|
19853
|
+
async function* rawJournalLines(input, offset) {
|
|
19735
19854
|
let pending = [];
|
|
19736
19855
|
let pendingBytes = 0;
|
|
19856
|
+
let end = offset;
|
|
19737
19857
|
for await (const value of input) {
|
|
19738
19858
|
const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
19739
19859
|
let start = 0;
|
|
19740
19860
|
let newline = chunk.indexOf(10, start);
|
|
19741
19861
|
while (newline !== -1) {
|
|
19742
19862
|
const tail = chunk.subarray(start, newline + 1);
|
|
19743
|
-
|
|
19744
|
-
|
|
19745
|
-
} else {
|
|
19863
|
+
let line = tail;
|
|
19864
|
+
if (pending.length > 0) {
|
|
19746
19865
|
pending.push(tail);
|
|
19747
|
-
|
|
19866
|
+
line = Buffer.concat(pending, pendingBytes + tail.length);
|
|
19748
19867
|
pending = [];
|
|
19749
19868
|
pendingBytes = 0;
|
|
19750
19869
|
}
|
|
19870
|
+
end += line.length;
|
|
19871
|
+
yield { bytes: line, end };
|
|
19751
19872
|
start = newline + 1;
|
|
19752
19873
|
newline = chunk.indexOf(10, start);
|
|
19753
19874
|
}
|
|
@@ -19758,21 +19879,46 @@ async function* rawJournalLines(input) {
|
|
|
19758
19879
|
}
|
|
19759
19880
|
}
|
|
19760
19881
|
if (pendingBytes > 0) {
|
|
19761
|
-
|
|
19882
|
+
const bytes = pending.length === 1 ? pending[0] : Buffer.concat(pending, pendingBytes);
|
|
19883
|
+
yield { bytes, end: end + pendingBytes };
|
|
19762
19884
|
}
|
|
19763
19885
|
}
|
|
19886
|
+
var drainProgressWarned = false;
|
|
19764
19887
|
async function drainFlushingPath(flushingPath, client2, binding, options = {}) {
|
|
19888
|
+
const rotation = rotationIdentity(statSync2(flushingPath));
|
|
19889
|
+
const resumeAt = drainResumeOffset(flushingPath, rotation);
|
|
19890
|
+
const recordProgress = (end) => {
|
|
19891
|
+
if (end === void 0 || end >= rotation.size) {
|
|
19892
|
+
return;
|
|
19893
|
+
}
|
|
19894
|
+
try {
|
|
19895
|
+
writeDrainCheckpoint(flushingPath, { v: 2, offset: end, ...rotation });
|
|
19896
|
+
drainProgressWarned = false;
|
|
19897
|
+
} catch (error) {
|
|
19898
|
+
if (!drainProgressWarned) {
|
|
19899
|
+
drainProgressWarned = true;
|
|
19900
|
+
process.stderr.write(
|
|
19901
|
+
`[prim] could not record drain progress: ${error instanceof Error ? error.message : String(error)}
|
|
19902
|
+
`
|
|
19903
|
+
);
|
|
19904
|
+
}
|
|
19905
|
+
}
|
|
19906
|
+
};
|
|
19907
|
+
const reportProgress = (delta) => {
|
|
19908
|
+
options.onProgress?.(delta);
|
|
19909
|
+
return delta;
|
|
19910
|
+
};
|
|
19765
19911
|
const postBatch = async (batch2) => {
|
|
19912
|
+
const moves = batch2.map((entry) => entry.move);
|
|
19766
19913
|
try {
|
|
19767
|
-
const request2 = buildOrganizationBoundMoveRequest(
|
|
19914
|
+
const request2 = buildOrganizationBoundMoveRequest(moves, binding);
|
|
19768
19915
|
const response = await client2.post("/api/cli/moves/ingest", request2, {
|
|
19769
19916
|
signal: AbortSignal.timeout(HTTP_TIMEOUT_MS)
|
|
19770
19917
|
});
|
|
19771
|
-
requireDurableIngestAcknowledgement(response,
|
|
19772
|
-
return { flushed: batch2.length, quarantined: 0 };
|
|
19918
|
+
requireDurableIngestAcknowledgement(response, moves.length, collectionScopeRoots(moves));
|
|
19773
19919
|
} catch (error) {
|
|
19774
19920
|
const reason = deadLetterReason(error);
|
|
19775
|
-
if (!reason) {
|
|
19921
|
+
if (!(reason || mayBeBatchSizeFailure(error))) {
|
|
19776
19922
|
throw error;
|
|
19777
19923
|
}
|
|
19778
19924
|
if (batch2.length > 1) {
|
|
@@ -19781,33 +19927,46 @@ async function drainFlushingPath(flushingPath, client2, binding, options = {}) {
|
|
|
19781
19927
|
const right = await postBatch(batch2.slice(midpoint));
|
|
19782
19928
|
return addDrainCounts(left, right);
|
|
19783
19929
|
}
|
|
19784
|
-
|
|
19930
|
+
if (!reason) {
|
|
19931
|
+
throw error;
|
|
19932
|
+
}
|
|
19933
|
+
const [entry] = batch2;
|
|
19785
19934
|
const quarantined = quarantineMove(
|
|
19786
19935
|
flushingPath,
|
|
19787
|
-
move,
|
|
19936
|
+
entry.move,
|
|
19788
19937
|
reason,
|
|
19789
19938
|
Date.now,
|
|
19790
19939
|
options.deadLetterPersistence
|
|
19791
19940
|
);
|
|
19941
|
+
recordProgress(entry.end);
|
|
19792
19942
|
process.stderr.write(
|
|
19793
19943
|
`[prim] quarantined rejected move ${quarantined.quarantineId.slice(0, 12)} (${reason})
|
|
19794
19944
|
`
|
|
19795
19945
|
);
|
|
19796
|
-
return { flushed: 0, quarantined: 1 };
|
|
19946
|
+
return reportProgress({ flushed: 0, quarantined: 1 });
|
|
19797
19947
|
}
|
|
19948
|
+
recordProgress(batch2.at(-1)?.end);
|
|
19949
|
+
return reportProgress({ flushed: batch2.length, quarantined: 0 });
|
|
19798
19950
|
};
|
|
19799
|
-
const input = createReadStream(flushingPath);
|
|
19951
|
+
const input = createReadStream(flushingPath, { start: resumeAt });
|
|
19800
19952
|
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
19801
19953
|
let batch = [];
|
|
19954
|
+
let batchBytes = 0;
|
|
19802
19955
|
let counts = { flushed: 0, quarantined: 0 };
|
|
19956
|
+
const postPending = async () => {
|
|
19957
|
+
counts = addDrainCounts(counts, await postBatch(batch));
|
|
19958
|
+
batch = [];
|
|
19959
|
+
batchBytes = 0;
|
|
19960
|
+
};
|
|
19803
19961
|
try {
|
|
19804
|
-
for await (const rawLine of rawJournalLines(input)) {
|
|
19962
|
+
for await (const { bytes: rawLine, end } of rawJournalLines(input, resumeAt)) {
|
|
19805
19963
|
if (rawLine.length === 1 && rawLine[0] === 10 || rawLine.length === 2 && rawLine[0] === 13 && rawLine[1] === 10) {
|
|
19806
19964
|
continue;
|
|
19807
19965
|
}
|
|
19966
|
+
let move;
|
|
19808
19967
|
try {
|
|
19809
19968
|
const line = decoder.decode(rawLine);
|
|
19810
|
-
|
|
19969
|
+
move = JSON.parse(line);
|
|
19811
19970
|
} catch {
|
|
19812
19971
|
const quarantined = quarantineRawLine(
|
|
19813
19972
|
flushingPath,
|
|
@@ -19819,24 +19978,35 @@ async function drainFlushingPath(flushingPath, client2, binding, options = {}) {
|
|
|
19819
19978
|
`[prim] quarantined invalid journal line ${quarantined.quarantineId.slice(0, 12)} (invalid_move)
|
|
19820
19979
|
`
|
|
19821
19980
|
);
|
|
19822
|
-
counts
|
|
19981
|
+
counts = addDrainCounts(counts, reportProgress({ flushed: 0, quarantined: 1 }));
|
|
19982
|
+
if (batch.length === 0) {
|
|
19983
|
+
recordProgress(end);
|
|
19984
|
+
}
|
|
19823
19985
|
continue;
|
|
19824
19986
|
}
|
|
19825
|
-
if (batch.length
|
|
19826
|
-
|
|
19827
|
-
|
|
19987
|
+
if (exceedsBatchBytes(batch.length, batchBytes, rawLine.length, BATCH_MAX_BYTES)) {
|
|
19988
|
+
await postPending();
|
|
19989
|
+
}
|
|
19990
|
+
batch.push({ move, end });
|
|
19991
|
+
batchBytes += rawLine.length;
|
|
19992
|
+
if (batch.length >= BATCH_SIZE) {
|
|
19993
|
+
await postPending();
|
|
19828
19994
|
}
|
|
19829
19995
|
}
|
|
19830
19996
|
if (batch.length > 0) {
|
|
19831
|
-
|
|
19997
|
+
await postPending();
|
|
19832
19998
|
}
|
|
19833
19999
|
} finally {
|
|
19834
20000
|
input.destroy();
|
|
19835
20001
|
}
|
|
19836
20002
|
unlinkSync(flushingPath);
|
|
20003
|
+
try {
|
|
20004
|
+
removeDrainCheckpoint(flushingPath);
|
|
20005
|
+
} catch {
|
|
20006
|
+
}
|
|
19837
20007
|
return counts;
|
|
19838
20008
|
}
|
|
19839
|
-
async function drainPath(path, client2, binding) {
|
|
20009
|
+
async function drainPath(path, client2, binding, onProgress) {
|
|
19840
20010
|
const tmpPath = `${path}.flushing.${String(Date.now())}.${String(process.pid)}`;
|
|
19841
20011
|
try {
|
|
19842
20012
|
renameSync3(path, tmpPath);
|
|
@@ -19846,7 +20016,19 @@ async function drainPath(path, client2, binding) {
|
|
|
19846
20016
|
}
|
|
19847
20017
|
throw err;
|
|
19848
20018
|
}
|
|
19849
|
-
return drainFlushingPath(tmpPath, client2, binding);
|
|
20019
|
+
return drainFlushingPath(tmpPath, client2, binding, { onProgress });
|
|
20020
|
+
}
|
|
20021
|
+
async function settleDrain(drain) {
|
|
20022
|
+
let progress = { flushed: 0, quarantined: 0 };
|
|
20023
|
+
try {
|
|
20024
|
+
return {
|
|
20025
|
+
counts: await drain((delta) => {
|
|
20026
|
+
progress = addDrainCounts(progress, delta);
|
|
20027
|
+
})
|
|
20028
|
+
};
|
|
20029
|
+
} catch (error) {
|
|
20030
|
+
return { counts: progress, failure: { error } };
|
|
20031
|
+
}
|
|
19850
20032
|
}
|
|
19851
20033
|
function selectRecoverable(files, now, opts = {}) {
|
|
19852
20034
|
const quarantineMs = opts.quarantineMs ?? ORPHAN_QUARANTINE_MS;
|
|
@@ -19863,7 +20045,8 @@ async function recoverOrphans(candidates, options) {
|
|
|
19863
20045
|
flushed: 0,
|
|
19864
20046
|
quarantined: 0,
|
|
19865
20047
|
errors: [],
|
|
19866
|
-
failedBuckets: /* @__PURE__ */ new Set()
|
|
20048
|
+
failedBuckets: /* @__PURE__ */ new Set(),
|
|
20049
|
+
failedDrainFlushed: 0
|
|
19867
20050
|
};
|
|
19868
20051
|
const recoverable = selectRecoverable(candidates, options.now ?? Date.now(), {
|
|
19869
20052
|
ownerPid: options.ownerPid ?? process.pid,
|
|
@@ -19874,28 +20057,41 @@ async function recoverOrphans(candidates, options) {
|
|
|
19874
20057
|
if (summary.failedBuckets.has(file.bucket)) {
|
|
19875
20058
|
continue;
|
|
19876
20059
|
}
|
|
19877
|
-
|
|
19878
|
-
|
|
19879
|
-
|
|
19880
|
-
|
|
19881
|
-
|
|
19882
|
-
summary.errors.push(
|
|
20060
|
+
const { counts, failure } = await settleDrain((onProgress) => drain(file.path, onProgress));
|
|
20061
|
+
summary.flushed += counts.flushed;
|
|
20062
|
+
summary.quarantined += counts.quarantined;
|
|
20063
|
+
if (failure) {
|
|
20064
|
+
summary.failedDrainFlushed += counts.flushed;
|
|
20065
|
+
summary.errors.push(failure.error);
|
|
19883
20066
|
summary.failedBuckets.add(file.bucket);
|
|
19884
20067
|
}
|
|
19885
20068
|
}
|
|
19886
20069
|
return summary;
|
|
19887
20070
|
}
|
|
19888
20071
|
var FlushError = class extends Error {
|
|
20072
|
+
/** Moves the whole sweep acknowledged, across every drain it ran. */
|
|
19889
20073
|
flushed;
|
|
19890
20074
|
quarantined;
|
|
19891
|
-
|
|
20075
|
+
/** Buckets the failed sweep held back without sending, as on success. */
|
|
20076
|
+
retained;
|
|
20077
|
+
/**
|
|
20078
|
+
* Of `flushed`, the Moves acknowledged by the drains that failed, before
|
|
20079
|
+
* they failed. Only this shows the failure itself is advancing: `flushed`
|
|
20080
|
+
* also counts other buckets' drains, so a bucket whose drain fails every
|
|
20081
|
+
* time would read as progress while a sibling bucket kept delivering.
|
|
20082
|
+
*/
|
|
20083
|
+
failedDrainFlushed;
|
|
20084
|
+
constructor(cause, flushed, quarantined, retained = [], failedDrainFlushed = 0) {
|
|
19892
20085
|
super(cause instanceof Error ? cause.message : String(cause), { cause });
|
|
19893
20086
|
this.name = "FlushError";
|
|
19894
20087
|
this.flushed = flushed;
|
|
19895
20088
|
this.quarantined = quarantined;
|
|
20089
|
+
this.retained = retained;
|
|
20090
|
+
this.failedDrainFlushed = failedDrainFlushed;
|
|
19896
20091
|
}
|
|
19897
20092
|
};
|
|
19898
20093
|
async function flushOnce() {
|
|
20094
|
+
sweepOrphanedDrainProgress();
|
|
19899
20095
|
const orphanCandidates = listFlushing({ sampleBytes: 0 });
|
|
19900
20096
|
const liveBuckets = listBuckets();
|
|
19901
20097
|
if (orphanCandidates.length === 0 && liveBuckets.length === 0) {
|
|
@@ -19912,10 +20108,11 @@ async function flushOnce() {
|
|
|
19912
20108
|
const binding = inspection.binding;
|
|
19913
20109
|
const recovered = await recoverOrphans(
|
|
19914
20110
|
orphanCandidates.filter((file) => inspection.deliverableBuckets.has(file.bucket)),
|
|
19915
|
-
{ drain: (path) => drainFlushingPath(path, client2, binding) }
|
|
20111
|
+
{ drain: (path, onProgress) => drainFlushingPath(path, client2, binding, { onProgress }) }
|
|
19916
20112
|
);
|
|
19917
20113
|
let total = recovered.flushed;
|
|
19918
20114
|
let quarantined = recovered.quarantined;
|
|
20115
|
+
let failedDrainFlushed = recovered.failedDrainFlushed;
|
|
19919
20116
|
const errors = recovered.errors;
|
|
19920
20117
|
for (const { bucket, path } of liveBuckets) {
|
|
19921
20118
|
if (!inspection.deliverableBuckets.has(bucket)) {
|
|
@@ -19924,21 +20121,29 @@ async function flushOnce() {
|
|
|
19924
20121
|
if (recovered.failedBuckets.has(bucket)) {
|
|
19925
20122
|
continue;
|
|
19926
20123
|
}
|
|
19927
|
-
|
|
19928
|
-
|
|
19929
|
-
|
|
19930
|
-
|
|
19931
|
-
|
|
19932
|
-
|
|
20124
|
+
const { counts, failure } = await settleDrain(
|
|
20125
|
+
(onProgress) => drainPath(path, client2, binding, onProgress)
|
|
20126
|
+
);
|
|
20127
|
+
total += counts.flushed;
|
|
20128
|
+
quarantined += counts.quarantined;
|
|
20129
|
+
if (failure) {
|
|
20130
|
+
failedDrainFlushed += counts.flushed;
|
|
20131
|
+
errors.push(failure.error);
|
|
19933
20132
|
}
|
|
19934
20133
|
}
|
|
19935
20134
|
if (errors.length > 0) {
|
|
19936
|
-
throw new FlushError(
|
|
20135
|
+
throw new FlushError(
|
|
20136
|
+
errors[0],
|
|
20137
|
+
total,
|
|
20138
|
+
quarantined,
|
|
20139
|
+
inspection.retainedBuckets,
|
|
20140
|
+
failedDrainFlushed
|
|
20141
|
+
);
|
|
19937
20142
|
}
|
|
19938
20143
|
return inspection.retainedBuckets.length > 0 ? { flushed: total, quarantined, retained: inspection.retainedBuckets } : { flushed: total, quarantined };
|
|
19939
20144
|
}
|
|
19940
20145
|
var flushInFlight;
|
|
19941
|
-
var FLUSH_LOCK_PATH =
|
|
20146
|
+
var FLUSH_LOCK_PATH = join9(dirname8(JOURNAL_DIR), ".flush.lock");
|
|
19942
20147
|
var FLUSH_LOCK_TIMEOUT_MS = 250;
|
|
19943
20148
|
function isFlushLockContended(error) {
|
|
19944
20149
|
return error instanceof Error && error.message.startsWith("timed out waiting for file lock");
|
|
@@ -19962,6 +20167,30 @@ function flush() {
|
|
|
19962
20167
|
flushInFlight = attempt;
|
|
19963
20168
|
return attempt;
|
|
19964
20169
|
}
|
|
20170
|
+
function hasPendingDrainWork(stats) {
|
|
20171
|
+
return stats.pendingCount > 0 || stats.sampled || stats.strandedFileCount > 0;
|
|
20172
|
+
}
|
|
20173
|
+
|
|
20174
|
+
// src/lib/delivery-backlog.ts
|
|
20175
|
+
var MS_PER_SECOND = 1e3;
|
|
20176
|
+
var MS_PER_MINUTE = 60 * MS_PER_SECOND;
|
|
20177
|
+
var MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
|
20178
|
+
var MS_PER_DAY = 24 * MS_PER_HOUR;
|
|
20179
|
+
function isNonNegativeInteger(value) {
|
|
20180
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
20181
|
+
}
|
|
20182
|
+
function isPositiveInteger(value) {
|
|
20183
|
+
return isNonNegativeInteger(value) && value > 0;
|
|
20184
|
+
}
|
|
20185
|
+
function deliveryBacklogState(ingestion) {
|
|
20186
|
+
if (ingestion?.healthy !== false) return void 0;
|
|
20187
|
+
const failures = ingestion.consecutiveFailures;
|
|
20188
|
+
const failedWithoutProgress = failures !== 0 && !(isPositiveInteger(failures) && isPositiveInteger(ingestion.lastFailedDrainAcknowledgedCount));
|
|
20189
|
+
if (failedWithoutProgress) return "failing";
|
|
20190
|
+
const retained = ingestion.lastRetainedBucketCount;
|
|
20191
|
+
if (!isNonNegativeInteger(retained)) return "failing";
|
|
20192
|
+
return retained > 0 ? "retained" : "draining";
|
|
20193
|
+
}
|
|
19965
20194
|
|
|
19966
20195
|
// src/lib/presence.ts
|
|
19967
20196
|
var DECISION_ORIGIN = "https://app.getprimitive.ai";
|
|
@@ -20037,7 +20266,8 @@ function formatStatusline(version, snapshot, resolveIngestionStatus, options = {
|
|
|
20037
20266
|
if (snapshot.ingestion?.healthy === false) {
|
|
20038
20267
|
const pending = snapshot.ingestion.pendingCount;
|
|
20039
20268
|
const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
|
|
20040
|
-
|
|
20269
|
+
const delivery = deliveryBacklogState(snapshot.ingestion) === "draining" && snapshot.heartbeat?.healthy === true ? "draining" : "stalled";
|
|
20270
|
+
return `primitive ${version} (daemon: degraded \xB7 delivery: ${delivery}${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""}${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
|
|
20041
20271
|
}
|
|
20042
20272
|
if (snapshot.heartbeat?.healthy === false) {
|
|
20043
20273
|
return `primitive ${version} (daemon: degraded \xB7 presence: unavailable${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
|
|
@@ -20115,8 +20345,8 @@ var StatuslineIngestionCache = class {
|
|
|
20115
20345
|
|
|
20116
20346
|
// src/daemon/client-instance-id.ts
|
|
20117
20347
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
20118
|
-
import { chmodSync as
|
|
20119
|
-
import { join as
|
|
20348
|
+
import { chmodSync as chmodSync4, lstatSync as lstatSync2, readFileSync as readFileSync7 } from "fs";
|
|
20349
|
+
import { join as join10 } from "path";
|
|
20120
20350
|
var CONFIG_DIRECTORY_MODE = 448;
|
|
20121
20351
|
var INSTANCE_FILE_MODE = 384;
|
|
20122
20352
|
var INSTANCE_ENTROPY_BYTES = 32;
|
|
@@ -20133,7 +20363,7 @@ function ensurePrivateConfigDirectory(configDir) {
|
|
|
20133
20363
|
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
20134
20364
|
throw new Error("prim config path must be a private directory");
|
|
20135
20365
|
}
|
|
20136
|
-
|
|
20366
|
+
chmodSync4(configDir, CONFIG_DIRECTORY_MODE);
|
|
20137
20367
|
syncDirectory(configDir);
|
|
20138
20368
|
}
|
|
20139
20369
|
function readExisting(path) {
|
|
@@ -20149,11 +20379,11 @@ function readExisting(path) {
|
|
|
20149
20379
|
if (!metadata.isFile() || metadata.isSymbolicLink()) {
|
|
20150
20380
|
throw new Error("stored client instance identity is invalid");
|
|
20151
20381
|
}
|
|
20152
|
-
|
|
20382
|
+
chmodSync4(path, INSTANCE_FILE_MODE);
|
|
20153
20383
|
if (metadata.size > MAX_STORED_BYTES) {
|
|
20154
20384
|
throw new Error("stored client instance identity is invalid");
|
|
20155
20385
|
}
|
|
20156
|
-
const raw =
|
|
20386
|
+
const raw = readFileSync7(path, "utf8");
|
|
20157
20387
|
const value = raw.endsWith("\n") ? raw.slice(0, -1) : raw;
|
|
20158
20388
|
if (raw !== value && raw !== `${value}
|
|
20159
20389
|
` || !isClientInstanceId(value)) {
|
|
@@ -20167,8 +20397,8 @@ function atomicWrite2(path, value) {
|
|
|
20167
20397
|
}
|
|
20168
20398
|
async function getOrCreateClientInstanceId(options) {
|
|
20169
20399
|
const configDir = options.configDir;
|
|
20170
|
-
const path =
|
|
20171
|
-
const lockPath =
|
|
20400
|
+
const path = join10(configDir, "client_instance_id");
|
|
20401
|
+
const lockPath = join10(configDir, "client-instance.lock");
|
|
20172
20402
|
ensurePrivateConfigDirectory(configDir);
|
|
20173
20403
|
return withFileLock(
|
|
20174
20404
|
lockPath,
|
|
@@ -20192,7 +20422,7 @@ async function getOrCreateClientInstanceId(options) {
|
|
|
20192
20422
|
|
|
20193
20423
|
// src/daemon/client.ts
|
|
20194
20424
|
import { createConnection } from "net";
|
|
20195
|
-
import { join as
|
|
20425
|
+
import { join as join11 } from "path";
|
|
20196
20426
|
|
|
20197
20427
|
// src/daemon/principal.ts
|
|
20198
20428
|
import { createHash as createHash5 } from "crypto";
|
|
@@ -20269,7 +20499,7 @@ function parseDaemonResponseEnvelope(value) {
|
|
|
20269
20499
|
}
|
|
20270
20500
|
|
|
20271
20501
|
// src/daemon/client.ts
|
|
20272
|
-
var SOCK_PATH =
|
|
20502
|
+
var SOCK_PATH = join11(primConfigDirectory(), "sock");
|
|
20273
20503
|
var DEFAULT_TIMEOUT_MS2 = 250;
|
|
20274
20504
|
var nextRequestId = 1;
|
|
20275
20505
|
function daemonRequest(method, params = {}, opts = {}) {
|
|
@@ -20538,8 +20768,8 @@ function assertCallerEnvMatches(callerEnv, boundEnv) {
|
|
|
20538
20768
|
}
|
|
20539
20769
|
|
|
20540
20770
|
// src/daemon/health.ts
|
|
20541
|
-
import { chmodSync as
|
|
20542
|
-
import { dirname as
|
|
20771
|
+
import { chmodSync as chmodSync5, existsSync as existsSync7, mkdirSync as mkdirSync6, renameSync as renameSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
20772
|
+
import { dirname as dirname9, join as join12 } from "path";
|
|
20543
20773
|
var CONFIG_DIR_MODE2 = 448;
|
|
20544
20774
|
var HEALTH_FILE_MODE = 384;
|
|
20545
20775
|
var INGESTION_SLA_MS = 3e4;
|
|
@@ -20547,12 +20777,13 @@ var HEARTBEAT_FRESH_MS = 9e4;
|
|
|
20547
20777
|
var HEARTBEAT_RETRY_CAP_MS = 3e4;
|
|
20548
20778
|
var INGESTION_RETRY_BASE_MS = 5e3;
|
|
20549
20779
|
var INGESTION_RETRY_CAP_MS = 15e3;
|
|
20550
|
-
var DAEMON_HEALTH_PATH =
|
|
20551
|
-
function createDaemonHealthState(version, pid, startedAt2) {
|
|
20780
|
+
var DAEMON_HEALTH_PATH = join12(primConfigDirectory(), "daemon-health.json");
|
|
20781
|
+
function createDaemonHealthState(version, pid, startedAt2, siteUrl) {
|
|
20552
20782
|
return {
|
|
20553
20783
|
schemaVersion: 1,
|
|
20554
20784
|
version,
|
|
20555
20785
|
pid,
|
|
20786
|
+
siteUrl,
|
|
20556
20787
|
startedAt: startedAt2,
|
|
20557
20788
|
updatedAt: startedAt2,
|
|
20558
20789
|
healthy: false,
|
|
@@ -20563,7 +20794,9 @@ function createDaemonHealthState(version, pid, startedAt2) {
|
|
|
20563
20794
|
pendingCount: 0,
|
|
20564
20795
|
pendingSampled: false,
|
|
20565
20796
|
strandedCount: 0,
|
|
20566
|
-
lastAcknowledgedCount: 0
|
|
20797
|
+
lastAcknowledgedCount: 0,
|
|
20798
|
+
lastFailedDrainAcknowledgedCount: 0,
|
|
20799
|
+
lastRetainedBucketCount: 0
|
|
20567
20800
|
}
|
|
20568
20801
|
};
|
|
20569
20802
|
}
|
|
@@ -20585,37 +20818,37 @@ function ingestionRetryDelayMs(consecutiveFailures, random = Math.random) {
|
|
|
20585
20818
|
return retryDelayMs(consecutiveFailures, INGESTION_RETRY_CAP_MS, random);
|
|
20586
20819
|
}
|
|
20587
20820
|
function writeDaemonHealthState(state, path = DAEMON_HEALTH_PATH) {
|
|
20588
|
-
const dir =
|
|
20589
|
-
if (!
|
|
20821
|
+
const dir = dirname9(path);
|
|
20822
|
+
if (!existsSync7(dir)) {
|
|
20590
20823
|
mkdirSync6(dir, { recursive: true, mode: CONFIG_DIR_MODE2 });
|
|
20591
20824
|
}
|
|
20592
|
-
const tmp =
|
|
20825
|
+
const tmp = join12(dir, `.${process.pid}.daemon-health.tmp`);
|
|
20593
20826
|
writeFileSync4(tmp, `${JSON.stringify(state, null, 2)}
|
|
20594
20827
|
`, { mode: HEALTH_FILE_MODE });
|
|
20595
|
-
|
|
20828
|
+
chmodSync5(tmp, HEALTH_FILE_MODE);
|
|
20596
20829
|
renameSync4(tmp, path);
|
|
20597
20830
|
}
|
|
20598
20831
|
|
|
20599
20832
|
// src/daemon/instance-lock.ts
|
|
20600
20833
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
20601
20834
|
import {
|
|
20602
|
-
chmodSync as
|
|
20603
|
-
existsSync as
|
|
20835
|
+
chmodSync as chmodSync6,
|
|
20836
|
+
existsSync as existsSync8,
|
|
20604
20837
|
mkdirSync as mkdirSync7,
|
|
20605
|
-
readFileSync as
|
|
20838
|
+
readFileSync as readFileSync8,
|
|
20606
20839
|
renameSync as renameSync5,
|
|
20607
|
-
rmSync as
|
|
20608
|
-
statSync as
|
|
20840
|
+
rmSync as rmSync6,
|
|
20841
|
+
statSync as statSync3,
|
|
20609
20842
|
unlinkSync as unlinkSync2,
|
|
20610
20843
|
writeFileSync as writeFileSync5
|
|
20611
20844
|
} from "fs";
|
|
20612
|
-
import { join as
|
|
20845
|
+
import { join as join13 } from "path";
|
|
20613
20846
|
var DIR_MODE = 448;
|
|
20614
|
-
var
|
|
20847
|
+
var FILE_MODE4 = 384;
|
|
20615
20848
|
var DAEMON_STARTUP_GRACE_MS = 5e3;
|
|
20616
20849
|
function readLockRecord(path) {
|
|
20617
20850
|
try {
|
|
20618
|
-
const value = JSON.parse(
|
|
20851
|
+
const value = JSON.parse(readFileSync8(path, "utf-8"));
|
|
20619
20852
|
if (Number.isSafeInteger(value.pid) && value.pid > 0 && typeof value.instanceId === "string" && typeof value.startedAt === "number") {
|
|
20620
20853
|
return value;
|
|
20621
20854
|
}
|
|
@@ -20625,23 +20858,23 @@ function readLockRecord(path) {
|
|
|
20625
20858
|
}
|
|
20626
20859
|
function numericPid(path) {
|
|
20627
20860
|
try {
|
|
20628
|
-
const pid = Number(
|
|
20861
|
+
const pid = Number(readFileSync8(path, "utf-8").trim());
|
|
20629
20862
|
return Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
|
|
20630
20863
|
} catch {
|
|
20631
20864
|
return;
|
|
20632
20865
|
}
|
|
20633
20866
|
}
|
|
20634
20867
|
function readDaemonOwner(configDir) {
|
|
20635
|
-
return readLockRecord(
|
|
20868
|
+
return readLockRecord(join13(configDir, "daemon.lock", "owner.json"));
|
|
20636
20869
|
}
|
|
20637
20870
|
function readDaemonPid(configDir) {
|
|
20638
|
-
const path =
|
|
20871
|
+
const path = join13(configDir, "daemon.pid");
|
|
20639
20872
|
const pid = numericPid(path);
|
|
20640
20873
|
if (pid === void 0) {
|
|
20641
20874
|
return;
|
|
20642
20875
|
}
|
|
20643
20876
|
try {
|
|
20644
|
-
return { pid, mtimeMs:
|
|
20877
|
+
return { pid, mtimeMs: statSync3(path).mtimeMs };
|
|
20645
20878
|
} catch {
|
|
20646
20879
|
return;
|
|
20647
20880
|
}
|
|
@@ -20650,11 +20883,11 @@ function acquireDaemonOwnership(configDir, opts = {}) {
|
|
|
20650
20883
|
const pid = opts.pid ?? process.pid;
|
|
20651
20884
|
const now = opts.now ?? Date.now();
|
|
20652
20885
|
const isAlive = opts.isAlive ?? processIsAlive;
|
|
20653
|
-
const lockDir =
|
|
20654
|
-
const ownerPath2 =
|
|
20655
|
-
const pidPath =
|
|
20886
|
+
const lockDir = join13(configDir, "daemon.lock");
|
|
20887
|
+
const ownerPath2 = join13(lockDir, "owner.json");
|
|
20888
|
+
const pidPath = join13(configDir, "daemon.pid");
|
|
20656
20889
|
const instanceId = randomUUID2();
|
|
20657
|
-
if (!
|
|
20890
|
+
if (!existsSync8(configDir)) {
|
|
20658
20891
|
mkdirSync7(configDir, { recursive: true, mode: DIR_MODE });
|
|
20659
20892
|
}
|
|
20660
20893
|
for (; ; ) {
|
|
@@ -20672,7 +20905,7 @@ function acquireDaemonOwnership(configDir, opts = {}) {
|
|
|
20672
20905
|
if (!existing) {
|
|
20673
20906
|
let mtimeMs;
|
|
20674
20907
|
try {
|
|
20675
|
-
mtimeMs =
|
|
20908
|
+
mtimeMs = statSync3(lockDir).mtimeMs;
|
|
20676
20909
|
} catch (statErr) {
|
|
20677
20910
|
if (statErr.code === "ENOENT") {
|
|
20678
20911
|
continue;
|
|
@@ -20686,7 +20919,7 @@ function acquireDaemonOwnership(configDir, opts = {}) {
|
|
|
20686
20919
|
const stale = `${lockDir}.stale.${String(pid)}.${instanceId}`;
|
|
20687
20920
|
try {
|
|
20688
20921
|
renameSync5(lockDir, stale);
|
|
20689
|
-
|
|
20922
|
+
rmSync6(stale, { recursive: true, force: true });
|
|
20690
20923
|
} catch (renameErr) {
|
|
20691
20924
|
if (renameErr.code !== "ENOENT") {
|
|
20692
20925
|
throw renameErr;
|
|
@@ -20704,15 +20937,15 @@ function acquireDaemonOwnership(configDir, opts = {}) {
|
|
|
20704
20937
|
};
|
|
20705
20938
|
const record = { pid, instanceId, startedAt: now };
|
|
20706
20939
|
writeFileSync5(ownerPath2, `${JSON.stringify(record)}
|
|
20707
|
-
`, { mode:
|
|
20708
|
-
|
|
20940
|
+
`, { mode: FILE_MODE4 });
|
|
20941
|
+
chmodSync6(ownerPath2, FILE_MODE4);
|
|
20709
20942
|
const legacyPid = numericPid(pidPath);
|
|
20710
20943
|
if (legacyPid !== void 0 && legacyPid !== pid && isAlive(legacyPid)) {
|
|
20711
|
-
|
|
20944
|
+
rmSync6(lockDir, { recursive: true, force: true });
|
|
20712
20945
|
throw new Error(`daemon already running (pid=${String(legacyPid)})`);
|
|
20713
20946
|
}
|
|
20714
|
-
writeFileSync5(pidPath, String(pid), { mode:
|
|
20715
|
-
|
|
20947
|
+
writeFileSync5(pidPath, String(pid), { mode: FILE_MODE4 });
|
|
20948
|
+
chmodSync6(pidPath, FILE_MODE4);
|
|
20716
20949
|
return ownership2;
|
|
20717
20950
|
}
|
|
20718
20951
|
function ownsDaemonInstance(ownership2) {
|
|
@@ -20742,7 +20975,7 @@ function releaseDaemonOwnership(ownership2, socketPath) {
|
|
|
20742
20975
|
}
|
|
20743
20976
|
}
|
|
20744
20977
|
}
|
|
20745
|
-
|
|
20978
|
+
rmSync6(ownership2.lockDir, { recursive: true, force: true });
|
|
20746
20979
|
if (cleanupError) {
|
|
20747
20980
|
throw cleanupError;
|
|
20748
20981
|
}
|
|
@@ -20837,7 +21070,7 @@ function parseStatuslineRequest(buffer) {
|
|
|
20837
21070
|
|
|
20838
21071
|
// src/daemon/server.ts
|
|
20839
21072
|
var CONFIG_DIR = primConfigDirectory();
|
|
20840
|
-
var SOCK_PATH2 =
|
|
21073
|
+
var SOCK_PATH2 = join14(CONFIG_DIR, "sock");
|
|
20841
21074
|
var CONFIG_DIR_MODE3 = 448;
|
|
20842
21075
|
var SOCKET_MODE = 384;
|
|
20843
21076
|
var MAX_SOCKET_CONNECTIONS = 64;
|
|
@@ -20854,7 +21087,7 @@ var EXIT_CRASH = 1;
|
|
|
20854
21087
|
var startedAt = Date.now();
|
|
20855
21088
|
var client = getClient();
|
|
20856
21089
|
var runtimeVersion = resolveRuntimeVersion();
|
|
20857
|
-
var daemonHealth = createDaemonHealthState(runtimeVersion, process.pid, startedAt);
|
|
21090
|
+
var daemonHealth = createDaemonHealthState(runtimeVersion, process.pid, startedAt, getSiteUrl());
|
|
20858
21091
|
var statuslineIngestionCache = new StatuslineIngestionCache(decisionIngestionStatus, {
|
|
20859
21092
|
resolveRepositoryBindingState: repositoryBindingState
|
|
20860
21093
|
});
|
|
@@ -20906,7 +21139,7 @@ function resolveRuntimeVersion() {
|
|
|
20906
21139
|
}
|
|
20907
21140
|
try {
|
|
20908
21141
|
const here = fileURLToPath(new URL(".", import.meta.url));
|
|
20909
|
-
const pkg = JSON.parse(
|
|
21142
|
+
const pkg = JSON.parse(readFileSync9(join14(here, "..", "..", "package.json"), "utf-8"));
|
|
20910
21143
|
return pkg.version ?? "unknown";
|
|
20911
21144
|
} catch {
|
|
20912
21145
|
return "unknown";
|
|
@@ -20924,6 +21157,7 @@ function purgePrincipalScopedState() {
|
|
|
20924
21157
|
lastOnlineNames = void 0;
|
|
20925
21158
|
lastOnlineTeammates = void 0;
|
|
20926
21159
|
lastOkAtLocal = void 0;
|
|
21160
|
+
clearSweepRecord();
|
|
20927
21161
|
}
|
|
20928
21162
|
function synchronizeDaemonCredential() {
|
|
20929
21163
|
const token = resolveAuthCredential()?.token;
|
|
@@ -20931,6 +21165,7 @@ function synchronizeDaemonCredential() {
|
|
|
20931
21165
|
if (credentialKey !== activeCredentialKey) {
|
|
20932
21166
|
activeCredentialKey = credentialKey;
|
|
20933
21167
|
purgePrincipalScopedState();
|
|
21168
|
+
persistHealth();
|
|
20934
21169
|
}
|
|
20935
21170
|
return resolveDaemonPrincipal(token);
|
|
20936
21171
|
}
|
|
@@ -20944,7 +21179,7 @@ function assertCallerPrincipalMatches(caller) {
|
|
|
20944
21179
|
}
|
|
20945
21180
|
function ensureDaemonDirectoryPermissions() {
|
|
20946
21181
|
mkdirSync8(CONFIG_DIR, { recursive: true, mode: CONFIG_DIR_MODE3 });
|
|
20947
|
-
|
|
21182
|
+
chmodSync7(CONFIG_DIR, CONFIG_DIR_MODE3);
|
|
20948
21183
|
}
|
|
20949
21184
|
function persistHealth() {
|
|
20950
21185
|
refreshDaemonHealth(daemonHealth, Date.now());
|
|
@@ -20962,6 +21197,15 @@ function updatePendingHealth() {
|
|
|
20962
21197
|
daemonHealth.ingestion.pendingSampled = pending.sampled;
|
|
20963
21198
|
daemonHealth.ingestion.oldestPendingAt = pending.oldestPendingAt;
|
|
20964
21199
|
daemonHealth.ingestion.strandedCount = pending.strandedCount;
|
|
21200
|
+
return pending;
|
|
21201
|
+
}
|
|
21202
|
+
function recordRetainedBuckets(retained) {
|
|
21203
|
+
daemonHealth.ingestion.lastRetainedBucketCount = retained.length;
|
|
21204
|
+
daemonHealth.ingestion.lastRetainedReasons = retained.length > 0 ? summarizeRetainedBuckets(retained) : void 0;
|
|
21205
|
+
}
|
|
21206
|
+
function clearSweepRecord() {
|
|
21207
|
+
daemonHealth.ingestion.lastFailedDrainAcknowledgedCount = 0;
|
|
21208
|
+
recordRetainedBuckets([]);
|
|
20965
21209
|
}
|
|
20966
21210
|
function enterReauthHold() {
|
|
20967
21211
|
if (reauthHold) {
|
|
@@ -20997,6 +21241,8 @@ function exitReauthHold() {
|
|
|
20997
21241
|
daemonHealth.ingestion.consecutiveFailures = 0;
|
|
20998
21242
|
daemonHealth.heartbeat.lastError = void 0;
|
|
20999
21243
|
daemonHealth.ingestion.lastError = void 0;
|
|
21244
|
+
clearSweepRecord();
|
|
21245
|
+
daemonHealth.heartbeat.lastSuccessAt = void 0;
|
|
21000
21246
|
persistHealth();
|
|
21001
21247
|
process.stderr.write(
|
|
21002
21248
|
"[prim-daemon] re-authentication detected \u2014 resuming heartbeat + ingestion + Decision cache\n"
|
|
@@ -21005,6 +21251,11 @@ function exitReauthHold() {
|
|
|
21005
21251
|
void runIngestionLoop();
|
|
21006
21252
|
void runDecisionDigestLoop();
|
|
21007
21253
|
}
|
|
21254
|
+
function resumeIfReauthenticated() {
|
|
21255
|
+
if (reauthHold && !isSessionEnded() && resolveAuthCredential()) {
|
|
21256
|
+
exitReauthHold();
|
|
21257
|
+
}
|
|
21258
|
+
}
|
|
21008
21259
|
async function takeOwnership() {
|
|
21009
21260
|
const now = Date.now();
|
|
21010
21261
|
const recordedOwner = readDaemonOwner(CONFIG_DIR);
|
|
@@ -21014,7 +21265,7 @@ async function takeOwnership() {
|
|
|
21014
21265
|
(pid) => pid !== void 0 && processIsAlive(pid)
|
|
21015
21266
|
)
|
|
21016
21267
|
);
|
|
21017
|
-
if (
|
|
21268
|
+
if (existsSync9(SOCK_PATH2) || candidatePids.size > 0) {
|
|
21018
21269
|
const snapshot = await daemonRequest(
|
|
21019
21270
|
"status_snapshot",
|
|
21020
21271
|
{},
|
|
@@ -21169,22 +21420,26 @@ function scheduleIngestion(delayMs) {
|
|
|
21169
21420
|
}
|
|
21170
21421
|
async function runIngestionLoop() {
|
|
21171
21422
|
synchronizeDaemonCredential();
|
|
21423
|
+
let pending;
|
|
21172
21424
|
try {
|
|
21173
|
-
updatePendingHealth();
|
|
21425
|
+
pending = updatePendingHealth();
|
|
21174
21426
|
} catch (err) {
|
|
21175
21427
|
const failures = daemonHealth.ingestion.consecutiveFailures + 1;
|
|
21176
21428
|
const delay = ingestionRetryDelayMs(failures);
|
|
21177
21429
|
daemonHealth.ingestion.consecutiveFailures = failures;
|
|
21178
21430
|
daemonHealth.ingestion.lastError = `journal scan failed: ${errorMessage2(err)}`;
|
|
21179
21431
|
daemonHealth.ingestion.nextRetryAt = Date.now() + delay;
|
|
21432
|
+
daemonHealth.ingestion.lastAcknowledgedCount = 0;
|
|
21433
|
+
daemonHealth.ingestion.lastFailedDrainAcknowledgedCount = 0;
|
|
21180
21434
|
persistHealth();
|
|
21181
21435
|
scheduleIngestion(delay);
|
|
21182
21436
|
return;
|
|
21183
21437
|
}
|
|
21184
|
-
if (
|
|
21438
|
+
if (!hasPendingDrainWork(pending)) {
|
|
21185
21439
|
daemonHealth.ingestion.consecutiveFailures = 0;
|
|
21186
21440
|
daemonHealth.ingestion.lastError = void 0;
|
|
21187
21441
|
daemonHealth.ingestion.nextRetryAt = void 0;
|
|
21442
|
+
clearSweepRecord();
|
|
21188
21443
|
persistHealth();
|
|
21189
21444
|
scheduleIngestion(INGESTION_POLL_INTERVAL_MS);
|
|
21190
21445
|
return;
|
|
@@ -21200,8 +21455,10 @@ async function runIngestionLoop() {
|
|
|
21200
21455
|
}
|
|
21201
21456
|
daemonHealth.ingestion.lastSuccessAt = Date.now();
|
|
21202
21457
|
daemonHealth.ingestion.lastAcknowledgedCount = result.flushed;
|
|
21458
|
+
daemonHealth.ingestion.lastFailedDrainAcknowledgedCount = 0;
|
|
21203
21459
|
daemonHealth.ingestion.consecutiveFailures = 0;
|
|
21204
21460
|
daemonHealth.ingestion.lastError = void 0;
|
|
21461
|
+
recordRetainedBuckets(result.retained ?? []);
|
|
21205
21462
|
updatePendingHealth();
|
|
21206
21463
|
persistHealth();
|
|
21207
21464
|
scheduleIngestion(INGESTION_POLL_INTERVAL_MS);
|
|
@@ -21209,6 +21466,8 @@ async function runIngestionLoop() {
|
|
|
21209
21466
|
const failures = daemonHealth.ingestion.consecutiveFailures + 1;
|
|
21210
21467
|
const delay = ingestionRetryDelayMs(failures);
|
|
21211
21468
|
daemonHealth.ingestion.lastAcknowledgedCount = err instanceof FlushError ? err.flushed : 0;
|
|
21469
|
+
daemonHealth.ingestion.lastFailedDrainAcknowledgedCount = err instanceof FlushError ? err.failedDrainFlushed : 0;
|
|
21470
|
+
if (err instanceof FlushError) recordRetainedBuckets(err.retained);
|
|
21212
21471
|
daemonHealth.ingestion.consecutiveFailures = failures;
|
|
21213
21472
|
daemonHealth.ingestion.lastError = errorMessage2(err);
|
|
21214
21473
|
daemonHealth.ingestion.nextRetryAt = Date.now() + delay;
|
|
@@ -21267,6 +21526,7 @@ async function proxyGet(params, allowedPrefix, caller) {
|
|
|
21267
21526
|
return result;
|
|
21268
21527
|
}
|
|
21269
21528
|
function handleStatusSnapshot(params, caller, enforcePrincipal = false) {
|
|
21529
|
+
resumeIfReauthenticated();
|
|
21270
21530
|
const daemonPrincipal = synchronizeDaemonCredential();
|
|
21271
21531
|
const wasHealthy = daemonHealth.healthy;
|
|
21272
21532
|
const heartbeatWasHealthy = daemonHealth.heartbeat.healthy;
|
|
@@ -21543,7 +21803,7 @@ function startSocketServer() {
|
|
|
21543
21803
|
});
|
|
21544
21804
|
socketServer.listen(SOCK_PATH2, () => {
|
|
21545
21805
|
try {
|
|
21546
|
-
|
|
21806
|
+
chmodSync7(SOCK_PATH2, SOCKET_MODE);
|
|
21547
21807
|
} catch (err) {
|
|
21548
21808
|
shutdown(EXIT_CRASH, `[prim-daemon] failed to secure socket: ${errorMessage2(err)}`);
|
|
21549
21809
|
return;
|
|
@@ -21570,9 +21830,7 @@ function startTimers() {
|
|
|
21570
21830
|
}
|
|
21571
21831
|
async function runTokenCheckLoop() {
|
|
21572
21832
|
if (reauthHold) {
|
|
21573
|
-
|
|
21574
|
-
exitReauthHold();
|
|
21575
|
-
}
|
|
21833
|
+
resumeIfReauthenticated();
|
|
21576
21834
|
} else {
|
|
21577
21835
|
await ensureTokenFresh();
|
|
21578
21836
|
synchronizeDaemonCredential();
|