@indigoai-us/hq-cloud 6.11.11 → 6.11.13

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 (220) hide show
  1. package/dist/bin/sync-runner-company.d.ts +35 -0
  2. package/dist/bin/sync-runner-company.d.ts.map +1 -0
  3. package/dist/bin/sync-runner-company.js +290 -0
  4. package/dist/bin/sync-runner-company.js.map +1 -0
  5. package/dist/bin/sync-runner-events.d.ts +12 -0
  6. package/dist/bin/sync-runner-events.d.ts.map +1 -0
  7. package/dist/bin/sync-runner-events.js +12 -0
  8. package/dist/bin/sync-runner-events.js.map +1 -0
  9. package/dist/bin/sync-runner-planning.d.ts +53 -0
  10. package/dist/bin/sync-runner-planning.d.ts.map +1 -0
  11. package/dist/bin/sync-runner-planning.js +59 -0
  12. package/dist/bin/sync-runner-planning.js.map +1 -0
  13. package/dist/bin/sync-runner-rollup.d.ts +24 -0
  14. package/dist/bin/sync-runner-rollup.d.ts.map +1 -0
  15. package/dist/bin/sync-runner-rollup.js +46 -0
  16. package/dist/bin/sync-runner-rollup.js.map +1 -0
  17. package/dist/bin/sync-runner-telemetry.d.ts +5 -0
  18. package/dist/bin/sync-runner-telemetry.d.ts.map +1 -0
  19. package/dist/bin/sync-runner-telemetry.js +5 -0
  20. package/dist/bin/sync-runner-telemetry.js.map +1 -0
  21. package/dist/bin/sync-runner-watch-loop.d.ts +17 -0
  22. package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -0
  23. package/dist/bin/sync-runner-watch-loop.js +372 -0
  24. package/dist/bin/sync-runner-watch-loop.js.map +1 -0
  25. package/dist/bin/sync-runner-watch-routes.d.ts +25 -0
  26. package/dist/bin/sync-runner-watch-routes.d.ts.map +1 -0
  27. package/dist/bin/sync-runner-watch-routes.js +74 -0
  28. package/dist/bin/sync-runner-watch-routes.js.map +1 -0
  29. package/dist/bin/sync-runner.d.ts +5 -54
  30. package/dist/bin/sync-runner.d.ts.map +1 -1
  31. package/dist/bin/sync-runner.js +76 -978
  32. package/dist/bin/sync-runner.js.map +1 -1
  33. package/dist/bin/sync-runner.test.js +265 -11
  34. package/dist/bin/sync-runner.test.js.map +1 -1
  35. package/dist/cli/reindex.d.ts.map +1 -1
  36. package/dist/cli/reindex.js +34 -17
  37. package/dist/cli/reindex.js.map +1 -1
  38. package/dist/cli/reindex.test.js +39 -5
  39. package/dist/cli/reindex.test.js.map +1 -1
  40. package/dist/cli/rescue-classify-ordering.test.js +75 -0
  41. package/dist/cli/rescue-classify-ordering.test.js.map +1 -1
  42. package/dist/cli/rescue-core.d.ts +45 -0
  43. package/dist/cli/rescue-core.d.ts.map +1 -1
  44. package/dist/cli/rescue-core.js +320 -170
  45. package/dist/cli/rescue-core.js.map +1 -1
  46. package/dist/cli/share.d.ts +2 -1
  47. package/dist/cli/share.d.ts.map +1 -1
  48. package/dist/cli/share.js +276 -660
  49. package/dist/cli/share.js.map +1 -1
  50. package/dist/cli/share.test.js +30 -0
  51. package/dist/cli/share.test.js.map +1 -1
  52. package/dist/cli/sync.d.ts +28 -1
  53. package/dist/cli/sync.d.ts.map +1 -1
  54. package/dist/cli/sync.js +541 -748
  55. package/dist/cli/sync.js.map +1 -1
  56. package/dist/cli/sync.test.js +382 -1
  57. package/dist/cli/sync.test.js.map +1 -1
  58. package/dist/cognito-auth.d.ts.map +1 -1
  59. package/dist/cognito-auth.js +55 -10
  60. package/dist/cognito-auth.js.map +1 -1
  61. package/dist/cognito-auth.test.js +61 -0
  62. package/dist/cognito-auth.test.js.map +1 -1
  63. package/dist/daemon-worker.d.ts +2 -2
  64. package/dist/daemon-worker.js +3 -3
  65. package/dist/daemon-worker.js.map +1 -1
  66. package/dist/index.d.ts +2 -1
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +1 -1
  69. package/dist/index.js.map +1 -1
  70. package/dist/journal.d.ts.map +1 -1
  71. package/dist/journal.js +93 -6
  72. package/dist/journal.js.map +1 -1
  73. package/dist/journal.test.js +59 -0
  74. package/dist/journal.test.js.map +1 -1
  75. package/dist/machine-auth.test.js +60 -2
  76. package/dist/machine-auth.test.js.map +1 -1
  77. package/dist/object-io.d.ts +37 -1
  78. package/dist/object-io.d.ts.map +1 -1
  79. package/dist/object-io.js +149 -30
  80. package/dist/object-io.js.map +1 -1
  81. package/dist/object-io.test.js +121 -0
  82. package/dist/object-io.test.js.map +1 -1
  83. package/dist/operation-lock.d.ts +8 -8
  84. package/dist/operation-lock.d.ts.map +1 -1
  85. package/dist/operation-lock.js +99 -32
  86. package/dist/operation-lock.js.map +1 -1
  87. package/dist/operation-lock.test.js +51 -4
  88. package/dist/operation-lock.test.js.map +1 -1
  89. package/dist/personal-vault.d.ts.map +1 -1
  90. package/dist/personal-vault.js +8 -2
  91. package/dist/personal-vault.js.map +1 -1
  92. package/dist/personal-vault.test.js +34 -0
  93. package/dist/personal-vault.test.js.map +1 -1
  94. package/dist/prefix-coalesce.d.ts +20 -9
  95. package/dist/prefix-coalesce.d.ts.map +1 -1
  96. package/dist/prefix-coalesce.js +124 -28
  97. package/dist/prefix-coalesce.js.map +1 -1
  98. package/dist/prefix-coalesce.test.js +57 -2
  99. package/dist/prefix-coalesce.test.js.map +1 -1
  100. package/dist/remote-pull.d.ts +8 -3
  101. package/dist/remote-pull.d.ts.map +1 -1
  102. package/dist/remote-pull.js +85 -16
  103. package/dist/remote-pull.js.map +1 -1
  104. package/dist/remote-pull.test.js +213 -2
  105. package/dist/remote-pull.test.js.map +1 -1
  106. package/dist/s3.d.ts +2 -0
  107. package/dist/s3.d.ts.map +1 -1
  108. package/dist/s3.js +197 -116
  109. package/dist/s3.js.map +1 -1
  110. package/dist/s3.test.js +109 -0
  111. package/dist/s3.test.js.map +1 -1
  112. package/dist/scope-shrink.d.ts +3 -2
  113. package/dist/scope-shrink.d.ts.map +1 -1
  114. package/dist/scope-shrink.js +1 -1
  115. package/dist/scope-shrink.js.map +1 -1
  116. package/dist/skill-telemetry.d.ts +1 -1
  117. package/dist/skill-telemetry.d.ts.map +1 -1
  118. package/dist/skill-telemetry.js +69 -9
  119. package/dist/skill-telemetry.js.map +1 -1
  120. package/dist/skill-telemetry.test.js +86 -0
  121. package/dist/skill-telemetry.test.js.map +1 -1
  122. package/dist/sync/event-sync.d.ts +6 -0
  123. package/dist/sync/event-sync.d.ts.map +1 -1
  124. package/dist/sync/event-sync.js +34 -1
  125. package/dist/sync/event-sync.js.map +1 -1
  126. package/dist/sync/event-sync.test.js +73 -0
  127. package/dist/sync/event-sync.test.js.map +1 -1
  128. package/dist/sync/metrics.d.ts +17 -1
  129. package/dist/sync/metrics.d.ts.map +1 -1
  130. package/dist/sync/metrics.js +32 -1
  131. package/dist/sync/metrics.js.map +1 -1
  132. package/dist/sync/metrics.test.js +74 -1
  133. package/dist/sync/metrics.test.js.map +1 -1
  134. package/dist/sync/pull-scope.d.ts.map +1 -1
  135. package/dist/sync/pull-scope.js +15 -7
  136. package/dist/sync/pull-scope.js.map +1 -1
  137. package/dist/sync/push-receiver.d.ts +12 -5
  138. package/dist/sync/push-receiver.d.ts.map +1 -1
  139. package/dist/sync/push-receiver.js +45 -17
  140. package/dist/sync/push-receiver.js.map +1 -1
  141. package/dist/sync/push-receiver.test.js +67 -1
  142. package/dist/sync/push-receiver.test.js.map +1 -1
  143. package/dist/sync-core.d.ts +27 -0
  144. package/dist/sync-core.d.ts.map +1 -0
  145. package/dist/sync-core.js +54 -0
  146. package/dist/sync-core.js.map +1 -0
  147. package/dist/telemetry.d.ts +1 -1
  148. package/dist/telemetry.d.ts.map +1 -1
  149. package/dist/telemetry.js +59 -6
  150. package/dist/telemetry.js.map +1 -1
  151. package/dist/telemetry.test.js +74 -0
  152. package/dist/telemetry.test.js.map +1 -1
  153. package/dist/types.d.ts +8 -0
  154. package/dist/types.d.ts.map +1 -1
  155. package/dist/vault-client.d.ts.map +1 -1
  156. package/dist/vault-client.js +284 -36
  157. package/dist/vault-client.js.map +1 -1
  158. package/dist/vault-client.test.js +59 -0
  159. package/dist/vault-client.test.js.map +1 -1
  160. package/dist/watcher.d.ts +38 -20
  161. package/dist/watcher.d.ts.map +1 -1
  162. package/dist/watcher.js +155 -143
  163. package/dist/watcher.js.map +1 -1
  164. package/dist/watcher.test.js +103 -0
  165. package/dist/watcher.test.js.map +1 -1
  166. package/package.json +1 -1
  167. package/src/bin/sync-runner-company.ts +350 -0
  168. package/src/bin/sync-runner-events.ts +25 -0
  169. package/src/bin/sync-runner-planning.ts +121 -0
  170. package/src/bin/sync-runner-rollup.ts +72 -0
  171. package/src/bin/sync-runner-telemetry.ts +8 -0
  172. package/src/bin/sync-runner-watch-loop.ts +443 -0
  173. package/src/bin/sync-runner-watch-routes.ts +86 -0
  174. package/src/bin/sync-runner.test.ts +298 -11
  175. package/src/bin/sync-runner.ts +99 -1054
  176. package/src/cli/reindex.test.ts +41 -3
  177. package/src/cli/reindex.ts +35 -19
  178. package/src/cli/rescue-classify-ordering.test.ts +81 -0
  179. package/src/cli/rescue-core.ts +400 -165
  180. package/src/cli/share.test.ts +38 -0
  181. package/src/cli/share.ts +420 -693
  182. package/src/cli/sync.test.ts +460 -1
  183. package/src/cli/sync.ts +788 -825
  184. package/src/cognito-auth.test.ts +77 -0
  185. package/src/cognito-auth.ts +73 -11
  186. package/src/daemon-worker.ts +3 -3
  187. package/src/index.ts +8 -0
  188. package/src/journal.test.ts +72 -0
  189. package/src/journal.ts +95 -8
  190. package/src/machine-auth.test.ts +64 -2
  191. package/src/object-io.test.ts +142 -0
  192. package/src/object-io.ts +183 -31
  193. package/src/operation-lock.test.ts +63 -4
  194. package/src/operation-lock.ts +99 -31
  195. package/src/personal-vault.test.ts +42 -0
  196. package/src/personal-vault.ts +8 -2
  197. package/src/prefix-coalesce.test.ts +71 -1
  198. package/src/prefix-coalesce.ts +155 -30
  199. package/src/remote-pull.test.ts +235 -1
  200. package/src/remote-pull.ts +106 -18
  201. package/src/s3.test.ts +126 -0
  202. package/src/s3.ts +237 -122
  203. package/src/scope-shrink.ts +6 -3
  204. package/src/skill-telemetry.test.ts +109 -0
  205. package/src/skill-telemetry.ts +82 -14
  206. package/src/sync/event-sync.test.ts +75 -0
  207. package/src/sync/event-sync.ts +54 -1
  208. package/src/sync/metrics.test.ts +81 -0
  209. package/src/sync/metrics.ts +59 -4
  210. package/src/sync/pull-scope.ts +23 -7
  211. package/src/sync/push-receiver.test.ts +73 -1
  212. package/src/sync/push-receiver.ts +56 -20
  213. package/src/sync-core.ts +58 -0
  214. package/src/telemetry.test.ts +85 -0
  215. package/src/telemetry.ts +69 -6
  216. package/src/types.ts +8 -0
  217. package/src/vault-client.test.ts +74 -0
  218. package/src/vault-client.ts +395 -43
  219. package/src/watcher.test.ts +117 -0
  220. package/src/watcher.ts +215 -174
