@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.
- package/dist/bin/sync-runner.d.ts +111 -0
- package/dist/bin/sync-runner.d.ts.map +1 -0
- package/dist/bin/sync-runner.js +285 -0
- package/dist/bin/sync-runner.js.map +1 -0
- package/dist/bin/sync-runner.test.d.ts +10 -0
- package/dist/bin/sync-runner.test.d.ts.map +1 -0
- package/dist/bin/sync-runner.test.js +492 -0
- package/dist/bin/sync-runner.test.js.map +1 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/share.js +2 -2
- package/dist/cli/share.js.map +1 -1
- package/dist/cli/share.test.js +9 -1
- package/dist/cli/share.test.js.map +1 -1
- package/dist/cli/sync.d.ts +28 -0
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +33 -10
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +15 -4
- package/dist/cli/sync.test.js.map +1 -1
- package/dist/cognito-auth.d.ts.map +1 -1
- package/dist/cognito-auth.js +19 -1
- package/dist/cognito-auth.js.map +1 -1
- package/dist/cognito-auth.test.d.ts +9 -0
- package/dist/cognito-auth.test.d.ts.map +1 -0
- package/dist/cognito-auth.test.js +113 -0
- package/dist/cognito-auth.test.js.map +1 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -0
- package/dist/context.js.map +1 -1
- package/dist/daemon-worker.d.ts +6 -1
- package/dist/daemon-worker.d.ts.map +1 -1
- package/dist/daemon-worker.js +12 -16
- package/dist/daemon-worker.js.map +1 -1
- package/dist/daemon.d.ts +2 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +2 -0
- package/dist/daemon.js.map +1 -1
- package/dist/ignore.d.ts +13 -2
- package/dist/ignore.d.ts.map +1 -1
- package/dist/ignore.js +69 -12
- package/dist/ignore.js.map +1 -1
- package/dist/index.d.ts +24 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -134
- package/dist/index.js.map +1 -1
- package/dist/journal.d.ts +20 -4
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +45 -8
- package/dist/journal.js.map +1 -1
- package/dist/journal.test.d.ts +9 -0
- package/dist/journal.test.d.ts.map +1 -0
- package/dist/journal.test.js +114 -0
- package/dist/journal.test.js.map +1 -0
- package/dist/s3.d.ts +18 -6
- package/dist/s3.d.ts.map +1 -1
- package/dist/s3.js +57 -56
- package/dist/s3.js.map +1 -1
- package/dist/types.d.ts +34 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/vault-client.d.ts +16 -0
- package/dist/vault-client.d.ts.map +1 -1
- package/dist/vault-client.js +19 -0
- package/dist/vault-client.js.map +1 -1
- package/dist/vault-client.test.js +25 -0
- package/dist/vault-client.test.js.map +1 -1
- package/dist/watcher.d.ts +7 -1
- package/dist/watcher.d.ts.map +1 -1
- package/dist/watcher.js +11 -5
- package/dist/watcher.js.map +1 -1
- package/package.json +15 -3
- package/src/bin/sync-runner.test.ts +617 -0
- package/src/bin/sync-runner.ts +390 -0
- package/src/cli/accept.ts +97 -0
- package/src/cli/conflict.ts +119 -0
- package/src/cli/index.ts +25 -0
- package/src/cli/invite.test.ts +247 -0
- package/src/cli/invite.ts +180 -0
- package/src/cli/promote.ts +123 -0
- package/src/cli/share.test.ts +155 -0
- package/src/cli/share.ts +212 -0
- package/src/cli/sync.test.ts +225 -0
- package/src/cli/sync.ts +225 -0
- package/src/cognito-auth.test.ts +156 -0
- package/src/cognito-auth.ts +18 -1
- package/src/context.test.ts +202 -0
- package/src/context.ts +178 -0
- package/src/daemon-worker.ts +13 -19
- package/src/daemon.ts +2 -0
- package/src/ignore.ts +76 -12
- package/src/index.ts +93 -165
- package/src/journal.test.ts +146 -0
- package/src/journal.ts +53 -11
- package/src/s3.ts +76 -66
- package/src/types.ts +37 -0
- package/src/vault-client.test.ts +390 -0
- package/src/vault-client.ts +400 -0
- package/src/watcher.ts +12 -5
- package/test/invite-flow.integration.test.ts +244 -0
- 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
|
+
});
|