@indigoai-us/hq-cloud 6.18.24 → 6.18.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/outpost-session-host.d.ts +1 -0
- package/dist/bin/outpost-session-host.d.ts.map +1 -1
- package/dist/bin/outpost-session-host.js +3 -2
- package/dist/bin/outpost-session-host.js.map +1 -1
- package/dist/bin/outpost-session-host.test.js +16 -1
- package/dist/bin/outpost-session-host.test.js.map +1 -1
- package/dist/bin/sync-runner-delete-snapshot-mixed.bench.test.js +129 -5
- package/dist/bin/sync-runner-delete-snapshot-mixed.bench.test.js.map +1 -1
- package/dist/bin/sync-runner.d.ts +6 -0
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.js +42 -3
- package/dist/bin/sync-runner.js.map +1 -1
- package/dist/bin/sync-runner.test.js +179 -23
- package/dist/bin/sync-runner.test.js.map +1 -1
- package/dist/hq-root-recovery.d.ts +0 -1
- package/dist/hq-root-recovery.d.ts.map +1 -1
- package/dist/hq-root-recovery.js +1 -1
- package/dist/hq-root-recovery.js.map +1 -1
- package/dist/journal-snapshot-memory.test.js +3 -3
- package/dist/outposts/client.d.ts.map +1 -1
- package/dist/outposts/client.js +4 -1
- package/dist/outposts/client.js.map +1 -1
- package/dist/outposts/client.test.js +53 -0
- package/dist/outposts/client.test.js.map +1 -1
- package/dist/outposts/transport.d.ts.map +1 -1
- package/dist/outposts/transport.js +18 -1
- package/dist/outposts/transport.js.map +1 -1
- package/dist/outposts/transport.test.js +89 -0
- package/dist/outposts/transport.test.js.map +1 -1
- package/dist/sync/push-walk-matchers.bench.test.js +92 -42
- package/dist/sync/push-walk-matchers.bench.test.js.map +1 -1
- package/dist/telemetry.d.ts +0 -14
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +2 -2
- package/dist/telemetry.js.map +1 -1
- package/dist/watch-parent.test.js +23 -0
- package/dist/watch-parent.test.js.map +1 -1
- package/package.json +2 -2
|
@@ -12,7 +12,7 @@ import * as os from "os";
|
|
|
12
12
|
import * as path from "path";
|
|
13
13
|
import * as v8 from "node:v8";
|
|
14
14
|
import * as vm from "node:vm";
|
|
15
|
-
import { monitorEventLoopDelay } from "node:perf_hooks";
|
|
15
|
+
import { monitorEventLoopDelay, PerformanceObserver } from "node:perf_hooks";
|
|
16
16
|
import { runRunner, parseArgs, loopPassDeps, runRunnerWithLoop as runRunnerWithOwnerLease, resolveDeletePolicy, resolveSkipPersonal, resolveSkipCompanies, selectObjectIOFactory, routeChangeToTarget, buildTargetedPushArgv, resolvePullScope, readPinnedPrefixes, SESSIONS_SCOPE_PREFIX, defaultCollectTelemetry, AUTH_REQUIRED_PASS_EXIT, conventionalWatchSignalExitCode, DEFAULT_WATCH_RUNNER_SHUTDOWN_TIMEOUT_MS, resolveWatchRunnerShutdownTimeoutMs, WATCH_RUNNER_SHUTDOWN_TIMEOUT_ENV, } from "./sync-runner.js";
|
|
17
17
|
import { SESSION_LOGS_PUSH_ONLY_PREFIX } from "../sync/pull-scope.js";
|
|
18
18
|
import { WATCH_RUNNER_ALREADY_OWNED_EXIT, WATCH_OWNER_LEASE_LOST_EXIT, } from "../lib/exit-codes.js";
|
|
@@ -23,7 +23,7 @@ import { POST_SYNC_FLAG_KEYS, } from "./sync-runner-post-sync-flags.js";
|
|
|
23
23
|
import { FakeClock, TreeWatcher, } from "../watcher.js";
|
|
24
24
|
import { adaptivePollMs, advanceMonotonicDeadline, DEFAULT_EVENT_BATCH_LIMIT, DEFAULT_FAST_LANE_MAX_BYTES, DEFAULT_FAST_LANE_MAX_FILE_BYTES, DEFAULT_FAST_LANE_MAX_FILES, DEFAULT_FULL_RECONCILE_MS, EVENT_BATCH_LIMIT_ENV, FAST_LANE_MAX_BYTES_ENV, FAST_LANE_MAX_FILES_ENV, FULL_RECONCILE_MS_ENV, isFastLocalBatch, isFastReceiverBatch, partitionLocalBatch, journalEntriesForWatcherDelete, resolveEventBatchLimit, resolveFullReconcileMs, resolveWatchPollMs, CHORE_STARVATION_CAP_MS, CooperativePassScheduler, REALTIME_ADMISSION_DEADLINE_MS, REALTIME_DRAIN_BURST_MS, REALTIME_WORKER_OCCUPANCY_CAP, RealtimeAdmissionTimeoutError, setRealtimeAdmissionDeadlineMsForTesting, setRealtimeDrainBurstMsForTesting, normalizeCoordinatorPaths, shouldQuarantineWatcherDeleteIntents, shouldUseAreaStorage, WATCHER_REDISCOVERY_RETRY_MS, WATCHER_REDISCOVERY_RETRY_MAX_MS, EVENT_SYNC_STARTUP_SHUTDOWN_WAIT_MS, } from "./sync-runner-watch-loop.js";
|
|
25
25
|
import { FullPassCapController, resetFullPassCapLogForTest, } from "../sync/full-pass-caps.js";
|
|
26
|
-
import { AreaLedgerMigration, JournalStore, openJournalStateStoreForMigration, PERSONAL_VAULT_JOURNAL_SLUG, readJournal, readJournalScoped, writeJournal, } from "../journal.js";
|
|
26
|
+
import { AreaLedgerMigration, JournalStore, openJournalStateStoreForMigration, PERSONAL_VAULT_JOURNAL_SLUG, readJournal, readJournalForPass, readJournalScoped, readJournalScopedAsync, writeJournal, } from "../journal.js";
|
|
27
27
|
import { AreaLedger } from "../sync/area-ledger.js";
|
|
28
28
|
import { AreaResolver } from "../sync/area-resolver.js";
|
|
29
29
|
import { AREA_LEGACY_MIRROR_LINGER_MS } from "../sync/area-ledger-migration.js";
|
|
@@ -285,6 +285,29 @@ describe("argv parsing", () => {
|
|
|
285
285
|
exitWithParent: false,
|
|
286
286
|
});
|
|
287
287
|
});
|
|
288
|
+
it("validates --watch-parent-pid requirements and values", () => {
|
|
289
|
+
expect(parseArgs(["--companies", "--watch-parent-pid", "42"])).toEqual({
|
|
290
|
+
error: "--watch-parent-pid requires --watch",
|
|
291
|
+
});
|
|
292
|
+
expect(parseArgs(["--companies", "--watch", "--watch-parent-pid", "42"])).toEqual({
|
|
293
|
+
error: "--watch-parent-pid requires --exit-with-parent",
|
|
294
|
+
});
|
|
295
|
+
expect(parseArgs(["--companies", "--watch", "--exit-with-parent", "--watch-parent-pid"])).toEqual({
|
|
296
|
+
error: "--watch-parent-pid requires a positive integer",
|
|
297
|
+
});
|
|
298
|
+
for (const value of ["0", "-1", "1.5", "nope"]) {
|
|
299
|
+
expect(parseArgs(["--companies", "--watch", "--exit-with-parent", "--watch-parent-pid", value])).toEqual({
|
|
300
|
+
error: "--watch-parent-pid requires a positive integer",
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
expect(parseArgs(["--companies", "--watch", "--exit-with-parent", "--watch-parent-pid", "42"])).toMatchObject({ watch: true, exitWithParent: true, watchParentPid: 42 });
|
|
304
|
+
expect(parseArgs(["--companies", "--watch", "--exit-with-parent", "--watch-parent-pid", "1"])).toEqual({
|
|
305
|
+
error: "--watch-parent-pid cannot be PID 1 or the runner process",
|
|
306
|
+
});
|
|
307
|
+
expect(parseArgs(["--companies", "--watch", "--exit-with-parent", "--watch-parent-pid", String(process.pid)])).toEqual({
|
|
308
|
+
error: "--watch-parent-pid cannot be PID 1 or the runner process",
|
|
309
|
+
});
|
|
310
|
+
});
|
|
288
311
|
it("rejects invalid --on-conflict value", async () => {
|
|
289
312
|
const deps = makeDeps();
|
|
290
313
|
const code = await runRunner(["--companies", "--on-conflict", "nuke"], deps);
|
|
@@ -5052,9 +5075,67 @@ describe("runRunnerWithLoop — event-push wiring", () => {
|
|
|
5052
5075
|
});
|
|
5053
5076
|
expect(await loop).toBe(0);
|
|
5054
5077
|
expect(startParentExitMonitor).toHaveBeenCalledOnce();
|
|
5078
|
+
expect(startParentExitMonitor.mock.calls[0]?.slice(1)).toEqual([
|
|
5079
|
+
process.ppid,
|
|
5080
|
+
process.ppid,
|
|
5081
|
+
]);
|
|
5055
5082
|
expect(release).toHaveBeenCalledOnce();
|
|
5056
5083
|
expect(stopMonitor).toHaveBeenCalledOnce();
|
|
5057
5084
|
});
|
|
5085
|
+
it("passes the explicit launcher PID to the exit monitor", async () => {
|
|
5086
|
+
const release = vi.fn(async () => { });
|
|
5087
|
+
const startParentExitMonitor = vi.fn((onParentExit) => {
|
|
5088
|
+
queueMicrotask(onParentExit);
|
|
5089
|
+
return { stop: vi.fn() };
|
|
5090
|
+
});
|
|
5091
|
+
const code = await runRunnerWithLoop(["--companies", "--watch", "--exit-with-parent", "--watch-parent-pid", "9001"], {
|
|
5092
|
+
parentExists: () => true,
|
|
5093
|
+
startParentExitMonitor,
|
|
5094
|
+
runPass: async () => 0,
|
|
5095
|
+
sleep: () => new Promise(() => { }),
|
|
5096
|
+
onShutdownSignal: () => () => { },
|
|
5097
|
+
acquireWatchOwnerLease: () => ({
|
|
5098
|
+
status: "acquired",
|
|
5099
|
+
lease: {
|
|
5100
|
+
statusPath: "test://watch-owner",
|
|
5101
|
+
lost: new Promise(() => { }),
|
|
5102
|
+
release,
|
|
5103
|
+
},
|
|
5104
|
+
}),
|
|
5105
|
+
});
|
|
5106
|
+
expect(code).toBe(0);
|
|
5107
|
+
expect(startParentExitMonitor.mock.calls[0]?.slice(1)).toEqual([9001, process.ppid]);
|
|
5108
|
+
expect(release).toHaveBeenCalledOnce();
|
|
5109
|
+
});
|
|
5110
|
+
it("shuts down before the first pass when the explicit launcher is already gone", async () => {
|
|
5111
|
+
const release = vi.fn(async () => { });
|
|
5112
|
+
const runPass = vi.fn(async () => 0);
|
|
5113
|
+
const stderr = makeWriter();
|
|
5114
|
+
const parentExists = vi.fn(() => false);
|
|
5115
|
+
const startParentExitMonitor = vi.fn(() => ({ stop: vi.fn() }));
|
|
5116
|
+
const code = await runRunnerWithLoop(["--companies", "--watch", "--exit-with-parent", "--watch-parent-pid", "9001"], {
|
|
5117
|
+
stderr,
|
|
5118
|
+
parentExists,
|
|
5119
|
+
startParentExitMonitor,
|
|
5120
|
+
runPass,
|
|
5121
|
+
sleep: () => new Promise(() => { }),
|
|
5122
|
+
onShutdownSignal: () => () => { },
|
|
5123
|
+
acquireWatchOwnerLease: () => ({
|
|
5124
|
+
status: "acquired",
|
|
5125
|
+
lease: {
|
|
5126
|
+
statusPath: "test://watch-owner",
|
|
5127
|
+
lost: new Promise(() => { }),
|
|
5128
|
+
release,
|
|
5129
|
+
},
|
|
5130
|
+
}),
|
|
5131
|
+
});
|
|
5132
|
+
expect(code).toBe(0);
|
|
5133
|
+
expect(parentExists).toHaveBeenCalledWith(9001);
|
|
5134
|
+
expect(stderr.raw()).toContain("--watch-parent-pid 9001 is not running");
|
|
5135
|
+
expect(runPass).not.toHaveBeenCalled();
|
|
5136
|
+
expect(startParentExitMonitor).not.toHaveBeenCalled();
|
|
5137
|
+
expect(release).toHaveBeenCalledOnce();
|
|
5138
|
+
});
|
|
5058
5139
|
it("bounds parent-exit shutdown when an active pass ignores cancellation", async () => {
|
|
5059
5140
|
const previousTimeout = process.env[WATCH_RUNNER_SHUTDOWN_TIMEOUT_ENV];
|
|
5060
5141
|
process.env[WATCH_RUNNER_SHUTDOWN_TIMEOUT_ENV] = "50";
|
|
@@ -7656,22 +7737,43 @@ describe("runRunnerWithLoop — event-push wiring", () => {
|
|
|
7656
7737
|
const watcher = makeBatchWatcherStub();
|
|
7657
7738
|
let shutdown = () => { };
|
|
7658
7739
|
let committedAt;
|
|
7740
|
+
let activeCommitSample;
|
|
7741
|
+
let scopedCommitSequence = 0;
|
|
7742
|
+
const commitPhases = [];
|
|
7659
7743
|
const runPass = vi.fn(async (argv) => {
|
|
7660
7744
|
if (!argv.includes("--scope-path")) {
|
|
7661
7745
|
// Production opens the packed journal during the initial full pass,
|
|
7662
7746
|
// before the watcher accepts realtime work. That recovered projection
|
|
7663
7747
|
// is the one a scoped commit must reuse.
|
|
7664
|
-
|
|
7748
|
+
readJournalForPass(PERSONAL_VAULT_JOURNAL_SLUG);
|
|
7665
7749
|
}
|
|
7666
7750
|
if (argv.includes("--scope-path") && argv.includes("push")) {
|
|
7667
|
-
const
|
|
7751
|
+
const passStartedAt = performance.now();
|
|
7752
|
+
const scopedReadStartedAt = performance.now();
|
|
7753
|
+
const scoped = await readJournalScopedAsync(PERSONAL_VAULT_JOURNAL_SLUG, targetPaths);
|
|
7754
|
+
const scopedReadMs = performance.now() - scopedReadStartedAt;
|
|
7755
|
+
const mutationStartedAt = performance.now();
|
|
7668
7756
|
const target = targetPaths[0];
|
|
7669
7757
|
scoped.files[target] = {
|
|
7670
7758
|
...scoped.files[target],
|
|
7671
|
-
syncedAt: "2026-09-16T21:38:18.000Z",
|
|
7759
|
+
syncedAt: new Date(Date.parse("2026-09-16T21:38:18.000Z") + scopedCommitSequence++).toISOString(),
|
|
7672
7760
|
};
|
|
7761
|
+
const entryMutationMs = performance.now() - mutationStartedAt;
|
|
7762
|
+
const journalWriteStartedAt = performance.now();
|
|
7673
7763
|
writeJournal(PERSONAL_VAULT_JOURNAL_SLUG, scoped);
|
|
7764
|
+
const journalWriteMs = performance.now() - journalWriteStartedAt;
|
|
7674
7765
|
committedAt = performance.now();
|
|
7766
|
+
if (activeCommitSample) {
|
|
7767
|
+
commitPhases.push({
|
|
7768
|
+
sample: activeCommitSample.sample,
|
|
7769
|
+
dispatchToPassMs: passStartedAt - activeCommitSample.submittedAt,
|
|
7770
|
+
scopedReadMs,
|
|
7771
|
+
entryMutationMs,
|
|
7772
|
+
journalWriteMs,
|
|
7773
|
+
passRemainderMs: committedAt - passStartedAt - scopedReadMs - entryMutationMs - journalWriteMs,
|
|
7774
|
+
submitToCommitMs: committedAt - activeCommitSample.submittedAt,
|
|
7775
|
+
});
|
|
7776
|
+
}
|
|
7675
7777
|
}
|
|
7676
7778
|
return completedPassOutcome();
|
|
7677
7779
|
});
|
|
@@ -7684,21 +7786,19 @@ describe("runRunnerWithLoop — event-push wiring", () => {
|
|
|
7684
7786
|
return () => { };
|
|
7685
7787
|
},
|
|
7686
7788
|
});
|
|
7789
|
+
const gcActivity = [];
|
|
7790
|
+
const gcObserver = new PerformanceObserver((list) => {
|
|
7791
|
+
for (const entry of list.getEntries()) {
|
|
7792
|
+
const detail = entry;
|
|
7793
|
+
gcActivity.push({ kind: detail.detail?.kind ?? null, duration: entry.duration, startTime: entry.startTime });
|
|
7794
|
+
}
|
|
7795
|
+
});
|
|
7796
|
+
gcObserver.observe({ entryTypes: ["gc"] });
|
|
7687
7797
|
try {
|
|
7688
7798
|
await flushLoopMicrotasks(30);
|
|
7689
7799
|
runPass.mockClear();
|
|
7690
7800
|
resetStateStoreSnapshotRecoveryCountForTest();
|
|
7691
|
-
const eventLoopDelay = monitorEventLoopDelay({ resolution: 10 });
|
|
7692
|
-
eventLoopDelay.enable();
|
|
7693
7801
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
7694
|
-
const submittedAt = performance.now();
|
|
7695
|
-
let timerDelayMs;
|
|
7696
|
-
const eventLoopTurn = new Promise((resolve) => {
|
|
7697
|
-
setTimeout(() => {
|
|
7698
|
-
timerDelayMs = performance.now() - submittedAt;
|
|
7699
|
-
resolve();
|
|
7700
|
-
}, 0);
|
|
7701
|
-
});
|
|
7702
7802
|
const batch = {
|
|
7703
7803
|
paths: new Map(targetPaths.map((relativePath) => [path.join(hqRoot, relativePath), relativePath])),
|
|
7704
7804
|
changes: new Map(targetPaths.map((relativePath) => [
|
|
@@ -7706,17 +7806,73 @@ describe("runRunnerWithLoop — event-push wiring", () => {
|
|
|
7706
7806
|
{ kind: "change" },
|
|
7707
7807
|
])),
|
|
7708
7808
|
};
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7809
|
+
// Measure each commit independently so one long event-loop block cannot
|
|
7810
|
+
// disappear inside an aggregate statistic.
|
|
7811
|
+
const loopDelayMaxMs = [];
|
|
7812
|
+
const commitDurations = [];
|
|
7813
|
+
for (let sample = 0; sample < 5; sample += 1) {
|
|
7814
|
+
const loopDelay = monitorEventLoopDelay({ resolution: 10 });
|
|
7815
|
+
loopDelay.enable();
|
|
7816
|
+
try {
|
|
7817
|
+
// Preserve the old pre-commit window as a separately reported idle
|
|
7818
|
+
// baseline. The guarded histogram starts only after this window.
|
|
7819
|
+
const warmupStart = performance.now();
|
|
7820
|
+
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
7821
|
+
const warmupEnd = performance.now();
|
|
7822
|
+
for (const entry of gcObserver.takeRecords()) {
|
|
7823
|
+
const detail = entry;
|
|
7824
|
+
gcActivity.push({ kind: detail.detail?.kind ?? null, duration: entry.duration, startTime: entry.startTime });
|
|
7825
|
+
}
|
|
7826
|
+
const warmupMaxDelayMs = loopDelay.max / 1e6;
|
|
7827
|
+
const warmupGc = gcActivity
|
|
7828
|
+
.filter((entry) => entry.startTime >= warmupStart && entry.startTime <= warmupEnd)
|
|
7829
|
+
.map((entry) => ({ kind: entry.kind, duration: Number(entry.duration.toFixed(2)), startTime: Number(entry.startTime.toFixed(2)) }));
|
|
7830
|
+
loopDelay.reset();
|
|
7831
|
+
committedAt = undefined;
|
|
7832
|
+
const windowStart = performance.now();
|
|
7833
|
+
activeCommitSample = { sample: sample + 1, submittedAt: windowStart };
|
|
7834
|
+
const watcherCallbackStartedAt = performance.now();
|
|
7835
|
+
watcher.emit(targetPaths[0], batch);
|
|
7836
|
+
const watcherCallbackSyncMs = performance.now() - watcherCallbackStartedAt;
|
|
7837
|
+
await vi.waitFor(() => expect(committedAt).toBeDefined(), { timeout: 30_000, interval: 10 });
|
|
7838
|
+
commitDurations.push(committedAt - windowStart);
|
|
7839
|
+
// Give the histogram's next 10 ms sample time to observe a stall that
|
|
7840
|
+
// ended in the commit callback before reading this window's maximum.
|
|
7841
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
7842
|
+
const windowEnd = performance.now();
|
|
7843
|
+
for (const entry of gcObserver.takeRecords()) {
|
|
7844
|
+
const detail = entry;
|
|
7845
|
+
gcActivity.push({ kind: detail.detail?.kind ?? null, duration: entry.duration, startTime: entry.startTime });
|
|
7846
|
+
}
|
|
7847
|
+
const maxDelayMs = loopDelay.max / 1e6;
|
|
7848
|
+
loopDelayMaxMs.push(maxDelayMs);
|
|
7849
|
+
const phases = commitPhases.find((phase) => phase.sample === sample + 1);
|
|
7850
|
+
const sampleGc = gcActivity
|
|
7851
|
+
.filter((entry) => entry.startTime >= windowStart && entry.startTime <= windowEnd)
|
|
7852
|
+
.map((entry) => ({ kind: entry.kind, duration: Number(entry.duration.toFixed(2)), startTime: Number(entry.startTime.toFixed(2)) }));
|
|
7853
|
+
process.stdout.write(`realtime-commit-sample ${JSON.stringify({
|
|
7854
|
+
sample: sample + 1,
|
|
7855
|
+
warmupMaxDelayMs: Number(warmupMaxDelayMs.toFixed(1)),
|
|
7856
|
+
warmupGc,
|
|
7857
|
+
maxDelayMs: Number(maxDelayMs.toFixed(1)),
|
|
7858
|
+
windowStart: Number(windowStart.toFixed(2)),
|
|
7859
|
+
windowEnd: Number(windowEnd.toFixed(2)),
|
|
7860
|
+
phases: phases ? { ...phases, watcherCallbackSyncMs } : null,
|
|
7861
|
+
gc: sampleGc,
|
|
7862
|
+
})}\n`);
|
|
7863
|
+
activeCommitSample = undefined;
|
|
7864
|
+
}
|
|
7865
|
+
finally {
|
|
7866
|
+
loopDelay.disable();
|
|
7867
|
+
}
|
|
7868
|
+
}
|
|
7869
|
+
expect(Math.max(...commitDurations)).toBeLessThan(1_000);
|
|
7870
|
+
expect(Math.max(...loopDelayMaxMs)).toBeLessThan(100);
|
|
7716
7871
|
expect(getStateStoreSnapshotRecoveryCountForTest()).toBe(0);
|
|
7717
|
-
expect(runPass.mock.calls.filter(([argv]) => argv.includes("--scope-path"))).toHaveLength(
|
|
7872
|
+
expect(runPass.mock.calls.filter(([argv]) => argv.includes("--scope-path"))).toHaveLength(5);
|
|
7718
7873
|
}
|
|
7719
7874
|
finally {
|
|
7875
|
+
gcObserver.disconnect();
|
|
7720
7876
|
shutdown();
|
|
7721
7877
|
await loop;
|
|
7722
7878
|
if (previousCoordinator === undefined)
|