@@ -8,6 +8,7 @@
8
8
 
9
9
  import type { ClientInfo, VaultServiceConfig } from "./types.js";
10
10
  import { buildClientHeaders } from "./client-info.js";
11
+ import { z } from "zod";
11
12
 
12
13
  // ---------------------------------------------------------------------------
13
14
  // Error classes
@@ -444,6 +445,306 @@ async function sleep(ms: number): Promise<void> {
444
445
  return new Promise((resolve) => setTimeout(resolve, ms));
445
446
  }
446
447
 
448
+ // ---------------------------------------------------------------------------
449
+ // Response schemas
450
+ // ---------------------------------------------------------------------------
451
+
452
+ type VaultResponseSchema<T> = z.ZodType<T>;
453
+
454
+ const membershipRoleSchema = z.enum(["owner", "admin", "member", "guest"]);
455
+ const membershipStatusSchema = z.enum(["pending", "active", "revoked"]);
456
+ const grantPermissionSchema = z.enum(["read", "write", "admin"]);
457
+ const grantSourceSchema = z.enum(["person", "email", "group", "open"]);
458
+ const presignOpSchema = z.enum(["get", "put", "delete"]);
459
+ const syncModeSchema = z.enum(["shared", "all", "custom"]);
460
+ const vendPurposeSchema = z.enum(["sync", "browse"]);
461
+ const vaultOperationSchema = z.enum(["read-only", "read-write", "staged-write"]);
462
+
463
+ // Membership/entity rows are intentionally permissive on field optionality:
464
+ // current command tests and server flows use short rows outside sync decisions.
465
+ const membershipSchema = z
466
+ .object({
467
+ membershipKey: z.string().optional(),
468
+ personUid: z.string().optional(),
469
+ companyUid: z.string().optional(),
470
+ role: membershipRoleSchema.optional(),
471
+ status: membershipStatusSchema.optional(),
472
+ allowedPrefixes: z.array(z.string()).optional(),
473
+ inviteToken: z.string().optional(),
474
+ invitedBy: z.string().optional(),
475
+ invitedAt: z.string().optional(),
476
+ acceptedAt: z.string().optional(),
477
+ revokedAt: z.string().optional(),
478
+ createdAt: z.string().optional(),
479
+ updatedAt: z.string().optional(),
480
+ })
481
+ .strip() as unknown as VaultResponseSchema<Membership>;
482
+
483
+ const entityInfoSchema = z
484
+ .object({
485
+ uid: z.string(),
486
+ slug: z.string(),
487
+ type: z.string(),
488
+ name: z.string().optional(),
489
+ bucketName: z.string().optional(),
490
+ status: z.string(),
491
+ createdAt: z.string().optional(),
492
+ })
493
+ .strip() as unknown as VaultResponseSchema<EntityInfo>;
494
+
495
+ const pendingInviteByEmailSchema: VaultResponseSchema<PendingInviteByEmail> = z
496
+ .object({
497
+ membershipKey: z.string(),
498
+ companyUid: z.string(),
499
+ role: membershipRoleSchema,
500
+ inviteToken: z.string().optional(),
501
+ invitedBy: z.string(),
502
+ invitedAt: z.string(),
503
+ })
504
+ .strip();
505
+
506
+ const explicitGrantSchema: VaultResponseSchema<ExplicitGrant> = z
507
+ .object({
508
+ companyUid: z.string(),
509
+ path: z.string(),
510
+ permission: grantPermissionSchema,
511
+ source: grantSourceSchema,
512
+ })
513
+ .strip();
514
+
515
+ const vaultListedObjectSchema: VaultResponseSchema<VaultListedObject> = z
516
+ .object({
517
+ key: z.string(),
518
+ size: z.number(),
519
+ lastModified: z.string().nullable(),
520
+ etag: z.string().nullable(),
521
+ permission: grantPermissionSchema,
522
+ })
523
+ .strip();
524
+
525
+ const presignResultRowSchema: VaultResponseSchema<PresignResultRow> = z
526
+ .object({
527
+ key: z.string(),
528
+ op: presignOpSchema,
529
+ url: z.string().optional(),
530
+ headers: z.record(z.string(), z.string()).optional(),
531
+ expiresIn: z.number().optional(),
532
+ expiresAt: z.string().optional(),
533
+ error: z.string().optional(),
534
+ code: z.string().optional(),
535
+ })
536
+ .strip();
537
+
538
+ const membershipSyncConfigSchema: VaultResponseSchema<MembershipSyncConfig> = z
539
+ .object({
540
+ membershipId: z.string(),
541
+ syncMode: syncModeSchema,
542
+ customPaths: z.array(z.string()).optional(),
543
+ isDefault: z.boolean(),
544
+ updatedAt: z.string().optional(),
545
+ updatedBy: z.string().optional(),
546
+ })
547
+ .strip();
548
+
549
+ const vendCredentialsSchema: VaultResponseSchema<VendCredentials> = z
550
+ .object({
551
+ accessKeyId: z.string(),
552
+ secretAccessKey: z.string(),
553
+ sessionToken: z.string(),
554
+ expiration: z.string(),
555
+ })
556
+ .strip();
557
+
558
+ const stsCredentialsSchema: VaultResponseSchema<StsChildCredentials> = z
559
+ .object({
560
+ accessKeyId: z.string(),
561
+ secretAccessKey: z.string(),
562
+ sessionToken: z.string(),
563
+ })
564
+ .strip();
565
+
566
+ const telemetryOptInResponseSchema: VaultResponseSchema<TelemetryOptInResponse> =
567
+ z
568
+ .object({
569
+ enabled: z.boolean().default(false),
570
+ updatedAt: z.string().nullable().default(null),
571
+ })
572
+ .strip();
573
+
574
+ const usageIngestResultSchema: VaultResponseSchema<UsageIngestResult> = z
575
+ .object({
576
+ ok: z.boolean(),
577
+ written: z.number(),
578
+ skipped: z.array(
579
+ z
580
+ .object({
581
+ index: z.number(),
582
+ code: z.string(),
583
+ error: z.string(),
584
+ })
585
+ .strip(),
586
+ ),
587
+ })
588
+ .strip();
589
+
590
+ const createInviteResponseSchema: VaultResponseSchema<CreateInviteResult> = z
591
+ .object({
592
+ membership: membershipSchema,
593
+ inviteToken: z.string(),
594
+ })
595
+ .strip();
596
+
597
+ const acceptInviteResponseSchema: VaultResponseSchema<AcceptInviteResult> = z
598
+ .object({
599
+ membership: membershipSchema,
600
+ })
601
+ .strip();
602
+
603
+ const membershipsResponseSchema: VaultResponseSchema<{ memberships: Membership[] }> =
604
+ z
605
+ .object({
606
+ memberships: z.array(membershipSchema),
607
+ })
608
+ .strip();
609
+
610
+ const pendingInvitesByEmailResponseSchema: VaultResponseSchema<{
611
+ invites?: PendingInviteByEmail[];
612
+ }> = z
613
+ .object({
614
+ invites: z.array(pendingInviteByEmailSchema).optional(),
615
+ })
616
+ .strip();
617
+
618
+ const membersResponseSchema: VaultResponseSchema<{ members: Membership[] }> = z
619
+ .object({
620
+ members: z.array(membershipSchema),
621
+ })
622
+ .strip();
623
+
624
+ const membershipResponseSchema: VaultResponseSchema<{ membership: Membership }> =
625
+ z
626
+ .object({
627
+ membership: membershipSchema,
628
+ })
629
+ .strip();
630
+
631
+ const invitesResponseSchema: VaultResponseSchema<{ invites: Membership[] }> = z
632
+ .object({
633
+ invites: z.array(membershipSchema),
634
+ })
635
+ .strip();
636
+
637
+ const explicitGrantsResponseSchema: VaultResponseSchema<{
638
+ grants?: ExplicitGrant[];
639
+ computedAt: string;
640
+ }> = z
641
+ .object({
642
+ grants: z.array(explicitGrantSchema).optional(),
643
+ computedAt: z.string(),
644
+ })
645
+ .strip();
646
+
647
+ const listFilesResponseSchema: VaultResponseSchema<{
648
+ objects: VaultListedObject[];
649
+ cursor: string | null;
650
+ truncated: boolean;
651
+ }> = z
652
+ .object({
653
+ objects: z.array(vaultListedObjectSchema).default([]),
654
+ cursor: z.string().nullable().default(null),
655
+ truncated: z.boolean().default(false),
656
+ })
657
+ .strip();
658
+
659
+ const presignResponseSchema: VaultResponseSchema<{
660
+ results: PresignResultRow[];
661
+ expiresAt: string;
662
+ }> = z
663
+ .object({
664
+ results: z.array(presignResultRowSchema),
665
+ expiresAt: z.string(),
666
+ })
667
+ .strip();
668
+
669
+ const entityResponseSchema: VaultResponseSchema<{ entity: EntityInfo }> = z
670
+ .object({
671
+ entity: entityInfoSchema,
672
+ })
673
+ .strip();
674
+
675
+ const checkSlugMeResponseSchema: VaultResponseSchema<{
676
+ available: boolean;
677
+ conflictingCompanyUid?: string;
678
+ }> = z
679
+ .object({
680
+ available: z.boolean(),
681
+ conflictingCompanyUid: z.string().optional(),
682
+ })
683
+ .strip();
684
+
685
+ const createEntityResponseSchema: VaultResponseSchema<CreateEntityResult> = z
686
+ .object({
687
+ entity: entityInfoSchema,
688
+ })
689
+ .strip();
690
+
691
+ const entitiesResponseSchema: VaultResponseSchema<{ entities?: EntityInfo[] }> =
692
+ z
693
+ .object({
694
+ entities: z.array(entityInfoSchema).optional(),
695
+ })
696
+ .strip();
697
+
698
+ const provisionBucketResponseSchema: VaultResponseSchema<{
699
+ bucketName: string;
700
+ kmsKeyId: string;
701
+ }> = z
702
+ .object({
703
+ bucketName: z.string(),
704
+ kmsKeyId: z.string(),
705
+ })
706
+ .strip();
707
+
708
+ const vendResponseSchema: VaultResponseSchema<VendResult> = z
709
+ .object({
710
+ credentials: vendCredentialsSchema,
711
+ paths: z.array(z.string()),
712
+ operations: vaultOperationSchema,
713
+ purpose: vendPurposeSchema,
714
+ policySize: z.number(),
715
+ requestId: z.string().optional(),
716
+ })
717
+ .strip();
718
+
719
+ const stsVendResponseSchema: VaultResponseSchema<{
720
+ credentials: StsChildCredentials;
721
+ expiresAt: string;
722
+ }> = z
723
+ .object({
724
+ credentials: stsCredentialsSchema,
725
+ expiresAt: z.string(),
726
+ })
727
+ .strip();
728
+
729
+ const vendChildResponseSchema: VaultResponseSchema<VendChildResult> = z
730
+ .object({
731
+ credentials: stsCredentialsSchema,
732
+ sessionName: z.string(),
733
+ expiresAt: z.string(),
734
+ })
735
+ .strip();
736
+
737
+ const emptyObjectResponseSchema = z.object({}).strip();
738
+
739
+ function summarizeZodIssues(issues: readonly z.core.$ZodIssue[]): string {
740
+ return issues
741
+ .map((issue) => {
742
+ const path = issue.path.length > 0 ? issue.path.join(".") : "<root>";
743
+ return `${path}: ${issue.message}`;
744
+ })
745
+ .join("; ");
746
+ }
747
+
447
748
  // ---------------------------------------------------------------------------
