@indigoai-us/hq-cloud 6.15.80 → 6.15.82

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.
Files changed (125) hide show
  1. package/dist/backup-prune.d.ts +18 -0
  2. package/dist/backup-prune.d.ts.map +1 -1
  3. package/dist/backup-prune.js +20 -0
  4. package/dist/backup-prune.js.map +1 -1
  5. package/dist/backup-prune.test.js +30 -1
  6. package/dist/backup-prune.test.js.map +1 -1
  7. package/dist/bin/backup-prune-runner.d.ts +4 -1
  8. package/dist/bin/backup-prune-runner.d.ts.map +1 -1
  9. package/dist/bin/backup-prune-runner.js +61 -38
  10. package/dist/bin/backup-prune-runner.js.map +1 -1
  11. package/dist/bin/backup-prune-runner.test.d.ts +2 -0
  12. package/dist/bin/backup-prune-runner.test.d.ts.map +1 -0
  13. package/dist/bin/backup-prune-runner.test.js +22 -0
  14. package/dist/bin/backup-prune-runner.test.js.map +1 -0
  15. package/dist/bin/layout-compatibility.d.ts +12 -0
  16. package/dist/bin/layout-compatibility.d.ts.map +1 -0
  17. package/dist/bin/layout-compatibility.js +50 -0
  18. package/dist/bin/layout-compatibility.js.map +1 -0
  19. package/dist/bin/layout-compatibility.test.d.ts +2 -0
  20. package/dist/bin/layout-compatibility.test.d.ts.map +1 -0
  21. package/dist/bin/layout-compatibility.test.js +87 -0
  22. package/dist/bin/layout-compatibility.test.js.map +1 -0
  23. package/dist/bin/sync-runner-company.d.ts.map +1 -1
  24. package/dist/bin/sync-runner-company.js +15 -0
  25. package/dist/bin/sync-runner-company.js.map +1 -1
  26. package/dist/bin/sync-runner-company.test.js +65 -0
  27. package/dist/bin/sync-runner-company.test.js.map +1 -1
  28. package/dist/bin/sync-runner-events.test.js +20 -0
  29. package/dist/bin/sync-runner-events.test.js.map +1 -1
  30. package/dist/bin/sync-runner-watch-loop.d.ts +9 -0
  31. package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
  32. package/dist/bin/sync-runner-watch-loop.js +369 -21
  33. package/dist/bin/sync-runner-watch-loop.js.map +1 -1
  34. package/dist/bin/sync-runner.d.ts +5 -0
  35. package/dist/bin/sync-runner.d.ts.map +1 -1
  36. package/dist/bin/sync-runner.js +11 -1
  37. package/dist/bin/sync-runner.js.map +1 -1
  38. package/dist/bin/sync-runner.test.js +165 -2
  39. package/dist/bin/sync-runner.test.js.map +1 -1
  40. package/dist/cli/share.d.ts.map +1 -1
  41. package/dist/cli/share.js +29 -5
  42. package/dist/cli/share.js.map +1 -1
  43. package/dist/cli/share.test.js +131 -24
  44. package/dist/cli/share.test.js.map +1 -1
  45. package/dist/cli/sync.d.ts +23 -0
  46. package/dist/cli/sync.d.ts.map +1 -1
  47. package/dist/cli/sync.js +72 -7
  48. package/dist/cli/sync.js.map +1 -1
  49. package/dist/cli/sync.test.js +199 -0
  50. package/dist/cli/sync.test.js.map +1 -1
  51. package/dist/cli/watch-event-push-conflict.test.js +22 -11
  52. package/dist/cli/watch-event-push-conflict.test.js.map +1 -1
  53. package/dist/index.d.ts +2 -2
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +1 -1
  56. package/dist/index.js.map +1 -1
  57. package/dist/journal.d.ts +18 -0
  58. package/dist/journal.d.ts.map +1 -1
  59. package/dist/journal.js +41 -17
  60. package/dist/journal.js.map +1 -1
  61. package/dist/journal.test.js +8 -1
  62. package/dist/journal.test.js.map +1 -1
  63. package/dist/outposts/self-deploy.d.ts.map +1 -1
  64. package/dist/outposts/self-deploy.js +10 -1
  65. package/dist/outposts/self-deploy.js.map +1 -1
  66. package/dist/outposts/self-deploy.test.js +4 -1
  67. package/dist/outposts/self-deploy.test.js.map +1 -1
  68. package/dist/s3.d.ts +62 -0
  69. package/dist/s3.d.ts.map +1 -1
  70. package/dist/s3.js +91 -1
  71. package/dist/s3.js.map +1 -1
  72. package/dist/s3.rename-retry.test.d.ts +2 -0
  73. package/dist/s3.rename-retry.test.d.ts.map +1 -0
  74. package/dist/s3.rename-retry.test.js +153 -0
  75. package/dist/s3.rename-retry.test.js.map +1 -0
  76. package/dist/sync/area-ledger-migration.d.ts +8 -1
  77. package/dist/sync/area-ledger-migration.d.ts.map +1 -1
  78. package/dist/sync/area-ledger-migration.js +23 -0
  79. package/dist/sync/area-ledger-migration.js.map +1 -1
  80. package/dist/sync/area-ledger-migration.test.js +20 -0
  81. package/dist/sync/area-ledger-migration.test.js.map +1 -1
  82. package/dist/sync/area-ledger.d.ts +10 -0
  83. package/dist/sync/area-ledger.d.ts.map +1 -1
  84. package/dist/sync/area-ledger.js +11 -0
  85. package/dist/sync/area-ledger.js.map +1 -1
  86. package/dist/sync/feature-flags.d.ts +29 -0
  87. package/dist/sync/feature-flags.d.ts.map +1 -1
  88. package/dist/sync/feature-flags.js +44 -0
  89. package/dist/sync/feature-flags.js.map +1 -1
  90. package/dist/sync/feature-flags.test.js +38 -1
  91. package/dist/sync/feature-flags.test.js.map +1 -1
  92. package/dist/sync/index.d.ts +6 -0
  93. package/dist/sync/index.d.ts.map +1 -1
  94. package/dist/sync/index.js +3 -0
  95. package/dist/sync/index.js.map +1 -1
  96. package/dist/sync/layout-compatibility.d.ts +17 -0
  97. package/dist/sync/layout-compatibility.d.ts.map +1 -0
  98. package/dist/sync/layout-compatibility.js +29 -0
  99. package/dist/sync/layout-compatibility.js.map +1 -0
  100. package/dist/sync/parallel-sync-coordinator.d.ts.map +1 -1
  101. package/dist/sync/parallel-sync-coordinator.js +0 -3
  102. package/dist/sync/parallel-sync-coordinator.js.map +1 -1
  103. package/dist/sync/parallel-sync-coordinator.test.js +14 -0
  104. package/dist/sync/parallel-sync-coordinator.test.js.map +1 -1
  105. package/dist/sync/per-area-maintenance.d.ts +45 -0
  106. package/dist/sync/per-area-maintenance.d.ts.map +1 -0
  107. package/dist/sync/per-area-maintenance.js +131 -0
  108. package/dist/sync/per-area-maintenance.js.map +1 -0
  109. package/dist/sync/per-area-maintenance.test.d.ts +2 -0
  110. package/dist/sync/per-area-maintenance.test.d.ts.map +1 -0
  111. package/dist/sync/per-area-maintenance.test.js +100 -0
  112. package/dist/sync/per-area-maintenance.test.js.map +1 -0
  113. package/dist/sync/realtime-telemetry.d.ts +49 -0
  114. package/dist/sync/realtime-telemetry.d.ts.map +1 -0
  115. package/dist/sync/realtime-telemetry.js +101 -0
  116. package/dist/sync/realtime-telemetry.js.map +1 -0
  117. package/dist/sync/realtime-telemetry.test.d.ts +2 -0
  118. package/dist/sync/realtime-telemetry.test.d.ts.map +1 -0
  119. package/dist/sync/realtime-telemetry.test.js +64 -0
  120. package/dist/sync/realtime-telemetry.test.js.map +1 -0
  121. package/dist/sync/state-store.d.ts +10 -0
  122. package/dist/sync/state-store.d.ts.map +1 -1
  123. package/dist/sync/state-store.js +18 -0
  124. package/dist/sync/state-store.js.map +1 -1
  125. package/package.json +3 -2
