@indigoai-us/hq-cloud 5.48.4 → 6.0.1
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/cli/index.d.ts +2 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/reindex.d.ts +23 -0
- package/dist/cli/reindex.d.ts.map +1 -0
- package/dist/cli/reindex.js +45 -0
- package/dist/cli/reindex.js.map +1 -0
- package/dist/cli/reindex.test.d.ts +11 -0
- package/dist/cli/reindex.test.d.ts.map +1 -0
- package/dist/cli/{master-sync.test.js → reindex.test.js} +15 -15
- package/dist/cli/reindex.test.js.map +1 -0
- package/dist/cli/rescue.d.ts.map +1 -1
- package/dist/cli/rescue.js +16 -1
- package/dist/cli/rescue.js.map +1 -1
- package/dist/cli/rescue.reindex.test.d.ts +2 -0
- package/dist/cli/rescue.reindex.test.d.ts.map +1 -0
- package/dist/cli/rescue.reindex.test.js +41 -0
- package/dist/cli/rescue.reindex.test.js.map +1 -0
- package/dist/cli/share.d.ts.map +1 -1
- package/dist/cli/share.js +21 -1
- package/dist/cli/share.js.map +1 -1
- package/dist/cli/share.test.js +108 -2
- package/dist/cli/share.test.js.map +1 -1
- package/dist/cli/sync.d.ts +9 -0
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +17 -0
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +20 -0
- package/dist/cli/sync.test.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/{master-sync.sh → reindex.sh} +10 -10
- package/scripts/replace-rescue.sh +20 -20
- package/src/cli/index.ts +3 -3
- package/src/cli/{master-sync.test.ts → reindex.test.ts} +14 -14
- package/src/cli/reindex.ts +57 -0
- package/src/cli/rescue.reindex.test.ts +46 -0
- package/src/cli/rescue.ts +15 -1
- package/src/cli/share.test.ts +138 -2
- package/src/cli/share.ts +23 -1
- package/src/cli/sync.test.ts +23 -0
- package/src/cli/sync.ts +27 -0
- package/src/index.ts +3 -3
- package/dist/cli/master-sync.d.ts +0 -22
- package/dist/cli/master-sync.d.ts.map +0 -1
- package/dist/cli/master-sync.js +0 -44
- package/dist/cli/master-sync.js.map +0 -1
- package/dist/cli/master-sync.test.d.ts +0 -11
- package/dist/cli/master-sync.test.d.ts.map +0 -1
- package/dist/cli/master-sync.test.js.map +0 -1
- package/src/cli/master-sync.ts +0 -56
package/dist/cli/share.test.js
CHANGED
|
@@ -898,6 +898,112 @@ describe("share", () => {
|
|
|
898
898
|
const journal = JSON.parse(fs.readFileSync(journalPath, "utf-8"));
|
|
899
899
|
expect(journal.files["dangling-link.md"]).toBeDefined();
|
|
900
900
|
});
|
|
901
|
+
it("personal-vault override: `owned-only` is coerced to `currency-gated` so a direction:'down' " +
|
|
902
|
+
"entry whose local file is gone DOES tombstone in the vault (the May-27 .drift-* leak class)", async () => {
|
|
903
|
+
// The personal vault is a single-human-many-machines target. The
|
|
904
|
+
// owned-only "I won't tombstone peer content" rule has no peer to
|
|
905
|
+
// protect against, so it traps legitimate local deletes as permanent
|
|
906
|
+
// vault litter — exactly what the May-27 `personal/.obsidian/*.drift-*`
|
|
907
|
+
// pair did on this EC2 (journal direction "down", local rm, push
|
|
908
|
+
// silently swallowed the delete). Coercing to currency-gated keeps the
|
|
909
|
+
// only safety intent that still applies (HEAD-verify etag matches the
|
|
910
|
+
// journal before tombstone) without the multi-user premise.
|
|
911
|
+
const stateDirPersonal = fs.mkdtempSync(path.join(os.tmpdir(), "hq-state-prs-"));
|
|
912
|
+
process.env.HQ_STATE_DIR = stateDirPersonal;
|
|
913
|
+
try {
|
|
914
|
+
// Personal-vault syncRoot is `hqRoot` itself in personalMode, so the
|
|
915
|
+
// missing file's absolute path resolves to <hqRoot>/<key>.
|
|
916
|
+
// No on-disk file = local missing. Journal records a prior pull.
|
|
917
|
+
const journalPath = path.join(stateDirPersonal, "sync-journal.__hq_personal_vault__.json");
|
|
918
|
+
fs.writeFileSync(journalPath, JSON.stringify({
|
|
919
|
+
version: "1",
|
|
920
|
+
lastSync: new Date().toISOString(),
|
|
921
|
+
files: {
|
|
922
|
+
"personal/scripts/run-project.sh.drift-1779863862-42519": {
|
|
923
|
+
hash: "personal-vault-drift-hash",
|
|
924
|
+
size: 1054,
|
|
925
|
+
syncedAt: new Date(Date.now() - 86400000).toISOString(),
|
|
926
|
+
direction: "down",
|
|
927
|
+
remoteEtag: "personal-vault-drift-etag",
|
|
928
|
+
},
|
|
929
|
+
},
|
|
930
|
+
}));
|
|
931
|
+
// Currency-gated HEADs the candidate; return an etag matching the
|
|
932
|
+
// journal so the entry resolves as "remote still current → safe to
|
|
933
|
+
// tombstone."
|
|
934
|
+
vi.mocked(headRemoteFile).mockResolvedValueOnce({
|
|
935
|
+
lastModified: new Date(),
|
|
936
|
+
etag: '"personal-vault-drift-etag"',
|
|
937
|
+
size: 1054,
|
|
938
|
+
});
|
|
939
|
+
const personalCtx = makeEntityContext({
|
|
940
|
+
uid: "prs_01HASSAANTESTUSER",
|
|
941
|
+
slug: "__hq_personal_vault__",
|
|
942
|
+
bucketName: "hq-vault-prs-01HASSAANTESTUSER",
|
|
943
|
+
});
|
|
944
|
+
const result = await share({
|
|
945
|
+
paths: [tmpDir],
|
|
946
|
+
entityContext: personalCtx,
|
|
947
|
+
hqRoot: tmpDir,
|
|
948
|
+
personalMode: true,
|
|
949
|
+
skipUnchanged: true,
|
|
950
|
+
propagateDeletes: true,
|
|
951
|
+
// Caller pinned owned-only. The override MUST take precedence on a
|
|
952
|
+
// personal-vault entity — the whole point of the fix is that owned-
|
|
953
|
+
// only's "don't tombstone peer-uploaded content" rule has no peer
|
|
954
|
+
// to protect against here, so we coerce to currency-gated.
|
|
955
|
+
propagateDeletePolicy: "owned-only",
|
|
956
|
+
});
|
|
957
|
+
expect(result.filesDeleted).toBe(1);
|
|
958
|
+
expect(deleteRemoteFile).toHaveBeenCalledWith(expect.anything(), "personal/scripts/run-project.sh.drift-1779863862-42519");
|
|
959
|
+
const journal = JSON.parse(fs.readFileSync(journalPath, "utf-8"));
|
|
960
|
+
expect(journal.files["personal/scripts/run-project.sh.drift-1779863862-42519"]).toBeUndefined();
|
|
961
|
+
}
|
|
962
|
+
finally {
|
|
963
|
+
fs.rmSync(stateDirPersonal, { recursive: true, force: true });
|
|
964
|
+
delete process.env.HQ_STATE_DIR;
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
it("personal-vault override does NOT apply to company vaults: `owned-only` on cmp_ entity still " +
|
|
968
|
+
"refuses to tombstone a direction:'down' entry (multi-user curation intact)", async () => {
|
|
969
|
+
// Sibling guard for the previous test: the override is scoped strictly
|
|
970
|
+
// to `prs_` entities. Company vaults preserve owned-only's multi-user
|
|
971
|
+
// semantics — a behind machine pulling Alice's upload, then rm'ing it
|
|
972
|
+
// locally, must NOT tombstone Alice's file. Without this guard the
|
|
973
|
+
// override could silently widen its blast radius the next time someone
|
|
974
|
+
// refactors share()'s entity-typing.
|
|
975
|
+
const companyRoot = path.join(tmpDir, "companies", "acme");
|
|
976
|
+
fs.mkdirSync(companyRoot, { recursive: true });
|
|
977
|
+
// No file on disk under companies/acme/peer-uploaded.md → local missing.
|
|
978
|
+
const journalPath = path.join(stateDir, "sync-journal.acme.json");
|
|
979
|
+
fs.writeFileSync(journalPath, JSON.stringify({
|
|
980
|
+
version: "1",
|
|
981
|
+
lastSync: new Date().toISOString(),
|
|
982
|
+
files: {
|
|
983
|
+
"peer-uploaded.md": {
|
|
984
|
+
hash: "alice-hash",
|
|
985
|
+
size: 100,
|
|
986
|
+
syncedAt: new Date(Date.now() - 86400000).toISOString(),
|
|
987
|
+
direction: "down",
|
|
988
|
+
remoteEtag: "alice-etag",
|
|
989
|
+
},
|
|
990
|
+
},
|
|
991
|
+
}));
|
|
992
|
+
const result = await share({
|
|
993
|
+
paths: [companyRoot],
|
|
994
|
+
company: "acme",
|
|
995
|
+
vaultConfig: mockConfig,
|
|
996
|
+
hqRoot: tmpDir,
|
|
997
|
+
skipUnchanged: true,
|
|
998
|
+
propagateDeletes: true,
|
|
999
|
+
// Company-vault entity (cmp_) → owned-only stays in effect.
|
|
1000
|
+
propagateDeletePolicy: "owned-only",
|
|
1001
|
+
});
|
|
1002
|
+
expect(result.filesDeleted).toBe(0);
|
|
1003
|
+
expect(deleteRemoteFile).not.toHaveBeenCalled();
|
|
1004
|
+
const journal = JSON.parse(fs.readFileSync(journalPath, "utf-8"));
|
|
1005
|
+
expect(journal.files["peer-uploaded.md"]).toBeDefined();
|
|
1006
|
+
});
|
|
901
1007
|
it("propagateDeletes: deletes journal-tracked files whose local copy is gone", async () => {
|
|
902
1008
|
const companyRoot = path.join(tmpDir, "companies", "acme");
|
|
903
1009
|
fs.mkdirSync(companyRoot, { recursive: true });
|
|
@@ -1899,7 +2005,7 @@ describe("share", () => {
|
|
|
1899
2005
|
// target's bytes under the link's key while a nested symlink was
|
|
1900
2006
|
// silently dropped from every push. The link topology never survived a
|
|
1901
2007
|
// round trip — fresh-machine pulls landed in a state where overlay
|
|
1902
|
-
// symlinks just didn't exist until
|
|
2008
|
+
// symlinks just didn't exist until reindex.sh recreated them
|
|
1903
2009
|
// locally. The fix detects symlinks via lstat / Dirent.isSymbolicLink
|
|
1904
2010
|
// and routes them to a new uploadSymlink primitive that PUTs a
|
|
1905
2011
|
// zero-byte object with x-amz-meta-hq-symlink-target carrying the
|
|
@@ -1933,7 +2039,7 @@ describe("share", () => {
|
|
|
1933
2039
|
const realPolicy = path.join(policiesDir, "real.md");
|
|
1934
2040
|
fs.writeFileSync(realPolicy, "real content");
|
|
1935
2041
|
const linkPolicy = path.join(policiesDir, "link.md");
|
|
1936
|
-
// Mirrors the
|
|
2042
|
+
// Mirrors the reindex.sh overlay shape: relative target pointing
|
|
1937
2043
|
// to a sibling in the same dir.
|
|
1938
2044
|
fs.symlinkSync("real.md", linkPolicy);
|
|
1939
2045
|
const result = await share({
|