@pstdio/pocketcoder-sdk 0.3.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.
package/dist/index.js ADDED
@@ -0,0 +1,2335 @@
1
+ import { z } from "zod";
2
+ import { isIP } from "node:net";
3
+ //#region ../contracts/src/pagination.ts
4
+ const CursorSchema = z.string().min(1).max(2048);
5
+ function CursorPageSchema(item) {
6
+ return z.object({
7
+ items: z.array(item),
8
+ next_cursor: CursorSchema.nullable()
9
+ });
10
+ }
11
+ function CursorQuerySchema(maxLimit, defaultLimit) {
12
+ return z.object({
13
+ cursor: CursorSchema.optional(),
14
+ limit: z.coerce.number().int().positive().max(maxLimit).default(defaultLimit)
15
+ });
16
+ }
17
+ //#endregion
18
+ //#region ../contracts/src/conversation.ts
19
+ const CONVERSATION_ROLES = [
20
+ "user",
21
+ "assistant",
22
+ "system",
23
+ "tool"
24
+ ];
25
+ const ConversationMetadataSchema = z.record(z.string().min(1).max(64), z.string().max(512)).refine((value) => Object.keys(value).length <= 32, "conversation metadata has at most 32 keys");
26
+ const ConversationMessageInputSchema = z.object({
27
+ message_id: z.string().min(1).max(256),
28
+ role: z.enum(CONVERSATION_ROLES),
29
+ content: z.string().max(262144),
30
+ occurred_at: z.iso.datetime(),
31
+ metadata: ConversationMetadataSchema.default({})
32
+ });
33
+ const ConversationMessageResourceSchema = ConversationMessageInputSchema.extend({ seq: z.number().int().positive() });
34
+ CursorQuerySchema(200, 100);
35
+ const ConversationPageSchema = CursorPageSchema(ConversationMessageResourceSchema).extend({ retention: z.object({
36
+ status: z.literal("retained"),
37
+ expires_at: z.iso.datetime().nullable()
38
+ }) });
39
+ const ConversationResumeOutcomeSchema = z.object({
40
+ status: z.literal("supported"),
41
+ reason: z.null(),
42
+ source_workspace_id: z.uuid(),
43
+ checkpoint_id: z.uuid()
44
+ });
45
+ //#endregion
46
+ //#region ../contracts/src/duration.ts
47
+ const DURATION_RE = /^(\d+)(ms|s|m|h)$/;
48
+ function isDuration(value) {
49
+ return DURATION_RE.test(value);
50
+ }
51
+ //#endregion
52
+ //#region ../contracts/src/persistence.ts
53
+ const DurationValueSchema = z.string().refine(isDuration, { message: "expected a duration like 15s, 20m, or 2h" });
54
+ const LAUNCH_MODES = ["create", "restore"];
55
+ const CONVERSATION_RESTORE_CAPABILITIES = [
56
+ "supported",
57
+ "filesystem_only",
58
+ "unknown"
59
+ ];
60
+ const SourceDescriptorSchema = z.object({
61
+ kind: z.literal("git"),
62
+ repository: z.string().min(1).max(64).regex(/^[a-z0-9]([a-z0-9._-]{0,62}[a-z0-9])?$/),
63
+ revision: z.string().min(1).max(256).refine((value) => !value.startsWith("-") && !value.startsWith("/") && !value.endsWith("/") && !value.endsWith(".") && !value.endsWith(".lock") && value !== "@" && !value.includes("..") && !value.includes("//") && !value.includes("@{") && !/[~^:?*[\]\\\s]/u.test(value) && [...value].every((character) => {
64
+ const code = character.codePointAt(0) ?? 0;
65
+ return code >= 32 && code !== 127;
66
+ }), { message: "revision is not a safe branch, tag, or commit name" })
67
+ });
68
+ const ResolvedSourceSchema = z.object({
69
+ kind: z.literal("git"),
70
+ repository: z.string().min(1).max(64),
71
+ requested_revision: z.string().min(1).max(256),
72
+ resolved_commit: z.string().regex(/^[0-9a-f]{40,64}$/)
73
+ });
74
+ const PersistenceMountSchema = z.object({
75
+ name: z.string().min(1).max(64).regex(/^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/),
76
+ target: z.string().min(1),
77
+ maxBytes: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
78
+ maxFiles: z.number().int().positive().max(1e7)
79
+ });
80
+ const CheckpointPolicySchema = z.object({
81
+ onIdle: z.enum(["preserve", "destroy"]).default("destroy"),
82
+ onDeadline: z.enum(["preserve", "destroy"]).default("destroy"),
83
+ onCleanExit: z.enum(["preserve", "destroy"]).default("destroy"),
84
+ onFailure: z.enum([
85
+ "preserve",
86
+ "retain-for-recovery",
87
+ "destroy"
88
+ ]).default("destroy"),
89
+ retention: DurationValueSchema.default("168h")
90
+ });
91
+ const PersistenceSpecSchema = z.object({
92
+ mounts: z.array(PersistenceMountSchema).max(16).default([]),
93
+ conversationRestore: z.enum(CONVERSATION_RESTORE_CAPABILITIES).default("filesystem_only"),
94
+ conversationRetention: DurationValueSchema.default("168h"),
95
+ sessionCompatibility: z.string().min(1).max(128).optional(),
96
+ checkpoint: CheckpointPolicySchema.prefault({})
97
+ });
98
+ const OutputDeclarationSchema = z.discriminatedUnion("type", [
99
+ z.object({ type: z.literal("gitSha") }),
100
+ z.object({
101
+ type: z.literal("string"),
102
+ maxLength: z.number().int().positive().max(4096).default(512)
103
+ }),
104
+ z.object({
105
+ type: z.literal("httpsUrl"),
106
+ maxLength: z.number().int().positive().max(4096).default(2048)
107
+ })
108
+ ]);
109
+ const CHECKPOINT_STATES = [
110
+ "creating",
111
+ "ready",
112
+ "failed",
113
+ "deleting",
114
+ "deleted"
115
+ ];
116
+ const OPERATION_KINDS = [
117
+ "preserve",
118
+ "restore",
119
+ "verify",
120
+ "delete"
121
+ ];
122
+ const OPERATION_STATES = [
123
+ "pending",
124
+ "running",
125
+ "succeeded",
126
+ "failed"
127
+ ];
128
+ const CheckpointResourceSchema = z.object({
129
+ id: z.uuid(),
130
+ workspace_id: z.uuid(),
131
+ state: z.enum(CHECKPOINT_STATES),
132
+ reason_code: z.string().nullable(),
133
+ template: z.object({
134
+ name: z.string(),
135
+ version: z.string(),
136
+ digest: z.string()
137
+ }),
138
+ manifest_digest: z.string().nullable(),
139
+ logical_bytes: z.number().int().nonnegative().nullable(),
140
+ stored_bytes: z.number().int().nonnegative().nullable(),
141
+ file_count: z.number().int().nonnegative().nullable(),
142
+ mounts: z.array(z.string()),
143
+ conversation_restore: z.enum(CONVERSATION_RESTORE_CAPABILITIES),
144
+ label: z.string().nullable(),
145
+ created_at: z.iso.datetime(),
146
+ ready_at: z.iso.datetime().nullable(),
147
+ expires_at: z.iso.datetime().nullable()
148
+ });
149
+ const OperationResourceSchema = z.object({
150
+ id: z.uuid(),
151
+ kind: z.enum(OPERATION_KINDS),
152
+ state: z.enum(OPERATION_STATES),
153
+ workspace_id: z.uuid().nullable(),
154
+ checkpoint_id: z.uuid().nullable(),
155
+ result_workspace_id: z.uuid().nullable(),
156
+ reason_code: z.string().nullable(),
157
+ created_at: z.iso.datetime(),
158
+ updated_at: z.iso.datetime(),
159
+ completed_at: z.iso.datetime().nullable()
160
+ });
161
+ z.object({
162
+ retention: DurationValueSchema.optional(),
163
+ label: z.string().min(1).max(128).optional()
164
+ });
165
+ z.object({
166
+ external_id: z.string().min(1).max(256),
167
+ metadata: z.record(z.string().max(64), z.string().max(512)).optional(),
168
+ launch_input: z.record(z.string(), z.unknown()).optional()
169
+ });
170
+ const CheckpointManifestEntrySchema = z.object({
171
+ path: z.string(),
172
+ kind: z.enum([
173
+ "directory",
174
+ "file",
175
+ "symlink"
176
+ ]),
177
+ mode: z.number().int().nonnegative(),
178
+ uid: z.number().int().nonnegative(),
179
+ gid: z.number().int().nonnegative(),
180
+ mtime_ns: z.string().regex(/^\d+$/),
181
+ size: z.number().int().nonnegative(),
182
+ digest: z.string().optional(),
183
+ link_target: z.string().optional()
184
+ });
185
+ z.object({
186
+ format: z.literal("pocketcoder-checkpoint/v1"),
187
+ checkpoint_id: z.uuid(),
188
+ template_digest: z.string(),
189
+ mounts: z.array(z.object({
190
+ name: z.string(),
191
+ entries: z.array(CheckpointManifestEntrySchema)
192
+ })),
193
+ logical_bytes: z.number().int().nonnegative(),
194
+ file_count: z.number().int().nonnegative()
195
+ });
196
+ //#endregion
197
+ //#region ../contracts/src/network.ts
198
+ const DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
199
+ function isDomain(value) {
200
+ const domain = value.startsWith("*.") ? value.slice(2) : value;
201
+ return domain.length <= 253 && !domain.endsWith(".") && isIP(domain) === 0 && domain.split(".").length >= 2 && domain.split(".").every((label) => DOMAIN_LABEL.test(label));
202
+ }
203
+ const NetworkRuleSchema = z.object({
204
+ domain: z.string().refine(isDomain, "expected a lowercase ASCII domain or leading *. wildcard"),
205
+ ports: z.array(z.number().int().min(1).max(65535)).min(1).max(32).default([80, 443]),
206
+ allowPrivate: z.boolean().default(false)
207
+ }).superRefine((rule, ctx) => {
208
+ if (new Set(rule.ports).size !== rule.ports.length) ctx.addIssue({
209
+ code: "custom",
210
+ path: ["ports"],
211
+ message: "ports must be unique"
212
+ });
213
+ });
214
+ const UnrestrictedNetworkPolicySchema = z.object({ mode: z.literal("unrestricted") });
215
+ const RestrictedNetworkPolicySchema = z.object({
216
+ mode: z.literal("restricted"),
217
+ allow: z.array(NetworkRuleSchema).max(256).default([])
218
+ });
219
+ const NetworkPolicySchema = z.preprocess((value) => value ?? { mode: "unrestricted" }, z.discriminatedUnion("mode", [UnrestrictedNetworkPolicySchema, RestrictedNetworkPolicySchema]));
220
+ const NETWORK_STATES = [
221
+ "disabled",
222
+ "starting",
223
+ "ready",
224
+ "degraded"
225
+ ];
226
+ const NetworkEventInputSchema = z.object({
227
+ source_seq: z.number().int().positive(),
228
+ occurred_at: z.iso.datetime(),
229
+ decision: z.enum(["allow", "deny"]),
230
+ transport: z.enum(["http", "https"]),
231
+ host: z.string().min(1).max(253),
232
+ port: z.number().int().min(1).max(65535),
233
+ method: z.string().max(32).nullable().default(null),
234
+ path: z.string().max(2048).refine((path) => !path.includes("?") && !path.includes("#"), "path cannot contain query or fragment").nullable().default(null),
235
+ matched_rule: z.string().max(512).nullable().default(null),
236
+ reason: z.string().min(1).max(128)
237
+ });
238
+ z.object({
239
+ source_session_id: z.uuid(),
240
+ events: z.array(NetworkEventInputSchema).min(1).max(100)
241
+ });
242
+ const NetworkEventSchema = NetworkEventInputSchema.omit({ source_seq: true }).extend({
243
+ seq: z.number().int().positive(),
244
+ workspace_id: z.uuid(),
245
+ source_session_id: z.uuid(),
246
+ source_seq: z.number().int().positive()
247
+ });
248
+ CursorQuerySchema(200, 100);
249
+ const NetworkEventPageSchema = CursorPageSchema(NetworkEventSchema);
250
+ //#endregion
251
+ //#region ../contracts/src/workspace.ts
252
+ const WORKSPACE_STATES = [
253
+ "queued",
254
+ "provisioning",
255
+ "connected",
256
+ "ready",
257
+ "preserving",
258
+ "terminating",
259
+ "succeeded",
260
+ "failed",
261
+ "canceled",
262
+ "expired",
263
+ "preserved"
264
+ ];
265
+ const TERMINAL_STATES = [
266
+ "succeeded",
267
+ "failed",
268
+ "canceled",
269
+ "expired",
270
+ "preserved"
271
+ ];
272
+ const REASON_CODES = [
273
+ "child_exit_success",
274
+ "child_exit_failure",
275
+ "setup_failed",
276
+ "bootstrap_failed",
277
+ "registration_timeout",
278
+ "health_failed",
279
+ "child_crash",
280
+ "provider_lost",
281
+ "disconnect_timeout",
282
+ "canceled_by_caller",
283
+ "deadline_expired",
284
+ "idle_expired",
285
+ "queue_timeout",
286
+ "launch_failed",
287
+ "preserve_requested",
288
+ "preserved_by_policy",
289
+ "checkpoint_created",
290
+ "checkpoint_failed",
291
+ "checkpoint_corrupt",
292
+ "checkpoint_quota_exceeded",
293
+ "checkpoint_storage_lost",
294
+ "restore_requested",
295
+ "restore_failed",
296
+ "image_unavailable",
297
+ "source_resolution_failed",
298
+ "secret_resolution_failed",
299
+ "operation_conflict",
300
+ "network_policy_failed"
301
+ ];
302
+ const AGENT_STATES = [
303
+ "unknown",
304
+ "running",
305
+ "stable"
306
+ ];
307
+ z.object({
308
+ external_id: z.string().min(1).max(256),
309
+ template: z.object({
310
+ name: z.string().min(1).max(64),
311
+ version: z.string().max(64).optional()
312
+ }),
313
+ launch_input: z.record(z.string(), z.unknown()).optional(),
314
+ source: SourceDescriptorSchema.optional(),
315
+ metadata: z.record(z.string().max(64), z.string().max(512)).optional()
316
+ });
317
+ const TemplateRefSchema = z.object({
318
+ name: z.string(),
319
+ version: z.string(),
320
+ digest: z.string()
321
+ });
322
+ const WorkspaceResourceSchema = z.object({
323
+ id: z.uuid(),
324
+ external_id: z.string(),
325
+ template: TemplateRefSchema,
326
+ state: z.enum(WORKSPACE_STATES),
327
+ reason_code: z.enum(REASON_CODES).nullable(),
328
+ agent_state: z.enum(AGENT_STATES),
329
+ change_cursor: z.number().int().nonnegative(),
330
+ provider_kind: z.string().nullable(),
331
+ provisioning_mode: z.enum(["cold", "warm"]).nullable(),
332
+ network: z.object({ state: z.enum(NETWORK_STATES) }),
333
+ health: z.record(z.string(), z.string()),
334
+ created_at: z.iso.datetime(),
335
+ updated_at: z.iso.datetime(),
336
+ connected_at: z.iso.datetime().nullable(),
337
+ ready_at: z.iso.datetime().nullable(),
338
+ deadline_at: z.iso.datetime(),
339
+ terminal_at: z.iso.datetime().nullable(),
340
+ metadata: z.record(z.string(), z.string()),
341
+ origin_workspace_id: z.uuid().nullable(),
342
+ restored_from_checkpoint_id: z.uuid().nullable(),
343
+ source: SourceDescriptorSchema.extend({
344
+ requested_revision: z.string(),
345
+ resolved_commit: ResolvedSourceSchema.shape.resolved_commit.nullable()
346
+ }).omit({ revision: true }).nullable(),
347
+ persistence: z.object({
348
+ enabled: z.boolean(),
349
+ conversation_restore: z.enum(CONVERSATION_RESTORE_CAPABILITIES),
350
+ conversation_resume: z.object({
351
+ status: z.enum([
352
+ "supported",
353
+ "unsupported",
354
+ "unknown"
355
+ ]),
356
+ reason: z.enum(["filesystem_only", "capability_unknown"]).nullable()
357
+ }),
358
+ latest_checkpoint_id: z.uuid().nullable()
359
+ }),
360
+ outputs: z.record(z.string(), z.unknown()),
361
+ failure: z.object({
362
+ reason_code: z.enum(REASON_CODES),
363
+ log_tail: z.string(),
364
+ log_tail_truncated: z.boolean(),
365
+ last_log_seq: z.number().int().nonnegative().nullable()
366
+ }).nullable()
367
+ });
368
+ const WorkspacePageSchema = CursorPageSchema(WorkspaceResourceSchema);
369
+ z.object({
370
+ external_id: z.string().optional(),
371
+ state: z.enum(WORKSPACE_STATES).optional(),
372
+ template: z.string().optional(),
373
+ metadata: z.string().max(4096).optional(),
374
+ created_after: z.iso.datetime().optional(),
375
+ created_before: z.iso.datetime().optional(),
376
+ limit: z.coerce.number().int().positive().max(200).default(50),
377
+ cursor: z.string().optional()
378
+ });
379
+ //#endregion
380
+ //#region ../contracts/src/api.ts
381
+ const TemplatePageSchema = CursorPageSchema(z.object({
382
+ name: z.string(),
383
+ version: z.string(),
384
+ digest: z.string(),
385
+ description: z.string().optional(),
386
+ status: z.enum([
387
+ "active",
388
+ "available",
389
+ "retired"
390
+ ])
391
+ }));
392
+ CursorQuerySchema(200, 100);
393
+ const CheckpointPageSchema = CursorPageSchema(CheckpointResourceSchema);
394
+ CursorQuerySchema(200, 50).extend({ state: z.enum(CHECKPOINT_STATES).optional() });
395
+ const WorkspaceChangeSchema = z.object({
396
+ cursor: z.number().int().nonnegative(),
397
+ changed: z.boolean(),
398
+ workspace: WorkspaceResourceSchema
399
+ });
400
+ const PreserveResponseSchema = z.object({
401
+ workspace: WorkspaceResourceSchema,
402
+ checkpoint: CheckpointResourceSchema,
403
+ operation: OperationResourceSchema
404
+ });
405
+ const RestoreResponseSchema = z.object({
406
+ workspace: WorkspaceResourceSchema,
407
+ operation: OperationResourceSchema
408
+ });
409
+ const ResumeResponseSchema = RestoreResponseSchema.extend({ resume: ConversationResumeOutcomeSchema });
410
+ const OutputPageSchema = CursorPageSchema(z.object({
411
+ seq: z.number().int().positive(),
412
+ name: z.string(),
413
+ value: z.unknown(),
414
+ occurred_at: z.iso.datetime()
415
+ }));
416
+ CursorQuerySchema(1e3, 200);
417
+ const WarmPoolInventorySchema = z.object({
418
+ items: z.array(z.object({
419
+ template: z.string(),
420
+ version: z.string(),
421
+ template_digest: z.string(),
422
+ driver: z.string(),
423
+ desired: z.number().int().nonnegative(),
424
+ counts: z.record(z.string(), z.number().int().nonnegative()),
425
+ oldest_ready_age_ms: z.number().nonnegative().nullable()
426
+ })),
427
+ metrics: z.record(z.string(), z.number())
428
+ });
429
+ const StorageInventorySchema = z.object({
430
+ backend: z.string(),
431
+ storage_count: z.number().int().nonnegative(),
432
+ checkpoint_count: z.number().int().nonnegative(),
433
+ unknown_storage: z.array(z.string()),
434
+ unknown_checkpoints: z.array(z.string())
435
+ });
436
+ const StoragePruneResultSchema = z.object({
437
+ deleted: z.number().int().nonnegative(),
438
+ skipped: z.number().int().nonnegative(),
439
+ transcripts_deleted: z.number().int().nonnegative()
440
+ });
441
+ //#endregion
442
+ //#region ../contracts/src/attachment.ts
443
+ const ATTACHMENT_MAX_FILE_BYTES = 26214400;
444
+ const ATTACHMENT_CHUNK_BYTES = 524288;
445
+ const AttachmentMediaTypeSchema = z.string().regex(/^[\w.+-]+\/[\w.+-]+$/).max(255);
446
+ const AttachmentDescriptorSchema = z.object({
447
+ id: z.uuid(),
448
+ name: z.string().min(1).max(255),
449
+ path: z.string().min(1),
450
+ media_type: AttachmentMediaTypeSchema,
451
+ size_bytes: z.number().int().min(0).max(ATTACHMENT_MAX_FILE_BYTES),
452
+ sha256: z.string().regex(/^[0-9a-f]{64}$/)
453
+ });
454
+ z.looseObject({
455
+ type: z.literal("user"),
456
+ content: z.string(),
457
+ attachment_ids: z.array(z.uuid()).min(1).max(10).refine((ids) => new Set(ids).size === ids.length, "attachment_ids must be unique").optional()
458
+ });
459
+ const MANIFEST_OPEN = "<pocketcoder-attachments>";
460
+ const MANIFEST_CLOSE = "</pocketcoder-attachments>";
461
+ function splitAttachmentManifest(content) {
462
+ const open = content.lastIndexOf(MANIFEST_OPEN);
463
+ if (open < 0 || !content.trimEnd().endsWith(MANIFEST_CLOSE)) return {
464
+ text: content,
465
+ attachments: null
466
+ };
467
+ const close = content.lastIndexOf(MANIFEST_CLOSE);
468
+ const parsed = z.array(AttachmentDescriptorSchema).safeParse(safeJson(content.slice(open + 25, close)));
469
+ if (!parsed.success) return {
470
+ text: content,
471
+ attachments: null
472
+ };
473
+ return {
474
+ text: content.slice(0, open).trimEnd(),
475
+ attachments: parsed.data
476
+ };
477
+ }
478
+ function safeJson(value) {
479
+ try {
480
+ return JSON.parse(value);
481
+ } catch {
482
+ return;
483
+ }
484
+ }
485
+ const AttachmentStartPayload = z.object({
486
+ operation_id: z.uuid(),
487
+ attachment_id: z.uuid(),
488
+ name: z.string().min(1).max(255),
489
+ media_type: AttachmentMediaTypeSchema,
490
+ size_bytes: z.number().int().min(0).max(ATTACHMENT_MAX_FILE_BYTES)
491
+ });
492
+ const AttachmentChunkPayload = z.object({
493
+ operation_id: z.uuid(),
494
+ seq: z.number().int().nonnegative(),
495
+ content_b64: z.string().max(Math.ceil(ATTACHMENT_CHUNK_BYTES / 3) * 4 + 4)
496
+ });
497
+ const AttachmentFinishPayload = z.object({ operation_id: z.uuid() });
498
+ const AttachmentAbortPayload = z.object({
499
+ operation_id: z.uuid(),
500
+ reason: z.string().max(512)
501
+ });
502
+ const AttachmentResolvePayload = z.object({
503
+ operation_id: z.uuid(),
504
+ attachment_ids: z.array(z.uuid()).min(1).max(10)
505
+ });
506
+ const AttachmentAckPayload = z.object({
507
+ operation_id: z.uuid(),
508
+ seq: z.number().int().nonnegative(),
509
+ received_bytes: z.number().int().nonnegative()
510
+ });
511
+ const AttachmentResultPayload = z.object({
512
+ operation_id: z.uuid(),
513
+ status: z.enum([
514
+ "created",
515
+ "existing",
516
+ "conflict",
517
+ "failed"
518
+ ]),
519
+ descriptor: AttachmentDescriptorSchema.optional(),
520
+ failure_code: z.enum([
521
+ "invalid",
522
+ "too_large",
523
+ "sequence",
524
+ "io",
525
+ "interrupted"
526
+ ]).optional(),
527
+ detail: z.string().max(512).optional()
528
+ });
529
+ const AttachmentResolvedPayload = z.object({
530
+ operation_id: z.uuid(),
531
+ descriptors: z.array(AttachmentDescriptorSchema).optional(),
532
+ missing_id: z.uuid().optional()
533
+ });
534
+ const errorCodes = Object.keys({
535
+ "auth.invalid_key": 401,
536
+ "auth.missing_scope": 403,
537
+ "auth.disabled_principal": 403,
538
+ "validation.invalid": 400,
539
+ "idempotency.conflict": 409,
540
+ "capacity.queue_full": 429,
541
+ "capacity.waiters_full": 429,
542
+ "template.not_found": 404,
543
+ "template.version_not_found": 404,
544
+ "template.not_authorized": 403,
545
+ "workspace.not_found": 404,
546
+ "workspace.external_id_conflict": 409,
547
+ "workspace.not_ready": 409,
548
+ "workspace.terminal": 410,
549
+ "workspace.disconnected": 503,
550
+ "terminal.not_declared": 422,
551
+ "terminal.session_limit": 409,
552
+ "terminal.session_not_found": 404,
553
+ "terminal.session_closed": 409,
554
+ "terminal.protocol_unsupported": 426,
555
+ "workspace.persistence_not_enabled": 409,
556
+ "workspace.preserving": 409,
557
+ "checkpoint.not_found": 404,
558
+ "checkpoint.not_ready": 409,
559
+ "checkpoint.none_ready": 409,
560
+ "checkpoint.corrupt": 409,
561
+ "checkpoint.quota_exceeded": 413,
562
+ "checkpoint.in_use": 409,
563
+ "restore.template_not_authorized": 403,
564
+ "restore.image_unavailable": 409,
565
+ "restore.incompatible": 409,
566
+ "resume.unsupported": 409,
567
+ "conversation.expired": 410,
568
+ "conversation.deleted": 410,
569
+ "operation.conflict": 409,
570
+ "source.not_allowed": 422,
571
+ "source.invalid_revision": 422,
572
+ "secret.unavailable": 503,
573
+ "storage.capacity_exhausted": 507,
574
+ "relay.body_too_large": 413,
575
+ "relay.route_not_allowed": 422,
576
+ "relay.streaming_unsupported": 409,
577
+ "relay.deadline_exceeded": 504,
578
+ "relay.upstream_error": 502,
579
+ "attachment.invalid": 400,
580
+ "attachment.too_large": 413,
581
+ "attachment.not_found": 404,
582
+ "attachment.conflict": 409,
583
+ "attachment.unsupported": 409,
584
+ "attachment.interrupted": 503,
585
+ "internal.error": 500
586
+ });
587
+ const ErrorEnvelopeSchema = z.object({ error: z.object({
588
+ code: z.enum(errorCodes),
589
+ message: z.string().min(1),
590
+ request_id: z.string().min(1),
591
+ details: z.record(z.string(), z.unknown()).optional()
592
+ }) });
593
+ WORKSPACE_STATES.map((s) => `workspace.${s}`);
594
+ z.object({
595
+ id: z.uuid(),
596
+ type: z.string(),
597
+ occurred_at: z.iso.datetime(),
598
+ workspace: z.object({
599
+ id: z.uuid(),
600
+ external_id: z.string(),
601
+ state: z.enum(WORKSPACE_STATES),
602
+ reason_code: z.string().nullable(),
603
+ agent_state: z.enum(AGENT_STATES),
604
+ provisioning_mode: z.enum(["cold", "warm"]).nullable().default(null),
605
+ change_cursor: z.number().int().nonnegative(),
606
+ failure: z.object({
607
+ reason_code: z.enum(REASON_CODES),
608
+ log_tail: z.string(),
609
+ log_tail_truncated: z.boolean(),
610
+ last_log_seq: z.number().int().nonnegative().nullable()
611
+ }).nullable(),
612
+ template: z.object({
613
+ name: z.string(),
614
+ version: z.string(),
615
+ digest: z.string()
616
+ }),
617
+ origin_workspace_id: z.uuid().nullable(),
618
+ restored_from_checkpoint_id: z.uuid().nullable(),
619
+ latest_checkpoint_id: z.uuid().nullable(),
620
+ outputs: z.record(z.string(), z.unknown())
621
+ })
622
+ });
623
+ //#endregion
624
+ //#region ../contracts/src/logs.ts
625
+ const LogChunkSchema = z.object({
626
+ seq: z.number().int().positive(),
627
+ stream: z.string(),
628
+ occurred_at: z.iso.datetime(),
629
+ content: z.string()
630
+ });
631
+ CursorQuerySchema(1e3, 200);
632
+ const LogPageSchema = CursorPageSchema(LogChunkSchema);
633
+ //#endregion
634
+ //#region ../contracts/src/protocol-stream.ts
635
+ const ProxyStreamStartPayload = z.object({
636
+ request_id: z.uuid(),
637
+ status: z.number().int().min(100).max(599),
638
+ headers: z.record(z.string(), z.string())
639
+ });
640
+ const ProxyStreamChunkPayload = z.object({
641
+ request_id: z.uuid(),
642
+ seq: z.number().int().nonnegative(),
643
+ content_b64: z.string().max(87400)
644
+ });
645
+ const ProxyStreamEndPayload = z.object({
646
+ request_id: z.uuid(),
647
+ error_code: z.enum([
648
+ "unreachable",
649
+ "deadline",
650
+ "too_large"
651
+ ]).optional()
652
+ });
653
+ const ProxyStreamAckPayload = z.object({
654
+ request_id: z.uuid(),
655
+ seq: z.number().int().nonnegative()
656
+ });
657
+ const ProxyStreamCancelPayload = z.object({
658
+ request_id: z.uuid(),
659
+ reason: z.enum([
660
+ "downstream_closed",
661
+ "deadline",
662
+ "too_large",
663
+ "workspace_disconnected"
664
+ ])
665
+ });
666
+ //#endregion
667
+ //#region ../contracts/src/template-constants.ts
668
+ function isAbsolutePath(value) {
669
+ return value.startsWith("/") && ![...value].some((character) => character.codePointAt(0) === 0);
670
+ }
671
+ const SECRET_ENV_PATTERN = /(SECRET|TOKEN|PASSWORD|PASSWD|API_?KEY|PRIVATE_?KEY|CREDENTIAL)/i;
672
+ const SECRET_REFERENCE_PREFIX = "secretRef:";
673
+ //#endregion
674
+ //#region ../contracts/src/template-schema.ts
675
+ const NAME_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
676
+ const SEMVER_RE = /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;
677
+ const IMAGE_DIGEST_RE = /^[^\s@]+@sha256:[0-9a-f]{64}$/;
678
+ function secretMountPath(reference) {
679
+ if (!reference.startsWith("secretRef:")) throw new Error("secret reference must start with secretRef:");
680
+ const name = reference.slice(10);
681
+ if (!/^[A-Za-z0-9][A-Za-z0-9._/-]{0,254}$/.test(name) || name.includes("..")) throw new Error("invalid secret reference");
682
+ return `/run/pocketcoder/secrets/${name.replaceAll("/", "%2F")}`;
683
+ }
684
+ const DurationSchema = z.string().refine(isDuration, { message: "expected a duration like 15s, 20m, or 2h" });
685
+ const CommandSchema = z.array(z.string().min(1)).min(1);
686
+ const EnvSchema = z.record(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/), z.string());
687
+ const SetupStepSchema = z.object({
688
+ name: z.string().regex(NAME_RE),
689
+ command: CommandSchema,
690
+ timeoutSeconds: z.number().int().positive().max(3600).default(300),
691
+ env: EnvSchema.default({}),
692
+ cwd: z.string().refine(isAbsolutePath, "expected an absolute path").optional(),
693
+ runOn: z.array(z.enum(LAUNCH_MODES)).min(1).default(["create"])
694
+ });
695
+ const HarnessSchema = z.object({
696
+ command: CommandSchema,
697
+ env: EnvSchema.default({}),
698
+ cwd: z.string().refine(isAbsolutePath, "expected an absolute path").optional()
699
+ });
700
+ const AgentSchema = HarnessSchema.extend({
701
+ type: z.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/).default("custom"),
702
+ transport: z.enum(["pty", "acp"]).default("pty"),
703
+ termWidth: z.number().int().min(10).max(65535).optional()
704
+ });
705
+ const TerminalSchema = z.object({
706
+ command: CommandSchema,
707
+ env: EnvSchema.default({}),
708
+ cwd: z.string().refine(isAbsolutePath, "expected an absolute path").optional(),
709
+ maxSessions: z.number().int().min(1).max(8).default(2),
710
+ idleTimeout: DurationSchema.default("10m")
711
+ });
712
+ const ServiceRouteSchema = z.object({
713
+ method: z.enum([
714
+ "GET",
715
+ "POST",
716
+ "PUT",
717
+ "PATCH",
718
+ "DELETE"
719
+ ]),
720
+ path: z.string(),
721
+ query: z.array(z.string().min(1)).default([]),
722
+ responseMode: z.enum(["buffered", "stream"]).default("buffered"),
723
+ maxRequestBytes: z.number().int().positive().max(16777216).default(65536),
724
+ maxResponseBytes: z.number().int().positive().max(16777216).default(1048576),
725
+ deadlineSeconds: z.number().int().positive().max(300).default(60)
726
+ });
727
+ const ServiceSchema = z.object({
728
+ baseUrl: z.string(),
729
+ required: z.boolean().default(true),
730
+ healthPath: z.string().default("/status"),
731
+ routes: z.array(ServiceRouteSchema).min(1)
732
+ });
733
+ const SecuritySchema = z.object({
734
+ uid: z.number().int().min(1e3).default(10001),
735
+ gid: z.number().int().min(1e3).default(10001),
736
+ readOnlyRoot: z.boolean().default(true),
737
+ writableMemoryPaths: z.array(z.string().refine(isAbsolutePath, "expected an absolute path")).default(["/tmp"]),
738
+ dropCapabilities: z.array(z.string()).default(["ALL"]),
739
+ allowPrivilegeEscalation: z.literal(false).default(false),
740
+ seccomp: z.literal("RuntimeDefault").default("RuntimeDefault")
741
+ });
742
+ const TimeoutsSchema = z.object({
743
+ start: DurationSchema.default("2m"),
744
+ maxAge: DurationSchema.default("2h"),
745
+ idle: DurationSchema.default("20m"),
746
+ disconnectGrace: DurationSchema.default("5m"),
747
+ terminateGrace: DurationSchema.default("15s")
748
+ });
749
+ const ResourcesSchema = z.object({
750
+ cpu: z.string().regex(/^\d+(\.\d+)?m?$/),
751
+ memory: z.string().regex(/^\d+(Mi|Gi)$/)
752
+ });
753
+ const RepositorySchema = z.object({
754
+ url: z.url().refine((value) => {
755
+ try {
756
+ return new URL(value).username === "" && new URL(value).password === "";
757
+ } catch {
758
+ return false;
759
+ }
760
+ }, "repository URLs must not contain userinfo"),
761
+ credential: z.string().startsWith(SECRET_REFERENCE_PREFIX).max(256).refine((value) => {
762
+ try {
763
+ secretMountPath(value);
764
+ return true;
765
+ } catch {
766
+ return false;
767
+ }
768
+ }, "credential must be a normalized secret reference").optional()
769
+ });
770
+ const SourceSpecSchema = z.object({
771
+ kind: z.literal("git"),
772
+ destinationMount: z.string().regex(NAME_RE),
773
+ repositories: z.record(z.string().regex(NAME_RE), RepositorySchema),
774
+ allowedRevision: z.literal("branch-tag-or-commit").default("branch-tag-or-commit")
775
+ });
776
+ const CheckpointHookSchema = z.object({
777
+ command: CommandSchema,
778
+ timeoutSeconds: z.number().int().positive().max(300).default(30),
779
+ env: EnvSchema.default({}),
780
+ cwd: z.string().refine(isAbsolutePath, "expected an absolute path").optional()
781
+ });
782
+ const TemplateSpecSchema = z.object({
783
+ version: z.string().regex(SEMVER_RE),
784
+ image: z.string().regex(IMAGE_DIGEST_RE, { message: "image must be digest-pinned (repo@sha256:<64 hex>)" }),
785
+ command: CommandSchema.default([
786
+ "/usr/local/bin/pocketcoder-agent",
787
+ "supervise",
788
+ "--launch-input",
789
+ "/run/pocketcoder/input"
790
+ ]),
791
+ setup: z.array(SetupStepSchema).max(32).default([]),
792
+ agent: AgentSchema.optional(),
793
+ harness: HarnessSchema.optional(),
794
+ terminal: TerminalSchema.optional(),
795
+ env: EnvSchema.default({}),
796
+ resources: ResourcesSchema,
797
+ timeouts: TimeoutsSchema.prefault({}),
798
+ services: z.record(z.string().regex(NAME_RE), ServiceSchema).optional(),
799
+ security: SecuritySchema.prefault({}),
800
+ network: NetworkPolicySchema,
801
+ maxLaunchInputBytes: z.number().int().positive().max(1048576).default(65536),
802
+ compat: z.object({
803
+ agent: z.string().optional(),
804
+ agentapi: z.string().optional()
805
+ }).default({}),
806
+ persistence: PersistenceSpecSchema.prefault({}),
807
+ source: SourceSpecSchema.nullable().default(null),
808
+ checkpointHook: CheckpointHookSchema.optional(),
809
+ outputs: z.record(z.string().regex(NAME_RE), OutputDeclarationSchema).default({})
810
+ }).superRefine((spec, ctx) => {
811
+ if (!spec.agent && !spec.harness) ctx.addIssue({
812
+ code: "custom",
813
+ path: ["harness"],
814
+ message: "either agent or harness is required"
815
+ });
816
+ if (!spec.agent) return;
817
+ if (spec.agent.transport === "acp" && spec.agent.termWidth !== void 0) ctx.addIssue({
818
+ code: "custom",
819
+ path: ["agent", "termWidth"],
820
+ message: "termWidth is only valid for PTY transport"
821
+ });
822
+ for (const field of [
823
+ "harness",
824
+ "services",
825
+ "checkpointHook"
826
+ ]) {
827
+ if (spec[field] === void 0) continue;
828
+ ctx.addIssue({
829
+ code: "custom",
830
+ path: [field],
831
+ message: `agent cannot be combined with ${field}`
832
+ });
833
+ }
834
+ }).transform((spec) => {
835
+ if (spec.agent) {
836
+ const { harness: _harness, services: _services, checkpointHook: _hook, ...native } = spec;
837
+ return {
838
+ ...native,
839
+ agent: spec.agent
840
+ };
841
+ }
842
+ const { agent: _agent, ...legacy } = spec;
843
+ return {
844
+ ...legacy,
845
+ harness: spec.harness,
846
+ services: spec.services ?? {}
847
+ };
848
+ });
849
+ const TemplateManifestBaseSchema = z.object({
850
+ apiVersion: z.literal("pocketcoder.dev/v1alpha1"),
851
+ kind: z.literal("Template"),
852
+ metadata: z.object({
853
+ name: z.string().regex(NAME_RE),
854
+ description: z.string().max(512).optional()
855
+ }),
856
+ spec: TemplateSpecSchema
857
+ });
858
+ //#endregion
859
+ //#region ../contracts/src/template-runtime.ts
860
+ const LEGACY_AGENTAPI_SERVICE = ServiceSchema.parse({
861
+ baseUrl: "http://127.0.0.1:3284",
862
+ routes: [
863
+ {
864
+ method: "GET",
865
+ path: "/status"
866
+ },
867
+ {
868
+ method: "GET",
869
+ path: "/messages",
870
+ query: ["after"]
871
+ },
872
+ {
873
+ method: "POST",
874
+ path: "/message"
875
+ }
876
+ ]
877
+ });
878
+ const AGENTAPI_SERVICE = ServiceSchema.parse({
879
+ ...LEGACY_AGENTAPI_SERVICE,
880
+ routes: [...LEGACY_AGENTAPI_SERVICE.routes, {
881
+ method: "GET",
882
+ path: "/events",
883
+ responseMode: "stream",
884
+ maxResponseBytes: 4194304,
885
+ deadlineSeconds: 300
886
+ }]
887
+ });
888
+ function isAgentApiNative(spec) {
889
+ return "agent" in spec && spec.agent !== void 0;
890
+ }
891
+ function agentApiHarness(spec) {
892
+ if (!isAgentApiNative(spec)) return spec.harness;
893
+ return {
894
+ command: [
895
+ "/usr/local/bin/agentapi",
896
+ "server",
897
+ "--type",
898
+ spec.agent.type,
899
+ ...spec.agent.transport === "acp" ? ["--experimental-acp"] : [],
900
+ ...spec.agent.termWidth === void 0 ? [] : ["--term-width", String(spec.agent.termWidth)],
901
+ "--port",
902
+ "3284",
903
+ "--",
904
+ ...spec.agent.command
905
+ ],
906
+ env: spec.agent.env,
907
+ ...spec.agent.cwd ? { cwd: spec.agent.cwd } : {}
908
+ };
909
+ }
910
+ function templateServices(spec) {
911
+ return isAgentApiNative(spec) ? { agent: AGENTAPI_SERVICE } : spec.services;
912
+ }
913
+ //#endregion
914
+ //#region ../contracts/src/template-validation.ts
915
+ function envSources(spec) {
916
+ const sources = [[["spec", "env"], spec.env], [isAgentApiNative(spec) ? [
917
+ "spec",
918
+ "agent",
919
+ "env"
920
+ ] : [
921
+ "spec",
922
+ "harness",
923
+ "env"
924
+ ], agentApiHarness(spec).env]];
925
+ for (const [i, step] of spec.setup.entries()) sources.push([[
926
+ "spec",
927
+ "setup",
928
+ i,
929
+ "env"
930
+ ], step.env]);
931
+ if (!isAgentApiNative(spec) && spec.checkpointHook) sources.push([[
932
+ "spec",
933
+ "checkpointHook",
934
+ "env"
935
+ ], spec.checkpointHook.env]);
936
+ if (spec.terminal) sources.push([[
937
+ "spec",
938
+ "terminal",
939
+ "env"
940
+ ], spec.terminal.env]);
941
+ return sources;
942
+ }
943
+ const NETWORK_ENV = /* @__PURE__ */ new Set([
944
+ "http_proxy",
945
+ "https_proxy",
946
+ "all_proxy",
947
+ "no_proxy"
948
+ ]);
949
+ function validateNetworkEnvironment(spec, ctx) {
950
+ if (spec.network.mode !== "restricted") return;
951
+ for (const [path, env] of envSources(spec)) for (const key of Object.keys(env)) {
952
+ if (!NETWORK_ENV.has(key.toLowerCase())) continue;
953
+ ctx.addIssue({
954
+ code: "custom",
955
+ path: [...path, key],
956
+ message: "proxy variables are reserved by restricted networking"
957
+ });
958
+ }
959
+ }
960
+ function validateServices(spec, ctx) {
961
+ for (const [serviceName, service] of Object.entries(templateServices(spec))) {
962
+ if (!isLoopbackBaseUrl(service.baseUrl)) ctx.addIssue({
963
+ code: "custom",
964
+ path: [
965
+ "spec",
966
+ "services",
967
+ serviceName,
968
+ "baseUrl"
969
+ ],
970
+ message: "service baseUrl must be a loopback http URL"
971
+ });
972
+ if (!isNormalizedPath(service.healthPath)) ctx.addIssue({
973
+ code: "custom",
974
+ path: [
975
+ "spec",
976
+ "services",
977
+ serviceName,
978
+ "healthPath"
979
+ ],
980
+ message: "healthPath must be a normalized absolute path"
981
+ });
982
+ validateServiceRoutes(serviceName, service.routes, ctx);
983
+ }
984
+ }
985
+ function validateServiceRoutes(serviceName, routes, ctx) {
986
+ const seen = /* @__PURE__ */ new Set();
987
+ for (const [index, route] of routes.entries()) {
988
+ if (!isNormalizedPath(route.path)) ctx.addIssue({
989
+ code: "custom",
990
+ path: [
991
+ "spec",
992
+ "services",
993
+ serviceName,
994
+ "routes",
995
+ index,
996
+ "path"
997
+ ],
998
+ message: "route path must be normalized, absolute, and exact"
999
+ });
1000
+ const key = `${route.method} ${route.path}`;
1001
+ if (seen.has(key)) ctx.addIssue({
1002
+ code: "custom",
1003
+ path: [
1004
+ "spec",
1005
+ "services",
1006
+ serviceName,
1007
+ "routes",
1008
+ index
1009
+ ],
1010
+ message: `duplicate route: ${key}`
1011
+ });
1012
+ seen.add(key);
1013
+ }
1014
+ }
1015
+ function validateEnvironment(spec, ctx) {
1016
+ for (const [where, env] of envSources(spec)) for (const [key, value] of Object.entries(env)) {
1017
+ if (SECRET_ENV_PATTERN.test(key) && !value.startsWith("secretRef:") && value !== "") ctx.addIssue({
1018
+ code: "custom",
1019
+ path: where,
1020
+ message: `env ${key} looks like a secret literal; use a "${SECRET_REFERENCE_PREFIX}" reference resolved by the deployment`
1021
+ });
1022
+ if (value.startsWith("secretRef:") && !validSecretReference(value)) ctx.addIssue({
1023
+ code: "custom",
1024
+ path: where,
1025
+ message: `env ${key} has an invalid secret reference`
1026
+ });
1027
+ }
1028
+ }
1029
+ function validateTerminal(spec, ctx) {
1030
+ if (!spec.terminal?.cwd || spec.terminal.cwd === "/" || isNormalizedFilesystemPath(spec.terminal.cwd)) return;
1031
+ ctx.addIssue({
1032
+ code: "custom",
1033
+ path: [
1034
+ "spec",
1035
+ "terminal",
1036
+ "cwd"
1037
+ ],
1038
+ message: "cwd must be a normalized absolute filesystem path"
1039
+ });
1040
+ }
1041
+ function validSecretReference(value) {
1042
+ try {
1043
+ secretMountPath(value);
1044
+ return true;
1045
+ } catch {
1046
+ return false;
1047
+ }
1048
+ }
1049
+ function validateOutputs(spec, ctx) {
1050
+ for (const name of Object.keys(spec.outputs)) {
1051
+ if (!SECRET_ENV_PATTERN.test(name)) continue;
1052
+ ctx.addIssue({
1053
+ code: "custom",
1054
+ path: [
1055
+ "spec",
1056
+ "outputs",
1057
+ name
1058
+ ],
1059
+ message: "secret-like names are not allowed as durable outputs"
1060
+ });
1061
+ }
1062
+ }
1063
+ function validateWritableMemoryPaths(spec, ctx) {
1064
+ for (const path of spec.security.writableMemoryPaths) {
1065
+ if (!path.includes("..")) continue;
1066
+ ctx.addIssue({
1067
+ code: "custom",
1068
+ path: [
1069
+ "spec",
1070
+ "security",
1071
+ "writableMemoryPaths"
1072
+ ],
1073
+ message: "writable paths must not contain .."
1074
+ });
1075
+ }
1076
+ }
1077
+ function isLoopbackBaseUrl(value) {
1078
+ let url;
1079
+ try {
1080
+ url = new URL(value);
1081
+ } catch {
1082
+ return false;
1083
+ }
1084
+ if (url.protocol !== "http:") return false;
1085
+ if (url.pathname !== "/" || url.search !== "" || url.hash !== "") return false;
1086
+ return [
1087
+ "127.0.0.1",
1088
+ "localhost",
1089
+ "[::1]"
1090
+ ].includes(url.hostname) || url.hostname === "::1";
1091
+ }
1092
+ const FORBIDDEN_PERSISTENCE_ROOTS = [
1093
+ "/",
1094
+ "/run/pocketcoder",
1095
+ "/run/pocketcoder/secrets",
1096
+ "/proc",
1097
+ "/sys",
1098
+ "/dev"
1099
+ ];
1100
+ function pathContains(parent, child) {
1101
+ return child === parent || child.startsWith(`${parent}/`);
1102
+ }
1103
+ function validatePersistence(spec, ctx) {
1104
+ const seenNames = /* @__PURE__ */ new Set();
1105
+ const mounts = spec.persistence.mounts;
1106
+ for (const [index, mount] of mounts.entries()) validatePersistenceMount(spec, mounts, mount, index, seenNames, ctx);
1107
+ validateSourceMount(spec, mounts, ctx);
1108
+ if (spec.persistence.conversationRestore === "supported" && (!spec.persistence.sessionCompatibility || mounts.length < 2)) ctx.addIssue({
1109
+ code: "custom",
1110
+ path: [
1111
+ "spec",
1112
+ "persistence",
1113
+ "conversationRestore"
1114
+ ],
1115
+ message: "supported conversation restore requires sessionCompatibility and a separate harness-state mount"
1116
+ });
1117
+ }
1118
+ function validatePersistenceMount(spec, mounts, mount, index, seenNames, ctx) {
1119
+ const path = [
1120
+ "spec",
1121
+ "persistence",
1122
+ "mounts",
1123
+ index,
1124
+ "target"
1125
+ ];
1126
+ if (!isNormalizedFilesystemPath(mount.target)) ctx.addIssue({
1127
+ code: "custom",
1128
+ path,
1129
+ message: "target must be a normalized absolute filesystem path"
1130
+ });
1131
+ if (FORBIDDEN_PERSISTENCE_ROOTS.some((root) => pathContains(root, mount.target))) ctx.addIssue({
1132
+ code: "custom",
1133
+ path,
1134
+ message: "target overlaps a protected runtime or kernel path"
1135
+ });
1136
+ if (seenNames.has(mount.name)) ctx.addIssue({
1137
+ code: "custom",
1138
+ path: [
1139
+ "spec",
1140
+ "persistence",
1141
+ "mounts",
1142
+ index,
1143
+ "name"
1144
+ ],
1145
+ message: "persistence mount names must be unique"
1146
+ });
1147
+ seenNames.add(mount.name);
1148
+ validateMountOverlap(mounts, mount, index, path, ctx);
1149
+ validateMemoryPathOverlap(spec, mount, path, ctx);
1150
+ }
1151
+ function validateMountOverlap(mounts, mount, index, path, ctx) {
1152
+ for (const [otherIndex, other] of mounts.entries()) {
1153
+ if (otherIndex >= index) continue;
1154
+ if (!pathContains(other.target, mount.target) && !pathContains(mount.target, other.target)) continue;
1155
+ ctx.addIssue({
1156
+ code: "custom",
1157
+ path,
1158
+ message: `target overlaps persistence mount ${other.name}`
1159
+ });
1160
+ }
1161
+ }
1162
+ function validateMemoryPathOverlap(spec, mount, path, ctx) {
1163
+ for (const memoryPath of spec.security.writableMemoryPaths) {
1164
+ if (!pathContains(memoryPath, mount.target) && !pathContains(mount.target, memoryPath)) continue;
1165
+ ctx.addIssue({
1166
+ code: "custom",
1167
+ path,
1168
+ message: `target overlaps writableMemoryPath ${memoryPath}`
1169
+ });
1170
+ }
1171
+ }
1172
+ function validateSourceMount(spec, mounts, ctx) {
1173
+ if (!spec.source) return;
1174
+ if (mounts.find((mount) => mount.name === spec.source?.destinationMount)) return;
1175
+ ctx.addIssue({
1176
+ code: "custom",
1177
+ path: [
1178
+ "spec",
1179
+ "source",
1180
+ "destinationMount"
1181
+ ],
1182
+ message: "source destinationMount must name a persistence mount"
1183
+ });
1184
+ }
1185
+ function isNormalizedFilesystemPath(path) {
1186
+ if (!isAbsolutePath(path) || path === "/") return false;
1187
+ if (path.endsWith("/") || path.includes("//") || path.includes("\\") || path.includes(",")) return false;
1188
+ if ([...path].some((character) => {
1189
+ const code = character.codePointAt(0) ?? 0;
1190
+ return code < 32 || code === 127;
1191
+ })) return false;
1192
+ return !path.split("/").some((part) => part === "." || part === "..");
1193
+ }
1194
+ function isNormalizedPath(path) {
1195
+ if (!path.startsWith("/")) return false;
1196
+ if (path.includes("..") || path.includes("//")) return false;
1197
+ if (/[?#\s]/.test(path)) return false;
1198
+ if (/%2e|%2f|%5c/i.test(path)) return false;
1199
+ if ([...path].some((character) => character.charCodeAt(0) <= 31)) return false;
1200
+ return true;
1201
+ }
1202
+ function validateTemplateSpec(spec, ctx) {
1203
+ validateServices(spec, ctx);
1204
+ validateEnvironment(spec, ctx);
1205
+ validateOutputs(spec, ctx);
1206
+ validateWritableMemoryPaths(spec, ctx);
1207
+ validatePersistence(spec, ctx);
1208
+ validateNetworkEnvironment(spec, ctx);
1209
+ validateTerminal(spec, ctx);
1210
+ }
1211
+ TemplateManifestBaseSchema.superRefine((manifest, ctx) => {
1212
+ validateTemplateSpec(manifest.spec, ctx);
1213
+ });
1214
+ //#endregion
1215
+ //#region ../contracts/src/terminal.ts
1216
+ const TERMINAL_CHUNK_BYTES = 32768;
1217
+ const TERMINAL_REPLAY_BUFFER_BYTES = 65536;
1218
+ function base64Schema(maxBytes) {
1219
+ return z.string().regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/).refine((value) => {
1220
+ const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
1221
+ return value.length / 4 * 3 - padding <= maxBytes;
1222
+ }, `decoded payload must not exceed ${maxBytes} bytes`);
1223
+ }
1224
+ const TerminalChunkSchema = base64Schema(TERMINAL_CHUNK_BYTES);
1225
+ const TerminalReplaySchema = base64Schema(TERMINAL_REPLAY_BUFFER_BYTES);
1226
+ const TerminalSizeSchema = z.number().int().min(1).max(1e3);
1227
+ const TerminalOpenPayload = z.object({
1228
+ session_id: z.uuid(),
1229
+ rows: TerminalSizeSchema,
1230
+ cols: TerminalSizeSchema,
1231
+ reattach: z.boolean()
1232
+ });
1233
+ const TerminalInputPayload = z.object({
1234
+ session_id: z.uuid(),
1235
+ data_b64: TerminalChunkSchema
1236
+ });
1237
+ const TerminalResizePayload = z.object({
1238
+ session_id: z.uuid(),
1239
+ rows: TerminalSizeSchema,
1240
+ cols: TerminalSizeSchema
1241
+ });
1242
+ const TerminalClosePayload = z.object({
1243
+ session_id: z.uuid(),
1244
+ reason: z.enum([
1245
+ "checkpoint",
1246
+ "workspace_ended",
1247
+ "closed"
1248
+ ])
1249
+ });
1250
+ const TerminalOpenedPayload = z.object({
1251
+ session_id: z.uuid(),
1252
+ replay_b64: TerminalReplaySchema.optional()
1253
+ });
1254
+ const TerminalOutputPayload = z.object({
1255
+ session_id: z.uuid(),
1256
+ data_b64: TerminalChunkSchema
1257
+ });
1258
+ const TerminalClosedPayload = z.object({
1259
+ session_id: z.uuid(),
1260
+ reason: z.enum([
1261
+ "exit",
1262
+ "idle",
1263
+ "checkpoint",
1264
+ "workspace_ended",
1265
+ "error",
1266
+ "closed"
1267
+ ]),
1268
+ exit_code: z.number().int().nullable().optional(),
1269
+ detail: z.string().max(512).optional()
1270
+ });
1271
+ z.discriminatedUnion("type", [z.object({
1272
+ type: z.literal("input"),
1273
+ data_b64: TerminalChunkSchema
1274
+ }), z.object({
1275
+ type: z.literal("resize"),
1276
+ rows: TerminalSizeSchema,
1277
+ cols: TerminalSizeSchema
1278
+ })]);
1279
+ const ServerTerminalMessageSchema = z.discriminatedUnion("type", [
1280
+ z.object({
1281
+ type: z.literal("opened"),
1282
+ session_id: z.uuid(),
1283
+ replay_b64: TerminalReplaySchema.optional()
1284
+ }),
1285
+ z.object({
1286
+ type: z.literal("output"),
1287
+ data_b64: TerminalChunkSchema
1288
+ }),
1289
+ z.object({
1290
+ type: z.literal("closed"),
1291
+ reason: z.enum([
1292
+ "exit",
1293
+ "idle",
1294
+ "checkpoint",
1295
+ "workspace_ended",
1296
+ "agent_detached"
1297
+ ]),
1298
+ exit_code: z.number().int().nullable().optional()
1299
+ }),
1300
+ z.object({
1301
+ type: z.literal("status"),
1302
+ state: z.enum(["reconnecting", "resumed"])
1303
+ })
1304
+ ]);
1305
+ const TerminalSessionSchema = z.object({
1306
+ session_id: z.uuid(),
1307
+ workspace_id: z.uuid(),
1308
+ key_id: z.uuid(),
1309
+ opened_at: z.iso.datetime(),
1310
+ closed_at: z.iso.datetime().nullable(),
1311
+ duration_ms: z.number().int().nonnegative().nullable(),
1312
+ close_reason: z.enum([
1313
+ "exit",
1314
+ "idle",
1315
+ "checkpoint",
1316
+ "workspace_ended",
1317
+ "agent_detached",
1318
+ "client_closed"
1319
+ ]).nullable(),
1320
+ exit_code: z.number().int().nullable(),
1321
+ bytes_in: z.number().int().nonnegative(),
1322
+ bytes_out: z.number().int().nonnegative()
1323
+ });
1324
+ CursorQuerySchema(200, 50);
1325
+ const TerminalSessionPageSchema = CursorPageSchema(TerminalSessionSchema);
1326
+ const ProviderInputSchema = z.object({
1327
+ workspace_id: z.uuid(),
1328
+ server_url: z.string(),
1329
+ registration_secret: z.string(),
1330
+ template_digest: z.string(),
1331
+ template_name: z.string().default("unknown"),
1332
+ template_version: z.string().default("unknown"),
1333
+ launch_mode: z.enum(LAUNCH_MODES).default("create"),
1334
+ source: SourceDescriptorSchema.optional(),
1335
+ restore: z.object({
1336
+ checkpoint_id: z.uuid(),
1337
+ origin_workspace_id: z.uuid()
1338
+ }).optional(),
1339
+ launch_input: z.record(z.string(), z.unknown()).optional()
1340
+ });
1341
+ const PoolProviderInputSchema = z.object({
1342
+ pool_runtime_id: z.uuid(),
1343
+ server_url: z.string(),
1344
+ enrollment_secret: z.string().min(1),
1345
+ template_digest: z.string(),
1346
+ template_name: z.string(),
1347
+ template_version: z.string()
1348
+ });
1349
+ z.union([ProviderInputSchema, PoolProviderInputSchema]);
1350
+ z.object({
1351
+ v: z.literal(3),
1352
+ type: z.literal("pool_registered"),
1353
+ pool_runtime_id: z.uuid(),
1354
+ template: z.object({
1355
+ name: z.string(),
1356
+ version: z.string(),
1357
+ digest: z.string()
1358
+ }),
1359
+ agent_version: z.string()
1360
+ });
1361
+ z.object({
1362
+ v: z.literal(3),
1363
+ type: z.literal("lease_assignment"),
1364
+ input: ProviderInputSchema
1365
+ });
1366
+ const SOURCE_CREDENTIAL_MAX_BYTES = 65536;
1367
+ const EnvelopeBase = z.object({
1368
+ v: z.union([
1369
+ z.literal(1),
1370
+ z.literal(2),
1371
+ z.literal(3),
1372
+ z.literal(4),
1373
+ z.literal(5),
1374
+ z.literal(6)
1375
+ ]),
1376
+ workspace_id: z.uuid(),
1377
+ connection_id: z.uuid(),
1378
+ seq: z.number().int().nonnegative(),
1379
+ sent_at: z.iso.datetime()
1380
+ });
1381
+ const RegisteredPayload = z.object({
1382
+ agent_version: z.string(),
1383
+ template: z.object({
1384
+ name: z.string(),
1385
+ version: z.string(),
1386
+ digest: z.string()
1387
+ }),
1388
+ agentapi_version: z.string().optional(),
1389
+ services: z.array(z.string()),
1390
+ pid: z.number().int().positive()
1391
+ });
1392
+ const HeartbeatPayload = z.object({
1393
+ child: z.enum([
1394
+ "starting",
1395
+ "setup",
1396
+ "running",
1397
+ "exited",
1398
+ "terminating"
1399
+ ]),
1400
+ agentapi_state: z.enum([
1401
+ "unknown",
1402
+ "stable",
1403
+ "running"
1404
+ ]).optional()
1405
+ });
1406
+ const ProcessStatePayload = z.object({
1407
+ phase: z.enum([
1408
+ "starting",
1409
+ "setup",
1410
+ "running",
1411
+ "exited",
1412
+ "terminating"
1413
+ ]),
1414
+ exit_code: z.number().int().nullable().optional(),
1415
+ setup_step: z.string().optional(),
1416
+ detail: z.string().max(512).optional()
1417
+ });
1418
+ const ServiceHealthPayload = z.object({
1419
+ service: z.string(),
1420
+ health: z.enum([
1421
+ "unknown",
1422
+ "starting",
1423
+ "healthy",
1424
+ "unhealthy"
1425
+ ]),
1426
+ detail: z.string().max(512).optional()
1427
+ });
1428
+ const AgentStatePayload = z.object({ state: z.enum(["running", "stable"]) });
1429
+ const NetworkStatePayload = z.object({
1430
+ state: z.enum([
1431
+ "starting",
1432
+ "ready",
1433
+ "degraded"
1434
+ ]),
1435
+ detail: z.string().max(512).optional()
1436
+ });
1437
+ const LogChunkPayload = z.object({
1438
+ stream: z.enum([
1439
+ "stdout",
1440
+ "stderr",
1441
+ "runtime"
1442
+ ]),
1443
+ content_b64: z.string().max(87400),
1444
+ occurred_at: z.iso.datetime()
1445
+ });
1446
+ const ProxyResponsePayload = z.object({
1447
+ request_id: z.uuid(),
1448
+ status: z.number().int().min(100).max(599).optional(),
1449
+ headers: z.record(z.string(), z.string()).default({}),
1450
+ body_b64: z.string().optional(),
1451
+ error_code: z.enum([
1452
+ "unreachable",
1453
+ "deadline",
1454
+ "too_large"
1455
+ ]).optional()
1456
+ });
1457
+ const TerminationAckPayload = z.object({ phase: z.enum([
1458
+ "term_sent",
1459
+ "killed",
1460
+ "exited"
1461
+ ]) });
1462
+ const SourceResolvedPayload = z.object({
1463
+ repository: z.string().min(1).max(64),
1464
+ requested_revision: z.string().min(1).max(256),
1465
+ resolved_commit: z.string().regex(/^[0-9a-f]{40,64}$/)
1466
+ });
1467
+ const CheckpointStatusPayload = z.object({
1468
+ operation_id: z.uuid(),
1469
+ phase: z.enum([
1470
+ "quiescing",
1471
+ "quiesced",
1472
+ "failed"
1473
+ ]),
1474
+ detail: z.string().max(512).optional()
1475
+ });
1476
+ const OutputPublishedPayload = z.object({
1477
+ name: z.string().min(1).max(64),
1478
+ value: z.unknown()
1479
+ });
1480
+ const RestoreStatusPayload = z.object({
1481
+ phase: z.enum([
1482
+ "validating",
1483
+ "ready",
1484
+ "failed"
1485
+ ]),
1486
+ capability: z.enum(CONVERSATION_RESTORE_CAPABILITIES),
1487
+ detail: z.string().max(512).optional()
1488
+ });
1489
+ const ConversationMessagePayload = ConversationMessageInputSchema;
1490
+ z.discriminatedUnion("type", [
1491
+ EnvelopeBase.extend({
1492
+ type: z.literal("registered"),
1493
+ payload: RegisteredPayload
1494
+ }),
1495
+ EnvelopeBase.extend({
1496
+ type: z.literal("heartbeat"),
1497
+ payload: HeartbeatPayload
1498
+ }),
1499
+ EnvelopeBase.extend({
1500
+ type: z.literal("process_state"),
1501
+ payload: ProcessStatePayload
1502
+ }),
1503
+ EnvelopeBase.extend({
1504
+ type: z.literal("service_health"),
1505
+ payload: ServiceHealthPayload
1506
+ }),
1507
+ EnvelopeBase.extend({
1508
+ type: z.literal("agent_state"),
1509
+ payload: AgentStatePayload
1510
+ }),
1511
+ EnvelopeBase.extend({
1512
+ type: z.literal("network_state"),
1513
+ payload: NetworkStatePayload
1514
+ }),
1515
+ EnvelopeBase.extend({
1516
+ type: z.literal("log_chunk"),
1517
+ payload: LogChunkPayload
1518
+ }),
1519
+ EnvelopeBase.extend({
1520
+ type: z.literal("proxy_response"),
1521
+ payload: ProxyResponsePayload
1522
+ }),
1523
+ EnvelopeBase.extend({
1524
+ type: z.literal("proxy_stream_start"),
1525
+ payload: ProxyStreamStartPayload
1526
+ }),
1527
+ EnvelopeBase.extend({
1528
+ type: z.literal("proxy_stream_chunk"),
1529
+ payload: ProxyStreamChunkPayload
1530
+ }),
1531
+ EnvelopeBase.extend({
1532
+ type: z.literal("proxy_stream_end"),
1533
+ payload: ProxyStreamEndPayload
1534
+ }),
1535
+ EnvelopeBase.extend({
1536
+ type: z.literal("terminal_opened"),
1537
+ payload: TerminalOpenedPayload
1538
+ }),
1539
+ EnvelopeBase.extend({
1540
+ type: z.literal("terminal_output"),
1541
+ payload: TerminalOutputPayload
1542
+ }),
1543
+ EnvelopeBase.extend({
1544
+ type: z.literal("terminal_closed"),
1545
+ payload: TerminalClosedPayload
1546
+ }),
1547
+ EnvelopeBase.extend({
1548
+ type: z.literal("termination_ack"),
1549
+ payload: TerminationAckPayload
1550
+ }),
1551
+ EnvelopeBase.extend({
1552
+ type: z.literal("source_resolved"),
1553
+ payload: SourceResolvedPayload
1554
+ }),
1555
+ EnvelopeBase.extend({
1556
+ type: z.literal("checkpoint_status"),
1557
+ payload: CheckpointStatusPayload
1558
+ }),
1559
+ EnvelopeBase.extend({
1560
+ type: z.literal("output_published"),
1561
+ payload: OutputPublishedPayload
1562
+ }),
1563
+ EnvelopeBase.extend({
1564
+ type: z.literal("restore_status"),
1565
+ payload: RestoreStatusPayload
1566
+ }),
1567
+ EnvelopeBase.extend({
1568
+ type: z.literal("conversation_message"),
1569
+ payload: ConversationMessagePayload
1570
+ }),
1571
+ EnvelopeBase.extend({
1572
+ type: z.literal("attachment_ack"),
1573
+ payload: AttachmentAckPayload
1574
+ }),
1575
+ EnvelopeBase.extend({
1576
+ type: z.literal("attachment_result"),
1577
+ payload: AttachmentResultPayload
1578
+ }),
1579
+ EnvelopeBase.extend({
1580
+ type: z.literal("attachment_resolved"),
1581
+ payload: AttachmentResolvedPayload
1582
+ })
1583
+ ]);
1584
+ const ExecSpecSchema = z.object({
1585
+ agentapi_native: z.boolean().default(false),
1586
+ setup: z.array(SetupStepSchema),
1587
+ harness: HarnessSchema,
1588
+ env: z.record(z.string(), z.string()),
1589
+ services: z.record(z.string(), ServiceSchema),
1590
+ terminal: z.object({
1591
+ command: z.array(z.string().min(1)).min(1),
1592
+ cwd: z.string().optional(),
1593
+ env: z.record(z.string(), z.string()),
1594
+ max_sessions: z.number().int().min(1).max(8),
1595
+ idle_timeout_seconds: z.number().int().positive(),
1596
+ replay_buffer_bytes: z.number().int().positive()
1597
+ }).nullable().default(null),
1598
+ timeouts: TimeoutsSchema,
1599
+ security: z.object({ writable_memory_paths: z.array(z.string()) }).default({ writable_memory_paths: [] }),
1600
+ network: z.discriminatedUnion("mode", [z.object({ mode: z.literal("unrestricted") }), z.object({
1601
+ mode: z.literal("restricted"),
1602
+ proxy_url: z.url(),
1603
+ health_url: z.url()
1604
+ })]).default({ mode: "unrestricted" }),
1605
+ launch_mode: z.enum(LAUNCH_MODES).default("create"),
1606
+ source: SourceDescriptorSchema.extend({
1607
+ url: z.url(),
1608
+ destination: z.string(),
1609
+ credential: z.string().min(1).max(SOURCE_CREDENTIAL_MAX_BYTES).refine((value) => !value.includes("\0"), "credential must not contain NUL bytes").nullable().default(null)
1610
+ }).nullable().default(null),
1611
+ restore: z.object({
1612
+ checkpoint_id: z.uuid(),
1613
+ origin_workspace_id: z.uuid()
1614
+ }).nullable().default(null),
1615
+ persistence: z.object({
1616
+ mounts: z.array(z.object({
1617
+ name: z.string(),
1618
+ target: z.string()
1619
+ })),
1620
+ conversation_restore: z.enum(CONVERSATION_RESTORE_CAPABILITIES)
1621
+ }),
1622
+ checkpoint_hook: z.object({
1623
+ command: z.array(z.string().min(1)).min(1),
1624
+ timeout_seconds: z.number().int().positive(),
1625
+ env: z.record(z.string(), z.string()),
1626
+ cwd: z.string().optional()
1627
+ }).nullable(),
1628
+ outputs: z.record(z.string(), z.unknown())
1629
+ });
1630
+ const RegisteredAckPayload = z.object({
1631
+ epoch: z.number().int().positive(),
1632
+ reconnect_credential: z.string().optional(),
1633
+ limits: z.object({
1634
+ max_frame_bytes: z.number().int().positive(),
1635
+ max_inflight_relay: z.number().int().positive(),
1636
+ log_chunk_bytes: z.number().int().positive(),
1637
+ heartbeat_seconds: z.number().int().positive()
1638
+ }),
1639
+ exec: ExecSpecSchema
1640
+ });
1641
+ const ProxyRequestPayload = z.object({
1642
+ request_id: z.uuid(),
1643
+ service: z.string(),
1644
+ method: z.enum([
1645
+ "GET",
1646
+ "POST",
1647
+ "PUT",
1648
+ "PATCH",
1649
+ "DELETE"
1650
+ ]),
1651
+ path: z.string(),
1652
+ query: z.record(z.string(), z.string()).default({}),
1653
+ headers: z.record(z.string(), z.string()).default({}),
1654
+ body_b64: z.string().optional(),
1655
+ deadline_ms: z.number().int().positive()
1656
+ });
1657
+ const SignalPayload = z.object({ signal: z.enum(["TERM", "KILL"]) });
1658
+ const HealthProbePayload = z.object({ service: z.string() });
1659
+ const ShutdownPayload = z.object({ reason: z.string().max(512) });
1660
+ const PrepareCheckpointPayload = z.object({
1661
+ operation_id: z.uuid(),
1662
+ deadline_ms: z.number().int().positive()
1663
+ });
1664
+ z.discriminatedUnion("type", [
1665
+ EnvelopeBase.extend({
1666
+ type: z.literal("registered_ack"),
1667
+ payload: RegisteredAckPayload
1668
+ }),
1669
+ EnvelopeBase.extend({
1670
+ type: z.literal("proxy_request"),
1671
+ payload: ProxyRequestPayload
1672
+ }),
1673
+ EnvelopeBase.extend({
1674
+ type: z.literal("proxy_stream_ack"),
1675
+ payload: ProxyStreamAckPayload
1676
+ }),
1677
+ EnvelopeBase.extend({
1678
+ type: z.literal("proxy_stream_cancel"),
1679
+ payload: ProxyStreamCancelPayload
1680
+ }),
1681
+ EnvelopeBase.extend({
1682
+ type: z.literal("terminal_open"),
1683
+ payload: TerminalOpenPayload
1684
+ }),
1685
+ EnvelopeBase.extend({
1686
+ type: z.literal("terminal_input"),
1687
+ payload: TerminalInputPayload
1688
+ }),
1689
+ EnvelopeBase.extend({
1690
+ type: z.literal("terminal_resize"),
1691
+ payload: TerminalResizePayload
1692
+ }),
1693
+ EnvelopeBase.extend({
1694
+ type: z.literal("terminal_close"),
1695
+ payload: TerminalClosePayload
1696
+ }),
1697
+ EnvelopeBase.extend({
1698
+ type: z.literal("signal"),
1699
+ payload: SignalPayload
1700
+ }),
1701
+ EnvelopeBase.extend({
1702
+ type: z.literal("health_probe"),
1703
+ payload: HealthProbePayload
1704
+ }),
1705
+ EnvelopeBase.extend({
1706
+ type: z.literal("shutdown"),
1707
+ payload: ShutdownPayload
1708
+ }),
1709
+ EnvelopeBase.extend({
1710
+ type: z.literal("prepare_checkpoint"),
1711
+ payload: PrepareCheckpointPayload
1712
+ }),
1713
+ EnvelopeBase.extend({
1714
+ type: z.literal("attachment_start"),
1715
+ payload: AttachmentStartPayload
1716
+ }),
1717
+ EnvelopeBase.extend({
1718
+ type: z.literal("attachment_chunk"),
1719
+ payload: AttachmentChunkPayload
1720
+ }),
1721
+ EnvelopeBase.extend({
1722
+ type: z.literal("attachment_finish"),
1723
+ payload: AttachmentFinishPayload
1724
+ }),
1725
+ EnvelopeBase.extend({
1726
+ type: z.literal("attachment_abort"),
1727
+ payload: AttachmentAbortPayload
1728
+ }),
1729
+ EnvelopeBase.extend({
1730
+ type: z.literal("attachment_resolve"),
1731
+ payload: AttachmentResolvePayload
1732
+ })
1733
+ ]);
1734
+ //#endregion
1735
+ //#region src/admin.ts
1736
+ var AdministrationApi = class {
1737
+ transport;
1738
+ constructor(transport) {
1739
+ this.transport = transport;
1740
+ }
1741
+ warmPools(options = {}) {
1742
+ return this.transport.request("/v1/warm-pools", WarmPoolInventorySchema, options);
1743
+ }
1744
+ storageInventory(options = {}) {
1745
+ return this.transport.request("/v1/storage/inventory", StorageInventorySchema, options);
1746
+ }
1747
+ pruneStorage(options = {}) {
1748
+ return this.transport.request("/v1/storage/prune", StoragePruneResultSchema, {
1749
+ method: "POST",
1750
+ signal: options.signal
1751
+ });
1752
+ }
1753
+ };
1754
+ //#endregion
1755
+ //#region src/errors.ts
1756
+ z.enum(["client.non_json_response", "client.invalid_response"]);
1757
+ var PocketCoderError = class extends Error {
1758
+ code;
1759
+ status;
1760
+ requestId;
1761
+ details;
1762
+ constructor(input) {
1763
+ super(input.message);
1764
+ this.name = "PocketCoderError";
1765
+ this.code = input.code;
1766
+ this.status = input.status;
1767
+ this.requestId = input.requestId;
1768
+ this.details = input.details;
1769
+ }
1770
+ };
1771
+ var ConversationGoneError = class extends PocketCoderError {};
1772
+ var WorkspaceTerminalError = class extends Error {
1773
+ workspace;
1774
+ constructor(workspace) {
1775
+ const reason = workspace.reason_code ?? workspace.failure?.reason_code ?? "no reason";
1776
+ const tail = workspace.failure?.log_tail.trim();
1777
+ super([`workspace ${workspace.id} reached ${workspace.state} (${reason})`, ...tail ? [`failure log:\n${tail}`] : []].join("\n"));
1778
+ this.name = "WorkspaceTerminalError";
1779
+ this.workspace = workspace;
1780
+ }
1781
+ };
1782
+ function responseError(response, body) {
1783
+ const parsed = ErrorEnvelopeSchema.safeParse(body);
1784
+ if (!parsed.success) return new PocketCoderError({
1785
+ message: `PocketCoder returned an invalid error response (${response.status})`,
1786
+ code: "client.invalid_response",
1787
+ status: response.status
1788
+ });
1789
+ const { code, message, request_id: requestId, details } = parsed.data.error;
1790
+ return new (code === "conversation.deleted" || code === "conversation.expired" ? ConversationGoneError : PocketCoderError)({
1791
+ message,
1792
+ code,
1793
+ status: response.status,
1794
+ requestId,
1795
+ details
1796
+ });
1797
+ }
1798
+ //#endregion
1799
+ //#region src/attachments.ts
1800
+ function contentDisposition(name) {
1801
+ const clean = name.replace(/[\r\n]/g, "");
1802
+ if (/^[ -~]*$/.test(clean)) return `attachment; filename="${clean.replace(/(["\\])/g, "\\$1")}"`;
1803
+ return `attachment; filename*=UTF-8''${encodeURIComponent(clean)}`;
1804
+ }
1805
+ function progressStream(body, total, onProgress) {
1806
+ const source = new Response(body).body ?? new ReadableStream({ start: (c) => c.close() });
1807
+ let uploaded = 0;
1808
+ return source.pipeThrough(new TransformStream({ transform(chunk, controller) {
1809
+ uploaded += chunk.byteLength;
1810
+ controller.enqueue(chunk);
1811
+ onProgress(Math.min(uploaded, total), total);
1812
+ } }));
1813
+ }
1814
+ var AttachmentsApi = class {
1815
+ transport;
1816
+ constructor(transport) {
1817
+ this.transport = transport;
1818
+ }
1819
+ async upload(workspaceId, input) {
1820
+ const id = (input.id ?? crypto.randomUUID()).toLowerCase();
1821
+ const body = input.onProgress ? progressStream(input.body, input.sizeBytes, input.onProgress) : input.body;
1822
+ const init = {
1823
+ method: "PUT",
1824
+ ...input.signal ? { signal: input.signal } : {},
1825
+ headers: {
1826
+ "content-type": input.mediaType ?? "application/octet-stream",
1827
+ "content-disposition": contentDisposition(input.name),
1828
+ "content-length": String(input.sizeBytes)
1829
+ },
1830
+ body,
1831
+ ...body instanceof ReadableStream ? { duplex: "half" } : {}
1832
+ };
1833
+ return await this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/attachments/${id}`, AttachmentDescriptorSchema, init);
1834
+ }
1835
+ };
1836
+ var AgentApi = class {
1837
+ transport;
1838
+ constructor(transport) {
1839
+ this.transport = transport;
1840
+ }
1841
+ async sendMessage(workspaceId, input) {
1842
+ const response = await this.transport.raw(`/v1/workspaces/${encodeURIComponent(workspaceId)}/agent/message`, {
1843
+ method: "POST",
1844
+ ...input.signal ? { signal: input.signal } : {},
1845
+ headers: { "content-type": "application/json" },
1846
+ body: JSON.stringify({
1847
+ type: "user",
1848
+ content: input.content,
1849
+ ...input.attachmentIds?.length ? { attachment_ids: input.attachmentIds } : {}
1850
+ })
1851
+ });
1852
+ if (response.ok) return;
1853
+ const text = await response.text();
1854
+ let body;
1855
+ try {
1856
+ body = JSON.parse(text);
1857
+ } catch {
1858
+ throw new PocketCoderError({
1859
+ message: `PocketCoder returned a non-JSON response (${response.status})`,
1860
+ code: "client.non_json_response",
1861
+ status: response.status
1862
+ });
1863
+ }
1864
+ throw responseError(response, body);
1865
+ }
1866
+ };
1867
+ //#endregion
1868
+ //#region src/common.ts
1869
+ function queryString(values) {
1870
+ const params = new URLSearchParams();
1871
+ for (const [key, value] of Object.entries(values)) if (value !== void 0) params.set(key, String(value));
1872
+ return params.toString();
1873
+ }
1874
+ function page(body) {
1875
+ return {
1876
+ items: body.items,
1877
+ nextCursor: body.next_cursor
1878
+ };
1879
+ }
1880
+ //#endregion
1881
+ //#region src/checkpoints.ts
1882
+ var CheckpointsApi = class {
1883
+ transport;
1884
+ constructor(transport) {
1885
+ this.transport = transport;
1886
+ }
1887
+ async list(workspaceId, query = {}, options = {}) {
1888
+ return page(await this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/checkpoints?${queryString({
1889
+ state: query.state,
1890
+ cursor: query.cursor,
1891
+ limit: query.limit ?? 50
1892
+ })}`, CheckpointPageSchema, options));
1893
+ }
1894
+ get(id, options = {}) {
1895
+ return this.transport.request(`/v1/checkpoints/${encodeURIComponent(id)}`, CheckpointResourceSchema, options);
1896
+ }
1897
+ verify(id, key, options = {}) {
1898
+ return this.operationRequest(`/v1/checkpoints/${encodeURIComponent(id)}/verify`, "POST", key, options);
1899
+ }
1900
+ delete(id, key, options = {}) {
1901
+ return this.operationRequest(`/v1/checkpoints/${encodeURIComponent(id)}`, "DELETE", key, options);
1902
+ }
1903
+ restore(id, input, key, options = {}) {
1904
+ return this.transport.request(`/v1/checkpoints/${encodeURIComponent(id)}/restore`, RestoreResponseSchema, {
1905
+ method: "POST",
1906
+ signal: options.signal,
1907
+ headers: { "Idempotency-Key": key },
1908
+ body: JSON.stringify(input)
1909
+ });
1910
+ }
1911
+ operationRequest(path, method, key, options) {
1912
+ return this.transport.request(path, OperationResourceSchema, {
1913
+ method,
1914
+ signal: options.signal,
1915
+ headers: { "Idempotency-Key": key }
1916
+ });
1917
+ }
1918
+ };
1919
+ var OperationsApi = class {
1920
+ transport;
1921
+ constructor(transport) {
1922
+ this.transport = transport;
1923
+ }
1924
+ get(id, options = {}) {
1925
+ return this.transport.request(`/v1/operations/${encodeURIComponent(id)}`, OperationResourceSchema, options);
1926
+ }
1927
+ };
1928
+ //#endregion
1929
+ //#region src/conversations.ts
1930
+ var ConversationsApi = class {
1931
+ transport;
1932
+ constructor(transport) {
1933
+ this.transport = transport;
1934
+ }
1935
+ async list(id, query = {}, options = {}) {
1936
+ return page(await this.transport.request(`/v1/workspaces/${encodeURIComponent(id)}/conversation?${queryString({
1937
+ cursor: query.cursor,
1938
+ limit: query.limit ?? 100
1939
+ })}`, ConversationPageSchema, options));
1940
+ }
1941
+ };
1942
+ //#endregion
1943
+ //#region src/diagnostics.ts
1944
+ var WorkspaceCursorApi = class {
1945
+ transport;
1946
+ resource;
1947
+ schema;
1948
+ constructor(transport, resource, schema) {
1949
+ this.transport = transport;
1950
+ this.resource = resource;
1951
+ this.schema = schema;
1952
+ }
1953
+ async list(workspaceId, query = {}, options = {}) {
1954
+ return page(await this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/${this.resource}?${queryString({
1955
+ cursor: query.cursor,
1956
+ limit: query.limit ?? 100
1957
+ })}`, this.schema, options));
1958
+ }
1959
+ };
1960
+ var LogsApi = class extends WorkspaceCursorApi {
1961
+ constructor(transport) {
1962
+ super(transport, "logs", LogPageSchema);
1963
+ }
1964
+ };
1965
+ var NetworkEventsApi = class extends WorkspaceCursorApi {
1966
+ constructor(transport) {
1967
+ super(transport, "network-events", NetworkEventPageSchema);
1968
+ }
1969
+ };
1970
+ var OutputsApi = class extends WorkspaceCursorApi {
1971
+ constructor(transport) {
1972
+ super(transport, "outputs", OutputPageSchema);
1973
+ }
1974
+ };
1975
+ //#endregion
1976
+ //#region src/templates.ts
1977
+ var TemplatesApi = class {
1978
+ transport;
1979
+ constructor(transport) {
1980
+ this.transport = transport;
1981
+ }
1982
+ async page(query = {}, options = {}) {
1983
+ return page(await this.transport.request(`/v1/templates?${queryString({
1984
+ limit: query.limit ?? 100,
1985
+ cursor: query.cursor
1986
+ })}`, TemplatePageSchema, options));
1987
+ }
1988
+ async list(options = {}) {
1989
+ return (await this.page({}, options)).items;
1990
+ }
1991
+ };
1992
+ //#endregion
1993
+ //#region src/terminals.ts
1994
+ var TerminalConnection = class {
1995
+ socket;
1996
+ messageListeners = /* @__PURE__ */ new Set();
1997
+ openListeners = /* @__PURE__ */ new Set();
1998
+ closeListeners = /* @__PURE__ */ new Set();
1999
+ errorListeners = /* @__PURE__ */ new Set();
2000
+ pendingMessages = [];
2001
+ opened = false;
2002
+ closedEvent = null;
2003
+ errored = false;
2004
+ constructor(socket) {
2005
+ this.socket = socket;
2006
+ this.opened = socket.readyState === WebSocket.OPEN;
2007
+ socket.addEventListener("open", () => {
2008
+ this.opened = true;
2009
+ for (const listener of this.openListeners) listener();
2010
+ });
2011
+ socket.addEventListener("message", (event) => this.handleMessage(event));
2012
+ socket.addEventListener("close", (event) => {
2013
+ this.closedEvent = event;
2014
+ for (const listener of this.closeListeners) listener(event);
2015
+ });
2016
+ socket.addEventListener("error", () => {
2017
+ this.errored = true;
2018
+ for (const listener of this.errorListeners) listener();
2019
+ });
2020
+ }
2021
+ onMessage(listener) {
2022
+ this.messageListeners.add(listener);
2023
+ for (const message of this.pendingMessages.splice(0)) listener(message);
2024
+ return () => this.messageListeners.delete(listener);
2025
+ }
2026
+ onOpen(listener) {
2027
+ this.openListeners.add(listener);
2028
+ if (this.opened) queueMicrotask(() => this.openListeners.has(listener) && listener());
2029
+ return () => this.openListeners.delete(listener);
2030
+ }
2031
+ onClose(listener) {
2032
+ this.closeListeners.add(listener);
2033
+ if (this.closedEvent) {
2034
+ const event = this.closedEvent;
2035
+ queueMicrotask(() => this.closeListeners.has(listener) && listener(event));
2036
+ }
2037
+ return () => this.closeListeners.delete(listener);
2038
+ }
2039
+ onError(listener) {
2040
+ this.errorListeners.add(listener);
2041
+ if (this.errored) queueMicrotask(() => this.errorListeners.has(listener) && listener());
2042
+ return () => this.errorListeners.delete(listener);
2043
+ }
2044
+ sendInput(value) {
2045
+ const bytes = typeof value === "string" ? Buffer.from(value) : value;
2046
+ for (let offset = 0; offset < bytes.byteLength; offset += TERMINAL_CHUNK_BYTES) this.socket.send(JSON.stringify({
2047
+ type: "input",
2048
+ data_b64: Buffer.from(bytes.subarray(offset, offset + TERMINAL_CHUNK_BYTES)).toString("base64")
2049
+ }));
2050
+ }
2051
+ resize(rows, cols) {
2052
+ this.socket.send(JSON.stringify({
2053
+ type: "resize",
2054
+ rows,
2055
+ cols
2056
+ }));
2057
+ }
2058
+ close(code = 1e3, reason = "client detached") {
2059
+ this.socket.close(code, reason);
2060
+ }
2061
+ handleMessage(event) {
2062
+ if (typeof event.data !== "string") return;
2063
+ let value;
2064
+ try {
2065
+ value = JSON.parse(event.data);
2066
+ } catch {
2067
+ return;
2068
+ }
2069
+ const parsed = ServerTerminalMessageSchema.safeParse(value);
2070
+ if (!parsed.success) return;
2071
+ if (this.messageListeners.size === 0) {
2072
+ this.pendingMessages.push(parsed.data);
2073
+ return;
2074
+ }
2075
+ for (const listener of this.messageListeners) listener(parsed.data);
2076
+ }
2077
+ };
2078
+ var TerminalsApi = class {
2079
+ transport;
2080
+ constructor(transport) {
2081
+ this.transport = transport;
2082
+ }
2083
+ connect(workspaceId, options = {}) {
2084
+ const query = options.sessionId ? `?${queryString({ session: options.sessionId })}` : "";
2085
+ return new TerminalConnection(this.transport.webSocket(`/v1/workspaces/${encodeURIComponent(workspaceId)}/terminal${query}`));
2086
+ }
2087
+ async list(workspaceId, query = {}, options = {}) {
2088
+ return page(await this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/terminal-sessions?${queryString({
2089
+ cursor: query.cursor,
2090
+ limit: query.limit ?? 50
2091
+ })}`, TerminalSessionPageSchema, options));
2092
+ }
2093
+ };
2094
+ //#endregion
2095
+ //#region src/transport.ts
2096
+ function baseUrlOf(value) {
2097
+ let url;
2098
+ try {
2099
+ url = new URL(value);
2100
+ } catch {
2101
+ throw new TypeError("PocketCoder baseUrl must be a valid HTTP(S) URL");
2102
+ }
2103
+ if (!["http:", "https:"].includes(url.protocol)) throw new TypeError("PocketCoder baseUrl must be a valid HTTP(S) URL");
2104
+ url.pathname = url.pathname.replace(/\/$/, "");
2105
+ url.search = "";
2106
+ url.hash = "";
2107
+ return url.toString().replace(/\/$/, "");
2108
+ }
2109
+ var PocketCoderTransport = class {
2110
+ baseUrl;
2111
+ apiKey;
2112
+ timeoutMs;
2113
+ maxRetries;
2114
+ fetchImpl;
2115
+ webSocketFactory;
2116
+ constructor(config, fetchImpl = fetch) {
2117
+ this.baseUrl = baseUrlOf(config.baseUrl);
2118
+ this.apiKey = config.apiKey;
2119
+ this.timeoutMs = config.timeoutMs ?? 6e4;
2120
+ this.maxRetries = config.maxRetries ?? 2;
2121
+ this.fetchImpl = config.fetch ?? fetchImpl;
2122
+ this.webSocketFactory = config.webSocket ?? ((url, headers) => new WebSocket(url, { headers }));
2123
+ }
2124
+ webSocket(path) {
2125
+ const url = new URL(`${this.baseUrl}${path.startsWith("/") ? path : `/${path}`}`);
2126
+ url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
2127
+ return this.webSocketFactory(url.toString(), { authorization: `Bearer ${this.apiKey}` });
2128
+ }
2129
+ async raw(path, init = {}) {
2130
+ const timeout = AbortSignal.timeout(this.timeoutMs);
2131
+ const signal = init.signal ? AbortSignal.any([init.signal, timeout]) : timeout;
2132
+ const request = {
2133
+ ...init,
2134
+ signal,
2135
+ headers: {
2136
+ authorization: `Bearer ${this.apiKey}`,
2137
+ ...init.body ? { "content-type": "application/json" } : {},
2138
+ ...init.headers
2139
+ }
2140
+ };
2141
+ const retryable = isIdempotent(request);
2142
+ for (let attempt = 0;; attempt += 1) try {
2143
+ const response = await this.fetchImpl(`${this.baseUrl}${path.startsWith("/") ? path : `/${path}`}`, request);
2144
+ if (!retryable || attempt >= this.maxRetries || !retryableStatus(response.status)) return response;
2145
+ await delay(retryDelayMs(response, attempt), signal);
2146
+ } catch (error) {
2147
+ if (!retryable || attempt >= this.maxRetries || signal.aborted) throw error;
2148
+ await delay(100 * 2 ** attempt, signal);
2149
+ }
2150
+ }
2151
+ async request(path, schema, init = {}) {
2152
+ const response = await this.raw(path, init);
2153
+ const text = await response.text();
2154
+ let body;
2155
+ try {
2156
+ body = text ? JSON.parse(text) : void 0;
2157
+ } catch {
2158
+ throw new PocketCoderError({
2159
+ message: `PocketCoder returned a non-JSON response (${response.status})`,
2160
+ code: "client.non_json_response",
2161
+ status: response.status
2162
+ });
2163
+ }
2164
+ if (!response.ok) throw responseError(response, body);
2165
+ const parsed = schema.safeParse(body);
2166
+ if (!parsed.success) throw new PocketCoderError({
2167
+ message: `PocketCoder returned an invalid response (${response.status})`,
2168
+ code: "client.invalid_response",
2169
+ status: response.status,
2170
+ details: { issues: z.treeifyError(parsed.error) }
2171
+ });
2172
+ return parsed.data;
2173
+ }
2174
+ };
2175
+ function isIdempotent(init) {
2176
+ const method = (init.method ?? "GET").toUpperCase();
2177
+ if ([
2178
+ "GET",
2179
+ "HEAD",
2180
+ "OPTIONS"
2181
+ ].includes(method)) return true;
2182
+ return new Headers(init.headers).has("idempotency-key");
2183
+ }
2184
+ function retryableStatus(status) {
2185
+ return status === 408 || status === 425 || status === 429 || status >= 500;
2186
+ }
2187
+ function retryDelayMs(response, attempt) {
2188
+ const retryAfter = response.headers.get("retry-after");
2189
+ if (retryAfter !== null) {
2190
+ const seconds = Number(retryAfter);
2191
+ if (Number.isFinite(seconds)) return Math.max(0, seconds * 1e3);
2192
+ const date = Date.parse(retryAfter);
2193
+ if (!Number.isNaN(date)) return Math.max(0, date - Date.now());
2194
+ }
2195
+ return 100 * 2 ** attempt;
2196
+ }
2197
+ async function delay(milliseconds, signal) {
2198
+ if (milliseconds === 0) return;
2199
+ await new Promise((resolve, reject) => {
2200
+ const onAbort = () => {
2201
+ clearTimeout(timer);
2202
+ reject(signal.reason);
2203
+ };
2204
+ const timer = setTimeout(() => {
2205
+ signal.removeEventListener("abort", onAbort);
2206
+ resolve();
2207
+ }, milliseconds);
2208
+ signal.addEventListener("abort", onAbort, { once: true });
2209
+ });
2210
+ }
2211
+ //#endregion
2212
+ //#region src/workspaces.ts
2213
+ const TERMINAL_WORKSPACE_STATES = new Set(TERMINAL_STATES);
2214
+ var WorkspacesApi = class {
2215
+ transport;
2216
+ constructor(transport) {
2217
+ this.transport = transport;
2218
+ }
2219
+ async list(query = {}, options = {}) {
2220
+ return page(await this.transport.request(`/v1/workspaces?${queryString({
2221
+ state: query.state,
2222
+ template: query.template,
2223
+ external_id: query.externalId,
2224
+ limit: query.limit ?? 50,
2225
+ cursor: query.cursor
2226
+ })}`, WorkspacePageSchema, options));
2227
+ }
2228
+ async *all(query = {}, options = {}) {
2229
+ let cursor;
2230
+ do {
2231
+ const current = await this.list({
2232
+ ...query,
2233
+ cursor
2234
+ }, options);
2235
+ for (const workspace of current.items) yield workspace;
2236
+ cursor = current.nextCursor ?? void 0;
2237
+ } while (cursor);
2238
+ }
2239
+ get(id, options = {}) {
2240
+ return this.transport.request(`/v1/workspaces/${encodeURIComponent(id)}`, WorkspaceResourceSchema, options);
2241
+ }
2242
+ create(input, options = {}) {
2243
+ const body = {
2244
+ external_id: input.externalId,
2245
+ template: {
2246
+ name: input.templateName,
2247
+ ...input.templateVersion ? { version: input.templateVersion } : {}
2248
+ },
2249
+ ...input.launchInput ? { launch_input: input.launchInput } : {},
2250
+ ...input.metadata ? { metadata: input.metadata } : {},
2251
+ ...input.source ? { source: input.source } : {}
2252
+ };
2253
+ return this.transport.request("/v1/workspaces", WorkspaceResourceSchema, {
2254
+ method: "POST",
2255
+ signal: options.signal,
2256
+ headers: { "Idempotency-Key": input.idempotencyKey ?? input.externalId },
2257
+ body: JSON.stringify(body)
2258
+ });
2259
+ }
2260
+ cancel(id, options = {}) {
2261
+ return this.transport.request(`/v1/workspaces/${encodeURIComponent(id)}/cancel`, WorkspaceResourceSchema, {
2262
+ method: "POST",
2263
+ signal: options.signal
2264
+ });
2265
+ }
2266
+ change(id, after, wait, options = {}) {
2267
+ return this.transport.request(`/v1/workspaces/${encodeURIComponent(id)}/changes?after=${after}&wait=${wait}`, WorkspaceChangeSchema, options);
2268
+ }
2269
+ async waitForReady(initial, timeoutMs, options = {}) {
2270
+ const deadline = Date.now() + timeoutMs;
2271
+ let workspace = initial;
2272
+ while (workspace.state !== "ready") {
2273
+ if (TERMINAL_STATES.includes(workspace.state)) throw new WorkspaceTerminalError(workspace);
2274
+ const remainingMs = deadline - Date.now();
2275
+ if (remainingMs <= 0) throw new Error(`workspace ${workspace.id} did not become ready within ${timeoutMs}ms`);
2276
+ workspace = (await this.change(workspace.id, workspace.change_cursor, Math.max(1, Math.min(30, Math.ceil(remainingMs / 1e3))), options)).workspace;
2277
+ options.onTick?.(workspace);
2278
+ }
2279
+ return workspace;
2280
+ }
2281
+ preserve(id, input, key, options = {}) {
2282
+ return this.jsonOperation(`/v1/workspaces/${encodeURIComponent(id)}/preserve`, input, key, PreserveResponseSchema, options);
2283
+ }
2284
+ recreate(id, input, key, options = {}) {
2285
+ return this.jsonOperation(`/v1/workspaces/${encodeURIComponent(id)}/recreate`, input, key, RestoreResponseSchema, options);
2286
+ }
2287
+ resume(id, input, key, options = {}) {
2288
+ return this.jsonOperation(`/v1/workspaces/${encodeURIComponent(id)}/resume`, input, key, ResumeResponseSchema, options);
2289
+ }
2290
+ jsonOperation(path, input, key, schema, options) {
2291
+ return this.transport.request(path, schema, {
2292
+ method: "POST",
2293
+ signal: options.signal,
2294
+ headers: { "Idempotency-Key": key },
2295
+ body: JSON.stringify(input)
2296
+ });
2297
+ }
2298
+ };
2299
+ //#endregion
2300
+ //#region src/client.ts
2301
+ var PocketCoderClient = class {
2302
+ transport;
2303
+ templates;
2304
+ workspaces;
2305
+ attachments;
2306
+ agent;
2307
+ conversations;
2308
+ checkpoints;
2309
+ operations;
2310
+ logs;
2311
+ networkEvents;
2312
+ outputs;
2313
+ administration;
2314
+ terminals;
2315
+ constructor(config, fetchImpl = fetch) {
2316
+ this.transport = new PocketCoderTransport(config, fetchImpl);
2317
+ this.templates = new TemplatesApi(this.transport);
2318
+ this.workspaces = new WorkspacesApi(this.transport);
2319
+ this.attachments = new AttachmentsApi(this.transport);
2320
+ this.agent = new AgentApi(this.transport);
2321
+ this.conversations = new ConversationsApi(this.transport);
2322
+ this.checkpoints = new CheckpointsApi(this.transport);
2323
+ this.operations = new OperationsApi(this.transport);
2324
+ this.logs = new LogsApi(this.transport);
2325
+ this.networkEvents = new NetworkEventsApi(this.transport);
2326
+ this.outputs = new OutputsApi(this.transport);
2327
+ this.administration = new AdministrationApi(this.transport);
2328
+ this.terminals = new TerminalsApi(this.transport);
2329
+ }
2330
+ raw(path, init = {}) {
2331
+ return this.transport.raw(path, init);
2332
+ }
2333
+ };
2334
+ //#endregion
2335
+ export { AdministrationApi, AgentApi, AttachmentsApi, CheckpointsApi, ConversationGoneError, ConversationsApi, LogsApi, NetworkEventsApi, OperationsApi, OutputsApi, PocketCoderClient, PocketCoderError, TERMINAL_WORKSPACE_STATES, TemplatesApi, TerminalConnection, TerminalsApi, WorkspaceTerminalError, WorkspacesApi, splitAttachmentManifest };