@pstdio/pocketcoder-sdk 0.6.1 → 0.8.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 +2164 -108
- package/dist/index.js +1829 -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,266 @@ 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
|
+
template_version: z.string().min(1).max(256).optional()
|
|
477
|
+
});
|
|
478
|
+
const CheckpointManifestEntrySchema = z.object({
|
|
479
|
+
path: z.string(),
|
|
480
|
+
kind: z.enum([
|
|
481
|
+
"directory",
|
|
482
|
+
"file",
|
|
483
|
+
"symlink"
|
|
484
|
+
]),
|
|
485
|
+
mode: z.number().int().nonnegative(),
|
|
486
|
+
uid: z.number().int().nonnegative(),
|
|
487
|
+
gid: z.number().int().nonnegative(),
|
|
488
|
+
mtime_ns: z.string().regex(/^\d+$/),
|
|
489
|
+
size: z.number().int().nonnegative(),
|
|
490
|
+
digest: z.string().optional(),
|
|
491
|
+
link_target: z.string().optional()
|
|
492
|
+
});
|
|
493
|
+
z.object({
|
|
494
|
+
format: z.literal("pocketcoder-checkpoint/v1"),
|
|
495
|
+
checkpoint_id: z.uuid(),
|
|
496
|
+
template_digest: z.string(),
|
|
497
|
+
mounts: z.array(z.object({
|
|
498
|
+
name: z.string(),
|
|
499
|
+
entries: z.array(CheckpointManifestEntrySchema)
|
|
500
|
+
})),
|
|
501
|
+
logical_bytes: z.number().int().nonnegative(),
|
|
502
|
+
file_count: z.number().int().nonnegative()
|
|
503
|
+
});
|
|
504
|
+
//#endregion
|
|
505
|
+
//#region ../contracts/src/checkpoints/archive-format.ts
|
|
506
|
+
const CHECKPOINT_ARCHIVE_FORMAT = "pocketcoder-checkpoint-tar/v1";
|
|
507
|
+
const count = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
508
|
+
const digest$1 = z.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
509
|
+
const mount = z.strictObject({
|
|
510
|
+
name: PersistenceMountSchema.shape.name,
|
|
511
|
+
logical_bytes: count,
|
|
512
|
+
file_count: count
|
|
513
|
+
});
|
|
514
|
+
const mounts = z.array(mount).max(16).refine((items) => new Set(items.map((item) => item.name)).size === items.length);
|
|
515
|
+
const CheckpointArchiveHeaderSchema = z.strictObject({
|
|
516
|
+
format: z.literal(CHECKPOINT_ARCHIVE_FORMAT),
|
|
517
|
+
checkpoint_id: z.uuid(),
|
|
518
|
+
workspace_id: z.uuid(),
|
|
519
|
+
template_digest: digest$1,
|
|
520
|
+
mounts
|
|
521
|
+
});
|
|
522
|
+
function validUnicode(value) {
|
|
523
|
+
return Buffer.from(value).toString("utf8") === value;
|
|
524
|
+
}
|
|
525
|
+
function safeCheckpointPath(value) {
|
|
526
|
+
return validUnicode(value) && value === value.normalize("NFC") && !value.includes("\0") && !value.includes("\\") && value.split("/").every((part) => part !== "" && part !== "." && part !== "..");
|
|
527
|
+
}
|
|
528
|
+
function safeCheckpointLink(path, target) {
|
|
529
|
+
if (!target || !validUnicode(target) || target.startsWith("/") || target.includes("\0") || target.includes("\\")) return false;
|
|
530
|
+
const resolved = posix.normalize(posix.join(posix.dirname(path), target));
|
|
531
|
+
return resolved !== ".." && !resolved.startsWith("../") && !posix.isAbsolute(resolved);
|
|
532
|
+
}
|
|
533
|
+
const common = {
|
|
534
|
+
mount: z.number().int().min(0).max(15),
|
|
535
|
+
path: z.string().refine(safeCheckpointPath),
|
|
536
|
+
mode: z.number().int().min(0).max(511),
|
|
537
|
+
mtime_ns: z.string().regex(/^(0|[1-9]\d*)$/)
|
|
538
|
+
};
|
|
539
|
+
z.discriminatedUnion("kind", [
|
|
540
|
+
z.strictObject({
|
|
541
|
+
...common,
|
|
542
|
+
kind: z.literal("directory"),
|
|
543
|
+
size: z.literal(0)
|
|
544
|
+
}),
|
|
545
|
+
z.strictObject({
|
|
546
|
+
...common,
|
|
547
|
+
kind: z.literal("file"),
|
|
548
|
+
size: count,
|
|
549
|
+
digest: digest$1
|
|
550
|
+
}),
|
|
551
|
+
z.strictObject({
|
|
552
|
+
...common,
|
|
553
|
+
kind: z.literal("symlink"),
|
|
554
|
+
mode: z.literal(511),
|
|
555
|
+
size: count,
|
|
556
|
+
digest: digest$1,
|
|
557
|
+
link_target: z.string()
|
|
558
|
+
}).refine((entry) => safeCheckpointLink(entry.path, entry.link_target) && entry.size === Buffer.byteLength(entry.link_target) && entry.digest === `sha256:${sha256Hex(entry.link_target)}`)
|
|
559
|
+
]);
|
|
560
|
+
z.strictObject({
|
|
561
|
+
mounts,
|
|
562
|
+
manifest_digest: digest$1,
|
|
563
|
+
content_digest: digest$1
|
|
564
|
+
});
|
|
153
565
|
//#endregion
|
|
154
566
|
//#region ../contracts/src/common/errors.ts
|
|
155
567
|
const ERROR_CODES = {
|
|
@@ -157,13 +569,16 @@ const ERROR_CODES = {
|
|
|
157
569
|
"auth.missing_scope": 403,
|
|
158
570
|
"auth.disabled_principal": 403,
|
|
159
571
|
"principal.not_found": 404,
|
|
572
|
+
"principal.name_conflict": 409,
|
|
160
573
|
"key.not_found": 404,
|
|
574
|
+
"request.body_too_large": 413,
|
|
161
575
|
"validation.invalid": 400,
|
|
162
576
|
"idempotency.conflict": 409,
|
|
163
577
|
"capacity.queue_full": 429,
|
|
164
578
|
"capacity.waiters_full": 429,
|
|
165
579
|
"template.not_found": 404,
|
|
166
580
|
"template.version_not_found": 404,
|
|
581
|
+
"template.version_immutable": 409,
|
|
167
582
|
"template.not_authorized": 403,
|
|
168
583
|
"workspace.not_found": 404,
|
|
169
584
|
"workspace.external_id_conflict": 409,
|
|
@@ -178,6 +593,10 @@ const ERROR_CODES = {
|
|
|
178
593
|
"workspace.persistence_not_enabled": 409,
|
|
179
594
|
"workspace.preserving": 409,
|
|
180
595
|
"checkpoint.not_found": 404,
|
|
596
|
+
"checkpoint.unsupported": 409,
|
|
597
|
+
"artifact.not_found": 404,
|
|
598
|
+
"artifact.workspace_active": 409,
|
|
599
|
+
"artifact.too_large": 413,
|
|
181
600
|
"checkpoint.not_ready": 409,
|
|
182
601
|
"checkpoint.none_ready": 409,
|
|
183
602
|
"checkpoint.corrupt": 409,
|
|
@@ -187,24 +606,35 @@ const ERROR_CODES = {
|
|
|
187
606
|
"restore.image_unavailable": 409,
|
|
188
607
|
"restore.incompatible": 409,
|
|
189
608
|
"resume.unsupported": 409,
|
|
609
|
+
"conversation.not_found": 404,
|
|
190
610
|
"conversation.expired": 410,
|
|
191
611
|
"conversation.deleted": 410,
|
|
192
612
|
"operation.conflict": 409,
|
|
193
613
|
"source.not_allowed": 422,
|
|
194
614
|
"source.invalid_revision": 422,
|
|
195
615
|
"secret.unavailable": 503,
|
|
616
|
+
"secret.not_found": 404,
|
|
196
617
|
"storage.capacity_exhausted": 507,
|
|
197
618
|
"relay.body_too_large": 413,
|
|
198
619
|
"relay.route_not_allowed": 422,
|
|
199
620
|
"relay.streaming_unsupported": 409,
|
|
200
621
|
"relay.deadline_exceeded": 504,
|
|
201
622
|
"relay.upstream_error": 502,
|
|
623
|
+
"agent.interrupt_unsupported": 409,
|
|
202
624
|
"attachment.invalid": 400,
|
|
203
625
|
"attachment.too_large": 413,
|
|
204
626
|
"attachment.not_found": 404,
|
|
205
627
|
"attachment.conflict": 409,
|
|
206
628
|
"attachment.unsupported": 409,
|
|
207
629
|
"attachment.interrupted": 503,
|
|
630
|
+
"maintenance.active": 503,
|
|
631
|
+
"maintenance.timeout": 503,
|
|
632
|
+
"backup.failed": 409,
|
|
633
|
+
"journal.pending": 503,
|
|
634
|
+
"account.purged": 409,
|
|
635
|
+
"account.purge_pending": 503,
|
|
636
|
+
"recovery.required": 503,
|
|
637
|
+
"recovery.failed": 409,
|
|
208
638
|
"internal.error": 500
|
|
209
639
|
};
|
|
210
640
|
const errorCodes = Object.keys(ERROR_CODES);
|
|
@@ -215,6 +645,9 @@ const ErrorEnvelopeSchema = z.object({ error: z.object({
|
|
|
215
645
|
details: z.record(z.string(), z.unknown()).optional()
|
|
216
646
|
}) });
|
|
217
647
|
//#endregion
|
|
648
|
+
//#region ../contracts/src/common/json-value.ts
|
|
649
|
+
const JsonValueSchema = z.json();
|
|
650
|
+
//#endregion
|
|
218
651
|
//#region ../contracts/src/common/pagination.ts
|
|
219
652
|
const CursorSchema = z.string().min(1).max(2048);
|
|
220
653
|
function CursorPageSchema(item) {
|
|
@@ -231,6 +664,7 @@ function CursorQuerySchema(maxLimit, defaultLimit) {
|
|
|
231
664
|
}
|
|
232
665
|
//#endregion
|
|
233
666
|
//#region ../contracts/src/conversations/facts.ts
|
|
667
|
+
const ProducerCodeSchema = z.string().regex(/^[a-z][a-z0-9_]{0,63}$/);
|
|
234
668
|
const ConversationFactSchema = z.discriminatedUnion("type", [
|
|
235
669
|
z.object({ type: z.literal("intermediate") }),
|
|
236
670
|
z.object({ type: z.literal("final_response") }),
|
|
@@ -245,7 +679,9 @@ const ConversationFactSchema = z.discriminatedUnion("type", [
|
|
|
245
679
|
"retrieval"
|
|
246
680
|
]),
|
|
247
681
|
label: z.string().min(1).max(160),
|
|
248
|
-
started_at: z.iso.datetime().nullable()
|
|
682
|
+
started_at: z.iso.datetime().nullable(),
|
|
683
|
+
action: ProducerCodeSchema.optional(),
|
|
684
|
+
subject: ProducerCodeSchema.optional()
|
|
249
685
|
}),
|
|
250
686
|
z.object({
|
|
251
687
|
type: z.literal("activity_finished"),
|
|
@@ -266,7 +702,8 @@ const ConversationFactSchema = z.discriminatedUnion("type", [
|
|
|
266
702
|
"interrupted",
|
|
267
703
|
"canceled",
|
|
268
704
|
"lost"
|
|
269
|
-
])
|
|
705
|
+
]),
|
|
706
|
+
reason: ProducerCodeSchema.optional()
|
|
270
707
|
})
|
|
271
708
|
]);
|
|
272
709
|
//#endregion
|
|
@@ -332,6 +769,160 @@ const ConversationResumeOutcomeSchema = z.object({
|
|
|
332
769
|
checkpoint_id: z.uuid()
|
|
333
770
|
});
|
|
334
771
|
//#endregion
|
|
772
|
+
//#region ../contracts/src/conversations/document.ts
|
|
773
|
+
const CONVERSATION_RECORD_STATUSES = [
|
|
774
|
+
"completed",
|
|
775
|
+
"failed",
|
|
776
|
+
"interrupted",
|
|
777
|
+
"canceled",
|
|
778
|
+
"lost",
|
|
779
|
+
"message",
|
|
780
|
+
"legacy"
|
|
781
|
+
];
|
|
782
|
+
const ConversationActivitySchema = z.object({
|
|
783
|
+
activity_id: z.uuid(),
|
|
784
|
+
kind: z.string(),
|
|
785
|
+
label: z.string(),
|
|
786
|
+
action: z.string().nullable(),
|
|
787
|
+
subject: z.string().nullable(),
|
|
788
|
+
started_at: z.iso.datetime().nullable(),
|
|
789
|
+
ended_at: z.iso.datetime().nullable(),
|
|
790
|
+
outcome: z.string().nullable()
|
|
791
|
+
});
|
|
792
|
+
z.object({
|
|
793
|
+
schema: z.literal("pocketcoder.conversation-record/v1"),
|
|
794
|
+
turn_id: z.uuid().nullable(),
|
|
795
|
+
request: ConversationRequestIdentitySchema.nullable(),
|
|
796
|
+
status: z.enum(CONVERSATION_RECORD_STATUSES),
|
|
797
|
+
reason: z.string().nullable(),
|
|
798
|
+
messages: z.array(ConversationMessageResourceSchema),
|
|
799
|
+
activities: z.array(ConversationActivitySchema),
|
|
800
|
+
final_response: z.string().nullable(),
|
|
801
|
+
started_at: z.iso.datetime().nullable(),
|
|
802
|
+
ended_at: z.iso.datetime().nullable()
|
|
803
|
+
});
|
|
804
|
+
const ConversationSummarySchema = z.object({
|
|
805
|
+
conversation_id: z.uuid().nullable(),
|
|
806
|
+
workspace_id: z.uuid(),
|
|
807
|
+
legacy: z.boolean(),
|
|
808
|
+
status: z.enum([
|
|
809
|
+
"retained",
|
|
810
|
+
"deleted",
|
|
811
|
+
"expired"
|
|
812
|
+
]),
|
|
813
|
+
message_count: z.number().int().nonnegative(),
|
|
814
|
+
turn_count: z.number().int().nonnegative(),
|
|
815
|
+
last_activity_at: z.iso.datetime(),
|
|
816
|
+
expires_at: z.iso.datetime().nullable()
|
|
817
|
+
});
|
|
818
|
+
const ConversationListSchema = z.object({ items: z.array(ConversationSummarySchema) });
|
|
819
|
+
const ConversationDocumentPageSchema = CursorPageSchema(ConversationMessageResourceSchema).extend({
|
|
820
|
+
conversation: ConversationSummarySchema,
|
|
821
|
+
protocol: ConversationProtocolSupportSchema
|
|
822
|
+
});
|
|
823
|
+
const ConversationLiveEventSchema = z.discriminatedUnion("type", [z.object({
|
|
824
|
+
type: z.literal("turn"),
|
|
825
|
+
conversation_id: z.uuid(),
|
|
826
|
+
turn_id: z.uuid(),
|
|
827
|
+
messages: z.array(ConversationMessageResourceSchema.omit({ seq: true }))
|
|
828
|
+
}), z.object({
|
|
829
|
+
type: z.literal("committed"),
|
|
830
|
+
conversation_id: z.uuid().nullable(),
|
|
831
|
+
turn_id: z.uuid().nullable(),
|
|
832
|
+
status: z.enum(CONVERSATION_RECORD_STATUSES),
|
|
833
|
+
last_seq: z.number().int().nonnegative()
|
|
834
|
+
})]);
|
|
835
|
+
//#endregion
|
|
836
|
+
//#region ../contracts/src/previews/view-session.ts
|
|
837
|
+
const HttpsOriginSchema = z.string().refine((value) => {
|
|
838
|
+
try {
|
|
839
|
+
const url = new URL(value);
|
|
840
|
+
return url.protocol === "https:" && url.origin === value && !url.username && !url.password;
|
|
841
|
+
} catch {
|
|
842
|
+
return false;
|
|
843
|
+
}
|
|
844
|
+
}, "An exact HTTPS origin is required.");
|
|
845
|
+
const ViewSessionSchema = z.discriminatedUnion("mode", [
|
|
846
|
+
z.strictObject({ mode: z.literal("local") }),
|
|
847
|
+
z.strictObject({ mode: z.literal("top_level") }),
|
|
848
|
+
z.strictObject({
|
|
849
|
+
mode: z.literal("embedded"),
|
|
850
|
+
parentOrigin: HttpsOriginSchema
|
|
851
|
+
})
|
|
852
|
+
]);
|
|
853
|
+
z.strictObject({ session: ViewSessionSchema.default({ mode: "local" }) });
|
|
854
|
+
//#endregion
|
|
855
|
+
//#region ../contracts/src/displays/display.ts
|
|
856
|
+
const DisplaySchema = z.strictObject({ mode: z.enum(["desktop", "browser"]) });
|
|
857
|
+
z.strictObject({
|
|
858
|
+
control: z.boolean().default(false),
|
|
859
|
+
session: ViewSessionSchema.default({ mode: "local" })
|
|
860
|
+
});
|
|
861
|
+
const BROWSER_WIDTH = 1280;
|
|
862
|
+
const coordinate = (max) => z.number().int().min(0).max(max - 1);
|
|
863
|
+
const url = z.string().max(2048).refine((value) => {
|
|
864
|
+
try {
|
|
865
|
+
const parsed = new URL(value);
|
|
866
|
+
return ["http:", "https:"].includes(parsed.protocol) && !parsed.username && !parsed.password;
|
|
867
|
+
} catch {
|
|
868
|
+
return false;
|
|
869
|
+
}
|
|
870
|
+
}, "Browser navigation requires an HTTP or HTTPS URL without credentials.");
|
|
871
|
+
z.discriminatedUnion("action", [
|
|
872
|
+
z.strictObject({
|
|
873
|
+
action: z.literal("navigate"),
|
|
874
|
+
url
|
|
875
|
+
}),
|
|
876
|
+
z.strictObject({
|
|
877
|
+
action: z.literal("click"),
|
|
878
|
+
x: coordinate(BROWSER_WIDTH),
|
|
879
|
+
y: coordinate(800)
|
|
880
|
+
}),
|
|
881
|
+
z.strictObject({
|
|
882
|
+
action: z.literal("text"),
|
|
883
|
+
text: z.string().min(1).max(1024)
|
|
884
|
+
}),
|
|
885
|
+
z.strictObject({
|
|
886
|
+
action: z.literal("key"),
|
|
887
|
+
key: z.enum([
|
|
888
|
+
"Enter",
|
|
889
|
+
"Tab",
|
|
890
|
+
"Backspace",
|
|
891
|
+
"Delete",
|
|
892
|
+
"Escape",
|
|
893
|
+
"ArrowLeft",
|
|
894
|
+
"ArrowRight",
|
|
895
|
+
"ArrowUp",
|
|
896
|
+
"ArrowDown",
|
|
897
|
+
"Home",
|
|
898
|
+
"End",
|
|
899
|
+
"PageUp",
|
|
900
|
+
"PageDown"
|
|
901
|
+
])
|
|
902
|
+
}),
|
|
903
|
+
z.strictObject({
|
|
904
|
+
action: z.literal("scroll"),
|
|
905
|
+
x: coordinate(BROWSER_WIDTH),
|
|
906
|
+
y: coordinate(800),
|
|
907
|
+
deltaY: z.number().int().min(-1e3).max(1e3)
|
|
908
|
+
})
|
|
909
|
+
]);
|
|
910
|
+
const ScreenshotResourceSchema = z.strictObject({
|
|
911
|
+
kind: z.literal("screenshot"),
|
|
912
|
+
id: z.uuid(),
|
|
913
|
+
workspace_id: z.uuid(),
|
|
914
|
+
content_type: z.literal("image/png"),
|
|
915
|
+
bytes: z.number().int().min(1).max(4 * 1024 ** 2),
|
|
916
|
+
digest: z.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
917
|
+
expires_at: z.iso.datetime()
|
|
918
|
+
});
|
|
919
|
+
const ScreenshotCapturePayload = z.strictObject({
|
|
920
|
+
output_id: z.uuid(),
|
|
921
|
+
credential: z.string().min(32).max(128),
|
|
922
|
+
url: z.url(),
|
|
923
|
+
expires_at: z.iso.datetime()
|
|
924
|
+
});
|
|
925
|
+
//#endregion
|
|
335
926
|
//#region ../contracts/src/network/network.ts
|
|
336
927
|
const DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
337
928
|
function isDomain(value) {
|
|
@@ -386,151 +977,153 @@ const NetworkEventSchema = NetworkEventInputSchema.omit({ source_seq: true }).ex
|
|
|
386
977
|
CursorQuerySchema(200, 100);
|
|
387
978
|
const NetworkEventPageSchema = CursorPageSchema(NetworkEventSchema);
|
|
388
979
|
//#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" })
|
|
980
|
+
//#region ../contracts/src/workspaces/workload-operation.ts
|
|
981
|
+
const WorkloadExecutionReferenceSchema = z.strictObject({
|
|
982
|
+
workspaceId: z.uuid(),
|
|
983
|
+
incarnationId: z.string().min(1).max(256),
|
|
984
|
+
owner: z.enum(["process", "conversation-request"]),
|
|
985
|
+
requestId: z.uuid()
|
|
404
986
|
});
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
987
|
+
const WorkloadAcceptanceSchema = z.strictObject({
|
|
988
|
+
execution: WorkloadExecutionReferenceSchema,
|
|
989
|
+
callerPrincipalId: z.uuid(),
|
|
990
|
+
templateDigest: WorkloadDigestSchema,
|
|
991
|
+
adapterDigest: WorkloadDigestSchema,
|
|
992
|
+
operation: WorkloadNameSchema,
|
|
993
|
+
input: z.record(z.string(), JsonValueSchema),
|
|
994
|
+
inputs: AcceptedInputManifestSchema,
|
|
995
|
+
bindingSetDigest: WorkloadDigestSchema,
|
|
996
|
+
fingerprint: WorkloadDigestSchema
|
|
997
|
+
}).superRefine((value, ctx) => {
|
|
998
|
+
const { fingerprint, ...facts } = value;
|
|
999
|
+
if (fingerprint !== digestOf(facts)) ctx.addIssue({
|
|
1000
|
+
code: "custom",
|
|
1001
|
+
path: ["fingerprint"],
|
|
1002
|
+
message: "acceptance fingerprint does not match its facts"
|
|
1003
|
+
});
|
|
1004
|
+
if (value.inputs.workspaceId !== value.execution.workspaceId || value.inputs.incarnationId !== value.execution.incarnationId) ctx.addIssue({
|
|
1005
|
+
code: "custom",
|
|
1006
|
+
path: ["inputs"],
|
|
1007
|
+
message: "accepted input identity must match the execution"
|
|
1008
|
+
});
|
|
410
1009
|
});
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
maxFiles: z.number().int().positive().max(1e7)
|
|
1010
|
+
const WorkloadCancelSchema = z.strictObject({
|
|
1011
|
+
execution: WorkloadExecutionReferenceSchema,
|
|
1012
|
+
cancelId: z.uuid(),
|
|
1013
|
+
scope: z.enum(["operation", "workspace"])
|
|
416
1014
|
});
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
retention: DurationValueSchema.default("168h")
|
|
1015
|
+
//#endregion
|
|
1016
|
+
//#region ../contracts/src/outputs/export-reference.ts
|
|
1017
|
+
const ExportObjectReferenceSchema = z.strictObject({
|
|
1018
|
+
objectId: z.uuid(),
|
|
1019
|
+
objectVersion: WorkloadDigestSchema,
|
|
1020
|
+
name: z.string().min(1).max(255),
|
|
1021
|
+
sizeBytes: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
1022
|
+
mediaType: AttachmentMediaTypeSchema,
|
|
1023
|
+
digest: WorkloadDigestSchema
|
|
427
1024
|
});
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
1025
|
+
z.strictObject({
|
|
1026
|
+
exportId: z.uuid(),
|
|
1027
|
+
execution: WorkloadExecutionReferenceSchema,
|
|
1028
|
+
manifestDigest: WorkloadDigestSchema,
|
|
1029
|
+
completeness: z.enum([
|
|
1030
|
+
"complete",
|
|
1031
|
+
"partial",
|
|
1032
|
+
"lost"
|
|
1033
|
+
]),
|
|
1034
|
+
publication: z.enum([
|
|
1035
|
+
"published",
|
|
1036
|
+
"unavailable",
|
|
1037
|
+
"expired"
|
|
1038
|
+
]),
|
|
1039
|
+
objects: z.array(ExportObjectReferenceSchema),
|
|
1040
|
+
errors: z.array(z.string().min(1).max(512)),
|
|
1041
|
+
publishedAt: z.iso.datetime(),
|
|
1042
|
+
expiresAt: z.iso.datetime()
|
|
1043
|
+
}).superRefine((value, ctx) => {
|
|
1044
|
+
const { execution, objects, completeness, errors } = value;
|
|
1045
|
+
if (value.manifestDigest !== digestOf({
|
|
1046
|
+
execution,
|
|
1047
|
+
objects,
|
|
1048
|
+
completeness,
|
|
1049
|
+
errors
|
|
1050
|
+
})) ctx.addIssue({
|
|
1051
|
+
code: "custom",
|
|
1052
|
+
path: ["manifestDigest"],
|
|
1053
|
+
message: "export digest does not match its frozen manifest"
|
|
1054
|
+
});
|
|
1055
|
+
if (Date.parse(value.expiresAt) <= Date.parse(value.publishedAt)) ctx.addIssue({
|
|
1056
|
+
code: "custom",
|
|
1057
|
+
path: ["expiresAt"],
|
|
1058
|
+
message: "export retention must be positive"
|
|
1059
|
+
});
|
|
1060
|
+
const ids = value.objects.map((object) => object.objectId);
|
|
1061
|
+
if (new Set(ids).size !== ids.length) ctx.addIssue({
|
|
1062
|
+
code: "custom",
|
|
1063
|
+
path: ["objects"],
|
|
1064
|
+
message: "export object IDs must be unique"
|
|
1065
|
+
});
|
|
434
1066
|
});
|
|
435
|
-
|
|
436
|
-
|
|
1067
|
+
//#endregion
|
|
1068
|
+
//#region ../contracts/src/previews/preview.ts
|
|
1069
|
+
const PreviewNameSchema = z.string().regex(/^[a-z][a-z0-9-]{0,19}$/).refine((name) => !(/* @__PURE__ */ new Set([
|
|
1070
|
+
"display",
|
|
1071
|
+
"control",
|
|
1072
|
+
"agent",
|
|
1073
|
+
"supervisor",
|
|
1074
|
+
"vnc",
|
|
1075
|
+
"cdp"
|
|
1076
|
+
])).has(name) && !name.startsWith("pc-"), "reserved preview name");
|
|
1077
|
+
const RESERVED_PORTS = /* @__PURE__ */ new Set([
|
|
1078
|
+
3284,
|
|
1079
|
+
5900,
|
|
1080
|
+
5901,
|
|
1081
|
+
6080,
|
|
1082
|
+
9222,
|
|
1083
|
+
9229
|
|
1084
|
+
]);
|
|
1085
|
+
const PreviewSchema = z.strictObject({ port: z.number().int().min(1024).max(65535).refine((port) => !RESERVED_PORTS.has(port), "reserved port") });
|
|
1086
|
+
const PreviewsSchema = z.record(PreviewNameSchema, PreviewSchema).refine((previews) => Object.keys(previews).length <= 16, "at most 16 previews");
|
|
1087
|
+
const PreviewOpenResponseSchema = z.object({
|
|
1088
|
+
url: z.url(),
|
|
1089
|
+
expires_at: z.iso.datetime()
|
|
1090
|
+
});
|
|
1091
|
+
const PreviewListSchema = z.array(z.object({
|
|
1092
|
+
name: PreviewNameSchema,
|
|
1093
|
+
port: z.number().int()
|
|
1094
|
+
}));
|
|
1095
|
+
const PreviewSocketPayload = z.discriminatedUnion("op", [
|
|
437
1096
|
z.object({
|
|
438
|
-
|
|
439
|
-
|
|
1097
|
+
op: z.literal("open"),
|
|
1098
|
+
request_id: z.uuid(),
|
|
1099
|
+
name: z.union([PreviewNameSchema, z.literal("display")]),
|
|
1100
|
+
path: z.string().max(8192),
|
|
1101
|
+
origin: z.url(),
|
|
1102
|
+
cookie: z.string().max(8192).optional(),
|
|
1103
|
+
protocols: z.array(z.string().max(128)).max(16)
|
|
440
1104
|
}),
|
|
441
1105
|
z.object({
|
|
442
|
-
|
|
443
|
-
|
|
1106
|
+
op: z.literal("ready"),
|
|
1107
|
+
request_id: z.uuid(),
|
|
1108
|
+
protocol: z.string().max(128)
|
|
1109
|
+
}),
|
|
1110
|
+
z.object({
|
|
1111
|
+
op: z.literal("data"),
|
|
1112
|
+
request_id: z.uuid(),
|
|
1113
|
+
seq: z.number().int().nonnegative(),
|
|
1114
|
+
binary: z.boolean(),
|
|
1115
|
+
content_b64: z.string().max(87384)
|
|
1116
|
+
}),
|
|
1117
|
+
z.object({
|
|
1118
|
+
op: z.literal("ack"),
|
|
1119
|
+
request_id: z.uuid(),
|
|
1120
|
+
seq: z.number().int().nonnegative()
|
|
1121
|
+
}),
|
|
1122
|
+
z.object({
|
|
1123
|
+
op: z.literal("close"),
|
|
1124
|
+
request_id: z.uuid()
|
|
444
1125
|
})
|
|
445
1126
|
]);
|
|
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
1127
|
//#endregion
|
|
535
1128
|
//#region ../contracts/src/workspaces/workspace.ts
|
|
536
1129
|
const WORKSPACE_STATES = [
|
|
@@ -554,6 +1147,15 @@ const TERMINAL_STATES = [
|
|
|
554
1147
|
"preserved"
|
|
555
1148
|
];
|
|
556
1149
|
const REASON_CODES = [
|
|
1150
|
+
...[
|
|
1151
|
+
"admission_global_capacity",
|
|
1152
|
+
"admission_principal_capacity",
|
|
1153
|
+
"admission_template_capacity",
|
|
1154
|
+
"admission_policy_global_capacity",
|
|
1155
|
+
"admission_actor_capacity",
|
|
1156
|
+
"admission_denied",
|
|
1157
|
+
"admission_unavailable"
|
|
1158
|
+
],
|
|
557
1159
|
"child_exit_success",
|
|
558
1160
|
"child_exit_failure",
|
|
559
1161
|
"setup_failed",
|
|
@@ -588,6 +1190,7 @@ const AGENT_STATES = [
|
|
|
588
1190
|
"running",
|
|
589
1191
|
"stable"
|
|
590
1192
|
];
|
|
1193
|
+
const AgentInterruptResponseSchema = z.object({ status: z.enum(["requested", "idle"]) });
|
|
591
1194
|
z.object({
|
|
592
1195
|
external_id: z.string().min(1).max(256),
|
|
593
1196
|
template: z.object({
|
|
@@ -603,10 +1206,15 @@ const TemplateRefSchema = z.object({
|
|
|
603
1206
|
version: z.string(),
|
|
604
1207
|
digest: z.string()
|
|
605
1208
|
});
|
|
1209
|
+
const AgentMetadataSchema = z.object({
|
|
1210
|
+
type: z.string(),
|
|
1211
|
+
model: z.string().min(1).max(255).nullable()
|
|
1212
|
+
});
|
|
606
1213
|
const WorkspaceResourceSchema = z.object({
|
|
607
1214
|
id: z.uuid(),
|
|
608
1215
|
external_id: z.string(),
|
|
609
1216
|
template: TemplateRefSchema,
|
|
1217
|
+
agent: AgentMetadataSchema.nullable(),
|
|
610
1218
|
state: z.enum(WORKSPACE_STATES),
|
|
611
1219
|
reason_code: z.enum(REASON_CODES).nullable(),
|
|
612
1220
|
agent_state: z.enum(AGENT_STATES),
|
|
@@ -660,19 +1268,21 @@ z.object({
|
|
|
660
1268
|
limit: z.coerce.number().int().positive().max(200).default(50),
|
|
661
1269
|
cursor: z.string().optional()
|
|
662
1270
|
});
|
|
663
|
-
|
|
664
|
-
//#region ../contracts/src/protocol/api.ts
|
|
665
|
-
const TemplatePageSchema = CursorPageSchema(z.object({
|
|
1271
|
+
const TemplateListItemSchema = z.object({
|
|
666
1272
|
name: z.string(),
|
|
667
1273
|
version: z.string(),
|
|
668
1274
|
digest: z.string(),
|
|
1275
|
+
agent: AgentMetadataSchema.nullable(),
|
|
669
1276
|
description: z.string().optional(),
|
|
670
1277
|
status: z.enum([
|
|
671
1278
|
"active",
|
|
672
1279
|
"available",
|
|
673
1280
|
"retired"
|
|
674
1281
|
])
|
|
675
|
-
})
|
|
1282
|
+
});
|
|
1283
|
+
//#endregion
|
|
1284
|
+
//#region ../contracts/src/protocol/api.ts
|
|
1285
|
+
const TemplatePageSchema = CursorPageSchema(TemplateListItemSchema);
|
|
676
1286
|
CursorQuerySchema(200, 100);
|
|
677
1287
|
const CheckpointPageSchema = CursorPageSchema(CheckpointResourceSchema);
|
|
678
1288
|
CursorQuerySchema(200, 50).extend({ state: z.enum(CHECKPOINT_STATES).optional() });
|
|
@@ -762,6 +1372,52 @@ const LogChunkSchema = z.object({
|
|
|
762
1372
|
});
|
|
763
1373
|
CursorQuerySchema(1e3, 200);
|
|
764
1374
|
const LogPageSchema = CursorPageSchema(LogChunkSchema);
|
|
1375
|
+
const SecretTypeSchema = z.enum([
|
|
1376
|
+
"setup-issuer",
|
|
1377
|
+
"runtime-issuer",
|
|
1378
|
+
"registry"
|
|
1379
|
+
]);
|
|
1380
|
+
const SecretNameSchema = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/);
|
|
1381
|
+
z.object({ name: SecretNameSchema });
|
|
1382
|
+
const issuerUrl = z.url().refine((value) => {
|
|
1383
|
+
if (!URL.canParse(value)) return false;
|
|
1384
|
+
const url = new URL(value);
|
|
1385
|
+
return url.protocol === "https:" && !url.username && !url.password && !url.search && !url.hash;
|
|
1386
|
+
});
|
|
1387
|
+
const IssuerConfigSchema = z.strictObject({
|
|
1388
|
+
url: issuerUrl,
|
|
1389
|
+
authorization: z.string().min(1).max(16384),
|
|
1390
|
+
policy: z.record(z.string(), z.json()).meta({
|
|
1391
|
+
type: "object",
|
|
1392
|
+
additionalProperties: true
|
|
1393
|
+
})
|
|
1394
|
+
});
|
|
1395
|
+
const RegistryConfigSchema = z.strictObject({
|
|
1396
|
+
server: z.string().max(255).regex(/^[A-Za-z0-9.-]+(?::[0-9]{1,5})?$/),
|
|
1397
|
+
username: z.string().min(1).max(1024),
|
|
1398
|
+
password: z.string().min(1).max(16384)
|
|
1399
|
+
});
|
|
1400
|
+
z.discriminatedUnion("type", [
|
|
1401
|
+
z.strictObject({
|
|
1402
|
+
type: z.literal("setup-issuer"),
|
|
1403
|
+
value: IssuerConfigSchema
|
|
1404
|
+
}),
|
|
1405
|
+
z.strictObject({
|
|
1406
|
+
type: z.literal("runtime-issuer"),
|
|
1407
|
+
value: IssuerConfigSchema
|
|
1408
|
+
}),
|
|
1409
|
+
z.strictObject({
|
|
1410
|
+
type: z.literal("registry"),
|
|
1411
|
+
value: RegistryConfigSchema
|
|
1412
|
+
})
|
|
1413
|
+
]);
|
|
1414
|
+
const SecretResourceSchema = z.strictObject({
|
|
1415
|
+
name: SecretNameSchema,
|
|
1416
|
+
type: SecretTypeSchema,
|
|
1417
|
+
updated_at: z.iso.datetime(),
|
|
1418
|
+
retired_at: z.iso.datetime().nullable()
|
|
1419
|
+
});
|
|
1420
|
+
const SecretListSchema = z.strictObject({ items: z.array(SecretResourceSchema) });
|
|
765
1421
|
//#endregion
|
|
766
1422
|
//#region ../contracts/src/templates/template-constants.ts
|
|
767
1423
|
function isAbsolutePath(value) {
|
|
@@ -770,7 +1426,7 @@ function isAbsolutePath(value) {
|
|
|
770
1426
|
const SECRET_ENV_PATTERN = /(SECRET|TOKEN|PASSWORD|PASSWD|API_?KEY|PRIVATE_?KEY|CREDENTIAL)/i;
|
|
771
1427
|
const SECRET_REFERENCE_PREFIX = "secretRef:";
|
|
772
1428
|
//#endregion
|
|
773
|
-
//#region ../contracts/src/templates/template-schema.ts
|
|
1429
|
+
//#region ../contracts/src/templates/template-alpha-schema.ts
|
|
774
1430
|
const NAME_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
775
1431
|
const SEMVER_RE = /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;
|
|
776
1432
|
const IMAGE_DIGEST_RE = /^[^\s@]+@sha256:[0-9a-f]{64}$/;
|
|
@@ -797,10 +1453,12 @@ const HarnessSchema = z.object({
|
|
|
797
1453
|
cwd: z.string().refine(isAbsolutePath, "expected an absolute path").optional()
|
|
798
1454
|
});
|
|
799
1455
|
const AgentSchema = HarnessSchema.extend({
|
|
1456
|
+
model: z.string().min(1).max(255).optional().describe("Display metadata only; does not configure the agent model."),
|
|
800
1457
|
type: z.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/).default("custom"),
|
|
801
1458
|
transport: z.enum(["pty", "acp"]).default("pty"),
|
|
802
1459
|
termWidth: z.number().int().min(10).max(65535).optional(),
|
|
803
|
-
stateFile: z.string().refine(isAbsolutePath, "expected an absolute path").optional()
|
|
1460
|
+
stateFile: z.string().refine(isAbsolutePath, "expected an absolute path").optional(),
|
|
1461
|
+
interruptKeys: z.string().min(1).max(16).optional()
|
|
804
1462
|
});
|
|
805
1463
|
const TerminalSchema = z.object({
|
|
806
1464
|
command: CommandSchema,
|
|
@@ -883,6 +1541,7 @@ const CheckpointHookSchema = z.object({
|
|
|
883
1541
|
const TemplateSpecSchema = z.object({
|
|
884
1542
|
version: z.string().regex(SEMVER_RE),
|
|
885
1543
|
image: z.string().regex(IMAGE_DIGEST_RE, { message: "image must be digest-pinned (repo@sha256:<64 hex>)" }),
|
|
1544
|
+
imagePullSecret: z.string().refine((value) => value.startsWith("secretRef:") && SecretNameSchema.safeParse(value.slice(10)).success, "imagePullSecret must name a stored secret").optional(),
|
|
886
1545
|
command: CommandSchema.default([
|
|
887
1546
|
"/usr/local/bin/pocketcoder-supervisor",
|
|
888
1547
|
"supervise",
|
|
@@ -893,6 +1552,8 @@ const TemplateSpecSchema = z.object({
|
|
|
893
1552
|
agent: AgentSchema.optional(),
|
|
894
1553
|
harness: HarnessSchema.optional(),
|
|
895
1554
|
terminal: TerminalSchema.optional(),
|
|
1555
|
+
previews: PreviewsSchema.optional(),
|
|
1556
|
+
display: DisplaySchema.optional(),
|
|
896
1557
|
env: EnvSchema.default({}),
|
|
897
1558
|
resources: ResourcesSchema,
|
|
898
1559
|
timeouts: TimeoutsSchema.prefault({}),
|
|
@@ -915,7 +1576,11 @@ const TemplateSpecSchema = z.object({
|
|
|
915
1576
|
message: "either agent or harness is required"
|
|
916
1577
|
});
|
|
917
1578
|
if (!spec.agent) return;
|
|
918
|
-
if (spec.agent.transport === "acp") for (const field of [
|
|
1579
|
+
if (spec.agent.transport === "acp") for (const field of [
|
|
1580
|
+
"termWidth",
|
|
1581
|
+
"stateFile",
|
|
1582
|
+
"interruptKeys"
|
|
1583
|
+
]) {
|
|
919
1584
|
if (spec.agent[field] === void 0) continue;
|
|
920
1585
|
ctx.addIssue({
|
|
921
1586
|
code: "custom",
|
|
@@ -951,7 +1616,7 @@ const TemplateSpecSchema = z.object({
|
|
|
951
1616
|
};
|
|
952
1617
|
});
|
|
953
1618
|
const TemplateManifestBaseSchema = z.object({
|
|
954
|
-
apiVersion: z.literal("pocketcoder.dev/v1alpha1"),
|
|
1619
|
+
apiVersion: z.literal("pocketcoder.dev/v1alpha1", { error: "unsupported template format for the alpha runtime; canonical workloads require an installed workload runtime" }),
|
|
955
1620
|
kind: z.literal("Template"),
|
|
956
1621
|
metadata: z.object({
|
|
957
1622
|
name: z.string().regex(NAME_RE),
|
|
@@ -960,61 +1625,105 @@ const TemplateManifestBaseSchema = z.object({
|
|
|
960
1625
|
spec: TemplateSpecSchema
|
|
961
1626
|
});
|
|
962
1627
|
//#endregion
|
|
963
|
-
//#region ../contracts/src/
|
|
964
|
-
const
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1628
|
+
//#region ../contracts/src/workspaces/finite-process.ts
|
|
1629
|
+
const ProcessReferenceSchema = WorkloadExecutionReferenceSchema.extend({ owner: z.literal("process") });
|
|
1630
|
+
const FiniteProcessFactSchema = z.strictObject({
|
|
1631
|
+
execution: ProcessReferenceSchema,
|
|
1632
|
+
fingerprint: WorkloadDigestSchema,
|
|
1633
|
+
phase: z.enum([
|
|
1634
|
+
"claimed",
|
|
1635
|
+
"running",
|
|
1636
|
+
"exited",
|
|
1637
|
+
"lost"
|
|
1638
|
+
]),
|
|
1639
|
+
processGroupId: z.number().int().positive().nullable(),
|
|
1640
|
+
claimedAt: z.iso.datetime(),
|
|
1641
|
+
startedAt: z.iso.datetime().nullable(),
|
|
1642
|
+
endedAt: z.iso.datetime().nullable(),
|
|
1643
|
+
exitCode: z.number().int().nullable(),
|
|
1644
|
+
signal: z.string().min(1).nullable()
|
|
1645
|
+
}).superRefine((fact, ctx) => {
|
|
1646
|
+
if ((fact.phase === "exited" || fact.phase === "lost") !== (fact.endedAt !== null)) ctx.addIssue({
|
|
1647
|
+
code: "custom",
|
|
1648
|
+
message: "terminal facts require an end time"
|
|
1649
|
+
});
|
|
1650
|
+
if (fact.phase === "exited" && fact.exitCode === null === (fact.signal === null)) ctx.addIssue({
|
|
1651
|
+
code: "custom",
|
|
1652
|
+
message: "exit requires exactly one observed code or signal"
|
|
1653
|
+
});
|
|
1654
|
+
if (fact.phase !== "exited" && (fact.exitCode !== null || fact.signal !== null)) ctx.addIssue({
|
|
1655
|
+
code: "custom",
|
|
1656
|
+
message: "unobserved exits cannot have a code or signal"
|
|
1657
|
+
});
|
|
1658
|
+
if (fact.phase === "running" && (!fact.processGroupId || !fact.startedAt)) ctx.addIssue({
|
|
1659
|
+
code: "custom",
|
|
1660
|
+
message: "running facts require an owned process group and start time"
|
|
1661
|
+
});
|
|
981
1662
|
});
|
|
982
|
-
const
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1663
|
+
const WaitingRuntimeSpecSchema = z.strictObject({
|
|
1664
|
+
incarnationId: z.string().min(1).max(256),
|
|
1665
|
+
profile: WorkloadProfileSchema,
|
|
1666
|
+
operations: z.record(z.string(), HarnessSchema),
|
|
1667
|
+
infrastructure: z.record(z.string(), HarnessSchema)
|
|
1668
|
+
});
|
|
1669
|
+
const ProcessStartPayload = z.strictObject({
|
|
1670
|
+
execution: ProcessReferenceSchema,
|
|
1671
|
+
fingerprint: WorkloadDigestSchema,
|
|
1672
|
+
input: z.record(z.string(), JsonValueSchema).default({}),
|
|
1673
|
+
inputDirectory: z.string().min(1).optional(),
|
|
1674
|
+
operation: z.string().min(1).max(64)
|
|
1675
|
+
});
|
|
1676
|
+
const ProcessTargetPayload = z.strictObject({ execution: ProcessReferenceSchema });
|
|
1677
|
+
const ProcessCancelPayload = ProcessTargetPayload.extend({
|
|
1678
|
+
cancelId: z.uuid(),
|
|
1679
|
+
signal: z.enum(["SIGTERM", "SIGKILL"]),
|
|
1680
|
+
deadlineAt: z.iso.datetime()
|
|
1681
|
+
});
|
|
1682
|
+
const ProcessCancelResultPayload = ProcessTargetPayload.extend({
|
|
1683
|
+
cancelId: z.uuid(),
|
|
1684
|
+
status: z.enum([
|
|
1685
|
+
"canceled",
|
|
1686
|
+
"requested",
|
|
1687
|
+
"terminal"
|
|
1688
|
+
])
|
|
1689
|
+
});
|
|
1690
|
+
//#endregion
|
|
1691
|
+
//#region ../contracts/src/secrets/credential-slot.ts
|
|
1692
|
+
const CredentialSlotSchema = z.strictObject({
|
|
1693
|
+
policyRef: z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._/-]{0,254}$/).refine((value) => !value.includes("..")),
|
|
1694
|
+
policyVersion: z.string().min(1).max(128),
|
|
1695
|
+
binding: z.enum(["fixed-scope", "post-create-grant"]),
|
|
1696
|
+
delivery: z.literal("workspace-expiring-file"),
|
|
1697
|
+
maxLeaseSeconds: z.number().int().positive().max(Number.MAX_SAFE_INTEGER)
|
|
1698
|
+
});
|
|
1699
|
+
//#endregion
|
|
1700
|
+
//#region ../contracts/src/templates/workload-input-schema.ts
|
|
1701
|
+
const WorkloadInputPropertySchema = z.lazy(() => z.discriminatedUnion("type", [
|
|
1702
|
+
z.strictObject({ type: z.enum([
|
|
1703
|
+
"string",
|
|
1704
|
+
"number",
|
|
1705
|
+
"integer",
|
|
1706
|
+
"boolean",
|
|
1707
|
+
"null"
|
|
1708
|
+
]) }),
|
|
1709
|
+
z.strictObject({
|
|
1710
|
+
type: z.literal("array"),
|
|
1711
|
+
items: WorkloadInputPropertySchema
|
|
1712
|
+
}),
|
|
1713
|
+
WorkloadInputSchema
|
|
1714
|
+
]));
|
|
1715
|
+
const WorkloadInputSchema = z.strictObject({
|
|
1716
|
+
type: z.literal("object"),
|
|
1717
|
+
properties: z.record(z.string(), WorkloadInputPropertySchema),
|
|
1718
|
+
required: z.array(z.string()).default([]),
|
|
1719
|
+
additionalProperties: z.literal(false)
|
|
1720
|
+
}).superRefine((schema, ctx) => {
|
|
1721
|
+
if (new Set(schema.required).size !== schema.required.length || schema.required.some((name) => !Object.hasOwn(schema.properties, name))) ctx.addIssue({
|
|
1722
|
+
code: "custom",
|
|
1723
|
+
path: ["required"],
|
|
1724
|
+
message: "required input properties must be declared and unique"
|
|
1725
|
+
});
|
|
991
1726
|
});
|
|
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
1727
|
//#endregion
|
|
1019
1728
|
//#region ../contracts/src/templates/template-validation.ts
|
|
1020
1729
|
function envSources(spec) {
|
|
@@ -1062,7 +1771,7 @@ function validateNetworkEnvironment(spec, ctx) {
|
|
|
1062
1771
|
});
|
|
1063
1772
|
}
|
|
1064
1773
|
}
|
|
1065
|
-
function validateServices(spec, ctx) {
|
|
1774
|
+
function validateServices$1(spec, ctx) {
|
|
1066
1775
|
for (const [serviceName, service] of Object.entries(templateServices(spec))) {
|
|
1067
1776
|
if (!isLoopbackBaseUrl(service.baseUrl)) ctx.addIssue({
|
|
1068
1777
|
code: "custom",
|
|
@@ -1205,7 +1914,7 @@ const FORBIDDEN_PERSISTENCE_ROOTS = [
|
|
|
1205
1914
|
function pathContains(parent, child) {
|
|
1206
1915
|
return child === parent || child.startsWith(`${parent}/`);
|
|
1207
1916
|
}
|
|
1208
|
-
function
|
|
1917
|
+
function validateTemplatePersistence(spec, ctx) {
|
|
1209
1918
|
const seenNames = /* @__PURE__ */ new Set();
|
|
1210
1919
|
const mounts = spec.persistence.mounts;
|
|
1211
1920
|
for (const [index, mount] of mounts.entries()) validatePersistenceMount(spec, mounts, mount, index, seenNames, ctx);
|
|
@@ -1222,7 +1931,7 @@ function validatePersistence(spec, ctx) {
|
|
|
1222
1931
|
});
|
|
1223
1932
|
}
|
|
1224
1933
|
function validateAgentStateFile(spec, mounts, ctx) {
|
|
1225
|
-
if (!
|
|
1934
|
+
if (!spec.agent) return;
|
|
1226
1935
|
const stateFile = spec.agent.stateFile;
|
|
1227
1936
|
const normalized = stateFile !== void 0 && isNormalizedFilesystemPath(stateFile);
|
|
1228
1937
|
if (stateFile !== void 0 && !normalized) ctx.addIssue({
|
|
@@ -1343,18 +2052,436 @@ function isNormalizedPath(path) {
|
|
|
1343
2052
|
return true;
|
|
1344
2053
|
}
|
|
1345
2054
|
function validateTemplateSpec(spec, ctx) {
|
|
1346
|
-
validateServices(spec, ctx);
|
|
2055
|
+
validateServices$1(spec, ctx);
|
|
2056
|
+
validatePreviews(spec, ctx);
|
|
1347
2057
|
validateEnvironment(spec, ctx);
|
|
1348
2058
|
validateOutputs(spec, ctx);
|
|
1349
2059
|
validateWritableMemoryPaths(spec, ctx);
|
|
1350
|
-
|
|
2060
|
+
validateTemplatePersistence(spec, ctx);
|
|
1351
2061
|
validateNetworkEnvironment(spec, ctx);
|
|
1352
2062
|
validateTerminal(spec, ctx);
|
|
1353
2063
|
}
|
|
1354
|
-
|
|
2064
|
+
function validatePreviews(spec, ctx) {
|
|
2065
|
+
const servicePorts = new Set(Object.values(templateServices(spec)).filter((service) => isLoopbackBaseUrl(service.baseUrl)).map((service) => {
|
|
2066
|
+
const url = new URL(service.baseUrl);
|
|
2067
|
+
return Number(url.port || 80);
|
|
2068
|
+
}));
|
|
2069
|
+
for (const [name, preview] of Object.entries(spec.previews ?? {})) if (servicePorts.has(preview.port)) ctx.addIssue({
|
|
2070
|
+
code: "custom",
|
|
2071
|
+
path: [
|
|
2072
|
+
"spec",
|
|
2073
|
+
"previews",
|
|
2074
|
+
name,
|
|
2075
|
+
"port"
|
|
2076
|
+
],
|
|
2077
|
+
message: "preview port is reserved by a template service"
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
//#endregion
|
|
2081
|
+
//#region ../contracts/src/templates/workload-validation.ts
|
|
2082
|
+
function isWorkloadPath(path) {
|
|
2083
|
+
return path.startsWith("/") && !/[\\,\s]/.test(path) && !path.includes("//") && !path.endsWith("/") && ![...path].some((character) => {
|
|
2084
|
+
const code = character.charCodeAt(0);
|
|
2085
|
+
return code < 32 || code === 127;
|
|
2086
|
+
}) && !path.split("/").some((part) => part === "." || part === "..");
|
|
2087
|
+
}
|
|
2088
|
+
function overlaps(left, right) {
|
|
2089
|
+
return left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
|
|
2090
|
+
}
|
|
2091
|
+
function validateProfile(spec, issue) {
|
|
2092
|
+
const operations = Object.values(spec.operations);
|
|
2093
|
+
if (operations.length === 0 && spec.profile !== "service") issue(["operations"], "at least one operation is required");
|
|
2094
|
+
if (spec.profile === "service" && Object.keys(spec.services).length === 0) issue(["services"], "service profile requires declared transport");
|
|
2095
|
+
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");
|
|
2096
|
+
}
|
|
2097
|
+
function validateSecurity(spec, issue) {
|
|
2098
|
+
if (!spec.security.readOnlyRoot || !spec.security.dropCapabilities.includes("ALL")) issue(["security"], "workload profiles require a read-only root and all capabilities dropped");
|
|
2099
|
+
for (const path of spec.security.writableMemoryPaths) if (!isWorkloadPath(path) || [
|
|
2100
|
+
"/run/pocketcoder",
|
|
2101
|
+
"/proc",
|
|
2102
|
+
"/sys",
|
|
2103
|
+
"/dev"
|
|
2104
|
+
].some((root) => overlaps(path, root))) issue(["security", "writableMemoryPaths"], "writable path overlaps protected storage or is not normalized");
|
|
2105
|
+
}
|
|
2106
|
+
function validateAdapters(spec, issue) {
|
|
2107
|
+
for (const [name, adapter] of Object.entries(spec.adapters)) {
|
|
2108
|
+
const path = ["adapters", name];
|
|
2109
|
+
if (adapter.kind === "service") {
|
|
2110
|
+
if (!adapter.service || !Object.hasOwn(spec.services, adapter.service) || adapter.command) issue(path, "service adapters require a declared service and no process command");
|
|
2111
|
+
} else if (!adapter.command || adapter.service) issue(path, "process, Pi and AgentAPI adapters require a command and no service reference");
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
function validateProcessEnvironment(spec, issue) {
|
|
2115
|
+
const processes = [
|
|
2116
|
+
...spec.lifecycle?.checkpointHook ? [{
|
|
2117
|
+
path: ["lifecycle", "checkpointHook"],
|
|
2118
|
+
process: spec.lifecycle.checkpointHook
|
|
2119
|
+
}] : [],
|
|
2120
|
+
...Object.entries(spec.adapters).map(([name, adapter]) => ({
|
|
2121
|
+
path: ["adapters", name],
|
|
2122
|
+
process: adapter
|
|
2123
|
+
})),
|
|
2124
|
+
...Object.entries(spec.services).flatMap(([name, service]) => service.process ? [{
|
|
2125
|
+
path: [
|
|
2126
|
+
"services",
|
|
2127
|
+
name,
|
|
2128
|
+
"process"
|
|
2129
|
+
],
|
|
2130
|
+
process: service.process
|
|
2131
|
+
}] : [])
|
|
2132
|
+
];
|
|
2133
|
+
for (const { path, process } of processes) {
|
|
2134
|
+
if (process.cwd && process.cwd !== "/" && !isWorkloadPath(process.cwd)) issue(path, "cwd must be normalized");
|
|
2135
|
+
validateEnv(spec, process.env, path, issue);
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
function validateEnv(spec, env, path, issue) {
|
|
2139
|
+
for (const [key, value] of Object.entries(env)) {
|
|
2140
|
+
if (value.startsWith("credentialSlot:")) {
|
|
2141
|
+
if (!Object.hasOwn(spec.credentialSlots, value.slice(15))) issue([
|
|
2142
|
+
...path,
|
|
2143
|
+
"env",
|
|
2144
|
+
key
|
|
2145
|
+
], "unknown credential slot");
|
|
2146
|
+
} else if (SECRET_ENV_PATTERN.test(key) && value !== "" || value.startsWith("secretRef:")) issue([
|
|
2147
|
+
...path,
|
|
2148
|
+
"env",
|
|
2149
|
+
key
|
|
2150
|
+
], "runtime credentials require a credentialSlot: reference");
|
|
2151
|
+
if (spec.network.mode === "restricted" && /^(http_proxy|https_proxy|all_proxy|no_proxy)$/i.test(key)) issue([
|
|
2152
|
+
...path,
|
|
2153
|
+
"env",
|
|
2154
|
+
key
|
|
2155
|
+
], "proxy variables are reserved by restricted networking");
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
function validateOperations(spec, issue) {
|
|
2159
|
+
for (const [name, operation] of Object.entries(spec.operations)) {
|
|
2160
|
+
const adapter = Object.hasOwn(spec.adapters, operation.adapter) ? spec.adapters[operation.adapter] : void 0;
|
|
2161
|
+
if (!adapter) issue([
|
|
2162
|
+
"operations",
|
|
2163
|
+
name,
|
|
2164
|
+
"adapter"
|
|
2165
|
+
], "operation must name a pinned adapter");
|
|
2166
|
+
if (spec.profile === "cloud-interactive" && adapter?.kind === "process") issue(["operations", name], "cloud-interactive requires request-aware adapters");
|
|
2167
|
+
for (const root of operation.outputRoots) if (!isWorkloadPath(root) || [
|
|
2168
|
+
"/run",
|
|
2169
|
+
"/proc",
|
|
2170
|
+
"/sys",
|
|
2171
|
+
"/dev"
|
|
2172
|
+
].some((path) => overlaps(path, root))) issue([
|
|
2173
|
+
"operations",
|
|
2174
|
+
name,
|
|
2175
|
+
"outputRoots"
|
|
2176
|
+
], "output roots must be normalized and outside protected storage");
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
function serviceUrl(value) {
|
|
2180
|
+
try {
|
|
2181
|
+
return new URL(value);
|
|
2182
|
+
} catch {
|
|
2183
|
+
return null;
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
function isLoopbackService(url) {
|
|
2187
|
+
return url.protocol === "http:" && [
|
|
2188
|
+
"127.0.0.1",
|
|
2189
|
+
"localhost",
|
|
2190
|
+
"[::1]"
|
|
2191
|
+
].includes(url.hostname) && !url.username && !url.password && url.pathname === "/" && !url.search && !url.hash;
|
|
2192
|
+
}
|
|
2193
|
+
function validateServices(spec, issue) {
|
|
2194
|
+
for (const [name, service] of Object.entries(spec.services)) {
|
|
2195
|
+
const url = serviceUrl(service.baseUrl);
|
|
2196
|
+
if (!url || !isLoopbackService(url)) {
|
|
2197
|
+
issue(["services", name], "service must use loopback HTTP without credentials or a path");
|
|
2198
|
+
continue;
|
|
2199
|
+
}
|
|
2200
|
+
const routes = /* @__PURE__ */ new Set();
|
|
2201
|
+
if (!isNormalizedPath(service.healthPath)) issue([
|
|
2202
|
+
"services",
|
|
2203
|
+
name,
|
|
2204
|
+
"healthPath"
|
|
2205
|
+
], "health path must be normalized");
|
|
2206
|
+
for (const route of service.routes) {
|
|
2207
|
+
const key = `${route.method} ${route.path}`;
|
|
2208
|
+
if (!isNormalizedPath(route.path) || routes.has(key)) issue([
|
|
2209
|
+
"services",
|
|
2210
|
+
name,
|
|
2211
|
+
"routes"
|
|
2212
|
+
], "routes must be normalized and unique");
|
|
2213
|
+
routes.add(key);
|
|
2214
|
+
}
|
|
2215
|
+
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");
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
function validateWorkloadTemplate(manifest, ctx) {
|
|
2219
|
+
const spec = manifest.spec;
|
|
2220
|
+
const issue = (path, message) => ctx.addIssue({
|
|
2221
|
+
code: "custom",
|
|
2222
|
+
path: ["spec", ...path],
|
|
2223
|
+
message
|
|
2224
|
+
});
|
|
2225
|
+
validateTemplatePersistence({
|
|
2226
|
+
security: spec.security,
|
|
2227
|
+
source: null,
|
|
2228
|
+
persistence: spec.lifecycle?.persistence ?? PersistenceSpecSchema.parse({})
|
|
2229
|
+
}, ctx);
|
|
2230
|
+
const timeouts = spec.lifecycle?.timeouts;
|
|
2231
|
+
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.");
|
|
2232
|
+
validateProfile(spec, issue);
|
|
2233
|
+
validateSecurity(spec, issue);
|
|
2234
|
+
validateAdapters(spec, issue);
|
|
2235
|
+
validateProcessEnvironment(spec, issue);
|
|
2236
|
+
validateOperations(spec, issue);
|
|
2237
|
+
validateServices(spec, issue);
|
|
2238
|
+
for (const [name, slot] of Object.entries(spec.credentialSlots)) if (slot.maxLeaseSeconds > spec.limits.deadlineSeconds) issue(["credentialSlots", name], "credential lifetime exceeds the workspace deadline");
|
|
2239
|
+
}
|
|
2240
|
+
//#endregion
|
|
2241
|
+
//#region ../contracts/src/templates/workload-template.ts
|
|
2242
|
+
const WorkloadAdapterSchema = z.strictObject({
|
|
2243
|
+
kind: z.enum([
|
|
2244
|
+
"process",
|
|
2245
|
+
"service",
|
|
2246
|
+
"pi",
|
|
2247
|
+
"agentapi"
|
|
2248
|
+
]),
|
|
2249
|
+
digest: WorkloadDigestSchema,
|
|
2250
|
+
protocol: z.string().min(1).max(128),
|
|
2251
|
+
command: HarnessSchema.shape.command.optional(),
|
|
2252
|
+
cwd: HarnessSchema.shape.cwd,
|
|
2253
|
+
env: HarnessSchema.shape.env,
|
|
2254
|
+
service: WorkloadNameSchema.optional()
|
|
2255
|
+
});
|
|
2256
|
+
const WorkloadOperationSchema = z.strictObject({
|
|
2257
|
+
adapter: WorkloadNameSchema,
|
|
2258
|
+
inputSchema: WorkloadInputSchema,
|
|
2259
|
+
requiredCapabilities: z.array(WorkloadCapabilitySchema),
|
|
2260
|
+
outputRoots: z.array(z.string()).max(16)
|
|
2261
|
+
});
|
|
2262
|
+
const WorkloadTemplateSchema = z.strictObject({
|
|
2263
|
+
apiVersion: z.literal("pocketcoder.dev/v1"),
|
|
2264
|
+
kind: z.literal("Template"),
|
|
2265
|
+
metadata: z.strictObject({
|
|
2266
|
+
name: WorkloadNameSchema,
|
|
2267
|
+
description: z.string().max(512).optional()
|
|
2268
|
+
}),
|
|
2269
|
+
spec: z.strictObject({
|
|
2270
|
+
version: z.string().regex(/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/),
|
|
2271
|
+
image: WorkloadImageSchema,
|
|
2272
|
+
runtime: WorkloadRuntimePinSchema,
|
|
2273
|
+
profile: WorkloadProfileSchema,
|
|
2274
|
+
adapters: z.record(WorkloadNameSchema, WorkloadAdapterSchema),
|
|
2275
|
+
operations: z.record(WorkloadNameSchema, WorkloadOperationSchema),
|
|
2276
|
+
services: z.record(WorkloadNameSchema, ServiceSchema.extend({ process: HarnessSchema.strict().optional() }).strict()),
|
|
2277
|
+
previews: PreviewsSchema,
|
|
2278
|
+
credentialSlots: z.record(WorkloadNameSchema, CredentialSlotSchema),
|
|
2279
|
+
resources: ResourcesSchema.strict(),
|
|
2280
|
+
network: NetworkPolicySchema,
|
|
2281
|
+
security: SecuritySchema.strict(),
|
|
2282
|
+
limits: WorkloadLimitsSchema,
|
|
2283
|
+
lifecycle: z.strictObject({
|
|
2284
|
+
persistence: PersistenceSpecSchema.prefault({}),
|
|
2285
|
+
checkpointHook: CheckpointHookSchema.optional(),
|
|
2286
|
+
timeouts: TimeoutsSchema.optional(),
|
|
2287
|
+
maxLaunchInputBytes: z.number().int().positive().max(1048576).default(65536)
|
|
2288
|
+
}).optional()
|
|
2289
|
+
})
|
|
2290
|
+
}).superRefine(validateWorkloadTemplate);
|
|
2291
|
+
const WorkloadTemplateSnapshotSchema = z.strictObject({
|
|
2292
|
+
format: z.literal("pocketcoder.dev/v1"),
|
|
2293
|
+
name: WorkloadNameSchema,
|
|
2294
|
+
version: z.string(),
|
|
2295
|
+
digest: WorkloadDigestSchema,
|
|
2296
|
+
manifest: WorkloadTemplateSchema
|
|
2297
|
+
}).superRefine((snapshot, ctx) => {
|
|
2298
|
+
if (snapshot.name !== snapshot.manifest.metadata.name || snapshot.version !== snapshot.manifest.spec.version || snapshot.digest !== digestOf(snapshot.manifest)) ctx.addIssue({
|
|
2299
|
+
code: "custom",
|
|
2300
|
+
message: "snapshot identity does not match its immutable manifest"
|
|
2301
|
+
});
|
|
2302
|
+
});
|
|
2303
|
+
//#endregion
|
|
2304
|
+
//#region ../contracts/src/templates/template-runtime.ts
|
|
2305
|
+
const LEGACY_AGENTAPI_SERVICE = ServiceSchema.parse({
|
|
2306
|
+
baseUrl: "http://127.0.0.1:3284",
|
|
2307
|
+
routes: [
|
|
2308
|
+
{
|
|
2309
|
+
method: "GET",
|
|
2310
|
+
path: "/status"
|
|
2311
|
+
},
|
|
2312
|
+
{
|
|
2313
|
+
method: "GET",
|
|
2314
|
+
path: "/messages",
|
|
2315
|
+
query: ["after"]
|
|
2316
|
+
},
|
|
2317
|
+
{
|
|
2318
|
+
method: "POST",
|
|
2319
|
+
path: "/message"
|
|
2320
|
+
}
|
|
2321
|
+
]
|
|
2322
|
+
});
|
|
2323
|
+
const AGENTAPI_SERVICE = ServiceSchema.parse({
|
|
2324
|
+
...LEGACY_AGENTAPI_SERVICE,
|
|
2325
|
+
routes: [...LEGACY_AGENTAPI_SERVICE.routes, {
|
|
2326
|
+
method: "GET",
|
|
2327
|
+
path: "/events",
|
|
2328
|
+
responseMode: "stream",
|
|
2329
|
+
maxResponseBytes: 4194304,
|
|
2330
|
+
deadlineSeconds: 300
|
|
2331
|
+
}]
|
|
2332
|
+
});
|
|
2333
|
+
function isAgentApiNative(spec) {
|
|
2334
|
+
return "agent" in spec && spec.agent !== void 0;
|
|
2335
|
+
}
|
|
2336
|
+
function agentApiHarness(spec) {
|
|
2337
|
+
if (!isAgentApiNative(spec)) {
|
|
2338
|
+
if (!spec.harness) throw new Error("A workload infrastructure process is not an alpha harness.");
|
|
2339
|
+
return spec.harness;
|
|
2340
|
+
}
|
|
2341
|
+
return {
|
|
2342
|
+
command: [
|
|
2343
|
+
"/usr/local/bin/agentapi",
|
|
2344
|
+
"server",
|
|
2345
|
+
"--type",
|
|
2346
|
+
spec.agent.type,
|
|
2347
|
+
...spec.agent.transport === "acp" ? ["--experimental-acp"] : [],
|
|
2348
|
+
...spec.agent.termWidth === void 0 ? [] : ["--term-width", String(spec.agent.termWidth)],
|
|
2349
|
+
...spec.agent.stateFile === void 0 ? [] : ["--state-file", spec.agent.stateFile],
|
|
2350
|
+
"--port",
|
|
2351
|
+
"3284",
|
|
2352
|
+
"--",
|
|
2353
|
+
...spec.agent.command
|
|
2354
|
+
],
|
|
2355
|
+
env: spec.agent.env,
|
|
2356
|
+
...spec.agent.cwd ? { cwd: spec.agent.cwd } : {}
|
|
2357
|
+
};
|
|
2358
|
+
}
|
|
2359
|
+
function templateServices(spec) {
|
|
2360
|
+
return isAgentApiNative(spec) ? { agent: AGENTAPI_SERVICE } : spec.services ?? {};
|
|
2361
|
+
}
|
|
2362
|
+
z.object({
|
|
2363
|
+
name: z.string(),
|
|
2364
|
+
version: z.string(),
|
|
2365
|
+
digest: WorkloadDigestSchema,
|
|
2366
|
+
spec: TemplateSpecSchema
|
|
2367
|
+
});
|
|
2368
|
+
//#endregion
|
|
2369
|
+
//#region ../contracts/src/templates/template.ts
|
|
2370
|
+
const TemplateManifestSchema = TemplateManifestBaseSchema.superRefine((manifest, ctx) => {
|
|
1355
2371
|
validateTemplateSpec(manifest.spec, ctx);
|
|
1356
2372
|
});
|
|
1357
2373
|
//#endregion
|
|
2374
|
+
//#region ../contracts/src/protocol/protocol-checkpoint.ts
|
|
2375
|
+
const bytes = z.number().int().positive().max(Number.MAX_SAFE_INTEGER);
|
|
2376
|
+
const digest = z.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
2377
|
+
const PrepareCheckpointArchivePayload = z.object({
|
|
2378
|
+
operation_id: z.uuid(),
|
|
2379
|
+
checkpoint_id: z.uuid(),
|
|
2380
|
+
deadline_ms: bytes,
|
|
2381
|
+
mounts: z.array(PersistenceMountSchema).max(16),
|
|
2382
|
+
max_archive_bytes: bytes,
|
|
2383
|
+
max_index_bytes: bytes,
|
|
2384
|
+
max_queue_bytes: bytes
|
|
2385
|
+
});
|
|
2386
|
+
const CheckpointPreparedPayload = z.object({
|
|
2387
|
+
operation_id: z.uuid(),
|
|
2388
|
+
checkpoint_id: z.uuid(),
|
|
2389
|
+
header: CheckpointArchiveHeaderSchema,
|
|
2390
|
+
archive_bytes: bytes
|
|
2391
|
+
}).refine((value) => value.header.checkpoint_id === value.checkpoint_id, "Checkpoint declaration identity differs.");
|
|
2392
|
+
const grant = z.object({
|
|
2393
|
+
operation_id: z.uuid(),
|
|
2394
|
+
transfer_id: z.uuid(),
|
|
2395
|
+
checkpoint_id: z.uuid(),
|
|
2396
|
+
credential: z.string().min(1).max(4096),
|
|
2397
|
+
url: z.url(),
|
|
2398
|
+
expires_at: z.iso.datetime()
|
|
2399
|
+
});
|
|
2400
|
+
const CheckpointUploadPayload = grant;
|
|
2401
|
+
const RestoreTransferSpecSchema = grant.extend({
|
|
2402
|
+
source: z.object({
|
|
2403
|
+
checkpoint_id: z.uuid(),
|
|
2404
|
+
workspace_id: z.uuid(),
|
|
2405
|
+
template_digest: digest,
|
|
2406
|
+
archive_digest: digest
|
|
2407
|
+
}),
|
|
2408
|
+
mounts: z.array(PersistenceMountSchema).max(16),
|
|
2409
|
+
max_archive_bytes: bytes,
|
|
2410
|
+
max_index_bytes: bytes,
|
|
2411
|
+
max_ledger_bytes: bytes
|
|
2412
|
+
}).refine((value) => value.source.checkpoint_id === value.checkpoint_id, "Restore source identity differs.");
|
|
2413
|
+
const CheckpointInstalledPayload = z.object({
|
|
2414
|
+
operation_id: z.uuid(),
|
|
2415
|
+
transfer_id: z.uuid(),
|
|
2416
|
+
checkpoint_id: z.uuid(),
|
|
2417
|
+
archive_digest: digest,
|
|
2418
|
+
phase: z.enum(["installed", "failed"])
|
|
2419
|
+
});
|
|
2420
|
+
const CheckpointUploadStatusPayload = z.object({
|
|
2421
|
+
operation_id: z.uuid(),
|
|
2422
|
+
transfer_id: z.uuid(),
|
|
2423
|
+
checkpoint_id: z.uuid(),
|
|
2424
|
+
phase: z.enum(["uploaded", "failed"])
|
|
2425
|
+
});
|
|
2426
|
+
//#endregion
|
|
2427
|
+
//#region ../contracts/src/protocol/protocol-exec.ts
|
|
2428
|
+
const SOURCE_CREDENTIAL_MAX_BYTES = 65536;
|
|
2429
|
+
const RestoreModeSchema = z.enum(["provider_installed", "controller_archive"]);
|
|
2430
|
+
const ExecSpecSchema = z.object({
|
|
2431
|
+
agentapi_native: z.boolean().default(false),
|
|
2432
|
+
setup: z.array(SetupStepSchema),
|
|
2433
|
+
harness: HarnessSchema.nullable(),
|
|
2434
|
+
workload: WaitingRuntimeSpecSchema.optional(),
|
|
2435
|
+
env: z.record(z.string(), z.string()),
|
|
2436
|
+
services: z.record(z.string(), ServiceSchema),
|
|
2437
|
+
previews: PreviewsSchema.optional(),
|
|
2438
|
+
display: DisplaySchema.optional(),
|
|
2439
|
+
terminal: z.object({
|
|
2440
|
+
command: z.array(z.string().min(1)).min(1),
|
|
2441
|
+
cwd: z.string().optional(),
|
|
2442
|
+
env: z.record(z.string(), z.string()),
|
|
2443
|
+
max_sessions: z.number().int().min(1).max(8),
|
|
2444
|
+
idle_timeout_seconds: z.number().int().positive(),
|
|
2445
|
+
replay_buffer_bytes: z.number().int().positive()
|
|
2446
|
+
}).nullable().default(null),
|
|
2447
|
+
timeouts: TimeoutsSchema,
|
|
2448
|
+
security: z.object({ writable_memory_paths: z.array(z.string()) }).default({ writable_memory_paths: [] }),
|
|
2449
|
+
network: z.discriminatedUnion("mode", [z.object({ mode: z.literal("unrestricted") }), z.object({
|
|
2450
|
+
mode: z.literal("restricted"),
|
|
2451
|
+
proxy_url: z.url(),
|
|
2452
|
+
health_url: z.url()
|
|
2453
|
+
})]).default({ mode: "unrestricted" }),
|
|
2454
|
+
launch_mode: z.enum(LAUNCH_MODES).default("create"),
|
|
2455
|
+
source: SourceDescriptorSchema.extend({
|
|
2456
|
+
url: z.url(),
|
|
2457
|
+
destination: z.string(),
|
|
2458
|
+
max_bytes: z.number().int().positive().optional(),
|
|
2459
|
+
max_files: z.number().int().positive().optional(),
|
|
2460
|
+
credential_expires_at: z.iso.datetime().nullable().default(null),
|
|
2461
|
+
credential: z.string().min(1).max(SOURCE_CREDENTIAL_MAX_BYTES).refine((value) => !value.includes("\0"), "credential must not contain NUL bytes").nullable().default(null)
|
|
2462
|
+
}).nullable().default(null),
|
|
2463
|
+
restore: z.object({
|
|
2464
|
+
mode: RestoreModeSchema,
|
|
2465
|
+
checkpoint_id: z.uuid(),
|
|
2466
|
+
origin_workspace_id: z.uuid(),
|
|
2467
|
+
transfer: RestoreTransferSpecSchema.nullable().default(null)
|
|
2468
|
+
}).nullable().default(null),
|
|
2469
|
+
persistence: z.object({
|
|
2470
|
+
mounts: z.array(z.object({
|
|
2471
|
+
name: z.string(),
|
|
2472
|
+
target: z.string()
|
|
2473
|
+
})),
|
|
2474
|
+
conversation_restore: z.enum(CONVERSATION_RESTORE_CAPABILITIES)
|
|
2475
|
+
}),
|
|
2476
|
+
checkpoint_hook: z.object({
|
|
2477
|
+
command: z.array(z.string().min(1)).min(1),
|
|
2478
|
+
timeout_seconds: z.number().int().positive(),
|
|
2479
|
+
env: z.record(z.string(), z.string()),
|
|
2480
|
+
cwd: z.string().optional()
|
|
2481
|
+
}).nullable(),
|
|
2482
|
+
outputs: z.record(z.string(), z.unknown())
|
|
2483
|
+
});
|
|
2484
|
+
//#endregion
|
|
1358
2485
|
//#region ../contracts/src/terminals/terminal.ts
|
|
1359
2486
|
const TERMINAL_CHUNK_BYTES = 32768;
|
|
1360
2487
|
const TERMINAL_REPLAY_BUFFER_BYTES = 65536;
|
|
@@ -1469,11 +2596,34 @@ const TerminalSessionSchema = z.object({
|
|
|
1469
2596
|
CursorQuerySchema(200, 50);
|
|
1470
2597
|
const TerminalSessionPageSchema = CursorPageSchema(TerminalSessionSchema);
|
|
1471
2598
|
//#endregion
|
|
2599
|
+
//#region ../contracts/src/protocol/protocol-credentials.ts
|
|
2600
|
+
const WorkspaceCredentialSchema = z.object({
|
|
2601
|
+
lease_id: z.uuid(),
|
|
2602
|
+
path: z.string().regex(/^\/run\/pocketcoder\/secrets\/leases\/[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/),
|
|
2603
|
+
credential: z.string().min(1).max(SOURCE_CREDENTIAL_MAX_BYTES).refine((value) => !value.includes("\0")),
|
|
2604
|
+
expires_at: z.iso.datetime(),
|
|
2605
|
+
purpose: z.enum(["setup-issuer", "runtime-issuer"])
|
|
2606
|
+
});
|
|
2607
|
+
const CredentialRenewPayload = z.object({
|
|
2608
|
+
lease_id: z.uuid(),
|
|
2609
|
+
request_id: z.uuid()
|
|
2610
|
+
});
|
|
2611
|
+
const CredentialRenewedPayload = z.object({
|
|
2612
|
+
previous_lease_id: z.uuid(),
|
|
2613
|
+
request_id: z.uuid(),
|
|
2614
|
+
credential: WorkspaceCredentialSchema
|
|
2615
|
+
});
|
|
2616
|
+
const CredentialInstalledPayload = z.object({
|
|
2617
|
+
previous_lease_id: z.uuid(),
|
|
2618
|
+
lease_id: z.uuid()
|
|
2619
|
+
});
|
|
2620
|
+
//#endregion
|
|
1472
2621
|
//#region ../contracts/src/protocol/protocol-stream.ts
|
|
1473
2622
|
const ProxyStreamStartPayload = z.object({
|
|
1474
2623
|
request_id: z.uuid(),
|
|
1475
2624
|
status: z.number().int().min(100).max(599),
|
|
1476
|
-
headers: z.record(z.string(), z.string())
|
|
2625
|
+
headers: z.record(z.string(), z.string()),
|
|
2626
|
+
cookies: z.array(z.string().max(4096)).max(16).optional()
|
|
1477
2627
|
});
|
|
1478
2628
|
const ProxyStreamChunkPayload = z.object({
|
|
1479
2629
|
request_id: z.uuid(),
|
|
@@ -1541,7 +2691,6 @@ z.object({
|
|
|
1541
2691
|
type: z.literal("lease_assignment"),
|
|
1542
2692
|
input: ProviderInputSchema
|
|
1543
2693
|
});
|
|
1544
|
-
const SOURCE_CREDENTIAL_MAX_BYTES = 65536;
|
|
1545
2694
|
const EnvelopeBase = z.object({
|
|
1546
2695
|
v: z.union([
|
|
1547
2696
|
z.literal(1),
|
|
@@ -1549,7 +2698,14 @@ const EnvelopeBase = z.object({
|
|
|
1549
2698
|
z.literal(3),
|
|
1550
2699
|
z.literal(4),
|
|
1551
2700
|
z.literal(5),
|
|
1552
|
-
z.literal(6)
|
|
2701
|
+
z.literal(6),
|
|
2702
|
+
z.literal(7),
|
|
2703
|
+
z.literal(8),
|
|
2704
|
+
z.literal(9),
|
|
2705
|
+
z.literal(10),
|
|
2706
|
+
z.literal(11),
|
|
2707
|
+
z.literal(12),
|
|
2708
|
+
z.literal(13)
|
|
1553
2709
|
]),
|
|
1554
2710
|
workspace_id: z.uuid(),
|
|
1555
2711
|
connection_id: z.uuid(),
|
|
@@ -1571,6 +2727,7 @@ const HeartbeatPayload = z.object({
|
|
|
1571
2727
|
child: z.enum([
|
|
1572
2728
|
"starting",
|
|
1573
2729
|
"setup",
|
|
2730
|
+
"waiting",
|
|
1574
2731
|
"running",
|
|
1575
2732
|
"exited",
|
|
1576
2733
|
"terminating"
|
|
@@ -1585,6 +2742,7 @@ const ProcessStatePayload = z.object({
|
|
|
1585
2742
|
phase: z.enum([
|
|
1586
2743
|
"starting",
|
|
1587
2744
|
"setup",
|
|
2745
|
+
"waiting",
|
|
1588
2746
|
"running",
|
|
1589
2747
|
"exited",
|
|
1590
2748
|
"terminating"
|
|
@@ -1666,6 +2824,38 @@ const RestoreStatusPayload = z.object({
|
|
|
1666
2824
|
});
|
|
1667
2825
|
const ConversationMessagePayload = ConversationMessageInputSchema;
|
|
1668
2826
|
z.discriminatedUnion("type", [
|
|
2827
|
+
EnvelopeBase.extend({
|
|
2828
|
+
type: z.literal("runtime_ready"),
|
|
2829
|
+
payload: z.object({ incarnationId: z.string().min(1) })
|
|
2830
|
+
}),
|
|
2831
|
+
EnvelopeBase.extend({
|
|
2832
|
+
type: z.literal("finite_process"),
|
|
2833
|
+
payload: FiniteProcessFactSchema
|
|
2834
|
+
}),
|
|
2835
|
+
EnvelopeBase.extend({
|
|
2836
|
+
type: z.literal("process_absent"),
|
|
2837
|
+
payload: ProcessTargetPayload
|
|
2838
|
+
}),
|
|
2839
|
+
EnvelopeBase.extend({
|
|
2840
|
+
type: z.literal("process_cancel_result"),
|
|
2841
|
+
payload: ProcessCancelResultPayload
|
|
2842
|
+
}),
|
|
2843
|
+
EnvelopeBase.extend({
|
|
2844
|
+
type: z.literal("preview_socket"),
|
|
2845
|
+
payload: PreviewSocketPayload
|
|
2846
|
+
}),
|
|
2847
|
+
EnvelopeBase.extend({
|
|
2848
|
+
type: z.literal("credential_renew"),
|
|
2849
|
+
payload: CredentialRenewPayload
|
|
2850
|
+
}),
|
|
2851
|
+
EnvelopeBase.extend({
|
|
2852
|
+
type: z.literal("credential_installed"),
|
|
2853
|
+
payload: CredentialInstalledPayload
|
|
2854
|
+
}),
|
|
2855
|
+
EnvelopeBase.extend({
|
|
2856
|
+
type: z.literal("setup_complete"),
|
|
2857
|
+
payload: z.object({ request_id: z.uuid() })
|
|
2858
|
+
}),
|
|
1669
2859
|
EnvelopeBase.extend({
|
|
1670
2860
|
type: z.literal("registered"),
|
|
1671
2861
|
payload: RegisteredPayload
|
|
@@ -1735,77 +2925,44 @@ z.discriminatedUnion("type", [
|
|
|
1735
2925
|
payload: CheckpointStatusPayload
|
|
1736
2926
|
}),
|
|
1737
2927
|
EnvelopeBase.extend({
|
|
1738
|
-
type: z.literal("
|
|
1739
|
-
payload:
|
|
1740
|
-
}),
|
|
1741
|
-
EnvelopeBase.extend({
|
|
1742
|
-
type: z.literal("restore_status"),
|
|
1743
|
-
payload: RestoreStatusPayload
|
|
2928
|
+
type: z.literal("checkpoint_prepared"),
|
|
2929
|
+
payload: CheckpointPreparedPayload
|
|
1744
2930
|
}),
|
|
1745
2931
|
EnvelopeBase.extend({
|
|
1746
|
-
type: z.literal("
|
|
1747
|
-
payload:
|
|
2932
|
+
type: z.literal("checkpoint_installed"),
|
|
2933
|
+
payload: CheckpointInstalledPayload
|
|
1748
2934
|
}),
|
|
1749
2935
|
EnvelopeBase.extend({
|
|
1750
|
-
type: z.literal("
|
|
1751
|
-
payload:
|
|
2936
|
+
type: z.literal("checkpoint_upload_status"),
|
|
2937
|
+
payload: CheckpointUploadStatusPayload
|
|
1752
2938
|
}),
|
|
1753
2939
|
EnvelopeBase.extend({
|
|
1754
|
-
type: z.literal("
|
|
1755
|
-
payload:
|
|
2940
|
+
type: z.literal("output_published"),
|
|
2941
|
+
payload: OutputPublishedPayload
|
|
1756
2942
|
}),
|
|
1757
2943
|
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)
|
|
2944
|
+
type: z.literal("restore_status"),
|
|
2945
|
+
payload: RestoreStatusPayload
|
|
1799
2946
|
}),
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
})
|
|
2947
|
+
EnvelopeBase.extend({
|
|
2948
|
+
type: z.literal("conversation_message"),
|
|
2949
|
+
payload: ConversationMessagePayload
|
|
2950
|
+
}),
|
|
2951
|
+
EnvelopeBase.extend({
|
|
2952
|
+
type: z.literal("attachment_ack"),
|
|
2953
|
+
payload: AttachmentAckPayload
|
|
2954
|
+
}),
|
|
2955
|
+
EnvelopeBase.extend({
|
|
2956
|
+
type: z.literal("attachment_result"),
|
|
2957
|
+
payload: AttachmentResultPayload
|
|
2958
|
+
}),
|
|
2959
|
+
EnvelopeBase.extend({
|
|
2960
|
+
type: z.literal("attachment_resolved"),
|
|
2961
|
+
payload: AttachmentResolvedPayload
|
|
2962
|
+
})
|
|
2963
|
+
]);
|
|
1808
2964
|
const RegisteredAckPayload = z.object({
|
|
2965
|
+
credentials: z.array(WorkspaceCredentialSchema).max(64).default([]),
|
|
1809
2966
|
epoch: z.number().int().positive(),
|
|
1810
2967
|
reconnect_credential: z.string().optional(),
|
|
1811
2968
|
limits: z.object({
|
|
@@ -1824,7 +2981,9 @@ const ProxyRequestPayload = z.object({
|
|
|
1824
2981
|
"POST",
|
|
1825
2982
|
"PUT",
|
|
1826
2983
|
"PATCH",
|
|
1827
|
-
"DELETE"
|
|
2984
|
+
"DELETE",
|
|
2985
|
+
"HEAD",
|
|
2986
|
+
"OPTIONS"
|
|
1828
2987
|
]),
|
|
1829
2988
|
path: z.string(),
|
|
1830
2989
|
query: z.record(z.string(), z.string()).default({}),
|
|
@@ -1840,6 +2999,42 @@ const PrepareCheckpointPayload = z.object({
|
|
|
1840
2999
|
deadline_ms: z.number().int().positive()
|
|
1841
3000
|
});
|
|
1842
3001
|
z.discriminatedUnion("type", [
|
|
3002
|
+
EnvelopeBase.extend({
|
|
3003
|
+
type: z.literal("process_start"),
|
|
3004
|
+
payload: ProcessStartPayload
|
|
3005
|
+
}),
|
|
3006
|
+
EnvelopeBase.extend({
|
|
3007
|
+
type: z.literal("process_lookup"),
|
|
3008
|
+
payload: ProcessTargetPayload
|
|
3009
|
+
}),
|
|
3010
|
+
EnvelopeBase.extend({
|
|
3011
|
+
type: z.literal("process_cancel"),
|
|
3012
|
+
payload: ProcessCancelPayload
|
|
3013
|
+
}),
|
|
3014
|
+
EnvelopeBase.extend({
|
|
3015
|
+
type: z.literal("process_ack"),
|
|
3016
|
+
payload: ProcessTargetPayload
|
|
3017
|
+
}),
|
|
3018
|
+
EnvelopeBase.extend({
|
|
3019
|
+
type: z.literal("screenshot_capture"),
|
|
3020
|
+
payload: ScreenshotCapturePayload
|
|
3021
|
+
}),
|
|
3022
|
+
EnvelopeBase.extend({
|
|
3023
|
+
type: z.literal("preview_socket"),
|
|
3024
|
+
payload: PreviewSocketPayload
|
|
3025
|
+
}),
|
|
3026
|
+
EnvelopeBase.extend({
|
|
3027
|
+
type: z.literal("credential_renewed"),
|
|
3028
|
+
payload: CredentialRenewedPayload
|
|
3029
|
+
}),
|
|
3030
|
+
EnvelopeBase.extend({
|
|
3031
|
+
type: z.literal("credential_installed_ack"),
|
|
3032
|
+
payload: CredentialInstalledPayload
|
|
3033
|
+
}),
|
|
3034
|
+
EnvelopeBase.extend({
|
|
3035
|
+
type: z.literal("setup_complete_ack"),
|
|
3036
|
+
payload: z.object({ request_id: z.uuid() })
|
|
3037
|
+
}),
|
|
1843
3038
|
EnvelopeBase.extend({
|
|
1844
3039
|
type: z.literal("registered_ack"),
|
|
1845
3040
|
payload: RegisteredAckPayload
|
|
@@ -1888,6 +3083,14 @@ z.discriminatedUnion("type", [
|
|
|
1888
3083
|
type: z.literal("prepare_checkpoint"),
|
|
1889
3084
|
payload: PrepareCheckpointPayload
|
|
1890
3085
|
}),
|
|
3086
|
+
EnvelopeBase.extend({
|
|
3087
|
+
type: z.literal("prepare_checkpoint_archive"),
|
|
3088
|
+
payload: PrepareCheckpointArchivePayload
|
|
3089
|
+
}),
|
|
3090
|
+
EnvelopeBase.extend({
|
|
3091
|
+
type: z.literal("checkpoint_upload"),
|
|
3092
|
+
payload: CheckpointUploadPayload
|
|
3093
|
+
}),
|
|
1891
3094
|
EnvelopeBase.extend({
|
|
1892
3095
|
type: z.literal("attachment_start"),
|
|
1893
3096
|
payload: AttachmentStartPayload
|
|
@@ -1910,6 +3113,107 @@ z.discriminatedUnion("type", [
|
|
|
1910
3113
|
})
|
|
1911
3114
|
]);
|
|
1912
3115
|
//#endregion
|
|
3116
|
+
//#region ../contracts/src/recovery/account-purge.ts
|
|
3117
|
+
const Digest = z.string().regex(/^[a-f0-9]{64}$/);
|
|
3118
|
+
const AccountPurgeIntentSchema = z.strictObject({
|
|
3119
|
+
format: z.literal("pocketcoder-account-purge/v1"),
|
|
3120
|
+
accountId: z.uuid(),
|
|
3121
|
+
operationId: z.uuid(),
|
|
3122
|
+
createRequestDigest: Digest,
|
|
3123
|
+
purgeRequestDigest: Digest,
|
|
3124
|
+
admittedAt: z.iso.datetime()
|
|
3125
|
+
});
|
|
3126
|
+
z.strictObject({
|
|
3127
|
+
format: z.literal("pocketcoder-account-purge-completion/v1"),
|
|
3128
|
+
intent: AccountPurgeIntentSchema,
|
|
3129
|
+
completedAt: z.iso.datetime()
|
|
3130
|
+
});
|
|
3131
|
+
//#endregion
|
|
3132
|
+
//#region ../contracts/src/recovery/source-writer.ts
|
|
3133
|
+
const fileIdentity = z.strictObject({
|
|
3134
|
+
device: z.string().regex(/^\d+$/).max(20),
|
|
3135
|
+
inode: z.string().regex(/^[1-9]\d*$/).max(20)
|
|
3136
|
+
});
|
|
3137
|
+
z.strictObject({
|
|
3138
|
+
format: z.literal("pocketcoder-source-writer/v1"),
|
|
3139
|
+
directory: z.string().startsWith("/").max(4096),
|
|
3140
|
+
root: fileIdentity,
|
|
3141
|
+
lock: fileIdentity
|
|
3142
|
+
});
|
|
3143
|
+
z.strictObject({ manifest: z.union([TemplateManifestSchema, WorkloadTemplateSchema]) });
|
|
3144
|
+
z.object({
|
|
3145
|
+
name: z.string().regex(/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/),
|
|
3146
|
+
version: z.string().min(1).max(256)
|
|
3147
|
+
});
|
|
3148
|
+
z.strictObject({
|
|
3149
|
+
workspaceId: z.uuid(),
|
|
3150
|
+
incarnationId: z.string().min(1).max(256),
|
|
3151
|
+
profiles: z.array(WorkloadProfileSchema),
|
|
3152
|
+
storageProfiles: z.array(z.enum(["controller-readonly/v1", "shared-writer-readonly/v1"])),
|
|
3153
|
+
ceilings: WorkloadLimitsSchema,
|
|
3154
|
+
now: z.iso.datetime(),
|
|
3155
|
+
expiresAt: z.iso.datetime(),
|
|
3156
|
+
installed: InstalledWorkloadSchema.optional()
|
|
3157
|
+
});
|
|
3158
|
+
//#endregion
|
|
3159
|
+
//#region ../contracts/src/workspaces/execution-dispatch.ts
|
|
3160
|
+
const ExecutionPhaseSchema = z.enum([
|
|
3161
|
+
"accepted",
|
|
3162
|
+
"dispatching",
|
|
3163
|
+
"running",
|
|
3164
|
+
"terminal",
|
|
3165
|
+
"canceled",
|
|
3166
|
+
"unknown",
|
|
3167
|
+
"lost"
|
|
3168
|
+
]);
|
|
3169
|
+
z.strictObject({
|
|
3170
|
+
acceptance: WorkloadAcceptanceSchema,
|
|
3171
|
+
conversationIdentity: ConversationRequestIdentitySchema.nullable(),
|
|
3172
|
+
outcome: JsonValueSchema.nullable(),
|
|
3173
|
+
acceptedAt: z.iso.datetime(),
|
|
3174
|
+
dispatchClaimedAt: z.iso.datetime().nullable(),
|
|
3175
|
+
phase: ExecutionPhaseSchema,
|
|
3176
|
+
cancel: z.strictObject({
|
|
3177
|
+
cancelId: z.uuid(),
|
|
3178
|
+
requestedAt: z.iso.datetime()
|
|
3179
|
+
}).nullable(),
|
|
3180
|
+
settledAt: z.iso.datetime().nullable(),
|
|
3181
|
+
acknowledgedAt: z.iso.datetime().nullable(),
|
|
3182
|
+
finalizedAt: z.iso.datetime().nullable()
|
|
3183
|
+
});
|
|
3184
|
+
z.strictObject({
|
|
3185
|
+
acceptance: WorkloadAcceptanceSchema,
|
|
3186
|
+
conversationIdentity: ConversationRequestIdentitySchema.optional()
|
|
3187
|
+
}).refine(({ acceptance, conversationIdentity }) => acceptance.execution.owner !== "conversation-request" || conversationIdentity !== void 0, {
|
|
3188
|
+
path: ["conversationIdentity"],
|
|
3189
|
+
message: "Conversation execution requires a conversation identity."
|
|
3190
|
+
});
|
|
3191
|
+
WorkloadCancelSchema.refine((cancel) => cancel.scope === "operation", { message: "Use workspace release for workspace cancellation" });
|
|
3192
|
+
z.strictObject({
|
|
3193
|
+
workspaceId: z.uuid(),
|
|
3194
|
+
callerPrincipalId: z.uuid(),
|
|
3195
|
+
incarnationId: z.string().min(1),
|
|
3196
|
+
template: WorkloadTemplateSnapshotSchema,
|
|
3197
|
+
producer: InstalledWorkloadSchema,
|
|
3198
|
+
readyAt: z.iso.datetime().nullable(),
|
|
3199
|
+
endedAt: z.iso.datetime().nullable()
|
|
3200
|
+
});
|
|
3201
|
+
z.discriminatedUnion("phase", [z.strictObject({
|
|
3202
|
+
execution: WorkloadExecutionReferenceSchema,
|
|
3203
|
+
phase: z.literal("absent")
|
|
3204
|
+
}), z.strictObject({
|
|
3205
|
+
execution: WorkloadExecutionReferenceSchema,
|
|
3206
|
+
fingerprint: WorkloadDigestSchema,
|
|
3207
|
+
phase: z.enum([
|
|
3208
|
+
"accepted",
|
|
3209
|
+
"running",
|
|
3210
|
+
"terminal",
|
|
3211
|
+
"canceled"
|
|
3212
|
+
]),
|
|
3213
|
+
outcome: JsonValueSchema.nullable()
|
|
3214
|
+
})]);
|
|
3215
|
+
WorkloadCancelSchema.extend({ fingerprint: WorkloadDigestSchema });
|
|
3216
|
+
//#endregion
|
|
1913
3217
|
//#region src/resources/admin/admin.ts
|
|
1914
3218
|
var AdministrationApi = class {
|
|
1915
3219
|
transport;
|
|
@@ -1987,44 +3291,8 @@ function responseError(response, body) {
|
|
|
1987
3291
|
});
|
|
1988
3292
|
}
|
|
1989
3293
|
//#endregion
|
|
1990
|
-
//#region src/resources/
|
|
3294
|
+
//#region src/resources/agent/agent.ts
|
|
1991
3295
|
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
3296
|
var AgentApi = class {
|
|
2029
3297
|
transport;
|
|
2030
3298
|
workspaces;
|
|
@@ -2058,6 +3326,58 @@ var AgentApi = class {
|
|
|
2058
3326
|
}
|
|
2059
3327
|
throw responseError(response, body);
|
|
2060
3328
|
}
|
|
3329
|
+
interrupt(workspaceId, options = {}) {
|
|
3330
|
+
return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/agent/interrupt`, AgentInterruptResponseSchema, {
|
|
3331
|
+
method: "POST",
|
|
3332
|
+
signal: options.signal
|
|
3333
|
+
});
|
|
3334
|
+
}
|
|
3335
|
+
};
|
|
3336
|
+
//#endregion
|
|
3337
|
+
//#region src/resources/attachments/attachments.ts
|
|
3338
|
+
function contentDisposition(name) {
|
|
3339
|
+
const clean = name.replace(/[\r\n]/g, "");
|
|
3340
|
+
if (/^[ -~]*$/.test(clean)) return `attachment; filename="${clean.replace(/(["\\])/g, "\\$1")}"`;
|
|
3341
|
+
return `attachment; filename*=UTF-8''${encodeURIComponent(clean)}`;
|
|
3342
|
+
}
|
|
3343
|
+
function progressStream(body, total, onProgress) {
|
|
3344
|
+
const source = new Response(body).body ?? new ReadableStream({ start: (c) => c.close() });
|
|
3345
|
+
let uploaded = 0;
|
|
3346
|
+
return source.pipeThrough(new TransformStream({ transform(chunk, controller) {
|
|
3347
|
+
uploaded += chunk.byteLength;
|
|
3348
|
+
controller.enqueue(chunk);
|
|
3349
|
+
onProgress(Math.min(uploaded, total), total);
|
|
3350
|
+
} }));
|
|
3351
|
+
}
|
|
3352
|
+
var AttachmentsApi = class {
|
|
3353
|
+
transport;
|
|
3354
|
+
constructor(transport) {
|
|
3355
|
+
this.transport = transport;
|
|
3356
|
+
}
|
|
3357
|
+
async upload(workspaceId, input) {
|
|
3358
|
+
const id = (input.id ?? crypto.randomUUID()).toLowerCase();
|
|
3359
|
+
let sha256 = input.sha256;
|
|
3360
|
+
if (!sha256) {
|
|
3361
|
+
if (input.body instanceof ReadableStream) throw new Error("Stream uploads require sha256 before body consumption.");
|
|
3362
|
+
const digest = await crypto.subtle.digest("SHA-256", await new Response(input.body).arrayBuffer());
|
|
3363
|
+
sha256 = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
3364
|
+
}
|
|
3365
|
+
if (!/^[0-9a-f]{64}$/.test(sha256)) throw new Error("sha256 must be 64 lowercase hexadecimal characters.");
|
|
3366
|
+
const body = input.onProgress ? progressStream(input.body, input.sizeBytes, input.onProgress) : input.body;
|
|
3367
|
+
const init = {
|
|
3368
|
+
method: "PUT",
|
|
3369
|
+
...input.signal ? { signal: input.signal } : {},
|
|
3370
|
+
headers: {
|
|
3371
|
+
"content-type": input.mediaType ?? "application/octet-stream",
|
|
3372
|
+
"content-disposition": contentDisposition(input.name),
|
|
3373
|
+
"content-length": String(input.sizeBytes),
|
|
3374
|
+
"x-content-sha256": sha256
|
|
3375
|
+
},
|
|
3376
|
+
body,
|
|
3377
|
+
...body instanceof ReadableStream ? { duplex: "half" } : {}
|
|
3378
|
+
};
|
|
3379
|
+
return await this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/attachments/${id}`, InputObjectReferenceSchema, init);
|
|
3380
|
+
}
|
|
2061
3381
|
};
|
|
2062
3382
|
//#endregion
|
|
2063
3383
|
//#region src/transport/common.ts
|
|
@@ -2122,13 +3442,32 @@ var OperationsApi = class {
|
|
|
2122
3442
|
};
|
|
2123
3443
|
//#endregion
|
|
2124
3444
|
//#region src/resources/conversations/conversations.ts
|
|
3445
|
+
function workspacePath(id) {
|
|
3446
|
+
return `/v1/workspaces/${encodeURIComponent(id)}`;
|
|
3447
|
+
}
|
|
3448
|
+
async function* liveEvents(body) {
|
|
3449
|
+
const decoder = new TextDecoder();
|
|
3450
|
+
let buffer = "";
|
|
3451
|
+
for await (const chunk of body) {
|
|
3452
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
3453
|
+
let end = buffer.indexOf("\n\n");
|
|
3454
|
+
while (end !== -1) {
|
|
3455
|
+
const block = buffer.slice(0, end);
|
|
3456
|
+
buffer = buffer.slice(end + 2);
|
|
3457
|
+
const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
3458
|
+
const event = data ? ConversationLiveEventSchema.safeParse(JSON.parse(data)) : null;
|
|
3459
|
+
if (event?.success) yield event.data;
|
|
3460
|
+
end = buffer.indexOf("\n\n");
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
2125
3464
|
var ConversationsApi = class {
|
|
2126
3465
|
transport;
|
|
2127
3466
|
constructor(transport) {
|
|
2128
3467
|
this.transport = transport;
|
|
2129
3468
|
}
|
|
2130
3469
|
async list(id, query = {}, options = {}) {
|
|
2131
|
-
const body = await this.transport.request(
|
|
3470
|
+
const body = await this.transport.request(`${workspacePath(id)}/conversation?${queryString({
|
|
2132
3471
|
cursor: query.cursor,
|
|
2133
3472
|
limit: query.limit ?? 100
|
|
2134
3473
|
})}`, ConversationPageSchema, options);
|
|
@@ -2139,12 +3478,37 @@ var ConversationsApi = class {
|
|
|
2139
3478
|
};
|
|
2140
3479
|
}
|
|
2141
3480
|
async acceptRequest(id, identity, options = {}) {
|
|
2142
|
-
return this.transport.request(
|
|
3481
|
+
return this.transport.request(`${workspacePath(id)}/conversation/requests`, ConversationRequestReceiptSchema, {
|
|
2143
3482
|
...options,
|
|
2144
3483
|
method: "POST",
|
|
2145
3484
|
body: JSON.stringify(identity)
|
|
2146
3485
|
});
|
|
2147
3486
|
}
|
|
3487
|
+
async listConversations(id, options = {}) {
|
|
3488
|
+
return (await this.transport.request(`${workspacePath(id)}/conversations`, ConversationListSchema, options)).items;
|
|
3489
|
+
}
|
|
3490
|
+
async read(id, conversationId, query = {}, options = {}) {
|
|
3491
|
+
const body = await this.transport.request(`${workspacePath(id)}/conversations/${encodeURIComponent(conversationId)}?${queryString({
|
|
3492
|
+
cursor: query.cursor,
|
|
3493
|
+
limit: query.limit ?? 100
|
|
3494
|
+
})}`, ConversationDocumentPageSchema, options);
|
|
3495
|
+
return {
|
|
3496
|
+
...page(body),
|
|
3497
|
+
conversation: body.conversation,
|
|
3498
|
+
protocol: body.protocol
|
|
3499
|
+
};
|
|
3500
|
+
}
|
|
3501
|
+
async delete(id, conversationId, options = {}) {
|
|
3502
|
+
await this.transport.request(`${workspacePath(id)}/conversations/${encodeURIComponent(conversationId)}`, z.undefined(), {
|
|
3503
|
+
...options,
|
|
3504
|
+
method: "DELETE"
|
|
3505
|
+
});
|
|
3506
|
+
}
|
|
3507
|
+
async *live(id, options = {}) {
|
|
3508
|
+
const path = options.conversationId ? `${workspacePath(id)}/conversations/${encodeURIComponent(options.conversationId)}/live` : `${workspacePath(id)}/conversation/live`;
|
|
3509
|
+
const response = await this.transport.stream(path, options.signal);
|
|
3510
|
+
if (response.body) yield* liveEvents(response.body);
|
|
3511
|
+
}
|
|
2148
3512
|
};
|
|
2149
3513
|
//#endregion
|
|
2150
3514
|
//#region src/resources/diagnostics/diagnostics.ts
|
|
@@ -2175,8 +3539,40 @@ var NetworkEventsApi = class extends WorkspaceCursorApi {
|
|
|
2175
3539
|
}
|
|
2176
3540
|
};
|
|
2177
3541
|
var OutputsApi = class extends WorkspaceCursorApi {
|
|
3542
|
+
binaryTransport;
|
|
3543
|
+
async download(workspaceId, outputId, options = {}) {
|
|
3544
|
+
const response = await this.binaryTransport.raw(`/v1/workspaces/${encodeURIComponent(workspaceId)}/outputs/${encodeURIComponent(outputId)}/content`, options);
|
|
3545
|
+
if (!response.ok) throw responseError(response, await response.json());
|
|
3546
|
+
return response;
|
|
3547
|
+
}
|
|
2178
3548
|
constructor(transport) {
|
|
2179
3549
|
super(transport, "outputs", OutputPageSchema);
|
|
3550
|
+
this.binaryTransport = transport;
|
|
3551
|
+
}
|
|
3552
|
+
};
|
|
3553
|
+
//#endregion
|
|
3554
|
+
//#region src/resources/displays/displays.ts
|
|
3555
|
+
var DisplaysApi = class {
|
|
3556
|
+
transport;
|
|
3557
|
+
constructor(transport) {
|
|
3558
|
+
this.transport = transport;
|
|
3559
|
+
}
|
|
3560
|
+
capture(workspaceId, options = {}) {
|
|
3561
|
+
return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/display/screenshot`, ScreenshotResourceSchema, {
|
|
3562
|
+
method: "POST",
|
|
3563
|
+
body: "{}",
|
|
3564
|
+
signal: options.signal
|
|
3565
|
+
});
|
|
3566
|
+
}
|
|
3567
|
+
open(workspaceId, control = false, options = {}) {
|
|
3568
|
+
return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/display`, PreviewOpenResponseSchema, {
|
|
3569
|
+
method: "POST",
|
|
3570
|
+
body: JSON.stringify({
|
|
3571
|
+
control,
|
|
3572
|
+
session: options.session ?? { mode: "local" }
|
|
3573
|
+
}),
|
|
3574
|
+
signal: options.signal
|
|
3575
|
+
});
|
|
2180
3576
|
}
|
|
2181
3577
|
};
|
|
2182
3578
|
//#endregion
|
|
@@ -2204,6 +3600,9 @@ var KeysApi = class {
|
|
|
2204
3600
|
cursor = page.next_cursor ?? void 0;
|
|
2205
3601
|
} while (cursor);
|
|
2206
3602
|
}
|
|
3603
|
+
expiring(query = {}, options = {}) {
|
|
3604
|
+
return this.transport.request(`/v1/keys/expiring?${queryString({ within_hours: query.withinHours })}`, ExpiringKeyListResponseSchema, options);
|
|
3605
|
+
}
|
|
2207
3606
|
issue(principalId, input, options = {}) {
|
|
2208
3607
|
return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys`, KeyIssueResponseSchema, {
|
|
2209
3608
|
method: "POST",
|
|
@@ -2211,6 +3610,13 @@ var KeysApi = class {
|
|
|
2211
3610
|
signal: options.signal
|
|
2212
3611
|
});
|
|
2213
3612
|
}
|
|
3613
|
+
rotate(principalId, keyId, input, options = {}) {
|
|
3614
|
+
return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys/${encodeURIComponent(keyId)}/rotate`, KeyIssueResponseSchema, {
|
|
3615
|
+
method: "POST",
|
|
3616
|
+
body: JSON.stringify(input),
|
|
3617
|
+
signal: options.signal
|
|
3618
|
+
});
|
|
3619
|
+
}
|
|
2214
3620
|
revoke(principalId, keyId, options = {}) {
|
|
2215
3621
|
return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys/${encodeURIComponent(keyId)}`, z.object({ revoked: z.literal(true) }), {
|
|
2216
3622
|
method: "DELETE",
|
|
@@ -2242,6 +3648,76 @@ var RecoveryApi = class {
|
|
|
2242
3648
|
}
|
|
2243
3649
|
};
|
|
2244
3650
|
//#endregion
|
|
3651
|
+
//#region src/resources/previews/previews.ts
|
|
3652
|
+
var PreviewsApi = class {
|
|
3653
|
+
transport;
|
|
3654
|
+
constructor(transport) {
|
|
3655
|
+
this.transport = transport;
|
|
3656
|
+
}
|
|
3657
|
+
list(workspaceId, options = {}) {
|
|
3658
|
+
return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/previews`, PreviewListSchema, options);
|
|
3659
|
+
}
|
|
3660
|
+
open(workspaceId, name, options = {}) {
|
|
3661
|
+
return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/previews/${encodeURIComponent(name)}`, PreviewOpenResponseSchema, {
|
|
3662
|
+
method: "POST",
|
|
3663
|
+
body: JSON.stringify({ session: options.session ?? { mode: "local" } }),
|
|
3664
|
+
signal: options.signal
|
|
3665
|
+
});
|
|
3666
|
+
}
|
|
3667
|
+
};
|
|
3668
|
+
//#endregion
|
|
3669
|
+
//#region src/resources/principals/principals.ts
|
|
3670
|
+
var PrincipalsApi = class {
|
|
3671
|
+
transport;
|
|
3672
|
+
constructor(transport) {
|
|
3673
|
+
this.transport = transport;
|
|
3674
|
+
}
|
|
3675
|
+
list(options = {}) {
|
|
3676
|
+
return this.transport.request("/v1/principals", PrincipalListResponseSchema, options);
|
|
3677
|
+
}
|
|
3678
|
+
get(principalId, options = {}) {
|
|
3679
|
+
return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}`, PrincipalResourceSchema, options);
|
|
3680
|
+
}
|
|
3681
|
+
create(input, options = {}) {
|
|
3682
|
+
return this.transport.request("/v1/principals", PrincipalResourceSchema, {
|
|
3683
|
+
method: "POST",
|
|
3684
|
+
body: JSON.stringify(input),
|
|
3685
|
+
signal: options.signal
|
|
3686
|
+
});
|
|
3687
|
+
}
|
|
3688
|
+
update(principalId, input, options = {}) {
|
|
3689
|
+
return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}`, PrincipalResourceSchema, {
|
|
3690
|
+
method: "PATCH",
|
|
3691
|
+
body: JSON.stringify(input),
|
|
3692
|
+
signal: options.signal
|
|
3693
|
+
});
|
|
3694
|
+
}
|
|
3695
|
+
};
|
|
3696
|
+
//#endregion
|
|
3697
|
+
//#region src/resources/secrets/secrets.ts
|
|
3698
|
+
var SecretsApi = class {
|
|
3699
|
+
transport;
|
|
3700
|
+
constructor(transport) {
|
|
3701
|
+
this.transport = transport;
|
|
3702
|
+
}
|
|
3703
|
+
async put(name, input, options = {}) {
|
|
3704
|
+
return this.transport.request(`/v1/secrets/${encodeURIComponent(name)}`, SecretResourceSchema, {
|
|
3705
|
+
...options,
|
|
3706
|
+
method: "PUT",
|
|
3707
|
+
body: JSON.stringify(input)
|
|
3708
|
+
});
|
|
3709
|
+
}
|
|
3710
|
+
async list(options = {}) {
|
|
3711
|
+
return (await this.transport.request("/v1/secrets", SecretListSchema, options)).items;
|
|
3712
|
+
}
|
|
3713
|
+
async retire(name, options = {}) {
|
|
3714
|
+
return this.transport.request(`/v1/secrets/${encodeURIComponent(name)}`, SecretResourceSchema, {
|
|
3715
|
+
...options,
|
|
3716
|
+
method: "DELETE"
|
|
3717
|
+
});
|
|
3718
|
+
}
|
|
3719
|
+
};
|
|
3720
|
+
//#endregion
|
|
2245
3721
|
//#region src/resources/templates/templates.ts
|
|
2246
3722
|
var TemplatesApi = class {
|
|
2247
3723
|
transport;
|
|
@@ -2255,7 +3731,27 @@ var TemplatesApi = class {
|
|
|
2255
3731
|
})}`, TemplatePageSchema, options));
|
|
2256
3732
|
}
|
|
2257
3733
|
async list(options = {}) {
|
|
2258
|
-
|
|
3734
|
+
const items = [];
|
|
3735
|
+
let cursor;
|
|
3736
|
+
do {
|
|
3737
|
+
const result = await this.page({ cursor }, options);
|
|
3738
|
+
items.push(...result.items);
|
|
3739
|
+
cursor = result.nextCursor ?? void 0;
|
|
3740
|
+
} while (cursor);
|
|
3741
|
+
return items;
|
|
3742
|
+
}
|
|
3743
|
+
async publish(manifest, options = {}) {
|
|
3744
|
+
return this.transport.request("/v1/templates", TemplateListItemSchema, {
|
|
3745
|
+
...options,
|
|
3746
|
+
method: "POST",
|
|
3747
|
+
body: JSON.stringify({ manifest })
|
|
3748
|
+
});
|
|
3749
|
+
}
|
|
3750
|
+
async retire(name, version, options = {}) {
|
|
3751
|
+
return this.transport.request(`/v1/templates/${encodeURIComponent(name)}/${encodeURIComponent(version)}`, TemplateListItemSchema, {
|
|
3752
|
+
...options,
|
|
3753
|
+
method: "DELETE"
|
|
3754
|
+
});
|
|
2259
3755
|
}
|
|
2260
3756
|
};
|
|
2261
3757
|
//#endregion
|
|
@@ -2519,6 +4015,20 @@ var PocketCoderTransport = class {
|
|
|
2519
4015
|
await delay(100 * 2 ** attempt, signal);
|
|
2520
4016
|
}
|
|
2521
4017
|
}
|
|
4018
|
+
async stream(path, signal) {
|
|
4019
|
+
const response = await this.fetchImpl(`${this.baseUrl}${path.startsWith("/") ? path : `/${path}`}`, {
|
|
4020
|
+
headers: {
|
|
4021
|
+
authorization: `Bearer ${this.apiKey}`,
|
|
4022
|
+
accept: "text/event-stream"
|
|
4023
|
+
},
|
|
4024
|
+
...signal ? { signal } : {}
|
|
4025
|
+
});
|
|
4026
|
+
if (!response.ok) {
|
|
4027
|
+
const text = await response.text();
|
|
4028
|
+
throw responseError(response, text ? JSON.parse(text) : void 0);
|
|
4029
|
+
}
|
|
4030
|
+
return response;
|
|
4031
|
+
}
|
|
2522
4032
|
async request(path, schema, init = {}) {
|
|
2523
4033
|
const response = await this.raw(path, init);
|
|
2524
4034
|
const text = await response.text();
|
|
@@ -2583,8 +4093,11 @@ async function delay(milliseconds, signal) {
|
|
|
2583
4093
|
//#region src/client.ts
|
|
2584
4094
|
var PocketCoderClient = class {
|
|
2585
4095
|
transport;
|
|
4096
|
+
secrets;
|
|
2586
4097
|
templates;
|
|
2587
4098
|
workspaces;
|
|
4099
|
+
previews;
|
|
4100
|
+
displays;
|
|
2588
4101
|
attachments;
|
|
2589
4102
|
agent;
|
|
2590
4103
|
conversations;
|
|
@@ -2595,12 +4108,16 @@ var PocketCoderClient = class {
|
|
|
2595
4108
|
outputs;
|
|
2596
4109
|
administration;
|
|
2597
4110
|
terminals;
|
|
4111
|
+
principals;
|
|
2598
4112
|
keys;
|
|
2599
4113
|
recovery;
|
|
2600
4114
|
constructor(config, fetchImpl = fetch) {
|
|
2601
4115
|
this.transport = new PocketCoderTransport(config, fetchImpl);
|
|
4116
|
+
this.secrets = new SecretsApi(this.transport);
|
|
2602
4117
|
this.templates = new TemplatesApi(this.transport);
|
|
2603
4118
|
this.workspaces = new WorkspacesApi(this.transport);
|
|
4119
|
+
this.previews = new PreviewsApi(this.transport);
|
|
4120
|
+
this.displays = new DisplaysApi(this.transport);
|
|
2604
4121
|
this.attachments = new AttachmentsApi(this.transport);
|
|
2605
4122
|
this.agent = new AgentApi(this.transport, this.workspaces);
|
|
2606
4123
|
this.conversations = new ConversationsApi(this.transport);
|
|
@@ -2611,6 +4128,7 @@ var PocketCoderClient = class {
|
|
|
2611
4128
|
this.outputs = new OutputsApi(this.transport);
|
|
2612
4129
|
this.administration = new AdministrationApi(this.transport);
|
|
2613
4130
|
this.terminals = new TerminalsApi(this.transport);
|
|
4131
|
+
this.principals = new PrincipalsApi(this.transport);
|
|
2614
4132
|
this.keys = new KeysApi(this.transport);
|
|
2615
4133
|
this.recovery = new RecoveryApi(this.transport);
|
|
2616
4134
|
}
|
|
@@ -2763,4 +4281,4 @@ var WorkspaceTurnResolver = class {
|
|
|
2763
4281
|
}
|
|
2764
4282
|
};
|
|
2765
4283
|
//#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 };
|
|
4284
|
+
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 };
|