@@ -17,30 +17,40 @@ vi.mock("fs", async (importOriginal) => {
17
17
  // response shape is `{ ETag: '"<hex>"' }`. uploadSymlink is the symlink-
18
18
  // preserving sibling that puts a zero-byte object with target metadata
19
19
  // instead of dereferencing the link.
20
- vi.mock("../s3.js", () => ({
21
- toPosixKey: (key) => key.split("\\").join("/"),
22
- uploadFile: vi.fn().mockResolvedValue({ etag: '"upload-etag"' }),
23
- uploadSymlink: vi.fn().mockResolvedValue({ etag: '"upload-symlink-etag"' }),
24
- downloadFile: vi.fn().mockResolvedValue(undefined),
25
- listRemoteFiles: vi.fn().mockResolvedValue([]),
26
- deleteRemoteFile: vi.fn().mockResolvedValue(undefined),
27
- headRemoteFile: vi.fn().mockResolvedValue(null),
28
- readVerifiedRemoteSymlinkTarget: vi.fn().mockResolvedValue(null),
29
- primeObjectTransport: vi.fn().mockResolvedValue(undefined),
30
- primeUploads: vi.fn().mockResolvedValue(undefined),
31
- // Real exports share.ts now imports for the HQ-DESKTOP-54 privilege skip.
32
- WINDOWS_SYMLINK_PRIVILEGE_REMEDY: "Enable Windows Developer Mode (Settings > Privacy & security > For developers) or run HQ sync once from an elevated terminal, then sync again.",
33
- WindowsSymlinkPrivilegeError: class WindowsSymlinkPrivilegeError extends Error {
34
- target;
35
- code;
36
- constructor(target, code) {
37
- super(`symlink skipped: creating a file symlink to ${target} requires a Windows privilege`);
38
- this.name = "WindowsSymlinkPrivilegeError";
39
- this.target = target;
40
- this.code = code;
41
- }
42
- },
43
- }));
20
+ vi.mock("../s3.js", async (importOriginal) => {
21
+ const actual = await importOriginal();
22
+ return {
23
+ toPosixKey: (key) => key.split("\\").join("/"),
24
+ uploadFile: vi.fn().mockResolvedValue({ etag: '"upload-etag"' }),
25
+ uploadSymlink: vi.fn().mockResolvedValue({ etag: '"upload-symlink-etag"' }),
26
+ downloadFile: vi.fn().mockResolvedValue(undefined),
27
+ listRemoteFiles: vi.fn().mockResolvedValue([]),
28
+ deleteRemoteFile: vi.fn().mockResolvedValue(undefined),
29
+ headRemoteFile: vi.fn().mockResolvedValue(null),
30
+ readVerifiedRemoteSymlinkTarget: vi.fn().mockResolvedValue(null),
31
+ primeObjectTransport: vi.fn().mockResolvedValue(undefined),
32
+ primeUploads: vi.fn().mockResolvedValue(undefined),
33
+ // Real exports share.ts now imports for the HQ-DESKTOP-54 privilege skip.
34
+ WINDOWS_SYMLINK_PRIVILEGE_REMEDY: "Enable Windows Developer Mode (Settings > Privacy & security > For developers) or run HQ sync once from an elevated terminal, then sync again.",
35
+ WindowsSymlinkPrivilegeError: class WindowsSymlinkPrivilegeError extends Error {
36
+ target;
37
+ code;
38
+ constructor(target, code) {
39
+ super(`symlink skipped: creating a file symlink to ${target} requires a Windows privilege`);
40
+ this.name = "WindowsSymlinkPrivilegeError";
41
+ this.target = target;
42
+ this.code = code;
43
+ }
44
+ },
45
+ // HQ-DESKTOP-54 (second path): the real win32 rename guard, typed error, and
46
+ // remedy that share.ts now imports. Real so `instanceof` matches the error the
47
+ // real helper throws, and the POSIX pass-through keeps existing push tests
48
+ // byte-identical.
49
+ renameOrRaiseWindowsBlocked: actual.renameOrRaiseWindowsBlocked,
50
+ WindowsRenameBlockedError: actual.WindowsRenameBlockedError,
51
+ WINDOWS_RENAME_BLOCKED_REMEDY: actual.WINDOWS_RENAME_BLOCKED_REMEDY,
52
+ };
53
+ });
44
54
  // Mock readline so the interactive-conflict serialization test can observe
45
55
  // prompt lifecycle (createInterface + close) without ever touching stdin.
46
56
  // Default impl never resolves; conflict-prompt tests below override per-test
@@ -1986,6 +1996,103 @@ describe("share", () => {
1986
1996
  expect(result.conflictPaths).toEqual(["v7b-priv.txt"]);
1987
1997
  expect(fs.readFileSync(testFile, "utf-8")).toBe("local-collision-content");
1988
1998
  });
1999
+ it("HQ-DESKTOP-54 (second path): a blocked push conflict-keep move-aside rename is a skip, not an error, and does not touch the local body", async () => {
2000
+ // Push keep moves the local body aside before adopting the remote. On a
2001
+ // Windows host where a program is holding that file open, the move-aside
2002
+ // rename EPERMs; s3 raises the typed WindowsRenameBlockedError
2003
+ // immediately (no retry). The push leg must downgrade it to the
2004
+ // deliberate skip (never type:"error") so the company stays complete and the
2005
+ // runner exits 0 — mirroring the symlink-privilege skip above.
2006
+ const companyRoot = path.join(tmpDir, "companies", "acme");
2007
+ fs.mkdirSync(companyRoot, { recursive: true });
2008
+ const testFile = path.join(companyRoot, "v7b-rename.txt");
2009
+ fs.writeFileSync(testFile, "local-collision-content");
2010
+ vi.mocked(headRemoteFile).mockResolvedValueOnce({
2011
+ lastModified: new Date(),
2012
+ etag: '"remote-version-etag"',
2013
+ size: 40,
2014
+ });
2015
+ // The reconciled probe fetches the (divergent) remote so a conflict is
2016
+ // detected; the move-aside rename below is what fails.
2017
+ vi.mocked(downloadFile).mockImplementation(async (_ctx, _key, dest) => {
2018
+ fs.writeFileSync(dest, "peer remote content");
2019
+ return undefined;
2020
+ });
2021
+ const realRenameSync = fs.renameSync;
2022
+ const renameSpy = vi.spyOn(fs, "renameSync").mockImplementation((from, to) => {
2023
+ if (String(from) === testFile && String(to).includes(".conflict-")) {
2024
+ const err = new Error("EPERM: operation not permitted, rename");
2025
+ err.code = "EPERM";
2026
+ throw err;
2027
+ }
2028
+ return realRenameSync(from, to);
2029
+ });
2030
+ const realPlatform = process.platform;
2031
+ Object.defineProperty(process, "platform", { value: "win32", configurable: true });
2032
+ const events = [];
2033
+ let result;
2034
+ try {
2035
+ result = await share({
2036
+ paths: [testFile],
2037
+ company: "acme",
2038
+ vaultConfig: mockConfig,
2039
+ hqRoot: tmpDir,
2040
+ onConflict: "keep",
2041
+ onEvent: (e) => events.push(e),
2042
+ });
2043
+ }
2044
+ finally {
2045
+ Object.defineProperty(process, "platform", { value: realPlatform, configurable: true });
2046
+ renameSpy.mockRestore();
2047
+ }
2048
+ expect(events).toContainEqual(expect.objectContaining({ type: "skip-rename-blocked", path: "v7b-rename.txt" }));
2049
+ expect(events.some((e) => e.type === "error")).toBe(false);
2050
+ // Nothing was moved or overwritten: the local body is intact and no upload
2051
+ // happened, so the conflict simply re-fires next pass.
2052
+ expect(fs.readFileSync(testFile, "utf-8")).toBe("local-collision-content");
2053
+ expect(result.filesUploaded).toBe(0);
2054
+ });
2055
+ it("HQ-DESKTOP-54 (second path): the push default console logger renders the rename-blocked skip and its remedy (a plain `hq sync` is not silent)", async () => {
2056
+ const companyRoot = path.join(tmpDir, "companies", "acme");
2057
+ fs.mkdirSync(companyRoot, { recursive: true });
2058
+ const testFile = path.join(companyRoot, "v7b-render.txt");
2059
+ fs.writeFileSync(testFile, "local-collision-content");
2060
+ vi.mocked(headRemoteFile).mockResolvedValueOnce({
2061
+ lastModified: new Date(),
2062
+ etag: '"remote-version-etag"',
2063
+ size: 40,
2064
+ });
2065
+ vi.mocked(downloadFile).mockImplementation(async (_ctx, _key, dest) => {
2066
+ fs.writeFileSync(dest, "peer remote content");
2067
+ return undefined;
2068
+ });
2069
+ const realRenameSync = fs.renameSync;
2070
+ const renameSpy = vi.spyOn(fs, "renameSync").mockImplementation((from, to) => {
2071
+ if (String(from) === testFile && String(to).includes(".conflict-")) {
2072
+ const err = new Error("EPERM: operation not permitted, rename");
2073
+ err.code = "EPERM";
2074
+ throw err;
2075
+ }
2076
+ return realRenameSync(from, to);
2077
+ });
2078
+ const realPlatform = process.platform;
2079
+ Object.defineProperty(process, "platform", { value: "win32", configurable: true });
2080
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => { });
2081
+ const log = vi.spyOn(console, "log").mockImplementation(() => { });
2082
+ let rendered = [];
2083
+ try {
2084
+ // No onEvent → the push defaultConsoleLogger renders the skip.
2085
+ await share({ paths: [testFile], company: "acme", vaultConfig: mockConfig, hqRoot: tmpDir, onConflict: "keep" });
2086
+ rendered = warn.mock.calls.map((c) => String(c[0]));
2087
+ }
2088
+ finally {
2089
+ Object.defineProperty(process, "platform", { value: realPlatform, configurable: true });
2090
+ warn.mockRestore();
2091
+ log.mockRestore();
2092
+ renameSpy.mockRestore();
2093
+ }
2094
+ expect(rendered.some((line) => /v7b-render\.txt skipped/.test(line) && /holding the file open/i.test(line))).toBe(true);
2095
+ });
1989
2096
  it("fresh-install SSE-KMS etag: byte-identical remote is reconciled without a conflict", async () => {
1990
2097
  const companyRoot = path.join(tmpDir, "companies", "acme");
1991
2098
  fs.mkdirSync(companyRoot, { recursive: true });