@helmr/sdk 0.1.0-preview.g52eb0eef.b35190749915
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/LICENSE +190 -0
- package/README.md +65 -0
- package/dist/actor.d.ts +3 -0
- package/dist/client-actor.d.ts +32 -0
- package/dist/client-deployment.d.ts +30 -0
- package/dist/client-sandbox.d.ts +26 -0
- package/dist/client-schedule.d.ts +44 -0
- package/dist/client-secret.d.ts +51 -0
- package/dist/client-session.d.ts +38 -0
- package/dist/client-workspace.d.ts +36 -0
- package/dist/client.d.ts +153 -0
- package/dist/config.d.ts +11 -0
- package/dist/contract.d.ts +341 -0
- package/dist/definitions.d.ts +42 -0
- package/dist/fuzzy.d.ts +1 -0
- package/dist/fuzzy.js +29 -0
- package/dist/image.d.ts +63 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +3029 -0
- package/dist/internal/abort.d.ts +1 -0
- package/dist/internal/definition-query.d.ts +10 -0
- package/dist/internal/id.d.ts +1 -0
- package/dist/internal/jsoncanon.d.ts +5 -0
- package/dist/internal/origin.d.ts +1 -0
- package/dist/internal/program.d.ts +10 -0
- package/dist/internal/run-failure.d.ts +2 -0
- package/dist/internal/run-handle.d.ts +3 -0
- package/dist/internal/runtime.d.ts +43 -0
- package/dist/internal/session.d.ts +5 -0
- package/dist/internal/strings.d.ts +1 -0
- package/dist/internal/timestamp.d.ts +1 -0
- package/dist/internal/utf8.d.ts +3 -0
- package/dist/internal.d.ts +13 -0
- package/dist/internal.js +994 -0
- package/dist/logger.d.ts +9 -0
- package/dist/metadata.d.ts +6 -0
- package/dist/request.d.ts +3 -0
- package/dist/schedules.d.ts +33 -0
- package/dist/schema/payload.d.ts +39 -0
- package/dist/schema/task.d.ts +3 -0
- package/dist/secret.d.ts +1 -0
- package/dist/session.d.ts +5 -0
- package/dist/task.d.ts +2 -0
- package/dist/timers.d.ts +6 -0
- package/dist/tokens.d.ts +77 -0
- package/dist/workspace.d.ts +142 -0
- package/package.json +49 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3029 @@
|
|
|
1
|
+
// sdk/typescript/src/schema/payload.ts
|
|
2
|
+
var payloadSchemaValidationErrorBrand = Symbol.for("helmr.sdk.PayloadSchemaValidationError");
|
|
3
|
+
|
|
4
|
+
class PayloadSchemaValidationError extends Error {
|
|
5
|
+
issues;
|
|
6
|
+
constructor(label, issues) {
|
|
7
|
+
super(formatPayloadSchemaValidationMessage(label, issues));
|
|
8
|
+
this.name = "PayloadSchemaValidationError";
|
|
9
|
+
this.issues = issues;
|
|
10
|
+
Object.defineProperty(this, payloadSchemaValidationErrorBrand, { value: true });
|
|
11
|
+
}
|
|
12
|
+
static [Symbol.hasInstance](value) {
|
|
13
|
+
return this === PayloadSchemaValidationError && typeof value === "object" && value !== null && payloadSchemaValidationErrorBrand in value;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function assertPayloadSchema(value, label = "payload") {
|
|
17
|
+
if (value === undefined) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
assertStandardSchema(value, label);
|
|
21
|
+
}
|
|
22
|
+
function assertStandardSchema(value, label = "schema") {
|
|
23
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") {
|
|
24
|
+
throw new Error(`${label} must implement the Standard Schema v1 interface`);
|
|
25
|
+
}
|
|
26
|
+
const standard = value["~standard"];
|
|
27
|
+
if (standard === null || typeof standard !== "object") {
|
|
28
|
+
throw new Error(`${label} must implement the Standard Schema v1 interface`);
|
|
29
|
+
}
|
|
30
|
+
const record = standard;
|
|
31
|
+
if (record["version"] !== 1 || typeof record["validate"] !== "function") {
|
|
32
|
+
throw new Error(`${label} must implement the Standard Schema v1 interface`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function parsePayloadWithSchema(schema, payload, label) {
|
|
36
|
+
assertStandardSchema(schema, label);
|
|
37
|
+
const result = await schema["~standard"].validate(payload);
|
|
38
|
+
if ("issues" in result && result.issues !== undefined) {
|
|
39
|
+
throw new PayloadSchemaValidationError(label, result.issues);
|
|
40
|
+
}
|
|
41
|
+
return result.value;
|
|
42
|
+
}
|
|
43
|
+
function formatPayloadSchemaValidationMessage(label, issues) {
|
|
44
|
+
if (issues.length === 0) {
|
|
45
|
+
return `${label} failed validation`;
|
|
46
|
+
}
|
|
47
|
+
const formattedIssues = issues.slice(0, 5).map(formatPayloadSchemaIssue);
|
|
48
|
+
const suffix = issues.length > formattedIssues.length ? `; and ${issues.length - formattedIssues.length} more` : "";
|
|
49
|
+
return `${label} failed validation: ${formattedIssues.join("; ")}${suffix}`;
|
|
50
|
+
}
|
|
51
|
+
function formatPayloadSchemaIssue(issue) {
|
|
52
|
+
const path = formatPayloadSchemaIssuePath(issue.path);
|
|
53
|
+
return path === "" ? issue.message : `${path}: ${issue.message}`;
|
|
54
|
+
}
|
|
55
|
+
function formatPayloadSchemaIssuePath(path) {
|
|
56
|
+
if (path === undefined || path.length === 0) {
|
|
57
|
+
return "";
|
|
58
|
+
}
|
|
59
|
+
let formatted = "payload";
|
|
60
|
+
for (const segment of path) {
|
|
61
|
+
const key = typeof segment === "object" && segment !== null && "key" in segment ? segment.key : segment;
|
|
62
|
+
if (typeof key === "string" && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
63
|
+
formatted += `.${key}`;
|
|
64
|
+
} else if (typeof key === "string" && isArrayIndexKey(key)) {
|
|
65
|
+
formatted += `[${key}]`;
|
|
66
|
+
} else if (typeof key === "number") {
|
|
67
|
+
formatted += `[${key}]`;
|
|
68
|
+
} else {
|
|
69
|
+
formatted += `[${JSON.stringify(String(key))}]`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return formatted;
|
|
73
|
+
}
|
|
74
|
+
function isArrayIndexKey(value) {
|
|
75
|
+
if (!/^(0|[1-9]\d*)$/.test(value)) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const parsed = Number(value);
|
|
79
|
+
return Number.isSafeInteger(parsed);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// sdk/typescript/src/schema/task.ts
|
|
83
|
+
var TASK_ID_PATTERN = "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$";
|
|
84
|
+
var TASK_ID_MAX_LENGTH = 128;
|
|
85
|
+
var QUEUE_NAME_PATTERN = "^[A-Za-z0-9][A-Za-z0-9._/-]{0,255}$";
|
|
86
|
+
var QUEUE_NAME_MAX_LENGTH = 256;
|
|
87
|
+
|
|
88
|
+
class TaskIdError extends Error {
|
|
89
|
+
name = "TaskIdError";
|
|
90
|
+
value;
|
|
91
|
+
constructor(value) {
|
|
92
|
+
super(`task id must match ${TASK_ID_PATTERN}: ${JSON.stringify(value)}`);
|
|
93
|
+
this.value = value;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function validateTaskId(value) {
|
|
97
|
+
if (!isValidTaskId(value)) {
|
|
98
|
+
throw new TaskIdError(value);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function isValidTaskId(value) {
|
|
102
|
+
if (value.length === 0 || value.length > TASK_ID_MAX_LENGTH) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
const first = value.charCodeAt(0);
|
|
106
|
+
if (!isAsciiAlnum(first)) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
for (let index = 1;index < value.length; index += 1) {
|
|
110
|
+
const code = value.charCodeAt(index);
|
|
111
|
+
if (!(isAsciiAlnum(code) || code === 46 || code === 95 || code === 45)) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
class TaskQueueNameError extends Error {
|
|
119
|
+
name = "TaskQueueNameError";
|
|
120
|
+
value;
|
|
121
|
+
constructor(value) {
|
|
122
|
+
super(`queue name must match ${QUEUE_NAME_PATTERN}: ${JSON.stringify(value)}`);
|
|
123
|
+
this.value = value;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
class TaskQueueConcurrencyLimitError extends Error {
|
|
128
|
+
name = "TaskQueueConcurrencyLimitError";
|
|
129
|
+
value;
|
|
130
|
+
constructor(value) {
|
|
131
|
+
super("queue concurrencyLimit must be a positive integer");
|
|
132
|
+
this.value = value;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function validateQueueName(value) {
|
|
136
|
+
if (!isValidQueueName(value)) {
|
|
137
|
+
throw new TaskQueueNameError(value);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function isValidQueueName(value) {
|
|
141
|
+
if (value.length === 0 || value.length > QUEUE_NAME_MAX_LENGTH) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
const first = value.charCodeAt(0);
|
|
145
|
+
if (!isAsciiAlnum(first)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
for (let index = 1;index < value.length; index += 1) {
|
|
149
|
+
const code = value.charCodeAt(index);
|
|
150
|
+
if (!(isAsciiAlnum(code) || code === 46 || code === 95 || code === 45 || code === 47)) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
function validateOptionalQueueConcurrencyLimit(value) {
|
|
157
|
+
if (value === undefined || value === null) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value > 0) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
throw new TaskQueueConcurrencyLimitError(value);
|
|
164
|
+
}
|
|
165
|
+
function isAsciiAlnum(code) {
|
|
166
|
+
return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// sdk/typescript/src/internal/runtime.ts
|
|
170
|
+
var runtimeOperationsSymbol = Symbol.for("helmr.sdk.v0.runtime_operations");
|
|
171
|
+
function currentRuntimeOperations() {
|
|
172
|
+
const operations = globalThis[runtimeOperationsSymbol];
|
|
173
|
+
if (operations === undefined) {
|
|
174
|
+
throw new Error("runtime operation is unavailable without the Helmr managed runtime");
|
|
175
|
+
}
|
|
176
|
+
return operations;
|
|
177
|
+
}
|
|
178
|
+
function runtimeOperationsInstalled() {
|
|
179
|
+
return globalThis[runtimeOperationsSymbol] !== undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// sdk/typescript/src/internal/run-failure.ts
|
|
183
|
+
function runFailureError(failure) {
|
|
184
|
+
const error = new Error(failure.message);
|
|
185
|
+
error.name = "RunFailure";
|
|
186
|
+
error.code = failure.code;
|
|
187
|
+
error.details = failure.details;
|
|
188
|
+
return error;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// sdk/typescript/src/internal/id.ts
|
|
192
|
+
var uuidV7Pattern = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
193
|
+
function resourceID(value, label) {
|
|
194
|
+
if (typeof value !== "string" || !uuidV7Pattern.test(value)) {
|
|
195
|
+
throw new Error(`${label} must be a canonical UUIDv7`);
|
|
196
|
+
}
|
|
197
|
+
return value;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// sdk/typescript/src/session.ts
|
|
201
|
+
function createRuntimeSessionRef(id) {
|
|
202
|
+
const sessionID = resourceID(id, "Session ID");
|
|
203
|
+
return Object.freeze({
|
|
204
|
+
id: sessionID,
|
|
205
|
+
input: Object.freeze({
|
|
206
|
+
send(input, request, options) {
|
|
207
|
+
return currentRuntimeOperations().actorInputSend(sessionID, input, request, options?.signal);
|
|
208
|
+
}
|
|
209
|
+
}),
|
|
210
|
+
output: Object.freeze({
|
|
211
|
+
list(query, options) {
|
|
212
|
+
return currentRuntimeOperations().sessionOutputPage(sessionID, query, options?.signal);
|
|
213
|
+
}
|
|
214
|
+
}),
|
|
215
|
+
retrieve(options = {}) {
|
|
216
|
+
return currentRuntimeOperations().sessionRetrieve(sessionID, options.signal);
|
|
217
|
+
},
|
|
218
|
+
close(request, options) {
|
|
219
|
+
return currentRuntimeOperations().sessionClose(sessionID, request, options?.signal);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
var sessions = Object.freeze({
|
|
224
|
+
ref(id) {
|
|
225
|
+
return createRuntimeSessionRef(id);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// sdk/typescript/src/internal/run-handle.ts
|
|
230
|
+
function createRunHandle(id) {
|
|
231
|
+
return Object.freeze({
|
|
232
|
+
id: resourceID(id, "Run ID")
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
function runHandleID(value) {
|
|
236
|
+
return resourceID(typeof value === "string" ? value : value.id, "Run ID");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// sdk/typescript/src/definitions.ts
|
|
240
|
+
var privateDefinitionBrand = Symbol.for("helmr.sdk.v0.definition");
|
|
241
|
+
var privateQueueBrand = Symbol.for("helmr.sdk.v0.queue");
|
|
242
|
+
function inspectDefinition(value) {
|
|
243
|
+
if (typeof value !== "object" && typeof value !== "function" || value === null) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (!Object.hasOwn(value, privateDefinitionBrand))
|
|
247
|
+
return;
|
|
248
|
+
const definition = value[privateDefinitionBrand];
|
|
249
|
+
if (!isInternalDefinition(definition)) {
|
|
250
|
+
throw new Error("invalid private definition record");
|
|
251
|
+
}
|
|
252
|
+
return definition;
|
|
253
|
+
}
|
|
254
|
+
function isQueue(value) {
|
|
255
|
+
if (typeof value !== "object" || value === null)
|
|
256
|
+
return false;
|
|
257
|
+
if (!Object.hasOwn(value, privateQueueBrand))
|
|
258
|
+
return false;
|
|
259
|
+
if (value[privateQueueBrand] !== true) {
|
|
260
|
+
throw new Error("invalid private queue record");
|
|
261
|
+
}
|
|
262
|
+
const queue = value;
|
|
263
|
+
if (typeof queue.name !== "string") {
|
|
264
|
+
throw new Error("invalid private queue record");
|
|
265
|
+
}
|
|
266
|
+
validateQueueName(queue.name);
|
|
267
|
+
validateOptionalQueueConcurrencyLimit(queue.concurrencyLimit);
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
function isInternalDefinition(value) {
|
|
271
|
+
if (typeof value !== "object" || value === null)
|
|
272
|
+
return false;
|
|
273
|
+
const definition = value;
|
|
274
|
+
if (typeof definition.id !== "string")
|
|
275
|
+
return false;
|
|
276
|
+
validateTaskId(definition.id);
|
|
277
|
+
switch (definition.kind) {
|
|
278
|
+
case "task":
|
|
279
|
+
if (typeof definition.handler !== "function" || typeof definition.hasPayload !== "boolean") {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
if (definition.hasPayload) {
|
|
283
|
+
assertPayloadSchema(definition.payloadSchema, `task ${JSON.stringify(definition.id)} payload`);
|
|
284
|
+
} else if (Object.hasOwn(definition, "payloadSchema")) {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
return true;
|
|
288
|
+
case "actor":
|
|
289
|
+
return typeof definition.handler === "function";
|
|
290
|
+
default:
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function queue(config) {
|
|
295
|
+
validateQueueName(config.name);
|
|
296
|
+
validateOptionalQueueConcurrencyLimit(config.concurrencyLimit);
|
|
297
|
+
return Object.freeze(Object.defineProperty({
|
|
298
|
+
name: config.name,
|
|
299
|
+
...config.concurrencyLimit === undefined ? {} : { concurrencyLimit: config.concurrencyLimit }
|
|
300
|
+
}, privateQueueBrand, { value: true }));
|
|
301
|
+
}
|
|
302
|
+
function task(config) {
|
|
303
|
+
validateDefinitionDefaults(config, `task ${JSON.stringify(config.id)}`);
|
|
304
|
+
const hasPayload = "payload" in config;
|
|
305
|
+
if (hasPayload) {
|
|
306
|
+
assertPayloadSchema(config.payload, `task ${JSON.stringify(config.id)} payload`);
|
|
307
|
+
}
|
|
308
|
+
const internal = Object.freeze({
|
|
309
|
+
kind: "task",
|
|
310
|
+
id: config.id,
|
|
311
|
+
hasPayload,
|
|
312
|
+
handler: config.run,
|
|
313
|
+
...hasPayload ? { payloadSchema: config.payload } : {},
|
|
314
|
+
...copyDefinitionDefaults(config)
|
|
315
|
+
});
|
|
316
|
+
return createTaskDefinition(internal);
|
|
317
|
+
}
|
|
318
|
+
function actor(config) {
|
|
319
|
+
validateDefinitionDefaults(config, `actor ${JSON.stringify(config.id)}`);
|
|
320
|
+
if (typeof config.run !== "function") {
|
|
321
|
+
throw new Error(`actor ${JSON.stringify(config.id)} run must be a function`);
|
|
322
|
+
}
|
|
323
|
+
const internal = Object.freeze({
|
|
324
|
+
kind: "actor",
|
|
325
|
+
id: config.id,
|
|
326
|
+
handler: config.run,
|
|
327
|
+
...copyDefinitionDefaults(config),
|
|
328
|
+
...config.idleTimeout === undefined ? {} : { idleTimeout: config.idleTimeout }
|
|
329
|
+
});
|
|
330
|
+
const value = {
|
|
331
|
+
id: internal.id,
|
|
332
|
+
async start(options) {
|
|
333
|
+
const started = await currentRuntimeOperations().actorStart(internal.id, options);
|
|
334
|
+
return Object.freeze({
|
|
335
|
+
session: createRuntimeSessionRef(started.sessionId),
|
|
336
|
+
run: createRunHandle(started.runId)
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
return brandDefinition(value, internal);
|
|
341
|
+
}
|
|
342
|
+
function createScheduledTask(config) {
|
|
343
|
+
const base = task({
|
|
344
|
+
id: config.id,
|
|
345
|
+
payload: config.payload,
|
|
346
|
+
run: config.run,
|
|
347
|
+
...config.queue === undefined ? {} : { queue: config.queue },
|
|
348
|
+
...config.maxDuration === undefined ? {} : { maxDuration: config.maxDuration },
|
|
349
|
+
...config.ttl === undefined ? {} : { ttl: config.ttl },
|
|
350
|
+
...config.retry === undefined ? {} : { retry: config.retry }
|
|
351
|
+
});
|
|
352
|
+
const inspected = inspectDefinition(base);
|
|
353
|
+
if (inspected?.kind !== "task") {
|
|
354
|
+
throw new Error("scheduled task construction failed");
|
|
355
|
+
}
|
|
356
|
+
const internal = Object.freeze({
|
|
357
|
+
...inspected,
|
|
358
|
+
schedule: config.schedule
|
|
359
|
+
});
|
|
360
|
+
return brandDefinition({
|
|
361
|
+
id: base.id,
|
|
362
|
+
hasPayload: true,
|
|
363
|
+
start: base.start.bind(base),
|
|
364
|
+
call: base.call.bind(base)
|
|
365
|
+
}, internal);
|
|
366
|
+
}
|
|
367
|
+
function createTaskDefinition(internal) {
|
|
368
|
+
if (internal.hasPayload) {
|
|
369
|
+
return brandDefinition({
|
|
370
|
+
id: internal.id,
|
|
371
|
+
hasPayload: true,
|
|
372
|
+
start(payload, options) {
|
|
373
|
+
return currentRuntimeOperations().taskStart(Object.freeze({ declaredId: internal.id, payloadPresent: true }), payload, options);
|
|
374
|
+
},
|
|
375
|
+
call(payload, options) {
|
|
376
|
+
return taskWait(currentRuntimeOperations().taskCall(Object.freeze({ declaredId: internal.id, payloadPresent: true }), payload, options));
|
|
377
|
+
}
|
|
378
|
+
}, internal);
|
|
379
|
+
}
|
|
380
|
+
return brandDefinition({
|
|
381
|
+
id: internal.id,
|
|
382
|
+
hasPayload: false,
|
|
383
|
+
start(options) {
|
|
384
|
+
return currentRuntimeOperations().taskStart(Object.freeze({ declaredId: internal.id, payloadPresent: false }), undefined, options);
|
|
385
|
+
},
|
|
386
|
+
call(options) {
|
|
387
|
+
return taskWait(currentRuntimeOperations().taskCall(Object.freeze({ declaredId: internal.id, payloadPresent: false }), undefined, options));
|
|
388
|
+
}
|
|
389
|
+
}, internal);
|
|
390
|
+
}
|
|
391
|
+
function brandDefinition(value, internal) {
|
|
392
|
+
Object.defineProperty(value, privateDefinitionBrand, {
|
|
393
|
+
value: internal,
|
|
394
|
+
enumerable: false
|
|
395
|
+
});
|
|
396
|
+
return Object.freeze(value);
|
|
397
|
+
}
|
|
398
|
+
function validateDefinitionDefaults(config, label) {
|
|
399
|
+
validateTaskId(config.id);
|
|
400
|
+
if (typeof config.run !== "function") {
|
|
401
|
+
throw new Error(`${label} run must be a function`);
|
|
402
|
+
}
|
|
403
|
+
if (config.maxDuration !== undefined && typeof config.maxDuration !== "string") {
|
|
404
|
+
throw new Error(`${label} maxDuration must be a duration string`);
|
|
405
|
+
}
|
|
406
|
+
if (typeof config.queue === "string") {
|
|
407
|
+
validateQueueName(config.queue);
|
|
408
|
+
} else if (config.queue !== undefined && !isQueue(config.queue)) {
|
|
409
|
+
throw new Error(`${label} queue must be a queue() definition or queue name`);
|
|
410
|
+
}
|
|
411
|
+
if (config.ttl !== undefined && typeof config.ttl !== "string") {
|
|
412
|
+
throw new Error(`${label} ttl must be a duration string`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
function copyDefinitionDefaults(config) {
|
|
416
|
+
return {
|
|
417
|
+
...config.queue === undefined ? {} : { queue: config.queue },
|
|
418
|
+
...config.maxDuration === undefined ? {} : { maxDuration: config.maxDuration },
|
|
419
|
+
...config.ttl === undefined ? {} : { ttl: config.ttl },
|
|
420
|
+
...config.retry === undefined ? {} : { retry: config.retry }
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
function taskWait(result) {
|
|
424
|
+
return Object.freeze({
|
|
425
|
+
then(onfulfilled, onrejected) {
|
|
426
|
+
return result.then(onfulfilled, onrejected);
|
|
427
|
+
},
|
|
428
|
+
async unwrap() {
|
|
429
|
+
const settled = await result;
|
|
430
|
+
if (!settled.ok)
|
|
431
|
+
throw runFailureError(settled.failure);
|
|
432
|
+
return settled.output;
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
// sdk/typescript/src/internal/abort.ts
|
|
437
|
+
function abortableDelay(milliseconds, signal) {
|
|
438
|
+
signal?.throwIfAborted();
|
|
439
|
+
return new Promise((resolve, reject) => {
|
|
440
|
+
const timer = setTimeout(done, milliseconds);
|
|
441
|
+
function done() {
|
|
442
|
+
signal?.removeEventListener("abort", aborted);
|
|
443
|
+
resolve();
|
|
444
|
+
}
|
|
445
|
+
function aborted() {
|
|
446
|
+
clearTimeout(timer);
|
|
447
|
+
signal?.removeEventListener("abort", aborted);
|
|
448
|
+
try {
|
|
449
|
+
signal?.throwIfAborted();
|
|
450
|
+
} catch (error) {
|
|
451
|
+
reject(error);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
reject(new Error("Operation was aborted"));
|
|
455
|
+
}
|
|
456
|
+
signal?.addEventListener("abort", aborted, { once: true });
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// sdk/typescript/src/internal/timestamp.ts
|
|
461
|
+
var utcRFC3339 = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?Z$/;
|
|
462
|
+
function timestampString(value, label) {
|
|
463
|
+
const match = typeof value === "string" ? utcRFC3339.exec(value) : null;
|
|
464
|
+
if (match === null || !validDateTime(match)) {
|
|
465
|
+
throw new Error(`${label} must be a UTC RFC 3339 timestamp`);
|
|
466
|
+
}
|
|
467
|
+
return value;
|
|
468
|
+
}
|
|
469
|
+
function validDateTime(match) {
|
|
470
|
+
const year = Number(match[1]);
|
|
471
|
+
const month = Number(match[2]);
|
|
472
|
+
const day = Number(match[3]);
|
|
473
|
+
const hour = Number(match[4]);
|
|
474
|
+
const minute = Number(match[5]);
|
|
475
|
+
const second = Number(match[6]);
|
|
476
|
+
if (month < 1 || month > 12 || hour > 23 || minute > 59 || second > 59) {
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
480
|
+
const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
481
|
+
return day >= 1 && day <= days[month - 1];
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// sdk/typescript/src/image.ts
|
|
485
|
+
var imageBrand = Symbol.for("helmr.sdk.v0.image");
|
|
486
|
+
var sourceFileBrand = Symbol.for("helmr.sdk.v0.source-file");
|
|
487
|
+
var sourceDirectoryBrand = Symbol.for("helmr.sdk.v0.source-directory");
|
|
488
|
+
|
|
489
|
+
class Image {
|
|
490
|
+
key;
|
|
491
|
+
steps;
|
|
492
|
+
constructor(key, steps = []) {
|
|
493
|
+
if (key.length === 0) {
|
|
494
|
+
throw new Error("image key must be non-empty");
|
|
495
|
+
}
|
|
496
|
+
this.key = key;
|
|
497
|
+
this.steps = Object.freeze([...steps]);
|
|
498
|
+
Object.defineProperty(this, imageBrand, { value: true });
|
|
499
|
+
Object.freeze(this);
|
|
500
|
+
}
|
|
501
|
+
from(ref, ...unexpected) {
|
|
502
|
+
if (unexpected.length !== 0) {
|
|
503
|
+
throw new Error("image.from() accepts only an image reference");
|
|
504
|
+
}
|
|
505
|
+
return new Image(this.key, [...this.steps, { kind: "from", ref }]);
|
|
506
|
+
}
|
|
507
|
+
run(argv, ...unexpected) {
|
|
508
|
+
if (unexpected.length !== 0) {
|
|
509
|
+
throw new Error("image.run() accepts only argv");
|
|
510
|
+
}
|
|
511
|
+
return new Image(this.key, [
|
|
512
|
+
...this.steps,
|
|
513
|
+
{
|
|
514
|
+
kind: "run",
|
|
515
|
+
argv: Object.freeze([...argv])
|
|
516
|
+
}
|
|
517
|
+
]);
|
|
518
|
+
}
|
|
519
|
+
copy(destination, source) {
|
|
520
|
+
if (isSourceFile(source)) {
|
|
521
|
+
return new Image(this.key, [
|
|
522
|
+
...this.steps,
|
|
523
|
+
{ kind: "copy_source_file", destination, source }
|
|
524
|
+
]);
|
|
525
|
+
}
|
|
526
|
+
if (isSourceDirectory(source)) {
|
|
527
|
+
return new Image(this.key, [
|
|
528
|
+
...this.steps,
|
|
529
|
+
{ kind: "copy_source_directory", destination, source }
|
|
530
|
+
]);
|
|
531
|
+
}
|
|
532
|
+
throw new Error("image.copy() requires source.file() or source.directory()");
|
|
533
|
+
}
|
|
534
|
+
copyFrom(destination, source, sourcePath) {
|
|
535
|
+
if (inspectImage(source) === undefined) {
|
|
536
|
+
throw new Error("image.copyFrom() requires an image() value");
|
|
537
|
+
}
|
|
538
|
+
return new Image(this.key, [
|
|
539
|
+
...this.steps,
|
|
540
|
+
{ kind: "copy_from_image", destination, source, sourcePath }
|
|
541
|
+
]);
|
|
542
|
+
}
|
|
543
|
+
workdir(path) {
|
|
544
|
+
return new Image(this.key, [...this.steps, { kind: "workdir", path }]);
|
|
545
|
+
}
|
|
546
|
+
env(key, value) {
|
|
547
|
+
return new Image(this.key, [...this.steps, { kind: "env", key, value }]);
|
|
548
|
+
}
|
|
549
|
+
user(name) {
|
|
550
|
+
return new Image(this.key, [...this.steps, { kind: "user", name }]);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
class SourceFileValue {
|
|
555
|
+
path;
|
|
556
|
+
constructor(path) {
|
|
557
|
+
this.path = path;
|
|
558
|
+
Object.defineProperty(this, sourceFileBrand, { value: true });
|
|
559
|
+
Object.freeze(this);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
class SourceDirectoryValue {
|
|
564
|
+
path;
|
|
565
|
+
constructor(path) {
|
|
566
|
+
this.path = path;
|
|
567
|
+
Object.defineProperty(this, sourceDirectoryBrand, { value: true });
|
|
568
|
+
Object.freeze(this);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
function image(key) {
|
|
572
|
+
return new Image(key);
|
|
573
|
+
}
|
|
574
|
+
var source = Object.freeze({
|
|
575
|
+
file(path) {
|
|
576
|
+
return new SourceFileValue(path);
|
|
577
|
+
},
|
|
578
|
+
directory(path) {
|
|
579
|
+
return new SourceDirectoryValue(path);
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
function inspectImage(value) {
|
|
583
|
+
if (typeof value !== "object" || value === null || value[imageBrand] !== true) {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
const imageValue = value;
|
|
587
|
+
return { key: imageValue.key, steps: imageValue.steps };
|
|
588
|
+
}
|
|
589
|
+
function isSourceFile(value) {
|
|
590
|
+
return typeof value === "object" && value !== null && value[sourceFileBrand] === true;
|
|
591
|
+
}
|
|
592
|
+
function isSourceDirectory(value) {
|
|
593
|
+
return typeof value === "object" && value !== null && value[sourceDirectoryBrand] === true;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// sdk/typescript/src/secret.ts
|
|
597
|
+
var secretNamePattern = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/;
|
|
598
|
+
function validateSecretName(value) {
|
|
599
|
+
if (typeof value !== "string" || !secretNamePattern.test(value)) {
|
|
600
|
+
throw new Error("Secret name is invalid");
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// sdk/typescript/src/internal/origin.ts
|
|
605
|
+
function canonicalSecretOrigin(value) {
|
|
606
|
+
if (typeof value !== "string" || value.trim() !== value || !/^https:\/\//i.test(value)) {
|
|
607
|
+
throw new Error("Secret origin must be an exact HTTPS origin");
|
|
608
|
+
}
|
|
609
|
+
const authority = value.replace(/^https:\/\//i, "").replace(/\/$/, "");
|
|
610
|
+
if (!/^[A-Za-z0-9.-]+(?::[0-9]+)?$/.test(authority))
|
|
611
|
+
throw new Error("Secret origin must contain only a DNS hostname and optional port");
|
|
612
|
+
const [rawHost, port] = authority.split(":");
|
|
613
|
+
const host = rawHost.toLowerCase();
|
|
614
|
+
if (host.length > 253 || host === "localhost" || host.endsWith(".localhost") || /^[0-9.]+$/.test(host) || host.split(".").some((label) => !/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(label))) {
|
|
615
|
+
throw new Error("Secret origin must use a DNS hostname without wildcards or IP addresses");
|
|
616
|
+
}
|
|
617
|
+
if (port !== undefined && (!/^[1-9][0-9]*$/.test(port) || Number(port) > 65535))
|
|
618
|
+
throw new Error("Secret origin port is invalid");
|
|
619
|
+
return `https://${host}${port === undefined || port === "443" ? "" : `:${port}`}`;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// sdk/typescript/src/workspace.ts
|
|
623
|
+
var sandboxDefinitionBrand = Symbol.for("helmr.sdk.v0.sandbox");
|
|
624
|
+
var workspaceAddressBrand = Symbol.for("helmr.sdk.v0.workspace-address");
|
|
625
|
+
|
|
626
|
+
class Builder {
|
|
627
|
+
id;
|
|
628
|
+
constructor(id) {
|
|
629
|
+
validateTaskId(id);
|
|
630
|
+
this.id = id;
|
|
631
|
+
Object.freeze(this);
|
|
632
|
+
}
|
|
633
|
+
image(value) {
|
|
634
|
+
const inspected = inspectImage(value);
|
|
635
|
+
if (inspected === undefined) {
|
|
636
|
+
throw new Error("sandbox.image() requires an image() value");
|
|
637
|
+
}
|
|
638
|
+
return new ResourceBuilder(this.id, inspected);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
class ResourceBuilder {
|
|
643
|
+
id;
|
|
644
|
+
imageValue;
|
|
645
|
+
constructor(id, imageValue) {
|
|
646
|
+
this.id = id;
|
|
647
|
+
this.imageValue = imageValue;
|
|
648
|
+
Object.freeze(this);
|
|
649
|
+
}
|
|
650
|
+
resources(value) {
|
|
651
|
+
assertResourceMembers(value);
|
|
652
|
+
return new Definition(this.id, this.imageValue, Object.freeze({ cpu: value.cpu, memory: value.memory }));
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
class Definition {
|
|
657
|
+
id;
|
|
658
|
+
internal;
|
|
659
|
+
constructor(id, imageValue, resources) {
|
|
660
|
+
this.id = id;
|
|
661
|
+
this.internal = Object.freeze({
|
|
662
|
+
kind: "sandbox",
|
|
663
|
+
id,
|
|
664
|
+
image: imageValue,
|
|
665
|
+
resources
|
|
666
|
+
});
|
|
667
|
+
Object.defineProperty(this, sandboxDefinitionBrand, { value: true });
|
|
668
|
+
Object.freeze(this);
|
|
669
|
+
}
|
|
670
|
+
createWorkspace(request, options) {
|
|
671
|
+
return currentRuntimeOperations().workspaceCreate(this.id, request, options?.signal).then(({ workspaceId }) => createWorkspaceRef(workspaceId));
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
function sandbox(config) {
|
|
675
|
+
return new Builder(config.id);
|
|
676
|
+
}
|
|
677
|
+
var workspaces = Object.freeze({
|
|
678
|
+
ref: createWorkspaceRef
|
|
679
|
+
});
|
|
680
|
+
function inspectWorkspaceAddress(value) {
|
|
681
|
+
if (typeof value !== "object" || value === null || value[workspaceAddressBrand] !== true) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
const address = value;
|
|
685
|
+
if (address.id === undefined) {
|
|
686
|
+
throw new Error("private Workspace address is invalid");
|
|
687
|
+
}
|
|
688
|
+
return createWorkspaceAddress(address.id);
|
|
689
|
+
}
|
|
690
|
+
function workspaceRefID(value) {
|
|
691
|
+
const address = inspectWorkspaceAddress(value);
|
|
692
|
+
if (address === undefined || typeof address.id !== "string") {
|
|
693
|
+
throw new Error("Workspace requires a Workspace ref");
|
|
694
|
+
}
|
|
695
|
+
return address.id;
|
|
696
|
+
}
|
|
697
|
+
function encodeWorkspaceSecrets(inputs) {
|
|
698
|
+
if (inputs === undefined)
|
|
699
|
+
return Object.freeze([]);
|
|
700
|
+
if (!Array.isArray(inputs) || inputs.length > 64)
|
|
701
|
+
throw new Error("Workspace secrets must be an array of at most 64 bindings");
|
|
702
|
+
const envNames = new Set;
|
|
703
|
+
const files = [];
|
|
704
|
+
let originCount = 0;
|
|
705
|
+
const result = inputs.map((input) => {
|
|
706
|
+
const value = workspaceObject(input, "Workspace Secret");
|
|
707
|
+
validateSecretName(value["secret"]);
|
|
708
|
+
const hasEnv = value["env"] !== undefined;
|
|
709
|
+
const hasFile = value["file"] !== undefined;
|
|
710
|
+
if (hasEnv === hasFile)
|
|
711
|
+
throw new Error("Workspace Secret requires exactly one of env or file");
|
|
712
|
+
exactBindingKeys(value, ["secret", "env", "file"]);
|
|
713
|
+
if (hasEnv) {
|
|
714
|
+
const env = workspaceObject(value["env"], "Workspace Secret env");
|
|
715
|
+
const mode = env["mode"];
|
|
716
|
+
if (mode !== "raw" && mode !== "protected")
|
|
717
|
+
throw new Error("Workspace Secret env requires explicit raw or protected mode");
|
|
718
|
+
exactBindingKeys(env, ["name", "mode", "allowedOrigins"]);
|
|
719
|
+
if (mode === "raw" && env["allowedOrigins"] !== undefined)
|
|
720
|
+
throw new Error("Raw env cannot specify allowedOrigins");
|
|
721
|
+
const name = env["name"];
|
|
722
|
+
if (typeof name !== "string" || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || name.startsWith("HELMR_") || name.startsWith("LD_") || ["NODE_OPTIONS", "NODE_PATH", "NODE_ICU_DATA", "OPENSSL_CONF", "OPENSSL_MODULES", "OPENSSL_ENGINES", "GCONV_PATH", "LOCPATH"].includes(name) || ["SSL_CERT_FILE", "SSL_CERT_DIR", "NODE_EXTRA_CA_CERTS", "NODE_USE_SYSTEM_CA"].includes(name.toUpperCase()))
|
|
723
|
+
throw new Error("Invalid or reserved Secret env name");
|
|
724
|
+
if (envNames.has(name))
|
|
725
|
+
throw new Error(`Duplicate Secret env target ${name}`);
|
|
726
|
+
envNames.add(name);
|
|
727
|
+
if (mode === "raw")
|
|
728
|
+
return Object.freeze({ secret: input.secret, env: Object.freeze({ name, mode }) });
|
|
729
|
+
const origins = env["allowedOrigins"];
|
|
730
|
+
if (!Array.isArray(origins) || origins.length === 0 || origins.length > 16)
|
|
731
|
+
throw new Error("Protected env requires 1 to 16 exact HTTPS origins");
|
|
732
|
+
const allowed_origins = Object.freeze([...new Set(origins.map(canonicalSecretOrigin))].sort());
|
|
733
|
+
originCount += allowed_origins.length;
|
|
734
|
+
return Object.freeze({ secret: input.secret, env: Object.freeze({ name, mode, allowed_origins }) });
|
|
735
|
+
}
|
|
736
|
+
const file = workspaceObject(value["file"], "Workspace Secret file");
|
|
737
|
+
exactBindingKeys(file, ["path"]);
|
|
738
|
+
const path = file["path"];
|
|
739
|
+
if (typeof path !== "string" || path.length > 4096 || !path.startsWith("/") || path === "/" || path.includes("\x00") || path.split("/").slice(1).some((part) => part === "" || part === "." || part === "..") || ["/workspace", "/var/lib/helmr", "/dev", "/opt/helmr", "/proc", "/sys", "/.helmr-old-root", "/run/helmr"].some((root) => path === root || path.startsWith(root + "/")))
|
|
740
|
+
throw new Error("Invalid or reserved Secret file path");
|
|
741
|
+
files.push(path);
|
|
742
|
+
return Object.freeze({ secret: input.secret, file: Object.freeze({ path }) });
|
|
743
|
+
});
|
|
744
|
+
files.sort();
|
|
745
|
+
if (files.some((path, index) => index > 0 && (path === files[index - 1] || path.startsWith(files[index - 1] + "/"))))
|
|
746
|
+
throw new Error("Conflicting Secret file paths");
|
|
747
|
+
if (originCount > 256)
|
|
748
|
+
throw new Error("Workspace Secret origins exceed 256");
|
|
749
|
+
return Object.freeze(result);
|
|
750
|
+
}
|
|
751
|
+
function exactBindingKeys(value, allowed) {
|
|
752
|
+
const unknown = Object.keys(value).find((key) => !allowed.includes(key));
|
|
753
|
+
if (unknown !== undefined)
|
|
754
|
+
throw new Error(`Workspace Secret has unknown member ${JSON.stringify(unknown)}`);
|
|
755
|
+
}
|
|
756
|
+
function inspectSandboxDefinition(value) {
|
|
757
|
+
if (typeof value !== "object" || value === null)
|
|
758
|
+
return;
|
|
759
|
+
if (!Object.hasOwn(value, sandboxDefinitionBrand))
|
|
760
|
+
return;
|
|
761
|
+
if (value[sandboxDefinitionBrand] !== true) {
|
|
762
|
+
throw new Error("invalid private Sandbox record");
|
|
763
|
+
}
|
|
764
|
+
const internal = value.internal;
|
|
765
|
+
if (typeof internal !== "object" || internal === null || internal.kind !== "sandbox" || typeof internal.id !== "string" || typeof internal.image !== "object" || internal.image === null || typeof internal.resources !== "object" || internal.resources === null) {
|
|
766
|
+
throw new Error("invalid private Sandbox record");
|
|
767
|
+
}
|
|
768
|
+
validateTaskId(internal.id);
|
|
769
|
+
return internal;
|
|
770
|
+
}
|
|
771
|
+
function createWorkspaceRef(id) {
|
|
772
|
+
const workspaceID = resourceID(id, "Workspace ID");
|
|
773
|
+
const operations = {
|
|
774
|
+
retrieve(options) {
|
|
775
|
+
return currentRuntimeOperations().workspaceRetrieve(workspaceID, options?.signal);
|
|
776
|
+
},
|
|
777
|
+
exec(request, options) {
|
|
778
|
+
return currentRuntimeOperations().workspaceExec(workspaceID, request, options?.signal);
|
|
779
|
+
},
|
|
780
|
+
delete(request, options) {
|
|
781
|
+
return currentRuntimeOperations().workspaceDelete(workspaceID, request, options?.signal);
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
return brandWorkspaceAddress({ id: workspaceID, ...operations });
|
|
785
|
+
}
|
|
786
|
+
function createWorkspaceAddress(id) {
|
|
787
|
+
return brandWorkspaceAddress({ id: resourceID(id, "Workspace ID") });
|
|
788
|
+
}
|
|
789
|
+
function brandWorkspaceAddress(value) {
|
|
790
|
+
resourceID(value.id, "Workspace ID");
|
|
791
|
+
return freezeWorkspaceAddress(value);
|
|
792
|
+
}
|
|
793
|
+
function freezeWorkspaceAddress(value) {
|
|
794
|
+
Object.defineProperty(value, workspaceAddressBrand, { value: true });
|
|
795
|
+
return Object.freeze(value);
|
|
796
|
+
}
|
|
797
|
+
function parseWorkspace(value) {
|
|
798
|
+
const input = workspaceObject(value, "Workspace response");
|
|
799
|
+
const key = input["key"];
|
|
800
|
+
if (key !== undefined && typeof key !== "string") {
|
|
801
|
+
throw new Error("Workspace response.key must be a string");
|
|
802
|
+
}
|
|
803
|
+
const sandboxId = input["sandbox_id"];
|
|
804
|
+
if (typeof sandboxId !== "string") {
|
|
805
|
+
throw new Error("Workspace response.sandbox_id must be a string");
|
|
806
|
+
}
|
|
807
|
+
validateTaskId(sandboxId);
|
|
808
|
+
const status = input["status"];
|
|
809
|
+
if (status !== "available" && status !== "recovery_required" && status !== "deleting") {
|
|
810
|
+
throw new Error("Workspace response.status is invalid");
|
|
811
|
+
}
|
|
812
|
+
if (!Array.isArray(input["secrets"])) {
|
|
813
|
+
throw new Error("Workspace response.secrets must be an array");
|
|
814
|
+
}
|
|
815
|
+
const owner = parseWorkspaceOwner(input["owner"], "Workspace response.owner");
|
|
816
|
+
return Object.freeze({
|
|
817
|
+
id: resourceID(input["id"], "Workspace response.id"),
|
|
818
|
+
...key === undefined ? {} : { key },
|
|
819
|
+
sandboxId,
|
|
820
|
+
deploymentId: resourceID(input["deployment_id"], "Workspace response.deployment_id"),
|
|
821
|
+
status,
|
|
822
|
+
...owner === undefined ? {} : { owner },
|
|
823
|
+
secrets: Object.freeze(input["secrets"].map(parseWorkspaceSecret)),
|
|
824
|
+
lastActivityAt: workspaceTimestamp(input["last_activity_at"], "last_activity_at"),
|
|
825
|
+
createdAt: workspaceTimestamp(input["created_at"], "created_at"),
|
|
826
|
+
updatedAt: workspaceTimestamp(input["updated_at"], "updated_at")
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
function parseWorkspaceOwner(value, label) {
|
|
830
|
+
if (value === undefined)
|
|
831
|
+
return;
|
|
832
|
+
const input = workspaceObject(value, label);
|
|
833
|
+
const hasSession = input["session_id"] !== undefined;
|
|
834
|
+
const hasRun = input["run_id"] !== undefined;
|
|
835
|
+
if (hasSession === hasRun) {
|
|
836
|
+
throw new Error(`${label} must name exactly one of session_id or run_id`);
|
|
837
|
+
}
|
|
838
|
+
return Object.freeze(hasSession ? { sessionId: resourceID(input["session_id"], `${label}.session_id`) } : { runId: resourceID(input["run_id"], `${label}.run_id`) });
|
|
839
|
+
}
|
|
840
|
+
function parseWorkspaceSecret(value) {
|
|
841
|
+
const wire = workspaceObject(value, "Workspace Secret");
|
|
842
|
+
exactBindingKeys(wire, wire["env"] !== undefined ? ["secret", "env"] : ["secret", "file"]);
|
|
843
|
+
let binding;
|
|
844
|
+
if (wire["env"] !== undefined) {
|
|
845
|
+
const env = workspaceObject(wire["env"], "Workspace Secret env");
|
|
846
|
+
exactBindingKeys(env, ["name", "mode", "allowed_origins"]);
|
|
847
|
+
binding = { secret: wire["secret"], env: { name: env["name"], mode: env["mode"], ...env["allowed_origins"] === undefined ? {} : { allowedOrigins: env["allowed_origins"] } } };
|
|
848
|
+
} else {
|
|
849
|
+
binding = { secret: wire["secret"], file: wire["file"] };
|
|
850
|
+
}
|
|
851
|
+
const normalized = encodeWorkspaceSecrets([binding])[0];
|
|
852
|
+
return normalized.env !== undefined ? Object.freeze({ secret: normalized.secret, env: Object.freeze({ name: normalized.env.name, mode: normalized.env.mode, ...normalized.env.allowed_origins === undefined ? {} : { allowedOrigins: normalized.env.allowed_origins } }) }) : Object.freeze({ secret: normalized.secret, file: normalized.file });
|
|
853
|
+
}
|
|
854
|
+
function parseWorkspaceExecResult(value) {
|
|
855
|
+
const response = workspaceObject(value, "Workspace exec response");
|
|
856
|
+
const exitCode = response["exit_code"];
|
|
857
|
+
if (!Number.isSafeInteger(exitCode)) {
|
|
858
|
+
throw new Error("Workspace exec response.exit_code must be an integer");
|
|
859
|
+
}
|
|
860
|
+
return Object.freeze({
|
|
861
|
+
exitCode,
|
|
862
|
+
stdout: decodeWorkspaceBase64(response["stdout_base64"], "Workspace exec response.stdout_base64"),
|
|
863
|
+
stderr: decodeWorkspaceBase64(response["stderr_base64"], "Workspace exec response.stderr_base64")
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
function parseWorkspaceDeleteReceipt(value) {
|
|
867
|
+
const response = workspaceObject(value, "Workspace delete response");
|
|
868
|
+
return Object.freeze({
|
|
869
|
+
workspaceId: resourceID(response["workspace_id"], "Workspace delete response.workspace_id")
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
function workspaceObject(value, label) {
|
|
873
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
874
|
+
throw new Error(`${label} must be an object`);
|
|
875
|
+
}
|
|
876
|
+
return value;
|
|
877
|
+
}
|
|
878
|
+
function workspaceTimestamp(value, field) {
|
|
879
|
+
return timestampString(value, `Workspace response.${field}`);
|
|
880
|
+
}
|
|
881
|
+
function decodeWorkspaceBase64(value, label) {
|
|
882
|
+
if (typeof value !== "string") {
|
|
883
|
+
throw new Error(`${label} must be canonical padded base64`);
|
|
884
|
+
}
|
|
885
|
+
let binary;
|
|
886
|
+
try {
|
|
887
|
+
binary = atob(value);
|
|
888
|
+
} catch {
|
|
889
|
+
throw new Error(`${label} must be canonical padded base64`);
|
|
890
|
+
}
|
|
891
|
+
if (btoa(binary) !== value) {
|
|
892
|
+
throw new Error(`${label} must be canonical padded base64`);
|
|
893
|
+
}
|
|
894
|
+
const output = new Uint8Array(binary.length);
|
|
895
|
+
for (let index = 0;index < binary.length; index++) {
|
|
896
|
+
output[index] = binary.charCodeAt(index);
|
|
897
|
+
}
|
|
898
|
+
return output;
|
|
899
|
+
}
|
|
900
|
+
function assertResourceMembers(value) {
|
|
901
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || !Object.hasOwn(value, "cpu") || !Object.hasOwn(value, "memory")) {
|
|
902
|
+
throw new Error("workspace resources require cpu and memory");
|
|
903
|
+
}
|
|
904
|
+
const members = Object.keys(value);
|
|
905
|
+
if (members.length !== 2 || !members.includes("cpu") || !members.includes("memory")) {
|
|
906
|
+
throw new Error("workspace resources support only cpu and memory");
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// sdk/typescript/src/client-workspace.ts
|
|
911
|
+
function createClientWorkspaces(transport) {
|
|
912
|
+
return Object.freeze({
|
|
913
|
+
async retrieve(id, options = {}) {
|
|
914
|
+
const workspaceID = resourceID(id, "Workspace ID");
|
|
915
|
+
return parseWorkspace(await transport.request("GET", `/v1/workspaces/${encodeURIComponent(workspaceID)}`, options.signal === undefined ? {} : { signal: options.signal }));
|
|
916
|
+
},
|
|
917
|
+
async list(queryInput = {}, options = {}) {
|
|
918
|
+
const query = workspaceListQuery(queryInput);
|
|
919
|
+
const response = objectValue(await transport.request("GET", `/v1/workspaces${query}`, options.signal === undefined ? {} : { signal: options.signal }), "Workspace list response");
|
|
920
|
+
if (!Array.isArray(response["workspaces"])) {
|
|
921
|
+
throw new Error("Workspace list response.workspaces must be an array");
|
|
922
|
+
}
|
|
923
|
+
const nextCursor = response["next_cursor"];
|
|
924
|
+
if (nextCursor !== undefined && typeof nextCursor !== "string") {
|
|
925
|
+
throw new Error("Workspace list response.next_cursor must be a string");
|
|
926
|
+
}
|
|
927
|
+
return Object.freeze({
|
|
928
|
+
items: Object.freeze(response["workspaces"].map(parseWorkspaceListItem)),
|
|
929
|
+
...nextCursor === undefined ? {} : { nextCursor }
|
|
930
|
+
});
|
|
931
|
+
},
|
|
932
|
+
ref(id) {
|
|
933
|
+
return createClientWorkspaceRef(resourceID(id, "Workspace ID"), transport);
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
function parseWorkspaceListItem(value) {
|
|
938
|
+
const input = objectValue(value, "Workspace list item");
|
|
939
|
+
const key = input["key"];
|
|
940
|
+
if (key !== undefined && typeof key !== "string") {
|
|
941
|
+
throw new Error("Workspace list item.key must be a string");
|
|
942
|
+
}
|
|
943
|
+
const sandboxId = input["sandbox_id"];
|
|
944
|
+
if (typeof sandboxId !== "string") {
|
|
945
|
+
throw new Error("Workspace list item.sandbox_id must be a string");
|
|
946
|
+
}
|
|
947
|
+
validateTaskId(sandboxId);
|
|
948
|
+
const status = input["status"];
|
|
949
|
+
if (status !== "available" && status !== "recovery_required" && status !== "deleting") {
|
|
950
|
+
throw new Error("Workspace list item.status is invalid");
|
|
951
|
+
}
|
|
952
|
+
const owner = parseWorkspaceOwner(input["owner"], "Workspace list item.owner");
|
|
953
|
+
return Object.freeze({
|
|
954
|
+
id: resourceID(input["id"], "Workspace list item.id"),
|
|
955
|
+
...key === undefined ? {} : { key },
|
|
956
|
+
sandboxId,
|
|
957
|
+
deploymentId: resourceID(input["deployment_id"], "Workspace list item.deployment_id"),
|
|
958
|
+
status,
|
|
959
|
+
...owner === undefined ? {} : { owner },
|
|
960
|
+
lastActivityAt: workspaceTimestamp2(input["last_activity_at"], "last_activity_at"),
|
|
961
|
+
createdAt: workspaceTimestamp2(input["created_at"], "created_at"),
|
|
962
|
+
updatedAt: workspaceTimestamp2(input["updated_at"], "updated_at")
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
function workspaceTimestamp2(value, field) {
|
|
966
|
+
return timestampString(value, `Workspace list item.${field}`);
|
|
967
|
+
}
|
|
968
|
+
function createClientWorkspaceRef(id, transport) {
|
|
969
|
+
const workspaceID = resourceID(id, "Workspace ID");
|
|
970
|
+
const path = `/v1/workspaces/${encodeURIComponent(workspaceID)}`;
|
|
971
|
+
const ref = {
|
|
972
|
+
id: workspaceID,
|
|
973
|
+
async retrieve(options = {}) {
|
|
974
|
+
return parseWorkspace(await transport.request("GET", path, options.signal === undefined ? {} : { signal: options.signal }));
|
|
975
|
+
},
|
|
976
|
+
async exec(request, options = {}) {
|
|
977
|
+
let process = parseWorkspaceExecProcess(await transport.request("POST", `${path}/exec`, {
|
|
978
|
+
body: {
|
|
979
|
+
command: [...request.command],
|
|
980
|
+
...request.cwd === undefined ? {} : { cwd: request.cwd },
|
|
981
|
+
...request.env === undefined ? {} : { env: request.env },
|
|
982
|
+
...request.stdin === undefined ? {} : { stdin_base64: encodeBase64(request.stdin) },
|
|
983
|
+
...request.timeout === undefined ? {} : { timeout: request.timeout },
|
|
984
|
+
idempotency_key: request.idempotencyKey
|
|
985
|
+
},
|
|
986
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
987
|
+
}));
|
|
988
|
+
const admittedProcessId = process.processId;
|
|
989
|
+
while (process.status === "pending" || process.status === "running") {
|
|
990
|
+
await abortableDelay(1000, options.signal);
|
|
991
|
+
process = parseWorkspaceExecProcess(await transport.request("GET", `${path}/exec/${encodeURIComponent(admittedProcessId)}`, options.signal === undefined ? {} : { signal: options.signal }));
|
|
992
|
+
if (process.processId !== admittedProcessId) {
|
|
993
|
+
throw new Error("Workspace exec poll response changed process ID");
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
if (process.status === "failed")
|
|
997
|
+
throw workspaceExecFailure(process.terminalReasonCode);
|
|
998
|
+
if (process.status !== "exited") {
|
|
999
|
+
throw new Error("Workspace exec process response did not reach a terminal status");
|
|
1000
|
+
}
|
|
1001
|
+
return process.result;
|
|
1002
|
+
},
|
|
1003
|
+
async delete(request = {}, options = {}) {
|
|
1004
|
+
return parseWorkspaceDeleteReceipt(await transport.request("DELETE", path, {
|
|
1005
|
+
body: request.idempotencyKey === undefined ? {} : { idempotency_key: request.idempotencyKey },
|
|
1006
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
1007
|
+
}));
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
return brandWorkspaceAddress(ref);
|
|
1011
|
+
}
|
|
1012
|
+
function parseWorkspaceExecProcess(value) {
|
|
1013
|
+
const response = objectValue(value, "Workspace exec process response");
|
|
1014
|
+
const processId = resourceID(response["process_id"], "Workspace exec process response.process_id");
|
|
1015
|
+
switch (response["status"]) {
|
|
1016
|
+
case "pending":
|
|
1017
|
+
case "running":
|
|
1018
|
+
return Object.freeze({ processId, status: response["status"] });
|
|
1019
|
+
case "exited":
|
|
1020
|
+
return Object.freeze({
|
|
1021
|
+
processId,
|
|
1022
|
+
status: "exited",
|
|
1023
|
+
result: parseWorkspaceExecResult(response)
|
|
1024
|
+
});
|
|
1025
|
+
case "failed": {
|
|
1026
|
+
const error = objectValue(response["error"], "Workspace exec process response.error");
|
|
1027
|
+
const terminalReasonCode = error["terminal_reason_code"];
|
|
1028
|
+
if (typeof terminalReasonCode !== "string" || ![
|
|
1029
|
+
"workspace_exec_timed_out",
|
|
1030
|
+
"workspace_exec_output_limit_exceeded",
|
|
1031
|
+
"workspace_exec_placement_timed_out",
|
|
1032
|
+
"workspace_exec_failed"
|
|
1033
|
+
].includes(terminalReasonCode)) {
|
|
1034
|
+
throw new Error("Workspace exec process response.error.terminal_reason_code is invalid");
|
|
1035
|
+
}
|
|
1036
|
+
return Object.freeze({ processId, status: "failed", terminalReasonCode });
|
|
1037
|
+
}
|
|
1038
|
+
default:
|
|
1039
|
+
throw new Error("Workspace exec process response.status is invalid");
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
function workspaceExecFailure(code) {
|
|
1043
|
+
const message = code === "workspace_exec_timed_out" ? "workspace exec timed out" : code === "workspace_exec_output_limit_exceeded" ? "workspace exec output limit was exceeded" : code === "workspace_exec_placement_timed_out" ? "workspace exec placement timed out" : "workspace exec failed";
|
|
1044
|
+
const error = new Error(message);
|
|
1045
|
+
error.name = "HelmrError";
|
|
1046
|
+
error.code = code;
|
|
1047
|
+
return error;
|
|
1048
|
+
}
|
|
1049
|
+
function workspaceListQuery(queryInput) {
|
|
1050
|
+
if (queryInput.key !== undefined && (queryInput.cursor !== undefined || queryInput.limit !== undefined)) {
|
|
1051
|
+
throw new Error("Workspace exact key lookup does not accept cursor or limit");
|
|
1052
|
+
}
|
|
1053
|
+
const query = new URLSearchParams;
|
|
1054
|
+
if (queryInput.key !== undefined) {
|
|
1055
|
+
if (queryInput.key.length === 0)
|
|
1056
|
+
throw new Error("Workspace key is required");
|
|
1057
|
+
query.set("key", queryInput.key);
|
|
1058
|
+
}
|
|
1059
|
+
if (queryInput.cursor !== undefined) {
|
|
1060
|
+
if (queryInput.cursor.length === 0)
|
|
1061
|
+
throw new Error("Workspace cursor is required");
|
|
1062
|
+
query.set("cursor", queryInput.cursor);
|
|
1063
|
+
}
|
|
1064
|
+
if (queryInput.limit !== undefined) {
|
|
1065
|
+
if (!Number.isInteger(queryInput.limit) || queryInput.limit < 1 || queryInput.limit > 100) {
|
|
1066
|
+
throw new Error("Workspace limit must be an integer in [1,100]");
|
|
1067
|
+
}
|
|
1068
|
+
query.set("limit", queryInput.limit.toString());
|
|
1069
|
+
}
|
|
1070
|
+
return query.size === 0 ? "" : `?${query.toString()}`;
|
|
1071
|
+
}
|
|
1072
|
+
function encodeBase64(value) {
|
|
1073
|
+
let binary = "";
|
|
1074
|
+
for (const byte of value)
|
|
1075
|
+
binary += String.fromCharCode(byte);
|
|
1076
|
+
return btoa(binary);
|
|
1077
|
+
}
|
|
1078
|
+
function objectValue(value, label) {
|
|
1079
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1080
|
+
throw new Error(`${label} must be an object`);
|
|
1081
|
+
}
|
|
1082
|
+
return value;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// sdk/typescript/src/internal/definition-query.ts
|
|
1086
|
+
function definitionItemQuery(queryInput, label) {
|
|
1087
|
+
if ("cursor" in queryInput || "limit" in queryInput) {
|
|
1088
|
+
throw new Error(`${label} item query does not accept cursor or limit`);
|
|
1089
|
+
}
|
|
1090
|
+
const query = new URLSearchParams;
|
|
1091
|
+
if (queryInput.deploymentId !== undefined) {
|
|
1092
|
+
query.set("deployment_id", resourceID(queryInput.deploymentId, "Deployment ID"));
|
|
1093
|
+
}
|
|
1094
|
+
return query.size === 0 ? "" : `?${query.toString()}`;
|
|
1095
|
+
}
|
|
1096
|
+
function definitionListQuery(queryInput, label) {
|
|
1097
|
+
const query = new URLSearchParams;
|
|
1098
|
+
if (queryInput.deploymentId !== undefined) {
|
|
1099
|
+
query.set("deployment_id", resourceID(queryInput.deploymentId, "Deployment ID"));
|
|
1100
|
+
}
|
|
1101
|
+
if (queryInput.cursor !== undefined) {
|
|
1102
|
+
if (queryInput.cursor.length === 0)
|
|
1103
|
+
throw new Error(`${label} cursor is required`);
|
|
1104
|
+
query.set("cursor", queryInput.cursor);
|
|
1105
|
+
}
|
|
1106
|
+
if (queryInput.limit !== undefined) {
|
|
1107
|
+
if (!Number.isInteger(queryInput.limit) || queryInput.limit < 1 || queryInput.limit > 100) {
|
|
1108
|
+
throw new Error(`${label} limit must be an integer in [1,100]`);
|
|
1109
|
+
}
|
|
1110
|
+
query.set("limit", queryInput.limit.toString());
|
|
1111
|
+
}
|
|
1112
|
+
return query.size === 0 ? "" : `?${query.toString()}`;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
// sdk/typescript/src/client-sandbox.ts
|
|
1116
|
+
function createClientSandboxes(transport) {
|
|
1117
|
+
return Object.freeze({
|
|
1118
|
+
async retrieve(id, query = {}, options = {}) {
|
|
1119
|
+
validateTaskId(id);
|
|
1120
|
+
return parseSandboxInfo(await transport.request("GET", `/v1/sandboxes/${encodeURIComponent(id)}${definitionItemQuery(query, "Sandbox")}`, options.signal === undefined ? {} : { signal: options.signal }));
|
|
1121
|
+
},
|
|
1122
|
+
async list(queryInput = {}, options = {}) {
|
|
1123
|
+
const response = objectValue2(await transport.request("GET", `/v1/sandboxes${definitionListQuery(queryInput, "Sandbox")}`, options.signal === undefined ? {} : { signal: options.signal }), "Sandbox list response");
|
|
1124
|
+
if (!Array.isArray(response["sandboxes"])) {
|
|
1125
|
+
throw new Error("Sandbox list response.sandboxes must be an array");
|
|
1126
|
+
}
|
|
1127
|
+
const nextCursor = response["next_cursor"];
|
|
1128
|
+
if (nextCursor !== undefined && typeof nextCursor !== "string") {
|
|
1129
|
+
throw new Error("Sandbox list response.next_cursor must be a string");
|
|
1130
|
+
}
|
|
1131
|
+
return Object.freeze({
|
|
1132
|
+
deploymentId: resourceID(response["deployment_id"], "Sandbox list response.deployment_id"),
|
|
1133
|
+
items: Object.freeze(response["sandboxes"].map(parseSandboxListItem)),
|
|
1134
|
+
...nextCursor === undefined ? {} : { nextCursor }
|
|
1135
|
+
});
|
|
1136
|
+
},
|
|
1137
|
+
async createWorkspace(id, request = {}, options = {}) {
|
|
1138
|
+
validateTaskId(id);
|
|
1139
|
+
const secrets = encodeWorkspaceSecrets(request.secrets);
|
|
1140
|
+
const workspace = parseWorkspace(await transport.request("POST", `/v1/sandboxes/${encodeURIComponent(id)}/workspaces`, {
|
|
1141
|
+
body: {
|
|
1142
|
+
...request.key === undefined ? {} : { key: request.key },
|
|
1143
|
+
...request.secrets === undefined ? {} : { secrets },
|
|
1144
|
+
...request.idempotencyKey === undefined ? {} : { idempotency_key: request.idempotencyKey }
|
|
1145
|
+
},
|
|
1146
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
1147
|
+
}));
|
|
1148
|
+
return createClientWorkspaceRef(workspace.id, transport);
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
function parseSandboxInfo(value) {
|
|
1153
|
+
const input = objectValue2(value, "Sandbox response");
|
|
1154
|
+
const id = input["id"];
|
|
1155
|
+
if (typeof id !== "string")
|
|
1156
|
+
throw new Error("Sandbox response.id must be a string");
|
|
1157
|
+
validateTaskId(id);
|
|
1158
|
+
return Object.freeze({
|
|
1159
|
+
id,
|
|
1160
|
+
deploymentId: resourceID(input["deployment_id"], "Sandbox response.deployment_id")
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
function parseSandboxListItem(value) {
|
|
1164
|
+
const input = objectValue2(value, "Sandbox list item");
|
|
1165
|
+
const id = input["id"];
|
|
1166
|
+
if (typeof id !== "string")
|
|
1167
|
+
throw new Error("Sandbox list item.id must be a string");
|
|
1168
|
+
validateTaskId(id);
|
|
1169
|
+
return Object.freeze({ id });
|
|
1170
|
+
}
|
|
1171
|
+
function objectValue2(value, label) {
|
|
1172
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1173
|
+
throw new Error(`${label} must be an object`);
|
|
1174
|
+
}
|
|
1175
|
+
return value;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// sdk/typescript/src/internal/utf8.ts
|
|
1179
|
+
var encoder = new TextEncoder;
|
|
1180
|
+
var encode = TextEncoder.prototype.encode.call.bind(TextEncoder.prototype.encode);
|
|
1181
|
+
var charCodeAt = String.prototype.charCodeAt.call.bind(String.prototype.charCodeAt);
|
|
1182
|
+
function compareUTF8(left, right) {
|
|
1183
|
+
const leftBytes = encode(encoder, left);
|
|
1184
|
+
const rightBytes = encode(encoder, right);
|
|
1185
|
+
const length = leftBytes.length < rightBytes.length ? leftBytes.length : rightBytes.length;
|
|
1186
|
+
for (let index = 0;index < length; index++) {
|
|
1187
|
+
const difference = leftBytes[index] - rightBytes[index];
|
|
1188
|
+
if (difference !== 0)
|
|
1189
|
+
return difference;
|
|
1190
|
+
}
|
|
1191
|
+
return leftBytes.length - rightBytes.length;
|
|
1192
|
+
}
|
|
1193
|
+
function hasOnlyUnicodeScalarValues(value) {
|
|
1194
|
+
for (let index = 0;index < value.length; index++) {
|
|
1195
|
+
const unit = charCodeAt(value, index);
|
|
1196
|
+
if (unit >= 55296 && unit <= 56319) {
|
|
1197
|
+
if (index + 1 === value.length)
|
|
1198
|
+
return false;
|
|
1199
|
+
const next = charCodeAt(value, index + 1);
|
|
1200
|
+
if (next < 56320 || next > 57343)
|
|
1201
|
+
return false;
|
|
1202
|
+
index++;
|
|
1203
|
+
} else if (unit >= 56320 && unit <= 57343) {
|
|
1204
|
+
return false;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
return true;
|
|
1208
|
+
}
|
|
1209
|
+
function assertUnicodeString(value) {
|
|
1210
|
+
if (!hasOnlyUnicodeScalarValues(value)) {
|
|
1211
|
+
throw new Error("canonical JSON contains an unpaired surrogate");
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// sdk/typescript/src/internal/jsoncanon.ts
|
|
1216
|
+
var textEncoder = new TextEncoder;
|
|
1217
|
+
function canonicalizeJsonValue(value) {
|
|
1218
|
+
return textEncoder.encode(serialize(value, new Set));
|
|
1219
|
+
}
|
|
1220
|
+
function serialize(value, ancestors) {
|
|
1221
|
+
if (value === null || typeof value === "boolean") {
|
|
1222
|
+
return String(value);
|
|
1223
|
+
}
|
|
1224
|
+
if (typeof value === "number") {
|
|
1225
|
+
if (!Number.isFinite(value)) {
|
|
1226
|
+
throw new Error("canonical JSON numbers must be finite IEEE 754 doubles");
|
|
1227
|
+
}
|
|
1228
|
+
return JSON.stringify(value);
|
|
1229
|
+
}
|
|
1230
|
+
if (typeof value === "string") {
|
|
1231
|
+
assertUnicodeString(value);
|
|
1232
|
+
return JSON.stringify(value);
|
|
1233
|
+
}
|
|
1234
|
+
if (typeof value !== "object") {
|
|
1235
|
+
throw new Error(`canonical JSON does not support ${typeof value}`);
|
|
1236
|
+
}
|
|
1237
|
+
if (ancestors.has(value)) {
|
|
1238
|
+
throw new Error("canonical JSON does not support cyclic values");
|
|
1239
|
+
}
|
|
1240
|
+
ancestors.add(value);
|
|
1241
|
+
try {
|
|
1242
|
+
if (Array.isArray(value)) {
|
|
1243
|
+
assertPlainArray(value);
|
|
1244
|
+
const items = value.map((item) => serialize(item, ancestors));
|
|
1245
|
+
return `[${items.join(",")}]`;
|
|
1246
|
+
}
|
|
1247
|
+
const objectValue3 = value;
|
|
1248
|
+
assertPlainObject(objectValue3);
|
|
1249
|
+
const entries = Object.keys(objectValue3).sort().map((key) => {
|
|
1250
|
+
assertUnicodeString(key);
|
|
1251
|
+
return `${JSON.stringify(key)}:${serialize(objectValue3[key], ancestors)}`;
|
|
1252
|
+
});
|
|
1253
|
+
return `{${entries.join(",")}}`;
|
|
1254
|
+
} finally {
|
|
1255
|
+
ancestors.delete(value);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
function assertPlainArray(value) {
|
|
1259
|
+
const keys = Reflect.ownKeys(value);
|
|
1260
|
+
const expected = Array.from({ length: value.length }, (_, index) => String(index));
|
|
1261
|
+
expected.push("length");
|
|
1262
|
+
if (keys.length !== expected.length || keys.some((key, index) => key !== expected[index])) {
|
|
1263
|
+
throw new Error("canonical JSON arrays must be dense and have no extra properties");
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
function assertPlainObject(value) {
|
|
1267
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1268
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
1269
|
+
throw new Error("canonical JSON objects must have a plain or null prototype");
|
|
1270
|
+
}
|
|
1271
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
1272
|
+
if (typeof key !== "string") {
|
|
1273
|
+
throw new Error("canonical JSON objects cannot have symbol properties");
|
|
1274
|
+
}
|
|
1275
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1276
|
+
if (!descriptor?.enumerable || !("value" in descriptor)) {
|
|
1277
|
+
throw new Error("canonical JSON object properties must be enumerable data properties");
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
// sdk/typescript/src/internal/session.ts
|
|
1283
|
+
function parseSession(value) {
|
|
1284
|
+
const input = objectValue3(value, "Session response");
|
|
1285
|
+
const status = sessionStatus(input["status"]);
|
|
1286
|
+
const failure = input["failure"] === undefined ? undefined : parseSessionFailure(input["failure"]);
|
|
1287
|
+
const terminalFailure = status === "cancelled" || status === "failed";
|
|
1288
|
+
if (terminalFailure !== (failure !== undefined)) {
|
|
1289
|
+
throw new Error("Session response has an inconsistent failure projection");
|
|
1290
|
+
}
|
|
1291
|
+
if (failure !== undefined && status === "cancelled" !== (failure.code === "cancelled")) {
|
|
1292
|
+
throw new Error("Session response failure code is inconsistent with status");
|
|
1293
|
+
}
|
|
1294
|
+
const actorId = requiredString(input, "actor_id", "Session response");
|
|
1295
|
+
validateTaskId(actorId);
|
|
1296
|
+
return Object.freeze({
|
|
1297
|
+
id: resourceID(input["id"], "Session response.id"),
|
|
1298
|
+
actorId,
|
|
1299
|
+
deploymentId: resourceID(input["deployment_id"], "Session response.deployment_id"),
|
|
1300
|
+
workspaceId: resourceID(input["workspace_id"], "Session response.workspace_id"),
|
|
1301
|
+
...input["key"] === undefined ? {} : { key: requiredString(input, "key", "Session response") },
|
|
1302
|
+
status,
|
|
1303
|
+
createdAt: timestampString(input["created_at"], "Session response.created_at"),
|
|
1304
|
+
updatedAt: timestampString(input["updated_at"], "Session response.updated_at"),
|
|
1305
|
+
...input["current_run_id"] === undefined ? {} : { currentRunId: resourceID(input["current_run_id"], "Session response.current_run_id") },
|
|
1306
|
+
...failure === undefined ? {} : { failure }
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
function parseSessionInputRecord(value) {
|
|
1310
|
+
const input = objectValue3(value, "Session input response");
|
|
1311
|
+
if (!Object.hasOwn(input, "data")) {
|
|
1312
|
+
throw new Error("Session input response.data is required");
|
|
1313
|
+
}
|
|
1314
|
+
const data = input["data"];
|
|
1315
|
+
canonicalizeJsonValue(data);
|
|
1316
|
+
const source2 = objectValue3(input["source"], "Session input response.source");
|
|
1317
|
+
const type = source2["type"];
|
|
1318
|
+
if (type !== "external" && type !== "run") {
|
|
1319
|
+
throw new Error("Session input response.source.type is invalid");
|
|
1320
|
+
}
|
|
1321
|
+
const parsedSource = type === "external" ? Object.freeze({ type }) : Object.freeze({
|
|
1322
|
+
type,
|
|
1323
|
+
runId: resourceID(source2["run_id"], "Session input response.source.run_id")
|
|
1324
|
+
});
|
|
1325
|
+
return Object.freeze({
|
|
1326
|
+
id: resourceID(input["id"], "Session input response.id"),
|
|
1327
|
+
sequence: safeSequence(input["sequence"], "Session input response.sequence"),
|
|
1328
|
+
data,
|
|
1329
|
+
source: parsedSource,
|
|
1330
|
+
createdAt: timestampString(input["created_at"], "Session input response.created_at")
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
function parseSessionOutputRecord(value) {
|
|
1334
|
+
const input = objectValue3(value, "Session output record");
|
|
1335
|
+
if (!Object.hasOwn(input, "data")) {
|
|
1336
|
+
throw new Error("Session output record.data is required");
|
|
1337
|
+
}
|
|
1338
|
+
const provenance = objectValue3(input["provenance"], "Session output provenance");
|
|
1339
|
+
return Object.freeze({
|
|
1340
|
+
id: resourceID(input["id"], "Session output record.id"),
|
|
1341
|
+
sequence: safeSequence(input["sequence"], "Session output record.sequence"),
|
|
1342
|
+
data: input["data"],
|
|
1343
|
+
contentType: requiredString(input, "content_type", "Session output record"),
|
|
1344
|
+
createdAt: timestampString(input["created_at"], "Session output record.created_at"),
|
|
1345
|
+
provenance: Object.freeze({
|
|
1346
|
+
runId: resourceID(provenance["run_id"], "Session output provenance.run_id"),
|
|
1347
|
+
attemptNumber: safeSequence(provenance["attempt_number"], "Session output provenance.attempt_number"),
|
|
1348
|
+
deploymentId: resourceID(provenance["deployment_id"], "Session output provenance.deployment_id")
|
|
1349
|
+
})
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
function parseSessionFailure(value) {
|
|
1353
|
+
const input = objectValue3(value, "Session failure");
|
|
1354
|
+
const code = requiredString(input, "code", "Session failure");
|
|
1355
|
+
const details = objectValue3(input["details"], "Session failure.details");
|
|
1356
|
+
const runId = details["run_id"] === undefined ? undefined : resourceID(details["run_id"], "Session failure.details.run_id");
|
|
1357
|
+
return Object.freeze({
|
|
1358
|
+
code,
|
|
1359
|
+
message: requiredString(input, "message", "Session failure"),
|
|
1360
|
+
details: Object.freeze(runId === undefined ? {} : { runId })
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
function sessionStatus(value, label = "Session response.status") {
|
|
1364
|
+
if (value !== "open" && value !== "closed" && value !== "cancelled" && value !== "failed") {
|
|
1365
|
+
throw new Error(`${label} is invalid`);
|
|
1366
|
+
}
|
|
1367
|
+
return value;
|
|
1368
|
+
}
|
|
1369
|
+
function requiredString(value, field, label) {
|
|
1370
|
+
const result = value[field];
|
|
1371
|
+
if (typeof result !== "string" || result.length === 0) {
|
|
1372
|
+
throw new Error(`${label}.${field} must be a non-empty string`);
|
|
1373
|
+
}
|
|
1374
|
+
return result;
|
|
1375
|
+
}
|
|
1376
|
+
function objectValue3(value, label) {
|
|
1377
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1378
|
+
throw new Error(`${label} must be an object`);
|
|
1379
|
+
}
|
|
1380
|
+
return value;
|
|
1381
|
+
}
|
|
1382
|
+
function safeSequence(value, label) {
|
|
1383
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
1384
|
+
throw new Error(`${label} must be a non-negative safe integer`);
|
|
1385
|
+
}
|
|
1386
|
+
return value;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
// sdk/typescript/src/client-session.ts
|
|
1390
|
+
function createClientSessions(transport) {
|
|
1391
|
+
return Object.freeze({
|
|
1392
|
+
async retrieve(id, options = {}) {
|
|
1393
|
+
const sessionID = resourceID(id, "Session ID");
|
|
1394
|
+
return parseSession(await transport.request("GET", `/v1/sessions/${encodeURIComponent(sessionID)}`, options.signal === undefined ? {} : { signal: options.signal }));
|
|
1395
|
+
},
|
|
1396
|
+
async list(queryInput = {}, options = {}) {
|
|
1397
|
+
const query = sessionListQuery(queryInput);
|
|
1398
|
+
const response = objectValue4(await transport.request("GET", `/v1/sessions${query}`, {
|
|
1399
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
1400
|
+
}), "Session list response");
|
|
1401
|
+
if (!Array.isArray(response["sessions"])) {
|
|
1402
|
+
throw new Error("Session list response.sessions must be an array");
|
|
1403
|
+
}
|
|
1404
|
+
const nextCursor = response["next_cursor"];
|
|
1405
|
+
if (nextCursor !== undefined && typeof nextCursor !== "string") {
|
|
1406
|
+
throw new Error("Session list response.next_cursor must be a string");
|
|
1407
|
+
}
|
|
1408
|
+
return Object.freeze({
|
|
1409
|
+
items: Object.freeze(response["sessions"].map(parseSession)),
|
|
1410
|
+
...nextCursor === undefined ? {} : { nextCursor }
|
|
1411
|
+
});
|
|
1412
|
+
},
|
|
1413
|
+
ref(id) {
|
|
1414
|
+
return createSessionRef(resourceID(id, "Session ID"), transport);
|
|
1415
|
+
}
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
function createSessionRef(id, transport) {
|
|
1419
|
+
const sessionID = resourceID(id, "Session ID");
|
|
1420
|
+
const basePath = `/v1/sessions/${encodeURIComponent(sessionID)}`;
|
|
1421
|
+
const readRecordPage = async (direction, label, parseRecord, queryInput, options) => {
|
|
1422
|
+
const query = new URLSearchParams;
|
|
1423
|
+
if (queryInput.after !== undefined) {
|
|
1424
|
+
query.set("after", safeSequence2(queryInput.after, `${label} after`).toString());
|
|
1425
|
+
}
|
|
1426
|
+
if (queryInput.limit !== undefined) {
|
|
1427
|
+
if (!Number.isInteger(queryInput.limit) || queryInput.limit < 1 || queryInput.limit > 100) {
|
|
1428
|
+
throw new Error(`${label} limit must be an integer in [1,100]`);
|
|
1429
|
+
}
|
|
1430
|
+
query.set("limit", queryInput.limit.toString());
|
|
1431
|
+
}
|
|
1432
|
+
const suffix = query.size === 0 ? "" : `?${query.toString()}`;
|
|
1433
|
+
const response = objectValue4(await transport.request("GET", `${basePath}/${direction}${suffix}`, options.signal === undefined ? {} : { signal: options.signal }), `${label} response`);
|
|
1434
|
+
if (!Array.isArray(response["records"])) {
|
|
1435
|
+
throw new Error(`${label} response.records must be an array`);
|
|
1436
|
+
}
|
|
1437
|
+
return Object.freeze({
|
|
1438
|
+
records: Object.freeze(response["records"].map(parseRecord)),
|
|
1439
|
+
nextAfter: safeSequence2(response["next_after"], `${label} next_after`),
|
|
1440
|
+
hasMore: requiredBoolean(response, "has_more", `${label} response`)
|
|
1441
|
+
});
|
|
1442
|
+
};
|
|
1443
|
+
const output = Object.freeze({
|
|
1444
|
+
async list(query = {}, options = {}) {
|
|
1445
|
+
return readRecordPage("outputs", "Session output", parseSessionOutputRecord, query, options);
|
|
1446
|
+
}
|
|
1447
|
+
});
|
|
1448
|
+
return Object.freeze({
|
|
1449
|
+
id: sessionID,
|
|
1450
|
+
input: Object.freeze({
|
|
1451
|
+
async list(query = {}, options = {}) {
|
|
1452
|
+
return readRecordPage("inputs", "Session input", parseSessionInputRecord, query, options);
|
|
1453
|
+
},
|
|
1454
|
+
async send(input, request = {}, options = {}) {
|
|
1455
|
+
return parseSessionInputRecord(await transport.request("POST", `${basePath}/inputs`, {
|
|
1456
|
+
body: {
|
|
1457
|
+
input,
|
|
1458
|
+
...request.idempotencyKey === undefined ? {} : { idempotency_key: request.idempotencyKey }
|
|
1459
|
+
},
|
|
1460
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
1461
|
+
}));
|
|
1462
|
+
}
|
|
1463
|
+
}),
|
|
1464
|
+
output,
|
|
1465
|
+
async retrieve(options = {}) {
|
|
1466
|
+
return parseSession(await transport.request("GET", basePath, options.signal === undefined ? {} : { signal: options.signal }));
|
|
1467
|
+
},
|
|
1468
|
+
async close(request = {}, options = {}) {
|
|
1469
|
+
const response = objectValue4(await transport.request("POST", `${basePath}/close`, {
|
|
1470
|
+
body: request.idempotencyKey === undefined ? {} : { idempotency_key: request.idempotencyKey },
|
|
1471
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
1472
|
+
}), "Session close response");
|
|
1473
|
+
return Object.freeze({
|
|
1474
|
+
sessionId: resourceID(response["session_id"], "Session close response.session_id"),
|
|
1475
|
+
acceptedAt: timestampString(response["accepted_at"], "Session close response.accepted_at")
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
});
|
|
1479
|
+
}
|
|
1480
|
+
function sessionListQuery(queryInput) {
|
|
1481
|
+
const exact = queryInput.actorId !== undefined || queryInput.key !== undefined;
|
|
1482
|
+
if (exact && (queryInput.actorId === undefined || queryInput.key === undefined)) {
|
|
1483
|
+
throw new Error("Session exact key lookup requires actorId and key");
|
|
1484
|
+
}
|
|
1485
|
+
if (exact && (queryInput.status !== undefined || queryInput.cursor !== undefined || queryInput.limit !== undefined)) {
|
|
1486
|
+
throw new Error("Session exact key lookup does not accept status, cursor or limit");
|
|
1487
|
+
}
|
|
1488
|
+
const query = new URLSearchParams;
|
|
1489
|
+
const statuses = queryInput.status === undefined ? [] : Array.isArray(queryInput.status) ? queryInput.status : [queryInput.status];
|
|
1490
|
+
for (const status of statuses) {
|
|
1491
|
+
query.append("status", sessionStatus(status, "Session list status"));
|
|
1492
|
+
}
|
|
1493
|
+
if (queryInput.actorId !== undefined) {
|
|
1494
|
+
validateTaskId(queryInput.actorId);
|
|
1495
|
+
query.set("actor_id", queryInput.actorId);
|
|
1496
|
+
}
|
|
1497
|
+
if (queryInput.key !== undefined) {
|
|
1498
|
+
if (queryInput.key.length === 0)
|
|
1499
|
+
throw new Error("Session key is required");
|
|
1500
|
+
query.set("key", queryInput.key);
|
|
1501
|
+
}
|
|
1502
|
+
if (queryInput.cursor !== undefined) {
|
|
1503
|
+
if (queryInput.cursor.length === 0)
|
|
1504
|
+
throw new Error("Session cursor is required");
|
|
1505
|
+
query.set("cursor", queryInput.cursor);
|
|
1506
|
+
}
|
|
1507
|
+
if (queryInput.limit !== undefined) {
|
|
1508
|
+
if (!Number.isInteger(queryInput.limit) || queryInput.limit < 1 || queryInput.limit > 100) {
|
|
1509
|
+
throw new Error("Session limit must be an integer in [1,100]");
|
|
1510
|
+
}
|
|
1511
|
+
query.set("limit", queryInput.limit.toString());
|
|
1512
|
+
}
|
|
1513
|
+
return query.size === 0 ? "" : `?${query.toString()}`;
|
|
1514
|
+
}
|
|
1515
|
+
function objectValue4(value, label) {
|
|
1516
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1517
|
+
throw new Error(`${label} must be an object`);
|
|
1518
|
+
}
|
|
1519
|
+
return value;
|
|
1520
|
+
}
|
|
1521
|
+
function requiredBoolean(value, field, label) {
|
|
1522
|
+
const result = value[field];
|
|
1523
|
+
if (typeof result !== "boolean")
|
|
1524
|
+
throw new Error(`${label}.${field} must be a boolean`);
|
|
1525
|
+
return result;
|
|
1526
|
+
}
|
|
1527
|
+
function safeSequence2(value, label) {
|
|
1528
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
1529
|
+
throw new Error(`${label} must be a non-negative safe integer`);
|
|
1530
|
+
}
|
|
1531
|
+
return value;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
// sdk/typescript/src/client-actor.ts
|
|
1535
|
+
function createClientActors(transport) {
|
|
1536
|
+
return Object.freeze({
|
|
1537
|
+
async retrieve(id, query = {}, options = {}) {
|
|
1538
|
+
validateTaskId(id);
|
|
1539
|
+
return parseActorInfo(await transport.request("GET", `/v1/actors/${encodeURIComponent(id)}${definitionItemQuery(query, "Actor")}`, options.signal === undefined ? {} : { signal: options.signal }));
|
|
1540
|
+
},
|
|
1541
|
+
async list(queryInput = {}, options = {}) {
|
|
1542
|
+
const response = objectValue5(await transport.request("GET", `/v1/actors${definitionListQuery(queryInput, "Actor")}`, options.signal === undefined ? {} : { signal: options.signal }), "Actor list response");
|
|
1543
|
+
if (!Array.isArray(response["actors"])) {
|
|
1544
|
+
throw new Error("Actor list response.actors must be an array");
|
|
1545
|
+
}
|
|
1546
|
+
const nextCursor = response["next_cursor"];
|
|
1547
|
+
if (nextCursor !== undefined && typeof nextCursor !== "string") {
|
|
1548
|
+
throw new Error("Actor list response.next_cursor must be a string");
|
|
1549
|
+
}
|
|
1550
|
+
return Object.freeze({
|
|
1551
|
+
deploymentId: resourceID(response["deployment_id"], "Actor list response.deployment_id"),
|
|
1552
|
+
items: Object.freeze(response["actors"].map(parseActorListItem)),
|
|
1553
|
+
...nextCursor === undefined ? {} : { nextCursor }
|
|
1554
|
+
});
|
|
1555
|
+
},
|
|
1556
|
+
async start(id, request, options = {}) {
|
|
1557
|
+
validateTaskId(id);
|
|
1558
|
+
const response = objectValue5(await transport.request("POST", `/v1/actors/${encodeURIComponent(id)}/start`, {
|
|
1559
|
+
body: actorStartBody(request),
|
|
1560
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
1561
|
+
}), "Actor start response");
|
|
1562
|
+
const sessionID = resourceID(response["session_id"], "Actor start response.session_id");
|
|
1563
|
+
const runID = resourceID(response["run_id"], "Actor start response.run_id");
|
|
1564
|
+
return Object.freeze({
|
|
1565
|
+
session: createSessionRef(sessionID, transport),
|
|
1566
|
+
run: createRunHandle(runID)
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
function parseActorInfo(value) {
|
|
1572
|
+
const input = objectValue5(value, "Actor response");
|
|
1573
|
+
const id = requiredString2(input, "id", "Actor response");
|
|
1574
|
+
validateTaskId(id);
|
|
1575
|
+
return Object.freeze({
|
|
1576
|
+
id,
|
|
1577
|
+
deploymentId: resourceID(input["deployment_id"], "Actor response.deployment_id")
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
function parseActorListItem(value) {
|
|
1581
|
+
const input = objectValue5(value, "Actor list item");
|
|
1582
|
+
const id = requiredString2(input, "id", "Actor list item");
|
|
1583
|
+
validateTaskId(id);
|
|
1584
|
+
return Object.freeze({ id });
|
|
1585
|
+
}
|
|
1586
|
+
function actorStartBody(request) {
|
|
1587
|
+
return {
|
|
1588
|
+
...request.key === undefined ? {} : { key: request.key },
|
|
1589
|
+
...request.input === undefined ? {} : { input: request.input },
|
|
1590
|
+
...request.idempotencyKey === undefined ? {} : { idempotency_key: request.idempotencyKey },
|
|
1591
|
+
workspace: { id: workspaceRefID(request.workspace) },
|
|
1592
|
+
...request.run === undefined ? {} : { run: runOptionsBody(request.run) }
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
function runOptionsBody(run) {
|
|
1596
|
+
return {
|
|
1597
|
+
...run.queue === undefined ? {} : { queue: run.queue },
|
|
1598
|
+
...run.concurrencyKey === undefined ? {} : { concurrency_key: run.concurrencyKey },
|
|
1599
|
+
...run.priority === undefined ? {} : { priority: run.priority },
|
|
1600
|
+
...run.ttl === undefined ? {} : { ttl: run.ttl },
|
|
1601
|
+
...run.retry === undefined ? {} : {
|
|
1602
|
+
retry: run.retry.enabled === false ? { enabled: false } : {
|
|
1603
|
+
...run.retry.enabled === undefined ? {} : { enabled: run.retry.enabled },
|
|
1604
|
+
max_attempts: run.retry.maxAttempts,
|
|
1605
|
+
...run.retry.backoff === undefined ? {} : {
|
|
1606
|
+
backoff: {
|
|
1607
|
+
...run.retry.backoff.minDelay === undefined ? {} : { min_delay: run.retry.backoff.minDelay },
|
|
1608
|
+
...run.retry.backoff.maxDelay === undefined ? {} : { max_delay: run.retry.backoff.maxDelay },
|
|
1609
|
+
...run.retry.backoff.factor === undefined ? {} : { factor: run.retry.backoff.factor },
|
|
1610
|
+
...run.retry.backoff.jitter === undefined ? {} : { jitter: run.retry.backoff.jitter }
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
},
|
|
1615
|
+
...run.metadata === undefined ? {} : { metadata: run.metadata },
|
|
1616
|
+
...run.tags === undefined ? {} : { tags: [...run.tags] }
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1619
|
+
function objectValue5(value, label) {
|
|
1620
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1621
|
+
throw new Error(`${label} must be an object`);
|
|
1622
|
+
}
|
|
1623
|
+
return value;
|
|
1624
|
+
}
|
|
1625
|
+
function requiredString2(value, field, label) {
|
|
1626
|
+
const result = value[field];
|
|
1627
|
+
if (typeof result !== "string" || result.length === 0) {
|
|
1628
|
+
throw new Error(`${label}.${field} must be a non-empty string`);
|
|
1629
|
+
}
|
|
1630
|
+
return result;
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
// sdk/typescript/src/client-deployment.ts
|
|
1634
|
+
function createClientDeployments(transport) {
|
|
1635
|
+
return Object.freeze({
|
|
1636
|
+
async list(queryInput = {}, options = {}) {
|
|
1637
|
+
const query = new URLSearchParams;
|
|
1638
|
+
if (queryInput.cursor !== undefined) {
|
|
1639
|
+
if (queryInput.cursor.length === 0)
|
|
1640
|
+
throw new Error("Deployment cursor is required");
|
|
1641
|
+
query.set("cursor", queryInput.cursor);
|
|
1642
|
+
}
|
|
1643
|
+
if (queryInput.limit !== undefined) {
|
|
1644
|
+
if (!Number.isInteger(queryInput.limit) || queryInput.limit < 1 || queryInput.limit > 100) {
|
|
1645
|
+
throw new Error("Deployment limit must be an integer in [1,100]");
|
|
1646
|
+
}
|
|
1647
|
+
query.set("limit", String(queryInput.limit));
|
|
1648
|
+
}
|
|
1649
|
+
const suffix = query.size === 0 ? "" : `?${query.toString()}`;
|
|
1650
|
+
const response = deploymentObject(await transport.request("GET", `/v1/deployments${suffix}`, options.signal === undefined ? {} : { signal: options.signal }), "Deployment list response");
|
|
1651
|
+
if (!Array.isArray(response["deployments"])) {
|
|
1652
|
+
throw new Error("Deployment list response.deployments must be an array");
|
|
1653
|
+
}
|
|
1654
|
+
const nextCursor = response["next_cursor"];
|
|
1655
|
+
if (nextCursor !== undefined && typeof nextCursor !== "string") {
|
|
1656
|
+
throw new Error("Deployment list response.next_cursor must be a string");
|
|
1657
|
+
}
|
|
1658
|
+
return Object.freeze({
|
|
1659
|
+
items: Object.freeze(response["deployments"].map(parseDeploymentListItem)),
|
|
1660
|
+
...nextCursor === undefined ? {} : { nextCursor }
|
|
1661
|
+
});
|
|
1662
|
+
},
|
|
1663
|
+
async current(options = {}) {
|
|
1664
|
+
try {
|
|
1665
|
+
return parseDeployment(await transport.request("GET", "/v1/deployments/current", options.signal === undefined ? {} : { signal: options.signal }));
|
|
1666
|
+
} catch (error) {
|
|
1667
|
+
if (errorCode(error) === "no_current_deployment")
|
|
1668
|
+
return null;
|
|
1669
|
+
throw error;
|
|
1670
|
+
}
|
|
1671
|
+
},
|
|
1672
|
+
async retrieve(deploymentId, options = {}) {
|
|
1673
|
+
return parseDeployment(await transport.request("GET", `/v1/deployments/${encodeURIComponent(resourceID(deploymentId, "Deployment ID"))}`, options.signal === undefined ? {} : { signal: options.signal }));
|
|
1674
|
+
}
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
function parseDeploymentListItem(value) {
|
|
1678
|
+
const input = deploymentObject(value, "Deployment list item");
|
|
1679
|
+
return Object.freeze({
|
|
1680
|
+
id: resourceID(input["id"], "Deployment list item.id"),
|
|
1681
|
+
version: requiredString3(input, "version", "Deployment list item"),
|
|
1682
|
+
bundleDigest: requiredString3(input, "bundle_digest", "Deployment list item"),
|
|
1683
|
+
createdAt: timestamp(input, "created_at", "Deployment list item")
|
|
1684
|
+
});
|
|
1685
|
+
}
|
|
1686
|
+
function errorCode(value) {
|
|
1687
|
+
if (value === null || typeof value !== "object")
|
|
1688
|
+
return;
|
|
1689
|
+
const code = value.code;
|
|
1690
|
+
return typeof code === "string" ? code : undefined;
|
|
1691
|
+
}
|
|
1692
|
+
function parseDeployment(value) {
|
|
1693
|
+
const input = deploymentObject(value, "Deployment response");
|
|
1694
|
+
const deployment = {
|
|
1695
|
+
id: resourceID(input["id"], "Deployment response.id"),
|
|
1696
|
+
version: requiredString3(input, "version"),
|
|
1697
|
+
bundleDigest: requiredString3(input, "bundle_digest"),
|
|
1698
|
+
createdAt: timestamp(input, "created_at", "Deployment response")
|
|
1699
|
+
};
|
|
1700
|
+
return Object.freeze(deployment);
|
|
1701
|
+
}
|
|
1702
|
+
function deploymentObject(value, label) {
|
|
1703
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1704
|
+
throw new Error(`${label} must be an object`);
|
|
1705
|
+
}
|
|
1706
|
+
return value;
|
|
1707
|
+
}
|
|
1708
|
+
function requiredString3(value, field, label = "Deployment response") {
|
|
1709
|
+
const result = value[field];
|
|
1710
|
+
if (typeof result !== "string" || result === "") {
|
|
1711
|
+
throw new Error(`${label}.${field} must be a non-empty string`);
|
|
1712
|
+
}
|
|
1713
|
+
return result;
|
|
1714
|
+
}
|
|
1715
|
+
function timestamp(value, field, label) {
|
|
1716
|
+
return timestampString(value[field], `${label}.${field}`);
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
// sdk/typescript/src/client-schedule.ts
|
|
1720
|
+
function createClientSchedules(transport) {
|
|
1721
|
+
return Object.freeze({
|
|
1722
|
+
async retrieve(scheduleId, options = {}) {
|
|
1723
|
+
return parseSchedule(await transport.request("GET", `/v1/schedules/${encodeURIComponent(resourceID(scheduleId, "Schedule ID"))}`, options.signal === undefined ? {} : { signal: options.signal }));
|
|
1724
|
+
},
|
|
1725
|
+
async list(query = {}, options = {}) {
|
|
1726
|
+
const values = new URLSearchParams;
|
|
1727
|
+
if (query.taskId !== undefined) {
|
|
1728
|
+
if (query.cursor !== undefined || query.limit !== undefined) {
|
|
1729
|
+
throw new Error("Schedule exact task lookup does not accept cursor or limit");
|
|
1730
|
+
}
|
|
1731
|
+
validateTaskId(query.taskId);
|
|
1732
|
+
values.set("task_id", query.taskId);
|
|
1733
|
+
}
|
|
1734
|
+
if (query.cursor !== undefined) {
|
|
1735
|
+
if (query.cursor.length === 0)
|
|
1736
|
+
throw new Error("Schedule cursor is required");
|
|
1737
|
+
values.set("cursor", query.cursor);
|
|
1738
|
+
}
|
|
1739
|
+
if (query.limit !== undefined) {
|
|
1740
|
+
if (!Number.isInteger(query.limit) || query.limit < 1 || query.limit > 100) {
|
|
1741
|
+
throw new Error("Schedule limit must be an integer in [1,100]");
|
|
1742
|
+
}
|
|
1743
|
+
values.set("limit", String(query.limit));
|
|
1744
|
+
}
|
|
1745
|
+
const suffix = values.size === 0 ? "" : `?${values}`;
|
|
1746
|
+
const response = scheduleObject(await transport.request("GET", `/v1/schedules${suffix}`, options.signal === undefined ? {} : { signal: options.signal }), "Schedule list response");
|
|
1747
|
+
if (!Array.isArray(response["schedules"])) {
|
|
1748
|
+
throw new Error("Schedule list response.schedules must be an array");
|
|
1749
|
+
}
|
|
1750
|
+
const nextCursor = response["next_cursor"];
|
|
1751
|
+
if (nextCursor !== undefined && typeof nextCursor !== "string") {
|
|
1752
|
+
throw new Error("Schedule list response.next_cursor must be a string");
|
|
1753
|
+
}
|
|
1754
|
+
return Object.freeze({
|
|
1755
|
+
items: Object.freeze(response["schedules"].map(parseSchedule)),
|
|
1756
|
+
...nextCursor === undefined ? {} : { nextCursor }
|
|
1757
|
+
});
|
|
1758
|
+
}
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1761
|
+
function parseSchedule(value) {
|
|
1762
|
+
const input = scheduleObject(value, "Schedule response");
|
|
1763
|
+
const cron = scheduleObject(input["cron"], "Schedule cron");
|
|
1764
|
+
const status = input["status"];
|
|
1765
|
+
if (status !== "active" && status !== "errored" && status !== "archived") {
|
|
1766
|
+
throw new Error("Schedule response.status is invalid");
|
|
1767
|
+
}
|
|
1768
|
+
const lastFailure = input["last_failure"] === undefined ? undefined : parseScheduleFailure(input["last_failure"]);
|
|
1769
|
+
if (status === "errored" && lastFailure === undefined) {
|
|
1770
|
+
throw new Error("Errored Schedule response must contain last_failure");
|
|
1771
|
+
}
|
|
1772
|
+
return Object.freeze({
|
|
1773
|
+
id: resourceID(input["id"], "Schedule response.id"),
|
|
1774
|
+
taskId: requiredString4(input, "task_id", "Schedule response"),
|
|
1775
|
+
generation: positiveInteger(input["generation"], "generation"),
|
|
1776
|
+
effectiveFrom: timestamp2(input["effective_from"], "effective_from"),
|
|
1777
|
+
cron: Object.freeze({
|
|
1778
|
+
pattern: requiredString4(cron, "pattern", "Schedule cron"),
|
|
1779
|
+
timezone: requiredString4(cron, "timezone", "Schedule cron")
|
|
1780
|
+
}),
|
|
1781
|
+
status,
|
|
1782
|
+
...lastFailure === undefined ? {} : { lastFailure },
|
|
1783
|
+
...input["next_fire_at"] === undefined ? {} : { nextFireAt: timestamp2(input["next_fire_at"], "next_fire_at") },
|
|
1784
|
+
...input["last_fire_at"] === undefined ? {} : { lastFireAt: timestamp2(input["last_fire_at"], "last_fire_at") },
|
|
1785
|
+
createdAt: timestamp2(input["created_at"], "created_at"),
|
|
1786
|
+
updatedAt: timestamp2(input["updated_at"], "updated_at")
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
function parseScheduleFailure(value) {
|
|
1790
|
+
const input = scheduleObject(value, "Schedule failure");
|
|
1791
|
+
const code = requiredString4(input, "code", "Schedule failure");
|
|
1792
|
+
return Object.freeze({
|
|
1793
|
+
code,
|
|
1794
|
+
message: requiredString4(input, "message", "Schedule failure"),
|
|
1795
|
+
details: Object.freeze({ ...scheduleObject(input["details"], "Schedule failure.details") })
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
function scheduleObject(value, label) {
|
|
1799
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1800
|
+
throw new Error(`${label} must be an object`);
|
|
1801
|
+
}
|
|
1802
|
+
return value;
|
|
1803
|
+
}
|
|
1804
|
+
function requiredString4(value, field, label) {
|
|
1805
|
+
const result = value[field];
|
|
1806
|
+
if (typeof result !== "string" || result === "") {
|
|
1807
|
+
throw new Error(`${label}.${field} must be a non-empty string`);
|
|
1808
|
+
}
|
|
1809
|
+
return result;
|
|
1810
|
+
}
|
|
1811
|
+
function timestamp2(value, field) {
|
|
1812
|
+
return timestampString(value, `Schedule response.${field}`);
|
|
1813
|
+
}
|
|
1814
|
+
function positiveInteger(value, field) {
|
|
1815
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
1816
|
+
throw new Error(`Schedule response.${field} must be a positive integer`);
|
|
1817
|
+
}
|
|
1818
|
+
return value;
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
// sdk/typescript/src/client-secret.ts
|
|
1822
|
+
function createClientSecrets(transport) {
|
|
1823
|
+
return Object.freeze({
|
|
1824
|
+
async create(request, options = {}) {
|
|
1825
|
+
validateSecretName(request.name);
|
|
1826
|
+
if (typeof request.value !== "string") {
|
|
1827
|
+
throw new Error("Secret create request.value must be a string");
|
|
1828
|
+
}
|
|
1829
|
+
validateOptionalIdempotencyKey(request.idempotencyKey);
|
|
1830
|
+
return parseSecret(await transport.request("POST", "/v1/secrets", {
|
|
1831
|
+
body: {
|
|
1832
|
+
name: request.name,
|
|
1833
|
+
value: request.value,
|
|
1834
|
+
...request.idempotencyKey === undefined ? {} : { idempotency_key: request.idempotencyKey }
|
|
1835
|
+
},
|
|
1836
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
1837
|
+
}));
|
|
1838
|
+
},
|
|
1839
|
+
async retrieve(id, options = {}) {
|
|
1840
|
+
const secretID = resourceID(id, "Secret ID");
|
|
1841
|
+
return parseSecret(await transport.request("GET", `/v1/secrets/${encodeURIComponent(secretID)}`, options.signal === undefined ? {} : { signal: options.signal }));
|
|
1842
|
+
},
|
|
1843
|
+
ref(id) {
|
|
1844
|
+
return createSecretRef(id, transport);
|
|
1845
|
+
},
|
|
1846
|
+
async list(queryInput = {}, options = {}) {
|
|
1847
|
+
const query = new URLSearchParams;
|
|
1848
|
+
if (queryInput.name !== undefined) {
|
|
1849
|
+
validateSecretName(queryInput.name);
|
|
1850
|
+
if (queryInput.cursor !== undefined || queryInput.limit !== undefined) {
|
|
1851
|
+
throw new Error("Secret exact name lookup does not accept cursor or limit");
|
|
1852
|
+
}
|
|
1853
|
+
query.set("name", queryInput.name);
|
|
1854
|
+
}
|
|
1855
|
+
if (queryInput.cursor !== undefined) {
|
|
1856
|
+
if (queryInput.cursor.trim() === "") {
|
|
1857
|
+
throw new Error("Secret list query.cursor must not be empty");
|
|
1858
|
+
}
|
|
1859
|
+
query.set("cursor", queryInput.cursor);
|
|
1860
|
+
}
|
|
1861
|
+
if (queryInput.limit !== undefined) {
|
|
1862
|
+
if (!Number.isInteger(queryInput.limit) || queryInput.limit < 1 || queryInput.limit > 100) {
|
|
1863
|
+
throw new Error("Secret list query.limit must be an integer in [1,100]");
|
|
1864
|
+
}
|
|
1865
|
+
query.set("limit", String(queryInput.limit));
|
|
1866
|
+
}
|
|
1867
|
+
const suffix = query.size === 0 ? "" : `?${query.toString()}`;
|
|
1868
|
+
const response = objectValue6(await transport.request("GET", `/v1/secrets${suffix}`, {
|
|
1869
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
1870
|
+
}), "Secret list response");
|
|
1871
|
+
if (!Array.isArray(response["secrets"])) {
|
|
1872
|
+
throw new Error("Secret list response.secrets must be an array");
|
|
1873
|
+
}
|
|
1874
|
+
const nextCursor = response["next_cursor"];
|
|
1875
|
+
if (nextCursor !== undefined && typeof nextCursor !== "string") {
|
|
1876
|
+
throw new Error("Secret list response.next_cursor must be a string");
|
|
1877
|
+
}
|
|
1878
|
+
return Object.freeze({
|
|
1879
|
+
items: Object.freeze(response["secrets"].map((value) => parseSecret(value))),
|
|
1880
|
+
...nextCursor === undefined ? {} : { nextCursor }
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
function createSecretRef(id, transport) {
|
|
1886
|
+
const secretID = resourceID(id, "Secret ID");
|
|
1887
|
+
const path = `/v1/secrets/${encodeURIComponent(secretID)}`;
|
|
1888
|
+
return Object.freeze({
|
|
1889
|
+
id: secretID,
|
|
1890
|
+
async rotate(request, options = {}) {
|
|
1891
|
+
if (typeof request.value !== "string") {
|
|
1892
|
+
throw new Error("Secret rotate request.value must be a string");
|
|
1893
|
+
}
|
|
1894
|
+
validateIdempotencyKey(request.idempotencyKey);
|
|
1895
|
+
return parseSecret(await transport.request("POST", `${path}/rotate`, {
|
|
1896
|
+
body: {
|
|
1897
|
+
value: request.value,
|
|
1898
|
+
idempotency_key: request.idempotencyKey
|
|
1899
|
+
},
|
|
1900
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
1901
|
+
}));
|
|
1902
|
+
},
|
|
1903
|
+
async revoke(request, options = {}) {
|
|
1904
|
+
validateIdempotencyKey(request.idempotencyKey);
|
|
1905
|
+
return parseSecret(await transport.request("POST", `${path}/revoke`, {
|
|
1906
|
+
body: { idempotency_key: request.idempotencyKey },
|
|
1907
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
1908
|
+
}));
|
|
1909
|
+
}
|
|
1910
|
+
});
|
|
1911
|
+
}
|
|
1912
|
+
function parseSecret(value) {
|
|
1913
|
+
const input = objectValue6(value, "Secret response");
|
|
1914
|
+
const id = input["id"];
|
|
1915
|
+
const name = input["name"];
|
|
1916
|
+
const status = input["status"];
|
|
1917
|
+
if (typeof id !== "string")
|
|
1918
|
+
throw new Error("Secret response.id must be a string");
|
|
1919
|
+
if (typeof name !== "string") {
|
|
1920
|
+
throw new Error("Secret response.name must be a string");
|
|
1921
|
+
}
|
|
1922
|
+
if (status !== "active" && status !== "revoked") {
|
|
1923
|
+
throw new Error("Secret response.status must be active or revoked");
|
|
1924
|
+
}
|
|
1925
|
+
const canonicalID = resourceID(id, "Secret response.id");
|
|
1926
|
+
validateSecretName(name);
|
|
1927
|
+
return Object.freeze({
|
|
1928
|
+
id: canonicalID,
|
|
1929
|
+
name,
|
|
1930
|
+
status,
|
|
1931
|
+
createdAt: dateValue(input["created_at"], "Secret response.created_at"),
|
|
1932
|
+
...optionalDate(input, "rotated_at", "rotatedAt"),
|
|
1933
|
+
...optionalDate(input, "revoked_at", "revokedAt")
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
function optionalDate(input, wireName, fieldName) {
|
|
1937
|
+
const value = input[wireName];
|
|
1938
|
+
return value === undefined ? {} : { [fieldName]: dateValue(value, `Secret response.${wireName}`) };
|
|
1939
|
+
}
|
|
1940
|
+
function dateValue(value, label) {
|
|
1941
|
+
return timestampString(value, label);
|
|
1942
|
+
}
|
|
1943
|
+
function validateOptionalIdempotencyKey(value) {
|
|
1944
|
+
if (value !== undefined)
|
|
1945
|
+
validateIdempotencyKey(value);
|
|
1946
|
+
}
|
|
1947
|
+
function validateIdempotencyKey(value) {
|
|
1948
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
1949
|
+
throw new Error("Secret idempotencyKey is required");
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
function objectValue6(value, label) {
|
|
1953
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1954
|
+
throw new Error(`${label} must be an object`);
|
|
1955
|
+
}
|
|
1956
|
+
return value;
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
// sdk/typescript/src/client.ts
|
|
1960
|
+
class HelmrClient {
|
|
1961
|
+
tasks;
|
|
1962
|
+
actors;
|
|
1963
|
+
sessions;
|
|
1964
|
+
sandboxes;
|
|
1965
|
+
deployments;
|
|
1966
|
+
schedules;
|
|
1967
|
+
secrets;
|
|
1968
|
+
runs;
|
|
1969
|
+
tokens;
|
|
1970
|
+
workspaces;
|
|
1971
|
+
constructor(options) {
|
|
1972
|
+
const transport = new ClientTransport(options);
|
|
1973
|
+
this.tasks = Object.freeze(new ClientTasks(transport));
|
|
1974
|
+
this.actors = createClientActors(transport);
|
|
1975
|
+
this.sessions = createClientSessions(transport);
|
|
1976
|
+
this.sandboxes = createClientSandboxes(transport);
|
|
1977
|
+
this.deployments = createClientDeployments(transport);
|
|
1978
|
+
this.schedules = createClientSchedules(transport);
|
|
1979
|
+
this.secrets = createClientSecrets(transport);
|
|
1980
|
+
this.runs = Object.freeze(new ClientRuns(transport));
|
|
1981
|
+
this.tokens = Object.freeze(new ClientTokens(transport));
|
|
1982
|
+
this.workspaces = createClientWorkspaces(transport);
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
class ClientTasks {
|
|
1987
|
+
#transport;
|
|
1988
|
+
constructor(transport) {
|
|
1989
|
+
this.#transport = transport;
|
|
1990
|
+
}
|
|
1991
|
+
async retrieve(id, query = {}, options = {}) {
|
|
1992
|
+
validateTaskId(id);
|
|
1993
|
+
return parseTask(await this.#transport.request("GET", `/v1/tasks/${encodeURIComponent(id)}${definitionItemQuery(query, "Task")}`, options.signal === undefined ? {} : { signal: options.signal }));
|
|
1994
|
+
}
|
|
1995
|
+
async list(query = {}, options = {}) {
|
|
1996
|
+
const response = objectValue7(await this.#transport.request("GET", `/v1/tasks${definitionListQuery(query, "Task")}`, options.signal === undefined ? {} : { signal: options.signal }), "Task list response");
|
|
1997
|
+
if (!Array.isArray(response["tasks"])) {
|
|
1998
|
+
throw new Error("Task list response.tasks must be an array");
|
|
1999
|
+
}
|
|
2000
|
+
const nextCursor = response["next_cursor"];
|
|
2001
|
+
if (nextCursor !== undefined && typeof nextCursor !== "string") {
|
|
2002
|
+
throw new Error("Task list response.next_cursor must be a string");
|
|
2003
|
+
}
|
|
2004
|
+
return Object.freeze({
|
|
2005
|
+
deploymentId: resourceID(response["deployment_id"], "Task list response.deployment_id"),
|
|
2006
|
+
items: Object.freeze(response["tasks"].map(parseTaskListItem)),
|
|
2007
|
+
...nextCursor === undefined ? {} : { nextCursor }
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
async start(taskId, request, options = {}) {
|
|
2011
|
+
validateTaskId(taskId);
|
|
2012
|
+
const response = objectValue7(await this.#transport.request("POST", `/v1/tasks/${encodeURIComponent(taskId)}/start`, {
|
|
2013
|
+
body: {
|
|
2014
|
+
..."payload" in request ? { payload: request.payload } : {},
|
|
2015
|
+
...taskStartRequest(request)
|
|
2016
|
+
},
|
|
2017
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
2018
|
+
}), "Task start response");
|
|
2019
|
+
const runId = resourceID(response["run_id"], "Task start response.run_id");
|
|
2020
|
+
return createRunHandle(runId);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
function parseTask(value) {
|
|
2024
|
+
const input = objectValue7(value, "Task response");
|
|
2025
|
+
const id = requiredStringFrom(input, "id", "Task response");
|
|
2026
|
+
validateTaskId(id);
|
|
2027
|
+
return Object.freeze({
|
|
2028
|
+
id,
|
|
2029
|
+
deploymentId: resourceID(input["deployment_id"], "Task response.deployment_id")
|
|
2030
|
+
});
|
|
2031
|
+
}
|
|
2032
|
+
function parseTaskListItem(value) {
|
|
2033
|
+
const input = objectValue7(value, "Task list item");
|
|
2034
|
+
const id = requiredStringFrom(input, "id", "Task list item");
|
|
2035
|
+
validateTaskId(id);
|
|
2036
|
+
return Object.freeze({ id });
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
class ClientRuns {
|
|
2040
|
+
#transport;
|
|
2041
|
+
constructor(transport) {
|
|
2042
|
+
this.#transport = transport;
|
|
2043
|
+
}
|
|
2044
|
+
async retrieve(run, options = {}) {
|
|
2045
|
+
const runId = runHandleID(run);
|
|
2046
|
+
return parseRun(await this.#transport.request("GET", `/v1/runs/${encodeURIComponent(runId)}`, options.signal === undefined ? {} : { signal: options.signal }));
|
|
2047
|
+
}
|
|
2048
|
+
async list(queryInput = {}, options = {}) {
|
|
2049
|
+
const query = new URLSearchParams;
|
|
2050
|
+
const statuses = queryInput.status === undefined ? [] : Array.isArray(queryInput.status) ? queryInput.status : [queryInput.status];
|
|
2051
|
+
for (const status of statuses)
|
|
2052
|
+
query.append("status", runStatus(status));
|
|
2053
|
+
const kinds = queryInput.kind === undefined ? [] : Array.isArray(queryInput.kind) ? queryInput.kind : [queryInput.kind];
|
|
2054
|
+
for (const kind of kinds) {
|
|
2055
|
+
if (kind !== "task" && kind !== "actor") {
|
|
2056
|
+
throw new Error("Run list kind is invalid");
|
|
2057
|
+
}
|
|
2058
|
+
query.append("kind", kind);
|
|
2059
|
+
}
|
|
2060
|
+
if (queryInput.sessionId !== undefined) {
|
|
2061
|
+
query.set("session_id", resourceID(queryInput.sessionId, "Run list Session ID"));
|
|
2062
|
+
}
|
|
2063
|
+
if (queryInput.cursor !== undefined) {
|
|
2064
|
+
if (queryInput.cursor.length === 0)
|
|
2065
|
+
throw new Error("Run cursor is required");
|
|
2066
|
+
query.set("cursor", queryInput.cursor);
|
|
2067
|
+
}
|
|
2068
|
+
if (queryInput.limit !== undefined) {
|
|
2069
|
+
if (!Number.isInteger(queryInput.limit) || queryInput.limit < 1 || queryInput.limit > 100) {
|
|
2070
|
+
throw new Error("Run limit must be an integer in [1,100]");
|
|
2071
|
+
}
|
|
2072
|
+
query.set("limit", String(queryInput.limit));
|
|
2073
|
+
}
|
|
2074
|
+
const suffix = query.size === 0 ? "" : `?${query.toString()}`;
|
|
2075
|
+
const response = objectValue7(await this.#transport.request("GET", `/v1/runs${suffix}`, {
|
|
2076
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
2077
|
+
}), "Run list response");
|
|
2078
|
+
if (!Array.isArray(response["runs"])) {
|
|
2079
|
+
throw new Error("Run list response.runs must be an array");
|
|
2080
|
+
}
|
|
2081
|
+
const nextCursor = response["next_cursor"];
|
|
2082
|
+
if (nextCursor !== undefined && typeof nextCursor !== "string") {
|
|
2083
|
+
throw new Error("Run list response.next_cursor must be a string");
|
|
2084
|
+
}
|
|
2085
|
+
return Object.freeze({
|
|
2086
|
+
items: Object.freeze(response["runs"].map(parseRunListItem)),
|
|
2087
|
+
...nextCursor === undefined ? {} : { nextCursor }
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
async cancel(runId, options = {}) {
|
|
2091
|
+
return parseRun(await this.#transport.request("POST", `/v1/runs/${encodeURIComponent(resourceID(runId, "Run ID"))}/cancel`, options.signal === undefined ? {} : { signal: options.signal }));
|
|
2092
|
+
}
|
|
2093
|
+
async logs(runId, queryInput = {}, options = {}) {
|
|
2094
|
+
const query = runTelemetryQuery(queryInput.cursor, queryInput.limit, "level", queryInput.level);
|
|
2095
|
+
const response = objectValue7(await this.#transport.request("GET", `/v1/runs/${encodeURIComponent(resourceID(runId, "Run ID"))}/logs${query}`, options.signal === undefined ? {} : { signal: options.signal }), "Run log page");
|
|
2096
|
+
if (!Array.isArray(response["logs"])) {
|
|
2097
|
+
throw new Error("Run log page.logs must be an array");
|
|
2098
|
+
}
|
|
2099
|
+
return cursorPage(response["logs"].map(parseRunLogRecord), response["next_cursor"], "Run log page");
|
|
2100
|
+
}
|
|
2101
|
+
async events(runId, queryInput = {}, options = {}) {
|
|
2102
|
+
const query = runTelemetryQuery(queryInput.cursor, queryInput.limit, "severity", queryInput.severity);
|
|
2103
|
+
const response = objectValue7(await this.#transport.request("GET", `/v1/runs/${encodeURIComponent(resourceID(runId, "Run ID"))}/events${query}`, options.signal === undefined ? {} : { signal: options.signal }), "Run event page");
|
|
2104
|
+
if (!Array.isArray(response["events"])) {
|
|
2105
|
+
throw new Error("Run event page.events must be an array");
|
|
2106
|
+
}
|
|
2107
|
+
return cursorPage(response["events"].map(parseRunEventRecord), response["next_cursor"], "Run event page");
|
|
2108
|
+
}
|
|
2109
|
+
wait(run, options = {}) {
|
|
2110
|
+
if (runtimeOperationsInstalled()) {
|
|
2111
|
+
throw new Error("client.runs.wait() is unavailable inside an active Helmr Run; use task.call()");
|
|
2112
|
+
}
|
|
2113
|
+
const id = runHandleID(run);
|
|
2114
|
+
const result = this.#waitForTerminal(id, options);
|
|
2115
|
+
return Object.freeze({
|
|
2116
|
+
then(onfulfilled, onrejected) {
|
|
2117
|
+
return result.then(onfulfilled, onrejected);
|
|
2118
|
+
},
|
|
2119
|
+
async unwrap() {
|
|
2120
|
+
const settled = await result;
|
|
2121
|
+
if (!settled.ok)
|
|
2122
|
+
throw runFailureError(settled.failure);
|
|
2123
|
+
return settled.output;
|
|
2124
|
+
}
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
async#waitForTerminal(runId, options) {
|
|
2128
|
+
let delayMilliseconds = 250;
|
|
2129
|
+
while (true) {
|
|
2130
|
+
options.signal?.throwIfAborted();
|
|
2131
|
+
const run = await this.retrieve(createRunHandle(runId), options);
|
|
2132
|
+
if (run.status === "succeeded") {
|
|
2133
|
+
if (run.output === undefined) {
|
|
2134
|
+
throw new Error("Succeeded Run response must include output");
|
|
2135
|
+
}
|
|
2136
|
+
return Object.freeze({
|
|
2137
|
+
ok: true,
|
|
2138
|
+
output: run.output,
|
|
2139
|
+
run: createRunHandle(run.id)
|
|
2140
|
+
});
|
|
2141
|
+
}
|
|
2142
|
+
if (runStatusIsTerminal(run.status)) {
|
|
2143
|
+
if (run.failure === undefined) {
|
|
2144
|
+
throw new Error("Non-success terminal Run response must include failure");
|
|
2145
|
+
}
|
|
2146
|
+
return Object.freeze({
|
|
2147
|
+
ok: false,
|
|
2148
|
+
failure: run.failure,
|
|
2149
|
+
run: createRunHandle(run.id)
|
|
2150
|
+
});
|
|
2151
|
+
}
|
|
2152
|
+
await abortableDelay(delayMilliseconds, options.signal);
|
|
2153
|
+
delayMilliseconds = Math.min(delayMilliseconds * 2, 2000);
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
function parseRunListItem(value) {
|
|
2158
|
+
const run = objectValue7(value, "Run list item");
|
|
2159
|
+
const status = runStatus(requiredStringFrom(run, "status", "Run list item"));
|
|
2160
|
+
const entrypoint = objectValue7(run["entrypoint"], "Run list item.entrypoint");
|
|
2161
|
+
const kind = requiredStringFrom(entrypoint, "kind", "Run list item.entrypoint");
|
|
2162
|
+
if (kind !== "task" && kind !== "actor") {
|
|
2163
|
+
throw new Error("Run list item.entrypoint.kind is invalid");
|
|
2164
|
+
}
|
|
2165
|
+
const item = {
|
|
2166
|
+
id: resourceID(requiredStringFrom(run, "id", "Run list item"), "Run list item.id"),
|
|
2167
|
+
status,
|
|
2168
|
+
entrypoint: Object.freeze({
|
|
2169
|
+
kind,
|
|
2170
|
+
id: requiredStringFrom(entrypoint, "id", "Run list item.entrypoint")
|
|
2171
|
+
}),
|
|
2172
|
+
workspaceId: resourceID(requiredStringFrom(run, "workspace_id", "Run list item"), "Run list item.workspace_id"),
|
|
2173
|
+
...run["session_id"] === undefined ? {} : {
|
|
2174
|
+
sessionId: resourceID(requiredStringFrom(run, "session_id", "Run list item"), "Run list item.session_id")
|
|
2175
|
+
},
|
|
2176
|
+
currentAttemptNumber: requiredPositiveInteger(run, "current_attempt_number", "Run list item"),
|
|
2177
|
+
createdAt: requiredTimestamp(run, "created_at", "Run list item"),
|
|
2178
|
+
...run["started_at"] === undefined ? {} : { startedAt: requiredTimestamp(run, "started_at", "Run list item") },
|
|
2179
|
+
...run["terminal_at"] === undefined ? {} : { terminalAt: requiredTimestamp(run, "terminal_at", "Run list item") }
|
|
2180
|
+
};
|
|
2181
|
+
if (runStatusIsTerminal(status) !== (item.terminalAt !== undefined)) {
|
|
2182
|
+
throw new Error("Run list item.terminal_at is inconsistent with status");
|
|
2183
|
+
}
|
|
2184
|
+
return Object.freeze(item);
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
class ClientTokens {
|
|
2188
|
+
#transport;
|
|
2189
|
+
constructor(transport) {
|
|
2190
|
+
this.#transport = transport;
|
|
2191
|
+
}
|
|
2192
|
+
async create(request = {}, options = {}) {
|
|
2193
|
+
const response = await this.#transport.request("POST", "/v1/tokens", {
|
|
2194
|
+
body: {
|
|
2195
|
+
...request.timeout === undefined ? {} : { timeout: request.timeout },
|
|
2196
|
+
...request.metadata === undefined ? {} : { metadata: request.metadata },
|
|
2197
|
+
...request.tags === undefined ? {} : { tags: [...request.tags] },
|
|
2198
|
+
...request.idempotencyKey === undefined ? {} : { idempotency_key: request.idempotencyKey }
|
|
2199
|
+
},
|
|
2200
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
2201
|
+
});
|
|
2202
|
+
const token = parseToken(response, true);
|
|
2203
|
+
if (token.status !== "pending") {
|
|
2204
|
+
throw new Error("Token create response must be pending");
|
|
2205
|
+
}
|
|
2206
|
+
return token;
|
|
2207
|
+
}
|
|
2208
|
+
async retrieve(id, options = {}) {
|
|
2209
|
+
return parseToken(await this.#transport.request("GET", `/v1/tokens/${encodeURIComponent(resourceID(id, "Token ID"))}`, options.signal === undefined ? {} : { signal: options.signal }), false);
|
|
2210
|
+
}
|
|
2211
|
+
async list(queryInput = {}, options = {}) {
|
|
2212
|
+
const query = new URLSearchParams;
|
|
2213
|
+
if (queryInput.status !== undefined)
|
|
2214
|
+
query.set("status", tokenStatus(queryInput.status));
|
|
2215
|
+
if (queryInput.cursor !== undefined) {
|
|
2216
|
+
if (queryInput.cursor.length === 0)
|
|
2217
|
+
throw new Error("Token cursor is required");
|
|
2218
|
+
query.set("cursor", queryInput.cursor);
|
|
2219
|
+
}
|
|
2220
|
+
if (queryInput.limit !== undefined) {
|
|
2221
|
+
if (!Number.isInteger(queryInput.limit) || queryInput.limit < 1 || queryInput.limit > 100) {
|
|
2222
|
+
throw new Error("Token limit must be an integer in [1,100]");
|
|
2223
|
+
}
|
|
2224
|
+
query.set("limit", String(queryInput.limit));
|
|
2225
|
+
}
|
|
2226
|
+
const suffix = query.size === 0 ? "" : `?${query.toString()}`;
|
|
2227
|
+
const response = objectValue7(await this.#transport.request("GET", `/v1/tokens${suffix}`, {
|
|
2228
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
2229
|
+
}), "Token list response");
|
|
2230
|
+
if (!Array.isArray(response["tokens"])) {
|
|
2231
|
+
throw new Error("Token list response.tokens must be an array");
|
|
2232
|
+
}
|
|
2233
|
+
const nextCursor = response["next_cursor"];
|
|
2234
|
+
if (nextCursor !== undefined && typeof nextCursor !== "string") {
|
|
2235
|
+
throw new Error("Token list response.next_cursor must be a string");
|
|
2236
|
+
}
|
|
2237
|
+
return Object.freeze({
|
|
2238
|
+
items: Object.freeze(response["tokens"].map(parseTokenListItem)),
|
|
2239
|
+
...nextCursor === undefined ? {} : { nextCursor }
|
|
2240
|
+
});
|
|
2241
|
+
}
|
|
2242
|
+
async complete(id, request, options = {}) {
|
|
2243
|
+
return parseToken(await this.#transport.request("POST", `/v1/tokens/${encodeURIComponent(resourceID(id, "Token ID"))}/complete`, {
|
|
2244
|
+
body: {
|
|
2245
|
+
result: request.result,
|
|
2246
|
+
...request.idempotencyKey === undefined ? {} : { idempotency_key: request.idempotencyKey }
|
|
2247
|
+
},
|
|
2248
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
2249
|
+
}), false);
|
|
2250
|
+
}
|
|
2251
|
+
async cancel(id, request = {}, options = {}) {
|
|
2252
|
+
return parseToken(await this.#transport.request("POST", `/v1/tokens/${encodeURIComponent(resourceID(id, "Token ID"))}/cancel`, {
|
|
2253
|
+
body: request.idempotencyKey === undefined ? {} : { idempotency_key: request.idempotencyKey },
|
|
2254
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
2255
|
+
}), false);
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
function taskStartRequest(request) {
|
|
2259
|
+
return {
|
|
2260
|
+
workspace: { id: workspaceRefID(request.workspace) },
|
|
2261
|
+
...request.idempotencyKey === undefined ? {} : { idempotency_key: request.idempotencyKey },
|
|
2262
|
+
...request.queue === undefined ? {} : { queue: request.queue },
|
|
2263
|
+
...request.concurrencyKey === undefined ? {} : { concurrency_key: request.concurrencyKey },
|
|
2264
|
+
...request.priority === undefined ? {} : { priority: request.priority },
|
|
2265
|
+
...request.ttl === undefined ? {} : { ttl: request.ttl },
|
|
2266
|
+
...request.retry === undefined ? {} : {
|
|
2267
|
+
retry: request.retry.enabled === false ? { enabled: false } : {
|
|
2268
|
+
...request.retry.enabled === undefined ? {} : { enabled: request.retry.enabled },
|
|
2269
|
+
max_attempts: request.retry.maxAttempts,
|
|
2270
|
+
...request.retry.backoff === undefined ? {} : {
|
|
2271
|
+
backoff: {
|
|
2272
|
+
...request.retry.backoff.minDelay === undefined ? {} : { min_delay: request.retry.backoff.minDelay },
|
|
2273
|
+
...request.retry.backoff.maxDelay === undefined ? {} : { max_delay: request.retry.backoff.maxDelay },
|
|
2274
|
+
...request.retry.backoff.factor === undefined ? {} : { factor: request.retry.backoff.factor },
|
|
2275
|
+
...request.retry.backoff.jitter === undefined ? {} : { jitter: request.retry.backoff.jitter }
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
},
|
|
2280
|
+
...request.metadata === undefined ? {} : { metadata: request.metadata },
|
|
2281
|
+
...request.tags === undefined ? {} : { tags: [...request.tags] }
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
class ClientTransport {
|
|
2286
|
+
#baseURL;
|
|
2287
|
+
#apiKey;
|
|
2288
|
+
#fetch;
|
|
2289
|
+
constructor(options) {
|
|
2290
|
+
this.#baseURL = clientBaseURL(options.url ?? "https://api.helmr.dev");
|
|
2291
|
+
this.#apiKey = options.apiKey.trim();
|
|
2292
|
+
if (this.#apiKey === "")
|
|
2293
|
+
throw new Error("Helmr API key is required");
|
|
2294
|
+
this.#fetch = options.fetch ?? globalThis.fetch;
|
|
2295
|
+
if (typeof this.#fetch !== "function") {
|
|
2296
|
+
throw new Error("fetch is unavailable");
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
async request(method, path, options = {}) {
|
|
2300
|
+
const response = await this.#fetch(new URL(path, this.#baseURL), {
|
|
2301
|
+
method,
|
|
2302
|
+
headers: {
|
|
2303
|
+
Authorization: `Bearer ${this.#apiKey}`,
|
|
2304
|
+
...options.body === undefined ? {} : { "Content-Type": "application/json" }
|
|
2305
|
+
},
|
|
2306
|
+
...options.body === undefined ? {} : { body: JSON.stringify(options.body) },
|
|
2307
|
+
...options.signal === undefined ? {} : { signal: options.signal }
|
|
2308
|
+
});
|
|
2309
|
+
const value = await response.json().catch(() => ({}));
|
|
2310
|
+
if (!response.ok) {
|
|
2311
|
+
throw clientRequestError(response, value);
|
|
2312
|
+
}
|
|
2313
|
+
return value;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
function clientRequestError(response, value) {
|
|
2317
|
+
const body = value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2318
|
+
const payload = body["error"] !== null && typeof body["error"] === "object" && !Array.isArray(body["error"]) ? body["error"] : {};
|
|
2319
|
+
const message = typeof payload["message"] === "string" && payload["message"] !== "" ? payload["message"] : `Helmr request failed with status ${response.status}`;
|
|
2320
|
+
const code = typeof payload["code"] === "string" && payload["code"] !== "" ? payload["code"] : responseStatusCode(response.status);
|
|
2321
|
+
const details = payload["details"] !== null && typeof payload["details"] === "object" && !Array.isArray(payload["details"]) ? payload["details"] : undefined;
|
|
2322
|
+
const requestId = response.headers.get("X-Request-ID")?.trim() || undefined;
|
|
2323
|
+
const error = new Error(message);
|
|
2324
|
+
error.name = "HelmrError";
|
|
2325
|
+
error.code = code;
|
|
2326
|
+
if (details !== undefined)
|
|
2327
|
+
error.details = details;
|
|
2328
|
+
if (requestId !== undefined)
|
|
2329
|
+
error.requestId = requestId;
|
|
2330
|
+
return error;
|
|
2331
|
+
}
|
|
2332
|
+
function responseStatusCode(status) {
|
|
2333
|
+
switch (status) {
|
|
2334
|
+
case 400:
|
|
2335
|
+
return "bad_request";
|
|
2336
|
+
case 401:
|
|
2337
|
+
return "unauthorized";
|
|
2338
|
+
case 403:
|
|
2339
|
+
return "forbidden";
|
|
2340
|
+
case 404:
|
|
2341
|
+
return "not_found";
|
|
2342
|
+
case 405:
|
|
2343
|
+
return "method_not_allowed";
|
|
2344
|
+
case 409:
|
|
2345
|
+
return "conflict";
|
|
2346
|
+
case 410:
|
|
2347
|
+
return "gone";
|
|
2348
|
+
case 413:
|
|
2349
|
+
return "request_too_large";
|
|
2350
|
+
case 422:
|
|
2351
|
+
return "unprocessable_entity";
|
|
2352
|
+
case 429:
|
|
2353
|
+
return "rate_limited";
|
|
2354
|
+
case 501:
|
|
2355
|
+
return "not_implemented";
|
|
2356
|
+
case 502:
|
|
2357
|
+
return "bad_gateway";
|
|
2358
|
+
case 503:
|
|
2359
|
+
return "service_unavailable";
|
|
2360
|
+
default:
|
|
2361
|
+
return status >= 500 ? "internal_error" : "request_failed";
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
function clientBaseURL(raw) {
|
|
2365
|
+
const url = new URL(raw);
|
|
2366
|
+
if (url.username !== "" || url.password !== "" || url.pathname !== "" && url.pathname !== "/" || url.search !== "" || url.hash !== "") {
|
|
2367
|
+
throw new Error("Helmr API URL must be an origin without credentials, path, query, or fragment");
|
|
2368
|
+
}
|
|
2369
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && (url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]"))) {
|
|
2370
|
+
throw new Error("Helmr API URL must use HTTPS except on loopback");
|
|
2371
|
+
}
|
|
2372
|
+
url.pathname = "/";
|
|
2373
|
+
return url;
|
|
2374
|
+
}
|
|
2375
|
+
function runStatus(value) {
|
|
2376
|
+
switch (value) {
|
|
2377
|
+
case "queued":
|
|
2378
|
+
case "running":
|
|
2379
|
+
case "waiting":
|
|
2380
|
+
case "retry_delayed":
|
|
2381
|
+
case "cancel_requested":
|
|
2382
|
+
case "succeeded":
|
|
2383
|
+
case "failed":
|
|
2384
|
+
case "cancelled":
|
|
2385
|
+
case "expired":
|
|
2386
|
+
case "system_failed":
|
|
2387
|
+
return value;
|
|
2388
|
+
default:
|
|
2389
|
+
throw new Error(`Run status ${JSON.stringify(value)} is invalid`);
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
function runStatusIsTerminal(status) {
|
|
2393
|
+
return status === "succeeded" || status === "failed" || status === "cancelled" || status === "expired" || status === "system_failed";
|
|
2394
|
+
}
|
|
2395
|
+
function parseRun(value) {
|
|
2396
|
+
const run = objectValue7(value, "Run response");
|
|
2397
|
+
const status = runStatus(requiredStringFrom(run, "status", "Run response"));
|
|
2398
|
+
const entrypoint = objectValue7(run["entrypoint"], "Run response.entrypoint");
|
|
2399
|
+
const entrypointKind = requiredStringFrom(entrypoint, "kind", "Run response.entrypoint");
|
|
2400
|
+
if (entrypointKind !== "task" && entrypointKind !== "actor") {
|
|
2401
|
+
throw new Error("Run response.entrypoint.kind is invalid");
|
|
2402
|
+
}
|
|
2403
|
+
const deployment = objectValue7(run["deployment"], "Run response.deployment");
|
|
2404
|
+
const metadata = objectValue7(run["metadata"], "Run response.metadata");
|
|
2405
|
+
const tags = run["tags"];
|
|
2406
|
+
if (!Array.isArray(tags) || tags.some((tag) => typeof tag !== "string")) {
|
|
2407
|
+
throw new Error("Run response.tags must be an array of strings");
|
|
2408
|
+
}
|
|
2409
|
+
const cause = parseRunCause(run["cause"]);
|
|
2410
|
+
const runResource = {
|
|
2411
|
+
...createRunHandle(resourceID(requiredStringFrom(run, "id", "Run response"), "Run response.id")),
|
|
2412
|
+
status,
|
|
2413
|
+
entrypoint: Object.freeze({
|
|
2414
|
+
kind: entrypointKind,
|
|
2415
|
+
id: requiredStringFrom(entrypoint, "id", "Run response.entrypoint")
|
|
2416
|
+
}),
|
|
2417
|
+
deployment: Object.freeze({
|
|
2418
|
+
id: resourceID(requiredStringFrom(deployment, "id", "Run response.deployment"), "Run response.deployment.id"),
|
|
2419
|
+
version: requiredStringFrom(deployment, "version", "Run response.deployment")
|
|
2420
|
+
}),
|
|
2421
|
+
workspaceId: resourceID(requiredStringFrom(run, "workspace_id", "Run response"), "Run response.workspace_id"),
|
|
2422
|
+
...run["session_id"] === undefined ? {} : {
|
|
2423
|
+
sessionId: resourceID(requiredStringFrom(run, "session_id", "Run response"), "Run response.session_id")
|
|
2424
|
+
},
|
|
2425
|
+
...run["parent_run_id"] === undefined ? {} : {
|
|
2426
|
+
parentRunId: resourceID(requiredStringFrom(run, "parent_run_id", "Run response"), "Run response.parent_run_id")
|
|
2427
|
+
},
|
|
2428
|
+
currentAttemptNumber: requiredPositiveInteger(run, "current_attempt_number", "Run response"),
|
|
2429
|
+
cause,
|
|
2430
|
+
metadata: Object.freeze({ ...metadata }),
|
|
2431
|
+
tags: Object.freeze([...tags]),
|
|
2432
|
+
...run["output"] === undefined ? {} : { output: run["output"] },
|
|
2433
|
+
...run["failure"] === undefined ? {} : { failure: parseRunFailure(run["failure"]) },
|
|
2434
|
+
createdAt: requiredTimestamp(run, "created_at", "Run response"),
|
|
2435
|
+
...run["started_at"] === undefined ? {} : { startedAt: requiredTimestamp(run, "started_at", "Run response") },
|
|
2436
|
+
...run["terminal_at"] === undefined ? {} : { terminalAt: requiredTimestamp(run, "terminal_at", "Run response") }
|
|
2437
|
+
};
|
|
2438
|
+
if (status === "succeeded") {
|
|
2439
|
+
if (runResource.failure !== undefined || runResource.terminalAt === undefined) {
|
|
2440
|
+
throw new Error("Succeeded Run response has an invalid terminal projection");
|
|
2441
|
+
}
|
|
2442
|
+
} else if (runStatusIsTerminal(status)) {
|
|
2443
|
+
if (runResource.output !== undefined || runResource.failure === undefined || runResource.terminalAt === undefined) {
|
|
2444
|
+
throw new Error("Terminal Run response has an invalid failure projection");
|
|
2445
|
+
}
|
|
2446
|
+
} else if (runResource.output !== undefined || runResource.failure !== undefined || runResource.terminalAt !== undefined) {
|
|
2447
|
+
throw new Error("Active Run response has terminal fields");
|
|
2448
|
+
}
|
|
2449
|
+
return Object.freeze(runResource);
|
|
2450
|
+
}
|
|
2451
|
+
function parseRunCause(value) {
|
|
2452
|
+
const cause = objectValue7(value, "Run response.cause");
|
|
2453
|
+
const type = requiredStringFrom(cause, "type", "Run response.cause");
|
|
2454
|
+
switch (type) {
|
|
2455
|
+
case "api":
|
|
2456
|
+
case "manual":
|
|
2457
|
+
case "actor_start":
|
|
2458
|
+
case "continuation":
|
|
2459
|
+
return Object.freeze({ type });
|
|
2460
|
+
case "child":
|
|
2461
|
+
return Object.freeze({
|
|
2462
|
+
type,
|
|
2463
|
+
parentRunId: resourceID(requiredStringFrom(cause, "parent_run_id", "Run response.cause"), "Run response.cause.parent_run_id")
|
|
2464
|
+
});
|
|
2465
|
+
case "schedule":
|
|
2466
|
+
return Object.freeze({
|
|
2467
|
+
type,
|
|
2468
|
+
scheduleId: resourceID(requiredStringFrom(cause, "schedule_id", "Run response.cause"), "Run response.cause.schedule_id"),
|
|
2469
|
+
scheduledAt: requiredTimestamp(cause, "scheduled_at", "Run response.cause"),
|
|
2470
|
+
...cause["last_scheduled_at"] === undefined ? {} : {
|
|
2471
|
+
lastScheduledAt: requiredTimestamp(cause, "last_scheduled_at", "Run response.cause")
|
|
2472
|
+
},
|
|
2473
|
+
timezone: requiredStringFrom(cause, "timezone", "Run response.cause")
|
|
2474
|
+
});
|
|
2475
|
+
default:
|
|
2476
|
+
throw new Error("Run response.cause.type is invalid");
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
function parseRunFailure(value) {
|
|
2480
|
+
const source2 = objectValue7(value, "Run response.failure");
|
|
2481
|
+
const details = objectValue7(source2["details"], "Run response.failure.details");
|
|
2482
|
+
return Object.freeze({
|
|
2483
|
+
code: requiredStringFrom(source2, "code", "Run response.failure"),
|
|
2484
|
+
message: requiredStringFrom(source2, "message", "Run response.failure"),
|
|
2485
|
+
details: Object.freeze({ ...details })
|
|
2486
|
+
});
|
|
2487
|
+
}
|
|
2488
|
+
function runTelemetryQuery(cursor, limit, filterName, filter) {
|
|
2489
|
+
const query = new URLSearchParams;
|
|
2490
|
+
if (cursor !== undefined) {
|
|
2491
|
+
if (cursor.length === 0)
|
|
2492
|
+
throw new Error("Run telemetry cursor is required");
|
|
2493
|
+
query.set("cursor", cursor);
|
|
2494
|
+
}
|
|
2495
|
+
if (limit !== undefined) {
|
|
2496
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 200) {
|
|
2497
|
+
throw new Error("Run telemetry limit must be an integer in [1,200]");
|
|
2498
|
+
}
|
|
2499
|
+
query.set("limit", String(limit));
|
|
2500
|
+
}
|
|
2501
|
+
const values = filter === undefined ? [] : Array.isArray(filter) ? filter : [filter];
|
|
2502
|
+
for (const value of values)
|
|
2503
|
+
query.append(filterName, runLogLevel(value));
|
|
2504
|
+
return query.size === 0 ? "" : `?${query.toString()}`;
|
|
2505
|
+
}
|
|
2506
|
+
function parseRunLogRecord(value) {
|
|
2507
|
+
const record = objectValue7(value, "Run log record");
|
|
2508
|
+
const kind = requiredStringFrom(record, "kind", "Run log record");
|
|
2509
|
+
const common = {
|
|
2510
|
+
id: requiredStringFrom(record, "id", "Run log record"),
|
|
2511
|
+
runId: resourceID(requiredStringFrom(record, "run_id", "Run log record"), "Run log record.run_id"),
|
|
2512
|
+
attemptNumber: requiredPositiveInteger(record, "attempt_number", "Run log record"),
|
|
2513
|
+
at: requiredTimestamp(record, "at", "Run log record")
|
|
2514
|
+
};
|
|
2515
|
+
if (kind === "structured") {
|
|
2516
|
+
const attributes = objectValue7(record["attributes"], "Run log record.attributes");
|
|
2517
|
+
return Object.freeze({
|
|
2518
|
+
...common,
|
|
2519
|
+
kind,
|
|
2520
|
+
level: runLogLevel(record["level"]),
|
|
2521
|
+
message: stringValue(record, "message", "Run log record"),
|
|
2522
|
+
attributes: Object.freeze({ ...attributes })
|
|
2523
|
+
});
|
|
2524
|
+
}
|
|
2525
|
+
if (kind !== "stdout" && kind !== "stderr") {
|
|
2526
|
+
throw new Error("Run log record.kind is invalid");
|
|
2527
|
+
}
|
|
2528
|
+
const contentBase64 = stringValue(record, "content_base64", "Run log record");
|
|
2529
|
+
if (!validBase64(contentBase64)) {
|
|
2530
|
+
throw new Error("Run log record.content_base64 must be canonical base64");
|
|
2531
|
+
}
|
|
2532
|
+
return Object.freeze({
|
|
2533
|
+
...common,
|
|
2534
|
+
kind,
|
|
2535
|
+
observedSequence: requiredPositiveInteger(record, "observed_sequence", "Run log record"),
|
|
2536
|
+
contentBase64,
|
|
2537
|
+
bytes: requiredNonnegativeInteger(record, "bytes", "Run log record")
|
|
2538
|
+
});
|
|
2539
|
+
}
|
|
2540
|
+
function parseRunEventRecord(value) {
|
|
2541
|
+
const event = objectValue7(value, "Run event record");
|
|
2542
|
+
return Object.freeze({
|
|
2543
|
+
id: requiredStringFrom(event, "id", "Run event record"),
|
|
2544
|
+
runId: resourceID(requiredStringFrom(event, "run_id", "Run event record"), "Run event record.run_id"),
|
|
2545
|
+
...event["attempt_number"] === undefined ? {} : {
|
|
2546
|
+
attemptNumber: requiredPositiveInteger(event, "attempt_number", "Run event record")
|
|
2547
|
+
},
|
|
2548
|
+
category: requiredStringFrom(event, "category", "Run event record"),
|
|
2549
|
+
severity: runLogLevel(event["severity"]),
|
|
2550
|
+
source: requiredStringFrom(event, "source", "Run event record"),
|
|
2551
|
+
kind: requiredStringFrom(event, "kind", "Run event record"),
|
|
2552
|
+
message: stringValue(event, "message", "Run event record"),
|
|
2553
|
+
attributes: event["attributes"],
|
|
2554
|
+
occurredAt: requiredTimestamp(event, "occurred_at", "Run event record"),
|
|
2555
|
+
at: requiredTimestamp(event, "at", "Run event record")
|
|
2556
|
+
});
|
|
2557
|
+
}
|
|
2558
|
+
function cursorPage(items, nextCursor, label) {
|
|
2559
|
+
if (nextCursor !== undefined && typeof nextCursor !== "string") {
|
|
2560
|
+
throw new Error(`${label}.next_cursor must be a string`);
|
|
2561
|
+
}
|
|
2562
|
+
return Object.freeze({
|
|
2563
|
+
items: Object.freeze(items),
|
|
2564
|
+
...nextCursor === undefined ? {} : { nextCursor }
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
function runLogLevel(value) {
|
|
2568
|
+
if (value !== "debug" && value !== "info" && value !== "warn" && value !== "error") {
|
|
2569
|
+
throw new Error("Run log level must be debug, info, warn, or error");
|
|
2570
|
+
}
|
|
2571
|
+
return value;
|
|
2572
|
+
}
|
|
2573
|
+
function validBase64(value) {
|
|
2574
|
+
return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value);
|
|
2575
|
+
}
|
|
2576
|
+
function parseToken(value, credentials) {
|
|
2577
|
+
const input = objectValue7(value, "Token response");
|
|
2578
|
+
const status = tokenStatus(input["status"]);
|
|
2579
|
+
const metadata = objectValue7(input["metadata"], "Token response.metadata");
|
|
2580
|
+
const tags = input["tags"];
|
|
2581
|
+
if (!Array.isArray(tags) || tags.some((tag) => typeof tag !== "string")) {
|
|
2582
|
+
throw new Error("Token response.tags must be an array of strings");
|
|
2583
|
+
}
|
|
2584
|
+
const token = {
|
|
2585
|
+
id: resourceID(requiredString5(input, "id"), "Token response.id"),
|
|
2586
|
+
status,
|
|
2587
|
+
...input["result"] === undefined ? {} : { result: input["result"] },
|
|
2588
|
+
metadata,
|
|
2589
|
+
tags: Object.freeze([...tags]),
|
|
2590
|
+
timeoutAt: requiredTimestamp(input, "timeout_at", "Token response"),
|
|
2591
|
+
...input["completed_at"] === undefined ? {} : { completedAt: requiredTimestamp(input, "completed_at", "Token response") },
|
|
2592
|
+
createdAt: requiredTimestamp(input, "created_at", "Token response"),
|
|
2593
|
+
updatedAt: requiredTimestamp(input, "updated_at", "Token response")
|
|
2594
|
+
};
|
|
2595
|
+
if (!credentials)
|
|
2596
|
+
return Object.freeze(token);
|
|
2597
|
+
return Object.freeze({
|
|
2598
|
+
...token,
|
|
2599
|
+
callbackUrl: requiredString5(input, "callback_url"),
|
|
2600
|
+
publicAccessToken: requiredString5(input, "public_access_token")
|
|
2601
|
+
});
|
|
2602
|
+
}
|
|
2603
|
+
function parseTokenListItem(value) {
|
|
2604
|
+
const token = objectValue7(value, "Token list item");
|
|
2605
|
+
const tags = token["tags"];
|
|
2606
|
+
if (!Array.isArray(tags) || tags.some((tag) => typeof tag !== "string")) {
|
|
2607
|
+
throw new Error("Token list item.tags must be an array of strings");
|
|
2608
|
+
}
|
|
2609
|
+
return Object.freeze({
|
|
2610
|
+
id: resourceID(requiredStringFrom(token, "id", "Token list item"), "Token list item.id"),
|
|
2611
|
+
status: tokenStatus(token["status"]),
|
|
2612
|
+
tags: Object.freeze([...tags]),
|
|
2613
|
+
timeoutAt: requiredTimestamp(token, "timeout_at", "Token list item"),
|
|
2614
|
+
...token["completed_at"] === undefined ? {} : { completedAt: requiredTimestamp(token, "completed_at", "Token list item") },
|
|
2615
|
+
createdAt: requiredTimestamp(token, "created_at", "Token list item"),
|
|
2616
|
+
updatedAt: requiredTimestamp(token, "updated_at", "Token list item")
|
|
2617
|
+
});
|
|
2618
|
+
}
|
|
2619
|
+
function tokenStatus(value) {
|
|
2620
|
+
if (value !== "pending" && value !== "completed" && value !== "cancelled" && value !== "expired") {
|
|
2621
|
+
throw new Error("Token status is invalid");
|
|
2622
|
+
}
|
|
2623
|
+
return value;
|
|
2624
|
+
}
|
|
2625
|
+
function objectValue7(value, label) {
|
|
2626
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2627
|
+
throw new Error(`${label} must be an object`);
|
|
2628
|
+
}
|
|
2629
|
+
return value;
|
|
2630
|
+
}
|
|
2631
|
+
function requiredString5(value, field) {
|
|
2632
|
+
return requiredStringFrom(value, field, "Token response");
|
|
2633
|
+
}
|
|
2634
|
+
function requiredStringFrom(value, field, label) {
|
|
2635
|
+
const result = value[field];
|
|
2636
|
+
if (typeof result !== "string" || result === "") {
|
|
2637
|
+
throw new Error(`${label}.${field} must be a non-empty string`);
|
|
2638
|
+
}
|
|
2639
|
+
return result;
|
|
2640
|
+
}
|
|
2641
|
+
function stringValue(value, field, label) {
|
|
2642
|
+
const result = value[field];
|
|
2643
|
+
if (typeof result !== "string") {
|
|
2644
|
+
throw new Error(`${label}.${field} must be a string`);
|
|
2645
|
+
}
|
|
2646
|
+
return result;
|
|
2647
|
+
}
|
|
2648
|
+
function requiredPositiveInteger(value, field, label) {
|
|
2649
|
+
const result = value[field];
|
|
2650
|
+
if (!Number.isSafeInteger(result) || result < 1) {
|
|
2651
|
+
throw new Error(`${label}.${field} must be a positive safe integer`);
|
|
2652
|
+
}
|
|
2653
|
+
return result;
|
|
2654
|
+
}
|
|
2655
|
+
function requiredNonnegativeInteger(value, field, label) {
|
|
2656
|
+
const result = value[field];
|
|
2657
|
+
if (!Number.isSafeInteger(result) || result < 0) {
|
|
2658
|
+
throw new Error(`${label}.${field} must be a nonnegative safe integer`);
|
|
2659
|
+
}
|
|
2660
|
+
return result;
|
|
2661
|
+
}
|
|
2662
|
+
function requiredTimestamp(value, field, label) {
|
|
2663
|
+
return timestampString(value[field], `${label}.${field}`);
|
|
2664
|
+
}
|
|
2665
|
+
// sdk/typescript/src/logger.ts
|
|
2666
|
+
function write(level, message, attributes = {}) {
|
|
2667
|
+
return currentRuntimeOperations().structuredLog(level, message, attributes);
|
|
2668
|
+
}
|
|
2669
|
+
var logger = Object.freeze({
|
|
2670
|
+
debug(message, attributes) {
|
|
2671
|
+
return write("debug", message, attributes);
|
|
2672
|
+
},
|
|
2673
|
+
info(message, attributes) {
|
|
2674
|
+
return write("info", message, attributes);
|
|
2675
|
+
},
|
|
2676
|
+
warn(message, attributes) {
|
|
2677
|
+
return write("warn", message, attributes);
|
|
2678
|
+
},
|
|
2679
|
+
error(message, attributes) {
|
|
2680
|
+
return write("error", message, attributes);
|
|
2681
|
+
}
|
|
2682
|
+
});
|
|
2683
|
+
// sdk/typescript/src/metadata.ts
|
|
2684
|
+
var metadata = Object.freeze({
|
|
2685
|
+
set(key, value) {
|
|
2686
|
+
return currentRuntimeOperations().metadataSet(key, value);
|
|
2687
|
+
},
|
|
2688
|
+
patch(values) {
|
|
2689
|
+
return currentRuntimeOperations().metadataPatch(values);
|
|
2690
|
+
},
|
|
2691
|
+
increment(key, amount = 1) {
|
|
2692
|
+
return currentRuntimeOperations().metadataIncrement(key, amount);
|
|
2693
|
+
}
|
|
2694
|
+
});
|
|
2695
|
+
// sdk/typescript/src/config.ts
|
|
2696
|
+
var arrayIsArray = Array.isArray;
|
|
2697
|
+
var arrayPrototype = Array.prototype;
|
|
2698
|
+
var defineProperty = Object.defineProperty;
|
|
2699
|
+
var objectPrototype = Object.prototype;
|
|
2700
|
+
var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
2701
|
+
var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors;
|
|
2702
|
+
var getPrototypeOf = Object.getPrototypeOf;
|
|
2703
|
+
var hasOwn = Object.hasOwn;
|
|
2704
|
+
var freeze = Object.freeze;
|
|
2705
|
+
var ownKeys = Reflect.ownKeys;
|
|
2706
|
+
var startsWith = String.prototype.startsWith.call.bind(String.prototype.startsWith);
|
|
2707
|
+
var endsWith = String.prototype.endsWith.call.bind(String.prototype.endsWith);
|
|
2708
|
+
var includes = String.prototype.includes.call.bind(String.prototype.includes);
|
|
2709
|
+
var split = String.prototype.split.call.bind(String.prototype.split);
|
|
2710
|
+
var slice = String.prototype.slice.call.bind(String.prototype.slice);
|
|
2711
|
+
var charCodeAt2 = String.prototype.charCodeAt.call.bind(String.prototype.charCodeAt);
|
|
2712
|
+
var regexpTest = RegExp.prototype.test.call.bind(RegExp.prototype.test);
|
|
2713
|
+
function defineConfig(input) {
|
|
2714
|
+
return normalizeConfig(input);
|
|
2715
|
+
}
|
|
2716
|
+
function validateDirectory(value) {
|
|
2717
|
+
if (typeof value !== "string" || value === "" || !hasOnlyUnicodeScalarValues(value) || startsWith(value, "/") || includes(value, "\\") || hasControl(value)) {
|
|
2718
|
+
throw new Error("config dirs entries must be non-empty root-relative POSIX paths");
|
|
2719
|
+
}
|
|
2720
|
+
const normalized = startsWith(value, "./") ? slice(value, 2) : value;
|
|
2721
|
+
const segments = split(normalized, "/");
|
|
2722
|
+
let invalidSegment = normalized === "";
|
|
2723
|
+
for (let index = 0;index < segments.length; index++) {
|
|
2724
|
+
const segment = segments[index];
|
|
2725
|
+
if (segment === "" || segment === "." || segment === "..") {
|
|
2726
|
+
invalidSegment = true;
|
|
2727
|
+
break;
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
if (invalidSegment) {
|
|
2731
|
+
throw new Error("config dirs entries must be normalized root-relative paths");
|
|
2732
|
+
}
|
|
2733
|
+
return normalized;
|
|
2734
|
+
}
|
|
2735
|
+
function validateIgnorePattern(value) {
|
|
2736
|
+
if (typeof value !== "string" || value === "" || !hasOnlyUnicodeScalarValues(value) || startsWith(value, "./") || startsWith(value, "/") || endsWith(value, "/") || includes(value, "//") || includes(value, "\\") || hasControl(value) || startsWith(value, "!") || regexpTest(/[[\]{}]/, value) || regexpTest(/[?*+@!]\(/, value)) {
|
|
2737
|
+
throw new Error(`unsupported ignorePattern ${JSON.stringify(value)}`);
|
|
2738
|
+
}
|
|
2739
|
+
const segments = split(value, "/");
|
|
2740
|
+
for (let index = 0;index < segments.length; index++) {
|
|
2741
|
+
const segment = segments[index];
|
|
2742
|
+
if (segment === ".." || includes(segment, "**") && segment !== "**") {
|
|
2743
|
+
throw new Error(`unsupported ignorePattern ${JSON.stringify(value)}`);
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
return value;
|
|
2747
|
+
}
|
|
2748
|
+
function hasControl(value) {
|
|
2749
|
+
for (let index = 0;index < value.length; index++) {
|
|
2750
|
+
const code = charCodeAt2(value, index);
|
|
2751
|
+
if (code <= 31 || code >= 127 && code <= 159)
|
|
2752
|
+
return true;
|
|
2753
|
+
}
|
|
2754
|
+
return false;
|
|
2755
|
+
}
|
|
2756
|
+
function normalizeConfig(value) {
|
|
2757
|
+
if (arrayIsArray(value) || getPrototypeOf(value) !== objectPrototype) {
|
|
2758
|
+
throw new Error("config must be an ordinary object");
|
|
2759
|
+
}
|
|
2760
|
+
const descriptors = getOwnPropertyDescriptors(value);
|
|
2761
|
+
const keys = ownKeys(value);
|
|
2762
|
+
let invalidKey = false;
|
|
2763
|
+
for (let index = 0;index < keys.length; index++) {
|
|
2764
|
+
const key = keys[index];
|
|
2765
|
+
if (typeof key !== "string" || key !== "dirs" && key !== "ignorePatterns") {
|
|
2766
|
+
invalidKey = true;
|
|
2767
|
+
break;
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
if (invalidKey) {
|
|
2771
|
+
throw new Error("config accepts only dirs and ignorePatterns");
|
|
2772
|
+
}
|
|
2773
|
+
for (let index = 0;index < keys.length; index++) {
|
|
2774
|
+
const key = keys[index];
|
|
2775
|
+
if (typeof key !== "string") {
|
|
2776
|
+
throw new Error("config accepts only dirs and ignorePatterns");
|
|
2777
|
+
}
|
|
2778
|
+
const descriptor = descriptors[key];
|
|
2779
|
+
if (descriptor === undefined || !descriptor.enumerable || !hasOwn(descriptor, "value")) {
|
|
2780
|
+
throw new Error("config properties must be enumerable data properties");
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
const dirs = normalizeStringSet(hasOwn(descriptors, "dirs") ? descriptors["dirs"]?.value : ["tasks"], "config dirs", validateDirectory, true);
|
|
2784
|
+
const ignorePatterns = normalizeStringSet(hasOwn(descriptors, "ignorePatterns") ? descriptors["ignorePatterns"]?.value : [], "config ignorePatterns", validateIgnorePattern, false);
|
|
2785
|
+
return freeze({
|
|
2786
|
+
dirs: freeze(dirs),
|
|
2787
|
+
ignorePatterns: freeze(ignorePatterns)
|
|
2788
|
+
});
|
|
2789
|
+
}
|
|
2790
|
+
function normalizeStringSet(value, name, normalize, nonempty) {
|
|
2791
|
+
if (!arrayIsArray(value) || getPrototypeOf(value) !== arrayPrototype) {
|
|
2792
|
+
throw new Error(`${name} must be an array`);
|
|
2793
|
+
}
|
|
2794
|
+
const keys = ownKeys(value);
|
|
2795
|
+
const lengthDescriptor = getOwnPropertyDescriptor(value, "length");
|
|
2796
|
+
const length = lengthDescriptor?.value;
|
|
2797
|
+
if (typeof length !== "number" || keys.length !== length + 1 || keys[length] !== "length") {
|
|
2798
|
+
throw new Error(`${name} must be a dense ordinary array`);
|
|
2799
|
+
}
|
|
2800
|
+
const normalized = [];
|
|
2801
|
+
for (let index = 0;index < length; index++) {
|
|
2802
|
+
const key = `${index}`;
|
|
2803
|
+
if (keys[index] !== key) {
|
|
2804
|
+
throw new Error(`${name} must be a dense ordinary array`);
|
|
2805
|
+
}
|
|
2806
|
+
const descriptor = getOwnPropertyDescriptor(value, key);
|
|
2807
|
+
if (descriptor === undefined || !descriptor.enumerable || !hasOwn(descriptor, "value")) {
|
|
2808
|
+
throw new Error(`${name} entries must be enumerable data properties`);
|
|
2809
|
+
}
|
|
2810
|
+
const current = normalize(descriptor.value);
|
|
2811
|
+
let insertion = normalized.length;
|
|
2812
|
+
while (insertion > 0 && compareUTF8(current, normalized[insertion - 1]) < 0) {
|
|
2813
|
+
setArrayIndex(normalized, insertion, normalized[insertion - 1]);
|
|
2814
|
+
insertion--;
|
|
2815
|
+
}
|
|
2816
|
+
setArrayIndex(normalized, insertion, current);
|
|
2817
|
+
}
|
|
2818
|
+
if (nonempty && length === 0) {
|
|
2819
|
+
throw new Error(`${name} must be non-empty`);
|
|
2820
|
+
}
|
|
2821
|
+
for (let index = 1;index < normalized.length; index++) {
|
|
2822
|
+
if (normalized[index] === normalized[index - 1]) {
|
|
2823
|
+
throw new Error(`${name} contains a duplicate entry`);
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
return normalized;
|
|
2827
|
+
}
|
|
2828
|
+
function setArrayIndex(array, index, value) {
|
|
2829
|
+
defineProperty(array, `${index}`, {
|
|
2830
|
+
configurable: true,
|
|
2831
|
+
enumerable: true,
|
|
2832
|
+
value,
|
|
2833
|
+
writable: true
|
|
2834
|
+
});
|
|
2835
|
+
}
|
|
2836
|
+
// sdk/typescript/src/schedules.ts
|
|
2837
|
+
function scheduledTask(config) {
|
|
2838
|
+
validateScheduleMembers(config);
|
|
2839
|
+
validateCron(config.cron);
|
|
2840
|
+
if (typeof config.workspace !== "object" || config.workspace === null || Array.isArray(config.workspace)) {
|
|
2841
|
+
throw new Error("schedule workspace must be an object");
|
|
2842
|
+
}
|
|
2843
|
+
const unknownWorkspaceMember = Object.keys(config.workspace).find((key) => key !== "sandbox" && key !== "secrets");
|
|
2844
|
+
if (unknownWorkspaceMember !== undefined) {
|
|
2845
|
+
throw new Error(`schedule workspace has unknown member ${JSON.stringify(unknownWorkspaceMember)}`);
|
|
2846
|
+
}
|
|
2847
|
+
const sandbox2 = config.workspace.sandbox;
|
|
2848
|
+
if (inspectSandboxDefinition(sandbox2) === undefined) {
|
|
2849
|
+
throw new Error("schedule workspace requires a Sandbox definition");
|
|
2850
|
+
}
|
|
2851
|
+
const secrets = encodeWorkspaceSecrets(config.workspace.secrets);
|
|
2852
|
+
return createScheduledTask({
|
|
2853
|
+
id: config.id,
|
|
2854
|
+
payload: scheduledTaskSchema,
|
|
2855
|
+
run: config.run,
|
|
2856
|
+
...config.queue === undefined ? {} : { queue: config.queue },
|
|
2857
|
+
...config.maxDuration === undefined ? {} : { maxDuration: config.maxDuration },
|
|
2858
|
+
...config.ttl === undefined ? {} : { ttl: config.ttl },
|
|
2859
|
+
...config.retry === undefined ? {} : { retry: config.retry },
|
|
2860
|
+
schedule: {
|
|
2861
|
+
cron: config.cron.pattern,
|
|
2862
|
+
timezone: config.cron.timezone,
|
|
2863
|
+
workspace: {
|
|
2864
|
+
sandbox: sandbox2,
|
|
2865
|
+
secrets
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
});
|
|
2869
|
+
}
|
|
2870
|
+
function validateScheduleMembers(value) {
|
|
2871
|
+
const allowed = new Set([
|
|
2872
|
+
"id",
|
|
2873
|
+
"cron",
|
|
2874
|
+
"workspace",
|
|
2875
|
+
"run",
|
|
2876
|
+
"queue",
|
|
2877
|
+
"maxDuration",
|
|
2878
|
+
"ttl",
|
|
2879
|
+
"retry"
|
|
2880
|
+
]);
|
|
2881
|
+
const unknown = Object.keys(value).find((key) => !allowed.has(key));
|
|
2882
|
+
if (unknown !== undefined) {
|
|
2883
|
+
throw new Error(`schedule has unknown member ${JSON.stringify(unknown)}`);
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
var schedules = Object.freeze({
|
|
2887
|
+
task: scheduledTask
|
|
2888
|
+
});
|
|
2889
|
+
var scheduledTaskSchema = {
|
|
2890
|
+
"~standard": {
|
|
2891
|
+
version: 1,
|
|
2892
|
+
vendor: "helmr",
|
|
2893
|
+
validate(value) {
|
|
2894
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2895
|
+
return { issues: [{ message: "expected scheduled task input object" }] };
|
|
2896
|
+
}
|
|
2897
|
+
const input = value;
|
|
2898
|
+
const allowed = new Set([
|
|
2899
|
+
"scheduledAt",
|
|
2900
|
+
"lastScheduledAt",
|
|
2901
|
+
"timezone",
|
|
2902
|
+
"scheduleId",
|
|
2903
|
+
"upcoming"
|
|
2904
|
+
]);
|
|
2905
|
+
if (Object.keys(input).some((key) => !allowed.has(key))) {
|
|
2906
|
+
return { issues: [{ message: "scheduled task input has unknown members" }] };
|
|
2907
|
+
}
|
|
2908
|
+
const scheduledAt = parseDate(input["scheduledAt"]);
|
|
2909
|
+
const lastScheduledAt = input["lastScheduledAt"] === undefined ? undefined : parseDate(input["lastScheduledAt"]);
|
|
2910
|
+
const upcoming = input["upcoming"];
|
|
2911
|
+
if (scheduledAt === undefined || input["lastScheduledAt"] !== undefined && lastScheduledAt === undefined || typeof input["timezone"] !== "string" || typeof input["scheduleId"] !== "string" || !Array.isArray(upcoming)) {
|
|
2912
|
+
return { issues: [{ message: "invalid scheduled task input" }] };
|
|
2913
|
+
}
|
|
2914
|
+
const parsedUpcoming = upcoming.map(parseDate);
|
|
2915
|
+
if (parsedUpcoming.some((date) => date === undefined)) {
|
|
2916
|
+
return { issues: [{ message: "invalid upcoming timestamp" }] };
|
|
2917
|
+
}
|
|
2918
|
+
let scheduleId;
|
|
2919
|
+
try {
|
|
2920
|
+
scheduleId = resourceID(input["scheduleId"], "Scheduled task input.scheduleId");
|
|
2921
|
+
} catch {
|
|
2922
|
+
return { issues: [{ message: "invalid scheduled task input" }] };
|
|
2923
|
+
}
|
|
2924
|
+
return {
|
|
2925
|
+
value: {
|
|
2926
|
+
scheduledAt,
|
|
2927
|
+
...lastScheduledAt === undefined ? {} : { lastScheduledAt },
|
|
2928
|
+
timezone: input["timezone"],
|
|
2929
|
+
scheduleId,
|
|
2930
|
+
upcoming: parsedUpcoming
|
|
2931
|
+
}
|
|
2932
|
+
};
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
};
|
|
2936
|
+
function parseDate(value) {
|
|
2937
|
+
try {
|
|
2938
|
+
return new Date(timestampString(value, "Scheduled task timestamp"));
|
|
2939
|
+
} catch {
|
|
2940
|
+
return;
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
function validateCron(cron) {
|
|
2944
|
+
if (cron === null || typeof cron !== "object" || Array.isArray(cron) || Object.keys(cron).length !== 2 || !Object.hasOwn(cron, "pattern") || !Object.hasOwn(cron, "timezone") || typeof cron.pattern !== "string" || typeof cron.timezone !== "string") {
|
|
2945
|
+
throw new Error("cron must contain exactly pattern and timezone");
|
|
2946
|
+
}
|
|
2947
|
+
if (new TextEncoder().encode(cron.pattern).length === 0 || new TextEncoder().encode(cron.pattern).length > 1024 || new TextEncoder().encode(cron.timezone).length === 0 || new TextEncoder().encode(cron.timezone).length > 255) {
|
|
2948
|
+
throw new Error("cron pattern and timezone must be non-empty bounded strings");
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
// sdk/typescript/src/timers.ts
|
|
2952
|
+
var timers = Object.freeze({
|
|
2953
|
+
waitFor(duration) {
|
|
2954
|
+
return currentRuntimeOperations().waitFor(duration);
|
|
2955
|
+
},
|
|
2956
|
+
waitUntil(date) {
|
|
2957
|
+
return currentRuntimeOperations().waitUntil(date);
|
|
2958
|
+
}
|
|
2959
|
+
});
|
|
2960
|
+
// sdk/typescript/src/tokens.ts
|
|
2961
|
+
var tokens = Object.freeze({
|
|
2962
|
+
async create(request = {}) {
|
|
2963
|
+
const token = await currentRuntimeOperations().tokenCreate(request);
|
|
2964
|
+
return Object.freeze({
|
|
2965
|
+
...token,
|
|
2966
|
+
wait: tokenWait(token.id)
|
|
2967
|
+
});
|
|
2968
|
+
},
|
|
2969
|
+
ref(id) {
|
|
2970
|
+
const normalized = resourceID(id, "Token ID");
|
|
2971
|
+
return Object.freeze({
|
|
2972
|
+
id: normalized,
|
|
2973
|
+
wait: tokenWait(normalized)
|
|
2974
|
+
});
|
|
2975
|
+
}
|
|
2976
|
+
});
|
|
2977
|
+
function tokenWait(tokenId) {
|
|
2978
|
+
const wait = (waitOptions = {}) => {
|
|
2979
|
+
const pending = currentRuntimeOperations().tokenWait(tokenId, waitOptions).then(async (value) => {
|
|
2980
|
+
const parsed = waitOptions.schema === undefined ? value : await parsePayloadWithSchema(waitOptions.schema, value, "Token completion result");
|
|
2981
|
+
return Object.freeze({
|
|
2982
|
+
ok: true,
|
|
2983
|
+
data: parsed,
|
|
2984
|
+
unwrap: () => parsed
|
|
2985
|
+
});
|
|
2986
|
+
}).catch((error) => Object.freeze({
|
|
2987
|
+
ok: false,
|
|
2988
|
+
error: tokenWaitError(error),
|
|
2989
|
+
unwrap() {
|
|
2990
|
+
throw this.error;
|
|
2991
|
+
}
|
|
2992
|
+
}));
|
|
2993
|
+
return Object.freeze({
|
|
2994
|
+
then(onfulfilled, onrejected) {
|
|
2995
|
+
return pending.then(onfulfilled, onrejected);
|
|
2996
|
+
},
|
|
2997
|
+
async unwrap() {
|
|
2998
|
+
const result = await pending;
|
|
2999
|
+
if (!result.ok)
|
|
3000
|
+
throw result.error;
|
|
3001
|
+
return result.data;
|
|
3002
|
+
}
|
|
3003
|
+
});
|
|
3004
|
+
};
|
|
3005
|
+
return wait;
|
|
3006
|
+
}
|
|
3007
|
+
function tokenWaitError(value) {
|
|
3008
|
+
if (value instanceof Error && "code" in value && (value.code === "wait_timeout" || value.code === "token_cancelled" || value.code === "token_expired")) {
|
|
3009
|
+
return value;
|
|
3010
|
+
}
|
|
3011
|
+
throw value;
|
|
3012
|
+
}
|
|
3013
|
+
export {
|
|
3014
|
+
workspaces,
|
|
3015
|
+
tokens,
|
|
3016
|
+
timers,
|
|
3017
|
+
task,
|
|
3018
|
+
source,
|
|
3019
|
+
sessions,
|
|
3020
|
+
schedules,
|
|
3021
|
+
sandbox,
|
|
3022
|
+
queue,
|
|
3023
|
+
metadata,
|
|
3024
|
+
logger,
|
|
3025
|
+
image,
|
|
3026
|
+
defineConfig,
|
|
3027
|
+
actor,
|
|
3028
|
+
HelmrClient
|
|
3029
|
+
};
|