@indigoai-us/hq-cloud 5.1.0 → 5.1.8

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 (100) hide show
  1. package/dist/bin/sync-runner.d.ts +111 -0
  2. package/dist/bin/sync-runner.d.ts.map +1 -0
  3. package/dist/bin/sync-runner.js +285 -0
  4. package/dist/bin/sync-runner.js.map +1 -0
  5. package/dist/bin/sync-runner.test.d.ts +10 -0
  6. package/dist/bin/sync-runner.test.d.ts.map +1 -0
  7. package/dist/bin/sync-runner.test.js +492 -0
  8. package/dist/bin/sync-runner.test.js.map +1 -0
  9. package/dist/cli/index.d.ts +1 -1
  10. package/dist/cli/index.d.ts.map +1 -1
  11. package/dist/cli/share.js +2 -2
  12. package/dist/cli/share.js.map +1 -1
  13. package/dist/cli/share.test.js +9 -1
  14. package/dist/cli/share.test.js.map +1 -1
  15. package/dist/cli/sync.d.ts +28 -0
  16. package/dist/cli/sync.d.ts.map +1 -1
  17. package/dist/cli/sync.js +33 -10
  18. package/dist/cli/sync.js.map +1 -1
  19. package/dist/cli/sync.test.js +15 -4
  20. package/dist/cli/sync.test.js.map +1 -1
  21. package/dist/cognito-auth.d.ts.map +1 -1
  22. package/dist/cognito-auth.js +19 -1
  23. package/dist/cognito-auth.js.map +1 -1
  24. package/dist/cognito-auth.test.d.ts +9 -0
  25. package/dist/cognito-auth.test.d.ts.map +1 -0
  26. package/dist/cognito-auth.test.js +113 -0
  27. package/dist/cognito-auth.test.js.map +1 -0
  28. package/dist/context.d.ts.map +1 -1
  29. package/dist/context.js +1 -0
  30. package/dist/context.js.map +1 -1
  31. package/dist/daemon-worker.d.ts +6 -1
  32. package/dist/daemon-worker.d.ts.map +1 -1
  33. package/dist/daemon-worker.js +12 -16
  34. package/dist/daemon-worker.js.map +1 -1
  35. package/dist/daemon.d.ts +2 -0
  36. package/dist/daemon.d.ts.map +1 -1
  37. package/dist/daemon.js +2 -0
  38. package/dist/daemon.js.map +1 -1
  39. package/dist/ignore.d.ts +13 -2
  40. package/dist/ignore.d.ts.map +1 -1
  41. package/dist/ignore.js +69 -12
  42. package/dist/ignore.js.map +1 -1
  43. package/dist/index.d.ts +24 -28
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +19 -134
  46. package/dist/index.js.map +1 -1
  47. package/dist/journal.d.ts +20 -4
  48. package/dist/journal.d.ts.map +1 -1
  49. package/dist/journal.js +45 -8
  50. package/dist/journal.js.map +1 -1
  51. package/dist/journal.test.d.ts +9 -0
  52. package/dist/journal.test.d.ts.map +1 -0
  53. package/dist/journal.test.js +114 -0
  54. package/dist/journal.test.js.map +1 -0
  55. package/dist/s3.d.ts +18 -6
  56. package/dist/s3.d.ts.map +1 -1
  57. package/dist/s3.js +57 -56
  58. package/dist/s3.js.map +1 -1
  59. package/dist/types.d.ts +34 -0
  60. package/dist/types.d.ts.map +1 -1
  61. package/dist/vault-client.d.ts +16 -0
  62. package/dist/vault-client.d.ts.map +1 -1
  63. package/dist/vault-client.js +19 -0
  64. package/dist/vault-client.js.map +1 -1
  65. package/dist/vault-client.test.js +25 -0
  66. package/dist/vault-client.test.js.map +1 -1
  67. package/dist/watcher.d.ts +7 -1
  68. package/dist/watcher.d.ts.map +1 -1
  69. package/dist/watcher.js +11 -5
  70. package/dist/watcher.js.map +1 -1
  71. package/package.json +15 -3
  72. package/src/bin/sync-runner.test.ts +617 -0
  73. package/src/bin/sync-runner.ts +390 -0
  74. package/src/cli/accept.ts +97 -0
  75. package/src/cli/conflict.ts +119 -0
  76. package/src/cli/index.ts +25 -0
  77. package/src/cli/invite.test.ts +247 -0
  78. package/src/cli/invite.ts +180 -0
  79. package/src/cli/promote.ts +123 -0
  80. package/src/cli/share.test.ts +155 -0
  81. package/src/cli/share.ts +212 -0
  82. package/src/cli/sync.test.ts +225 -0
  83. package/src/cli/sync.ts +225 -0
  84. package/src/cognito-auth.test.ts +156 -0
  85. package/src/cognito-auth.ts +18 -1
  86. package/src/context.test.ts +202 -0
  87. package/src/context.ts +178 -0
  88. package/src/daemon-worker.ts +13 -19
  89. package/src/daemon.ts +2 -0
  90. package/src/ignore.ts +76 -12
  91. package/src/index.ts +93 -165
  92. package/src/journal.test.ts +146 -0
  93. package/src/journal.ts +53 -11
  94. package/src/s3.ts +76 -66
  95. package/src/types.ts +37 -0
  96. package/src/vault-client.test.ts +390 -0
  97. package/src/vault-client.ts +400 -0
  98. package/src/watcher.ts +12 -5
  99. package/test/invite-flow.integration.test.ts +244 -0
  100. package/test/share-sync.integration.test.ts +210 -0
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Integration test: share/sync lifecycle (VLT-5 US-003).
3
+ *
4
+ * Exercises the full share/sync cycle against a real dev-stage company entity
5
+ * using real STS vending (VLT-3). Requires:
6
+ * - VAULT_API_URL env var (vault-service API endpoint)
7
+ * - VAULT_AUTH_TOKEN env var (Cognito JWT for an active member)
8
+ * - VAULT_TEST_COMPANY env var (company slug with an active entity + bucket)
9
+ *
10
+ * Run: pnpm test:e2e
11
+ *
12
+ * This test:
13
+ * 1. M1 shares file A
14
+ * 2. M2 (same creds, different local dir) syncs → receives A
15
+ * 3. M2 shares file B
16
+ * 4. M1 syncs → receives B
17
+ * 5. M1 edits A locally + M2 pushes newer A → M1 syncs with --on-conflict=keep
18
+ *
19
+ * Cleanup: all shared files are deleted from S3 on teardown.
20
+ */
21
+
22
+ import { describe, it, expect, afterAll } from "vitest";
23
+ import * as fs from "fs";
24
+ import * as path from "path";
25
+ import * as os from "os";
26
+ import {
27
+ resolveEntityContext,
28
+ clearContextCache,
29
+ deleteRemoteFile,
30
+ listRemoteFiles,
31
+ } from "../src/index.js";
32
+ import { share } from "../src/cli/share.js";
33
+ import { sync } from "../src/cli/sync.js";
34
+ import type { VaultServiceConfig, EntityContext } from "../src/types.js";
35
+
36
+ // Skip if env vars not set
37
+ const VAULT_API_URL = process.env.VAULT_API_URL;
38
+ const VAULT_AUTH_TOKEN = process.env.VAULT_AUTH_TOKEN;
39
+ const VAULT_TEST_COMPANY = process.env.VAULT_TEST_COMPANY;
40
+
41
+ const canRun = VAULT_API_URL && VAULT_AUTH_TOKEN && VAULT_TEST_COMPANY;
42
+
43
+ const TEST_PREFIX = `__integration-test-${Date.now()}`;
44
+
45
+ describe.skipIf(!canRun)("share-sync integration", () => {
46
+ let vaultConfig: VaultServiceConfig;
47
+ let m1Root: string;
48
+ let m2Root: string;
49
+ let ctx: EntityContext;
50
+
51
+ // Track files to clean up
52
+ const sharedKeys: string[] = [];
53
+
54
+ // Create two simulated machine roots
55
+ const setup = async () => {
56
+ vaultConfig = {
57
+ apiUrl: VAULT_API_URL!,
58
+ authToken: VAULT_AUTH_TOKEN!,
59
+ region: "us-east-1",
60
+ };
61
+
62
+ m1Root = fs.mkdtempSync(path.join(os.tmpdir(), "hq-integ-m1-"));
63
+ m2Root = fs.mkdtempSync(path.join(os.tmpdir(), "hq-integ-m2-"));
64
+
65
+ // Resolve entity context to verify connectivity
66
+ clearContextCache();
67
+ ctx = await resolveEntityContext(VAULT_TEST_COMPANY!, vaultConfig);
68
+
69
+ console.log(`Integration test: entity=${ctx.uid}, bucket=${ctx.bucketName}`);
70
+ console.log(`M1 root: ${m1Root}`);
71
+ console.log(`M2 root: ${m2Root}`);
72
+ };
73
+
74
+ afterAll(async () => {
75
+ // Cleanup: delete all test files from S3
76
+ try {
77
+ if (ctx) {
78
+ for (const key of sharedKeys) {
79
+ try {
80
+ await deleteRemoteFile(ctx, key);
81
+ } catch {
82
+ // Best-effort cleanup
83
+ }
84
+ }
85
+
86
+ // Also scan for any orphaned test files
87
+ const remoteFiles = await listRemoteFiles(ctx, TEST_PREFIX);
88
+ for (const file of remoteFiles) {
89
+ try {
90
+ await deleteRemoteFile(ctx, file.key);
91
+ } catch {
92
+ // Best-effort
93
+ }
94
+ }
95
+ }
96
+ } finally {
97
+ // Clean up temp directories
98
+ if (m1Root) fs.rmSync(m1Root, { recursive: true, force: true });
99
+ if (m2Root) fs.rmSync(m2Root, { recursive: true, force: true });
100
+ }
101
+ });
102
+
103
+ it("completes the full share/sync lifecycle", async () => {
104
+ await setup();
105
+
106
+ // --- Step 1: M1 shares file A ---
107
+ const fileA = `${TEST_PREFIX}/docs/handoff.md`;
108
+ const fileALocal = path.join(m1Root, fileA);
109
+ fs.mkdirSync(path.dirname(fileALocal), { recursive: true });
110
+ fs.writeFileSync(fileALocal, "# Handoff from M1\n\nDiscovery notes here.");
111
+ sharedKeys.push(fileA);
112
+
113
+ const shareResult1 = await share({
114
+ paths: [fileALocal],
115
+ company: VAULT_TEST_COMPANY!,
116
+ message: "Initial handoff notes",
117
+ vaultConfig,
118
+ hqRoot: m1Root,
119
+ });
120
+
121
+ expect(shareResult1.filesUploaded).toBe(1);
122
+ expect(shareResult1.aborted).toBe(false);
123
+ console.log("Step 1 PASS: M1 shared file A");
124
+
125
+ // --- Step 2: M2 syncs → receives A ---
126
+ clearContextCache();
127
+
128
+ const syncResult1 = await sync({
129
+ company: VAULT_TEST_COMPANY!,
130
+ vaultConfig,
131
+ hqRoot: m2Root,
132
+ });
133
+
134
+ expect(syncResult1.filesDownloaded).toBeGreaterThanOrEqual(1);
135
+ expect(syncResult1.aborted).toBe(false);
136
+
137
+ const m2FileA = path.join(m2Root, fileA);
138
+ expect(fs.existsSync(m2FileA)).toBe(true);
139
+ expect(fs.readFileSync(m2FileA, "utf-8")).toContain("Handoff from M1");
140
+ console.log("Step 2 PASS: M2 synced and received file A");
141
+
142
+ // --- Step 3: M2 shares file B ---
143
+ const fileB = `${TEST_PREFIX}/knowledge/findings.md`;
144
+ const fileBLocal = path.join(m2Root, fileB);
145
+ fs.mkdirSync(path.dirname(fileBLocal), { recursive: true });
146
+ fs.writeFileSync(fileBLocal, "# Findings from M2\n\nNew discovery.");
147
+ sharedKeys.push(fileB);
148
+
149
+ const shareResult2 = await share({
150
+ paths: [fileBLocal],
151
+ company: VAULT_TEST_COMPANY!,
152
+ message: "Research findings",
153
+ vaultConfig,
154
+ hqRoot: m2Root,
155
+ });
156
+
157
+ expect(shareResult2.filesUploaded).toBe(1);
158
+ console.log("Step 3 PASS: M2 shared file B");
159
+
160
+ // --- Step 4: M1 syncs → receives B ---
161
+ clearContextCache();
162
+
163
+ const syncResult2 = await sync({
164
+ company: VAULT_TEST_COMPANY!,
165
+ vaultConfig,
166
+ hqRoot: m1Root,
167
+ });
168
+
169
+ expect(syncResult2.filesDownloaded).toBeGreaterThanOrEqual(1);
170
+
171
+ const m1FileB = path.join(m1Root, fileB);
172
+ expect(fs.existsSync(m1FileB)).toBe(true);
173
+ expect(fs.readFileSync(m1FileB, "utf-8")).toContain("Findings from M2");
174
+ console.log("Step 4 PASS: M1 synced and received file B");
175
+
176
+ // --- Step 5: Conflict — M1 edits A locally, M2 pushes newer A, M1 syncs with keep ---
177
+ // M1 edits locally
178
+ fs.writeFileSync(fileALocal, "# Handoff from M1\n\nEDITED LOCALLY by M1.");
179
+
180
+ // M2 pushes newer version
181
+ fs.writeFileSync(m2FileA, "# Handoff from M1\n\nUPDATED by M2.");
182
+ clearContextCache();
183
+
184
+ await share({
185
+ paths: [m2FileA],
186
+ company: VAULT_TEST_COMPANY!,
187
+ onConflict: "overwrite",
188
+ vaultConfig,
189
+ hqRoot: m2Root,
190
+ });
191
+
192
+ // M1 syncs with --on-conflict=keep
193
+ clearContextCache();
194
+
195
+ await sync({
196
+ company: VAULT_TEST_COMPANY!,
197
+ onConflict: "keep",
198
+ vaultConfig,
199
+ hqRoot: m1Root,
200
+ });
201
+
202
+ // M1's local version should be preserved
203
+ const m1Content = fs.readFileSync(fileALocal, "utf-8");
204
+ expect(m1Content).toContain("EDITED LOCALLY by M1");
205
+ expect(m1Content).not.toContain("UPDATED by M2");
206
+ console.log("Step 5 PASS: M1 kept local version on conflict");
207
+
208
+ console.log("\n=== All 5 lifecycle steps passed ===");
209
+ }, 60_000); // 60s timeout for network ops
210
+ });