@mycadet/assistant 0.12.10 → 0.13.0

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 (180) hide show
  1. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
  2. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
  3. package/node_modules/@mycadet/ces-client/src/http-credentials.ts +18 -1
  4. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
  5. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
  6. package/node_modules/@mycadet/gateway-client/src/gateway-ipc-contracts.ts +0 -20
  7. package/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +10 -10
  8. package/node_modules/@mycadet/service-contracts/src/ingress.ts +3 -3
  9. package/openapi.yaml +2 -708
  10. package/package.json +2 -3
  11. package/scripts/sync-web-search-catalog.ts +1 -2
  12. package/src/__tests__/cloud-profile-guard.test.ts +41 -5
  13. package/src/__tests__/config-schema.test.ts +6 -38
  14. package/src/__tests__/conversation-queue.test.ts +45 -6
  15. package/src/__tests__/db-readiness-http-gate.test.ts +0 -6
  16. package/src/__tests__/persist-onboarding-artifacts.test.ts +0 -1
  17. package/src/__tests__/persona-resolver.test.ts +0 -51
  18. package/src/__tests__/provider-cloud-proxy-integration.test.ts +2 -110
  19. package/src/__tests__/run-due-schedules.test.ts +0 -4
  20. package/src/__tests__/schedule-store.test.ts +0 -12
  21. package/src/__tests__/stt-stream-session.test.ts +4 -4
  22. package/src/__tests__/telephony-tts-guard.test.ts +2 -50
  23. package/src/__tests__/voice-config-update.test.ts +12 -167
  24. package/src/__tests__/web-search-catalog-parity.test.ts +0 -2
  25. package/src/__tests__/workspace-migration-130-speech-mode-to-provider.test.ts +9 -7
  26. package/src/__tests__/workspace-migration-132-web-search-mode-to-provider.test.ts +6 -6
  27. package/src/calls/__tests__/voice-session-bridge.test.ts +1 -4
  28. package/src/calls/resolve-call-tts-provider.ts +6 -25
  29. package/src/calls/telephony-credential-preflight.ts +0 -10
  30. package/src/calls/telephony-tts-capability.ts +1 -17
  31. package/src/cli/commands/tts.ts +1 -2
  32. package/src/cli/index.help.ts +0 -2
  33. package/src/cli/program.ts +0 -2
  34. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +4 -42
  35. package/src/config/schemas/__tests__/stt.test.ts +10 -14
  36. package/src/config/schemas/llm.ts +3 -5
  37. package/src/config/schemas/services.ts +5 -6
  38. package/src/config/schemas/stt.ts +1 -3
  39. package/src/config/schemas/tts.ts +1 -30
  40. package/src/daemon/daemon-readiness.ts +4 -29
  41. package/src/daemon/lifecycle.ts +0 -31
  42. package/src/daemon/providers-setup.ts +0 -8
  43. package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
  44. package/src/heartbeat/heartbeat-service.ts +1 -66
  45. package/src/ipc/__tests__/db-readiness-ipc-gate.test.ts +1 -9
  46. package/src/live-voice/__tests__/live-voice-credential-preflight.test.ts +0 -44
  47. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +4 -6
  48. package/src/live-voice/__tests__/live-voice-stt.test.ts +1 -1
  49. package/src/live-voice/live-voice-credential-preflight.ts +9 -33
  50. package/src/live-voice/live-voice-session.ts +10 -6
  51. package/src/live-voice/live-voice-tts.ts +1 -8
  52. package/src/plugins/defaults/memory/worker.ts +3 -3
  53. package/src/prompts/persona-resolver.ts +0 -35
  54. package/src/providers/__tests__/search-provider-catalog.test.ts +5 -8
  55. package/src/providers/__tests__/voice-error-copy.test.ts +9 -10
  56. package/src/providers/search-provider-catalog.ts +7 -39
  57. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +3 -47
  58. package/src/providers/speech-to-text/__tests__/resolve.test.ts +6 -372
  59. package/src/providers/speech-to-text/provider-catalog.ts +2 -73
  60. package/src/providers/speech-to-text/resolve.ts +3 -120
  61. package/src/runtime/http-router-types.ts +1 -3
  62. package/src/runtime/http-server.ts +1 -1
  63. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +13 -11
  64. package/src/runtime/routes/__tests__/tts-routes.test.ts +2 -6
  65. package/src/runtime/routes/conversation-query-routes.ts +0 -96
  66. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +7 -7
  67. package/src/runtime/routes/index.ts +0 -4
  68. package/src/runtime/routes/live-voice-routes.test.ts +4 -11
  69. package/src/runtime/routes/live-voice-routes.ts +8 -11
  70. package/src/runtime/routes/secret-routes.ts +0 -6
  71. package/src/runtime/routes/tts-routes.ts +0 -38
  72. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1 -7
  73. package/src/schedule/__tests__/worker-mcp-readiness.test.ts +0 -4
  74. package/src/schedule/schedule-store.ts +0 -7
  75. package/src/schedule/scheduler.ts +0 -5
  76. package/src/schedule/worker.ts +1 -1
  77. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +2 -10
  78. package/src/stt/daemon-batch-transcriber.ts +4 -38
  79. package/src/stt/types.ts +1 -9
  80. package/src/tools/network/__tests__/web-search.test.ts +3 -250
  81. package/src/tools/network/web-search.ts +4 -165
  82. package/src/tts/__tests__/provider-resolution.test.ts +0 -5
  83. package/src/tts/provider-catalog.ts +0 -4
  84. package/src/tts/types.ts +0 -1
  85. package/src/__tests__/cloud-speech-defaults.test.ts +0 -330
  86. package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1708
  87. package/src/__tests__/migration-export-http.test.ts +0 -557
  88. package/src/__tests__/migration-export-streaming.test.ts +0 -350
  89. package/src/__tests__/migration-export-to-gcs.test.ts +0 -706
  90. package/src/__tests__/migration-import-commit-http.test.ts +0 -1176
  91. package/src/__tests__/migration-import-from-gcs.test.ts +0 -534
  92. package/src/__tests__/migration-import-from-path.test.ts +0 -324
  93. package/src/__tests__/migration-import-from-url.test.ts +0 -588
  94. package/src/__tests__/migration-import-preflight-http.test.ts +0 -739
  95. package/src/__tests__/migration-jobs-status.test.ts +0 -139
  96. package/src/__tests__/migration-parity-persistence.test.ts +0 -1891
  97. package/src/__tests__/migration-transport.test.ts +0 -1089
  98. package/src/__tests__/migration-validate-http.test.ts +0 -634
  99. package/src/__tests__/migration-wizard.test.ts +0 -1395
  100. package/src/__tests__/rebind-secrets-screen.test.ts +0 -876
  101. package/src/__tests__/sanitize-config-for-transfer.test.ts +0 -259
  102. package/src/__tests__/transfer-progress-screen.test.ts +0 -1217
  103. package/src/__tests__/validation-results-screen.test.ts +0 -1130
  104. package/src/__tests__/vbundle-pax-and-symlink.test.ts +0 -147
  105. package/src/background-wake/background-wake-routes.test.ts +0 -906
  106. package/src/background-wake/cloud-client.test.ts +0 -328
  107. package/src/background-wake/cloud-client.ts +0 -196
  108. package/src/background-wake/next-wake.test.ts +0 -335
  109. package/src/background-wake/next-wake.ts +0 -188
  110. package/src/background-wake/publisher.ts +0 -93
  111. package/src/background-wake/wake-intent-hooks.test.ts +0 -234
  112. package/src/cli/commands/__tests__/backup.test.ts +0 -363
  113. package/src/cli/commands/backup.help.ts +0 -180
  114. package/src/cli/commands/backup.ts +0 -398
  115. package/src/cloud/cloud-speech.test.ts +0 -335
  116. package/src/cloud/cloud-speech.ts +0 -300
  117. package/src/config/cloud-speech-defaults.ts +0 -300
  118. package/src/config/sanitize-for-transfer.ts +0 -111
  119. package/src/live-voice/__tests__/live-voice-cloud-speech-fallback.test.ts +0 -250
  120. package/src/providers/speech-to-text/__tests__/cadet-cloud-realtime.test.ts +0 -457
  121. package/src/providers/speech-to-text/__tests__/cadet-cloud.test.ts +0 -271
  122. package/src/providers/speech-to-text/__tests__/cadet-speech-relay-connection.test.ts +0 -126
  123. package/src/providers/speech-to-text/cadet-cloud-flux-realtime.ts +0 -309
  124. package/src/providers/speech-to-text/cadet-cloud-realtime.ts +0 -315
  125. package/src/providers/speech-to-text/cadet-cloud.ts +0 -108
  126. package/src/providers/speech-to-text/cadet-speech-relay-connection.ts +0 -176
  127. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +0 -254
  128. package/src/runtime/migrations/__tests__/job-registry.test.ts +0 -346
  129. package/src/runtime/migrations/__tests__/origin-mode.test.ts +0 -62
  130. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +0 -184
  131. package/src/runtime/migrations/__tests__/staged-import-path.test.ts +0 -110
  132. package/src/runtime/migrations/__tests__/v1-test-helpers.ts +0 -112
  133. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +0 -285
  134. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +0 -126
  135. package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +0 -253
  136. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +0 -208
  137. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +0 -485
  138. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +0 -349
  139. package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +0 -196
  140. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +0 -551
  141. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +0 -428
  142. package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +0 -220
  143. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +0 -1823
  144. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +0 -598
  145. package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +0 -463
  146. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -538
  147. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +0 -515
  148. package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +0 -453
  149. package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +0 -319
  150. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +0 -240
  151. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +0 -473
  152. package/src/runtime/migrations/gcs-signed-url.ts +0 -190
  153. package/src/runtime/migrations/job-registry.ts +0 -297
  154. package/src/runtime/migrations/migration-transport.ts +0 -612
  155. package/src/runtime/migrations/migration-wizard.ts +0 -747
  156. package/src/runtime/migrations/origin-mode.ts +0 -42
  157. package/src/runtime/migrations/rebind-secrets-screen.ts +0 -412
  158. package/src/runtime/migrations/staged-import-path.ts +0 -116
  159. package/src/runtime/migrations/transfer-progress-screen.ts +0 -323
  160. package/src/runtime/migrations/validation-results-screen.ts +0 -469
  161. package/src/runtime/migrations/vbundle-builder.ts +0 -1382
  162. package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -405
  163. package/src/runtime/migrations/vbundle-import-policy.ts +0 -241
  164. package/src/runtime/migrations/vbundle-importer.ts +0 -826
  165. package/src/runtime/migrations/vbundle-metadata-merge.ts +0 -113
  166. package/src/runtime/migrations/vbundle-streaming-importer.ts +0 -2844
  167. package/src/runtime/migrations/vbundle-streaming-validator.ts +0 -426
  168. package/src/runtime/migrations/vbundle-tar-stream.ts +0 -244
  169. package/src/runtime/migrations/vbundle-validator.ts +0 -724
  170. package/src/runtime/routes/__tests__/migration-cadet-metadata-reconcile.test.ts +0 -227
  171. package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +0 -54
  172. package/src/runtime/routes/__tests__/migration-import-allowed-hosts-env.test.ts +0 -70
  173. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +0 -257
  174. package/src/runtime/routes/background-wake-routes.ts +0 -387
  175. package/src/runtime/routes/migration-routes.ts +0 -2584
  176. package/src/tools/network/__tests__/cloud-search-proxy.test.ts +0 -282
  177. package/src/tools/network/cloud-search-proxy.ts +0 -211
  178. package/src/tts/__tests__/cadet-provider.test.ts +0 -253
  179. package/src/tts/providers/cadet-provider.ts +0 -244
  180. package/src/types/tar-stream.d.ts +0 -66
