@pstdio/pocketcoder-sdk 0.6.0 → 0.7.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/README.md +9 -0
- package/dist/index.d.ts +2321 -89
- package/dist/index.js +1984 -380
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { posix } from "node:path";
|
|
2
4
|
import { isIP } from "node:net";
|
|
3
5
|
//#region ../contracts/src/attachments/attachment.ts
|
|
4
6
|
const ATTACHMENT_MAX_FILE_BYTES = 26214400;
|
|
@@ -93,14 +95,160 @@ const AttachmentResolvedPayload = z.object({
|
|
|
93
95
|
missing_id: z.uuid().optional()
|
|
94
96
|
});
|
|
95
97
|
//#endregion
|
|
98
|
+
//#region ../contracts/src/common/canonical.ts
|
|
99
|
+
function canonicalJson(value) {
|
|
100
|
+
return JSON.stringify(sortValue(value));
|
|
101
|
+
}
|
|
102
|
+
function sortValue(value) {
|
|
103
|
+
if (Array.isArray(value)) return value.map(sortValue);
|
|
104
|
+
if (value !== null && typeof value === "object") {
|
|
105
|
+
const out = {};
|
|
106
|
+
for (const key of Object.keys(value).sort()) {
|
|
107
|
+
const v = value[key];
|
|
108
|
+
if (v !== void 0) out[key] = sortValue(v);
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
function sha256Hex(input) {
|
|
115
|
+
return createHash("sha256").update(input).digest("hex");
|
|
116
|
+
}
|
|
117
|
+
function digestOf(value) {
|
|
118
|
+
return `sha256:${sha256Hex(canonicalJson(value))}`;
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region ../contracts/src/templates/workload-capabilities.ts
|
|
122
|
+
const WorkloadDigestSchema = z.string().regex(/^sha256:[0-9a-f]{64}$/);
|
|
123
|
+
const WorkloadImageSchema = z.string().regex(/^[^\s@]+@sha256:[0-9a-f]{64}$/);
|
|
124
|
+
const WorkloadNameSchema = z.string().regex(/^[a-z][a-z0-9-]{0,62}$/);
|
|
125
|
+
const WorkloadBoundSchema = z.number().int().positive().max(Number.MAX_SAFE_INTEGER);
|
|
126
|
+
const WorkloadCapabilitySchema = z.enum([
|
|
127
|
+
"ready-without-task",
|
|
128
|
+
"live-inputs",
|
|
129
|
+
"immutable-inputs",
|
|
130
|
+
"serial-operations",
|
|
131
|
+
"retained-exports",
|
|
132
|
+
"explicit-start",
|
|
133
|
+
"exact-exit",
|
|
134
|
+
"targeted-cancel",
|
|
135
|
+
"stop-before-export",
|
|
136
|
+
"request-lookup",
|
|
137
|
+
"deduplicated-accept",
|
|
138
|
+
"explicit-publication",
|
|
139
|
+
"workspace-services",
|
|
140
|
+
"revocable-preview-grants",
|
|
141
|
+
"workspace-cancel",
|
|
142
|
+
"unknown-outcome"
|
|
143
|
+
]);
|
|
144
|
+
const WorkloadProfileSchema = z.enum([
|
|
145
|
+
"finite-process",
|
|
146
|
+
"cloud-interactive",
|
|
147
|
+
"service"
|
|
148
|
+
]);
|
|
149
|
+
const common$1 = [
|
|
150
|
+
"ready-without-task",
|
|
151
|
+
"live-inputs",
|
|
152
|
+
"immutable-inputs",
|
|
153
|
+
"serial-operations",
|
|
154
|
+
"retained-exports"
|
|
155
|
+
];
|
|
156
|
+
[...common$1], [...common$1];
|
|
157
|
+
const WorkloadLimitsSchema = z.strictObject({
|
|
158
|
+
deadlineSeconds: WorkloadBoundSchema,
|
|
159
|
+
finalizationSeconds: WorkloadBoundSchema,
|
|
160
|
+
inputFileBytes: WorkloadBoundSchema,
|
|
161
|
+
stagedInputBytes: WorkloadBoundSchema,
|
|
162
|
+
inputFiles: WorkloadBoundSchema,
|
|
163
|
+
inFlightTransfers: WorkloadBoundSchema,
|
|
164
|
+
exportBytes: WorkloadBoundSchema,
|
|
165
|
+
exportFiles: WorkloadBoundSchema,
|
|
166
|
+
unreferencedInputRetentionSeconds: WorkloadBoundSchema,
|
|
167
|
+
exportRetentionSeconds: WorkloadBoundSchema
|
|
168
|
+
}).superRefine((limits, ctx) => {
|
|
169
|
+
for (const [smaller, larger] of [["inputFileBytes", "stagedInputBytes"], ["finalizationSeconds", "deadlineSeconds"]]) if (limits[smaller] > limits[larger]) ctx.addIssue({
|
|
170
|
+
code: "custom",
|
|
171
|
+
path: [smaller],
|
|
172
|
+
message: `${smaller} must not exceed ${larger}`
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
const WorkloadRuntimePinSchema = z.strictObject({
|
|
176
|
+
digest: WorkloadDigestSchema,
|
|
177
|
+
protocol: z.literal("pocketcoder.workload/v1")
|
|
178
|
+
});
|
|
179
|
+
const InstalledWorkloadSchema = z.strictObject({
|
|
180
|
+
workspaceId: z.uuid(),
|
|
181
|
+
incarnationId: z.string().min(1).max(256),
|
|
182
|
+
templateDigest: WorkloadDigestSchema,
|
|
183
|
+
image: WorkloadImageSchema,
|
|
184
|
+
runtime: WorkloadRuntimePinSchema,
|
|
185
|
+
storage: z.strictObject({
|
|
186
|
+
identity: z.string().min(1).max(256),
|
|
187
|
+
profile: z.enum(["controller-readonly/v1", "shared-writer-readonly/v1"])
|
|
188
|
+
}),
|
|
189
|
+
capabilities: z.array(WorkloadCapabilitySchema),
|
|
190
|
+
adapters: z.record(WorkloadNameSchema, z.strictObject({
|
|
191
|
+
kind: z.enum([
|
|
192
|
+
"process",
|
|
193
|
+
"service",
|
|
194
|
+
"pi",
|
|
195
|
+
"agentapi"
|
|
196
|
+
]),
|
|
197
|
+
digest: WorkloadDigestSchema,
|
|
198
|
+
protocol: z.string().min(1).max(128),
|
|
199
|
+
capabilities: z.array(WorkloadCapabilitySchema)
|
|
200
|
+
}))
|
|
201
|
+
});
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region ../contracts/src/attachments/input-reference.ts
|
|
204
|
+
const InputObjectReferenceSchema = z.strictObject({
|
|
205
|
+
workspaceId: z.uuid(),
|
|
206
|
+
fileId: z.uuid(),
|
|
207
|
+
objectVersion: WorkloadDigestSchema,
|
|
208
|
+
name: z.string().min(1).max(255),
|
|
209
|
+
mediaType: AttachmentMediaTypeSchema,
|
|
210
|
+
sizeBytes: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
211
|
+
digest: WorkloadDigestSchema,
|
|
212
|
+
visibility: z.enum([
|
|
213
|
+
"ready",
|
|
214
|
+
"unavailable",
|
|
215
|
+
"expired"
|
|
216
|
+
])
|
|
217
|
+
});
|
|
218
|
+
const AcceptedInputManifestSchema = z.strictObject({
|
|
219
|
+
workspaceId: z.uuid(),
|
|
220
|
+
incarnationId: z.string().min(1).max(256),
|
|
221
|
+
digest: WorkloadDigestSchema,
|
|
222
|
+
objects: z.array(InputObjectReferenceSchema)
|
|
223
|
+
}).superRefine((manifest, ctx) => {
|
|
224
|
+
const { digest, ...contents } = manifest;
|
|
225
|
+
if (digest !== digestOf(contents)) ctx.addIssue({
|
|
226
|
+
code: "custom",
|
|
227
|
+
path: ["digest"],
|
|
228
|
+
message: "input manifest digest does not match its objects"
|
|
229
|
+
});
|
|
230
|
+
const ids = /* @__PURE__ */ new Set();
|
|
231
|
+
for (const [index, object] of manifest.objects.entries()) {
|
|
232
|
+
if (object.workspaceId !== manifest.workspaceId || object.visibility !== "ready" || ids.has(object.fileId)) ctx.addIssue({
|
|
233
|
+
code: "custom",
|
|
234
|
+
path: ["objects", index],
|
|
235
|
+
message: "accepted inputs must be unique, ready and workspace-scoped"
|
|
236
|
+
});
|
|
237
|
+
ids.add(object.fileId);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
//#endregion
|
|
96
241
|
//#region ../contracts/src/common/scopes.ts
|
|
97
242
|
const SCOPES = [
|
|
98
243
|
"templates:read",
|
|
244
|
+
"templates:write",
|
|
245
|
+
"secrets:write",
|
|
99
246
|
"workspaces:create",
|
|
100
247
|
"workspaces:read",
|
|
101
248
|
"workspaces:cancel",
|
|
102
249
|
"workspaces:purge",
|
|
103
250
|
"workspaces:recover",
|
|
251
|
+
"principals:admin",
|
|
104
252
|
"keys:read",
|
|
105
253
|
"keys:write",
|
|
106
254
|
"workspaces:preserve",
|
|
@@ -109,8 +257,12 @@ const SCOPES = [
|
|
|
109
257
|
"checkpoints:delete",
|
|
110
258
|
"outputs:read",
|
|
111
259
|
"conversations:read",
|
|
260
|
+
"conversations:write",
|
|
112
261
|
"conversations:delete",
|
|
113
262
|
"services:relay",
|
|
263
|
+
"previews:open",
|
|
264
|
+
"display:view",
|
|
265
|
+
"display:control",
|
|
114
266
|
"attachments:write",
|
|
115
267
|
"logs:read",
|
|
116
268
|
"network:read",
|
|
@@ -121,13 +273,20 @@ const SCOPES = [
|
|
|
121
273
|
z.strictObject({
|
|
122
274
|
request_id: z.string().min(1).max(128).regex(/^[A-Za-z0-9._:-]+$/),
|
|
123
275
|
scopes: z.array(z.enum(SCOPES)).min(1).max(SCOPES.length),
|
|
276
|
+
templates: z.array(z.string().min(1).max(128)).max(100).optional(),
|
|
277
|
+
managed_principal_ids: z.array(z.uuid()).max(100).optional(),
|
|
124
278
|
expires_at: z.iso.datetime()
|
|
125
|
-
})
|
|
279
|
+
}).pick({
|
|
280
|
+
request_id: true,
|
|
281
|
+
expires_at: true
|
|
282
|
+
}).partial({ expires_at: true });
|
|
126
283
|
const KeyResourceSchema = z.object({
|
|
127
284
|
id: z.uuid(),
|
|
128
285
|
principal_id: z.uuid(),
|
|
129
286
|
scopes: z.array(z.string()),
|
|
130
287
|
effective_scopes: z.array(z.string()),
|
|
288
|
+
templates: z.array(z.string()).nullable(),
|
|
289
|
+
effective_templates: z.array(z.string()),
|
|
131
290
|
managed_principal_ids: z.array(z.uuid()),
|
|
132
291
|
issuance_request_id: z.string().nullable(),
|
|
133
292
|
created_at: z.iso.datetime(),
|
|
@@ -143,12 +302,265 @@ const KeyListResponseSchema = z.object({
|
|
|
143
302
|
items: z.array(KeyResourceSchema),
|
|
144
303
|
next_cursor: z.uuid().nullable()
|
|
145
304
|
});
|
|
305
|
+
const ExpiringKeySchema = KeyResourceSchema.extend({ principal_name: z.string() });
|
|
306
|
+
const ExpiringKeyListResponseSchema = z.object({ items: z.array(ExpiringKeySchema) });
|
|
307
|
+
//#endregion
|
|
308
|
+
//#region ../contracts/src/auth/principals.ts
|
|
309
|
+
const PrincipalGrantsSchema = z.strictObject({
|
|
310
|
+
scopes: z.array(z.enum(SCOPES)).max(SCOPES.length),
|
|
311
|
+
templates: z.array(z.string().min(1).max(128)).max(100)
|
|
312
|
+
});
|
|
313
|
+
PrincipalGrantsSchema.extend({ name: z.string().min(1).max(128) });
|
|
314
|
+
const PrincipalResourceSchema = z.object({
|
|
315
|
+
id: z.uuid(),
|
|
316
|
+
name: z.string(),
|
|
317
|
+
scopes: z.array(z.string()),
|
|
318
|
+
templates: z.array(z.string()),
|
|
319
|
+
disabled_at: z.iso.datetime().nullable(),
|
|
320
|
+
created_at: z.iso.datetime()
|
|
321
|
+
});
|
|
322
|
+
const PrincipalListResponseSchema = z.object({ items: z.array(PrincipalResourceSchema) });
|
|
323
|
+
PrincipalGrantsSchema.partial().extend({ disabled: z.boolean().optional() }).refine((value) => Object.keys(value).length > 0, { message: "Provide a grant or disabled status." });
|
|
146
324
|
//#endregion
|
|
147
325
|
//#region ../contracts/src/common/duration.ts
|
|
148
326
|
const DURATION_RE = /^(\d+)(ms|s|m|h)$/;
|
|
327
|
+
const UNIT_MS = {
|
|
328
|
+
ms: 1,
|
|
329
|
+
s: 1e3,
|
|
330
|
+
m: 6e4,
|
|
331
|
+
h: 36e5
|
|
332
|
+
};
|
|
149
333
|
function isDuration(value) {
|
|
150
334
|
return DURATION_RE.test(value);
|
|
151
335
|
}
|
|
336
|
+
function parseDurationMs(value) {
|
|
337
|
+
const match = DURATION_RE.exec(value);
|
|
338
|
+
if (!match) throw new Error(`invalid duration: ${value}`);
|
|
339
|
+
const amount = Number(match[1]);
|
|
340
|
+
const unit = UNIT_MS[match[2]];
|
|
341
|
+
if (unit === void 0) throw new Error(`invalid duration unit: ${value}`);
|
|
342
|
+
return amount * unit;
|
|
343
|
+
}
|
|
344
|
+
//#endregion
|
|
345
|
+
//#region ../contracts/src/persistence/artifacts.ts
|
|
346
|
+
const SEGMENT = /^(?!\.\.?$)[A-Za-z0-9._-]{1,128}$/;
|
|
347
|
+
const PrivateArtifactSchema = z.object({
|
|
348
|
+
name: z.string().regex(/^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/),
|
|
349
|
+
mount: z.string().regex(/^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/),
|
|
350
|
+
pattern: z.string().max(512).refine((pattern) => {
|
|
351
|
+
const segments = pattern.split("/");
|
|
352
|
+
return segments.every((segment) => segment === "*" || SEGMENT.test(segment)) && segments.filter((segment) => segment === "*").length <= 1;
|
|
353
|
+
}, "pattern must be relative path segments with at most one * segment"),
|
|
354
|
+
maxBytes: z.number().int().positive().max(67108864).default(8388608)
|
|
355
|
+
});
|
|
356
|
+
//#endregion
|
|
357
|
+
//#region ../contracts/src/persistence/persistence.ts
|
|
358
|
+
const DurationValueSchema = z.string().refine(isDuration, { message: "expected a duration like 15s, 20m, or 2h" });
|
|
359
|
+
const LAUNCH_MODES = ["create", "restore"];
|
|
360
|
+
const CONVERSATION_RESTORE_CAPABILITIES = [
|
|
361
|
+
"supported",
|
|
362
|
+
"filesystem_only",
|
|
363
|
+
"unknown"
|
|
364
|
+
];
|
|
365
|
+
const SourceDescriptorSchema = z.object({
|
|
366
|
+
kind: z.literal("git"),
|
|
367
|
+
repository: z.string().min(1).max(64).regex(/^[a-z0-9]([a-z0-9._-]{0,62}[a-z0-9])?$/),
|
|
368
|
+
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) => {
|
|
369
|
+
const code = character.codePointAt(0) ?? 0;
|
|
370
|
+
return code >= 32 && code !== 127;
|
|
371
|
+
}), { message: "revision is not a safe branch, tag, or commit name" })
|
|
372
|
+
});
|
|
373
|
+
const ResolvedSourceSchema = z.object({
|
|
374
|
+
kind: z.literal("git"),
|
|
375
|
+
repository: z.string().min(1).max(64),
|
|
376
|
+
requested_revision: z.string().min(1).max(256),
|
|
377
|
+
resolved_commit: z.string().regex(/^[0-9a-f]{40,64}$/)
|
|
378
|
+
});
|
|
379
|
+
const PersistenceMountSchema = z.object({
|
|
380
|
+
name: z.string().min(1).max(64).regex(/^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/),
|
|
381
|
+
target: z.string().min(1),
|
|
382
|
+
maxBytes: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
383
|
+
maxFiles: z.number().int().positive().max(1e7)
|
|
384
|
+
});
|
|
385
|
+
const CheckpointPolicySchema = z.object({
|
|
386
|
+
onIdle: z.enum(["preserve", "destroy"]).default("destroy"),
|
|
387
|
+
onDeadline: z.enum(["preserve", "destroy"]).default("destroy"),
|
|
388
|
+
onCleanExit: z.enum(["preserve", "destroy"]).default("destroy"),
|
|
389
|
+
onFailure: z.enum([
|
|
390
|
+
"preserve",
|
|
391
|
+
"retain-for-recovery",
|
|
392
|
+
"destroy"
|
|
393
|
+
]).default("destroy"),
|
|
394
|
+
retention: DurationValueSchema.default("168h")
|
|
395
|
+
});
|
|
396
|
+
const PersistenceSpecSchema = z.object({
|
|
397
|
+
mounts: z.array(PersistenceMountSchema).max(16).default([]),
|
|
398
|
+
conversationRestore: z.enum(CONVERSATION_RESTORE_CAPABILITIES).default("filesystem_only"),
|
|
399
|
+
conversationRetention: DurationValueSchema.default("168h"),
|
|
400
|
+
sessionCompatibility: z.string().min(1).max(128).optional(),
|
|
401
|
+
checkpoint: CheckpointPolicySchema.prefault({}),
|
|
402
|
+
artifacts: z.array(PrivateArtifactSchema).max(16).optional()
|
|
403
|
+
});
|
|
404
|
+
const OutputDeclarationSchema = z.discriminatedUnion("type", [
|
|
405
|
+
z.object({ type: z.literal("gitSha") }),
|
|
406
|
+
z.object({
|
|
407
|
+
type: z.literal("string"),
|
|
408
|
+
maxLength: z.number().int().positive().max(4096).default(512)
|
|
409
|
+
}),
|
|
410
|
+
z.object({
|
|
411
|
+
type: z.literal("httpsUrl"),
|
|
412
|
+
maxLength: z.number().int().positive().max(4096).default(2048)
|
|
413
|
+
})
|
|
414
|
+
]);
|
|
415
|
+
const CHECKPOINT_STATES = [
|
|
416
|
+
"creating",
|
|
417
|
+
"ready",
|
|
418
|
+
"failed",
|
|
419
|
+
"deleting",
|
|
420
|
+
"deleted"
|
|
421
|
+
];
|
|
422
|
+
const OPERATION_KINDS = [
|
|
423
|
+
"preserve",
|
|
424
|
+
"restore",
|
|
425
|
+
"verify",
|
|
426
|
+
"delete",
|
|
427
|
+
"purge"
|
|
428
|
+
];
|
|
429
|
+
const OPERATION_STATES = [
|
|
430
|
+
"pending",
|
|
431
|
+
"running",
|
|
432
|
+
"succeeded",
|
|
433
|
+
"failed"
|
|
434
|
+
];
|
|
435
|
+
const CheckpointResourceSchema = z.object({
|
|
436
|
+
id: z.uuid(),
|
|
437
|
+
workspace_id: z.uuid(),
|
|
438
|
+
state: z.enum(CHECKPOINT_STATES),
|
|
439
|
+
reason_code: z.string().nullable(),
|
|
440
|
+
template: z.object({
|
|
441
|
+
name: z.string(),
|
|
442
|
+
version: z.string(),
|
|
443
|
+
digest: z.string()
|
|
444
|
+
}),
|
|
445
|
+
manifest_digest: z.string().nullable(),
|
|
446
|
+
logical_bytes: z.number().int().nonnegative().nullable(),
|
|
447
|
+
stored_bytes: z.number().int().nonnegative().nullable(),
|
|
448
|
+
file_count: z.number().int().nonnegative().nullable(),
|
|
449
|
+
mounts: z.array(z.string()),
|
|
450
|
+
conversation_restore: z.enum(CONVERSATION_RESTORE_CAPABILITIES),
|
|
451
|
+
label: z.string().nullable(),
|
|
452
|
+
created_at: z.iso.datetime(),
|
|
453
|
+
ready_at: z.iso.datetime().nullable(),
|
|
454
|
+
expires_at: z.iso.datetime().nullable()
|
|
455
|
+
});
|
|
456
|
+
const OperationResourceSchema = z.object({
|
|
457
|
+
id: z.uuid(),
|
|
458
|
+
kind: z.enum(OPERATION_KINDS),
|
|
459
|
+
state: z.enum(OPERATION_STATES),
|
|
460
|
+
workspace_id: z.uuid().nullable(),
|
|
461
|
+
checkpoint_id: z.uuid().nullable(),
|
|
462
|
+
result_workspace_id: z.uuid().nullable(),
|
|
463
|
+
reason_code: z.string().nullable(),
|
|
464
|
+
created_at: z.iso.datetime(),
|
|
465
|
+
updated_at: z.iso.datetime(),
|
|
466
|
+
completed_at: z.iso.datetime().nullable()
|
|
467
|
+
});
|
|
468
|
+
z.object({
|
|
469
|
+
retention: DurationValueSchema.optional(),
|
|
470
|
+
label: z.string().min(1).max(128).optional()
|
|
471
|
+
});
|
|
472
|
+
z.object({
|
|
473
|
+
external_id: z.string().min(1).max(256),
|
|
474
|
+
metadata: z.record(z.string().max(64), z.string().max(512)).optional(),
|
|
475
|
+
launch_input: z.record(z.string(), z.unknown()).optional()
|
|
476
|
+
});
|
|
477
|
+
const CheckpointManifestEntrySchema = z.object({
|
|
478
|
+
path: z.string(),
|
|
479
|
+
kind: z.enum([
|
|
480
|
+
"directory",
|
|
481
|
+
"file",
|
|
482
|
+
"symlink"
|
|
483
|
+
]),
|
|
484
|
+
mode: z.number().int().nonnegative(),
|
|
485
|
+
uid: z.number().int().nonnegative(),
|
|
486
|
+
gid: z.number().int().nonnegative(),
|
|
487
|
+
mtime_ns: z.string().regex(/^\d+$/),
|
|
488
|
+
size: z.number().int().nonnegative(),
|
|
489
|
+
digest: z.string().optional(),
|
|
490
|
+
link_target: z.string().optional()
|
|
491
|
+
});
|
|
492
|
+
z.object({
|
|
493
|
+
format: z.literal("pocketcoder-checkpoint/v1"),
|
|
494
|
+
checkpoint_id: z.uuid(),
|
|
495
|
+
template_digest: z.string(),
|
|
496
|
+
mounts: z.array(z.object({
|
|
497
|
+
name: z.string(),
|
|
498
|
+
entries: z.array(CheckpointManifestEntrySchema)
|
|
499
|
+
})),
|
|
500
|
+
logical_bytes: z.number().int().nonnegative(),
|
|
501
|
+
file_count: z.number().int().nonnegative()
|
|
502
|
+
});
|
|
503
|
+
//#endregion
|
|
504
|
+
//#region ../contracts/src/checkpoints/archive-format.ts
|
|
505
|
+
const CHECKPOINT_ARCHIVE_FORMAT = "pocketcoder-checkpoint-tar/v1";
|
|
506
|
+
const count = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
507
|
+
const digest$1 = z.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
508
|
+
const mount = z.strictObject({
|
|
509
|
+
name: PersistenceMountSchema.shape.name,
|
|
510
|
+
logical_bytes: count,
|
|
511
|
+
file_count: count
|
|
512
|
+
});
|
|
513
|
+
const mounts = z.array(mount).max(16).refine((items) => new Set(items.map((item) => item.name)).size === items.length);
|
|
514
|
+
const CheckpointArchiveHeaderSchema = z.strictObject({
|
|
515
|
+
format: z.literal(CHECKPOINT_ARCHIVE_FORMAT),
|
|
516
|
+
checkpoint_id: z.uuid(),
|
|
517
|
+
workspace_id: z.uuid(),
|
|
518
|
+
template_digest: digest$1,
|
|
519
|
+
mounts
|
|
520
|
+
});
|
|
521
|
+
function validUnicode(value) {
|
|
522
|
+
return Buffer.from(value).toString("utf8") === value;
|
|
523
|
+
}
|
|
524
|
+
function safeCheckpointPath(value) {
|
|
525
|
+
return validUnicode(value) && value === value.normalize("NFC") && !value.includes("\0") && !value.includes("\\") && value.split("/").every((part) => part !== "" && part !== "." && part !== "..");
|
|
526
|
+
}
|
|
527
|
+
function safeCheckpointLink(path, target) {
|
|
528
|
+
if (!target || !validUnicode(target) || target.startsWith("/") || target.includes("\0") || target.includes("\\")) return false;
|
|
529
|
+
const resolved = posix.normalize(posix.join(posix.dirname(path), target));
|
|
530
|
+
return resolved !== ".." && !resolved.startsWith("../") && !posix.isAbsolute(resolved);
|
|
531
|
+
}
|
|
532
|
+
const common = {
|
|
533
|
+
mount: z.number().int().min(0).max(15),
|
|
534
|
+
path: z.string().refine(safeCheckpointPath),
|
|
535
|
+
mode: z.number().int().min(0).max(511),
|
|
536
|
+
mtime_ns: z.string().regex(/^(0|[1-9]\d*)$/)
|
|
537
|
+
};
|
|
538
|
+
z.discriminatedUnion("kind", [
|
|
539
|
+
z.strictObject({
|
|
540
|
+
...common,
|
|
541
|
+
kind: z.literal("directory"),
|
|
542
|
+
size: z.literal(0)
|
|
543
|
+
}),
|
|
544
|
+
z.strictObject({
|
|
545
|
+
...common,
|
|
546
|
+
kind: z.literal("file"),
|
|
547
|
+
size: count,
|
|
548
|
+
digest: digest$1
|
|
549
|
+
}),
|
|
550
|
+
z.strictObject({
|
|
551
|
+
...common,
|
|
552
|
+
kind: z.literal("symlink"),
|
|
553
|
+
mode: z.literal(511),
|
|
554
|
+
size: count,
|
|
555
|
+
digest: digest$1,
|
|
556
|
+
link_target: z.string()
|
|
557
|
+
}).refine((entry) => safeCheckpointLink(entry.path, entry.link_target) && entry.size === Buffer.byteLength(entry.link_target) && entry.digest === `sha256:${sha256Hex(entry.link_target)}`)
|
|
558
|
+
]);
|
|
559
|
+
z.strictObject({
|
|
560
|
+
mounts,
|
|
561
|
+
manifest_digest: digest$1,
|
|
562
|
+
content_digest: digest$1
|
|
563
|
+
});
|
|
152
564
|
//#endregion
|
|
153
565
|
//#region ../contracts/src/common/errors.ts
|
|
154
566
|
const ERROR_CODES = {
|
|
@@ -156,13 +568,16 @@ const ERROR_CODES = {
|
|
|
156
568
|
"auth.missing_scope": 403,
|
|
157
569
|
"auth.disabled_principal": 403,
|
|
158
570
|
"principal.not_found": 404,
|
|
571
|
+
"principal.name_conflict": 409,
|
|
159
572
|
"key.not_found": 404,
|
|
573
|
+
"request.body_too_large": 413,
|
|
160
574
|
"validation.invalid": 400,
|
|
161
575
|
"idempotency.conflict": 409,
|
|
162
576
|
"capacity.queue_full": 429,
|
|
163
577
|
"capacity.waiters_full": 429,
|
|
164
578
|
"template.not_found": 404,
|
|
165
579
|
"template.version_not_found": 404,
|
|
580
|
+
"template.version_immutable": 409,
|
|
166
581
|
"template.not_authorized": 403,
|
|
167
582
|
"workspace.not_found": 404,
|
|
168
583
|
"workspace.external_id_conflict": 409,
|
|
@@ -177,6 +592,10 @@ const ERROR_CODES = {
|
|
|
177
592
|
"workspace.persistence_not_enabled": 409,
|
|
178
593
|
"workspace.preserving": 409,
|
|
179
594
|
"checkpoint.not_found": 404,
|
|
595
|
+
"checkpoint.unsupported": 409,
|
|
596
|
+
"artifact.not_found": 404,
|
|
597
|
+
"artifact.workspace_active": 409,
|
|
598
|
+
"artifact.too_large": 413,
|
|
180
599
|
"checkpoint.not_ready": 409,
|
|
181
600
|
"checkpoint.none_ready": 409,
|
|
182
601
|
"checkpoint.corrupt": 409,
|
|
@@ -186,24 +605,35 @@ const ERROR_CODES = {
|
|
|
186
605
|
"restore.image_unavailable": 409,
|
|
187
606
|
"restore.incompatible": 409,
|
|
188
607
|
"resume.unsupported": 409,
|
|
608
|
+
"conversation.not_found": 404,
|
|
189
609
|
"conversation.expired": 410,
|
|
190
610
|
"conversation.deleted": 410,
|
|
191
611
|
"operation.conflict": 409,
|
|
192
612
|
"source.not_allowed": 422,
|
|
193
613
|
"source.invalid_revision": 422,
|
|
194
614
|
"secret.unavailable": 503,
|
|
615
|
+
"secret.not_found": 404,
|
|
195
616
|
"storage.capacity_exhausted": 507,
|
|
196
617
|
"relay.body_too_large": 413,
|
|
197
618
|
"relay.route_not_allowed": 422,
|
|
198
619
|
"relay.streaming_unsupported": 409,
|
|
199
620
|
"relay.deadline_exceeded": 504,
|
|
200
621
|
"relay.upstream_error": 502,
|
|
622
|
+
"agent.interrupt_unsupported": 409,
|
|
201
623
|
"attachment.invalid": 400,
|
|
202
624
|
"attachment.too_large": 413,
|
|
203
625
|
"attachment.not_found": 404,
|
|
204
626
|
"attachment.conflict": 409,
|
|
205
627
|
"attachment.unsupported": 409,
|
|
206
628
|
"attachment.interrupted": 503,
|
|
629
|
+
"maintenance.active": 503,
|
|
630
|
+
"maintenance.timeout": 503,
|
|
631
|
+
"backup.failed": 409,
|
|
632
|
+
"journal.pending": 503,
|
|
633
|
+
"account.purged": 409,
|
|
634
|
+
"account.purge_pending": 503,
|
|
635
|
+
"recovery.required": 503,
|
|
636
|
+
"recovery.failed": 409,
|
|
207
637
|
"internal.error": 500
|
|
208
638
|
};
|
|
209
639
|
const errorCodes = Object.keys(ERROR_CODES);
|
|
@@ -214,6 +644,9 @@ const ErrorEnvelopeSchema = z.object({ error: z.object({
|
|
|
214
644
|
details: z.record(z.string(), z.unknown()).optional()
|
|
215
645
|
}) });
|
|
216
646
|
//#endregion
|
|
647
|
+
//#region ../contracts/src/common/json-value.ts
|
|
648
|
+
const JsonValueSchema = z.json();
|
|
649
|
+
//#endregion
|
|
217
650
|
//#region ../contracts/src/common/pagination.ts
|
|
218
651
|
const CursorSchema = z.string().min(1).max(2048);
|
|
219
652
|
function CursorPageSchema(item) {
|
|
@@ -229,6 +662,77 @@ function CursorQuerySchema(maxLimit, defaultLimit) {
|
|
|
229
662
|
});
|
|
230
663
|
}
|
|
231
664
|
//#endregion
|
|
665
|
+
//#region ../contracts/src/conversations/facts.ts
|
|
666
|
+
const ProducerCodeSchema = z.string().regex(/^[a-z][a-z0-9_]{0,63}$/);
|
|
667
|
+
const ConversationFactSchema = z.discriminatedUnion("type", [
|
|
668
|
+
z.object({ type: z.literal("intermediate") }),
|
|
669
|
+
z.object({ type: z.literal("final_response") }),
|
|
670
|
+
z.object({ type: z.literal("tool_only") }),
|
|
671
|
+
z.object({
|
|
672
|
+
type: z.literal("activity_started"),
|
|
673
|
+
activity_id: z.uuid(),
|
|
674
|
+
kind: z.enum([
|
|
675
|
+
"tool",
|
|
676
|
+
"attachment",
|
|
677
|
+
"workspace",
|
|
678
|
+
"retrieval"
|
|
679
|
+
]),
|
|
680
|
+
label: z.string().min(1).max(160),
|
|
681
|
+
started_at: z.iso.datetime().nullable(),
|
|
682
|
+
action: ProducerCodeSchema.optional(),
|
|
683
|
+
subject: ProducerCodeSchema.optional()
|
|
684
|
+
}),
|
|
685
|
+
z.object({
|
|
686
|
+
type: z.literal("activity_finished"),
|
|
687
|
+
activity_id: z.uuid(),
|
|
688
|
+
ended_at: z.iso.datetime().nullable(),
|
|
689
|
+
outcome: z.enum([
|
|
690
|
+
"completed",
|
|
691
|
+
"failed",
|
|
692
|
+
"interrupted",
|
|
693
|
+
"canceled"
|
|
694
|
+
])
|
|
695
|
+
}),
|
|
696
|
+
z.object({
|
|
697
|
+
type: z.literal("turn_terminal"),
|
|
698
|
+
outcome: z.enum([
|
|
699
|
+
"completed",
|
|
700
|
+
"failed",
|
|
701
|
+
"interrupted",
|
|
702
|
+
"canceled",
|
|
703
|
+
"lost"
|
|
704
|
+
]),
|
|
705
|
+
reason: ProducerCodeSchema.optional()
|
|
706
|
+
})
|
|
707
|
+
]);
|
|
708
|
+
//#endregion
|
|
709
|
+
//#region ../contracts/src/conversations/identity.ts
|
|
710
|
+
const ConversationRequestIdentitySchema = z.object({
|
|
711
|
+
schema: z.literal("pocketcoder.conversation/v2"),
|
|
712
|
+
authority: z.enum(["external", "generic"]),
|
|
713
|
+
conversation_id: z.uuid(),
|
|
714
|
+
request_id: z.uuid(),
|
|
715
|
+
turn_id: z.uuid(),
|
|
716
|
+
request_fingerprint: z.string().regex(/^[0-9a-f]{64}$/)
|
|
717
|
+
});
|
|
718
|
+
const ConversationRequestReceiptSchema = z.object({
|
|
719
|
+
kind: z.enum(["accepted", "replayed"]),
|
|
720
|
+
identity: ConversationRequestIdentitySchema,
|
|
721
|
+
accepted_at: z.iso.datetime()
|
|
722
|
+
});
|
|
723
|
+
const ConversationMessageIdentitySchema = ConversationRequestIdentitySchema.extend({
|
|
724
|
+
event_id: z.uuid(),
|
|
725
|
+
message_id: z.string().min(1).max(256),
|
|
726
|
+
sequence: z.number().int().nonnegative(),
|
|
727
|
+
revision: z.number().int().positive(),
|
|
728
|
+
fact: ConversationFactSchema.optional()
|
|
729
|
+
});
|
|
730
|
+
const ConversationProtocolSupportSchema = z.object({
|
|
731
|
+
schema: z.literal("pocketcoder.conversation/v2"),
|
|
732
|
+
request_identity_authorities: z.array(z.enum(["external", "generic"])),
|
|
733
|
+
producer_capabilities: z.null()
|
|
734
|
+
});
|
|
735
|
+
//#endregion
|
|
232
736
|
//#region ../contracts/src/conversations/conversation.ts
|
|
233
737
|
const CONVERSATION_ROLES = [
|
|
234
738
|
"user",
|
|
@@ -242,14 +746,21 @@ const ConversationMessageInputSchema = z.object({
|
|
|
242
746
|
role: z.enum(CONVERSATION_ROLES),
|
|
243
747
|
content: z.string().max(262144),
|
|
244
748
|
occurred_at: z.iso.datetime(),
|
|
245
|
-
metadata: ConversationMetadataSchema.default({})
|
|
749
|
+
metadata: ConversationMetadataSchema.default({}),
|
|
750
|
+
identity: ConversationMessageIdentitySchema.nullable().optional()
|
|
751
|
+
});
|
|
752
|
+
const ConversationMessageResourceSchema = ConversationMessageInputSchema.extend({
|
|
753
|
+
seq: z.number().int().positive(),
|
|
754
|
+
identity: ConversationMessageIdentitySchema.nullable().default(null)
|
|
246
755
|
});
|
|
247
|
-
const ConversationMessageResourceSchema = ConversationMessageInputSchema.extend({ seq: z.number().int().positive() });
|
|
248
756
|
CursorQuerySchema(200, 100);
|
|
249
|
-
const ConversationPageSchema = CursorPageSchema(ConversationMessageResourceSchema).extend({
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
757
|
+
const ConversationPageSchema = CursorPageSchema(ConversationMessageResourceSchema).extend({
|
|
758
|
+
protocol: ConversationProtocolSupportSchema.nullable().default(null),
|
|
759
|
+
retention: z.object({
|
|
760
|
+
status: z.literal("retained"),
|
|
761
|
+
expires_at: z.iso.datetime().nullable()
|
|
762
|
+
})
|
|
763
|
+
});
|
|
253
764
|
const ConversationResumeOutcomeSchema = z.object({
|
|
254
765
|
status: z.literal("supported"),
|
|
255
766
|
reason: z.null(),
|
|
@@ -257,6 +768,160 @@ const ConversationResumeOutcomeSchema = z.object({
|
|
|
257
768
|
checkpoint_id: z.uuid()
|
|
258
769
|
});
|
|
259
770
|
//#endregion
|
|
771
|
+
//#region ../contracts/src/conversations/document.ts
|
|
772
|
+
const CONVERSATION_RECORD_STATUSES = [
|
|
773
|
+
"completed",
|
|
774
|
+
"failed",
|
|
775
|
+
"interrupted",
|
|
776
|
+
"canceled",
|
|
777
|
+
"lost",
|
|
778
|
+
"message",
|
|
779
|
+
"legacy"
|
|
780
|
+
];
|
|
781
|
+
const ConversationActivitySchema = z.object({
|
|
782
|
+
activity_id: z.uuid(),
|
|
783
|
+
kind: z.string(),
|
|
784
|
+
label: z.string(),
|
|
785
|
+
action: z.string().nullable(),
|
|
786
|
+
subject: z.string().nullable(),
|
|
787
|
+
started_at: z.iso.datetime().nullable(),
|
|
788
|
+
ended_at: z.iso.datetime().nullable(),
|
|
789
|
+
outcome: z.string().nullable()
|
|
790
|
+
});
|
|
791
|
+
z.object({
|
|
792
|
+
schema: z.literal("pocketcoder.conversation-record/v1"),
|
|
793
|
+
turn_id: z.uuid().nullable(),
|
|
794
|
+
request: ConversationRequestIdentitySchema.nullable(),
|
|
795
|
+
status: z.enum(CONVERSATION_RECORD_STATUSES),
|
|
796
|
+
reason: z.string().nullable(),
|
|
797
|
+
messages: z.array(ConversationMessageResourceSchema),
|
|
798
|
+
activities: z.array(ConversationActivitySchema),
|
|
799
|
+
final_response: z.string().nullable(),
|
|
800
|
+
started_at: z.iso.datetime().nullable(),
|
|
801
|
+
ended_at: z.iso.datetime().nullable()
|
|
802
|
+
});
|
|
803
|
+
const ConversationSummarySchema = z.object({
|
|
804
|
+
conversation_id: z.uuid().nullable(),
|
|
805
|
+
workspace_id: z.uuid(),
|
|
806
|
+
legacy: z.boolean(),
|
|
807
|
+
status: z.enum([
|
|
808
|
+
"retained",
|
|
809
|
+
"deleted",
|
|
810
|
+
"expired"
|
|
811
|
+
]),
|
|
812
|
+
message_count: z.number().int().nonnegative(),
|
|
813
|
+
turn_count: z.number().int().nonnegative(),
|
|
814
|
+
last_activity_at: z.iso.datetime(),
|
|
815
|
+
expires_at: z.iso.datetime().nullable()
|
|
816
|
+
});
|
|
817
|
+
const ConversationListSchema = z.object({ items: z.array(ConversationSummarySchema) });
|
|
818
|
+
const ConversationDocumentPageSchema = CursorPageSchema(ConversationMessageResourceSchema).extend({
|
|
819
|
+
conversation: ConversationSummarySchema,
|
|
820
|
+
protocol: ConversationProtocolSupportSchema
|
|
821
|
+
});
|
|
822
|
+
const ConversationLiveEventSchema = z.discriminatedUnion("type", [z.object({
|
|
823
|
+
type: z.literal("turn"),
|
|
824
|
+
conversation_id: z.uuid(),
|
|
825
|
+
turn_id: z.uuid(),
|
|
826
|
+
messages: z.array(ConversationMessageResourceSchema.omit({ seq: true }))
|
|
827
|
+
}), z.object({
|
|
828
|
+
type: z.literal("committed"),
|
|
829
|
+
conversation_id: z.uuid().nullable(),
|
|
830
|
+
turn_id: z.uuid().nullable(),
|
|
831
|
+
status: z.enum(CONVERSATION_RECORD_STATUSES),
|
|
832
|
+
last_seq: z.number().int().nonnegative()
|
|
833
|
+
})]);
|
|
834
|
+
//#endregion
|
|
835
|
+
//#region ../contracts/src/previews/view-session.ts
|
|
836
|
+
const HttpsOriginSchema = z.string().refine((value) => {
|
|
837
|
+
try {
|
|
838
|
+
const url = new URL(value);
|
|
839
|
+
return url.protocol === "https:" && url.origin === value && !url.username && !url.password;
|
|
840
|
+
} catch {
|
|
841
|
+
return false;
|
|
842
|
+
}
|
|
843
|
+
}, "An exact HTTPS origin is required.");
|
|
844
|
+
const ViewSessionSchema = z.discriminatedUnion("mode", [
|
|
845
|
+
z.strictObject({ mode: z.literal("local") }),
|
|
846
|
+
z.strictObject({ mode: z.literal("top_level") }),
|
|
847
|
+
z.strictObject({
|
|
848
|
+
mode: z.literal("embedded"),
|
|
849
|
+
parentOrigin: HttpsOriginSchema
|
|
850
|
+
})
|
|
851
|
+
]);
|
|
852
|
+
z.strictObject({ session: ViewSessionSchema.default({ mode: "local" }) });
|
|
853
|
+
//#endregion
|
|
854
|
+
//#region ../contracts/src/displays/display.ts
|
|
855
|
+
const DisplaySchema = z.strictObject({ mode: z.enum(["desktop", "browser"]) });
|
|
856
|
+
z.strictObject({
|
|
857
|
+
control: z.boolean().default(false),
|
|
858
|
+
session: ViewSessionSchema.default({ mode: "local" })
|
|
859
|
+
});
|
|
860
|
+
const BROWSER_WIDTH = 1280;
|
|
861
|
+
const coordinate = (max) => z.number().int().min(0).max(max - 1);
|
|
862
|
+
const url = z.string().max(2048).refine((value) => {
|
|
863
|
+
try {
|
|
864
|
+
const parsed = new URL(value);
|
|
865
|
+
return ["http:", "https:"].includes(parsed.protocol) && !parsed.username && !parsed.password;
|
|
866
|
+
} catch {
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
869
|
+
}, "Browser navigation requires an HTTP or HTTPS URL without credentials.");
|
|
870
|
+
z.discriminatedUnion("action", [
|
|
871
|
+
z.strictObject({
|
|
872
|
+
action: z.literal("navigate"),
|
|
873
|
+
url
|
|
874
|
+
}),
|
|
875
|
+
z.strictObject({
|
|
876
|
+
action: z.literal("click"),
|
|
877
|
+
x: coordinate(BROWSER_WIDTH),
|
|
878
|
+
y: coordinate(800)
|
|
879
|
+
}),
|
|
880
|
+
z.strictObject({
|
|
881
|
+
action: z.literal("text"),
|
|
882
|
+
text: z.string().min(1).max(1024)
|
|
883
|
+
}),
|
|
884
|
+
z.strictObject({
|
|
885
|
+
action: z.literal("key"),
|
|
886
|
+
key: z.enum([
|
|
887
|
+
"Enter",
|
|
888
|
+
"Tab",
|
|
889
|
+
"Backspace",
|
|
890
|
+
"Delete",
|
|
891
|
+
"Escape",
|
|
892
|
+
"ArrowLeft",
|
|
893
|
+
"ArrowRight",
|
|
894
|
+
"ArrowUp",
|
|
895
|
+
"ArrowDown",
|
|
896
|
+
"Home",
|
|
897
|
+
"End",
|
|
898
|
+
"PageUp",
|
|
899
|
+
"PageDown"
|
|
900
|
+
])
|
|
901
|
+
}),
|
|
902
|
+
z.strictObject({
|
|
903
|
+
action: z.literal("scroll"),
|
|
904
|
+
x: coordinate(BROWSER_WIDTH),
|
|
905
|
+
y: coordinate(800),
|
|
906
|
+
deltaY: z.number().int().min(-1e3).max(1e3)
|
|
907
|
+
})
|
|
908
|
+
]);
|
|
909
|
+
const ScreenshotResourceSchema = z.strictObject({
|
|
910
|
+
kind: z.literal("screenshot"),
|
|
911
|
+
id: z.uuid(),
|
|
912
|
+
workspace_id: z.uuid(),
|
|
913
|
+
content_type: z.literal("image/png"),
|
|
914
|
+
bytes: z.number().int().min(1).max(4 * 1024 ** 2),
|
|
915
|
+
digest: z.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
916
|
+
expires_at: z.iso.datetime()
|
|
917
|
+
});
|
|
918
|
+
const ScreenshotCapturePayload = z.strictObject({
|
|
919
|
+
output_id: z.uuid(),
|
|
920
|
+
credential: z.string().min(32).max(128),
|
|
921
|
+
url: z.url(),
|
|
922
|
+
expires_at: z.iso.datetime()
|
|
923
|
+
});
|
|
924
|
+
//#endregion
|
|
260
925
|
//#region ../contracts/src/network/network.ts
|
|
261
926
|
const DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
262
927
|
function isDomain(value) {
|
|
@@ -289,173 +954,175 @@ const NETWORK_STATES = [
|
|
|
289
954
|
const NetworkEventInputSchema = z.object({
|
|
290
955
|
source_seq: z.number().int().positive(),
|
|
291
956
|
occurred_at: z.iso.datetime(),
|
|
292
|
-
decision: z.enum(["allow", "deny"]),
|
|
293
|
-
transport: z.enum(["http", "https"]),
|
|
294
|
-
host: z.string().min(1).max(253),
|
|
295
|
-
port: z.number().int().min(1).max(65535),
|
|
296
|
-
method: z.string().max(32).nullable().default(null),
|
|
297
|
-
path: z.string().max(2048).refine((path) => !path.includes("?") && !path.includes("#"), "path cannot contain query or fragment").nullable().default(null),
|
|
298
|
-
matched_rule: z.string().max(512).nullable().default(null),
|
|
299
|
-
reason: z.string().min(1).max(128)
|
|
300
|
-
});
|
|
301
|
-
z.object({
|
|
302
|
-
source_session_id: z.uuid(),
|
|
303
|
-
events: z.array(NetworkEventInputSchema).min(1).max(100)
|
|
304
|
-
});
|
|
305
|
-
const NetworkEventSchema = NetworkEventInputSchema.omit({ source_seq: true }).extend({
|
|
306
|
-
seq: z.number().int().positive(),
|
|
307
|
-
workspace_id: z.uuid(),
|
|
308
|
-
source_session_id: z.uuid(),
|
|
309
|
-
source_seq: z.number().int().positive()
|
|
310
|
-
});
|
|
311
|
-
CursorQuerySchema(200, 100);
|
|
312
|
-
const NetworkEventPageSchema = CursorPageSchema(NetworkEventSchema);
|
|
313
|
-
//#endregion
|
|
314
|
-
//#region ../contracts/src/persistence/persistence.ts
|
|
315
|
-
const DurationValueSchema = z.string().refine(isDuration, { message: "expected a duration like 15s, 20m, or 2h" });
|
|
316
|
-
const LAUNCH_MODES = ["create", "restore"];
|
|
317
|
-
const CONVERSATION_RESTORE_CAPABILITIES = [
|
|
318
|
-
"supported",
|
|
319
|
-
"filesystem_only",
|
|
320
|
-
"unknown"
|
|
321
|
-
];
|
|
322
|
-
const SourceDescriptorSchema = z.object({
|
|
323
|
-
kind: z.literal("git"),
|
|
324
|
-
repository: z.string().min(1).max(64).regex(/^[a-z0-9]([a-z0-9._-]{0,62}[a-z0-9])?$/),
|
|
325
|
-
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) => {
|
|
326
|
-
const code = character.codePointAt(0) ?? 0;
|
|
327
|
-
return code >= 32 && code !== 127;
|
|
328
|
-
}), { message: "revision is not a safe branch, tag, or commit name" })
|
|
329
|
-
});
|
|
330
|
-
const ResolvedSourceSchema = z.object({
|
|
331
|
-
kind: z.literal("git"),
|
|
332
|
-
repository: z.string().min(1).max(64),
|
|
333
|
-
requested_revision: z.string().min(1).max(256),
|
|
334
|
-
resolved_commit: z.string().regex(/^[0-9a-f]{40,64}$/)
|
|
335
|
-
});
|
|
336
|
-
const PersistenceMountSchema = z.object({
|
|
337
|
-
name: z.string().min(1).max(64).regex(/^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/),
|
|
338
|
-
target: z.string().min(1),
|
|
339
|
-
maxBytes: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
340
|
-
maxFiles: z.number().int().positive().max(1e7)
|
|
341
|
-
});
|
|
342
|
-
const CheckpointPolicySchema = z.object({
|
|
343
|
-
onIdle: z.enum(["preserve", "destroy"]).default("destroy"),
|
|
344
|
-
onDeadline: z.enum(["preserve", "destroy"]).default("destroy"),
|
|
345
|
-
onCleanExit: z.enum(["preserve", "destroy"]).default("destroy"),
|
|
346
|
-
onFailure: z.enum([
|
|
347
|
-
"preserve",
|
|
348
|
-
"retain-for-recovery",
|
|
349
|
-
"destroy"
|
|
350
|
-
]).default("destroy"),
|
|
351
|
-
retention: DurationValueSchema.default("168h")
|
|
352
|
-
});
|
|
353
|
-
const PersistenceSpecSchema = z.object({
|
|
354
|
-
mounts: z.array(PersistenceMountSchema).max(16).default([]),
|
|
355
|
-
conversationRestore: z.enum(CONVERSATION_RESTORE_CAPABILITIES).default("filesystem_only"),
|
|
356
|
-
conversationRetention: DurationValueSchema.default("168h"),
|
|
357
|
-
sessionCompatibility: z.string().min(1).max(128).optional(),
|
|
358
|
-
checkpoint: CheckpointPolicySchema.prefault({})
|
|
359
|
-
});
|
|
360
|
-
const OutputDeclarationSchema = z.discriminatedUnion("type", [
|
|
361
|
-
z.object({ type: z.literal("gitSha") }),
|
|
362
|
-
z.object({
|
|
363
|
-
type: z.literal("string"),
|
|
364
|
-
maxLength: z.number().int().positive().max(4096).default(512)
|
|
365
|
-
}),
|
|
366
|
-
z.object({
|
|
367
|
-
type: z.literal("httpsUrl"),
|
|
368
|
-
maxLength: z.number().int().positive().max(4096).default(2048)
|
|
369
|
-
})
|
|
370
|
-
]);
|
|
371
|
-
const CHECKPOINT_STATES = [
|
|
372
|
-
"creating",
|
|
373
|
-
"ready",
|
|
374
|
-
"failed",
|
|
375
|
-
"deleting",
|
|
376
|
-
"deleted"
|
|
377
|
-
];
|
|
378
|
-
const OPERATION_KINDS = [
|
|
379
|
-
"preserve",
|
|
380
|
-
"restore",
|
|
381
|
-
"verify",
|
|
382
|
-
"delete",
|
|
383
|
-
"purge"
|
|
384
|
-
];
|
|
385
|
-
const OPERATION_STATES = [
|
|
386
|
-
"pending",
|
|
387
|
-
"running",
|
|
388
|
-
"succeeded",
|
|
389
|
-
"failed"
|
|
390
|
-
];
|
|
391
|
-
const CheckpointResourceSchema = z.object({
|
|
392
|
-
id: z.uuid(),
|
|
393
|
-
workspace_id: z.uuid(),
|
|
394
|
-
state: z.enum(CHECKPOINT_STATES),
|
|
395
|
-
reason_code: z.string().nullable(),
|
|
396
|
-
template: z.object({
|
|
397
|
-
name: z.string(),
|
|
398
|
-
version: z.string(),
|
|
399
|
-
digest: z.string()
|
|
400
|
-
}),
|
|
401
|
-
manifest_digest: z.string().nullable(),
|
|
402
|
-
logical_bytes: z.number().int().nonnegative().nullable(),
|
|
403
|
-
stored_bytes: z.number().int().nonnegative().nullable(),
|
|
404
|
-
file_count: z.number().int().nonnegative().nullable(),
|
|
405
|
-
mounts: z.array(z.string()),
|
|
406
|
-
conversation_restore: z.enum(CONVERSATION_RESTORE_CAPABILITIES),
|
|
407
|
-
label: z.string().nullable(),
|
|
408
|
-
created_at: z.iso.datetime(),
|
|
409
|
-
ready_at: z.iso.datetime().nullable(),
|
|
410
|
-
expires_at: z.iso.datetime().nullable()
|
|
411
|
-
});
|
|
412
|
-
const OperationResourceSchema = z.object({
|
|
413
|
-
id: z.uuid(),
|
|
414
|
-
kind: z.enum(OPERATION_KINDS),
|
|
415
|
-
state: z.enum(OPERATION_STATES),
|
|
416
|
-
workspace_id: z.uuid().nullable(),
|
|
417
|
-
checkpoint_id: z.uuid().nullable(),
|
|
418
|
-
result_workspace_id: z.uuid().nullable(),
|
|
419
|
-
reason_code: z.string().nullable(),
|
|
420
|
-
created_at: z.iso.datetime(),
|
|
421
|
-
updated_at: z.iso.datetime(),
|
|
422
|
-
completed_at: z.iso.datetime().nullable()
|
|
957
|
+
decision: z.enum(["allow", "deny"]),
|
|
958
|
+
transport: z.enum(["http", "https"]),
|
|
959
|
+
host: z.string().min(1).max(253),
|
|
960
|
+
port: z.number().int().min(1).max(65535),
|
|
961
|
+
method: z.string().max(32).nullable().default(null),
|
|
962
|
+
path: z.string().max(2048).refine((path) => !path.includes("?") && !path.includes("#"), "path cannot contain query or fragment").nullable().default(null),
|
|
963
|
+
matched_rule: z.string().max(512).nullable().default(null),
|
|
964
|
+
reason: z.string().min(1).max(128)
|
|
423
965
|
});
|
|
424
966
|
z.object({
|
|
425
|
-
|
|
426
|
-
|
|
967
|
+
source_session_id: z.uuid(),
|
|
968
|
+
events: z.array(NetworkEventInputSchema).min(1).max(100)
|
|
427
969
|
});
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
970
|
+
const NetworkEventSchema = NetworkEventInputSchema.omit({ source_seq: true }).extend({
|
|
971
|
+
seq: z.number().int().positive(),
|
|
972
|
+
workspace_id: z.uuid(),
|
|
973
|
+
source_session_id: z.uuid(),
|
|
974
|
+
source_seq: z.number().int().positive()
|
|
432
975
|
});
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
976
|
+
CursorQuerySchema(200, 100);
|
|
977
|
+
const NetworkEventPageSchema = CursorPageSchema(NetworkEventSchema);
|
|
978
|
+
//#endregion
|
|
979
|
+
//#region ../contracts/src/workspaces/workload-operation.ts
|
|
980
|
+
const WorkloadExecutionReferenceSchema = z.strictObject({
|
|
981
|
+
workspaceId: z.uuid(),
|
|
982
|
+
incarnationId: z.string().min(1).max(256),
|
|
983
|
+
owner: z.enum(["process", "conversation-request"]),
|
|
984
|
+
requestId: z.uuid()
|
|
985
|
+
});
|
|
986
|
+
const WorkloadAcceptanceSchema = z.strictObject({
|
|
987
|
+
execution: WorkloadExecutionReferenceSchema,
|
|
988
|
+
callerPrincipalId: z.uuid(),
|
|
989
|
+
templateDigest: WorkloadDigestSchema,
|
|
990
|
+
adapterDigest: WorkloadDigestSchema,
|
|
991
|
+
operation: WorkloadNameSchema,
|
|
992
|
+
input: z.record(z.string(), JsonValueSchema),
|
|
993
|
+
inputs: AcceptedInputManifestSchema,
|
|
994
|
+
bindingSetDigest: WorkloadDigestSchema,
|
|
995
|
+
fingerprint: WorkloadDigestSchema
|
|
996
|
+
}).superRefine((value, ctx) => {
|
|
997
|
+
const { fingerprint, ...facts } = value;
|
|
998
|
+
if (fingerprint !== digestOf(facts)) ctx.addIssue({
|
|
999
|
+
code: "custom",
|
|
1000
|
+
path: ["fingerprint"],
|
|
1001
|
+
message: "acceptance fingerprint does not match its facts"
|
|
1002
|
+
});
|
|
1003
|
+
if (value.inputs.workspaceId !== value.execution.workspaceId || value.inputs.incarnationId !== value.execution.incarnationId) ctx.addIssue({
|
|
1004
|
+
code: "custom",
|
|
1005
|
+
path: ["inputs"],
|
|
1006
|
+
message: "accepted input identity must match the execution"
|
|
1007
|
+
});
|
|
1008
|
+
});
|
|
1009
|
+
const WorkloadCancelSchema = z.strictObject({
|
|
1010
|
+
execution: WorkloadExecutionReferenceSchema,
|
|
1011
|
+
cancelId: z.uuid(),
|
|
1012
|
+
scope: z.enum(["operation", "workspace"])
|
|
1013
|
+
});
|
|
1014
|
+
//#endregion
|
|
1015
|
+
//#region ../contracts/src/outputs/export-reference.ts
|
|
1016
|
+
const ExportObjectReferenceSchema = z.strictObject({
|
|
1017
|
+
objectId: z.uuid(),
|
|
1018
|
+
objectVersion: WorkloadDigestSchema,
|
|
1019
|
+
name: z.string().min(1).max(255),
|
|
1020
|
+
sizeBytes: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
1021
|
+
mediaType: AttachmentMediaTypeSchema,
|
|
1022
|
+
digest: WorkloadDigestSchema
|
|
1023
|
+
});
|
|
1024
|
+
z.strictObject({
|
|
1025
|
+
exportId: z.uuid(),
|
|
1026
|
+
execution: WorkloadExecutionReferenceSchema,
|
|
1027
|
+
manifestDigest: WorkloadDigestSchema,
|
|
1028
|
+
completeness: z.enum([
|
|
1029
|
+
"complete",
|
|
1030
|
+
"partial",
|
|
1031
|
+
"lost"
|
|
439
1032
|
]),
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
1033
|
+
publication: z.enum([
|
|
1034
|
+
"published",
|
|
1035
|
+
"unavailable",
|
|
1036
|
+
"expired"
|
|
1037
|
+
]),
|
|
1038
|
+
objects: z.array(ExportObjectReferenceSchema),
|
|
1039
|
+
errors: z.array(z.string().min(1).max(512)),
|
|
1040
|
+
publishedAt: z.iso.datetime(),
|
|
1041
|
+
expiresAt: z.iso.datetime()
|
|
1042
|
+
}).superRefine((value, ctx) => {
|
|
1043
|
+
const { execution, objects, completeness, errors } = value;
|
|
1044
|
+
if (value.manifestDigest !== digestOf({
|
|
1045
|
+
execution,
|
|
1046
|
+
objects,
|
|
1047
|
+
completeness,
|
|
1048
|
+
errors
|
|
1049
|
+
})) ctx.addIssue({
|
|
1050
|
+
code: "custom",
|
|
1051
|
+
path: ["manifestDigest"],
|
|
1052
|
+
message: "export digest does not match its frozen manifest"
|
|
1053
|
+
});
|
|
1054
|
+
if (Date.parse(value.expiresAt) <= Date.parse(value.publishedAt)) ctx.addIssue({
|
|
1055
|
+
code: "custom",
|
|
1056
|
+
path: ["expiresAt"],
|
|
1057
|
+
message: "export retention must be positive"
|
|
1058
|
+
});
|
|
1059
|
+
const ids = value.objects.map((object) => object.objectId);
|
|
1060
|
+
if (new Set(ids).size !== ids.length) ctx.addIssue({
|
|
1061
|
+
code: "custom",
|
|
1062
|
+
path: ["objects"],
|
|
1063
|
+
message: "export object IDs must be unique"
|
|
1064
|
+
});
|
|
447
1065
|
});
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
1066
|
+
//#endregion
|
|
1067
|
+
//#region ../contracts/src/previews/preview.ts
|
|
1068
|
+
const PreviewNameSchema = z.string().regex(/^[a-z][a-z0-9-]{0,19}$/).refine((name) => !(/* @__PURE__ */ new Set([
|
|
1069
|
+
"display",
|
|
1070
|
+
"control",
|
|
1071
|
+
"agent",
|
|
1072
|
+
"supervisor",
|
|
1073
|
+
"vnc",
|
|
1074
|
+
"cdp"
|
|
1075
|
+
])).has(name) && !name.startsWith("pc-"), "reserved preview name");
|
|
1076
|
+
const RESERVED_PORTS = /* @__PURE__ */ new Set([
|
|
1077
|
+
3284,
|
|
1078
|
+
5900,
|
|
1079
|
+
5901,
|
|
1080
|
+
6080,
|
|
1081
|
+
9222,
|
|
1082
|
+
9229
|
|
1083
|
+
]);
|
|
1084
|
+
const PreviewSchema = z.strictObject({ port: z.number().int().min(1024).max(65535).refine((port) => !RESERVED_PORTS.has(port), "reserved port") });
|
|
1085
|
+
const PreviewsSchema = z.record(PreviewNameSchema, PreviewSchema).refine((previews) => Object.keys(previews).length <= 16, "at most 16 previews");
|
|
1086
|
+
const PreviewOpenResponseSchema = z.object({
|
|
1087
|
+
url: z.url(),
|
|
1088
|
+
expires_at: z.iso.datetime()
|
|
458
1089
|
});
|
|
1090
|
+
const PreviewListSchema = z.array(z.object({
|
|
1091
|
+
name: PreviewNameSchema,
|
|
1092
|
+
port: z.number().int()
|
|
1093
|
+
}));
|
|
1094
|
+
const PreviewSocketPayload = z.discriminatedUnion("op", [
|
|
1095
|
+
z.object({
|
|
1096
|
+
op: z.literal("open"),
|
|
1097
|
+
request_id: z.uuid(),
|
|
1098
|
+
name: z.union([PreviewNameSchema, z.literal("display")]),
|
|
1099
|
+
path: z.string().max(8192),
|
|
1100
|
+
origin: z.url(),
|
|
1101
|
+
cookie: z.string().max(8192).optional(),
|
|
1102
|
+
protocols: z.array(z.string().max(128)).max(16)
|
|
1103
|
+
}),
|
|
1104
|
+
z.object({
|
|
1105
|
+
op: z.literal("ready"),
|
|
1106
|
+
request_id: z.uuid(),
|
|
1107
|
+
protocol: z.string().max(128)
|
|
1108
|
+
}),
|
|
1109
|
+
z.object({
|
|
1110
|
+
op: z.literal("data"),
|
|
1111
|
+
request_id: z.uuid(),
|
|
1112
|
+
seq: z.number().int().nonnegative(),
|
|
1113
|
+
binary: z.boolean(),
|
|
1114
|
+
content_b64: z.string().max(87384)
|
|
1115
|
+
}),
|
|
1116
|
+
z.object({
|
|
1117
|
+
op: z.literal("ack"),
|
|
1118
|
+
request_id: z.uuid(),
|
|
1119
|
+
seq: z.number().int().nonnegative()
|
|
1120
|
+
}),
|
|
1121
|
+
z.object({
|
|
1122
|
+
op: z.literal("close"),
|
|
1123
|
+
request_id: z.uuid()
|
|
1124
|
+
})
|
|
1125
|
+
]);
|
|
459
1126
|
//#endregion
|
|
460
1127
|
//#region ../contracts/src/workspaces/workspace.ts
|
|
461
1128
|
const WORKSPACE_STATES = [
|
|
@@ -479,6 +1146,15 @@ const TERMINAL_STATES = [
|
|
|
479
1146
|
"preserved"
|
|
480
1147
|
];
|
|
481
1148
|
const REASON_CODES = [
|
|
1149
|
+
...[
|
|
1150
|
+
"admission_global_capacity",
|
|
1151
|
+
"admission_principal_capacity",
|
|
1152
|
+
"admission_template_capacity",
|
|
1153
|
+
"admission_policy_global_capacity",
|
|
1154
|
+
"admission_actor_capacity",
|
|
1155
|
+
"admission_denied",
|
|
1156
|
+
"admission_unavailable"
|
|
1157
|
+
],
|
|
482
1158
|
"child_exit_success",
|
|
483
1159
|
"child_exit_failure",
|
|
484
1160
|
"setup_failed",
|
|
@@ -513,6 +1189,7 @@ const AGENT_STATES = [
|
|
|
513
1189
|
"running",
|
|
514
1190
|
"stable"
|
|
515
1191
|
];
|
|
1192
|
+
const AgentInterruptResponseSchema = z.object({ status: z.enum(["requested", "idle"]) });
|
|
516
1193
|
z.object({
|
|
517
1194
|
external_id: z.string().min(1).max(256),
|
|
518
1195
|
template: z.object({
|
|
@@ -528,10 +1205,15 @@ const TemplateRefSchema = z.object({
|
|
|
528
1205
|
version: z.string(),
|
|
529
1206
|
digest: z.string()
|
|
530
1207
|
});
|
|
1208
|
+
const AgentMetadataSchema = z.object({
|
|
1209
|
+
type: z.string(),
|
|
1210
|
+
model: z.string().min(1).max(255).nullable()
|
|
1211
|
+
});
|
|
531
1212
|
const WorkspaceResourceSchema = z.object({
|
|
532
1213
|
id: z.uuid(),
|
|
533
1214
|
external_id: z.string(),
|
|
534
1215
|
template: TemplateRefSchema,
|
|
1216
|
+
agent: AgentMetadataSchema.nullable(),
|
|
535
1217
|
state: z.enum(WORKSPACE_STATES),
|
|
536
1218
|
reason_code: z.enum(REASON_CODES).nullable(),
|
|
537
1219
|
agent_state: z.enum(AGENT_STATES),
|
|
@@ -585,19 +1267,21 @@ z.object({
|
|
|
585
1267
|
limit: z.coerce.number().int().positive().max(200).default(50),
|
|
586
1268
|
cursor: z.string().optional()
|
|
587
1269
|
});
|
|
588
|
-
|
|
589
|
-
//#region ../contracts/src/protocol/api.ts
|
|
590
|
-
const TemplatePageSchema = CursorPageSchema(z.object({
|
|
1270
|
+
const TemplateListItemSchema = z.object({
|
|
591
1271
|
name: z.string(),
|
|
592
1272
|
version: z.string(),
|
|
593
1273
|
digest: z.string(),
|
|
1274
|
+
agent: AgentMetadataSchema.nullable(),
|
|
594
1275
|
description: z.string().optional(),
|
|
595
1276
|
status: z.enum([
|
|
596
1277
|
"active",
|
|
597
1278
|
"available",
|
|
598
1279
|
"retired"
|
|
599
1280
|
])
|
|
600
|
-
})
|
|
1281
|
+
});
|
|
1282
|
+
//#endregion
|
|
1283
|
+
//#region ../contracts/src/protocol/api.ts
|
|
1284
|
+
const TemplatePageSchema = CursorPageSchema(TemplateListItemSchema);
|
|
601
1285
|
CursorQuerySchema(200, 100);
|
|
602
1286
|
const CheckpointPageSchema = CursorPageSchema(CheckpointResourceSchema);
|
|
603
1287
|
CursorQuerySchema(200, 50).extend({ state: z.enum(CHECKPOINT_STATES).optional() });
|
|
@@ -687,6 +1371,52 @@ const LogChunkSchema = z.object({
|
|
|
687
1371
|
});
|
|
688
1372
|
CursorQuerySchema(1e3, 200);
|
|
689
1373
|
const LogPageSchema = CursorPageSchema(LogChunkSchema);
|
|
1374
|
+
const SecretTypeSchema = z.enum([
|
|
1375
|
+
"setup-issuer",
|
|
1376
|
+
"runtime-issuer",
|
|
1377
|
+
"registry"
|
|
1378
|
+
]);
|
|
1379
|
+
const SecretNameSchema = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/);
|
|
1380
|
+
z.object({ name: SecretNameSchema });
|
|
1381
|
+
const issuerUrl = z.url().refine((value) => {
|
|
1382
|
+
if (!URL.canParse(value)) return false;
|
|
1383
|
+
const url = new URL(value);
|
|
1384
|
+
return url.protocol === "https:" && !url.username && !url.password && !url.search && !url.hash;
|
|
1385
|
+
});
|
|
1386
|
+
const IssuerConfigSchema = z.strictObject({
|
|
1387
|
+
url: issuerUrl,
|
|
1388
|
+
authorization: z.string().min(1).max(16384),
|
|
1389
|
+
policy: z.record(z.string(), z.json()).meta({
|
|
1390
|
+
type: "object",
|
|
1391
|
+
additionalProperties: true
|
|
1392
|
+
})
|
|
1393
|
+
});
|
|
1394
|
+
const RegistryConfigSchema = z.strictObject({
|
|
1395
|
+
server: z.string().max(255).regex(/^[A-Za-z0-9.-]+(?::[0-9]{1,5})?$/),
|
|
1396
|
+
username: z.string().min(1).max(1024),
|
|
1397
|
+
password: z.string().min(1).max(16384)
|
|
1398
|
+
});
|
|
1399
|
+
z.discriminatedUnion("type", [
|
|
1400
|
+
z.strictObject({
|
|
1401
|
+
type: z.literal("setup-issuer"),
|
|
1402
|
+
value: IssuerConfigSchema
|
|
1403
|
+
}),
|
|
1404
|
+
z.strictObject({
|
|
1405
|
+
type: z.literal("runtime-issuer"),
|
|
1406
|
+
value: IssuerConfigSchema
|
|
1407
|
+
}),
|
|
1408
|
+
z.strictObject({
|
|
1409
|
+
type: z.literal("registry"),
|
|
1410
|
+
value: RegistryConfigSchema
|
|
1411
|
+
})
|
|
1412
|
+
]);
|
|
1413
|
+
const SecretResourceSchema = z.strictObject({
|
|
1414
|
+
name: SecretNameSchema,
|
|
1415
|
+
type: SecretTypeSchema,
|
|
1416
|
+
updated_at: z.iso.datetime(),
|
|
1417
|
+
retired_at: z.iso.datetime().nullable()
|
|
1418
|
+
});
|
|
1419
|
+
const SecretListSchema = z.strictObject({ items: z.array(SecretResourceSchema) });
|
|
690
1420
|
//#endregion
|
|
691
1421
|
//#region ../contracts/src/templates/template-constants.ts
|
|
692
1422
|
function isAbsolutePath(value) {
|
|
@@ -695,7 +1425,7 @@ function isAbsolutePath(value) {
|
|
|
695
1425
|
const SECRET_ENV_PATTERN = /(SECRET|TOKEN|PASSWORD|PASSWD|API_?KEY|PRIVATE_?KEY|CREDENTIAL)/i;
|
|
696
1426
|
const SECRET_REFERENCE_PREFIX = "secretRef:";
|
|
697
1427
|
//#endregion
|
|
698
|
-
//#region ../contracts/src/templates/template-schema.ts
|
|
1428
|
+
//#region ../contracts/src/templates/template-alpha-schema.ts
|
|
699
1429
|
const NAME_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
700
1430
|
const SEMVER_RE = /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;
|
|
701
1431
|
const IMAGE_DIGEST_RE = /^[^\s@]+@sha256:[0-9a-f]{64}$/;
|
|
@@ -722,10 +1452,12 @@ const HarnessSchema = z.object({
|
|
|
722
1452
|
cwd: z.string().refine(isAbsolutePath, "expected an absolute path").optional()
|
|
723
1453
|
});
|
|
724
1454
|
const AgentSchema = HarnessSchema.extend({
|
|
1455
|
+
model: z.string().min(1).max(255).optional().describe("Display metadata only; does not configure the agent model."),
|
|
725
1456
|
type: z.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/).default("custom"),
|
|
726
1457
|
transport: z.enum(["pty", "acp"]).default("pty"),
|
|
727
1458
|
termWidth: z.number().int().min(10).max(65535).optional(),
|
|
728
|
-
stateFile: z.string().refine(isAbsolutePath, "expected an absolute path").optional()
|
|
1459
|
+
stateFile: z.string().refine(isAbsolutePath, "expected an absolute path").optional(),
|
|
1460
|
+
interruptKeys: z.string().min(1).max(16).optional()
|
|
729
1461
|
});
|
|
730
1462
|
const TerminalSchema = z.object({
|
|
731
1463
|
command: CommandSchema,
|
|
@@ -808,6 +1540,7 @@ const CheckpointHookSchema = z.object({
|
|
|
808
1540
|
const TemplateSpecSchema = z.object({
|
|
809
1541
|
version: z.string().regex(SEMVER_RE),
|
|
810
1542
|
image: z.string().regex(IMAGE_DIGEST_RE, { message: "image must be digest-pinned (repo@sha256:<64 hex>)" }),
|
|
1543
|
+
imagePullSecret: z.string().refine((value) => value.startsWith("secretRef:") && SecretNameSchema.safeParse(value.slice(10)).success, "imagePullSecret must name a stored secret").optional(),
|
|
811
1544
|
command: CommandSchema.default([
|
|
812
1545
|
"/usr/local/bin/pocketcoder-supervisor",
|
|
813
1546
|
"supervise",
|
|
@@ -818,6 +1551,8 @@ const TemplateSpecSchema = z.object({
|
|
|
818
1551
|
agent: AgentSchema.optional(),
|
|
819
1552
|
harness: HarnessSchema.optional(),
|
|
820
1553
|
terminal: TerminalSchema.optional(),
|
|
1554
|
+
previews: PreviewsSchema.optional(),
|
|
1555
|
+
display: DisplaySchema.optional(),
|
|
821
1556
|
env: EnvSchema.default({}),
|
|
822
1557
|
resources: ResourcesSchema,
|
|
823
1558
|
timeouts: TimeoutsSchema.prefault({}),
|
|
@@ -840,7 +1575,11 @@ const TemplateSpecSchema = z.object({
|
|
|
840
1575
|
message: "either agent or harness is required"
|
|
841
1576
|
});
|
|
842
1577
|
if (!spec.agent) return;
|
|
843
|
-
if (spec.agent.transport === "acp") for (const field of [
|
|
1578
|
+
if (spec.agent.transport === "acp") for (const field of [
|
|
1579
|
+
"termWidth",
|
|
1580
|
+
"stateFile",
|
|
1581
|
+
"interruptKeys"
|
|
1582
|
+
]) {
|
|
844
1583
|
if (spec.agent[field] === void 0) continue;
|
|
845
1584
|
ctx.addIssue({
|
|
846
1585
|
code: "custom",
|
|
@@ -876,7 +1615,7 @@ const TemplateSpecSchema = z.object({
|
|
|
876
1615
|
};
|
|
877
1616
|
});
|
|
878
1617
|
const TemplateManifestBaseSchema = z.object({
|
|
879
|
-
apiVersion: z.literal("pocketcoder.dev/v1alpha1"),
|
|
1618
|
+
apiVersion: z.literal("pocketcoder.dev/v1alpha1", { error: "unsupported template format for the alpha runtime; canonical workloads require an installed workload runtime" }),
|
|
880
1619
|
kind: z.literal("Template"),
|
|
881
1620
|
metadata: z.object({
|
|
882
1621
|
name: z.string().regex(NAME_RE),
|
|
@@ -885,61 +1624,105 @@ const TemplateManifestBaseSchema = z.object({
|
|
|
885
1624
|
spec: TemplateSpecSchema
|
|
886
1625
|
});
|
|
887
1626
|
//#endregion
|
|
888
|
-
//#region ../contracts/src/
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1627
|
+
//#region ../contracts/src/workspaces/finite-process.ts
|
|
1628
|
+
const ProcessReferenceSchema = WorkloadExecutionReferenceSchema.extend({ owner: z.literal("process") });
|
|
1629
|
+
const FiniteProcessFactSchema = z.strictObject({
|
|
1630
|
+
execution: ProcessReferenceSchema,
|
|
1631
|
+
fingerprint: WorkloadDigestSchema,
|
|
1632
|
+
phase: z.enum([
|
|
1633
|
+
"claimed",
|
|
1634
|
+
"running",
|
|
1635
|
+
"exited",
|
|
1636
|
+
"lost"
|
|
1637
|
+
]),
|
|
1638
|
+
processGroupId: z.number().int().positive().nullable(),
|
|
1639
|
+
claimedAt: z.iso.datetime(),
|
|
1640
|
+
startedAt: z.iso.datetime().nullable(),
|
|
1641
|
+
endedAt: z.iso.datetime().nullable(),
|
|
1642
|
+
exitCode: z.number().int().nullable(),
|
|
1643
|
+
signal: z.string().min(1).nullable()
|
|
1644
|
+
}).superRefine((fact, ctx) => {
|
|
1645
|
+
if ((fact.phase === "exited" || fact.phase === "lost") !== (fact.endedAt !== null)) ctx.addIssue({
|
|
1646
|
+
code: "custom",
|
|
1647
|
+
message: "terminal facts require an end time"
|
|
1648
|
+
});
|
|
1649
|
+
if (fact.phase === "exited" && fact.exitCode === null === (fact.signal === null)) ctx.addIssue({
|
|
1650
|
+
code: "custom",
|
|
1651
|
+
message: "exit requires exactly one observed code or signal"
|
|
1652
|
+
});
|
|
1653
|
+
if (fact.phase !== "exited" && (fact.exitCode !== null || fact.signal !== null)) ctx.addIssue({
|
|
1654
|
+
code: "custom",
|
|
1655
|
+
message: "unobserved exits cannot have a code or signal"
|
|
1656
|
+
});
|
|
1657
|
+
if (fact.phase === "running" && (!fact.processGroupId || !fact.startedAt)) ctx.addIssue({
|
|
1658
|
+
code: "custom",
|
|
1659
|
+
message: "running facts require an owned process group and start time"
|
|
1660
|
+
});
|
|
906
1661
|
});
|
|
907
|
-
const
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1662
|
+
const WaitingRuntimeSpecSchema = z.strictObject({
|
|
1663
|
+
incarnationId: z.string().min(1).max(256),
|
|
1664
|
+
profile: WorkloadProfileSchema,
|
|
1665
|
+
operations: z.record(z.string(), HarnessSchema),
|
|
1666
|
+
infrastructure: z.record(z.string(), HarnessSchema)
|
|
1667
|
+
});
|
|
1668
|
+
const ProcessStartPayload = z.strictObject({
|
|
1669
|
+
execution: ProcessReferenceSchema,
|
|
1670
|
+
fingerprint: WorkloadDigestSchema,
|
|
1671
|
+
input: z.record(z.string(), JsonValueSchema).default({}),
|
|
1672
|
+
inputDirectory: z.string().min(1).optional(),
|
|
1673
|
+
operation: z.string().min(1).max(64)
|
|
1674
|
+
});
|
|
1675
|
+
const ProcessTargetPayload = z.strictObject({ execution: ProcessReferenceSchema });
|
|
1676
|
+
const ProcessCancelPayload = ProcessTargetPayload.extend({
|
|
1677
|
+
cancelId: z.uuid(),
|
|
1678
|
+
signal: z.enum(["SIGTERM", "SIGKILL"]),
|
|
1679
|
+
deadlineAt: z.iso.datetime()
|
|
1680
|
+
});
|
|
1681
|
+
const ProcessCancelResultPayload = ProcessTargetPayload.extend({
|
|
1682
|
+
cancelId: z.uuid(),
|
|
1683
|
+
status: z.enum([
|
|
1684
|
+
"canceled",
|
|
1685
|
+
"requested",
|
|
1686
|
+
"terminal"
|
|
1687
|
+
])
|
|
1688
|
+
});
|
|
1689
|
+
//#endregion
|
|
1690
|
+
//#region ../contracts/src/secrets/credential-slot.ts
|
|
1691
|
+
const CredentialSlotSchema = z.strictObject({
|
|
1692
|
+
policyRef: z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._/-]{0,254}$/).refine((value) => !value.includes("..")),
|
|
1693
|
+
policyVersion: z.string().min(1).max(128),
|
|
1694
|
+
binding: z.enum(["fixed-scope", "post-create-grant"]),
|
|
1695
|
+
delivery: z.literal("workspace-expiring-file"),
|
|
1696
|
+
maxLeaseSeconds: z.number().int().positive().max(Number.MAX_SAFE_INTEGER)
|
|
1697
|
+
});
|
|
1698
|
+
//#endregion
|
|
1699
|
+
//#region ../contracts/src/templates/workload-input-schema.ts
|
|
1700
|
+
const WorkloadInputPropertySchema = z.lazy(() => z.discriminatedUnion("type", [
|
|
1701
|
+
z.strictObject({ type: z.enum([
|
|
1702
|
+
"string",
|
|
1703
|
+
"number",
|
|
1704
|
+
"integer",
|
|
1705
|
+
"boolean",
|
|
1706
|
+
"null"
|
|
1707
|
+
]) }),
|
|
1708
|
+
z.strictObject({
|
|
1709
|
+
type: z.literal("array"),
|
|
1710
|
+
items: WorkloadInputPropertySchema
|
|
1711
|
+
}),
|
|
1712
|
+
WorkloadInputSchema
|
|
1713
|
+
]));
|
|
1714
|
+
const WorkloadInputSchema = z.strictObject({
|
|
1715
|
+
type: z.literal("object"),
|
|
1716
|
+
properties: z.record(z.string(), WorkloadInputPropertySchema),
|
|
1717
|
+
required: z.array(z.string()).default([]),
|
|
1718
|
+
additionalProperties: z.literal(false)
|
|
1719
|
+
}).superRefine((schema, ctx) => {
|
|
1720
|
+
if (new Set(schema.required).size !== schema.required.length || schema.required.some((name) => !Object.hasOwn(schema.properties, name))) ctx.addIssue({
|
|
1721
|
+
code: "custom",
|
|
1722
|
+
path: ["required"],
|
|
1723
|
+
message: "required input properties must be declared and unique"
|
|
1724
|
+
});
|
|
916
1725
|
});
|
|
917
|
-
function isAgentApiNative(spec) {
|
|
918
|
-
return "agent" in spec && spec.agent !== void 0;
|
|
919
|
-
}
|
|
920
|
-
function agentApiHarness(spec) {
|
|
921
|
-
if (!isAgentApiNative(spec)) return spec.harness;
|
|
922
|
-
return {
|
|
923
|
-
command: [
|
|
924
|
-
"/usr/local/bin/agentapi",
|
|
925
|
-
"server",
|
|
926
|
-
"--type",
|
|
927
|
-
spec.agent.type,
|
|
928
|
-
...spec.agent.transport === "acp" ? ["--experimental-acp"] : [],
|
|
929
|
-
...spec.agent.termWidth === void 0 ? [] : ["--term-width", String(spec.agent.termWidth)],
|
|
930
|
-
...spec.agent.stateFile === void 0 ? [] : ["--state-file", spec.agent.stateFile],
|
|
931
|
-
"--port",
|
|
932
|
-
"3284",
|
|
933
|
-
"--",
|
|
934
|
-
...spec.agent.command
|
|
935
|
-
],
|
|
936
|
-
env: spec.agent.env,
|
|
937
|
-
...spec.agent.cwd ? { cwd: spec.agent.cwd } : {}
|
|
938
|
-
};
|
|
939
|
-
}
|
|
940
|
-
function templateServices(spec) {
|
|
941
|
-
return isAgentApiNative(spec) ? { agent: AGENTAPI_SERVICE } : spec.services;
|
|
942
|
-
}
|
|
943
1726
|
//#endregion
|
|
944
1727
|
//#region ../contracts/src/templates/template-validation.ts
|
|
945
1728
|
function envSources(spec) {
|
|
@@ -987,7 +1770,7 @@ function validateNetworkEnvironment(spec, ctx) {
|
|
|
987
1770
|
});
|
|
988
1771
|
}
|
|
989
1772
|
}
|
|
990
|
-
function validateServices(spec, ctx) {
|
|
1773
|
+
function validateServices$1(spec, ctx) {
|
|
991
1774
|
for (const [serviceName, service] of Object.entries(templateServices(spec))) {
|
|
992
1775
|
if (!isLoopbackBaseUrl(service.baseUrl)) ctx.addIssue({
|
|
993
1776
|
code: "custom",
|
|
@@ -1130,7 +1913,7 @@ const FORBIDDEN_PERSISTENCE_ROOTS = [
|
|
|
1130
1913
|
function pathContains(parent, child) {
|
|
1131
1914
|
return child === parent || child.startsWith(`${parent}/`);
|
|
1132
1915
|
}
|
|
1133
|
-
function
|
|
1916
|
+
function validateTemplatePersistence(spec, ctx) {
|
|
1134
1917
|
const seenNames = /* @__PURE__ */ new Set();
|
|
1135
1918
|
const mounts = spec.persistence.mounts;
|
|
1136
1919
|
for (const [index, mount] of mounts.entries()) validatePersistenceMount(spec, mounts, mount, index, seenNames, ctx);
|
|
@@ -1147,7 +1930,7 @@ function validatePersistence(spec, ctx) {
|
|
|
1147
1930
|
});
|
|
1148
1931
|
}
|
|
1149
1932
|
function validateAgentStateFile(spec, mounts, ctx) {
|
|
1150
|
-
if (!
|
|
1933
|
+
if (!spec.agent) return;
|
|
1151
1934
|
const stateFile = spec.agent.stateFile;
|
|
1152
1935
|
const normalized = stateFile !== void 0 && isNormalizedFilesystemPath(stateFile);
|
|
1153
1936
|
if (stateFile !== void 0 && !normalized) ctx.addIssue({
|
|
@@ -1227,59 +2010,477 @@ function validateMountOverlap(mounts, mount, index, path, ctx) {
|
|
|
1227
2010
|
});
|
|
1228
2011
|
}
|
|
1229
2012
|
}
|
|
1230
|
-
function validateMemoryPathOverlap(spec, mount, path, ctx) {
|
|
1231
|
-
for (const memoryPath of spec.security.writableMemoryPaths) {
|
|
1232
|
-
if (!pathContains(memoryPath, mount.target) && !pathContains(mount.target, memoryPath)) continue;
|
|
1233
|
-
ctx.addIssue({
|
|
1234
|
-
code: "custom",
|
|
1235
|
-
path,
|
|
1236
|
-
message: `target overlaps writableMemoryPath ${memoryPath}`
|
|
1237
|
-
});
|
|
2013
|
+
function validateMemoryPathOverlap(spec, mount, path, ctx) {
|
|
2014
|
+
for (const memoryPath of spec.security.writableMemoryPaths) {
|
|
2015
|
+
if (!pathContains(memoryPath, mount.target) && !pathContains(mount.target, memoryPath)) continue;
|
|
2016
|
+
ctx.addIssue({
|
|
2017
|
+
code: "custom",
|
|
2018
|
+
path,
|
|
2019
|
+
message: `target overlaps writableMemoryPath ${memoryPath}`
|
|
2020
|
+
});
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
function validateSourceMount(spec, mounts, ctx) {
|
|
2024
|
+
if (!spec.source) return;
|
|
2025
|
+
if (mounts.find((mount) => mount.name === spec.source?.destinationMount)) return;
|
|
2026
|
+
ctx.addIssue({
|
|
2027
|
+
code: "custom",
|
|
2028
|
+
path: [
|
|
2029
|
+
"spec",
|
|
2030
|
+
"source",
|
|
2031
|
+
"destinationMount"
|
|
2032
|
+
],
|
|
2033
|
+
message: "source destinationMount must name a persistence mount"
|
|
2034
|
+
});
|
|
2035
|
+
}
|
|
2036
|
+
function isNormalizedFilesystemPath(path) {
|
|
2037
|
+
if (!isAbsolutePath(path) || path === "/") return false;
|
|
2038
|
+
if (path.endsWith("/") || path.includes("//") || path.includes("\\") || path.includes(",")) return false;
|
|
2039
|
+
if ([...path].some((character) => {
|
|
2040
|
+
const code = character.codePointAt(0) ?? 0;
|
|
2041
|
+
return code < 32 || code === 127;
|
|
2042
|
+
})) return false;
|
|
2043
|
+
return !path.split("/").some((part) => part === "." || part === "..");
|
|
2044
|
+
}
|
|
2045
|
+
function isNormalizedPath(path) {
|
|
2046
|
+
if (!path.startsWith("/")) return false;
|
|
2047
|
+
if (path.includes("..") || path.includes("//")) return false;
|
|
2048
|
+
if (/[?#\s]/.test(path)) return false;
|
|
2049
|
+
if (/%2e|%2f|%5c/i.test(path)) return false;
|
|
2050
|
+
if ([...path].some((character) => character.charCodeAt(0) <= 31)) return false;
|
|
2051
|
+
return true;
|
|
2052
|
+
}
|
|
2053
|
+
function validateTemplateSpec(spec, ctx) {
|
|
2054
|
+
validateServices$1(spec, ctx);
|
|
2055
|
+
validatePreviews(spec, ctx);
|
|
2056
|
+
validateEnvironment(spec, ctx);
|
|
2057
|
+
validateOutputs(spec, ctx);
|
|
2058
|
+
validateWritableMemoryPaths(spec, ctx);
|
|
2059
|
+
validateTemplatePersistence(spec, ctx);
|
|
2060
|
+
validateNetworkEnvironment(spec, ctx);
|
|
2061
|
+
validateTerminal(spec, ctx);
|
|
2062
|
+
}
|
|
2063
|
+
function validatePreviews(spec, ctx) {
|
|
2064
|
+
const servicePorts = new Set(Object.values(templateServices(spec)).filter((service) => isLoopbackBaseUrl(service.baseUrl)).map((service) => {
|
|
2065
|
+
const url = new URL(service.baseUrl);
|
|
2066
|
+
return Number(url.port || 80);
|
|
2067
|
+
}));
|
|
2068
|
+
for (const [name, preview] of Object.entries(spec.previews ?? {})) if (servicePorts.has(preview.port)) ctx.addIssue({
|
|
2069
|
+
code: "custom",
|
|
2070
|
+
path: [
|
|
2071
|
+
"spec",
|
|
2072
|
+
"previews",
|
|
2073
|
+
name,
|
|
2074
|
+
"port"
|
|
2075
|
+
],
|
|
2076
|
+
message: "preview port is reserved by a template service"
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
//#endregion
|
|
2080
|
+
//#region ../contracts/src/templates/workload-validation.ts
|
|
2081
|
+
function isWorkloadPath(path) {
|
|
2082
|
+
return path.startsWith("/") && !/[\\,\s]/.test(path) && !path.includes("//") && !path.endsWith("/") && ![...path].some((character) => {
|
|
2083
|
+
const code = character.charCodeAt(0);
|
|
2084
|
+
return code < 32 || code === 127;
|
|
2085
|
+
}) && !path.split("/").some((part) => part === "." || part === "..");
|
|
2086
|
+
}
|
|
2087
|
+
function overlaps(left, right) {
|
|
2088
|
+
return left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
|
|
2089
|
+
}
|
|
2090
|
+
function validateProfile(spec, issue) {
|
|
2091
|
+
const operations = Object.values(spec.operations);
|
|
2092
|
+
if (operations.length === 0 && spec.profile !== "service") issue(["operations"], "at least one operation is required");
|
|
2093
|
+
if (spec.profile === "service" && Object.keys(spec.services).length === 0) issue(["services"], "service profile requires declared transport");
|
|
2094
|
+
if (spec.profile === "finite-process" && (operations.length !== 1 || !Object.hasOwn(spec.adapters, operations[0]?.adapter ?? "") || spec.adapters[operations[0]?.adapter ?? ""]?.kind !== "process")) issue(["operations"], "finite-process requires one process operation");
|
|
2095
|
+
}
|
|
2096
|
+
function validateSecurity(spec, issue) {
|
|
2097
|
+
if (!spec.security.readOnlyRoot || !spec.security.dropCapabilities.includes("ALL")) issue(["security"], "workload profiles require a read-only root and all capabilities dropped");
|
|
2098
|
+
for (const path of spec.security.writableMemoryPaths) if (!isWorkloadPath(path) || [
|
|
2099
|
+
"/run/pocketcoder",
|
|
2100
|
+
"/proc",
|
|
2101
|
+
"/sys",
|
|
2102
|
+
"/dev"
|
|
2103
|
+
].some((root) => overlaps(path, root))) issue(["security", "writableMemoryPaths"], "writable path overlaps protected storage or is not normalized");
|
|
2104
|
+
}
|
|
2105
|
+
function validateAdapters(spec, issue) {
|
|
2106
|
+
for (const [name, adapter] of Object.entries(spec.adapters)) {
|
|
2107
|
+
const path = ["adapters", name];
|
|
2108
|
+
if (adapter.kind === "service") {
|
|
2109
|
+
if (!adapter.service || !Object.hasOwn(spec.services, adapter.service) || adapter.command) issue(path, "service adapters require a declared service and no process command");
|
|
2110
|
+
} else if (!adapter.command || adapter.service) issue(path, "process, Pi and AgentAPI adapters require a command and no service reference");
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
function validateProcessEnvironment(spec, issue) {
|
|
2114
|
+
const processes = [
|
|
2115
|
+
...spec.lifecycle?.checkpointHook ? [{
|
|
2116
|
+
path: ["lifecycle", "checkpointHook"],
|
|
2117
|
+
process: spec.lifecycle.checkpointHook
|
|
2118
|
+
}] : [],
|
|
2119
|
+
...Object.entries(spec.adapters).map(([name, adapter]) => ({
|
|
2120
|
+
path: ["adapters", name],
|
|
2121
|
+
process: adapter
|
|
2122
|
+
})),
|
|
2123
|
+
...Object.entries(spec.services).flatMap(([name, service]) => service.process ? [{
|
|
2124
|
+
path: [
|
|
2125
|
+
"services",
|
|
2126
|
+
name,
|
|
2127
|
+
"process"
|
|
2128
|
+
],
|
|
2129
|
+
process: service.process
|
|
2130
|
+
}] : [])
|
|
2131
|
+
];
|
|
2132
|
+
for (const { path, process } of processes) {
|
|
2133
|
+
if (process.cwd && process.cwd !== "/" && !isWorkloadPath(process.cwd)) issue(path, "cwd must be normalized");
|
|
2134
|
+
validateEnv(spec, process.env, path, issue);
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
function validateEnv(spec, env, path, issue) {
|
|
2138
|
+
for (const [key, value] of Object.entries(env)) {
|
|
2139
|
+
if (value.startsWith("credentialSlot:")) {
|
|
2140
|
+
if (!Object.hasOwn(spec.credentialSlots, value.slice(15))) issue([
|
|
2141
|
+
...path,
|
|
2142
|
+
"env",
|
|
2143
|
+
key
|
|
2144
|
+
], "unknown credential slot");
|
|
2145
|
+
} else if (SECRET_ENV_PATTERN.test(key) && value !== "" || value.startsWith("secretRef:")) issue([
|
|
2146
|
+
...path,
|
|
2147
|
+
"env",
|
|
2148
|
+
key
|
|
2149
|
+
], "runtime credentials require a credentialSlot: reference");
|
|
2150
|
+
if (spec.network.mode === "restricted" && /^(http_proxy|https_proxy|all_proxy|no_proxy)$/i.test(key)) issue([
|
|
2151
|
+
...path,
|
|
2152
|
+
"env",
|
|
2153
|
+
key
|
|
2154
|
+
], "proxy variables are reserved by restricted networking");
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
function validateOperations(spec, issue) {
|
|
2158
|
+
for (const [name, operation] of Object.entries(spec.operations)) {
|
|
2159
|
+
const adapter = Object.hasOwn(spec.adapters, operation.adapter) ? spec.adapters[operation.adapter] : void 0;
|
|
2160
|
+
if (!adapter) issue([
|
|
2161
|
+
"operations",
|
|
2162
|
+
name,
|
|
2163
|
+
"adapter"
|
|
2164
|
+
], "operation must name a pinned adapter");
|
|
2165
|
+
if (spec.profile === "cloud-interactive" && adapter?.kind === "process") issue(["operations", name], "cloud-interactive requires request-aware adapters");
|
|
2166
|
+
for (const root of operation.outputRoots) if (!isWorkloadPath(root) || [
|
|
2167
|
+
"/run",
|
|
2168
|
+
"/proc",
|
|
2169
|
+
"/sys",
|
|
2170
|
+
"/dev"
|
|
2171
|
+
].some((path) => overlaps(path, root))) issue([
|
|
2172
|
+
"operations",
|
|
2173
|
+
name,
|
|
2174
|
+
"outputRoots"
|
|
2175
|
+
], "output roots must be normalized and outside protected storage");
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
function serviceUrl(value) {
|
|
2179
|
+
try {
|
|
2180
|
+
return new URL(value);
|
|
2181
|
+
} catch {
|
|
2182
|
+
return null;
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
function isLoopbackService(url) {
|
|
2186
|
+
return url.protocol === "http:" && [
|
|
2187
|
+
"127.0.0.1",
|
|
2188
|
+
"localhost",
|
|
2189
|
+
"[::1]"
|
|
2190
|
+
].includes(url.hostname) && !url.username && !url.password && url.pathname === "/" && !url.search && !url.hash;
|
|
2191
|
+
}
|
|
2192
|
+
function validateServices(spec, issue) {
|
|
2193
|
+
for (const [name, service] of Object.entries(spec.services)) {
|
|
2194
|
+
const url = serviceUrl(service.baseUrl);
|
|
2195
|
+
if (!url || !isLoopbackService(url)) {
|
|
2196
|
+
issue(["services", name], "service must use loopback HTTP without credentials or a path");
|
|
2197
|
+
continue;
|
|
2198
|
+
}
|
|
2199
|
+
const routes = /* @__PURE__ */ new Set();
|
|
2200
|
+
if (!isNormalizedPath(service.healthPath)) issue([
|
|
2201
|
+
"services",
|
|
2202
|
+
name,
|
|
2203
|
+
"healthPath"
|
|
2204
|
+
], "health path must be normalized");
|
|
2205
|
+
for (const route of service.routes) {
|
|
2206
|
+
const key = `${route.method} ${route.path}`;
|
|
2207
|
+
if (!isNormalizedPath(route.path) || routes.has(key)) issue([
|
|
2208
|
+
"services",
|
|
2209
|
+
name,
|
|
2210
|
+
"routes"
|
|
2211
|
+
], "routes must be normalized and unique");
|
|
2212
|
+
routes.add(key);
|
|
2213
|
+
}
|
|
2214
|
+
for (const [previewName, preview] of Object.entries(spec.previews)) if (preview.port === Number(url.port || 80)) issue(["previews", previewName], "preview port is reserved by a service");
|
|
1238
2215
|
}
|
|
1239
2216
|
}
|
|
1240
|
-
function
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
ctx.addIssue({
|
|
2217
|
+
function validateWorkloadTemplate(manifest, ctx) {
|
|
2218
|
+
const spec = manifest.spec;
|
|
2219
|
+
const issue = (path, message) => ctx.addIssue({
|
|
1244
2220
|
code: "custom",
|
|
1245
|
-
path: [
|
|
1246
|
-
|
|
1247
|
-
"source",
|
|
1248
|
-
"destinationMount"
|
|
1249
|
-
],
|
|
1250
|
-
message: "source destinationMount must name a persistence mount"
|
|
2221
|
+
path: ["spec", ...path],
|
|
2222
|
+
message
|
|
1251
2223
|
});
|
|
2224
|
+
validateTemplatePersistence({
|
|
2225
|
+
security: spec.security,
|
|
2226
|
+
source: null,
|
|
2227
|
+
persistence: spec.lifecycle?.persistence ?? PersistenceSpecSchema.parse({})
|
|
2228
|
+
}, ctx);
|
|
2229
|
+
const timeouts = spec.lifecycle?.timeouts;
|
|
2230
|
+
if (timeouts && (parseDurationMs(timeouts.maxAge) > spec.limits.deadlineSeconds * 1e3 || parseDurationMs(timeouts.terminateGrace) > spec.limits.finalizationSeconds * 1e3)) issue(["lifecycle", "timeouts"], "Lifecycle timeouts exceed the workload limits.");
|
|
2231
|
+
validateProfile(spec, issue);
|
|
2232
|
+
validateSecurity(spec, issue);
|
|
2233
|
+
validateAdapters(spec, issue);
|
|
2234
|
+
validateProcessEnvironment(spec, issue);
|
|
2235
|
+
validateOperations(spec, issue);
|
|
2236
|
+
validateServices(spec, issue);
|
|
2237
|
+
for (const [name, slot] of Object.entries(spec.credentialSlots)) if (slot.maxLeaseSeconds > spec.limits.deadlineSeconds) issue(["credentialSlots", name], "credential lifetime exceeds the workspace deadline");
|
|
1252
2238
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
2239
|
+
//#endregion
|
|
2240
|
+
//#region ../contracts/src/templates/workload-template.ts
|
|
2241
|
+
const WorkloadAdapterSchema = z.strictObject({
|
|
2242
|
+
kind: z.enum([
|
|
2243
|
+
"process",
|
|
2244
|
+
"service",
|
|
2245
|
+
"pi",
|
|
2246
|
+
"agentapi"
|
|
2247
|
+
]),
|
|
2248
|
+
digest: WorkloadDigestSchema,
|
|
2249
|
+
protocol: z.string().min(1).max(128),
|
|
2250
|
+
command: HarnessSchema.shape.command.optional(),
|
|
2251
|
+
cwd: HarnessSchema.shape.cwd,
|
|
2252
|
+
env: HarnessSchema.shape.env,
|
|
2253
|
+
service: WorkloadNameSchema.optional()
|
|
2254
|
+
});
|
|
2255
|
+
const WorkloadOperationSchema = z.strictObject({
|
|
2256
|
+
adapter: WorkloadNameSchema,
|
|
2257
|
+
inputSchema: WorkloadInputSchema,
|
|
2258
|
+
requiredCapabilities: z.array(WorkloadCapabilitySchema),
|
|
2259
|
+
outputRoots: z.array(z.string()).max(16)
|
|
2260
|
+
});
|
|
2261
|
+
const WorkloadTemplateSchema = z.strictObject({
|
|
2262
|
+
apiVersion: z.literal("pocketcoder.dev/v1"),
|
|
2263
|
+
kind: z.literal("Template"),
|
|
2264
|
+
metadata: z.strictObject({
|
|
2265
|
+
name: WorkloadNameSchema,
|
|
2266
|
+
description: z.string().max(512).optional()
|
|
2267
|
+
}),
|
|
2268
|
+
spec: z.strictObject({
|
|
2269
|
+
version: z.string().regex(/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/),
|
|
2270
|
+
image: WorkloadImageSchema,
|
|
2271
|
+
runtime: WorkloadRuntimePinSchema,
|
|
2272
|
+
profile: WorkloadProfileSchema,
|
|
2273
|
+
adapters: z.record(WorkloadNameSchema, WorkloadAdapterSchema),
|
|
2274
|
+
operations: z.record(WorkloadNameSchema, WorkloadOperationSchema),
|
|
2275
|
+
services: z.record(WorkloadNameSchema, ServiceSchema.extend({ process: HarnessSchema.strict().optional() }).strict()),
|
|
2276
|
+
previews: PreviewsSchema,
|
|
2277
|
+
credentialSlots: z.record(WorkloadNameSchema, CredentialSlotSchema),
|
|
2278
|
+
resources: ResourcesSchema.strict(),
|
|
2279
|
+
network: NetworkPolicySchema,
|
|
2280
|
+
security: SecuritySchema.strict(),
|
|
2281
|
+
limits: WorkloadLimitsSchema,
|
|
2282
|
+
lifecycle: z.strictObject({
|
|
2283
|
+
persistence: PersistenceSpecSchema.prefault({}),
|
|
2284
|
+
checkpointHook: CheckpointHookSchema.optional(),
|
|
2285
|
+
timeouts: TimeoutsSchema.optional(),
|
|
2286
|
+
maxLaunchInputBytes: z.number().int().positive().max(1048576).default(65536)
|
|
2287
|
+
}).optional()
|
|
2288
|
+
})
|
|
2289
|
+
}).superRefine(validateWorkloadTemplate);
|
|
2290
|
+
const WorkloadTemplateSnapshotSchema = z.strictObject({
|
|
2291
|
+
format: z.literal("pocketcoder.dev/v1"),
|
|
2292
|
+
name: WorkloadNameSchema,
|
|
2293
|
+
version: z.string(),
|
|
2294
|
+
digest: WorkloadDigestSchema,
|
|
2295
|
+
manifest: WorkloadTemplateSchema
|
|
2296
|
+
}).superRefine((snapshot, ctx) => {
|
|
2297
|
+
if (snapshot.name !== snapshot.manifest.metadata.name || snapshot.version !== snapshot.manifest.spec.version || snapshot.digest !== digestOf(snapshot.manifest)) ctx.addIssue({
|
|
2298
|
+
code: "custom",
|
|
2299
|
+
message: "snapshot identity does not match its immutable manifest"
|
|
2300
|
+
});
|
|
2301
|
+
});
|
|
2302
|
+
//#endregion
|
|
2303
|
+
//#region ../contracts/src/templates/template-runtime.ts
|
|
2304
|
+
const LEGACY_AGENTAPI_SERVICE = ServiceSchema.parse({
|
|
2305
|
+
baseUrl: "http://127.0.0.1:3284",
|
|
2306
|
+
routes: [
|
|
2307
|
+
{
|
|
2308
|
+
method: "GET",
|
|
2309
|
+
path: "/status"
|
|
2310
|
+
},
|
|
2311
|
+
{
|
|
2312
|
+
method: "GET",
|
|
2313
|
+
path: "/messages",
|
|
2314
|
+
query: ["after"]
|
|
2315
|
+
},
|
|
2316
|
+
{
|
|
2317
|
+
method: "POST",
|
|
2318
|
+
path: "/message"
|
|
2319
|
+
}
|
|
2320
|
+
]
|
|
2321
|
+
});
|
|
2322
|
+
const AGENTAPI_SERVICE = ServiceSchema.parse({
|
|
2323
|
+
...LEGACY_AGENTAPI_SERVICE,
|
|
2324
|
+
routes: [...LEGACY_AGENTAPI_SERVICE.routes, {
|
|
2325
|
+
method: "GET",
|
|
2326
|
+
path: "/events",
|
|
2327
|
+
responseMode: "stream",
|
|
2328
|
+
maxResponseBytes: 4194304,
|
|
2329
|
+
deadlineSeconds: 300
|
|
2330
|
+
}]
|
|
2331
|
+
});
|
|
2332
|
+
function isAgentApiNative(spec) {
|
|
2333
|
+
return "agent" in spec && spec.agent !== void 0;
|
|
1261
2334
|
}
|
|
1262
|
-
function
|
|
1263
|
-
if (!
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
2335
|
+
function agentApiHarness(spec) {
|
|
2336
|
+
if (!isAgentApiNative(spec)) {
|
|
2337
|
+
if (!spec.harness) throw new Error("A workload infrastructure process is not an alpha harness.");
|
|
2338
|
+
return spec.harness;
|
|
2339
|
+
}
|
|
2340
|
+
return {
|
|
2341
|
+
command: [
|
|
2342
|
+
"/usr/local/bin/agentapi",
|
|
2343
|
+
"server",
|
|
2344
|
+
"--type",
|
|
2345
|
+
spec.agent.type,
|
|
2346
|
+
...spec.agent.transport === "acp" ? ["--experimental-acp"] : [],
|
|
2347
|
+
...spec.agent.termWidth === void 0 ? [] : ["--term-width", String(spec.agent.termWidth)],
|
|
2348
|
+
...spec.agent.stateFile === void 0 ? [] : ["--state-file", spec.agent.stateFile],
|
|
2349
|
+
"--port",
|
|
2350
|
+
"3284",
|
|
2351
|
+
"--",
|
|
2352
|
+
...spec.agent.command
|
|
2353
|
+
],
|
|
2354
|
+
env: spec.agent.env,
|
|
2355
|
+
...spec.agent.cwd ? { cwd: spec.agent.cwd } : {}
|
|
2356
|
+
};
|
|
1269
2357
|
}
|
|
1270
|
-
function
|
|
1271
|
-
|
|
1272
|
-
validateEnvironment(spec, ctx);
|
|
1273
|
-
validateOutputs(spec, ctx);
|
|
1274
|
-
validateWritableMemoryPaths(spec, ctx);
|
|
1275
|
-
validatePersistence(spec, ctx);
|
|
1276
|
-
validateNetworkEnvironment(spec, ctx);
|
|
1277
|
-
validateTerminal(spec, ctx);
|
|
2358
|
+
function templateServices(spec) {
|
|
2359
|
+
return isAgentApiNative(spec) ? { agent: AGENTAPI_SERVICE } : spec.services ?? {};
|
|
1278
2360
|
}
|
|
1279
|
-
|
|
2361
|
+
z.object({
|
|
2362
|
+
name: z.string(),
|
|
2363
|
+
version: z.string(),
|
|
2364
|
+
digest: WorkloadDigestSchema,
|
|
2365
|
+
spec: TemplateSpecSchema
|
|
2366
|
+
});
|
|
2367
|
+
//#endregion
|
|
2368
|
+
//#region ../contracts/src/templates/template.ts
|
|
2369
|
+
const TemplateManifestSchema = TemplateManifestBaseSchema.superRefine((manifest, ctx) => {
|
|
1280
2370
|
validateTemplateSpec(manifest.spec, ctx);
|
|
1281
2371
|
});
|
|
1282
2372
|
//#endregion
|
|
2373
|
+
//#region ../contracts/src/protocol/protocol-checkpoint.ts
|
|
2374
|
+
const bytes = z.number().int().positive().max(Number.MAX_SAFE_INTEGER);
|
|
2375
|
+
const digest = z.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
2376
|
+
const PrepareCheckpointArchivePayload = z.object({
|
|
2377
|
+
operation_id: z.uuid(),
|
|
2378
|
+
checkpoint_id: z.uuid(),
|
|
2379
|
+
deadline_ms: bytes,
|
|
2380
|
+
mounts: z.array(PersistenceMountSchema).max(16),
|
|
2381
|
+
max_archive_bytes: bytes,
|
|
2382
|
+
max_index_bytes: bytes,
|
|
2383
|
+
max_queue_bytes: bytes
|
|
2384
|
+
});
|
|
2385
|
+
const CheckpointPreparedPayload = z.object({
|
|
2386
|
+
operation_id: z.uuid(),
|
|
2387
|
+
checkpoint_id: z.uuid(),
|
|
2388
|
+
header: CheckpointArchiveHeaderSchema,
|
|
2389
|
+
archive_bytes: bytes
|
|
2390
|
+
}).refine((value) => value.header.checkpoint_id === value.checkpoint_id, "Checkpoint declaration identity differs.");
|
|
2391
|
+
const grant = z.object({
|
|
2392
|
+
operation_id: z.uuid(),
|
|
2393
|
+
transfer_id: z.uuid(),
|
|
2394
|
+
checkpoint_id: z.uuid(),
|
|
2395
|
+
credential: z.string().min(1).max(4096),
|
|
2396
|
+
url: z.url(),
|
|
2397
|
+
expires_at: z.iso.datetime()
|
|
2398
|
+
});
|
|
2399
|
+
const CheckpointUploadPayload = grant;
|
|
2400
|
+
const RestoreTransferSpecSchema = grant.extend({
|
|
2401
|
+
source: z.object({
|
|
2402
|
+
checkpoint_id: z.uuid(),
|
|
2403
|
+
workspace_id: z.uuid(),
|
|
2404
|
+
template_digest: digest,
|
|
2405
|
+
archive_digest: digest
|
|
2406
|
+
}),
|
|
2407
|
+
mounts: z.array(PersistenceMountSchema).max(16),
|
|
2408
|
+
max_archive_bytes: bytes,
|
|
2409
|
+
max_index_bytes: bytes,
|
|
2410
|
+
max_ledger_bytes: bytes
|
|
2411
|
+
}).refine((value) => value.source.checkpoint_id === value.checkpoint_id, "Restore source identity differs.");
|
|
2412
|
+
const CheckpointInstalledPayload = z.object({
|
|
2413
|
+
operation_id: z.uuid(),
|
|
2414
|
+
transfer_id: z.uuid(),
|
|
2415
|
+
checkpoint_id: z.uuid(),
|
|
2416
|
+
archive_digest: digest,
|
|
2417
|
+
phase: z.enum(["installed", "failed"])
|
|
2418
|
+
});
|
|
2419
|
+
const CheckpointUploadStatusPayload = z.object({
|
|
2420
|
+
operation_id: z.uuid(),
|
|
2421
|
+
transfer_id: z.uuid(),
|
|
2422
|
+
checkpoint_id: z.uuid(),
|
|
2423
|
+
phase: z.enum(["uploaded", "failed"])
|
|
2424
|
+
});
|
|
2425
|
+
//#endregion
|
|
2426
|
+
//#region ../contracts/src/protocol/protocol-exec.ts
|
|
2427
|
+
const SOURCE_CREDENTIAL_MAX_BYTES = 65536;
|
|
2428
|
+
const RestoreModeSchema = z.enum(["provider_installed", "controller_archive"]);
|
|
2429
|
+
const ExecSpecSchema = z.object({
|
|
2430
|
+
agentapi_native: z.boolean().default(false),
|
|
2431
|
+
setup: z.array(SetupStepSchema),
|
|
2432
|
+
harness: HarnessSchema.nullable(),
|
|
2433
|
+
workload: WaitingRuntimeSpecSchema.optional(),
|
|
2434
|
+
env: z.record(z.string(), z.string()),
|
|
2435
|
+
services: z.record(z.string(), ServiceSchema),
|
|
2436
|
+
previews: PreviewsSchema.optional(),
|
|
2437
|
+
display: DisplaySchema.optional(),
|
|
2438
|
+
terminal: z.object({
|
|
2439
|
+
command: z.array(z.string().min(1)).min(1),
|
|
2440
|
+
cwd: z.string().optional(),
|
|
2441
|
+
env: z.record(z.string(), z.string()),
|
|
2442
|
+
max_sessions: z.number().int().min(1).max(8),
|
|
2443
|
+
idle_timeout_seconds: z.number().int().positive(),
|
|
2444
|
+
replay_buffer_bytes: z.number().int().positive()
|
|
2445
|
+
}).nullable().default(null),
|
|
2446
|
+
timeouts: TimeoutsSchema,
|
|
2447
|
+
security: z.object({ writable_memory_paths: z.array(z.string()) }).default({ writable_memory_paths: [] }),
|
|
2448
|
+
network: z.discriminatedUnion("mode", [z.object({ mode: z.literal("unrestricted") }), z.object({
|
|
2449
|
+
mode: z.literal("restricted"),
|
|
2450
|
+
proxy_url: z.url(),
|
|
2451
|
+
health_url: z.url()
|
|
2452
|
+
})]).default({ mode: "unrestricted" }),
|
|
2453
|
+
launch_mode: z.enum(LAUNCH_MODES).default("create"),
|
|
2454
|
+
source: SourceDescriptorSchema.extend({
|
|
2455
|
+
url: z.url(),
|
|
2456
|
+
destination: z.string(),
|
|
2457
|
+
max_bytes: z.number().int().positive().optional(),
|
|
2458
|
+
max_files: z.number().int().positive().optional(),
|
|
2459
|
+
credential_expires_at: z.iso.datetime().nullable().default(null),
|
|
2460
|
+
credential: z.string().min(1).max(SOURCE_CREDENTIAL_MAX_BYTES).refine((value) => !value.includes("\0"), "credential must not contain NUL bytes").nullable().default(null)
|
|
2461
|
+
}).nullable().default(null),
|
|
2462
|
+
restore: z.object({
|
|
2463
|
+
mode: RestoreModeSchema,
|
|
2464
|
+
checkpoint_id: z.uuid(),
|
|
2465
|
+
origin_workspace_id: z.uuid(),
|
|
2466
|
+
transfer: RestoreTransferSpecSchema.nullable().default(null)
|
|
2467
|
+
}).nullable().default(null),
|
|
2468
|
+
persistence: z.object({
|
|
2469
|
+
mounts: z.array(z.object({
|
|
2470
|
+
name: z.string(),
|
|
2471
|
+
target: z.string()
|
|
2472
|
+
})),
|
|
2473
|
+
conversation_restore: z.enum(CONVERSATION_RESTORE_CAPABILITIES)
|
|
2474
|
+
}),
|
|
2475
|
+
checkpoint_hook: z.object({
|
|
2476
|
+
command: z.array(z.string().min(1)).min(1),
|
|
2477
|
+
timeout_seconds: z.number().int().positive(),
|
|
2478
|
+
env: z.record(z.string(), z.string()),
|
|
2479
|
+
cwd: z.string().optional()
|
|
2480
|
+
}).nullable(),
|
|
2481
|
+
outputs: z.record(z.string(), z.unknown())
|
|
2482
|
+
});
|
|
2483
|
+
//#endregion
|
|
1283
2484
|
//#region ../contracts/src/terminals/terminal.ts
|
|
1284
2485
|
const TERMINAL_CHUNK_BYTES = 32768;
|
|
1285
2486
|
const TERMINAL_REPLAY_BUFFER_BYTES = 65536;
|
|
@@ -1394,11 +2595,34 @@ const TerminalSessionSchema = z.object({
|
|
|
1394
2595
|
CursorQuerySchema(200, 50);
|
|
1395
2596
|
const TerminalSessionPageSchema = CursorPageSchema(TerminalSessionSchema);
|
|
1396
2597
|
//#endregion
|
|
2598
|
+
//#region ../contracts/src/protocol/protocol-credentials.ts
|
|
2599
|
+
const WorkspaceCredentialSchema = z.object({
|
|
2600
|
+
lease_id: z.uuid(),
|
|
2601
|
+
path: z.string().regex(/^\/run\/pocketcoder\/secrets\/leases\/[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/),
|
|
2602
|
+
credential: z.string().min(1).max(SOURCE_CREDENTIAL_MAX_BYTES).refine((value) => !value.includes("\0")),
|
|
2603
|
+
expires_at: z.iso.datetime(),
|
|
2604
|
+
purpose: z.enum(["setup-issuer", "runtime-issuer"])
|
|
2605
|
+
});
|
|
2606
|
+
const CredentialRenewPayload = z.object({
|
|
2607
|
+
lease_id: z.uuid(),
|
|
2608
|
+
request_id: z.uuid()
|
|
2609
|
+
});
|
|
2610
|
+
const CredentialRenewedPayload = z.object({
|
|
2611
|
+
previous_lease_id: z.uuid(),
|
|
2612
|
+
request_id: z.uuid(),
|
|
2613
|
+
credential: WorkspaceCredentialSchema
|
|
2614
|
+
});
|
|
2615
|
+
const CredentialInstalledPayload = z.object({
|
|
2616
|
+
previous_lease_id: z.uuid(),
|
|
2617
|
+
lease_id: z.uuid()
|
|
2618
|
+
});
|
|
2619
|
+
//#endregion
|
|
1397
2620
|
//#region ../contracts/src/protocol/protocol-stream.ts
|
|
1398
2621
|
const ProxyStreamStartPayload = z.object({
|
|
1399
2622
|
request_id: z.uuid(),
|
|
1400
2623
|
status: z.number().int().min(100).max(599),
|
|
1401
|
-
headers: z.record(z.string(), z.string())
|
|
2624
|
+
headers: z.record(z.string(), z.string()),
|
|
2625
|
+
cookies: z.array(z.string().max(4096)).max(16).optional()
|
|
1402
2626
|
});
|
|
1403
2627
|
const ProxyStreamChunkPayload = z.object({
|
|
1404
2628
|
request_id: z.uuid(),
|
|
@@ -1466,7 +2690,6 @@ z.object({
|
|
|
1466
2690
|
type: z.literal("lease_assignment"),
|
|
1467
2691
|
input: ProviderInputSchema
|
|
1468
2692
|
});
|
|
1469
|
-
const SOURCE_CREDENTIAL_MAX_BYTES = 65536;
|
|
1470
2693
|
const EnvelopeBase = z.object({
|
|
1471
2694
|
v: z.union([
|
|
1472
2695
|
z.literal(1),
|
|
@@ -1474,7 +2697,14 @@ const EnvelopeBase = z.object({
|
|
|
1474
2697
|
z.literal(3),
|
|
1475
2698
|
z.literal(4),
|
|
1476
2699
|
z.literal(5),
|
|
1477
|
-
z.literal(6)
|
|
2700
|
+
z.literal(6),
|
|
2701
|
+
z.literal(7),
|
|
2702
|
+
z.literal(8),
|
|
2703
|
+
z.literal(9),
|
|
2704
|
+
z.literal(10),
|
|
2705
|
+
z.literal(11),
|
|
2706
|
+
z.literal(12),
|
|
2707
|
+
z.literal(13)
|
|
1478
2708
|
]),
|
|
1479
2709
|
workspace_id: z.uuid(),
|
|
1480
2710
|
connection_id: z.uuid(),
|
|
@@ -1496,6 +2726,7 @@ const HeartbeatPayload = z.object({
|
|
|
1496
2726
|
child: z.enum([
|
|
1497
2727
|
"starting",
|
|
1498
2728
|
"setup",
|
|
2729
|
+
"waiting",
|
|
1499
2730
|
"running",
|
|
1500
2731
|
"exited",
|
|
1501
2732
|
"terminating"
|
|
@@ -1510,6 +2741,7 @@ const ProcessStatePayload = z.object({
|
|
|
1510
2741
|
phase: z.enum([
|
|
1511
2742
|
"starting",
|
|
1512
2743
|
"setup",
|
|
2744
|
+
"waiting",
|
|
1513
2745
|
"running",
|
|
1514
2746
|
"exited",
|
|
1515
2747
|
"terminating"
|
|
@@ -1591,6 +2823,38 @@ const RestoreStatusPayload = z.object({
|
|
|
1591
2823
|
});
|
|
1592
2824
|
const ConversationMessagePayload = ConversationMessageInputSchema;
|
|
1593
2825
|
z.discriminatedUnion("type", [
|
|
2826
|
+
EnvelopeBase.extend({
|
|
2827
|
+
type: z.literal("runtime_ready"),
|
|
2828
|
+
payload: z.object({ incarnationId: z.string().min(1) })
|
|
2829
|
+
}),
|
|
2830
|
+
EnvelopeBase.extend({
|
|
2831
|
+
type: z.literal("finite_process"),
|
|
2832
|
+
payload: FiniteProcessFactSchema
|
|
2833
|
+
}),
|
|
2834
|
+
EnvelopeBase.extend({
|
|
2835
|
+
type: z.literal("process_absent"),
|
|
2836
|
+
payload: ProcessTargetPayload
|
|
2837
|
+
}),
|
|
2838
|
+
EnvelopeBase.extend({
|
|
2839
|
+
type: z.literal("process_cancel_result"),
|
|
2840
|
+
payload: ProcessCancelResultPayload
|
|
2841
|
+
}),
|
|
2842
|
+
EnvelopeBase.extend({
|
|
2843
|
+
type: z.literal("preview_socket"),
|
|
2844
|
+
payload: PreviewSocketPayload
|
|
2845
|
+
}),
|
|
2846
|
+
EnvelopeBase.extend({
|
|
2847
|
+
type: z.literal("credential_renew"),
|
|
2848
|
+
payload: CredentialRenewPayload
|
|
2849
|
+
}),
|
|
2850
|
+
EnvelopeBase.extend({
|
|
2851
|
+
type: z.literal("credential_installed"),
|
|
2852
|
+
payload: CredentialInstalledPayload
|
|
2853
|
+
}),
|
|
2854
|
+
EnvelopeBase.extend({
|
|
2855
|
+
type: z.literal("setup_complete"),
|
|
2856
|
+
payload: z.object({ request_id: z.uuid() })
|
|
2857
|
+
}),
|
|
1594
2858
|
EnvelopeBase.extend({
|
|
1595
2859
|
type: z.literal("registered"),
|
|
1596
2860
|
payload: RegisteredPayload
|
|
@@ -1660,77 +2924,44 @@ z.discriminatedUnion("type", [
|
|
|
1660
2924
|
payload: CheckpointStatusPayload
|
|
1661
2925
|
}),
|
|
1662
2926
|
EnvelopeBase.extend({
|
|
1663
|
-
type: z.literal("
|
|
1664
|
-
payload:
|
|
1665
|
-
}),
|
|
1666
|
-
EnvelopeBase.extend({
|
|
1667
|
-
type: z.literal("restore_status"),
|
|
1668
|
-
payload: RestoreStatusPayload
|
|
2927
|
+
type: z.literal("checkpoint_prepared"),
|
|
2928
|
+
payload: CheckpointPreparedPayload
|
|
1669
2929
|
}),
|
|
1670
2930
|
EnvelopeBase.extend({
|
|
1671
|
-
type: z.literal("
|
|
1672
|
-
payload:
|
|
2931
|
+
type: z.literal("checkpoint_installed"),
|
|
2932
|
+
payload: CheckpointInstalledPayload
|
|
1673
2933
|
}),
|
|
1674
2934
|
EnvelopeBase.extend({
|
|
1675
|
-
type: z.literal("
|
|
1676
|
-
payload:
|
|
2935
|
+
type: z.literal("checkpoint_upload_status"),
|
|
2936
|
+
payload: CheckpointUploadStatusPayload
|
|
1677
2937
|
}),
|
|
1678
2938
|
EnvelopeBase.extend({
|
|
1679
|
-
type: z.literal("
|
|
1680
|
-
payload:
|
|
2939
|
+
type: z.literal("output_published"),
|
|
2940
|
+
payload: OutputPublishedPayload
|
|
1681
2941
|
}),
|
|
1682
|
-
EnvelopeBase.extend({
|
|
1683
|
-
type: z.literal("
|
|
1684
|
-
payload:
|
|
1685
|
-
})
|
|
1686
|
-
]);
|
|
1687
|
-
const ExecSpecSchema = z.object({
|
|
1688
|
-
agentapi_native: z.boolean().default(false),
|
|
1689
|
-
setup: z.array(SetupStepSchema),
|
|
1690
|
-
harness: HarnessSchema,
|
|
1691
|
-
env: z.record(z.string(), z.string()),
|
|
1692
|
-
services: z.record(z.string(), ServiceSchema),
|
|
1693
|
-
terminal: z.object({
|
|
1694
|
-
command: z.array(z.string().min(1)).min(1),
|
|
1695
|
-
cwd: z.string().optional(),
|
|
1696
|
-
env: z.record(z.string(), z.string()),
|
|
1697
|
-
max_sessions: z.number().int().min(1).max(8),
|
|
1698
|
-
idle_timeout_seconds: z.number().int().positive(),
|
|
1699
|
-
replay_buffer_bytes: z.number().int().positive()
|
|
1700
|
-
}).nullable().default(null),
|
|
1701
|
-
timeouts: TimeoutsSchema,
|
|
1702
|
-
security: z.object({ writable_memory_paths: z.array(z.string()) }).default({ writable_memory_paths: [] }),
|
|
1703
|
-
network: z.discriminatedUnion("mode", [z.object({ mode: z.literal("unrestricted") }), z.object({
|
|
1704
|
-
mode: z.literal("restricted"),
|
|
1705
|
-
proxy_url: z.url(),
|
|
1706
|
-
health_url: z.url()
|
|
1707
|
-
})]).default({ mode: "unrestricted" }),
|
|
1708
|
-
launch_mode: z.enum(LAUNCH_MODES).default("create"),
|
|
1709
|
-
source: SourceDescriptorSchema.extend({
|
|
1710
|
-
url: z.url(),
|
|
1711
|
-
destination: z.string(),
|
|
1712
|
-
credential: z.string().min(1).max(SOURCE_CREDENTIAL_MAX_BYTES).refine((value) => !value.includes("\0"), "credential must not contain NUL bytes").nullable().default(null)
|
|
1713
|
-
}).nullable().default(null),
|
|
1714
|
-
restore: z.object({
|
|
1715
|
-
checkpoint_id: z.uuid(),
|
|
1716
|
-
origin_workspace_id: z.uuid()
|
|
1717
|
-
}).nullable().default(null),
|
|
1718
|
-
persistence: z.object({
|
|
1719
|
-
mounts: z.array(z.object({
|
|
1720
|
-
name: z.string(),
|
|
1721
|
-
target: z.string()
|
|
1722
|
-
})),
|
|
1723
|
-
conversation_restore: z.enum(CONVERSATION_RESTORE_CAPABILITIES)
|
|
2942
|
+
EnvelopeBase.extend({
|
|
2943
|
+
type: z.literal("restore_status"),
|
|
2944
|
+
payload: RestoreStatusPayload
|
|
1724
2945
|
}),
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
})
|
|
2946
|
+
EnvelopeBase.extend({
|
|
2947
|
+
type: z.literal("conversation_message"),
|
|
2948
|
+
payload: ConversationMessagePayload
|
|
2949
|
+
}),
|
|
2950
|
+
EnvelopeBase.extend({
|
|
2951
|
+
type: z.literal("attachment_ack"),
|
|
2952
|
+
payload: AttachmentAckPayload
|
|
2953
|
+
}),
|
|
2954
|
+
EnvelopeBase.extend({
|
|
2955
|
+
type: z.literal("attachment_result"),
|
|
2956
|
+
payload: AttachmentResultPayload
|
|
2957
|
+
}),
|
|
2958
|
+
EnvelopeBase.extend({
|
|
2959
|
+
type: z.literal("attachment_resolved"),
|
|
2960
|
+
payload: AttachmentResolvedPayload
|
|
2961
|
+
})
|
|
2962
|
+
]);
|
|
1733
2963
|
const RegisteredAckPayload = z.object({
|
|
2964
|
+
credentials: z.array(WorkspaceCredentialSchema).max(64).default([]),
|
|
1734
2965
|
epoch: z.number().int().positive(),
|
|
1735
2966
|
reconnect_credential: z.string().optional(),
|
|
1736
2967
|
limits: z.object({
|
|
@@ -1749,7 +2980,9 @@ const ProxyRequestPayload = z.object({
|
|
|
1749
2980
|
"POST",
|
|
1750
2981
|
"PUT",
|
|
1751
2982
|
"PATCH",
|
|
1752
|
-
"DELETE"
|
|
2983
|
+
"DELETE",
|
|
2984
|
+
"HEAD",
|
|
2985
|
+
"OPTIONS"
|
|
1753
2986
|
]),
|
|
1754
2987
|
path: z.string(),
|
|
1755
2988
|
query: z.record(z.string(), z.string()).default({}),
|
|
@@ -1765,6 +2998,42 @@ const PrepareCheckpointPayload = z.object({
|
|
|
1765
2998
|
deadline_ms: z.number().int().positive()
|
|
1766
2999
|
});
|
|
1767
3000
|
z.discriminatedUnion("type", [
|
|
3001
|
+
EnvelopeBase.extend({
|
|
3002
|
+
type: z.literal("process_start"),
|
|
3003
|
+
payload: ProcessStartPayload
|
|
3004
|
+
}),
|
|
3005
|
+
EnvelopeBase.extend({
|
|
3006
|
+
type: z.literal("process_lookup"),
|
|
3007
|
+
payload: ProcessTargetPayload
|
|
3008
|
+
}),
|
|
3009
|
+
EnvelopeBase.extend({
|
|
3010
|
+
type: z.literal("process_cancel"),
|
|
3011
|
+
payload: ProcessCancelPayload
|
|
3012
|
+
}),
|
|
3013
|
+
EnvelopeBase.extend({
|
|
3014
|
+
type: z.literal("process_ack"),
|
|
3015
|
+
payload: ProcessTargetPayload
|
|
3016
|
+
}),
|
|
3017
|
+
EnvelopeBase.extend({
|
|
3018
|
+
type: z.literal("screenshot_capture"),
|
|
3019
|
+
payload: ScreenshotCapturePayload
|
|
3020
|
+
}),
|
|
3021
|
+
EnvelopeBase.extend({
|
|
3022
|
+
type: z.literal("preview_socket"),
|
|
3023
|
+
payload: PreviewSocketPayload
|
|
3024
|
+
}),
|
|
3025
|
+
EnvelopeBase.extend({
|
|
3026
|
+
type: z.literal("credential_renewed"),
|
|
3027
|
+
payload: CredentialRenewedPayload
|
|
3028
|
+
}),
|
|
3029
|
+
EnvelopeBase.extend({
|
|
3030
|
+
type: z.literal("credential_installed_ack"),
|
|
3031
|
+
payload: CredentialInstalledPayload
|
|
3032
|
+
}),
|
|
3033
|
+
EnvelopeBase.extend({
|
|
3034
|
+
type: z.literal("setup_complete_ack"),
|
|
3035
|
+
payload: z.object({ request_id: z.uuid() })
|
|
3036
|
+
}),
|
|
1768
3037
|
EnvelopeBase.extend({
|
|
1769
3038
|
type: z.literal("registered_ack"),
|
|
1770
3039
|
payload: RegisteredAckPayload
|
|
@@ -1813,6 +3082,14 @@ z.discriminatedUnion("type", [
|
|
|
1813
3082
|
type: z.literal("prepare_checkpoint"),
|
|
1814
3083
|
payload: PrepareCheckpointPayload
|
|
1815
3084
|
}),
|
|
3085
|
+
EnvelopeBase.extend({
|
|
3086
|
+
type: z.literal("prepare_checkpoint_archive"),
|
|
3087
|
+
payload: PrepareCheckpointArchivePayload
|
|
3088
|
+
}),
|
|
3089
|
+
EnvelopeBase.extend({
|
|
3090
|
+
type: z.literal("checkpoint_upload"),
|
|
3091
|
+
payload: CheckpointUploadPayload
|
|
3092
|
+
}),
|
|
1816
3093
|
EnvelopeBase.extend({
|
|
1817
3094
|
type: z.literal("attachment_start"),
|
|
1818
3095
|
payload: AttachmentStartPayload
|
|
@@ -1835,6 +3112,107 @@ z.discriminatedUnion("type", [
|
|
|
1835
3112
|
})
|
|
1836
3113
|
]);
|
|
1837
3114
|
//#endregion
|
|
3115
|
+
//#region ../contracts/src/recovery/account-purge.ts
|
|
3116
|
+
const Digest = z.string().regex(/^[a-f0-9]{64}$/);
|
|
3117
|
+
const AccountPurgeIntentSchema = z.strictObject({
|
|
3118
|
+
format: z.literal("pocketcoder-account-purge/v1"),
|
|
3119
|
+
accountId: z.uuid(),
|
|
3120
|
+
operationId: z.uuid(),
|
|
3121
|
+
createRequestDigest: Digest,
|
|
3122
|
+
purgeRequestDigest: Digest,
|
|
3123
|
+
admittedAt: z.iso.datetime()
|
|
3124
|
+
});
|
|
3125
|
+
z.strictObject({
|
|
3126
|
+
format: z.literal("pocketcoder-account-purge-completion/v1"),
|
|
3127
|
+
intent: AccountPurgeIntentSchema,
|
|
3128
|
+
completedAt: z.iso.datetime()
|
|
3129
|
+
});
|
|
3130
|
+
//#endregion
|
|
3131
|
+
//#region ../contracts/src/recovery/source-writer.ts
|
|
3132
|
+
const fileIdentity = z.strictObject({
|
|
3133
|
+
device: z.string().regex(/^\d+$/).max(20),
|
|
3134
|
+
inode: z.string().regex(/^[1-9]\d*$/).max(20)
|
|
3135
|
+
});
|
|
3136
|
+
z.strictObject({
|
|
3137
|
+
format: z.literal("pocketcoder-source-writer/v1"),
|
|
3138
|
+
directory: z.string().startsWith("/").max(4096),
|
|
3139
|
+
root: fileIdentity,
|
|
3140
|
+
lock: fileIdentity
|
|
3141
|
+
});
|
|
3142
|
+
z.strictObject({ manifest: z.union([TemplateManifestSchema, WorkloadTemplateSchema]) });
|
|
3143
|
+
z.object({
|
|
3144
|
+
name: z.string().regex(/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/),
|
|
3145
|
+
version: z.string().min(1).max(256)
|
|
3146
|
+
});
|
|
3147
|
+
z.strictObject({
|
|
3148
|
+
workspaceId: z.uuid(),
|
|
3149
|
+
incarnationId: z.string().min(1).max(256),
|
|
3150
|
+
profiles: z.array(WorkloadProfileSchema),
|
|
3151
|
+
storageProfiles: z.array(z.enum(["controller-readonly/v1", "shared-writer-readonly/v1"])),
|
|
3152
|
+
ceilings: WorkloadLimitsSchema,
|
|
3153
|
+
now: z.iso.datetime(),
|
|
3154
|
+
expiresAt: z.iso.datetime(),
|
|
3155
|
+
installed: InstalledWorkloadSchema.optional()
|
|
3156
|
+
});
|
|
3157
|
+
//#endregion
|
|
3158
|
+
//#region ../contracts/src/workspaces/execution-dispatch.ts
|
|
3159
|
+
const ExecutionPhaseSchema = z.enum([
|
|
3160
|
+
"accepted",
|
|
3161
|
+
"dispatching",
|
|
3162
|
+
"running",
|
|
3163
|
+
"terminal",
|
|
3164
|
+
"canceled",
|
|
3165
|
+
"unknown",
|
|
3166
|
+
"lost"
|
|
3167
|
+
]);
|
|
3168
|
+
z.strictObject({
|
|
3169
|
+
acceptance: WorkloadAcceptanceSchema,
|
|
3170
|
+
conversationIdentity: ConversationRequestIdentitySchema.nullable(),
|
|
3171
|
+
outcome: JsonValueSchema.nullable(),
|
|
3172
|
+
acceptedAt: z.iso.datetime(),
|
|
3173
|
+
dispatchClaimedAt: z.iso.datetime().nullable(),
|
|
3174
|
+
phase: ExecutionPhaseSchema,
|
|
3175
|
+
cancel: z.strictObject({
|
|
3176
|
+
cancelId: z.uuid(),
|
|
3177
|
+
requestedAt: z.iso.datetime()
|
|
3178
|
+
}).nullable(),
|
|
3179
|
+
settledAt: z.iso.datetime().nullable(),
|
|
3180
|
+
acknowledgedAt: z.iso.datetime().nullable(),
|
|
3181
|
+
finalizedAt: z.iso.datetime().nullable()
|
|
3182
|
+
});
|
|
3183
|
+
z.strictObject({
|
|
3184
|
+
acceptance: WorkloadAcceptanceSchema,
|
|
3185
|
+
conversationIdentity: ConversationRequestIdentitySchema.optional()
|
|
3186
|
+
}).refine(({ acceptance, conversationIdentity }) => acceptance.execution.owner !== "conversation-request" || conversationIdentity !== void 0, {
|
|
3187
|
+
path: ["conversationIdentity"],
|
|
3188
|
+
message: "Conversation execution requires a conversation identity."
|
|
3189
|
+
});
|
|
3190
|
+
WorkloadCancelSchema.refine((cancel) => cancel.scope === "operation", { message: "Use workspace release for workspace cancellation" });
|
|
3191
|
+
z.strictObject({
|
|
3192
|
+
workspaceId: z.uuid(),
|
|
3193
|
+
callerPrincipalId: z.uuid(),
|
|
3194
|
+
incarnationId: z.string().min(1),
|
|
3195
|
+
template: WorkloadTemplateSnapshotSchema,
|
|
3196
|
+
producer: InstalledWorkloadSchema,
|
|
3197
|
+
readyAt: z.iso.datetime().nullable(),
|
|
3198
|
+
endedAt: z.iso.datetime().nullable()
|
|
3199
|
+
});
|
|
3200
|
+
z.discriminatedUnion("phase", [z.strictObject({
|
|
3201
|
+
execution: WorkloadExecutionReferenceSchema,
|
|
3202
|
+
phase: z.literal("absent")
|
|
3203
|
+
}), z.strictObject({
|
|
3204
|
+
execution: WorkloadExecutionReferenceSchema,
|
|
3205
|
+
fingerprint: WorkloadDigestSchema,
|
|
3206
|
+
phase: z.enum([
|
|
3207
|
+
"accepted",
|
|
3208
|
+
"running",
|
|
3209
|
+
"terminal",
|
|
3210
|
+
"canceled"
|
|
3211
|
+
]),
|
|
3212
|
+
outcome: JsonValueSchema.nullable()
|
|
3213
|
+
})]);
|
|
3214
|
+
WorkloadCancelSchema.extend({ fingerprint: WorkloadDigestSchema });
|
|
3215
|
+
//#endregion
|
|
1838
3216
|
//#region src/resources/admin/admin.ts
|
|
1839
3217
|
var AdministrationApi = class {
|
|
1840
3218
|
transport;
|
|
@@ -1912,44 +3290,8 @@ function responseError(response, body) {
|
|
|
1912
3290
|
});
|
|
1913
3291
|
}
|
|
1914
3292
|
//#endregion
|
|
1915
|
-
//#region src/resources/
|
|
3293
|
+
//#region src/resources/agent/agent.ts
|
|
1916
3294
|
const DEFAULT_READY_TIMEOUT_MS = 12e4;
|
|
1917
|
-
function contentDisposition(name) {
|
|
1918
|
-
const clean = name.replace(/[\r\n]/g, "");
|
|
1919
|
-
if (/^[ -~]*$/.test(clean)) return `attachment; filename="${clean.replace(/(["\\])/g, "\\$1")}"`;
|
|
1920
|
-
return `attachment; filename*=UTF-8''${encodeURIComponent(clean)}`;
|
|
1921
|
-
}
|
|
1922
|
-
function progressStream(body, total, onProgress) {
|
|
1923
|
-
const source = new Response(body).body ?? new ReadableStream({ start: (c) => c.close() });
|
|
1924
|
-
let uploaded = 0;
|
|
1925
|
-
return source.pipeThrough(new TransformStream({ transform(chunk, controller) {
|
|
1926
|
-
uploaded += chunk.byteLength;
|
|
1927
|
-
controller.enqueue(chunk);
|
|
1928
|
-
onProgress(Math.min(uploaded, total), total);
|
|
1929
|
-
} }));
|
|
1930
|
-
}
|
|
1931
|
-
var AttachmentsApi = class {
|
|
1932
|
-
transport;
|
|
1933
|
-
constructor(transport) {
|
|
1934
|
-
this.transport = transport;
|
|
1935
|
-
}
|
|
1936
|
-
async upload(workspaceId, input) {
|
|
1937
|
-
const id = (input.id ?? crypto.randomUUID()).toLowerCase();
|
|
1938
|
-
const body = input.onProgress ? progressStream(input.body, input.sizeBytes, input.onProgress) : input.body;
|
|
1939
|
-
const init = {
|
|
1940
|
-
method: "PUT",
|
|
1941
|
-
...input.signal ? { signal: input.signal } : {},
|
|
1942
|
-
headers: {
|
|
1943
|
-
"content-type": input.mediaType ?? "application/octet-stream",
|
|
1944
|
-
"content-disposition": contentDisposition(input.name),
|
|
1945
|
-
"content-length": String(input.sizeBytes)
|
|
1946
|
-
},
|
|
1947
|
-
body,
|
|
1948
|
-
...body instanceof ReadableStream ? { duplex: "half" } : {}
|
|
1949
|
-
};
|
|
1950
|
-
return await this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/attachments/${id}`, AttachmentDescriptorSchema, init);
|
|
1951
|
-
}
|
|
1952
|
-
};
|
|
1953
3295
|
var AgentApi = class {
|
|
1954
3296
|
transport;
|
|
1955
3297
|
workspaces;
|
|
@@ -1983,6 +3325,58 @@ var AgentApi = class {
|
|
|
1983
3325
|
}
|
|
1984
3326
|
throw responseError(response, body);
|
|
1985
3327
|
}
|
|
3328
|
+
interrupt(workspaceId, options = {}) {
|
|
3329
|
+
return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/agent/interrupt`, AgentInterruptResponseSchema, {
|
|
3330
|
+
method: "POST",
|
|
3331
|
+
signal: options.signal
|
|
3332
|
+
});
|
|
3333
|
+
}
|
|
3334
|
+
};
|
|
3335
|
+
//#endregion
|
|
3336
|
+
//#region src/resources/attachments/attachments.ts
|
|
3337
|
+
function contentDisposition(name) {
|
|
3338
|
+
const clean = name.replace(/[\r\n]/g, "");
|
|
3339
|
+
if (/^[ -~]*$/.test(clean)) return `attachment; filename="${clean.replace(/(["\\])/g, "\\$1")}"`;
|
|
3340
|
+
return `attachment; filename*=UTF-8''${encodeURIComponent(clean)}`;
|
|
3341
|
+
}
|
|
3342
|
+
function progressStream(body, total, onProgress) {
|
|
3343
|
+
const source = new Response(body).body ?? new ReadableStream({ start: (c) => c.close() });
|
|
3344
|
+
let uploaded = 0;
|
|
3345
|
+
return source.pipeThrough(new TransformStream({ transform(chunk, controller) {
|
|
3346
|
+
uploaded += chunk.byteLength;
|
|
3347
|
+
controller.enqueue(chunk);
|
|
3348
|
+
onProgress(Math.min(uploaded, total), total);
|
|
3349
|
+
} }));
|
|
3350
|
+
}
|
|
3351
|
+
var AttachmentsApi = class {
|
|
3352
|
+
transport;
|
|
3353
|
+
constructor(transport) {
|
|
3354
|
+
this.transport = transport;
|
|
3355
|
+
}
|
|
3356
|
+
async upload(workspaceId, input) {
|
|
3357
|
+
const id = (input.id ?? crypto.randomUUID()).toLowerCase();
|
|
3358
|
+
let sha256 = input.sha256;
|
|
3359
|
+
if (!sha256) {
|
|
3360
|
+
if (input.body instanceof ReadableStream) throw new Error("Stream uploads require sha256 before body consumption.");
|
|
3361
|
+
const digest = await crypto.subtle.digest("SHA-256", await new Response(input.body).arrayBuffer());
|
|
3362
|
+
sha256 = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
3363
|
+
}
|
|
3364
|
+
if (!/^[0-9a-f]{64}$/.test(sha256)) throw new Error("sha256 must be 64 lowercase hexadecimal characters.");
|
|
3365
|
+
const body = input.onProgress ? progressStream(input.body, input.sizeBytes, input.onProgress) : input.body;
|
|
3366
|
+
const init = {
|
|
3367
|
+
method: "PUT",
|
|
3368
|
+
...input.signal ? { signal: input.signal } : {},
|
|
3369
|
+
headers: {
|
|
3370
|
+
"content-type": input.mediaType ?? "application/octet-stream",
|
|
3371
|
+
"content-disposition": contentDisposition(input.name),
|
|
3372
|
+
"content-length": String(input.sizeBytes),
|
|
3373
|
+
"x-content-sha256": sha256
|
|
3374
|
+
},
|
|
3375
|
+
body,
|
|
3376
|
+
...body instanceof ReadableStream ? { duplex: "half" } : {}
|
|
3377
|
+
};
|
|
3378
|
+
return await this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/attachments/${id}`, InputObjectReferenceSchema, init);
|
|
3379
|
+
}
|
|
1986
3380
|
};
|
|
1987
3381
|
//#endregion
|
|
1988
3382
|
//#region src/transport/common.ts
|
|
@@ -2047,16 +3441,72 @@ var OperationsApi = class {
|
|
|
2047
3441
|
};
|
|
2048
3442
|
//#endregion
|
|
2049
3443
|
//#region src/resources/conversations/conversations.ts
|
|
3444
|
+
function workspacePath(id) {
|
|
3445
|
+
return `/v1/workspaces/${encodeURIComponent(id)}`;
|
|
3446
|
+
}
|
|
3447
|
+
async function* liveEvents(body) {
|
|
3448
|
+
const decoder = new TextDecoder();
|
|
3449
|
+
let buffer = "";
|
|
3450
|
+
for await (const chunk of body) {
|
|
3451
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
3452
|
+
let end = buffer.indexOf("\n\n");
|
|
3453
|
+
while (end !== -1) {
|
|
3454
|
+
const block = buffer.slice(0, end);
|
|
3455
|
+
buffer = buffer.slice(end + 2);
|
|
3456
|
+
const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
3457
|
+
const event = data ? ConversationLiveEventSchema.safeParse(JSON.parse(data)) : null;
|
|
3458
|
+
if (event?.success) yield event.data;
|
|
3459
|
+
end = buffer.indexOf("\n\n");
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
2050
3463
|
var ConversationsApi = class {
|
|
2051
3464
|
transport;
|
|
2052
3465
|
constructor(transport) {
|
|
2053
3466
|
this.transport = transport;
|
|
2054
3467
|
}
|
|
2055
3468
|
async list(id, query = {}, options = {}) {
|
|
2056
|
-
|
|
3469
|
+
const body = await this.transport.request(`${workspacePath(id)}/conversation?${queryString({
|
|
3470
|
+
cursor: query.cursor,
|
|
3471
|
+
limit: query.limit ?? 100
|
|
3472
|
+
})}`, ConversationPageSchema, options);
|
|
3473
|
+
return {
|
|
3474
|
+
...page(body),
|
|
3475
|
+
protocol: body.protocol,
|
|
3476
|
+
retention: body.retention
|
|
3477
|
+
};
|
|
3478
|
+
}
|
|
3479
|
+
async acceptRequest(id, identity, options = {}) {
|
|
3480
|
+
return this.transport.request(`${workspacePath(id)}/conversation/requests`, ConversationRequestReceiptSchema, {
|
|
3481
|
+
...options,
|
|
3482
|
+
method: "POST",
|
|
3483
|
+
body: JSON.stringify(identity)
|
|
3484
|
+
});
|
|
3485
|
+
}
|
|
3486
|
+
async listConversations(id, options = {}) {
|
|
3487
|
+
return (await this.transport.request(`${workspacePath(id)}/conversations`, ConversationListSchema, options)).items;
|
|
3488
|
+
}
|
|
3489
|
+
async read(id, conversationId, query = {}, options = {}) {
|
|
3490
|
+
const body = await this.transport.request(`${workspacePath(id)}/conversations/${encodeURIComponent(conversationId)}?${queryString({
|
|
2057
3491
|
cursor: query.cursor,
|
|
2058
3492
|
limit: query.limit ?? 100
|
|
2059
|
-
})}`,
|
|
3493
|
+
})}`, ConversationDocumentPageSchema, options);
|
|
3494
|
+
return {
|
|
3495
|
+
...page(body),
|
|
3496
|
+
conversation: body.conversation,
|
|
3497
|
+
protocol: body.protocol
|
|
3498
|
+
};
|
|
3499
|
+
}
|
|
3500
|
+
async delete(id, conversationId, options = {}) {
|
|
3501
|
+
await this.transport.request(`${workspacePath(id)}/conversations/${encodeURIComponent(conversationId)}`, z.undefined(), {
|
|
3502
|
+
...options,
|
|
3503
|
+
method: "DELETE"
|
|
3504
|
+
});
|
|
3505
|
+
}
|
|
3506
|
+
async *live(id, options = {}) {
|
|
3507
|
+
const path = options.conversationId ? `${workspacePath(id)}/conversations/${encodeURIComponent(options.conversationId)}/live` : `${workspacePath(id)}/conversation/live`;
|
|
3508
|
+
const response = await this.transport.stream(path, options.signal);
|
|
3509
|
+
if (response.body) yield* liveEvents(response.body);
|
|
2060
3510
|
}
|
|
2061
3511
|
};
|
|
2062
3512
|
//#endregion
|
|
@@ -2088,8 +3538,40 @@ var NetworkEventsApi = class extends WorkspaceCursorApi {
|
|
|
2088
3538
|
}
|
|
2089
3539
|
};
|
|
2090
3540
|
var OutputsApi = class extends WorkspaceCursorApi {
|
|
3541
|
+
binaryTransport;
|
|
3542
|
+
async download(workspaceId, outputId, options = {}) {
|
|
3543
|
+
const response = await this.binaryTransport.raw(`/v1/workspaces/${encodeURIComponent(workspaceId)}/outputs/${encodeURIComponent(outputId)}/content`, options);
|
|
3544
|
+
if (!response.ok) throw responseError(response, await response.json());
|
|
3545
|
+
return response;
|
|
3546
|
+
}
|
|
2091
3547
|
constructor(transport) {
|
|
2092
3548
|
super(transport, "outputs", OutputPageSchema);
|
|
3549
|
+
this.binaryTransport = transport;
|
|
3550
|
+
}
|
|
3551
|
+
};
|
|
3552
|
+
//#endregion
|
|
3553
|
+
//#region src/resources/displays/displays.ts
|
|
3554
|
+
var DisplaysApi = class {
|
|
3555
|
+
transport;
|
|
3556
|
+
constructor(transport) {
|
|
3557
|
+
this.transport = transport;
|
|
3558
|
+
}
|
|
3559
|
+
capture(workspaceId, options = {}) {
|
|
3560
|
+
return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/display/screenshot`, ScreenshotResourceSchema, {
|
|
3561
|
+
method: "POST",
|
|
3562
|
+
body: "{}",
|
|
3563
|
+
signal: options.signal
|
|
3564
|
+
});
|
|
3565
|
+
}
|
|
3566
|
+
open(workspaceId, control = false, options = {}) {
|
|
3567
|
+
return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/display`, PreviewOpenResponseSchema, {
|
|
3568
|
+
method: "POST",
|
|
3569
|
+
body: JSON.stringify({
|
|
3570
|
+
control,
|
|
3571
|
+
session: options.session ?? { mode: "local" }
|
|
3572
|
+
}),
|
|
3573
|
+
signal: options.signal
|
|
3574
|
+
});
|
|
2093
3575
|
}
|
|
2094
3576
|
};
|
|
2095
3577
|
//#endregion
|
|
@@ -2117,6 +3599,9 @@ var KeysApi = class {
|
|
|
2117
3599
|
cursor = page.next_cursor ?? void 0;
|
|
2118
3600
|
} while (cursor);
|
|
2119
3601
|
}
|
|
3602
|
+
expiring(query = {}, options = {}) {
|
|
3603
|
+
return this.transport.request(`/v1/keys/expiring?${queryString({ within_hours: query.withinHours })}`, ExpiringKeyListResponseSchema, options);
|
|
3604
|
+
}
|
|
2120
3605
|
issue(principalId, input, options = {}) {
|
|
2121
3606
|
return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys`, KeyIssueResponseSchema, {
|
|
2122
3607
|
method: "POST",
|
|
@@ -2124,6 +3609,13 @@ var KeysApi = class {
|
|
|
2124
3609
|
signal: options.signal
|
|
2125
3610
|
});
|
|
2126
3611
|
}
|
|
3612
|
+
rotate(principalId, keyId, input, options = {}) {
|
|
3613
|
+
return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys/${encodeURIComponent(keyId)}/rotate`, KeyIssueResponseSchema, {
|
|
3614
|
+
method: "POST",
|
|
3615
|
+
body: JSON.stringify(input),
|
|
3616
|
+
signal: options.signal
|
|
3617
|
+
});
|
|
3618
|
+
}
|
|
2127
3619
|
revoke(principalId, keyId, options = {}) {
|
|
2128
3620
|
return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys/${encodeURIComponent(keyId)}`, z.object({ revoked: z.literal(true) }), {
|
|
2129
3621
|
method: "DELETE",
|
|
@@ -2155,6 +3647,76 @@ var RecoveryApi = class {
|
|
|
2155
3647
|
}
|
|
2156
3648
|
};
|
|
2157
3649
|
//#endregion
|
|
3650
|
+
//#region src/resources/previews/previews.ts
|
|
3651
|
+
var PreviewsApi = class {
|
|
3652
|
+
transport;
|
|
3653
|
+
constructor(transport) {
|
|
3654
|
+
this.transport = transport;
|
|
3655
|
+
}
|
|
3656
|
+
list(workspaceId, options = {}) {
|
|
3657
|
+
return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/previews`, PreviewListSchema, options);
|
|
3658
|
+
}
|
|
3659
|
+
open(workspaceId, name, options = {}) {
|
|
3660
|
+
return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/previews/${encodeURIComponent(name)}`, PreviewOpenResponseSchema, {
|
|
3661
|
+
method: "POST",
|
|
3662
|
+
body: JSON.stringify({ session: options.session ?? { mode: "local" } }),
|
|
3663
|
+
signal: options.signal
|
|
3664
|
+
});
|
|
3665
|
+
}
|
|
3666
|
+
};
|
|
3667
|
+
//#endregion
|
|
3668
|
+
//#region src/resources/principals/principals.ts
|
|
3669
|
+
var PrincipalsApi = class {
|
|
3670
|
+
transport;
|
|
3671
|
+
constructor(transport) {
|
|
3672
|
+
this.transport = transport;
|
|
3673
|
+
}
|
|
3674
|
+
list(options = {}) {
|
|
3675
|
+
return this.transport.request("/v1/principals", PrincipalListResponseSchema, options);
|
|
3676
|
+
}
|
|
3677
|
+
get(principalId, options = {}) {
|
|
3678
|
+
return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}`, PrincipalResourceSchema, options);
|
|
3679
|
+
}
|
|
3680
|
+
create(input, options = {}) {
|
|
3681
|
+
return this.transport.request("/v1/principals", PrincipalResourceSchema, {
|
|
3682
|
+
method: "POST",
|
|
3683
|
+
body: JSON.stringify(input),
|
|
3684
|
+
signal: options.signal
|
|
3685
|
+
});
|
|
3686
|
+
}
|
|
3687
|
+
update(principalId, input, options = {}) {
|
|
3688
|
+
return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}`, PrincipalResourceSchema, {
|
|
3689
|
+
method: "PATCH",
|
|
3690
|
+
body: JSON.stringify(input),
|
|
3691
|
+
signal: options.signal
|
|
3692
|
+
});
|
|
3693
|
+
}
|
|
3694
|
+
};
|
|
3695
|
+
//#endregion
|
|
3696
|
+
//#region src/resources/secrets/secrets.ts
|
|
3697
|
+
var SecretsApi = class {
|
|
3698
|
+
transport;
|
|
3699
|
+
constructor(transport) {
|
|
3700
|
+
this.transport = transport;
|
|
3701
|
+
}
|
|
3702
|
+
async put(name, input, options = {}) {
|
|
3703
|
+
return this.transport.request(`/v1/secrets/${encodeURIComponent(name)}`, SecretResourceSchema, {
|
|
3704
|
+
...options,
|
|
3705
|
+
method: "PUT",
|
|
3706
|
+
body: JSON.stringify(input)
|
|
3707
|
+
});
|
|
3708
|
+
}
|
|
3709
|
+
async list(options = {}) {
|
|
3710
|
+
return (await this.transport.request("/v1/secrets", SecretListSchema, options)).items;
|
|
3711
|
+
}
|
|
3712
|
+
async retire(name, options = {}) {
|
|
3713
|
+
return this.transport.request(`/v1/secrets/${encodeURIComponent(name)}`, SecretResourceSchema, {
|
|
3714
|
+
...options,
|
|
3715
|
+
method: "DELETE"
|
|
3716
|
+
});
|
|
3717
|
+
}
|
|
3718
|
+
};
|
|
3719
|
+
//#endregion
|
|
2158
3720
|
//#region src/resources/templates/templates.ts
|
|
2159
3721
|
var TemplatesApi = class {
|
|
2160
3722
|
transport;
|
|
@@ -2168,7 +3730,27 @@ var TemplatesApi = class {
|
|
|
2168
3730
|
})}`, TemplatePageSchema, options));
|
|
2169
3731
|
}
|
|
2170
3732
|
async list(options = {}) {
|
|
2171
|
-
|
|
3733
|
+
const items = [];
|
|
3734
|
+
let cursor;
|
|
3735
|
+
do {
|
|
3736
|
+
const result = await this.page({ cursor }, options);
|
|
3737
|
+
items.push(...result.items);
|
|
3738
|
+
cursor = result.nextCursor ?? void 0;
|
|
3739
|
+
} while (cursor);
|
|
3740
|
+
return items;
|
|
3741
|
+
}
|
|
3742
|
+
async publish(manifest, options = {}) {
|
|
3743
|
+
return this.transport.request("/v1/templates", TemplateListItemSchema, {
|
|
3744
|
+
...options,
|
|
3745
|
+
method: "POST",
|
|
3746
|
+
body: JSON.stringify({ manifest })
|
|
3747
|
+
});
|
|
3748
|
+
}
|
|
3749
|
+
async retire(name, version, options = {}) {
|
|
3750
|
+
return this.transport.request(`/v1/templates/${encodeURIComponent(name)}/${encodeURIComponent(version)}`, TemplateListItemSchema, {
|
|
3751
|
+
...options,
|
|
3752
|
+
method: "DELETE"
|
|
3753
|
+
});
|
|
2172
3754
|
}
|
|
2173
3755
|
};
|
|
2174
3756
|
//#endregion
|
|
@@ -2432,6 +4014,20 @@ var PocketCoderTransport = class {
|
|
|
2432
4014
|
await delay(100 * 2 ** attempt, signal);
|
|
2433
4015
|
}
|
|
2434
4016
|
}
|
|
4017
|
+
async stream(path, signal) {
|
|
4018
|
+
const response = await this.fetchImpl(`${this.baseUrl}${path.startsWith("/") ? path : `/${path}`}`, {
|
|
4019
|
+
headers: {
|
|
4020
|
+
authorization: `Bearer ${this.apiKey}`,
|
|
4021
|
+
accept: "text/event-stream"
|
|
4022
|
+
},
|
|
4023
|
+
...signal ? { signal } : {}
|
|
4024
|
+
});
|
|
4025
|
+
if (!response.ok) {
|
|
4026
|
+
const text = await response.text();
|
|
4027
|
+
throw responseError(response, text ? JSON.parse(text) : void 0);
|
|
4028
|
+
}
|
|
4029
|
+
return response;
|
|
4030
|
+
}
|
|
2435
4031
|
async request(path, schema, init = {}) {
|
|
2436
4032
|
const response = await this.raw(path, init);
|
|
2437
4033
|
const text = await response.text();
|
|
@@ -2496,8 +4092,11 @@ async function delay(milliseconds, signal) {
|
|
|
2496
4092
|
//#region src/client.ts
|
|
2497
4093
|
var PocketCoderClient = class {
|
|
2498
4094
|
transport;
|
|
4095
|
+
secrets;
|
|
2499
4096
|
templates;
|
|
2500
4097
|
workspaces;
|
|
4098
|
+
previews;
|
|
4099
|
+
displays;
|
|
2501
4100
|
attachments;
|
|
2502
4101
|
agent;
|
|
2503
4102
|
conversations;
|
|
@@ -2508,12 +4107,16 @@ var PocketCoderClient = class {
|
|
|
2508
4107
|
outputs;
|
|
2509
4108
|
administration;
|
|
2510
4109
|
terminals;
|
|
4110
|
+
principals;
|
|
2511
4111
|
keys;
|
|
2512
4112
|
recovery;
|
|
2513
4113
|
constructor(config, fetchImpl = fetch) {
|
|
2514
4114
|
this.transport = new PocketCoderTransport(config, fetchImpl);
|
|
4115
|
+
this.secrets = new SecretsApi(this.transport);
|
|
2515
4116
|
this.templates = new TemplatesApi(this.transport);
|
|
2516
4117
|
this.workspaces = new WorkspacesApi(this.transport);
|
|
4118
|
+
this.previews = new PreviewsApi(this.transport);
|
|
4119
|
+
this.displays = new DisplaysApi(this.transport);
|
|
2517
4120
|
this.attachments = new AttachmentsApi(this.transport);
|
|
2518
4121
|
this.agent = new AgentApi(this.transport, this.workspaces);
|
|
2519
4122
|
this.conversations = new ConversationsApi(this.transport);
|
|
@@ -2524,6 +4127,7 @@ var PocketCoderClient = class {
|
|
|
2524
4127
|
this.outputs = new OutputsApi(this.transport);
|
|
2525
4128
|
this.administration = new AdministrationApi(this.transport);
|
|
2526
4129
|
this.terminals = new TerminalsApi(this.transport);
|
|
4130
|
+
this.principals = new PrincipalsApi(this.transport);
|
|
2527
4131
|
this.keys = new KeysApi(this.transport);
|
|
2528
4132
|
this.recovery = new RecoveryApi(this.transport);
|
|
2529
4133
|
}
|
|
@@ -2676,4 +4280,4 @@ var WorkspaceTurnResolver = class {
|
|
|
2676
4280
|
}
|
|
2677
4281
|
};
|
|
2678
4282
|
//#endregion
|
|
2679
|
-
export { AdministrationApi, AgentApi, AgentNotReadyError, AttachmentsApi, CheckpointsApi, ConversationGoneError, ConversationsApi, KeysApi, LogsApi, NetworkEventsApi, OperationsApi, OutputsApi, PocketCoderClient, PocketCoderError, RecoveryApi, TERMINAL_WORKSPACE_STATES, TemplatesApi, TerminalConnection, TerminalsApi, WorkspaceTerminalError, WorkspaceTurnResolutionError, WorkspaceTurnResolver, WorkspacesApi, isPocketCoderErrorCode, splitAttachmentManifest };
|
|
4283
|
+
export { AdministrationApi, AgentApi, AgentNotReadyError, AttachmentsApi, CheckpointsApi, ConversationGoneError, ConversationsApi, KeysApi, LogsApi, NetworkEventsApi, OperationsApi, OutputsApi, PocketCoderClient, PocketCoderError, PrincipalsApi, RecoveryApi, SecretsApi, TERMINAL_WORKSPACE_STATES, TemplatesApi, TerminalConnection, TerminalsApi, WorkspaceTerminalError, WorkspaceTurnResolutionError, WorkspaceTurnResolver, WorkspacesApi, isPocketCoderErrorCode, splitAttachmentManifest };
|