448
749
  // VaultClient
449
750
  // ---------------------------------------------------------------------------
@@ -472,17 +773,19 @@ export class VaultClient {
472
773
  // -- Membership operations ------------------------------------------------
473
774
 
474
775
  async createInvite(input: CreateInviteInput): Promise<CreateInviteResult> {
475
- const data = await this.post<{ membership: Membership; inviteToken: string }>(
776
+ const data = await this.post(
476
777
  "/membership/invite",
477
778
  input,
779
+ createInviteResponseSchema,
478
780
  );
479
781
  return data;
480
782
  }
481
783
 
482
784
  async acceptInvite(token: string, personUid: string): Promise<AcceptInviteResult> {
483
- const data = await this.post<{ membership: Membership }>(
785
+ const data = await this.post(
484
786
  "/membership/accept",
485
787
  { token, personUid },
788
+ acceptInviteResponseSchema,
486
789
  );
487
790
  return data;
488
791
  }
@@ -494,7 +797,11 @@ export class VaultClient {
494
797
  * alone without an extra DDB read, and the caller already knows it.)
495
798
  */
496
799
  async revokeMembership(membershipKey: string, companyUid: string): Promise<void> {
497
- await this.post("/membership/revoke", { membershipKey, companyUid });
800
+ await this.post(
801
+ "/membership/revoke",
802
+ { membershipKey, companyUid },
803
+ emptyObjectResponseSchema,
804
+ );
498
805
  }
499
806
 
500
807
  /**
@@ -513,7 +820,7 @@ export class VaultClient {
513
820
  * Backed by `GET /membership/me` (see hq-pro ADR-0002).
514
821
  */
515
822
  async listMyMemberships(): Promise<Membership[]> {
516
- const data = await this.get<{ memberships: Membership[] }>("/membership/me");
823
+ const data = await this.get("/membership/me", membershipsResponseSchema);
517
824
  return data.memberships;
518
825
  }
519
826
 
@@ -527,8 +834,9 @@ export class VaultClient {
527
834
  * person exists.
528
835
  */
529
836
  async listMyPendingInvitesByEmail(): Promise<PendingInviteByEmail[]> {
530
- const data = await this.get<{ invites: PendingInviteByEmail[] }>(
837
+ const data = await this.get(
531
838
  "/membership/pending-by-email",
839
+ pendingInvitesByEmailResponseSchema,
532
840
  );
533
841
  return data.invites ?? [];
534
842
  }
@@ -539,27 +847,34 @@ export class VaultClient {
539
847
  * have no pending invites. The caller's email is inferred from the JWT.
540
848
  */
541
849
  async claimPendingInvitesByEmail(personUid: string): Promise<void> {
542
- await this.post("/membership/claim-by-email", { personUid });
850
+ await this.post(
851
+ "/membership/claim-by-email",
852
+ { personUid },
853
+ emptyObjectResponseSchema,
854
+ );
543
855
  }
544
856
 
545
857
  async listMembersOfCompany(companyUid: string): Promise<Membership[]> {
546
- const data = await this.get<{ members: Membership[] }>(
858
+ const data = await this.get(
547
859
  `/membership/company/${encodeURIComponent(companyUid)}`,
860
+ membersResponseSchema,
548
861
  );
549
862
  return data.members;
550
863
  }
551
864
 
552
865
  async updateRole(input: UpdateRoleInput): Promise<Membership> {
553
- const data = await this.post<{ membership: Membership }>(
866
+ const data = await this.post(
554
867
  "/membership/role",
555
868
  input,
869
+ membershipResponseSchema,
556
870
  );
557
871
  return data.membership;
558
872
  }
559
873
 
560
874
  async listPendingInvites(companyUid: string): Promise<Membership[]> {
561
- const data = await this.get<{ invites: Membership[] }>(
875
+ const data = await this.get(
562
876
  `/membership/company/${encodeURIComponent(companyUid)}/pending`,
877
+ invitesResponseSchema,
563
878
  );
564
879
  return data.invites;
565
880
  }
@@ -581,8 +896,9 @@ export class VaultClient {
581
896
  * state without catching errors.
582
897
  */
583
898
  async listMyExplicitGrants(companyUid: string): Promise<ExplicitGrant[]> {
584
- const data = await this.get<{ grants: ExplicitGrant[]; computedAt: string }>(
899
+ const data = await this.get(
585
900
  `/v1/files/grants?company=${encodeURIComponent(companyUid)}`,
901
+ explicitGrantsResponseSchema,
586
902
  );
587
903
  return data.grants ?? [];
588
904
  }
@@ -608,15 +924,14 @@ export class VaultClient {
608
924
  const qs = new URLSearchParams({ company: companyUid });
609
925
  if (prefix) qs.set("prefix", prefix);
610
926
  if (cursor) qs.set("cursor", cursor);
611
- const data = await this.get<{
612
- objects?: VaultListedObject[];
613
- cursor?: string | null;
614
- truncated?: boolean;
615
- }>(`/v1/files/list?${qs.toString()}`);
927
+ const data = await this.get(
928
+ `/v1/files/list?${qs.toString()}`,
929
+ listFilesResponseSchema,
930
+ );
616
931
  return {
617
- objects: data.objects ?? [],
618
- cursor: data.cursor ?? null,
619
- truncated: data.truncated ?? false,
932
+ objects: data.objects,
933
+ cursor: data.cursor,
934
+ truncated: data.truncated,
620
935
  };
621
936
  }
622
937
 
@@ -632,7 +947,7 @@ export class VaultClient {
632
947
  expiresIn?: number;
633
948
  keys: PresignKeyInput[];
634
949
  }): Promise<{ results: PresignResultRow[]; expiresAt: string }> {
635
- return this.post<{ results: PresignResultRow[]; expiresAt: string }>(
950
+ return this.post(
636
951
  `/v1/files/presign`,
637
952
  {
638
953
  company: input.companyUid,
@@ -640,6 +955,7 @@ export class VaultClient {
640
955
  ...(input.expiresIn ? { expiresIn: input.expiresIn } : {}),
641
956
  keys: input.keys,
642
957
  },
958
+ presignResponseSchema,
643
959
  );
644
960
  }
645
961
 
@@ -659,8 +975,9 @@ export class VaultClient {
659
975
  async getMembershipSyncConfig(
660
976
  membershipId: string,
661
977
  ): Promise<MembershipSyncConfig> {
662
- return this.get<MembershipSyncConfig>(
978
+ return this.get(
663
979
  `/v1/memberships/${encodeURIComponent(membershipId)}/sync-config`,
980
+ membershipSyncConfigSchema,
664
981
  );
665
982
  }
666
983
 
@@ -678,10 +995,11 @@ export class VaultClient {
678
995
  membershipId: string,
679
996
  partial: SetMembershipSyncConfigInput,
680
997
  ): Promise<MembershipSyncConfig> {
681
- return this.request<MembershipSyncConfig>(
998
+ return this.request(
682
999
  "PUT",
683
1000
  `/v1/memberships/${encodeURIComponent(membershipId)}/sync-config`,
684
1001
  partial,
1002
+ membershipSyncConfigSchema,
685
1003
  );
686
1004
  }
687
1005
 
@@ -689,7 +1007,10 @@ export class VaultClient {
689
1007
 
690
1008
  readonly entity = {
691
1009
  get: async (uid: string): Promise<EntityInfo> => {
692
- const data = await this.get<{ entity: EntityInfo }>(`/entity/${encodeURIComponent(uid)}`);
1010
+ const data = await this.get(
1011
+ `/entity/${encodeURIComponent(uid)}`,
1012
+ entityResponseSchema,
1013
+ );
693
1014
  return data.entity;
694
1015
  },
695
1016
 
@@ -705,8 +1026,9 @@ export class VaultClient {
705
1026
  * global lookup (admin tooling) should still use this method.
706
1027
  */
707
1028
  findBySlug: async (type: string, slug: string): Promise<EntityInfo> => {
708
- const data = await this.get<{ entity: EntityInfo }>(
1029
+ const data = await this.get(
709
1030
  `/entity/by-slug/${encodeURIComponent(type)}/${encodeURIComponent(slug)}`,
1031
+ entityResponseSchema,
710
1032
  );
711
1033
  return data.entity;
712
1034
  },
@@ -727,25 +1049,24 @@ export class VaultClient {
727
1049
  type: string,
728
1050
  slug: string,
729
1051
  ): Promise<EntityInfo | null> => {
730
- const check = await this.get<{
731
- available: boolean;
732
- conflictingCompanyUid?: string;
733
- }>(
1052
+ const check = await this.get(
734
1053
  `/entity/check-slug/me?type=${encodeURIComponent(type)}&slug=${encodeURIComponent(slug)}`,
1054
+ checkSlugMeResponseSchema,
735
1055
  );
736
1056
  if (check.available || !check.conflictingCompanyUid) return null;
737
1057
  return this.entity.get(check.conflictingCompanyUid);
738
1058
  },
739
1059
 
740
1060
  create: async (input: CreateEntityInput): Promise<EntityInfo> => {
741
- const data = await this.post<CreateEntityResult>("/entity", input);
1061
+ const data = await this.post("/entity", input, createEntityResponseSchema);
742
1062
  return data.entity;
743
1063
  },
744
1064
 
745
1065
  /** Return every entity of `type` owned by the caller (scoped by JWT). */
746
1066
  listByType: async (type: string): Promise<EntityInfo[]> => {
747
- const data = await this.get<{ entities: EntityInfo[] }>(
1067
+ const data = await this.get(
748
1068
  `/entity/by-type/${encodeURIComponent(type)}`,
1069
+ entitiesResponseSchema,
749
1070
  );
750
1071
  return data.entities ?? [];
751
1072
  },
@@ -789,9 +1110,10 @@ export class VaultClient {
789
1110
  // -- Provisioning operations (VLT-2) -----------------------------------------
790
1111
 
791
1112
  async provisionBucket(companyUid: string): Promise<{ bucketName: string; kmsKeyId: string }> {
792
- const data = await this.post<{ bucketName: string; kmsKeyId: string }>(
1113
+ const data = await this.post(
793
1114
  "/provision/bucket",
794
1115
  { companyUid },
1116
+ provisionBucketResponseSchema,
795
1117
  );
796
1118
  return data;
797
1119
  }
@@ -820,7 +1142,7 @@ export class VaultClient {
820
1142
  * objects without ever materialising them under `companies/{co}/`.
821
1143
  */
822
1144
  async vend(input: VendInput): Promise<VendResult> {
823
- return this.post<VendResult>("/vend", input);
1145
+ return this.post("/vend", input, vendResponseSchema);
824
1146
  }
825
1147
 
826
1148
  // -- STS operations (VLT-8) -----------------------------------------------
@@ -848,7 +1170,7 @@ export class VaultClient {
848
1170
  };
849
1171
  expiresAt: string;
850
1172
  }> => {
851
- return this.post("/sts/vend", input);
1173
+ return this.post("/sts/vend", input, stsVendResponseSchema);
852
1174
  },
853
1175
  /**
854
1176
  * Vend task-scoped child credentials strictly narrower than the caller's
@@ -864,14 +1186,14 @@ export class VaultClient {
864
1186
  * the parent task for incident response.
865
1187
  */
866
1188
  vendChild: async (input: VendChildInput): Promise<VendChildResult> => {
867
- const data = await this.post<VendChildResult>("/sts/vend-child", input);
1189
+ const data = await this.post("/sts/vend-child", input, vendChildResponseSchema);
868
1190
  return data;
869
1191
  },
870
1192
  vendSelf: async (input: { personUid: string; durationSeconds?: number }): Promise<{
871
1193
  credentials: { accessKeyId: string; secretAccessKey: string; sessionToken: string };
872
1194
  expiresAt: string;
873
1195
  }> => {
874
- return this.post("/sts/vend-self", input);
1196
+ return this.post("/sts/vend-self", input, stsVendResponseSchema);
875
1197
  },
876
1198
  };
877
1199
 
@@ -891,7 +1213,7 @@ export class VaultClient {
891
1213
  * either yes or no; see `./telemetry.ts::collectAndSendTelemetry`.
892
1214
  */
893
1215
  async getTelemetryOptIn(): Promise<TelemetryOptInResponse> {
894
- return this.get<TelemetryOptInResponse>("/v1/usage/opt-in");
1216
+ return this.get("/v1/usage/opt-in", telemetryOptInResponseSchema);
895
1217
  }
896
1218
 
897
1219
  /**
@@ -903,7 +1225,7 @@ export class VaultClient {
903
1225
  * 1 MiB pre-flush cap which is the binding limit in practice.
904
1226
  */
905
1227
  async postUsage(batch: UsageBatch): Promise<UsageIngestResult> {
906
- return this.post<UsageIngestResult>("/v1/usage", batch);
1228
+ return this.post("/v1/usage", batch, usageIngestResultSchema);
907
1229
  }
908
1230
 
909
1231
  /**
@@ -916,20 +1238,29 @@ export class VaultClient {
916
1238
  async postSkillInvocations(
917
1239
  batch: SkillInvocationBatch,
918
1240
  ): Promise<SkillInvocationIngestResult> {
919
- return this.post<SkillInvocationIngestResult>("/v1/skill-invocations", batch);
1241
+ return this.post("/v1/skill-invocations", batch, usageIngestResultSchema);
920
1242
  }
921
1243
 
922
1244
  // -- HTTP primitives with retry -------------------------------------------
923
1245
 
924
- private async get<T>(path: string): Promise<T> {
925
- return this.request<T>("GET", path);
1246
+ private async get<T>(path: string, schema: VaultResponseSchema<T>): Promise<T> {
1247
+ return this.request("GET", path, undefined, schema);
926
1248
  }
927
1249
 
928
- private async post<T>(path: string, body?: unknown): Promise<T> {
929
- return this.request<T>("POST", path, body);
1250
+ private async post<T>(
1251
+ path: string,
1252
+ body: unknown | undefined,
1253
+ schema: VaultResponseSchema<T>,
1254
+ ): Promise<T> {
1255
+ return this.request("POST", path, body, schema);
930
1256
  }
931
1257
 
932
- private async request<T>(method: string, path: string, body?: unknown): Promise<T> {
1258
+ private async request<T>(
1259
+ method: string,
1260
+ path: string,
1261
+ body: unknown,
1262
+ schema: VaultResponseSchema<T>,
1263
+ ): Promise<T> {
933
1264
  let lastError: Error | undefined;
934
1265
 
935
1266
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
@@ -962,7 +1293,28 @@ export class VaultClient {
962
1293
 
963
1294
  if (res.ok) {
964
1295
  if (res.status === 204) return undefined as T;
965
- return (await res.json()) as T;
1296
+ const responseBody = await res.text();
1297
+ let decoded: unknown;
1298
+ try {
1299
+ decoded = JSON.parse(responseBody);
1300
+ } catch (err) {
1301
+ throw new VaultClientError(
1302
+ `Invalid JSON response from vault-service for ${method} ${path}`,
1303
+ 502,
1304
+ err instanceof Error ? err.message : responseBody,
1305
+ );
1306
+ }
1307
+
1308
+ const parsed = schema.safeParse(decoded);
1309
+ if (!parsed.success) {
1310
+ throw new VaultClientError(
1311
+ `Invalid response from vault-service for ${method} ${path}: ${summarizeZodIssues(parsed.error.issues)}`,
1312
+ 502,
1313
+ responseBody,
1314
+ );
1315
+ }
1316
+
1317
+ return parsed.data;
966
1318
  }
967
1319
 
968
1320
  const responseBody = await res.text();