@pstdio/pocketcoder-sdk 0.6.1 → 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 +2148 -93
- package/dist/index.js +1828 -311
- package/package.json +1 -1
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",
|
|
@@ -112,6 +260,9 @@ const SCOPES = [
|
|
|
112
260
|
"conversations:write",
|
|
113
261
|
"conversations:delete",
|
|
114
262
|
"services:relay",
|
|
263
|
+
"previews:open",
|
|
264
|
+
"display:view",
|
|
265
|
+
"display:control",
|
|
115
266
|
"attachments:write",
|
|
116
267
|
"logs:read",
|
|
117
268
|
"network:read",
|
|
@@ -122,13 +273,20 @@ const SCOPES = [
|
|
|
122
273
|
z.strictObject({
|
|
123
274
|
request_id: z.string().min(1).max(128).regex(/^[A-Za-z0-9._:-]+$/),
|
|
124
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(),
|
|
125
278
|
expires_at: z.iso.datetime()
|
|
126
|
-
})
|
|
279
|
+
}).pick({
|
|
280
|
+
request_id: true,
|
|
281
|
+
expires_at: true
|
|
282
|
+
}).partial({ expires_at: true });
|
|
127
283
|
const KeyResourceSchema = z.object({
|
|
128
284
|
id: z.uuid(),
|
|
129
285
|
principal_id: z.uuid(),
|
|
130
286
|
scopes: z.array(z.string()),
|
|
131
287
|
effective_scopes: z.array(z.string()),
|
|
288
|
+
templates: z.array(z.string()).nullable(),
|
|
289
|
+
effective_templates: z.array(z.string()),
|
|
132
290
|
managed_principal_ids: z.array(z.uuid()),
|
|
133
291
|
issuance_request_id: z.string().nullable(),
|
|
134
292
|
created_at: z.iso.datetime(),
|
|
@@ -144,12 +302,265 @@ const KeyListResponseSchema = z.object({
|
|
|
144
302
|
items: z.array(KeyResourceSchema),
|
|
145
303
|
next_cursor: z.uuid().nullable()
|
|
146
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." });
|
|
147
324
|
//#endregion
|
|
148
325
|
//#region ../contracts/src/common/duration.ts
|
|
149
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
|
+
};
|
|
150
333
|
function isDuration(value) {
|
|
151
334
|
return DURATION_RE.test(value);
|
|
152
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
|
+
});
|
|
153
564
|
//#endregion
|
|
154
565
|
//#region ../contracts/src/common/errors.ts
|
|
155
566
|
const ERROR_CODES = {
|
|
@@ -157,13 +568,16 @@ const ERROR_CODES = {
|
|
|
157
568
|
"auth.missing_scope": 403,
|
|
158
569
|
"auth.disabled_principal": 403,
|
|
159
570
|
"principal.not_found": 404,
|
|
571
|
+
"principal.name_conflict": 409,
|
|
160
572
|
"key.not_found": 404,
|
|
573
|
+
"request.body_too_large": 413,
|
|
161
574
|
"validation.invalid": 400,
|
|
162
575
|
"idempotency.conflict": 409,
|
|
163
576
|
"capacity.queue_full": 429,
|
|
164
577
|
"capacity.waiters_full": 429,
|
|
165
578
|
"template.not_found": 404,
|
|
166
579
|
"template.version_not_found": 404,
|
|
580
|
+
"template.version_immutable": 409,
|
|
167
581
|
"template.not_authorized": 403,
|
|
168
582
|
"workspace.not_found": 404,
|
|
169
583
|
"workspace.external_id_conflict": 409,
|
|
@@ -178,6 +592,10 @@ const ERROR_CODES = {
|
|
|
178
592
|
"workspace.persistence_not_enabled": 409,
|
|
179
593
|
"workspace.preserving": 409,
|
|
180
594
|
"checkpoint.not_found": 404,
|
|
595
|
+
"checkpoint.unsupported": 409,
|
|
596
|
+
"artifact.not_found": 404,
|
|
597
|
+
"artifact.workspace_active": 409,
|
|
598
|
+
"artifact.too_large": 413,
|
|
181
599
|
"checkpoint.not_ready": 409,
|
|
182
600
|
"checkpoint.none_ready": 409,
|
|
183
601
|
"checkpoint.corrupt": 409,
|
|
@@ -187,24 +605,35 @@ const ERROR_CODES = {
|
|
|
187
605
|
"restore.image_unavailable": 409,
|
|
188
606
|
"restore.incompatible": 409,
|
|
189
607
|
"resume.unsupported": 409,
|
|
608
|
+
"conversation.not_found": 404,
|
|
190
609
|
"conversation.expired": 410,
|
|
191
610
|
"conversation.deleted": 410,
|
|
192
611
|
"operation.conflict": 409,
|
|
193
612
|
"source.not_allowed": 422,
|
|
194
613
|
"source.invalid_revision": 422,
|
|
195
614
|
"secret.unavailable": 503,
|
|
615
|
+
"secret.not_found": 404,
|
|
196
616
|
"storage.capacity_exhausted": 507,
|
|
197
617
|
"relay.body_too_large": 413,
|
|
198
618
|
"relay.route_not_allowed": 422,
|
|
199
619
|
"relay.streaming_unsupported": 409,
|
|
200
620
|
"relay.deadline_exceeded": 504,
|
|
201
621
|
"relay.upstream_error": 502,
|
|
622
|
+
"agent.interrupt_unsupported": 409,
|
|
202
623
|
"attachment.invalid": 400,
|
|
203
624
|
"attachment.too_large": 413,
|
|
204
625
|
"attachment.not_found": 404,
|
|
205
626
|
"attachment.conflict": 409,
|
|
206
627
|
"attachment.unsupported": 409,
|
|
207
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,
|
|
208
637
|
"internal.error": 500
|
|
209
638
|
};
|
|
210
639
|
const errorCodes = Object.keys(ERROR_CODES);
|
|
@@ -215,6 +644,9 @@ const ErrorEnvelopeSchema = z.object({ error: z.object({
|
|
|
215
644
|
details: z.record(z.string(), z.unknown()).optional()
|
|
216
645
|
}) });
|
|
217
646
|
//#endregion
|
|
647
|
+
//#region ../contracts/src/common/json-value.ts
|
|
648
|
+
const JsonValueSchema = z.json();
|
|
649
|
+
//#endregion
|
|
218
650
|
//#region ../contracts/src/common/pagination.ts
|
|
219
651
|
const CursorSchema = z.string().min(1).max(2048);
|
|
220
652
|
function CursorPageSchema(item) {
|
|
@@ -231,6 +663,7 @@ function CursorQuerySchema(maxLimit, defaultLimit) {
|
|
|
231
663
|
}
|
|
232
664
|
//#endregion
|
|
233
665
|
//#region ../contracts/src/conversations/facts.ts
|
|
666
|
+
const ProducerCodeSchema = z.string().regex(/^[a-z][a-z0-9_]{0,63}$/);
|
|
234
667
|
const ConversationFactSchema = z.discriminatedUnion("type", [
|
|
235
668
|
z.object({ type: z.literal("intermediate") }),
|
|
236
669
|
z.object({ type: z.literal("final_response") }),
|
|
@@ -245,7 +678,9 @@ const ConversationFactSchema = z.discriminatedUnion("type", [
|
|
|
245
678
|
"retrieval"
|
|
246
679
|
]),
|
|
247
680
|
label: z.string().min(1).max(160),
|
|
248
|
-
started_at: z.iso.datetime().nullable()
|
|
681
|
+
started_at: z.iso.datetime().nullable(),
|
|
682
|
+
action: ProducerCodeSchema.optional(),
|
|
683
|
+
subject: ProducerCodeSchema.optional()
|
|
249
684
|
}),
|
|
250
685
|
z.object({
|
|
251
686
|
type: z.literal("activity_finished"),
|
|
@@ -266,7 +701,8 @@ const ConversationFactSchema = z.discriminatedUnion("type", [
|
|
|
266
701
|
"interrupted",
|
|
267
702
|
"canceled",
|
|
268
703
|
"lost"
|
|
269
|
-
])
|
|
704
|
+
]),
|
|
705
|
+
reason: ProducerCodeSchema.optional()
|
|
270
706
|
})
|
|
271
707
|
]);
|
|
272
708
|
//#endregion
|
|
@@ -332,6 +768,160 @@ const ConversationResumeOutcomeSchema = z.object({
|
|
|
332
768
|
checkpoint_id: z.uuid()
|
|
333
769
|
});
|
|
334
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
|
|
335
925
|
//#region ../contracts/src/network/network.ts
|
|
336
926
|
const DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
337
927
|
function isDomain(value) {
|
|
@@ -386,151 +976,153 @@ const NetworkEventSchema = NetworkEventInputSchema.omit({ source_seq: true }).ex
|
|
|
386
976
|
CursorQuerySchema(200, 100);
|
|
387
977
|
const NetworkEventPageSchema = CursorPageSchema(NetworkEventSchema);
|
|
388
978
|
//#endregion
|
|
389
|
-
//#region ../contracts/src/
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
"
|
|
394
|
-
|
|
395
|
-
"unknown"
|
|
396
|
-
];
|
|
397
|
-
const SourceDescriptorSchema = z.object({
|
|
398
|
-
kind: z.literal("git"),
|
|
399
|
-
repository: z.string().min(1).max(64).regex(/^[a-z0-9]([a-z0-9._-]{0,62}[a-z0-9])?$/),
|
|
400
|
-
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) => {
|
|
401
|
-
const code = character.codePointAt(0) ?? 0;
|
|
402
|
-
return code >= 32 && code !== 127;
|
|
403
|
-
}), { message: "revision is not a safe branch, tag, or commit name" })
|
|
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()
|
|
404
985
|
});
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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
|
+
});
|
|
410
1008
|
});
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
maxFiles: z.number().int().positive().max(1e7)
|
|
1009
|
+
const WorkloadCancelSchema = z.strictObject({
|
|
1010
|
+
execution: WorkloadExecutionReferenceSchema,
|
|
1011
|
+
cancelId: z.uuid(),
|
|
1012
|
+
scope: z.enum(["operation", "workspace"])
|
|
416
1013
|
});
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
retention: DurationValueSchema.default("168h")
|
|
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
|
|
427
1023
|
});
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
1024
|
+
z.strictObject({
|
|
1025
|
+
exportId: z.uuid(),
|
|
1026
|
+
execution: WorkloadExecutionReferenceSchema,
|
|
1027
|
+
manifestDigest: WorkloadDigestSchema,
|
|
1028
|
+
completeness: z.enum([
|
|
1029
|
+
"complete",
|
|
1030
|
+
"partial",
|
|
1031
|
+
"lost"
|
|
1032
|
+
]),
|
|
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
|
+
});
|
|
434
1065
|
});
|
|
435
|
-
|
|
436
|
-
|
|
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()
|
|
1089
|
+
});
|
|
1090
|
+
const PreviewListSchema = z.array(z.object({
|
|
1091
|
+
name: PreviewNameSchema,
|
|
1092
|
+
port: z.number().int()
|
|
1093
|
+
}));
|
|
1094
|
+
const PreviewSocketPayload = z.discriminatedUnion("op", [
|
|
437
1095
|
z.object({
|
|
438
|
-
|
|
439
|
-
|
|
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)
|
|
440
1103
|
}),
|
|
441
1104
|
z.object({
|
|
442
|
-
|
|
443
|
-
|
|
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()
|
|
444
1124
|
})
|
|
445
1125
|
]);
|
|
446
|
-
const CHECKPOINT_STATES = [
|
|
447
|
-
"creating",
|
|
448
|
-
"ready",
|
|
449
|
-
"failed",
|
|
450
|
-
"deleting",
|
|
451
|
-
"deleted"
|
|
452
|
-
];
|
|
453
|
-
const OPERATION_KINDS = [
|
|
454
|
-
"preserve",
|
|
455
|
-
"restore",
|
|
456
|
-
"verify",
|
|
457
|
-
"delete",
|
|
458
|
-
"purge"
|
|
459
|
-
];
|
|
460
|
-
const OPERATION_STATES = [
|
|
461
|
-
"pending",
|
|
462
|
-
"running",
|
|
463
|
-
"succeeded",
|
|
464
|
-
"failed"
|
|
465
|
-
];
|
|
466
|
-
const CheckpointResourceSchema = z.object({
|
|
467
|
-
id: z.uuid(),
|
|
468
|
-
workspace_id: z.uuid(),
|
|
469
|
-
state: z.enum(CHECKPOINT_STATES),
|
|
470
|
-
reason_code: z.string().nullable(),
|
|
471
|
-
template: z.object({
|
|
472
|
-
name: z.string(),
|
|
473
|
-
version: z.string(),
|
|
474
|
-
digest: z.string()
|
|
475
|
-
}),
|
|
476
|
-
manifest_digest: z.string().nullable(),
|
|
477
|
-
logical_bytes: z.number().int().nonnegative().nullable(),
|
|
478
|
-
stored_bytes: z.number().int().nonnegative().nullable(),
|
|
479
|
-
file_count: z.number().int().nonnegative().nullable(),
|
|
480
|
-
mounts: z.array(z.string()),
|
|
481
|
-
conversation_restore: z.enum(CONVERSATION_RESTORE_CAPABILITIES),
|
|
482
|
-
label: z.string().nullable(),
|
|
483
|
-
created_at: z.iso.datetime(),
|
|
484
|
-
ready_at: z.iso.datetime().nullable(),
|
|
485
|
-
expires_at: z.iso.datetime().nullable()
|
|
486
|
-
});
|
|
487
|
-
const OperationResourceSchema = z.object({
|
|
488
|
-
id: z.uuid(),
|
|
489
|
-
kind: z.enum(OPERATION_KINDS),
|
|
490
|
-
state: z.enum(OPERATION_STATES),
|
|
491
|
-
workspace_id: z.uuid().nullable(),
|
|
492
|
-
checkpoint_id: z.uuid().nullable(),
|
|
493
|
-
result_workspace_id: z.uuid().nullable(),
|
|
494
|
-
reason_code: z.string().nullable(),
|
|
495
|
-
created_at: z.iso.datetime(),
|
|
496
|
-
updated_at: z.iso.datetime(),
|
|
497
|
-
completed_at: z.iso.datetime().nullable()
|
|
498
|
-
});
|
|
499
|
-
z.object({
|
|
500
|
-
retention: DurationValueSchema.optional(),
|
|
501
|
-
label: z.string().min(1).max(128).optional()
|
|
502
|
-
});
|
|
503
|
-
z.object({
|
|
504
|
-
external_id: z.string().min(1).max(256),
|
|
505
|
-
metadata: z.record(z.string().max(64), z.string().max(512)).optional(),
|
|
506
|
-
launch_input: z.record(z.string(), z.unknown()).optional()
|
|
507
|
-
});
|
|
508
|
-
const CheckpointManifestEntrySchema = z.object({
|
|
509
|
-
path: z.string(),
|
|
510
|
-
kind: z.enum([
|
|
511
|
-
"directory",
|
|
512
|
-
"file",
|
|
513
|
-
"symlink"
|
|
514
|
-
]),
|
|
515
|
-
mode: z.number().int().nonnegative(),
|
|
516
|
-
uid: z.number().int().nonnegative(),
|
|
517
|
-
gid: z.number().int().nonnegative(),
|
|
518
|
-
mtime_ns: z.string().regex(/^\d+$/),
|
|
519
|
-
size: z.number().int().nonnegative(),
|
|
520
|
-
digest: z.string().optional(),
|
|
521
|
-
link_target: z.string().optional()
|
|
522
|
-
});
|
|
523
|
-
z.object({
|
|
524
|
-
format: z.literal("pocketcoder-checkpoint/v1"),
|
|
525
|
-
checkpoint_id: z.uuid(),
|
|
526
|
-
template_digest: z.string(),
|
|
527
|
-
mounts: z.array(z.object({
|
|
528
|
-
name: z.string(),
|
|
529
|
-
entries: z.array(CheckpointManifestEntrySchema)
|
|
530
|
-
})),
|
|
531
|
-
logical_bytes: z.number().int().nonnegative(),
|
|
532
|
-
file_count: z.number().int().nonnegative()
|
|
533
|
-
});
|
|
534
1126
|
//#endregion
|
|
535
1127
|
//#region ../contracts/src/workspaces/workspace.ts
|
|
536
1128
|
const WORKSPACE_STATES = [
|
|
@@ -554,6 +1146,15 @@ const TERMINAL_STATES = [
|
|
|
554
1146
|
"preserved"
|
|
555
1147
|
];
|
|
556
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
|
+
],
|
|
557
1158
|
"child_exit_success",
|
|
558
1159
|
"child_exit_failure",
|
|
559
1160
|
"setup_failed",
|
|
@@ -588,6 +1189,7 @@ const AGENT_STATES = [
|
|
|
588
1189
|
"running",
|
|
589
1190
|
"stable"
|
|
590
1191
|
];
|
|
1192
|
+
const AgentInterruptResponseSchema = z.object({ status: z.enum(["requested", "idle"]) });
|
|
591
1193
|
z.object({
|
|
592
1194
|
external_id: z.string().min(1).max(256),
|
|
593
1195
|
template: z.object({
|
|
@@ -603,10 +1205,15 @@ const TemplateRefSchema = z.object({
|
|
|
603
1205
|
version: z.string(),
|
|
604
1206
|
digest: z.string()
|
|
605
1207
|
});
|
|
1208
|
+
const AgentMetadataSchema = z.object({
|
|
1209
|
+
type: z.string(),
|
|
1210
|
+
model: z.string().min(1).max(255).nullable()
|
|
1211
|
+
});
|
|
606
1212
|
const WorkspaceResourceSchema = z.object({
|
|
607
1213
|
id: z.uuid(),
|
|
608
1214
|
external_id: z.string(),
|
|
609
1215
|
template: TemplateRefSchema,
|
|
1216
|
+
agent: AgentMetadataSchema.nullable(),
|
|
610
1217
|
state: z.enum(WORKSPACE_STATES),
|
|
611
1218
|
reason_code: z.enum(REASON_CODES).nullable(),
|
|
612
1219
|
agent_state: z.enum(AGENT_STATES),
|
|
@@ -660,19 +1267,21 @@ z.object({
|
|
|
660
1267
|
limit: z.coerce.number().int().positive().max(200).default(50),
|
|
661
1268
|
cursor: z.string().optional()
|
|
662
1269
|
});
|
|
663
|
-
|
|
664
|
-
//#region ../contracts/src/protocol/api.ts
|
|
665
|
-
const TemplatePageSchema = CursorPageSchema(z.object({
|
|
1270
|
+
const TemplateListItemSchema = z.object({
|
|
666
1271
|
name: z.string(),
|
|
667
1272
|
version: z.string(),
|
|
668
1273
|
digest: z.string(),
|
|
1274
|
+
agent: AgentMetadataSchema.nullable(),
|
|
669
1275
|
description: z.string().optional(),
|
|
670
1276
|
status: z.enum([
|
|
671
1277
|
"active",
|
|
672
1278
|
"available",
|
|
673
1279
|
"retired"
|
|
674
1280
|
])
|
|
675
|
-
})
|
|
1281
|
+
});
|
|
1282
|
+
//#endregion
|
|
1283
|
+
//#region ../contracts/src/protocol/api.ts
|
|
1284
|
+
const TemplatePageSchema = CursorPageSchema(TemplateListItemSchema);
|
|
676
1285
|
CursorQuerySchema(200, 100);
|
|
677
1286
|
const CheckpointPageSchema = CursorPageSchema(CheckpointResourceSchema);
|
|
678
1287
|
CursorQuerySchema(200, 50).extend({ state: z.enum(CHECKPOINT_STATES).optional() });
|
|
@@ -762,6 +1371,52 @@ const LogChunkSchema = z.object({
|
|
|
762
1371
|
});
|
|
763
1372
|
CursorQuerySchema(1e3, 200);
|
|
764
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) });
|
|
765
1420
|
//#endregion
|
|
766
1421
|
//#region ../contracts/src/templates/template-constants.ts
|
|
767
1422
|
function isAbsolutePath(value) {
|
|
@@ -770,7 +1425,7 @@ function isAbsolutePath(value) {
|
|
|
770
1425
|
const SECRET_ENV_PATTERN = /(SECRET|TOKEN|PASSWORD|PASSWD|API_?KEY|PRIVATE_?KEY|CREDENTIAL)/i;
|
|
771
1426
|
const SECRET_REFERENCE_PREFIX = "secretRef:";
|
|
772
1427
|
//#endregion
|
|
773
|
-
//#region ../contracts/src/templates/template-schema.ts
|
|
1428
|
+
//#region ../contracts/src/templates/template-alpha-schema.ts
|
|
774
1429
|
const NAME_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
775
1430
|
const SEMVER_RE = /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;
|
|
776
1431
|
const IMAGE_DIGEST_RE = /^[^\s@]+@sha256:[0-9a-f]{64}$/;
|
|
@@ -797,10 +1452,12 @@ const HarnessSchema = z.object({
|
|
|
797
1452
|
cwd: z.string().refine(isAbsolutePath, "expected an absolute path").optional()
|
|
798
1453
|
});
|
|
799
1454
|
const AgentSchema = HarnessSchema.extend({
|
|
1455
|
+
model: z.string().min(1).max(255).optional().describe("Display metadata only; does not configure the agent model."),
|
|
800
1456
|
type: z.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/).default("custom"),
|
|
801
1457
|
transport: z.enum(["pty", "acp"]).default("pty"),
|
|
802
1458
|
termWidth: z.number().int().min(10).max(65535).optional(),
|
|
803
|
-
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()
|
|
804
1461
|
});
|
|
805
1462
|
const TerminalSchema = z.object({
|
|
806
1463
|
command: CommandSchema,
|
|
@@ -883,6 +1540,7 @@ const CheckpointHookSchema = z.object({
|
|
|
883
1540
|
const TemplateSpecSchema = z.object({
|
|
884
1541
|
version: z.string().regex(SEMVER_RE),
|
|
885
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(),
|
|
886
1544
|
command: CommandSchema.default([
|
|
887
1545
|
"/usr/local/bin/pocketcoder-supervisor",
|
|
888
1546
|
"supervise",
|
|
@@ -893,6 +1551,8 @@ const TemplateSpecSchema = z.object({
|
|
|
893
1551
|
agent: AgentSchema.optional(),
|
|
894
1552
|
harness: HarnessSchema.optional(),
|
|
895
1553
|
terminal: TerminalSchema.optional(),
|
|
1554
|
+
previews: PreviewsSchema.optional(),
|
|
1555
|
+
display: DisplaySchema.optional(),
|
|
896
1556
|
env: EnvSchema.default({}),
|
|
897
1557
|
resources: ResourcesSchema,
|
|
898
1558
|
timeouts: TimeoutsSchema.prefault({}),
|
|
@@ -915,7 +1575,11 @@ const TemplateSpecSchema = z.object({
|
|
|
915
1575
|
message: "either agent or harness is required"
|
|
916
1576
|
});
|
|
917
1577
|
if (!spec.agent) return;
|
|
918
|
-
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
|
+
]) {
|
|
919
1583
|
if (spec.agent[field] === void 0) continue;
|
|
920
1584
|
ctx.addIssue({
|
|
921
1585
|
code: "custom",
|
|
@@ -951,7 +1615,7 @@ const TemplateSpecSchema = z.object({
|
|
|
951
1615
|
};
|
|
952
1616
|
});
|
|
953
1617
|
const TemplateManifestBaseSchema = z.object({
|
|
954
|
-
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" }),
|
|
955
1619
|
kind: z.literal("Template"),
|
|
956
1620
|
metadata: z.object({
|
|
957
1621
|
name: z.string().regex(NAME_RE),
|
|
@@ -960,61 +1624,105 @@ const TemplateManifestBaseSchema = z.object({
|
|
|
960
1624
|
spec: TemplateSpecSchema
|
|
961
1625
|
});
|
|
962
1626
|
//#endregion
|
|
963
|
-
//#region ../contracts/src/
|
|
964
|
-
const
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
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
|
+
});
|
|
981
1661
|
});
|
|
982
|
-
const
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
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
|
+
});
|
|
991
1725
|
});
|
|
992
|
-
function isAgentApiNative(spec) {
|
|
993
|
-
return "agent" in spec && spec.agent !== void 0;
|
|
994
|
-
}
|
|
995
|
-
function agentApiHarness(spec) {
|
|
996
|
-
if (!isAgentApiNative(spec)) return spec.harness;
|
|
997
|
-
return {
|
|
998
|
-
command: [
|
|
999
|
-
"/usr/local/bin/agentapi",
|
|
1000
|
-
"server",
|
|
1001
|
-
"--type",
|
|
1002
|
-
spec.agent.type,
|
|
1003
|
-
...spec.agent.transport === "acp" ? ["--experimental-acp"] : [],
|
|
1004
|
-
...spec.agent.termWidth === void 0 ? [] : ["--term-width", String(spec.agent.termWidth)],
|
|
1005
|
-
...spec.agent.stateFile === void 0 ? [] : ["--state-file", spec.agent.stateFile],
|
|
1006
|
-
"--port",
|
|
1007
|
-
"3284",
|
|
1008
|
-
"--",
|
|
1009
|
-
...spec.agent.command
|
|
1010
|
-
],
|
|
1011
|
-
env: spec.agent.env,
|
|
1012
|
-
...spec.agent.cwd ? { cwd: spec.agent.cwd } : {}
|
|
1013
|
-
};
|
|
1014
|
-
}
|
|
1015
|
-
function templateServices(spec) {
|
|
1016
|
-
return isAgentApiNative(spec) ? { agent: AGENTAPI_SERVICE } : spec.services;
|
|
1017
|
-
}
|
|
1018
1726
|
//#endregion
|
|
1019
1727
|
//#region ../contracts/src/templates/template-validation.ts
|
|
1020
1728
|
function envSources(spec) {
|
|
@@ -1062,7 +1770,7 @@ function validateNetworkEnvironment(spec, ctx) {
|
|
|
1062
1770
|
});
|
|
1063
1771
|
}
|
|
1064
1772
|
}
|
|
1065
|
-
function validateServices(spec, ctx) {
|
|
1773
|
+
function validateServices$1(spec, ctx) {
|
|
1066
1774
|
for (const [serviceName, service] of Object.entries(templateServices(spec))) {
|
|
1067
1775
|
if (!isLoopbackBaseUrl(service.baseUrl)) ctx.addIssue({
|
|
1068
1776
|
code: "custom",
|
|
@@ -1205,7 +1913,7 @@ const FORBIDDEN_PERSISTENCE_ROOTS = [
|
|
|
1205
1913
|
function pathContains(parent, child) {
|
|
1206
1914
|
return child === parent || child.startsWith(`${parent}/`);
|
|
1207
1915
|
}
|
|
1208
|
-
function
|
|
1916
|
+
function validateTemplatePersistence(spec, ctx) {
|
|
1209
1917
|
const seenNames = /* @__PURE__ */ new Set();
|
|
1210
1918
|
const mounts = spec.persistence.mounts;
|
|
1211
1919
|
for (const [index, mount] of mounts.entries()) validatePersistenceMount(spec, mounts, mount, index, seenNames, ctx);
|
|
@@ -1222,7 +1930,7 @@ function validatePersistence(spec, ctx) {
|
|
|
1222
1930
|
});
|
|
1223
1931
|
}
|
|
1224
1932
|
function validateAgentStateFile(spec, mounts, ctx) {
|
|
1225
|
-
if (!
|
|
1933
|
+
if (!spec.agent) return;
|
|
1226
1934
|
const stateFile = spec.agent.stateFile;
|
|
1227
1935
|
const normalized = stateFile !== void 0 && isNormalizedFilesystemPath(stateFile);
|
|
1228
1936
|
if (stateFile !== void 0 && !normalized) ctx.addIssue({
|
|
@@ -1343,18 +2051,436 @@ function isNormalizedPath(path) {
|
|
|
1343
2051
|
return true;
|
|
1344
2052
|
}
|
|
1345
2053
|
function validateTemplateSpec(spec, ctx) {
|
|
1346
|
-
validateServices(spec, ctx);
|
|
2054
|
+
validateServices$1(spec, ctx);
|
|
2055
|
+
validatePreviews(spec, ctx);
|
|
1347
2056
|
validateEnvironment(spec, ctx);
|
|
1348
2057
|
validateOutputs(spec, ctx);
|
|
1349
2058
|
validateWritableMemoryPaths(spec, ctx);
|
|
1350
|
-
|
|
2059
|
+
validateTemplatePersistence(spec, ctx);
|
|
1351
2060
|
validateNetworkEnvironment(spec, ctx);
|
|
1352
2061
|
validateTerminal(spec, ctx);
|
|
1353
2062
|
}
|
|
1354
|
-
|
|
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");
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
function validateWorkloadTemplate(manifest, ctx) {
|
|
2218
|
+
const spec = manifest.spec;
|
|
2219
|
+
const issue = (path, message) => ctx.addIssue({
|
|
2220
|
+
code: "custom",
|
|
2221
|
+
path: ["spec", ...path],
|
|
2222
|
+
message
|
|
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");
|
|
2238
|
+
}
|
|
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;
|
|
2334
|
+
}
|
|
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
|
+
};
|
|
2357
|
+
}
|
|
2358
|
+
function templateServices(spec) {
|
|
2359
|
+
return isAgentApiNative(spec) ? { agent: AGENTAPI_SERVICE } : spec.services ?? {};
|
|
2360
|
+
}
|
|
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) => {
|
|
1355
2370
|
validateTemplateSpec(manifest.spec, ctx);
|
|
1356
2371
|
});
|
|
1357
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
|
|
1358
2484
|
//#region ../contracts/src/terminals/terminal.ts
|
|
1359
2485
|
const TERMINAL_CHUNK_BYTES = 32768;
|
|
1360
2486
|
const TERMINAL_REPLAY_BUFFER_BYTES = 65536;
|
|
@@ -1469,11 +2595,34 @@ const TerminalSessionSchema = z.object({
|
|
|
1469
2595
|
CursorQuerySchema(200, 50);
|
|
1470
2596
|
const TerminalSessionPageSchema = CursorPageSchema(TerminalSessionSchema);
|
|
1471
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
|
|
1472
2620
|
//#region ../contracts/src/protocol/protocol-stream.ts
|
|
1473
2621
|
const ProxyStreamStartPayload = z.object({
|
|
1474
2622
|
request_id: z.uuid(),
|
|
1475
2623
|
status: z.number().int().min(100).max(599),
|
|
1476
|
-
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()
|
|
1477
2626
|
});
|
|
1478
2627
|
const ProxyStreamChunkPayload = z.object({
|
|
1479
2628
|
request_id: z.uuid(),
|
|
@@ -1541,7 +2690,6 @@ z.object({
|
|
|
1541
2690
|
type: z.literal("lease_assignment"),
|
|
1542
2691
|
input: ProviderInputSchema
|
|
1543
2692
|
});
|
|
1544
|
-
const SOURCE_CREDENTIAL_MAX_BYTES = 65536;
|
|
1545
2693
|
const EnvelopeBase = z.object({
|
|
1546
2694
|
v: z.union([
|
|
1547
2695
|
z.literal(1),
|
|
@@ -1549,7 +2697,14 @@ const EnvelopeBase = z.object({
|
|
|
1549
2697
|
z.literal(3),
|
|
1550
2698
|
z.literal(4),
|
|
1551
2699
|
z.literal(5),
|
|
1552
|
-
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)
|
|
1553
2708
|
]),
|
|
1554
2709
|
workspace_id: z.uuid(),
|
|
1555
2710
|
connection_id: z.uuid(),
|
|
@@ -1571,6 +2726,7 @@ const HeartbeatPayload = z.object({
|
|
|
1571
2726
|
child: z.enum([
|
|
1572
2727
|
"starting",
|
|
1573
2728
|
"setup",
|
|
2729
|
+
"waiting",
|
|
1574
2730
|
"running",
|
|
1575
2731
|
"exited",
|
|
1576
2732
|
"terminating"
|
|
@@ -1585,6 +2741,7 @@ const ProcessStatePayload = z.object({
|
|
|
1585
2741
|
phase: z.enum([
|
|
1586
2742
|
"starting",
|
|
1587
2743
|
"setup",
|
|
2744
|
+
"waiting",
|
|
1588
2745
|
"running",
|
|
1589
2746
|
"exited",
|
|
1590
2747
|
"terminating"
|
|
@@ -1666,6 +2823,38 @@ const RestoreStatusPayload = z.object({
|
|
|
1666
2823
|
});
|
|
1667
2824
|
const ConversationMessagePayload = ConversationMessageInputSchema;
|
|
1668
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
|
+
}),
|
|
1669
2858
|
EnvelopeBase.extend({
|
|
1670
2859
|
type: z.literal("registered"),
|
|
1671
2860
|
payload: RegisteredPayload
|
|
@@ -1735,77 +2924,44 @@ z.discriminatedUnion("type", [
|
|
|
1735
2924
|
payload: CheckpointStatusPayload
|
|
1736
2925
|
}),
|
|
1737
2926
|
EnvelopeBase.extend({
|
|
1738
|
-
type: z.literal("
|
|
1739
|
-
payload:
|
|
1740
|
-
}),
|
|
1741
|
-
EnvelopeBase.extend({
|
|
1742
|
-
type: z.literal("restore_status"),
|
|
1743
|
-
payload: RestoreStatusPayload
|
|
2927
|
+
type: z.literal("checkpoint_prepared"),
|
|
2928
|
+
payload: CheckpointPreparedPayload
|
|
1744
2929
|
}),
|
|
1745
2930
|
EnvelopeBase.extend({
|
|
1746
|
-
type: z.literal("
|
|
1747
|
-
payload:
|
|
2931
|
+
type: z.literal("checkpoint_installed"),
|
|
2932
|
+
payload: CheckpointInstalledPayload
|
|
1748
2933
|
}),
|
|
1749
2934
|
EnvelopeBase.extend({
|
|
1750
|
-
type: z.literal("
|
|
1751
|
-
payload:
|
|
2935
|
+
type: z.literal("checkpoint_upload_status"),
|
|
2936
|
+
payload: CheckpointUploadStatusPayload
|
|
1752
2937
|
}),
|
|
1753
2938
|
EnvelopeBase.extend({
|
|
1754
|
-
type: z.literal("
|
|
1755
|
-
payload:
|
|
2939
|
+
type: z.literal("output_published"),
|
|
2940
|
+
payload: OutputPublishedPayload
|
|
1756
2941
|
}),
|
|
1757
2942
|
EnvelopeBase.extend({
|
|
1758
|
-
type: z.literal("
|
|
1759
|
-
payload:
|
|
1760
|
-
})
|
|
1761
|
-
]);
|
|
1762
|
-
const ExecSpecSchema = z.object({
|
|
1763
|
-
agentapi_native: z.boolean().default(false),
|
|
1764
|
-
setup: z.array(SetupStepSchema),
|
|
1765
|
-
harness: HarnessSchema,
|
|
1766
|
-
env: z.record(z.string(), z.string()),
|
|
1767
|
-
services: z.record(z.string(), ServiceSchema),
|
|
1768
|
-
terminal: z.object({
|
|
1769
|
-
command: z.array(z.string().min(1)).min(1),
|
|
1770
|
-
cwd: z.string().optional(),
|
|
1771
|
-
env: z.record(z.string(), z.string()),
|
|
1772
|
-
max_sessions: z.number().int().min(1).max(8),
|
|
1773
|
-
idle_timeout_seconds: z.number().int().positive(),
|
|
1774
|
-
replay_buffer_bytes: z.number().int().positive()
|
|
1775
|
-
}).nullable().default(null),
|
|
1776
|
-
timeouts: TimeoutsSchema,
|
|
1777
|
-
security: z.object({ writable_memory_paths: z.array(z.string()) }).default({ writable_memory_paths: [] }),
|
|
1778
|
-
network: z.discriminatedUnion("mode", [z.object({ mode: z.literal("unrestricted") }), z.object({
|
|
1779
|
-
mode: z.literal("restricted"),
|
|
1780
|
-
proxy_url: z.url(),
|
|
1781
|
-
health_url: z.url()
|
|
1782
|
-
})]).default({ mode: "unrestricted" }),
|
|
1783
|
-
launch_mode: z.enum(LAUNCH_MODES).default("create"),
|
|
1784
|
-
source: SourceDescriptorSchema.extend({
|
|
1785
|
-
url: z.url(),
|
|
1786
|
-
destination: z.string(),
|
|
1787
|
-
credential: z.string().min(1).max(SOURCE_CREDENTIAL_MAX_BYTES).refine((value) => !value.includes("\0"), "credential must not contain NUL bytes").nullable().default(null)
|
|
1788
|
-
}).nullable().default(null),
|
|
1789
|
-
restore: z.object({
|
|
1790
|
-
checkpoint_id: z.uuid(),
|
|
1791
|
-
origin_workspace_id: z.uuid()
|
|
1792
|
-
}).nullable().default(null),
|
|
1793
|
-
persistence: z.object({
|
|
1794
|
-
mounts: z.array(z.object({
|
|
1795
|
-
name: z.string(),
|
|
1796
|
-
target: z.string()
|
|
1797
|
-
})),
|
|
1798
|
-
conversation_restore: z.enum(CONVERSATION_RESTORE_CAPABILITIES)
|
|
2943
|
+
type: z.literal("restore_status"),
|
|
2944
|
+
payload: RestoreStatusPayload
|
|
1799
2945
|
}),
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
})
|
|
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
|
+
]);
|
|
1808
2963
|
const RegisteredAckPayload = z.object({
|
|
2964
|
+
credentials: z.array(WorkspaceCredentialSchema).max(64).default([]),
|
|
1809
2965
|
epoch: z.number().int().positive(),
|
|
1810
2966
|
reconnect_credential: z.string().optional(),
|
|
1811
2967
|
limits: z.object({
|
|
@@ -1824,7 +2980,9 @@ const ProxyRequestPayload = z.object({
|
|
|
1824
2980
|
"POST",
|
|
1825
2981
|
"PUT",
|
|
1826
2982
|
"PATCH",
|
|
1827
|
-
"DELETE"
|
|
2983
|
+
"DELETE",
|
|
2984
|
+
"HEAD",
|
|
2985
|
+
"OPTIONS"
|
|
1828
2986
|
]),
|
|
1829
2987
|
path: z.string(),
|
|
1830
2988
|
query: z.record(z.string(), z.string()).default({}),
|
|
@@ -1840,6 +2998,42 @@ const PrepareCheckpointPayload = z.object({
|
|
|
1840
2998
|
deadline_ms: z.number().int().positive()
|
|
1841
2999
|
});
|
|
1842
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
|
+
}),
|
|
1843
3037
|
EnvelopeBase.extend({
|
|
1844
3038
|
type: z.literal("registered_ack"),
|
|
1845
3039
|
payload: RegisteredAckPayload
|
|
@@ -1888,6 +3082,14 @@ z.discriminatedUnion("type", [
|
|
|
1888
3082
|
type: z.literal("prepare_checkpoint"),
|
|
1889
3083
|
payload: PrepareCheckpointPayload
|
|
1890
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
|
+
}),
|
|
1891
3093
|
EnvelopeBase.extend({
|
|
1892
3094
|
type: z.literal("attachment_start"),
|
|
1893
3095
|
payload: AttachmentStartPayload
|
|
@@ -1910,6 +3112,107 @@ z.discriminatedUnion("type", [
|
|
|
1910
3112
|
})
|
|
1911
3113
|
]);
|
|
1912
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
|
|
1913
3216
|
//#region src/resources/admin/admin.ts
|
|
1914
3217
|
var AdministrationApi = class {
|
|
1915
3218
|
transport;
|
|
@@ -1987,44 +3290,8 @@ function responseError(response, body) {
|
|
|
1987
3290
|
});
|
|
1988
3291
|
}
|
|
1989
3292
|
//#endregion
|
|
1990
|
-
//#region src/resources/
|
|
3293
|
+
//#region src/resources/agent/agent.ts
|
|
1991
3294
|
const DEFAULT_READY_TIMEOUT_MS = 12e4;
|
|
1992
|
-
function contentDisposition(name) {
|
|
1993
|
-
const clean = name.replace(/[\r\n]/g, "");
|
|
1994
|
-
if (/^[ -~]*$/.test(clean)) return `attachment; filename="${clean.replace(/(["\\])/g, "\\$1")}"`;
|
|
1995
|
-
return `attachment; filename*=UTF-8''${encodeURIComponent(clean)}`;
|
|
1996
|
-
}
|
|
1997
|
-
function progressStream(body, total, onProgress) {
|
|
1998
|
-
const source = new Response(body).body ?? new ReadableStream({ start: (c) => c.close() });
|
|
1999
|
-
let uploaded = 0;
|
|
2000
|
-
return source.pipeThrough(new TransformStream({ transform(chunk, controller) {
|
|
2001
|
-
uploaded += chunk.byteLength;
|
|
2002
|
-
controller.enqueue(chunk);
|
|
2003
|
-
onProgress(Math.min(uploaded, total), total);
|
|
2004
|
-
} }));
|
|
2005
|
-
}
|
|
2006
|
-
var AttachmentsApi = class {
|
|
2007
|
-
transport;
|
|
2008
|
-
constructor(transport) {
|
|
2009
|
-
this.transport = transport;
|
|
2010
|
-
}
|
|
2011
|
-
async upload(workspaceId, input) {
|
|
2012
|
-
const id = (input.id ?? crypto.randomUUID()).toLowerCase();
|
|
2013
|
-
const body = input.onProgress ? progressStream(input.body, input.sizeBytes, input.onProgress) : input.body;
|
|
2014
|
-
const init = {
|
|
2015
|
-
method: "PUT",
|
|
2016
|
-
...input.signal ? { signal: input.signal } : {},
|
|
2017
|
-
headers: {
|
|
2018
|
-
"content-type": input.mediaType ?? "application/octet-stream",
|
|
2019
|
-
"content-disposition": contentDisposition(input.name),
|
|
2020
|
-
"content-length": String(input.sizeBytes)
|
|
2021
|
-
},
|
|
2022
|
-
body,
|
|
2023
|
-
...body instanceof ReadableStream ? { duplex: "half" } : {}
|
|
2024
|
-
};
|
|
2025
|
-
return await this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/attachments/${id}`, AttachmentDescriptorSchema, init);
|
|
2026
|
-
}
|
|
2027
|
-
};
|
|
2028
3295
|
var AgentApi = class {
|
|
2029
3296
|
transport;
|
|
2030
3297
|
workspaces;
|
|
@@ -2058,6 +3325,58 @@ var AgentApi = class {
|
|
|
2058
3325
|
}
|
|
2059
3326
|
throw responseError(response, body);
|
|
2060
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
|
+
}
|
|
2061
3380
|
};
|
|
2062
3381
|
//#endregion
|
|
2063
3382
|
//#region src/transport/common.ts
|
|
@@ -2122,13 +3441,32 @@ var OperationsApi = class {
|
|
|
2122
3441
|
};
|
|
2123
3442
|
//#endregion
|
|
2124
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
|
+
}
|
|
2125
3463
|
var ConversationsApi = class {
|
|
2126
3464
|
transport;
|
|
2127
3465
|
constructor(transport) {
|
|
2128
3466
|
this.transport = transport;
|
|
2129
3467
|
}
|
|
2130
3468
|
async list(id, query = {}, options = {}) {
|
|
2131
|
-
const body = await this.transport.request(
|
|
3469
|
+
const body = await this.transport.request(`${workspacePath(id)}/conversation?${queryString({
|
|
2132
3470
|
cursor: query.cursor,
|
|
2133
3471
|
limit: query.limit ?? 100
|
|
2134
3472
|
})}`, ConversationPageSchema, options);
|
|
@@ -2139,12 +3477,37 @@ var ConversationsApi = class {
|
|
|
2139
3477
|
};
|
|
2140
3478
|
}
|
|
2141
3479
|
async acceptRequest(id, identity, options = {}) {
|
|
2142
|
-
return this.transport.request(
|
|
3480
|
+
return this.transport.request(`${workspacePath(id)}/conversation/requests`, ConversationRequestReceiptSchema, {
|
|
2143
3481
|
...options,
|
|
2144
3482
|
method: "POST",
|
|
2145
3483
|
body: JSON.stringify(identity)
|
|
2146
3484
|
});
|
|
2147
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({
|
|
3491
|
+
cursor: query.cursor,
|
|
3492
|
+
limit: query.limit ?? 100
|
|
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);
|
|
3510
|
+
}
|
|
2148
3511
|
};
|
|
2149
3512
|
//#endregion
|
|
2150
3513
|
//#region src/resources/diagnostics/diagnostics.ts
|
|
@@ -2175,8 +3538,40 @@ var NetworkEventsApi = class extends WorkspaceCursorApi {
|
|
|
2175
3538
|
}
|
|
2176
3539
|
};
|
|
2177
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
|
+
}
|
|
2178
3547
|
constructor(transport) {
|
|
2179
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
|
+
});
|
|
2180
3575
|
}
|
|
2181
3576
|
};
|
|
2182
3577
|
//#endregion
|
|
@@ -2204,6 +3599,9 @@ var KeysApi = class {
|
|
|
2204
3599
|
cursor = page.next_cursor ?? void 0;
|
|
2205
3600
|
} while (cursor);
|
|
2206
3601
|
}
|
|
3602
|
+
expiring(query = {}, options = {}) {
|
|
3603
|
+
return this.transport.request(`/v1/keys/expiring?${queryString({ within_hours: query.withinHours })}`, ExpiringKeyListResponseSchema, options);
|
|
3604
|
+
}
|
|
2207
3605
|
issue(principalId, input, options = {}) {
|
|
2208
3606
|
return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys`, KeyIssueResponseSchema, {
|
|
2209
3607
|
method: "POST",
|
|
@@ -2211,6 +3609,13 @@ var KeysApi = class {
|
|
|
2211
3609
|
signal: options.signal
|
|
2212
3610
|
});
|
|
2213
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
|
+
}
|
|
2214
3619
|
revoke(principalId, keyId, options = {}) {
|
|
2215
3620
|
return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys/${encodeURIComponent(keyId)}`, z.object({ revoked: z.literal(true) }), {
|
|
2216
3621
|
method: "DELETE",
|
|
@@ -2242,6 +3647,76 @@ var RecoveryApi = class {
|
|
|
2242
3647
|
}
|
|
2243
3648
|
};
|
|
2244
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
|
|
2245
3720
|
//#region src/resources/templates/templates.ts
|
|
2246
3721
|
var TemplatesApi = class {
|
|
2247
3722
|
transport;
|
|
@@ -2255,7 +3730,27 @@ var TemplatesApi = class {
|
|
|
2255
3730
|
})}`, TemplatePageSchema, options));
|
|
2256
3731
|
}
|
|
2257
3732
|
async list(options = {}) {
|
|
2258
|
-
|
|
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
|
+
});
|
|
2259
3754
|
}
|
|
2260
3755
|
};
|
|
2261
3756
|
//#endregion
|
|
@@ -2519,6 +4014,20 @@ var PocketCoderTransport = class {
|
|
|
2519
4014
|
await delay(100 * 2 ** attempt, signal);
|
|
2520
4015
|
}
|
|
2521
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
|
+
}
|
|
2522
4031
|
async request(path, schema, init = {}) {
|
|
2523
4032
|
const response = await this.raw(path, init);
|
|
2524
4033
|
const text = await response.text();
|
|
@@ -2583,8 +4092,11 @@ async function delay(milliseconds, signal) {
|
|
|
2583
4092
|
//#region src/client.ts
|
|
2584
4093
|
var PocketCoderClient = class {
|
|
2585
4094
|
transport;
|
|
4095
|
+
secrets;
|
|
2586
4096
|
templates;
|
|
2587
4097
|
workspaces;
|
|
4098
|
+
previews;
|
|
4099
|
+
displays;
|
|
2588
4100
|
attachments;
|
|
2589
4101
|
agent;
|
|
2590
4102
|
conversations;
|
|
@@ -2595,12 +4107,16 @@ var PocketCoderClient = class {
|
|
|
2595
4107
|
outputs;
|
|
2596
4108
|
administration;
|
|
2597
4109
|
terminals;
|
|
4110
|
+
principals;
|
|
2598
4111
|
keys;
|
|
2599
4112
|
recovery;
|
|
2600
4113
|
constructor(config, fetchImpl = fetch) {
|
|
2601
4114
|
this.transport = new PocketCoderTransport(config, fetchImpl);
|
|
4115
|
+
this.secrets = new SecretsApi(this.transport);
|
|
2602
4116
|
this.templates = new TemplatesApi(this.transport);
|
|
2603
4117
|
this.workspaces = new WorkspacesApi(this.transport);
|
|
4118
|
+
this.previews = new PreviewsApi(this.transport);
|
|
4119
|
+
this.displays = new DisplaysApi(this.transport);
|
|
2604
4120
|
this.attachments = new AttachmentsApi(this.transport);
|
|
2605
4121
|
this.agent = new AgentApi(this.transport, this.workspaces);
|
|
2606
4122
|
this.conversations = new ConversationsApi(this.transport);
|
|
@@ -2611,6 +4127,7 @@ var PocketCoderClient = class {
|
|
|
2611
4127
|
this.outputs = new OutputsApi(this.transport);
|
|
2612
4128
|
this.administration = new AdministrationApi(this.transport);
|
|
2613
4129
|
this.terminals = new TerminalsApi(this.transport);
|
|
4130
|
+
this.principals = new PrincipalsApi(this.transport);
|
|
2614
4131
|
this.keys = new KeysApi(this.transport);
|
|
2615
4132
|
this.recovery = new RecoveryApi(this.transport);
|
|
2616
4133
|
}
|
|
@@ -2763,4 +4280,4 @@ var WorkspaceTurnResolver = class {
|
|
|
2763
4280
|
}
|
|
2764
4281
|
};
|
|
2765
4282
|
//#endregion
|
|
2766
|
-
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 };
|