@indigoai-us/hq-cloud 6.14.35 → 6.14.37
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/sync-runner-company.d.ts.map +1 -1
- package/dist/bin/sync-runner-company.js +20 -3
- package/dist/bin/sync-runner-company.js.map +1 -1
- package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
- package/dist/bin/sync-runner-watch-loop.js +13 -3
- package/dist/bin/sync-runner-watch-loop.js.map +1 -1
- package/dist/bin/sync-runner.d.ts +5 -0
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.js.map +1 -1
- package/dist/bin/sync-runner.test.js +87 -1
- package/dist/bin/sync-runner.test.js.map +1 -1
- package/dist/cli/share.d.ts +179 -1
- package/dist/cli/share.d.ts.map +1 -1
- package/dist/cli/share.js +504 -26
- package/dist/cli/share.js.map +1 -1
- package/dist/cli/share.test.js +639 -2
- package/dist/cli/share.test.js.map +1 -1
- package/dist/cli/sync.d.ts +24 -0
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +45 -3
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +34 -0
- package/dist/cli/sync.test.js.map +1 -1
- package/package.json +1 -1
- package/src/bin/sync-runner-company.ts +19 -3
- package/src/bin/sync-runner-watch-loop.ts +13 -3
- package/src/bin/sync-runner.test.ts +105 -2
- package/src/bin/sync-runner.ts +4 -0
- package/src/cli/share.test.ts +794 -2
- package/src/cli/share.ts +624 -26
- package/src/cli/sync.test.ts +42 -0
- package/src/cli/sync.ts +78 -3
|
@@ -92,6 +92,8 @@ function defaultShareResult(overrides = {}) {
|
|
|
92
92
|
filesExcludedByPolicy: 0,
|
|
93
93
|
filesExcludedByScope: 0,
|
|
94
94
|
filesExcludedByIgnore: 0,
|
|
95
|
+
unreachablePaths: [],
|
|
96
|
+
linkedSubtreesNotShipped: [],
|
|
95
97
|
conflictPaths: [],
|
|
96
98
|
aborted: false,
|
|
97
99
|
...overrides,
|
|
@@ -6189,6 +6191,9 @@ describe("watcher delete intents without pre-seeded authorization", () => {
|
|
|
6189
6191
|
});
|
|
6190
6192
|
let initialPullGated = options.gateInitialPull === true;
|
|
6191
6193
|
const runPass = vi.fn(async (argv) => {
|
|
6194
|
+
// Lets a test hold a pass mid-flight and act while the loop is awaiting
|
|
6195
|
+
// it, which is where producer-side state that outlives a pass is decided.
|
|
6196
|
+
await options.onPass?.(argv);
|
|
6192
6197
|
const direction = argv[argv.indexOf("--direction") + 1];
|
|
6193
6198
|
if (direction === "push") {
|
|
6194
6199
|
const journal = readJournal("indigo");
|
|
@@ -6259,7 +6264,8 @@ describe("watcher delete intents without pre-seeded authorization", () => {
|
|
|
6259
6264
|
return 0;
|
|
6260
6265
|
});
|
|
6261
6266
|
const loop = runRunnerWithLoop([
|
|
6262
|
-
"--companies", "--watch", "--event-push",
|
|
6267
|
+
"--companies", "--watch", "--event-push",
|
|
6268
|
+
"--direction", options.direction ?? "both",
|
|
6263
6269
|
"--hq-root", hqRoot, "--poll-remote-ms", "60000",
|
|
6264
6270
|
], {
|
|
6265
6271
|
runPass,
|
|
@@ -6660,6 +6666,86 @@ describe("watcher delete intents without pre-seeded authorization", () => {
|
|
|
6660
6666
|
expect(h.remote.size).toBe(11);
|
|
6661
6667
|
await stopHarness(h);
|
|
6662
6668
|
});
|
|
6669
|
+
// A pass is awaited, so a bulk removal observed while one is in flight
|
|
6670
|
+
// registers its quarantine root only after that pass's batch was taken — its
|
|
6671
|
+
// own snapshots ride the NEXT batch. Expiring the whole set once the pass
|
|
6672
|
+
// returns would drop the root before `prepareWatcherChanges` ever sees that
|
|
6673
|
+
// batch, re-stamping the revoked descendant intents and handing the engine
|
|
6674
|
+
// the bulk delete the quarantine refused (remotely deleting the subtree under
|
|
6675
|
+
// HQ_SYNC_DELETE_POLICY=all).
|
|
6676
|
+
it("a bulk removal observed during an in-flight pass keeps its quarantine", async () => {
|
|
6677
|
+
const keys = Array.from({ length: 12 }, (_, i) => `knowledge/moved/f-${i}.md`);
|
|
6678
|
+
let releaseGatedPush = () => { };
|
|
6679
|
+
const gatedPush = new Promise((resolve) => {
|
|
6680
|
+
releaseGatedPush = resolve;
|
|
6681
|
+
});
|
|
6682
|
+
let gateArmed = false;
|
|
6683
|
+
let gateReached = false;
|
|
6684
|
+
// Push-only: a pull would restore the quarantined subtree (the documented
|
|
6685
|
+
// recovery for an intent-less disappearance) before the batch carrying the
|
|
6686
|
+
// episode is prepared, masking whether the refusal itself survived.
|
|
6687
|
+
const h = startDeleteHarness([...keys, "knowledge/other.md"], {
|
|
6688
|
+
direction: "push",
|
|
6689
|
+
onPass: async (_argv) => {
|
|
6690
|
+
if (!gateArmed)
|
|
6691
|
+
return;
|
|
6692
|
+
gateArmed = false;
|
|
6693
|
+
gateReached = true;
|
|
6694
|
+
await gatedPush;
|
|
6695
|
+
},
|
|
6696
|
+
});
|
|
6697
|
+
await settle();
|
|
6698
|
+
const intentCount = () => Object.values(readJournal("indigo").files).filter((entry) => entry.localDeleteIntent).length;
|
|
6699
|
+
// An unrelated edit drains as a scoped push, which then blocks on the gate
|
|
6700
|
+
// with its batch already taken from the pending buffer.
|
|
6701
|
+
gateArmed = true;
|
|
6702
|
+
const otherPath = path.join(h.companyRoot, "knowledge/other.md");
|
|
6703
|
+
fs.writeFileSync(otherPath, "edited");
|
|
6704
|
+
h.watcher.emit("companies/indigo/knowledge/other.md", {
|
|
6705
|
+
paths: new Map([[otherPath, "companies/indigo/knowledge/other.md"]]),
|
|
6706
|
+
});
|
|
6707
|
+
h.tick();
|
|
6708
|
+
await settle();
|
|
6709
|
+
expect(gateReached).toBe(true);
|
|
6710
|
+
// The whole subtree disappears while that pass is still in flight.
|
|
6711
|
+
const childChanges = new Map();
|
|
6712
|
+
for (const key of keys) {
|
|
6713
|
+
const absolutePath = path.join(h.companyRoot, key);
|
|
6714
|
+
fs.unlinkSync(absolutePath);
|
|
6715
|
+
childChanges.set(absolutePath, {
|
|
6716
|
+
kind: "unlink",
|
|
6717
|
+
deleteSnapshots: h.capture()(`companies/indigo/${key}`, "unlink"),
|
|
6718
|
+
});
|
|
6719
|
+
}
|
|
6720
|
+
const subtree = path.join(h.companyRoot, "knowledge/moved");
|
|
6721
|
+
fs.rmSync(subtree, { recursive: true, force: true });
|
|
6722
|
+
const dirSnapshots = h.capture()("companies/indigo/knowledge/moved", "unlinkDir");
|
|
6723
|
+
expect(dirSnapshots).toHaveLength(0);
|
|
6724
|
+
expect(intentCount()).toBe(0);
|
|
6725
|
+
h.watcher.emit("companies/indigo/knowledge/moved", {
|
|
6726
|
+
paths: new Map([
|
|
6727
|
+
...keys.map((key) => [
|
|
6728
|
+
path.join(h.companyRoot, key),
|
|
6729
|
+
`companies/indigo/${key}`,
|
|
6730
|
+
]),
|
|
6731
|
+
[subtree, "companies/indigo/knowledge/moved"],
|
|
6732
|
+
]),
|
|
6733
|
+
changes: new Map([
|
|
6734
|
+
...childChanges,
|
|
6735
|
+
[subtree, { kind: "unlinkDir", deleteSnapshots: dirSnapshots }],
|
|
6736
|
+
]),
|
|
6737
|
+
});
|
|
6738
|
+
// Completing the overlapped pass must not expire the refusal recorded
|
|
6739
|
+
// during it: the next drain prepares the batch that carries the episode.
|
|
6740
|
+
releaseGatedPush();
|
|
6741
|
+
await settle();
|
|
6742
|
+
h.tick();
|
|
6743
|
+
await settle();
|
|
6744
|
+
expect(intentCount()).toBe(0);
|
|
6745
|
+
for (const key of keys)
|
|
6746
|
+
expect(h.remote.has(key)).toBe(true);
|
|
6747
|
+
await stopHarness(h);
|
|
6748
|
+
});
|
|
6663
6749
|
it("HQ_SYNC_DELETE_BULK_OVERRIDE=1 lets a whole-shard unlinkDir propagate", async () => {
|
|
6664
6750
|
process.env.HQ_SYNC_DELETE_BULK_OVERRIDE = "1";
|
|
6665
6751
|
try {
|