@@ -1,612 +0,0 @@
1
- /**
2
- * Shared transport methods for managed and runtime migration APIs.
3
- *
4
- * Provides typed functions for each migration endpoint, usable by
5
- * client web views (macOS/iOS WKWebView), the gateway, or any
6
- * TypeScript consumer that needs to call migration APIs.
7
- *
8
- * Two target APIs:
9
- *
10
- * Runtime (local daemon):
11
- * POST /v1/migrations/validate
12
- * POST /v1/migrations/export
13
- * POST /v1/migrations/import-preflight
14
- * POST /v1/migrations/import
15
- *
16
- * Managed (platform/Django):
17
- * POST /v1/migrations/validate/
18
- * POST /v1/migrations/export/
19
- * POST /v1/migrations/import-preflight/
20
- * POST /v1/migrations/import/
21
- * GET /v1/migrations/export/{job_id}/status/
22
- * GET /v1/migrations/import/{job_id}/status/
23
- *
24
- * All methods accept a `fetchFn` parameter for testability (defaults to
25
- * the global `fetch`). Auth headers are passed via `TransportConfig`.
26
- */
27
-
28
- import { z } from "zod";
29
-
30
- export type MigrationTarget = "runtime" | "managed";
31
-
32
- export interface TransportConfig {
33
- /** Base URL of the target API (e.g. "https://cloud.mycadet.ai"). */
34
- baseURL: string;
35
- /** Which API surface to target. Managed endpoints use trailing slashes. */
36
- target: MigrationTarget;
37
- /** Authorization header value (e.g. "Bearer <jwt>" or session token). */
38
- authHeader?: string;
39
- /** Header name for auth. Defaults to "Authorization" for runtime, "X-Session-Token" for managed. */
40
- authHeaderName?: string;
41
- /**
42
- * Additional headers to include with every request.
43
- * Merged after Content-Type but before auth-header injection,
44
- * so an explicit auth header from the transport always wins
45
- * over a same-named entry in defaultHeaders.
46
- */
47
- defaultHeaders?: Record<string, string>;
48
- /** Custom fetch implementation for testing or environments without global fetch. */
49
- fetchFn?: typeof fetch;
50
- }
51
-
52
- export interface ValidationError {
53
- code: string;
54
- message: string;
55
- path?: string;
56
- }
57
-
58
- interface ManifestFileEntry {
59
- path: string;
60
- sha256: string;
61
- size_bytes: number;
62
- }
63
-
64
- interface ManifestAssistantInfo {
65
- id: string;
66
- name: string;
67
- runtime_version: string;
68
- }
69
-
70
- interface ManifestOrigin {
71
- mode: "managed" | "self-hosted-remote" | "self-hosted-local";
72
- platform_version?: string;
73
- hostname?: string;
74
- }
75
-
76
- interface ManifestCompatibility {
77
- min_runtime_version: string;
78
- max_runtime_version: string | null;
79
- }
80
-
81
- interface ManifestExportOptions {
82
- include_logs: boolean;
83
- include_browser_state: boolean;
84
- include_memory_vectors: boolean;
85
- }
86
-
87
- export interface Manifest {
88
- schema_version: number;
89
- bundle_id: string;
90
- created_at: string;
91
- assistant: ManifestAssistantInfo;
92
- origin: ManifestOrigin;
93
- compatibility: ManifestCompatibility;
94
- contents: ManifestFileEntry[];
95
- checksum: string;
96
- secrets_redacted: boolean;
97
- export_options: ManifestExportOptions;
98
- }
99
-
100
- export interface ValidateSuccessResponse {
101
- is_valid: true;
102
- errors: ValidationError[];
103
- manifest: Manifest;
104
- }
105
-
106
- export interface ValidateFailureResponse {
107
- is_valid: false;
108
- errors: ValidationError[];
109
- }
110
-
111
- export type ValidateResponse =
112
- | ValidateSuccessResponse
113
- | ValidateFailureResponse;
114
-
115
- /** Runtime export returns the binary archive directly. */
116
- export interface ExportRuntimeResult {
117
- ok: true;
118
- archive: ArrayBuffer;
119
- filename: string;
120
- schemaVersion: number;
121
- checksum: string;
122
- }
123
-
124
- /** Managed export initiates an async job and returns a job ID. */
125
- export interface ExportManagedResult {
126
- ok: true;
127
- jobId: string;
128
- status: string;
129
- }
130
-
131
- type ExportResult = ExportRuntimeResult | ExportManagedResult;
132
-
133
- export interface ImportPreflightFileReport {
134
- path: string;
135
- action: "create" | "overwrite" | "unchanged" | "skip";
136
- bundle_size: number;
137
- current_size: number | null;
138
- bundle_sha256: string;
139
- current_sha256: string | null;
140
- }
141
-
142
- export interface ImportPreflightConflict {
143
- code: string;
144
- message: string;
145
- path?: string;
146
- }
147
-
148
- export interface ImportPreflightSuccessResponse {
149
- can_import: true;
150
- summary: {
151
- total_files: number;
152
- files_to_create: number;
153
- files_to_overwrite: number;
154
- files_unchanged: number;
155
- files_to_skip: number;
156
- };
157
- files: ImportPreflightFileReport[];
158
- conflicts: ImportPreflightConflict[];
159
- manifest: Manifest;
160
- }
161
-
162
- export interface ImportPreflightValidationFailedResponse {
163
- can_import: false;
164
- validation: {
165
- is_valid: false;
166
- errors: ValidationError[];
167
- };
168
- }
169
-
170
- export interface ImportPreflightConflictResponse {
171
- can_import: false;
172
- summary: {
173
- total_files: number;
174
- files_to_create: number;
175
- files_to_overwrite: number;
176
- files_unchanged: number;
177
- files_to_skip: number;
178
- };
179
- files: ImportPreflightFileReport[];
180
- conflicts: ImportPreflightConflict[];
181
- manifest: Manifest;
182
- }
183
-
184
- export type ImportPreflightResponse =
185
- | ImportPreflightSuccessResponse
186
- | ImportPreflightValidationFailedResponse
187
- | ImportPreflightConflictResponse;
188
-
189
- export interface ImportedFileReport {
190
- path: string;
191
- disk_path: string;
192
- action: "created" | "overwritten" | "skipped";
193
- size: number;
194
- sha256: string;
195
- backup_path: string | null;
196
- }
197
-
198
- export interface ImportCommitSuccessResponse {
199
- success: true;
200
- summary: {
201
- total_files: number;
202
- files_created: number;
203
- files_overwritten: number;
204
- files_skipped: number;
205
- backups_created: number;
206
- };
207
- files: ImportedFileReport[];
208
- manifest: Manifest;
209
- warnings: string[];
210
- credentialsImported?: {
211
- total: number;
212
- succeeded: number;
213
- failed: number;
214
- failedAccounts: string[];
215
- skippedPlatform?: number;
216
- };
217
- }
218
-
219
- export interface ImportCommitFailureResponse {
220
- success: false;
221
- reason: "validation_failed" | "extraction_failed" | "write_failed";
222
- errors?: ValidationError[];
223
- message?: string;
224
- partial_report?: unknown;
225
- }
226
-
227
- export type ImportCommitResponse =
228
- | ImportCommitSuccessResponse
229
- | ImportCommitFailureResponse;
230
-
231
- export interface JobStatusPending {
232
- status: "pending" | "processing";
233
- jobId: string;
234
- progress?: number;
235
- }
236
-
237
- export interface JobStatusComplete {
238
- status: "complete";
239
- jobId: string;
240
- downloadUrl?: string;
241
- result?: unknown;
242
- }
243
-
244
- export interface JobStatusFailed {
245
- status: "failed";
246
- jobId: string;
247
- error: string;
248
- }
249
-
250
- export type JobStatusResponse =
251
- | JobStatusPending
252
- | JobStatusComplete
253
- | JobStatusFailed;
254
-
255
- export class MigrationTransportError extends Error {
256
- readonly statusCode: number;
257
- readonly responseBody: string;
258
-
259
- constructor(message: string, statusCode: number, responseBody: string) {
260
- super(message);
261
- this.name = "MigrationTransportError";
262
- this.statusCode = statusCode;
263
- this.responseBody = responseBody;
264
- }
265
- }
266
-
267
- function resolveFetch(config: TransportConfig): typeof fetch {
268
- return config.fetchFn ?? globalThis.fetch;
269
- }
270
-
271
- function buildURL(config: TransportConfig, path: string): string {
272
- const base = config.baseURL.replace(/\/+$/, "");
273
- const trailingSlash = config.target === "managed" ? "/" : "";
274
- return `${base}/v1/migrations/${path}${trailingSlash}`;
275
- }
276
-
277
- function buildHeaders(
278
- config: TransportConfig,
279
- contentType?: string,
280
- ): Record<string, string> {
281
- const headers: Record<string, string> = {};
282
- const headerKeysByLowerName = new Map<string, string>();
283
-
284
- const setHeader = (headerName: string, headerValue: string): void => {
285
- const lowerName = headerName.toLowerCase();
286
- const existingHeaderName = headerKeysByLowerName.get(lowerName);
287
- if (existingHeaderName && existingHeaderName !== headerName) {
288
- delete headers[existingHeaderName];
289
- }
290
- headerKeysByLowerName.set(lowerName, headerName);
291
- headers[headerName] = headerValue;
292
- };
293
-
294
- if (contentType) {
295
- setHeader("Content-Type", contentType);
296
- }
297
-
298
- // Merge defaultHeaders after Content-Type but before auth injection
299
- if (config.defaultHeaders) {
300
- for (const [headerName, headerValue] of Object.entries(
301
- config.defaultHeaders,
302
- )) {
303
- setHeader(headerName, headerValue);
304
- }
305
- }
306
-
307
- // Auth header is applied last so it always wins over defaultHeaders
308
- if (config.authHeader) {
309
- const headerName =
310
- config.authHeaderName ??
311
- (config.target === "managed" ? "X-Session-Token" : "Authorization");
312
- setHeader(headerName, config.authHeader);
313
- }
314
-
315
- return headers;
316
- }
317
-
318
- async function assertOk(response: Response, context: string): Promise<void> {
319
- if (!response.ok) {
320
- const body = await response.text().catch(() => "<unreadable>");
321
- throw new MigrationTransportError(
322
- `${context}: HTTP ${response.status}`,
323
- response.status,
324
- body,
325
- );
326
- }
327
- }
328
-
329
- /**
330
- * Validate a .vbundle archive.
331
- *
332
- * Sends the archive as binary body and returns structured validation results.
333
- */
334
- export async function validateBundle(
335
- config: TransportConfig,
336
- fileData: ArrayBuffer,
337
- ): Promise<ValidateResponse> {
338
- const doFetch = resolveFetch(config);
339
- const url = buildURL(config, "validate");
340
-
341
- const response = await doFetch(url, {
342
- method: "POST",
343
- headers: buildHeaders(config, "application/octet-stream"),
344
- body: fileData,
345
- });
346
-
347
- await assertOk(response, "validate");
348
- return (await response.json()) as ValidateResponse;
349
- }
350
-
351
- /**
352
- * Export assistant data as a .vbundle archive.
353
- *
354
- * Runtime target: returns the binary archive directly.
355
- * Managed target: initiates an async export job and returns the job ID.
356
- */
357
- export async function exportBundle(
358
- config: TransportConfig,
359
- options?: { description?: string },
360
- ): Promise<ExportResult> {
361
- const doFetch = resolveFetch(config);
362
- const url = buildURL(config, "export");
363
-
364
- const body = options?.description
365
- ? JSON.stringify({ description: options.description })
366
- : undefined;
367
-
368
- const contentType = body ? "application/json" : undefined;
369
-
370
- const response = await doFetch(url, {
371
- method: "POST",
372
- headers: buildHeaders(config, contentType),
373
- ...(body ? { body } : {}),
374
- });
375
-
376
- await assertOk(response, "export");
377
-
378
- if (config.target === "managed") {
379
- const json = (await response.json()) as {
380
- job_id: string;
381
- status: string;
382
- };
383
- return {
384
- ok: true,
385
- jobId: json.job_id,
386
- status: json.status,
387
- } as ExportManagedResult;
388
- }
389
-
390
- // Runtime returns the binary archive. The legacy
391
- // `X-Vbundle-Manifest-Sha256` response header name is preserved for
392
- // cross-version client compat — its value is now sourced from the
393
- // renamed manifest `checksum` field.
394
- const archive = await response.arrayBuffer();
395
- const schemaVersionHeader =
396
- response.headers.get("X-Vbundle-Schema-Version") ?? "";
397
- const parsedSchemaVersion = Number.parseInt(schemaVersionHeader, 10);
398
- return {
399
- ok: true,
400
- archive,
401
- filename:
402
- response.headers
403
- .get("Content-Disposition")
404
- ?.match(/filename="?(.+?)"?$/)?.[1] ?? "export.vbundle",
405
- schemaVersion: Number.isFinite(parsedSchemaVersion)
406
- ? parsedSchemaVersion
407
- : 0,
408
- checksum: response.headers.get("X-Vbundle-Manifest-Sha256") ?? "",
409
- } as ExportRuntimeResult;
410
- }
411
-
412
- /**
413
- * Dry-run import analysis of a .vbundle archive.
414
- *
415
- * Returns a detailed report of what would change if the bundle were imported,
416
- * without modifying any data on disk.
417
- */
418
- export async function importPreflight(
419
- config: TransportConfig,
420
- fileData: ArrayBuffer,
421
- ): Promise<ImportPreflightResponse> {
422
- const doFetch = resolveFetch(config);
423
- const url = buildURL(config, "import-preflight");
424
-
425
- const response = await doFetch(url, {
426
- method: "POST",
427
- headers: buildHeaders(config, "application/octet-stream"),
428
- body: fileData,
429
- });
430
-
431
- await assertOk(response, "import-preflight");
432
- return (await response.json()) as ImportPreflightResponse;
433
- }
434
-
435
- /**
436
- * Commit a .vbundle archive import.
437
- *
438
- * This is a destructive operation that writes bundle files to their
439
- * target locations. For managed targets, this may initiate an async
440
- * import job.
441
- */
442
- export async function importCommit(
443
- config: TransportConfig,
444
- fileData: ArrayBuffer,
445
- ): Promise<ImportCommitResponse> {
446
- const doFetch = resolveFetch(config);
447
- const url = buildURL(config, "import");
448
-
449
- const response = await doFetch(url, {
450
- method: "POST",
451
- headers: buildHeaders(config, "application/octet-stream"),
452
- body: fileData,
453
- });
454
-
455
- // The import endpoint can return non-2xx for server errors but
456
- // also returns 200 with success: false for validation/write failures.
457
- // We only throw on non-2xx status codes.
458
- if (response.status >= 500) {
459
- const body = await response.text().catch(() => "<unreadable>");
460
- throw new MigrationTransportError(
461
- `import: HTTP ${response.status}`,
462
- response.status,
463
- body,
464
- );
465
- }
466
-
467
- // 400-level errors are also transport failures
468
- if (response.status >= 400 && response.status < 500) {
469
- const body = await response.text().catch(() => "<unreadable>");
470
- throw new MigrationTransportError(
471
- `import: HTTP ${response.status}`,
472
- response.status,
473
- body,
474
- );
475
- }
476
-
477
- return (await response.json()) as ImportCommitResponse;
478
- }
479
-
480
- /**
481
- * Poll the status of a cloud export job.
482
- *
483
- * Only applicable to managed targets. Runtime exports are synchronous
484
- * and do not require status polling.
485
- */
486
- export async function pollExportStatus(
487
- config: TransportConfig,
488
- jobId: string,
489
- ): Promise<JobStatusResponse> {
490
- return fetchJobStatus(config, "export", jobId);
491
- }
492
-
493
- /**
494
- * Poll the status of a cloud import job.
495
- *
496
- * Only applicable to managed targets. Runtime imports are synchronous
497
- * and do not require status polling.
498
- */
499
- export async function pollImportStatus(
500
- config: TransportConfig,
501
- jobId: string,
502
- ): Promise<JobStatusResponse> {
503
- return fetchJobStatus(config, "import", jobId);
504
- }
505
-
506
- const jobStatusWireSchema = z.object({
507
- status: z.enum(["pending", "processing", "complete", "failed"]),
508
- job_id: z.string().optional(),
509
- download_url: z.string().optional(),
510
- progress: z.number().optional(),
511
- error: z.string().optional(),
512
- result: z.unknown().optional(),
513
- });
514
-
515
- function jobStatusFromWire(
516
- wire: z.infer<typeof jobStatusWireSchema>,
517
- fallbackJobId: string,
518
- ): JobStatusResponse {
519
- const jobId = wire.job_id ?? fallbackJobId;
520
- if (wire.status === "complete") {
521
- return {
522
- status: "complete",
523
- jobId,
524
- downloadUrl: wire.download_url,
525
- result: wire.result,
526
- };
527
- }
528
- if (wire.status === "failed") {
529
- return {
530
- status: "failed",
531
- jobId,
532
- error: wire.error ?? "unknown export error",
533
- };
534
- }
535
- return { status: wire.status, jobId, progress: wire.progress };
536
- }
537
-
538
- async function fetchJobStatus(
539
- config: TransportConfig,
540
- kind: "export" | "import",
541
- jobId: string,
542
- ): Promise<JobStatusResponse> {
543
- if (config.target !== "managed") {
544
- throw new Error(
545
- `poll${kind === "export" ? "Export" : "Import"}Status is only supported for managed targets`,
546
- );
547
- }
548
-
549
- const doFetch = resolveFetch(config);
550
- const base = config.baseURL.replace(/\/+$/, "");
551
- const url = `${base}/v1/migrations/${kind}/${encodeURIComponent(
552
- jobId,
553
- )}/status/`;
554
-
555
- const response = await doFetch(url, {
556
- method: "GET",
557
- headers: buildHeaders(config),
558
- });
559
-
560
- await assertOk(response, `${kind} status (job ${jobId})`);
561
- const parsed = jobStatusWireSchema.safeParse(await response.json());
562
- if (!parsed.success) {
563
- throw new MigrationTransportError(
564
- `${kind} status (job ${jobId}): malformed response`,
565
- response.status,
566
- "",
567
- );
568
- }
569
- return jobStatusFromWire(parsed.data, jobId);
570
- }
571
-
572
- interface PollOptions {
573
- /** Polling interval in milliseconds. Defaults to 2000. */
574
- intervalMs?: number;
575
- /** Maximum number of polls before giving up. Defaults to 60. */
576
- maxAttempts?: number;
577
- /** Callback invoked on each poll with the latest status. */
578
- onProgress?: (status: JobStatusResponse) => void;
579
- }
580
-
581
- /**
582
- * Poll a cloud job until it reaches a terminal state (complete or failed).
583
- *
584
- * Returns the final status response. Throws if maxAttempts is exceeded.
585
- */
586
- export async function pollUntilComplete(
587
- config: TransportConfig,
588
- jobType: "export" | "import",
589
- jobId: string,
590
- options?: PollOptions,
591
- ): Promise<JobStatusComplete | JobStatusFailed> {
592
- const intervalMs = options?.intervalMs ?? 2000;
593
- const maxAttempts = options?.maxAttempts ?? 60;
594
- const pollFn = jobType === "export" ? pollExportStatus : pollImportStatus;
595
-
596
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
597
- const status = await pollFn(config, jobId);
598
- options?.onProgress?.(status);
599
-
600
- if (status.status === "complete" || status.status === "failed") {
601
- return status;
602
- }
603
-
604
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
605
- }
606
-
607
- throw new MigrationTransportError(
608
- `${jobType} job ${jobId} did not complete within ${maxAttempts} polls`,
609
- 0,
610
- "",
611
- );
612
- }