@indigoai-us/hq-cli 5.6.0 → 5.6.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.
|
@@ -55,10 +55,12 @@ export interface ProvisionResult {
|
|
|
55
55
|
manifest_patched: boolean;
|
|
56
56
|
config_written: boolean;
|
|
57
57
|
initial_sync: {
|
|
58
|
-
ok
|
|
58
|
+
ok?: boolean;
|
|
59
59
|
files_uploaded?: number;
|
|
60
60
|
bytes_uploaded?: number;
|
|
61
61
|
error?: string;
|
|
62
|
+
/** True if the caller passed --skip-initial-sync; ok/files/bytes will be absent. */
|
|
63
|
+
skipped?: boolean;
|
|
62
64
|
};
|
|
63
65
|
}
|
|
64
66
|
/** Options for the high-level `provisionCompany` orchestrator. */
|
|
@@ -68,6 +70,15 @@ export interface ProvisionCompanyOptions {
|
|
|
68
70
|
ownerUid?: string;
|
|
69
71
|
hqRoot: string;
|
|
70
72
|
vaultApiUrl: string;
|
|
73
|
+
/**
|
|
74
|
+
* Skip the initial-sync step. The vault entity, manifest patch, and
|
|
75
|
+
* `.hq/config.json` write still happen; the post-provision `share()` call
|
|
76
|
+
* is no-op'd. Use this when the caller has its own upload pipeline (e.g.
|
|
77
|
+
* AppBar HQ Sync's `first_push_company` with STS-vended credentials and
|
|
78
|
+
* Tauri progress events) and would otherwise double-upload the same files.
|
|
79
|
+
* When true, `initial_sync` in the result is `{ skipped: true }`.
|
|
80
|
+
*/
|
|
81
|
+
skipInitialSync?: boolean;
|
|
71
82
|
/** Injected vault HTTP client (override for tests). */
|
|
72
83
|
vaultClient?: VaultClient;
|
|
73
84
|
/** Injected access-token resolver (override for tests). */
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* `initial_sync.ok=false`). Manifest + config may have been written.
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
28
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="325d7bbf-c44d-5bba-a156-072302ab1536")}catch(e){}}();
|
|
29
29
|
import chalk from "chalk";
|
|
30
30
|
import * as fs from "node:fs";
|
|
31
31
|
import * as path from "node:path";
|
|
@@ -317,39 +317,47 @@ export async function provisionCompany(options) {
|
|
|
317
317
|
vaultApiUrl: options.vaultApiUrl,
|
|
318
318
|
});
|
|
319
319
|
log(`wrote companies/${options.slug}/.hq/config.json`);
|
|
320
|
-
// Step 8: trigger initial sync (failure ⇒ exit 3 with cloud_uid populated)
|
|
321
|
-
|
|
320
|
+
// Step 8: trigger initial sync (failure ⇒ exit 3 with cloud_uid populated).
|
|
321
|
+
// Skipped when caller passed --skip-initial-sync (e.g. AppBar HQ Sync, which
|
|
322
|
+
// owns its own STS-credentialed upload pipeline + Tauri progress events).
|
|
322
323
|
let initialSync;
|
|
323
|
-
|
|
324
|
-
log(`
|
|
325
|
-
|
|
326
|
-
slug: options.slug,
|
|
327
|
-
hqRoot: options.hqRoot,
|
|
328
|
-
accessToken,
|
|
329
|
-
vaultApiUrl: options.vaultApiUrl,
|
|
330
|
-
});
|
|
331
|
-
initialSync = {
|
|
332
|
-
ok: true,
|
|
333
|
-
files_uploaded: sync.filesUploaded,
|
|
334
|
-
bytes_uploaded: sync.bytesUploaded,
|
|
335
|
-
};
|
|
336
|
-
log(`initial sync complete — files=${sync.filesUploaded} bytes=${sync.bytesUploaded}`);
|
|
324
|
+
if (options.skipInitialSync) {
|
|
325
|
+
log(`skipping initial sync (--skip-initial-sync)`);
|
|
326
|
+
initialSync = { skipped: true };
|
|
337
327
|
}
|
|
338
|
-
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
328
|
+
else {
|
|
329
|
+
const runner = options.runInitialSync ?? defaultRunInitialSync;
|
|
330
|
+
try {
|
|
331
|
+
log(`triggering initial sync via share()`);
|
|
332
|
+
const sync = await runner({
|
|
333
|
+
slug: options.slug,
|
|
334
|
+
hqRoot: options.hqRoot,
|
|
335
|
+
accessToken,
|
|
336
|
+
vaultApiUrl: options.vaultApiUrl,
|
|
337
|
+
});
|
|
338
|
+
initialSync = {
|
|
339
|
+
ok: true,
|
|
340
|
+
files_uploaded: sync.filesUploaded,
|
|
341
|
+
bytes_uploaded: sync.bytesUploaded,
|
|
342
|
+
};
|
|
343
|
+
log(`initial sync complete — files=${sync.filesUploaded} bytes=${sync.bytesUploaded}`);
|
|
344
|
+
}
|
|
345
|
+
catch (err) {
|
|
346
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
347
|
+
log(`initial sync failed: ${msg}`);
|
|
348
|
+
throw new ProvisionError(3, `Initial sync failed: ${msg}`, {
|
|
349
|
+
ok: false,
|
|
350
|
+
company_slug: options.slug,
|
|
351
|
+
cloud_uid: cloudUid,
|
|
352
|
+
bucket_name: bucketName,
|
|
353
|
+
vault_api_url: options.vaultApiUrl,
|
|
354
|
+
kms_key_id: kmsKeyId,
|
|
355
|
+
created_entity: createdEntity,
|
|
356
|
+
manifest_patched: true,
|
|
357
|
+
config_written: true,
|
|
358
|
+
initial_sync: { ok: false, error: msg },
|
|
359
|
+
});
|
|
360
|
+
}
|
|
353
361
|
}
|
|
354
362
|
return {
|
|
355
363
|
ok: true,
|
|
@@ -385,6 +393,9 @@ export function registerCloudProvisionCommands(program) {
|
|
|
385
393
|
.option("--owner <uid>", "Owner person UID (default: current Cognito user sub)")
|
|
386
394
|
.option("--hq-root <path>", `Local HQ tree root (default: ${DEFAULT_HQ_ROOT})`, DEFAULT_HQ_ROOT)
|
|
387
395
|
.option("--vault-api-url <url>", `Vault API URL (default: ${DEFAULT_VAULT_API_URL})`, DEFAULT_VAULT_API_URL)
|
|
396
|
+
.option("--skip-initial-sync", "Skip the post-provision share() initial sync. Use when the caller " +
|
|
397
|
+
"(e.g. AppBar HQ Sync) has its own upload pipeline. Result includes " +
|
|
398
|
+
"{ initial_sync: { skipped: true } } when set.")
|
|
388
399
|
.action(async (slug, options) => {
|
|
389
400
|
try {
|
|
390
401
|
const result = await provisionCompany({
|
|
@@ -393,6 +404,7 @@ export function registerCloudProvisionCommands(program) {
|
|
|
393
404
|
ownerUid: options.owner,
|
|
394
405
|
hqRoot: options.hqRoot,
|
|
395
406
|
vaultApiUrl: options.vaultApiUrl,
|
|
407
|
+
skipInitialSync: options.skipInitialSync,
|
|
396
408
|
});
|
|
397
409
|
// Final stdout line — single JSON document for downstream consumers
|
|
398
410
|
process.stdout.write(JSON.stringify(result) + "\n");
|
|
@@ -414,4 +426,4 @@ export function registerCloudProvisionCommands(program) {
|
|
|
414
426
|
});
|
|
415
427
|
}
|
|
416
428
|
//# sourceMappingURL=cloud-provision.js.map
|
|
417
|
-
//# debugId=
|
|
429
|
+
//# debugId=325d7bbf-c44d-5bba-a156-072302ab1536
|
package/package.json
CHANGED
|
@@ -649,4 +649,75 @@ describe("provisionCompany", () => {
|
|
|
649
649
|
ownerUid: undefined,
|
|
650
650
|
});
|
|
651
651
|
});
|
|
652
|
+
|
|
653
|
+
it("skipInitialSync=true — runner is NOT called; result has initial_sync.skipped", async () => {
|
|
654
|
+
setupValid();
|
|
655
|
+
const entity: VaultEntity = {
|
|
656
|
+
uid: "cmp_01H",
|
|
657
|
+
type: "company",
|
|
658
|
+
slug: "indigo",
|
|
659
|
+
name: "Indigo",
|
|
660
|
+
bucketName: "hq-vault-cmp-01H",
|
|
661
|
+
};
|
|
662
|
+
const vaultClient = makeVaultClient({
|
|
663
|
+
findCompanyBySlug: vi.fn().mockResolvedValue(null),
|
|
664
|
+
createCompanyEntity: vi.fn().mockResolvedValue(entity),
|
|
665
|
+
});
|
|
666
|
+
const runInitialSync = vi.fn();
|
|
667
|
+
|
|
668
|
+
const result = await provisionCompany({
|
|
669
|
+
slug: "indigo",
|
|
670
|
+
hqRoot: tmpRoot,
|
|
671
|
+
vaultApiUrl,
|
|
672
|
+
vaultClient,
|
|
673
|
+
resolveAccessToken: async () => accessToken,
|
|
674
|
+
runInitialSync,
|
|
675
|
+
skipInitialSync: true,
|
|
676
|
+
log: () => {},
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
expect(runInitialSync).not.toHaveBeenCalled();
|
|
680
|
+
expect(result.initial_sync).toEqual({ skipped: true });
|
|
681
|
+
expect(result.ok).toBe(true);
|
|
682
|
+
expect(result.cloud_uid).toBe("cmp_01H");
|
|
683
|
+
expect(result.manifest_patched).toBe(true);
|
|
684
|
+
expect(result.config_written).toBe(true);
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
it("skipInitialSync=true — manifest + .hq/config.json are STILL written", async () => {
|
|
688
|
+
setupValid();
|
|
689
|
+
const entity: VaultEntity = {
|
|
690
|
+
uid: "cmp_SKIP",
|
|
691
|
+
type: "company",
|
|
692
|
+
slug: "indigo",
|
|
693
|
+
name: "Indigo",
|
|
694
|
+
bucketName: "hq-vault-cmp-SKIP",
|
|
695
|
+
};
|
|
696
|
+
const vaultClient = makeVaultClient({
|
|
697
|
+
findCompanyBySlug: vi.fn().mockResolvedValue(null),
|
|
698
|
+
createCompanyEntity: vi.fn().mockResolvedValue(entity),
|
|
699
|
+
});
|
|
700
|
+
await provisionCompany({
|
|
701
|
+
slug: "indigo",
|
|
702
|
+
hqRoot: tmpRoot,
|
|
703
|
+
vaultApiUrl,
|
|
704
|
+
vaultClient,
|
|
705
|
+
resolveAccessToken: async () => accessToken,
|
|
706
|
+
runInitialSync: vi.fn(),
|
|
707
|
+
skipInitialSync: true,
|
|
708
|
+
log: () => {},
|
|
709
|
+
});
|
|
710
|
+
// Manifest patched on disk
|
|
711
|
+
const m = yaml.load(fs.readFileSync(manifestPath(tmpRoot), "utf-8")) as {
|
|
712
|
+
companies: Record<string, Record<string, unknown>>;
|
|
713
|
+
};
|
|
714
|
+
expect(m.companies.indigo.cloud_uid).toBe("cmp_SKIP");
|
|
715
|
+
expect(m.companies.indigo.bucket_name).toBe("hq-vault-cmp-SKIP");
|
|
716
|
+
// .hq/config.json written
|
|
717
|
+
const c = JSON.parse(
|
|
718
|
+
fs.readFileSync(companyConfigPath(tmpRoot, "indigo"), "utf-8"),
|
|
719
|
+
);
|
|
720
|
+
expect(c.companyUid).toBe("cmp_SKIP");
|
|
721
|
+
expect(c.bucketName).toBe("hq-vault-cmp-SKIP");
|
|
722
|
+
});
|
|
652
723
|
});
|
|
@@ -74,10 +74,12 @@ export interface ProvisionResult {
|
|
|
74
74
|
manifest_patched: boolean;
|
|
75
75
|
config_written: boolean;
|
|
76
76
|
initial_sync: {
|
|
77
|
-
ok
|
|
77
|
+
ok?: boolean;
|
|
78
78
|
files_uploaded?: number;
|
|
79
79
|
bytes_uploaded?: number;
|
|
80
80
|
error?: string;
|
|
81
|
+
/** True if the caller passed --skip-initial-sync; ok/files/bytes will be absent. */
|
|
82
|
+
skipped?: boolean;
|
|
81
83
|
};
|
|
82
84
|
}
|
|
83
85
|
|
|
@@ -88,6 +90,15 @@ export interface ProvisionCompanyOptions {
|
|
|
88
90
|
ownerUid?: string;
|
|
89
91
|
hqRoot: string;
|
|
90
92
|
vaultApiUrl: string;
|
|
93
|
+
/**
|
|
94
|
+
* Skip the initial-sync step. The vault entity, manifest patch, and
|
|
95
|
+
* `.hq/config.json` write still happen; the post-provision `share()` call
|
|
96
|
+
* is no-op'd. Use this when the caller has its own upload pipeline (e.g.
|
|
97
|
+
* AppBar HQ Sync's `first_push_company` with STS-vended credentials and
|
|
98
|
+
* Tauri progress events) and would otherwise double-upload the same files.
|
|
99
|
+
* When true, `initial_sync` in the result is `{ skipped: true }`.
|
|
100
|
+
*/
|
|
101
|
+
skipInitialSync?: boolean;
|
|
91
102
|
/** Injected vault HTTP client (override for tests). */
|
|
92
103
|
vaultClient?: VaultClient;
|
|
93
104
|
/** Injected access-token resolver (override for tests). */
|
|
@@ -510,40 +521,47 @@ export async function provisionCompany(
|
|
|
510
521
|
});
|
|
511
522
|
log(`wrote companies/${options.slug}/.hq/config.json`);
|
|
512
523
|
|
|
513
|
-
// Step 8: trigger initial sync (failure ⇒ exit 3 with cloud_uid populated)
|
|
514
|
-
|
|
524
|
+
// Step 8: trigger initial sync (failure ⇒ exit 3 with cloud_uid populated).
|
|
525
|
+
// Skipped when caller passed --skip-initial-sync (e.g. AppBar HQ Sync, which
|
|
526
|
+
// owns its own STS-credentialed upload pipeline + Tauri progress events).
|
|
515
527
|
let initialSync: ProvisionResult["initial_sync"];
|
|
516
|
-
|
|
517
|
-
log(`
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
528
|
+
if (options.skipInitialSync) {
|
|
529
|
+
log(`skipping initial sync (--skip-initial-sync)`);
|
|
530
|
+
initialSync = { skipped: true };
|
|
531
|
+
} else {
|
|
532
|
+
const runner = options.runInitialSync ?? defaultRunInitialSync;
|
|
533
|
+
try {
|
|
534
|
+
log(`triggering initial sync via share()`);
|
|
535
|
+
const sync = await runner({
|
|
536
|
+
slug: options.slug,
|
|
537
|
+
hqRoot: options.hqRoot,
|
|
538
|
+
accessToken,
|
|
539
|
+
vaultApiUrl: options.vaultApiUrl,
|
|
540
|
+
});
|
|
541
|
+
initialSync = {
|
|
542
|
+
ok: true,
|
|
543
|
+
files_uploaded: sync.filesUploaded,
|
|
544
|
+
bytes_uploaded: sync.bytesUploaded,
|
|
545
|
+
};
|
|
546
|
+
log(
|
|
547
|
+
`initial sync complete — files=${sync.filesUploaded} bytes=${sync.bytesUploaded}`,
|
|
548
|
+
);
|
|
549
|
+
} catch (err) {
|
|
550
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
551
|
+
log(`initial sync failed: ${msg}`);
|
|
552
|
+
throw new ProvisionError(3, `Initial sync failed: ${msg}`, {
|
|
553
|
+
ok: false,
|
|
554
|
+
company_slug: options.slug,
|
|
555
|
+
cloud_uid: cloudUid,
|
|
556
|
+
bucket_name: bucketName,
|
|
557
|
+
vault_api_url: options.vaultApiUrl,
|
|
558
|
+
kms_key_id: kmsKeyId,
|
|
559
|
+
created_entity: createdEntity,
|
|
560
|
+
manifest_patched: true,
|
|
561
|
+
config_written: true,
|
|
562
|
+
initial_sync: { ok: false, error: msg },
|
|
563
|
+
});
|
|
564
|
+
}
|
|
547
565
|
}
|
|
548
566
|
|
|
549
567
|
return {
|
|
@@ -593,6 +611,12 @@ export function registerCloudProvisionCommands(program: Command): void {
|
|
|
593
611
|
`Vault API URL (default: ${DEFAULT_VAULT_API_URL})`,
|
|
594
612
|
DEFAULT_VAULT_API_URL,
|
|
595
613
|
)
|
|
614
|
+
.option(
|
|
615
|
+
"--skip-initial-sync",
|
|
616
|
+
"Skip the post-provision share() initial sync. Use when the caller " +
|
|
617
|
+
"(e.g. AppBar HQ Sync) has its own upload pipeline. Result includes " +
|
|
618
|
+
"{ initial_sync: { skipped: true } } when set.",
|
|
619
|
+
)
|
|
596
620
|
.action(
|
|
597
621
|
async (
|
|
598
622
|
slug: string,
|
|
@@ -601,6 +625,7 @@ export function registerCloudProvisionCommands(program: Command): void {
|
|
|
601
625
|
owner?: string;
|
|
602
626
|
hqRoot: string;
|
|
603
627
|
vaultApiUrl: string;
|
|
628
|
+
skipInitialSync?: boolean;
|
|
604
629
|
},
|
|
605
630
|
) => {
|
|
606
631
|
try {
|
|
@@ -610,6 +635,7 @@ export function registerCloudProvisionCommands(program: Command): void {
|
|
|
610
635
|
ownerUid: options.owner,
|
|
611
636
|
hqRoot: options.hqRoot,
|
|
612
637
|
vaultApiUrl: options.vaultApiUrl,
|
|
638
|
+
skipInitialSync: options.skipInitialSync,
|
|
613
639
|
});
|
|
614
640
|
// Final stdout line — single JSON document for downstream consumers
|
|
615
641
|
process.stdout.write(JSON.stringify(result) + "\n");
|