@indigoai-us/hq-cloud 5.1.0 → 5.1.9

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 +134 -0
  2. package/dist/bin/sync-runner.d.ts.map +1 -0
  3. package/dist/bin/sync-runner.js +360 -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 +648 -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 +59 -0
  62. package/dist/vault-client.d.ts.map +1 -1
  63. package/dist/vault-client.js +72 -0
  64. package/dist/vault-client.js.map +1 -1
  65. package/dist/vault-client.test.js +160 -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 +804 -0
  73. package/src/bin/sync-runner.ts +499 -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 +94 -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 +563 -0
  97. package/src/vault-client.ts +478 -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,134 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * hq-sync-runner — machine-targeted entrypoint for `@indigoai-us/hq-cloud`
4
+ * (ADR-0001).
5
+ *
6
+ * The AppBar Sync menubar (Tauri + Rust) spawns this binary as a subprocess
7
+ * and reads ndjson events from stdout. The protocol is intentionally narrow
8
+ * and versioned-by-shape, not by tooling — no chalk, no colors, no human
9
+ * prose. If you want to invoke sync as a human, use `hq sync` in
10
+ * `@indigoai-us/hq-cli`.
11
+ *
12
+ * Flags:
13
+ * --companies Fan out across every membership the caller has
14
+ * --company <slug-or-uid> Sync a single company (alternative to --companies)
15
+ * --on-conflict <strategy> abort | overwrite | keep (default: abort)
16
+ * --hq-root <path> Local HQ directory (default: $HOME/hq)
17
+ * --json Ignored — ndjson on stdout is the default and
18
+ * only output mode. Accepted for symmetry with the
19
+ * AppBar's argv in case someone passes it.
20
+ *
21
+ * Event protocol (one JSON object per line on stdout):
22
+ * setup-needed — caller signed in but has no person entity yet
23
+ * auth-error — no valid token available (interactive login disabled)
24
+ * fanout-plan — list of companies we're about to sync
25
+ * progress — per-file download
26
+ * error — per-file or per-company error
27
+ * complete — per-company summary
28
+ * all-complete — aggregate summary after fanout
29
+ *
30
+ * Exit code:
31
+ * 0 — event stream describes the outcome (including setup-needed)
32
+ * 1 — argv parse error or unrecoverable pre-sync failure
33
+ */
34
+ import { type VaultServiceConfig, type Membership, type EntityInfo, type PendingInviteByEmail } from "../index.js";
35
+ import type { SyncOptions, SyncResult, SyncProgressEvent } from "../cli/sync.js";
36
+ /**
37
+ * Every event emitted on stdout. The `company` field is present on every
38
+ * event except `setup-needed` / `auth-error` / `fanout-plan` / `all-complete`
39
+ * (which describe the whole run) — consumers should treat its absence as
40
+ * "meta-event, not tied to a specific company".
41
+ */
42
+ export type RunnerEvent = {
43
+ type: "setup-needed";
44
+ } | {
45
+ type: "auth-error";
46
+ message: string;
47
+ } | {
48
+ type: "fanout-plan";
49
+ companies: Array<{
50
+ uid: string;
51
+ slug: string;
52
+ name?: string;
53
+ }>;
54
+ } | ({
55
+ type: "progress";
56
+ company: string;
57
+ } & Omit<Extract<SyncProgressEvent, {
58
+ type: "progress";
59
+ }>, "type">) | ({
60
+ type: "error";
61
+ company?: string;
62
+ } & Omit<Extract<SyncProgressEvent, {
63
+ type: "error";
64
+ }>, "type">) | ({
65
+ type: "complete";
66
+ company: string;
67
+ } & SyncResult) | {
68
+ type: "all-complete";
69
+ companiesAttempted: number;
70
+ filesDownloaded: number;
71
+ bytesDownloaded: number;
72
+ errors: Array<{
73
+ company: string;
74
+ message: string;
75
+ }>;
76
+ };
77
+ /**
78
+ * The narrow VaultClient surface the runner actually uses. Declared here (not
79
+ * `Pick<VaultClient, ...>`) because `Pick` preserves the *entire* `entity`
80
+ * accessor object — but the runner only needs `entity.get`, and forcing test
81
+ * stubs to also implement `findBySlug`/`create` would be dishonest about the
82
+ * real dependency. Keep this interface in sync with the real VaultClient
83
+ * method signatures (both return types come straight from the SDK).
84
+ */
85
+ export interface VaultClientSurface {
86
+ listMyMemberships: () => Promise<Membership[]>;
87
+ listMyPendingInvitesByEmail: () => Promise<PendingInviteByEmail[]>;
88
+ claimPendingInvitesByEmail: (personUid: string) => Promise<void>;
89
+ ensureMyPersonEntity: (hints: {
90
+ ownerSub: string;
91
+ displayName: string;
92
+ }) => Promise<EntityInfo>;
93
+ entity: {
94
+ get: (uid: string) => Promise<EntityInfo>;
95
+ };
96
+ }
97
+ /** Minimal shape of the claims we read off the Cognito idToken. */
98
+ interface IdTokenClaims {
99
+ sub?: string;
100
+ email?: string;
101
+ name?: string;
102
+ given_name?: string;
103
+ family_name?: string;
104
+ }
105
+ export interface RunnerDeps {
106
+ /** Where to write ndjson events. Defaults to `process.stdout`. */
107
+ stdout?: {
108
+ write: (chunk: string) => boolean | void;
109
+ };
110
+ /** Where to write diagnostics. Defaults to `process.stderr`. */
111
+ stderr?: {
112
+ write: (chunk: string) => boolean | void;
113
+ };
114
+ /** Resolve a valid access token. Defaults to `getValidAccessToken` non-interactive. */
115
+ getAccessToken?: () => Promise<string>;
116
+ /**
117
+ * Read the caller's identity claims (sub/email/name) off the cached Cognito
118
+ * idToken. Defaults to decoding `loadCachedTokens().idToken`. Returns `null`
119
+ * when no cached tokens exist — the runner will then skip the claim-dance
120
+ * and fall through to the usual listMyMemberships path.
121
+ */
122
+ getIdTokenClaims?: () => IdTokenClaims | null;
123
+ /**
124
+ * Produce a VaultClient-like object. Defaults to `new VaultClient(config)`.
125
+ * Tests inject a stub here — the runner only calls the methods listed in
126
+ * `VaultClientSurface`.
127
+ */
128
+ createVaultClient?: (config: VaultServiceConfig) => VaultClientSurface;
129
+ /** Sync function. Defaults to `cli/sync.sync`. */
130
+ sync?: (options: SyncOptions) => Promise<SyncResult>;
131
+ }
132
+ export declare function runRunner(argv: string[], deps?: RunnerDeps): Promise<number>;
133
+ export {};
134
+ //# sourceMappingURL=sync-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-runner.d.ts","sourceRoot":"","sources":["../../src/bin/sync-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,360 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * hq-sync-runner — machine-targeted entrypoint for `@indigoai-us/hq-cloud`
4
+ * (ADR-0001).
5
+ *
6
+ * The AppBar Sync menubar (Tauri + Rust) spawns this binary as a subprocess
7
+ * and reads ndjson events from stdout. The protocol is intentionally narrow
8
+ * and versioned-by-shape, not by tooling — no chalk, no colors, no human
9
+ * prose. If you want to invoke sync as a human, use `hq sync` in
10
+ * `@indigoai-us/hq-cli`.
11
+ *
12
+ * Flags:
13
+ * --companies Fan out across every membership the caller has
14
+ * --company <slug-or-uid> Sync a single company (alternative to --companies)
15
+ * --on-conflict <strategy> abort | overwrite | keep (default: abort)
16
+ * --hq-root <path> Local HQ directory (default: $HOME/hq)
17
+ * --json Ignored — ndjson on stdout is the default and
18
+ * only output mode. Accepted for symmetry with the
19
+ * AppBar's argv in case someone passes it.
20
+ *
21
+ * Event protocol (one JSON object per line on stdout):
22
+ * setup-needed — caller signed in but has no person entity yet
23
+ * auth-error — no valid token available (interactive login disabled)
24
+ * fanout-plan — list of companies we're about to sync
25
+ * progress — per-file download
26
+ * error — per-file or per-company error
27
+ * complete — per-company summary
28
+ * all-complete — aggregate summary after fanout
29
+ *
30
+ * Exit code:
31
+ * 0 — event stream describes the outcome (including setup-needed)
32
+ * 1 — argv parse error or unrecoverable pre-sync failure
33
+ */
34
+ import * as os from "os";
35
+ import * as path from "path";
36
+ import * as fs from "fs";
37
+ import { fileURLToPath } from "url";
38
+ import { getValidAccessToken, loadCachedTokens, VaultClient, VaultAuthError, } from "../index.js";
39
+ import { sync as defaultSync } from "../cli/sync.js";
40
+ // ---------------------------------------------------------------------------
41
+ // Defaults — mirror `hq-cli/src/utils/cognito-session.ts`. Inlined (not
42
+ // imported) to avoid a circular dep between hq-cli and hq-cloud. If these
43
+ // drift, the symptom is "runner talks to a different stage than hq sync"
44
+ // — keep both files lined up.
45
+ // ---------------------------------------------------------------------------
46
+ const DEFAULT_COGNITO = {
47
+ region: process.env.AWS_REGION ?? "us-east-1",
48
+ userPoolDomain: process.env.HQ_COGNITO_DOMAIN ?? "hq-vault-dev",
49
+ clientId: process.env.HQ_COGNITO_CLIENT_ID ?? "4mmujmjq3srakdueg656b9m0mp",
50
+ port: process.env.HQ_COGNITO_CALLBACK_PORT
51
+ ? Number(process.env.HQ_COGNITO_CALLBACK_PORT)
52
+ : 8765,
53
+ };
54
+ const DEFAULT_VAULT_API_URL = process.env.HQ_VAULT_API_URL ??
55
+ "https://tqdwdqxv75.execute-api.us-east-1.amazonaws.com";
56
+ const DEFAULT_HQ_ROOT = path.join(os.homedir(), "hq");
57
+ // ---------------------------------------------------------------------------
58
+ // JWT claim decoder — inlined to avoid pulling a dep just to read an idToken.
59
+ // We do NOT verify the signature here — Cognito already did that when it
60
+ // issued the token, and we only read the public claims (sub/email/name) to
61
+ // drive the claim-dance + create the person entity. If the token is tampered
62
+ // with, the downstream vault-service call will reject it (signature-verified
63
+ // there) long before any claimed value causes harm.
64
+ // ---------------------------------------------------------------------------
65
+ function decodeJwtClaims(jwt) {
66
+ const parts = jwt.split(".");
67
+ if (parts.length !== 3)
68
+ return null;
69
+ try {
70
+ const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
71
+ const padded = payload + "=".repeat((4 - (payload.length % 4)) % 4);
72
+ const json = Buffer.from(padded, "base64").toString("utf-8");
73
+ return JSON.parse(json);
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ }
79
+ function defaultGetIdTokenClaims() {
80
+ const tokens = loadCachedTokens();
81
+ if (!tokens?.idToken)
82
+ return null;
83
+ return decodeJwtClaims(tokens.idToken);
84
+ }
85
+ /**
86
+ * Best-effort: claim any email-keyed pending invites that were sent before
87
+ * this user had a person entity. Mirrors the installer's vault-handoff flow.
88
+ *
89
+ * Silent on the happy path — only logs to stderr on soft failures (so a
90
+ * transient network blip doesn't block the sync). Never throws: a caller who
91
+ * can't list memberships despite an unclaimed invite is no worse off than the
92
+ * pre-claim-dance behavior (which was to emit setup-needed).
93
+ */
94
+ async function runClaimDance(client, claims, stderr) {
95
+ try {
96
+ const pending = await client.listMyPendingInvitesByEmail();
97
+ if (pending.length === 0)
98
+ return;
99
+ const displayName = claims.name ??
100
+ [claims.given_name, claims.family_name].filter(Boolean).join(" ") ??
101
+ claims.email ??
102
+ "";
103
+ const ownerSub = claims.sub ?? "";
104
+ if (!ownerSub || !displayName) {
105
+ stderr.write("hq-sync-runner: skipping claim-dance — idToken missing sub/name\n");
106
+ return;
107
+ }
108
+ const person = await client.ensureMyPersonEntity({
109
+ ownerSub,
110
+ displayName,
111
+ });
112
+ await client.claimPendingInvitesByEmail(person.uid);
113
+ }
114
+ catch (err) {
115
+ const msg = err instanceof Error ? err.message : String(err);
116
+ stderr.write(`hq-sync-runner: claim-dance skipped — ${msg}\n`);
117
+ }
118
+ }
119
+ function parseArgs(argv) {
120
+ let companies = false;
121
+ let company;
122
+ let onConflict = "abort";
123
+ let hqRoot = DEFAULT_HQ_ROOT;
124
+ for (let i = 0; i < argv.length; i++) {
125
+ const arg = argv[i];
126
+ switch (arg) {
127
+ case "--companies":
128
+ companies = true;
129
+ break;
130
+ case "--company":
131
+ company = argv[++i];
132
+ if (!company)
133
+ return { error: "--company requires a value" };
134
+ break;
135
+ case "--on-conflict": {
136
+ const val = argv[++i];
137
+ if (val !== "abort" && val !== "overwrite" && val !== "keep") {
138
+ return {
139
+ error: `--on-conflict must be one of abort|overwrite|keep, got: ${val ?? "(missing)"}`,
140
+ };
141
+ }
142
+ onConflict = val;
143
+ break;
144
+ }
145
+ case "--hq-root":
146
+ hqRoot = argv[++i];
147
+ if (!hqRoot)
148
+ return { error: "--hq-root requires a value" };
149
+ break;
150
+ case "--json":
151
+ // Accepted but ignored — ndjson is the only output mode.
152
+ break;
153
+ default:
154
+ return { error: `Unknown argument: ${arg}` };
155
+ }
156
+ }
157
+ if (companies && company) {
158
+ return { error: "Pass --companies OR --company <slug>, not both" };
159
+ }
160
+ if (!companies && !company) {
161
+ return { error: "Pass --companies or --company <slug>" };
162
+ }
163
+ return { companies, company, onConflict, hqRoot };
164
+ }
165
+ // ---------------------------------------------------------------------------
166
+ // runRunner — testable entrypoint
167
+ // ---------------------------------------------------------------------------
168
+ export async function runRunner(argv, deps = {}) {
169
+ const stdout = deps.stdout ?? process.stdout;
170
+ const stderr = deps.stderr ?? process.stderr;
171
+ const emit = (event) => {
172
+ stdout.write(`${JSON.stringify(event)}\n`);
173
+ };
174
+ // ---- argv -------------------------------------------------------------
175
+ const parsed = parseArgs(argv);
176
+ if ("error" in parsed) {
177
+ stderr.write(`hq-sync-runner: ${parsed.error}\n`);
178
+ return 1;
179
+ }
180
+ // ---- auth -------------------------------------------------------------
181
+ let accessToken;
182
+ try {
183
+ const getAccessToken = deps.getAccessToken ??
184
+ (() => getValidAccessToken(DEFAULT_COGNITO, { interactive: false }));
185
+ accessToken = await getAccessToken();
186
+ }
187
+ catch (err) {
188
+ emit({
189
+ type: "auth-error",
190
+ message: err instanceof Error ? err.message : String(err),
191
+ });
192
+ return 0;
193
+ }
194
+ // ---- vault client -----------------------------------------------------
195
+ const vaultConfig = {
196
+ apiUrl: DEFAULT_VAULT_API_URL,
197
+ authToken: accessToken,
198
+ region: DEFAULT_COGNITO.region,
199
+ };
200
+ const client = deps.createVaultClient?.(vaultConfig) ?? new VaultClient(vaultConfig);
201
+ // ---- resolve targets --------------------------------------------------
202
+ let memberships;
203
+ try {
204
+ if (parsed.companies) {
205
+ // Before giving up on memberships, run the claim-dance: new users signed
206
+ // in via the tray may have email-keyed invites waiting for them. Without
207
+ // this, an invited user would see "setup-needed" on every tray click.
208
+ const getClaims = deps.getIdTokenClaims ?? defaultGetIdTokenClaims;
209
+ const claims = getClaims();
210
+ if (claims) {
211
+ await runClaimDance(client, claims, stderr);
212
+ }
213
+ memberships = await client.listMyMemberships();
214
+ if (memberships.length === 0) {
215
+ // Truly empty — still a valid state (no memberships = nothing to
216
+ // sync). The tray will show a friendly "create your first company"
217
+ // CTA rather than an alarm banner.
218
+ emit({ type: "setup-needed" });
219
+ return 0;
220
+ }
221
+ }
222
+ else {
223
+ // Single-company mode: fabricate a minimal membership so the fanout
224
+ // loop below treats it uniformly. We don't need to hit
225
+ // /membership/me — the caller already told us which company.
226
+ memberships = [{ companyUid: parsed.company }];
227
+ }
228
+ }
229
+ catch (err) {
230
+ if (err instanceof VaultAuthError) {
231
+ emit({
232
+ type: "auth-error",
233
+ message: err.message,
234
+ });
235
+ return 0;
236
+ }
237
+ // Any other failure is unrecoverable — surface as an error event and
238
+ // exit non-zero so the spawner knows the runner didn't get far enough
239
+ // to emit a useful protocol stream.
240
+ emit({
241
+ type: "error",
242
+ message: err instanceof Error ? err.message : String(err),
243
+ path: "(discovery)",
244
+ });
245
+ return 1;
246
+ }
247
+ // ---- resolve slugs for the fanout plan --------------------------------
248
+ // The menubar wants "Syncing indigo" in its UI, not the raw cmp_* ULID.
249
+ // If the entity fetch fails for some row (entity deleted, scoping issue),
250
+ // degrade to using the UID as the slug rather than aborting the run.
251
+ const plan = [];
252
+ for (const m of memberships) {
253
+ let slug = m.companyUid;
254
+ let name;
255
+ try {
256
+ const info = await client.entity.get(m.companyUid);
257
+ slug = info.slug || m.companyUid;
258
+ name = info.name;
259
+ }
260
+ catch {
261
+ // Best-effort — keep UID as the display identifier.
262
+ }
263
+ plan.push({ uid: m.companyUid, slug, ...(name ? { name } : {}) });
264
+ }
265
+ emit({ type: "fanout-plan", companies: plan });
266
+ // ---- fanout -----------------------------------------------------------
267
+ const syncFn = deps.sync ?? defaultSync;
268
+ let totalFiles = 0;
269
+ let totalBytes = 0;
270
+ const errors = [];
271
+ for (const target of plan) {
272
+ const companyLabel = target.slug;
273
+ try {
274
+ const result = await syncFn({
275
+ company: target.uid,
276
+ vaultConfig,
277
+ hqRoot: parsed.hqRoot,
278
+ onConflict: parsed.onConflict,
279
+ onEvent: (event) => {
280
+ // Tag per-file events with the company they belong to so the
281
+ // menubar can route them to the right company's progress bar.
282
+ if (event.type === "progress") {
283
+ emit({
284
+ type: "progress",
285
+ company: companyLabel,
286
+ path: event.path,
287
+ bytes: event.bytes,
288
+ ...(event.message ? { message: event.message } : {}),
289
+ });
290
+ }
291
+ else {
292
+ emit({
293
+ type: "error",
294
+ company: companyLabel,
295
+ path: event.path,
296
+ message: event.message,
297
+ });
298
+ }
299
+ },
300
+ });
301
+ emit({ type: "complete", company: companyLabel, ...result });
302
+ totalFiles += result.filesDownloaded;
303
+ totalBytes += result.bytesDownloaded;
304
+ }
305
+ catch (err) {
306
+ const message = err instanceof Error ? err.message : String(err);
307
+ errors.push({ company: companyLabel, message });
308
+ emit({
309
+ type: "error",
310
+ company: companyLabel,
311
+ path: "(company)",
312
+ message,
313
+ });
314
+ // Continue — one company's failure shouldn't abort the whole fanout.
315
+ }
316
+ }
317
+ emit({
318
+ type: "all-complete",
319
+ companiesAttempted: plan.length,
320
+ filesDownloaded: totalFiles,
321
+ bytesDownloaded: totalBytes,
322
+ errors,
323
+ });
324
+ return 0;
325
+ }
326
+ // ---------------------------------------------------------------------------
327
+ // Entrypoint — only runs when invoked directly, not when imported for tests
328
+ // ---------------------------------------------------------------------------
329
+ // Detect whether this module is the entry point. The obvious check
330
+ // (`import.meta.url === file://${argv[1]}`) breaks for every real-world
331
+ // install shape: npm-link'd binaries, global installs via Homebrew, and
332
+ // pnpm's `node_modules/.bin` shims all leave `process.argv[1]` pointing
333
+ // at a symlink named `hq-sync-runner` (no `.js` suffix) while
334
+ // `import.meta.url` always resolves to the underlying `sync-runner.js`.
335
+ //
336
+ // Resolve both sides through realpath before comparing — that's the only
337
+ // way to handle all symlink layouts without false negatives. If realpath
338
+ // fails (argv[1] gone, permissions), fall through to `false` so we
339
+ // don't run twice when imported as a library.
340
+ const isDirectInvocation = (() => {
341
+ if (!process.argv[1])
342
+ return false;
343
+ try {
344
+ const modulePath = fs.realpathSync(fileURLToPath(import.meta.url));
345
+ const argvPath = fs.realpathSync(process.argv[1]);
346
+ return modulePath === argvPath;
347
+ }
348
+ catch {
349
+ return false;
350
+ }
351
+ })();
352
+ if (isDirectInvocation) {
353
+ runRunner(process.argv.slice(2))
354
+ .then((code) => process.exit(code))
355
+ .catch((err) => {
356
+ process.stderr.write(`hq-sync-runner: uncaught error — ${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
357
+ process.exit(1);
358
+ });
359
+ }
360
+ //# sourceMappingURL=sync-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-runner.js","sourceRoot":"","sources":["../../src/bin/sync-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Unit tests for hq-sync-runner (ADR-0001).
3
+ *
4
+ * The runner is designed around `RunnerDeps` — every side effect is
5
+ * injectable, so tests assert on captured ndjson output rather than mocking
6
+ * modules. That keeps each test honest about what the runner does vs what
7
+ * its collaborators do.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=sync-runner.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-runner.test.d.ts","sourceRoot":"","sources":["../../src/bin/sync-runner.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}