@helmr/sdk 0.1.1-rc.4 → 0.1.1-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -12
- package/dist/compile.js +33 -2
- package/dist/idempotency.d.ts +30 -0
- package/dist/index.d.ts +25 -7
- package/dist/index.js +684 -170
- package/dist/internal.d.ts +131 -32
- package/dist/internal.js +264 -25
- package/dist/runtime/client.d.ts +114 -56
- package/dist/runtime/run.d.ts +32 -48
- package/dist/schedules.d.ts +30 -0
- package/dist/schema/payload.d.ts +40 -0
- package/dist/schema/task.d.ts +15 -0
- package/dist/task.d.ts +6 -3
- package/dist/trigger.d.ts +10 -20
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,9 +1,92 @@
|
|
|
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
|
+
|
|
1
82
|
// sdk/typescript/src/schema/task.ts
|
|
2
83
|
var TASK_ID_PATTERN = "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$";
|
|
3
84
|
var TASK_ID_MAX_LENGTH = 128;
|
|
4
85
|
var DEFAULT_MAX_DURATION_SECONDS = 900;
|
|
5
86
|
var MIN_MAX_DURATION_SECONDS = 5;
|
|
6
87
|
var MAX_DURATION_SECONDS = 86400;
|
|
88
|
+
var QUEUE_NAME_PATTERN = "^[A-Za-z0-9][A-Za-z0-9._/-]{0,255}$";
|
|
89
|
+
var QUEUE_NAME_MAX_LENGTH = 256;
|
|
7
90
|
|
|
8
91
|
class TaskIdError extends Error {
|
|
9
92
|
name = "TaskIdError";
|
|
@@ -57,36 +140,60 @@ function readOptionalMaxDurationSeconds(value, label = "task maxDuration") {
|
|
|
57
140
|
function validateOptionalMaxDurationSeconds(value, label = "task maxDuration") {
|
|
58
141
|
readOptionalMaxDurationSeconds(value, label);
|
|
59
142
|
}
|
|
60
|
-
function isAsciiAlnum(code) {
|
|
61
|
-
return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
|
|
62
|
-
}
|
|
63
143
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
144
|
+
class TaskQueueNameError extends Error {
|
|
145
|
+
name = "TaskQueueNameError";
|
|
146
|
+
value;
|
|
147
|
+
constructor(value) {
|
|
148
|
+
super(`queue name must match ${QUEUE_NAME_PATTERN}: ${JSON.stringify(value)}`);
|
|
149
|
+
this.value = value;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
68
152
|
|
|
69
|
-
class
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
153
|
+
class TaskQueueConcurrencyLimitError extends Error {
|
|
154
|
+
name = "TaskQueueConcurrencyLimitError";
|
|
155
|
+
value;
|
|
156
|
+
constructor(value) {
|
|
157
|
+
super("queue concurrencyLimit must be a positive integer");
|
|
158
|
+
this.value = value;
|
|
74
159
|
}
|
|
75
|
-
|
|
76
|
-
|
|
160
|
+
}
|
|
161
|
+
function validateQueueName(value) {
|
|
162
|
+
if (!isValidQueueName(value)) {
|
|
163
|
+
throw new TaskQueueNameError(value);
|
|
77
164
|
}
|
|
78
165
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
super(message);
|
|
83
|
-
this.name = "MessageTimeoutError";
|
|
84
|
-
Object.defineProperty(this, messageTimeoutErrorBrand, { value: true });
|
|
166
|
+
function isValidQueueName(value) {
|
|
167
|
+
if (value.length === 0 || value.length > QUEUE_NAME_MAX_LENGTH) {
|
|
168
|
+
return false;
|
|
85
169
|
}
|
|
86
|
-
|
|
87
|
-
|
|
170
|
+
const first = value.charCodeAt(0);
|
|
171
|
+
if (!isAsciiAlnum(first)) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
for (let index = 1;index < value.length; index += 1) {
|
|
175
|
+
const code = value.charCodeAt(index);
|
|
176
|
+
if (!(isAsciiAlnum(code) || code === 46 || code === 95 || code === 45 || code === 47)) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
function validateOptionalQueueConcurrencyLimit(value) {
|
|
183
|
+
if (value === undefined || value === null) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (typeof value === "number" && Number.isInteger(value) && Number.isFinite(value) && value > 0) {
|
|
187
|
+
return;
|
|
88
188
|
}
|
|
189
|
+
throw new TaskQueueConcurrencyLimitError(value);
|
|
89
190
|
}
|
|
191
|
+
function isAsciiAlnum(code) {
|
|
192
|
+
return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// sdk/typescript/src/internal.ts
|
|
196
|
+
var concurrentWaitErrorBrand = Symbol.for("helmr.sdk.ConcurrentWaitError");
|
|
90
197
|
|
|
91
198
|
class ConcurrentWaitError extends Error {
|
|
92
199
|
constructor(message) {
|
|
@@ -162,12 +269,136 @@ var sandboxBuilderBrand = Symbol.for("helmr.sdk.SandboxBuilder");
|
|
|
162
269
|
var sourceFileRefBrand = Symbol.for("helmr.sdk.SourceFileRef");
|
|
163
270
|
var sourceDirRefBrand = Symbol.for("helmr.sdk.SourceDirRef");
|
|
164
271
|
function markTask(config) {
|
|
272
|
+
if ("schedule" in config) {
|
|
273
|
+
throw new Error(`task ${JSON.stringify(config.id)} must use schedules.task(...) for declarative schedules`);
|
|
274
|
+
}
|
|
275
|
+
return markTaskInternal(config, undefined);
|
|
276
|
+
}
|
|
277
|
+
function markScheduledTask(config, schedule) {
|
|
278
|
+
return markTaskInternal(config, schedule);
|
|
279
|
+
}
|
|
280
|
+
function markTaskInternal(config, schedule) {
|
|
165
281
|
validateTaskId(config.id);
|
|
166
282
|
validateOptionalMaxDurationSeconds(config.maxDuration);
|
|
283
|
+
validateTaskQueue(config.id, config.queue);
|
|
284
|
+
validateTaskSchedule(config.id, schedule);
|
|
285
|
+
validateOptionalTTL(config.ttl, `task ${JSON.stringify(config.id)} ttl`);
|
|
286
|
+
assertPayloadSchema(config.payload, `task ${JSON.stringify(config.id)} payload`);
|
|
287
|
+
if (schedule !== undefined) {
|
|
288
|
+
Object.defineProperty(config, "schedule", {
|
|
289
|
+
value: Object.freeze({ ...schedule }),
|
|
290
|
+
enumerable: true
|
|
291
|
+
});
|
|
292
|
+
}
|
|
167
293
|
Object.defineProperty(config, taskBrand, { value: true });
|
|
168
294
|
Object.defineProperty(config, taskOriginBrand, { value: captureTaskOrigin() });
|
|
169
295
|
return config;
|
|
170
296
|
}
|
|
297
|
+
function validateTaskSchedule(taskId, value) {
|
|
298
|
+
if (value === undefined) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (value.cron.trim() === "") {
|
|
302
|
+
throw new Error(`task ${JSON.stringify(taskId)} schedule cron is required`);
|
|
303
|
+
}
|
|
304
|
+
if (value.timezone !== undefined && value.timezone.trim() === "") {
|
|
305
|
+
throw new Error(`task ${JSON.stringify(taskId)} schedule timezone must not be empty`);
|
|
306
|
+
}
|
|
307
|
+
if (value.workspace.kind !== "github") {
|
|
308
|
+
throw new Error(`task ${JSON.stringify(taskId)} schedule workspace must be workspace.github(...)`);
|
|
309
|
+
}
|
|
310
|
+
if (value.secretBindings !== undefined) {
|
|
311
|
+
if (value.secretBindings === null || typeof value.secretBindings !== "object" || Array.isArray(value.secretBindings)) {
|
|
312
|
+
throw new Error(`task ${JSON.stringify(taskId)} schedule secretBindings must be an object`);
|
|
313
|
+
}
|
|
314
|
+
for (const [name, binding] of Object.entries(value.secretBindings)) {
|
|
315
|
+
validateSecretName(name, `task ${JSON.stringify(taskId)} schedule secretBindings.${name}`);
|
|
316
|
+
if (typeof binding !== "string" || binding.trim() === "") {
|
|
317
|
+
throw new Error(`task ${JSON.stringify(taskId)} schedule secretBindings.${name} must be a non-empty string`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function validateTaskQueue(taskId, value) {
|
|
323
|
+
if (value === undefined) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (value.name !== undefined) {
|
|
327
|
+
validateQueueName(value.name);
|
|
328
|
+
}
|
|
329
|
+
validateOptionalQueueConcurrencyLimit(value.concurrencyLimit);
|
|
330
|
+
if (value.name === undefined && value.concurrencyLimit === undefined) {
|
|
331
|
+
throw new Error(`task ${JSON.stringify(taskId)} queue must include name or concurrencyLimit`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function validateOptionalTTL(value, label = "ttl") {
|
|
335
|
+
if (value === undefined) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (typeof value === "string" && isPositiveDurationString(value)) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
throw new Error(`${label} must be a positive duration string`);
|
|
342
|
+
}
|
|
343
|
+
function isPositiveDurationString(value) {
|
|
344
|
+
const raw = value.trim();
|
|
345
|
+
if (raw === "") {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
if (/^[1-9][0-9]*d$/.test(raw)) {
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
const sign = raw.startsWith("+") || raw.startsWith("-") ? raw.slice(0, 1) : "";
|
|
352
|
+
if (sign === "-") {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
const body = sign === "" ? raw : raw.slice(1);
|
|
356
|
+
const tokenPattern = /([0-9]+(?:\.[0-9]*)?|\.[0-9]+)(ns|us|µs|μs|ms|s|m|h)/gy;
|
|
357
|
+
let totalNanoseconds = 0;
|
|
358
|
+
let offset = 0;
|
|
359
|
+
for (;; ) {
|
|
360
|
+
tokenPattern.lastIndex = offset;
|
|
361
|
+
const match = tokenPattern.exec(body);
|
|
362
|
+
if (match === null) {
|
|
363
|
+
return offset === body.length && totalNanoseconds >= 1;
|
|
364
|
+
}
|
|
365
|
+
if (match.index !== offset) {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
const amount = Number(match[1]);
|
|
369
|
+
if (!Number.isFinite(amount) || amount < 0) {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
totalNanoseconds += amount * durationUnitNanoseconds(match[2] ?? "");
|
|
373
|
+
offset = tokenPattern.lastIndex;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function durationUnitNanoseconds(unit) {
|
|
377
|
+
switch (unit) {
|
|
378
|
+
case "ns":
|
|
379
|
+
return 1;
|
|
380
|
+
case "us":
|
|
381
|
+
case "µs":
|
|
382
|
+
case "μs":
|
|
383
|
+
return 1000;
|
|
384
|
+
case "ms":
|
|
385
|
+
return 1e6;
|
|
386
|
+
case "s":
|
|
387
|
+
return 1e9;
|
|
388
|
+
case "m":
|
|
389
|
+
return 60000000000;
|
|
390
|
+
case "h":
|
|
391
|
+
return 3600000000000;
|
|
392
|
+
default:
|
|
393
|
+
return 0;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function parseTaskPayload(task, payload) {
|
|
397
|
+
if (task.payload === undefined) {
|
|
398
|
+
throw new Error(`task ${JSON.stringify(task.id)} does not accept payload`);
|
|
399
|
+
}
|
|
400
|
+
return await parsePayloadWithSchema(task.payload, payload, `task ${JSON.stringify(task.id)} payload`);
|
|
401
|
+
}
|
|
171
402
|
function markConfig(config) {
|
|
172
403
|
Object.defineProperty(config, configBrand, { value: true });
|
|
173
404
|
return config;
|
|
@@ -300,6 +531,55 @@ function hasBrand(value, brand) {
|
|
|
300
531
|
return value !== null && typeof value === "object" && value[brand] === true;
|
|
301
532
|
}
|
|
302
533
|
|
|
534
|
+
// sdk/typescript/src/idempotency.ts
|
|
535
|
+
import { createHash } from "node:crypto";
|
|
536
|
+
var idempotencyKeys = {
|
|
537
|
+
create(key, options = {}) {
|
|
538
|
+
const scope = options.scope ?? "run";
|
|
539
|
+
return {
|
|
540
|
+
value: createIdempotencyKey(key, scope, options),
|
|
541
|
+
key,
|
|
542
|
+
scope
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
function runIdempotencyRequestFields(input, ttl) {
|
|
547
|
+
if (input === undefined) {
|
|
548
|
+
return {};
|
|
549
|
+
}
|
|
550
|
+
const key = isIdempotencyKey(input) ? input : idempotencyKeys.create(input);
|
|
551
|
+
return {
|
|
552
|
+
idempotency_key: key.value,
|
|
553
|
+
...ttl === undefined ? {} : { idempotency_key_ttl: ttl },
|
|
554
|
+
idempotency_key_options: {
|
|
555
|
+
key: key.key,
|
|
556
|
+
scope: key.scope
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
function createIdempotencyKey(key, scope, options) {
|
|
561
|
+
const material = {
|
|
562
|
+
scope,
|
|
563
|
+
key: Array.isArray(key) ? [...key] : [key]
|
|
564
|
+
};
|
|
565
|
+
const runId = "runId" in options ? options.runId : undefined;
|
|
566
|
+
const attemptNumber = "attemptNumber" in options ? options.attemptNumber : undefined;
|
|
567
|
+
if (scope === "run" && runId !== undefined) {
|
|
568
|
+
material["runId"] = runId;
|
|
569
|
+
}
|
|
570
|
+
if (scope === "attempt") {
|
|
571
|
+
if (runId === undefined || attemptNumber === undefined) {
|
|
572
|
+
throw new Error("attempt-scoped idempotency keys require runId and attemptNumber");
|
|
573
|
+
}
|
|
574
|
+
material["runId"] = runId;
|
|
575
|
+
material["attemptNumber"] = attemptNumber;
|
|
576
|
+
}
|
|
577
|
+
return createHash("sha256").update(JSON.stringify(material)).digest("hex");
|
|
578
|
+
}
|
|
579
|
+
function isIdempotencyKey(value) {
|
|
580
|
+
return typeof value === "object" && value !== null && "value" in value && "key" in value && "scope" in value;
|
|
581
|
+
}
|
|
582
|
+
|
|
303
583
|
// sdk/typescript/src/runtime/errors.ts
|
|
304
584
|
class RunNotFoundError extends Error {
|
|
305
585
|
runId;
|
|
@@ -352,27 +632,18 @@ function runSnapshot(snapshot) {
|
|
|
352
632
|
function pendingWaitpointFromResponse(runId, wait) {
|
|
353
633
|
if (wait === undefined || wait === null)
|
|
354
634
|
return null;
|
|
355
|
-
if (wait.kind === "approval") {
|
|
356
|
-
return {
|
|
357
|
-
kind: "approval",
|
|
358
|
-
runId,
|
|
359
|
-
waitpointId: wait.waitpoint_id,
|
|
360
|
-
message: wait.message ?? "",
|
|
361
|
-
timeout: wait.timeout ?? null,
|
|
362
|
-
requestedAt: wait.requested_at
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
635
|
return {
|
|
366
|
-
kind:
|
|
636
|
+
kind: wait.kind,
|
|
367
637
|
runId,
|
|
368
638
|
waitpointId: wait.waitpoint_id,
|
|
369
|
-
prompt: wait.prompt ?? null,
|
|
370
639
|
timeout: wait.timeout ?? null,
|
|
640
|
+
request: wait.request === undefined ? {} : wait.request,
|
|
641
|
+
displayText: wait.display_text ?? "",
|
|
371
642
|
requestedAt: wait.requested_at
|
|
372
643
|
};
|
|
373
644
|
}
|
|
374
645
|
function isTerminalRunStatus(status) {
|
|
375
|
-
return status === "succeeded" || status === "failed" || status === "cancelled";
|
|
646
|
+
return status === "succeeded" || status === "failed" || status === "cancelled" || status === "expired";
|
|
376
647
|
}
|
|
377
648
|
function runId(value) {
|
|
378
649
|
return typeof value === "string" ? value : value.id;
|
|
@@ -396,6 +667,7 @@ function runStatus(status) {
|
|
|
396
667
|
case "succeeded":
|
|
397
668
|
case "failed":
|
|
398
669
|
case "cancelled":
|
|
670
|
+
case "expired":
|
|
399
671
|
return status;
|
|
400
672
|
default:
|
|
401
673
|
throw new Error(`unsupported run status ${JSON.stringify(status)}`);
|
|
@@ -413,6 +685,7 @@ function runWorkspaceFromSpec(spec) {
|
|
|
413
685
|
|
|
414
686
|
// sdk/typescript/src/runtime/client.ts
|
|
415
687
|
var MAX_SSE_BUFFER_CHARS = 1024 * 1024;
|
|
688
|
+
var triggerTaskClientMethod = Symbol.for("helmr.sdk.client.triggerTask");
|
|
416
689
|
|
|
417
690
|
class HelmrClient {
|
|
418
691
|
#baseUrl;
|
|
@@ -445,24 +718,57 @@ class HelmrClient {
|
|
|
445
718
|
}
|
|
446
719
|
}
|
|
447
720
|
tasks = {
|
|
448
|
-
trigger: async (
|
|
449
|
-
const
|
|
450
|
-
const
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
workspace: runWorkspace,
|
|
458
|
-
max_duration_seconds: maxDurationSeconds
|
|
459
|
-
}),
|
|
460
|
-
headers: { "content-type": "application/json" }
|
|
461
|
-
});
|
|
462
|
-
const run = await response.json();
|
|
463
|
-
return runHandle(run.id, run.task_id);
|
|
721
|
+
trigger: async (...args) => {
|
|
722
|
+
const taskId = args[0];
|
|
723
|
+
const hasPayload = args.length === 3;
|
|
724
|
+
const payload = hasPayload ? args[1] : undefined;
|
|
725
|
+
const opts = hasPayload ? args[2] : args[1];
|
|
726
|
+
if (hasPayload && payload === undefined) {
|
|
727
|
+
throw new Error(`task ${JSON.stringify(taskId)} requires payload`);
|
|
728
|
+
}
|
|
729
|
+
return await this.#triggerRun(taskId, payload, opts);
|
|
464
730
|
}
|
|
465
731
|
};
|
|
732
|
+
async[triggerTaskClientMethod](task, ...args) {
|
|
733
|
+
const hasPayload = task.payload !== undefined;
|
|
734
|
+
const payload = hasPayload ? args[0] : undefined;
|
|
735
|
+
const opts = hasPayload ? args[1] : args[0];
|
|
736
|
+
if (task.payload !== undefined) {
|
|
737
|
+
if (payload === undefined) {
|
|
738
|
+
throw new Error(`task ${JSON.stringify(task.id)} requires payload`);
|
|
739
|
+
}
|
|
740
|
+
await parseTaskPayload(task, payload);
|
|
741
|
+
} else if (args.length > 1) {
|
|
742
|
+
throw new Error(`task ${JSON.stringify(task.id)} does not accept payload`);
|
|
743
|
+
}
|
|
744
|
+
return await this.#triggerRun(task.id, payload, opts, readOptionalMaxDurationSeconds(task.maxDuration));
|
|
745
|
+
}
|
|
746
|
+
async#triggerRun(taskId, payload, opts, maxDurationSeconds) {
|
|
747
|
+
const runWorkspace = runWorkspaceFromSpec(opts.workspace);
|
|
748
|
+
const runOptions = {
|
|
749
|
+
...opts.deploymentId === undefined ? {} : { deployment_id: opts.deploymentId },
|
|
750
|
+
...opts.version === undefined ? {} : { version: opts.version },
|
|
751
|
+
...opts.queue === undefined ? {} : { queue: { name: opts.queue } },
|
|
752
|
+
...opts.concurrencyKey === undefined ? {} : { concurrency_key: opts.concurrencyKey },
|
|
753
|
+
...opts.priority === undefined ? {} : { priority: opts.priority },
|
|
754
|
+
...opts.ttl === undefined ? {} : { ttl: opts.ttl },
|
|
755
|
+
...maxDurationSeconds === undefined ? {} : { max_duration_seconds: maxDurationSeconds },
|
|
756
|
+
...runIdempotencyRequestFields(opts.idempotencyKey, opts.idempotencyKeyTTL)
|
|
757
|
+
};
|
|
758
|
+
const response = await this.#fetch("/api/runs", {
|
|
759
|
+
method: "POST",
|
|
760
|
+
body: JSON.stringify({
|
|
761
|
+
task_id: taskId,
|
|
762
|
+
secrets: opts.secrets ?? {},
|
|
763
|
+
...payload === undefined ? {} : { payload },
|
|
764
|
+
workspace: runWorkspace,
|
|
765
|
+
...Object.keys(runOptions).length === 0 ? {} : { options: runOptions }
|
|
766
|
+
}),
|
|
767
|
+
headers: { "content-type": "application/json" }
|
|
768
|
+
});
|
|
769
|
+
const run = await response.json();
|
|
770
|
+
return runHandle(run.id, run.task_id);
|
|
771
|
+
}
|
|
466
772
|
runs = {
|
|
467
773
|
retrieve: async (idOrHandle, opts = {}) => {
|
|
468
774
|
const response = await this.#json(`/api/runs/${encodeURIComponent(runId(idOrHandle))}`, requestSignal(opts.signal));
|
|
@@ -519,27 +825,19 @@ class HelmrClient {
|
|
|
519
825
|
}
|
|
520
826
|
};
|
|
521
827
|
waitpoints = {
|
|
522
|
-
|
|
523
|
-
const
|
|
524
|
-
await this.#fetch(`/api/runs/${encodeURIComponent(resolved.runId)}/waitpoints/${encodeURIComponent(resolved.waitpointId)}/approve`, {
|
|
828
|
+
create: async (opts) => {
|
|
829
|
+
const response = await this.#json("/api/waitpoints", {
|
|
525
830
|
method: "POST",
|
|
526
|
-
body: JSON.stringify(
|
|
831
|
+
body: JSON.stringify(waitpointCreateBody(opts)),
|
|
527
832
|
headers: { "content-type": "application/json" }
|
|
528
833
|
});
|
|
834
|
+
return waitpointFromResponse(response);
|
|
529
835
|
},
|
|
530
|
-
|
|
836
|
+
respond: async (target, waitpointIdOrOpts, opts = {}) => {
|
|
531
837
|
const resolved = resolveWaitpointArgs(target, waitpointIdOrOpts, opts);
|
|
532
|
-
await this.#fetch(`/api/
|
|
838
|
+
await this.#fetch(`/api/waitpoints/${encodeURIComponent(resolved.waitpointId)}/respond`, {
|
|
533
839
|
method: "POST",
|
|
534
|
-
body: JSON.stringify(
|
|
535
|
-
headers: { "content-type": "application/json" }
|
|
536
|
-
});
|
|
537
|
-
},
|
|
538
|
-
reply: async (target, waitpointIdOrOpts, opts) => {
|
|
539
|
-
const resolved = resolveWaitpointArgs(target, waitpointIdOrOpts, opts);
|
|
540
|
-
await this.#fetch(`/api/runs/${encodeURIComponent(resolved.runId)}/waitpoints/${encodeURIComponent(resolved.waitpointId)}/message`, {
|
|
541
|
-
method: "POST",
|
|
542
|
-
body: JSON.stringify({ text: resolved.opts.text, attachments: [] }),
|
|
840
|
+
body: JSON.stringify(waitpointRespondBody(resolved.opts)),
|
|
543
841
|
headers: { "content-type": "application/json" }
|
|
544
842
|
});
|
|
545
843
|
},
|
|
@@ -548,21 +846,77 @@ class HelmrClient {
|
|
|
548
846
|
const resolved = resolveWaitpointArgs(target, waitpointIdOrOpts, opts);
|
|
549
847
|
const response = await this.#json("/api/waitpoints/tokens", {
|
|
550
848
|
method: "POST",
|
|
551
|
-
body: JSON.stringify(waitpointTokenCreateBody(resolved.
|
|
849
|
+
body: JSON.stringify(waitpointTokenCreateBody(resolved.waitpointId, resolved.opts)),
|
|
552
850
|
headers: { "content-type": "application/json" }
|
|
553
851
|
});
|
|
554
852
|
return waitpointResponseTokenFromResponse(response);
|
|
555
853
|
},
|
|
556
|
-
|
|
557
|
-
const resolved = typeof target === "string" ?
|
|
558
|
-
await this.#fetch(`/api/waitpoints/tokens/${encodeURIComponent(resolved.id)}/
|
|
854
|
+
respond: async (target, tokenOrOpts, maybeOpts) => {
|
|
855
|
+
const resolved = typeof target === "string" ? resolveWaitpointTokenRespondArgs(target, tokenOrOpts, maybeOpts) : { id: target.id, token: target.token, opts: tokenOrOpts };
|
|
856
|
+
await this.#fetch(`/api/waitpoints/tokens/${encodeURIComponent(resolved.id)}/respond`, {
|
|
559
857
|
method: "POST",
|
|
560
|
-
body: JSON.stringify(
|
|
858
|
+
body: JSON.stringify(waitpointTokenRespondBody(resolved.token, resolved.opts)),
|
|
561
859
|
headers: { "content-type": "application/json" }
|
|
562
860
|
});
|
|
563
861
|
}
|
|
564
862
|
}
|
|
565
863
|
};
|
|
864
|
+
schedules = {
|
|
865
|
+
create: async (opts) => {
|
|
866
|
+
const response = await this.#json("/api/schedules", {
|
|
867
|
+
method: "POST",
|
|
868
|
+
body: JSON.stringify(scheduleCreateBody(opts)),
|
|
869
|
+
headers: { "content-type": "application/json" }
|
|
870
|
+
});
|
|
871
|
+
return scheduleFromResponse(response);
|
|
872
|
+
},
|
|
873
|
+
list: async (opts = {}) => {
|
|
874
|
+
const query = scheduleScopeQuery(opts);
|
|
875
|
+
const suffix = query.size === 0 ? "" : `?${query}`;
|
|
876
|
+
const response = await this.#json(`/api/schedules${suffix}`, requestSignal(opts.signal));
|
|
877
|
+
return response.schedules.map(scheduleFromResponse);
|
|
878
|
+
},
|
|
879
|
+
update: async (id, opts) => {
|
|
880
|
+
const query = scheduleScopeQuery(opts);
|
|
881
|
+
const suffix = query.size === 0 ? "" : `?${query}`;
|
|
882
|
+
const response = await this.#json(`/api/schedules/${encodeURIComponent(id)}${suffix}`, {
|
|
883
|
+
method: "PUT",
|
|
884
|
+
body: JSON.stringify(scheduleCreateBody(opts)),
|
|
885
|
+
headers: { "content-type": "application/json" },
|
|
886
|
+
...requestSignal(opts.signal)
|
|
887
|
+
});
|
|
888
|
+
return scheduleFromResponse(response);
|
|
889
|
+
},
|
|
890
|
+
retrieve: async (id, opts = {}) => {
|
|
891
|
+
const query = scheduleScopeQuery(opts);
|
|
892
|
+
const suffix = query.size === 0 ? "" : `?${query}`;
|
|
893
|
+
return scheduleFromResponse(await this.#json(`/api/schedules/${encodeURIComponent(id)}${suffix}`, requestSignal(opts.signal)));
|
|
894
|
+
},
|
|
895
|
+
activate: async (id, opts = {}) => {
|
|
896
|
+
const query = scheduleScopeQuery(opts);
|
|
897
|
+
const suffix = query.size === 0 ? "" : `?${query}`;
|
|
898
|
+
return scheduleFromResponse(await this.#json(`/api/schedules/${encodeURIComponent(id)}/activate${suffix}`, {
|
|
899
|
+
method: "POST",
|
|
900
|
+
...requestSignal(opts.signal)
|
|
901
|
+
}));
|
|
902
|
+
},
|
|
903
|
+
deactivate: async (id, opts = {}) => {
|
|
904
|
+
const query = scheduleScopeQuery(opts);
|
|
905
|
+
const suffix = query.size === 0 ? "" : `?${query}`;
|
|
906
|
+
return scheduleFromResponse(await this.#json(`/api/schedules/${encodeURIComponent(id)}/deactivate${suffix}`, {
|
|
907
|
+
method: "POST",
|
|
908
|
+
...requestSignal(opts.signal)
|
|
909
|
+
}));
|
|
910
|
+
},
|
|
911
|
+
delete: async (id, opts = {}) => {
|
|
912
|
+
const query = scheduleScopeQuery(opts);
|
|
913
|
+
const suffix = query.size === 0 ? "" : `?${query}`;
|
|
914
|
+
await this.#fetch(`/api/schedules/${encodeURIComponent(id)}${suffix}`, {
|
|
915
|
+
method: "DELETE",
|
|
916
|
+
...requestSignal(opts.signal)
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
};
|
|
566
920
|
async#retrieveLogs(id, signal) {
|
|
567
921
|
const response = await this.#json(`/api/runs/${encodeURIComponent(id)}/logs`, requestSignal(signal));
|
|
568
922
|
return {
|
|
@@ -664,43 +1018,122 @@ function runResponseToSnapshot(response) {
|
|
|
664
1018
|
exitCode: response.exit_code ?? null,
|
|
665
1019
|
...response.created_at === undefined ? {} : { createdAt: response.created_at },
|
|
666
1020
|
...response.updated_at === undefined ? {} : { updatedAt: response.updated_at },
|
|
667
|
-
pendingWaitpoint: pendingWaitpointFromResponse(response.id, response.
|
|
1021
|
+
pendingWaitpoint: pendingWaitpointFromResponse(response.id, response.pending_waitpoint),
|
|
668
1022
|
..."output" in response ? { output: response.output } : {}
|
|
669
1023
|
});
|
|
670
1024
|
}
|
|
671
|
-
function
|
|
672
|
-
return
|
|
1025
|
+
function scheduleCreateBody(opts) {
|
|
1026
|
+
return {
|
|
1027
|
+
..."deduplicationKey" in opts && opts.deduplicationKey !== undefined ? { deduplication_key: opts.deduplicationKey } : {},
|
|
1028
|
+
...opts.projectId === undefined ? {} : { project_id: opts.projectId },
|
|
1029
|
+
...opts.environmentId === undefined ? {} : { environment_id: opts.environmentId },
|
|
1030
|
+
...opts.externalId === undefined ? {} : { external_id: opts.externalId },
|
|
1031
|
+
task: opts.task,
|
|
1032
|
+
cron: opts.cron,
|
|
1033
|
+
...opts.timezone === undefined ? {} : { timezone: opts.timezone },
|
|
1034
|
+
...opts.active === undefined ? {} : { active: opts.active },
|
|
1035
|
+
workspace: runWorkspaceFromSpec(opts.workspace),
|
|
1036
|
+
...opts.secretBindings === undefined ? {} : { secret_bindings: opts.secretBindings },
|
|
1037
|
+
...opts.options === undefined ? {} : { options: runOptionsBody(opts.options) }
|
|
1038
|
+
};
|
|
673
1039
|
}
|
|
674
|
-
function
|
|
1040
|
+
function runOptionsBody(opts) {
|
|
1041
|
+
if (opts === undefined)
|
|
1042
|
+
return {};
|
|
1043
|
+
return {
|
|
1044
|
+
...opts.deploymentId === undefined ? {} : { deployment_id: opts.deploymentId },
|
|
1045
|
+
...opts.version === undefined ? {} : { version: opts.version },
|
|
1046
|
+
...opts.queue === undefined ? {} : { queue: { name: opts.queue } },
|
|
1047
|
+
...opts.concurrencyKey === undefined ? {} : { concurrency_key: opts.concurrencyKey },
|
|
1048
|
+
...opts.priority === undefined ? {} : { priority: opts.priority },
|
|
1049
|
+
...opts.ttl === undefined ? {} : { ttl: opts.ttl },
|
|
1050
|
+
...opts.maxDurationSeconds === undefined ? {} : { max_duration_seconds: opts.maxDurationSeconds }
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
function scheduleScopeQuery(opts) {
|
|
1054
|
+
const query = new URLSearchParams;
|
|
1055
|
+
if (opts.projectId !== undefined)
|
|
1056
|
+
query.set("project_id", opts.projectId);
|
|
1057
|
+
if (opts.environmentId !== undefined)
|
|
1058
|
+
query.set("environment_id", opts.environmentId);
|
|
1059
|
+
return query;
|
|
1060
|
+
}
|
|
1061
|
+
function scheduleFromResponse(response) {
|
|
1062
|
+
return {
|
|
1063
|
+
id: response.id,
|
|
1064
|
+
type: response.type,
|
|
1065
|
+
projectId: response.project_id,
|
|
1066
|
+
environmentId: response.environment_id,
|
|
1067
|
+
task: response.task,
|
|
1068
|
+
...response.deduplication_key === undefined || response.deduplication_key === "" ? {} : { deduplicationKey: response.deduplication_key },
|
|
1069
|
+
...response.external_id === undefined || response.external_id === "" ? {} : { externalId: response.external_id },
|
|
1070
|
+
cron: response.cron,
|
|
1071
|
+
timezone: response.timezone,
|
|
1072
|
+
active: response.active,
|
|
1073
|
+
status: response.status,
|
|
1074
|
+
...response.last_error === undefined || response.last_error === "" ? {} : { lastError: response.last_error },
|
|
1075
|
+
..."workspace" in response ? { workspace: response.workspace } : {},
|
|
1076
|
+
...response.next_scheduled_at === undefined ? {} : { nextScheduledAt: response.next_scheduled_at },
|
|
1077
|
+
...response.last_scheduled_at === undefined ? {} : { lastScheduledAt: response.last_scheduled_at },
|
|
1078
|
+
createdAt: response.created_at,
|
|
1079
|
+
updatedAt: response.updated_at
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
function waitpointTokenCreateBody(waitpointId, opts) {
|
|
675
1083
|
return {
|
|
676
|
-
run_id: runId2,
|
|
677
1084
|
waitpoint_id: waitpointId,
|
|
678
|
-
...opts.actions === undefined ? {} : { actions: opts.actions },
|
|
679
1085
|
...opts.expiresInSeconds === undefined ? {} : { expires_in_seconds: opts.expiresInSeconds },
|
|
680
1086
|
...opts.expiresAt === undefined ? {} : { expires_at: opts.expiresAt },
|
|
681
1087
|
...opts.metadata === undefined ? {} : { metadata: opts.metadata }
|
|
682
1088
|
};
|
|
683
1089
|
}
|
|
684
|
-
function
|
|
1090
|
+
function waitpointCreateBody(opts) {
|
|
685
1091
|
return {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
...
|
|
689
|
-
...
|
|
1092
|
+
...opts.projectId === undefined ? {} : { project_id: opts.projectId },
|
|
1093
|
+
...opts.environmentId === undefined ? {} : { environment_id: opts.environmentId },
|
|
1094
|
+
...opts.request === undefined ? {} : { request: opts.request },
|
|
1095
|
+
...opts.displayText === undefined ? {} : { display_text: opts.displayText },
|
|
1096
|
+
expires_at: opts.expiresAt,
|
|
1097
|
+
...opts.idempotencyKey === undefined ? {} : { idempotency_key: opts.idempotencyKey },
|
|
1098
|
+
...opts.idempotencyKeyExpiresAt === undefined ? {} : { idempotency_key_expires_at: opts.idempotencyKeyExpiresAt },
|
|
1099
|
+
...opts.idempotencyKeyTTLSeconds === undefined ? {} : { idempotency_key_ttl_seconds: opts.idempotencyKeyTTLSeconds }
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
function waitpointFromResponse(response) {
|
|
1103
|
+
return {
|
|
1104
|
+
id: response.id,
|
|
1105
|
+
projectId: response.project_id,
|
|
1106
|
+
environmentId: response.environment_id,
|
|
1107
|
+
kind: response.kind,
|
|
1108
|
+
status: response.status,
|
|
1109
|
+
request: response.request ?? {},
|
|
1110
|
+
displayText: response.display_text ?? "",
|
|
1111
|
+
expiresAt: response.expires_at ?? null,
|
|
1112
|
+
createdAt: response.created_at
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
function waitpointRespondBody(opts) {
|
|
1116
|
+
return {
|
|
1117
|
+
..."value" in opts ? { value: opts.value } : {},
|
|
690
1118
|
...opts.externalSubject === undefined ? {} : { external_subject: opts.externalSubject },
|
|
691
1119
|
...opts.metadata === undefined ? {} : { metadata: opts.metadata }
|
|
692
1120
|
};
|
|
693
1121
|
}
|
|
694
|
-
function
|
|
1122
|
+
function waitpointTokenRespondBody(token, opts) {
|
|
1123
|
+
return {
|
|
1124
|
+
token,
|
|
1125
|
+
...waitpointRespondBody(opts)
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
function resolveWaitpointTokenRespondArgs(id, token, opts) {
|
|
695
1129
|
if (typeof token !== "string" || opts === undefined) {
|
|
696
|
-
throw new Error("waitpoint token secret is required when
|
|
1130
|
+
throw new Error("waitpoint token secret is required when responding by token id");
|
|
697
1131
|
}
|
|
698
1132
|
return { id, token, opts };
|
|
699
1133
|
}
|
|
700
1134
|
function waitpointResponseTokenFromResponse(response) {
|
|
701
1135
|
return {
|
|
702
1136
|
id: response.id,
|
|
703
|
-
runId: response.run_id,
|
|
704
1137
|
waitpointId: response.waitpoint_id,
|
|
705
1138
|
url: response.url,
|
|
706
1139
|
token: response.token,
|
|
@@ -709,27 +1142,21 @@ function waitpointResponseTokenFromResponse(response) {
|
|
|
709
1142
|
}
|
|
710
1143
|
function resolveWaitpointArgs(target, waitpointIdOrOpts, opts) {
|
|
711
1144
|
if (isWaitpointRef(target)) {
|
|
712
|
-
const resolvedOpts = waitpointIdOrOpts !== undefined && typeof waitpointIdOrOpts !== "string" ? waitpointIdOrOpts : opts;
|
|
713
1145
|
return {
|
|
714
|
-
runId: target.runId,
|
|
715
1146
|
waitpointId: target.waitpointId,
|
|
716
|
-
opts:
|
|
1147
|
+
opts: waitpointIdOrOpts ?? opts ?? {}
|
|
717
1148
|
};
|
|
718
1149
|
}
|
|
719
|
-
if (typeof waitpointIdOrOpts !== "string") {
|
|
720
|
-
throw new Error("waitpoint id is required when resolving a waitpoint by run id");
|
|
721
|
-
}
|
|
722
1150
|
return {
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
opts: opts ?? {}
|
|
1151
|
+
waitpointId: target,
|
|
1152
|
+
opts: waitpointIdOrOpts ?? opts ?? {}
|
|
726
1153
|
};
|
|
727
1154
|
}
|
|
728
1155
|
function isWaitpointRef(value) {
|
|
729
1156
|
if (value === null || typeof value !== "object")
|
|
730
1157
|
return false;
|
|
731
1158
|
const record = value;
|
|
732
|
-
return typeof record["
|
|
1159
|
+
return typeof record["waitpointId"] === "string";
|
|
733
1160
|
}
|
|
734
1161
|
function retrieveOptions(signal) {
|
|
735
1162
|
return signal === undefined ? {} : { signal };
|
|
@@ -831,7 +1258,14 @@ function parseSseFrame(raw) {
|
|
|
831
1258
|
if (data === "") {
|
|
832
1259
|
return;
|
|
833
1260
|
}
|
|
834
|
-
|
|
1261
|
+
try {
|
|
1262
|
+
return runEventRecordToRunEvent(JSON.parse(data));
|
|
1263
|
+
} catch (error) {
|
|
1264
|
+
if (error instanceof SyntaxError) {
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
throw error;
|
|
1268
|
+
}
|
|
835
1269
|
}
|
|
836
1270
|
function findSseBoundary(buffer) {
|
|
837
1271
|
const lf = buffer.indexOf(`
|
|
@@ -847,121 +1281,102 @@ function findSseBoundary(buffer) {
|
|
|
847
1281
|
return Math.min(lf, crlf);
|
|
848
1282
|
}
|
|
849
1283
|
function runEventRecordToRunEvent(event) {
|
|
850
|
-
const
|
|
851
|
-
const
|
|
852
|
-
|
|
853
|
-
|
|
1284
|
+
const record = objectRecord(event);
|
|
1285
|
+
const message = stringValue(record?.["message"]);
|
|
1286
|
+
const at = stringValue(record?.["at"]);
|
|
1287
|
+
if (record === undefined || message === undefined || at === undefined) {
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
const attributes = objectRecord(record["attributes"]);
|
|
1291
|
+
const runId2 = stringValue(record["run_id"]) ?? stringValue(attributes?.["run_id"]) ?? "";
|
|
1292
|
+
if (message === "log.stdout" || message === "log.stderr") {
|
|
1293
|
+
const stream = message === "log.stdout" ? "stdout" : "stderr";
|
|
854
1294
|
return {
|
|
855
1295
|
type: "log",
|
|
856
1296
|
run_id: runId2,
|
|
857
1297
|
stream,
|
|
858
1298
|
bytes: numberValue(attributes?.["bytes"]) ?? 0,
|
|
859
1299
|
observed_seq: numberValue(attributes?.["observed_seq"]) ?? 0,
|
|
860
|
-
at
|
|
1300
|
+
at
|
|
861
1301
|
};
|
|
862
1302
|
}
|
|
863
|
-
if (
|
|
864
|
-
const message = stringValue(attributes?.["display_text"]);
|
|
1303
|
+
if (message === "waitpoint.requested") {
|
|
865
1304
|
const waitpointId = stringValue(attributes?.["waitpoint_id"]);
|
|
1305
|
+
const kind = stringValue(attributes?.["kind"]);
|
|
866
1306
|
if (waitpointId === undefined)
|
|
867
1307
|
return;
|
|
868
|
-
|
|
869
|
-
type: "approval_request",
|
|
870
|
-
run_id: runId2,
|
|
871
|
-
waitpoint_id: waitpointId,
|
|
872
|
-
message: message ?? "",
|
|
873
|
-
...optionalNumber("timeout", attributes?.["timeout"]),
|
|
874
|
-
at: event.at
|
|
875
|
-
};
|
|
876
|
-
}
|
|
877
|
-
if (event.message === "waitpoint.resolved" && stringValue(attributes?.["kind"]) === "approval") {
|
|
878
|
-
const waitpointId = stringValue(attributes?.["waitpoint_id"]);
|
|
879
|
-
const resolution = stringValue(attributes?.["resolution_kind"]);
|
|
880
|
-
if (waitpointId === undefined)
|
|
881
|
-
return;
|
|
882
|
-
if (resolution !== "approved" && resolution !== "denied")
|
|
883
|
-
return;
|
|
884
|
-
return {
|
|
885
|
-
type: "approval_decided",
|
|
886
|
-
run_id: runId2,
|
|
887
|
-
waitpoint_id: waitpointId,
|
|
888
|
-
decision: resolution,
|
|
889
|
-
...optionalString("reason", attributes?.["reason"]),
|
|
890
|
-
at: event.at
|
|
891
|
-
};
|
|
892
|
-
}
|
|
893
|
-
if (event.message === "waitpoint.requested" && stringValue(attributes?.["kind"]) === "message") {
|
|
894
|
-
const request = objectRecord(attributes?.["request"]);
|
|
895
|
-
const message = stringValue(attributes?.["display_text"]) ?? stringValue(request?.["prompt"]);
|
|
896
|
-
const waitpointId = stringValue(attributes?.["waitpoint_id"]);
|
|
897
|
-
if (waitpointId === undefined)
|
|
1308
|
+
if (kind === undefined)
|
|
898
1309
|
return;
|
|
899
1310
|
return {
|
|
900
|
-
type: "
|
|
1311
|
+
type: "waitpoint_request",
|
|
901
1312
|
run_id: runId2,
|
|
902
1313
|
waitpoint_id: waitpointId,
|
|
903
|
-
|
|
1314
|
+
kind,
|
|
1315
|
+
displayText: stringValue(attributes?.["display_text"]) ?? "",
|
|
1316
|
+
request: attributes?.["request"] ?? {},
|
|
904
1317
|
...optionalNumber("timeout", attributes?.["timeout"]),
|
|
905
|
-
at
|
|
1318
|
+
at
|
|
906
1319
|
};
|
|
907
1320
|
}
|
|
908
|
-
if (
|
|
909
|
-
const result = objectRecord(attributes?.["result"]);
|
|
910
|
-
const text = stringValue(result?.["text"]);
|
|
1321
|
+
if (message === "waitpoint.resolved") {
|
|
911
1322
|
const waitpointId = stringValue(attributes?.["waitpoint_id"]);
|
|
1323
|
+
const kind = stringValue(attributes?.["kind"]);
|
|
1324
|
+
const resolution = stringValue(attributes?.["resolution_kind"]);
|
|
912
1325
|
if (waitpointId === undefined)
|
|
913
1326
|
return;
|
|
914
|
-
if (
|
|
1327
|
+
if (kind === undefined || resolution === undefined)
|
|
915
1328
|
return;
|
|
916
1329
|
return {
|
|
917
|
-
type: "
|
|
1330
|
+
type: "waitpoint_resolved",
|
|
918
1331
|
run_id: runId2,
|
|
919
1332
|
waitpoint_id: waitpointId,
|
|
920
|
-
|
|
921
|
-
|
|
1333
|
+
kind,
|
|
1334
|
+
resolutionKind: resolution,
|
|
1335
|
+
value: attributes?.["result"],
|
|
1336
|
+
at
|
|
922
1337
|
};
|
|
923
1338
|
}
|
|
924
|
-
if (
|
|
1339
|
+
if (message.startsWith("emit.")) {
|
|
925
1340
|
return {
|
|
926
1341
|
type: "emit",
|
|
927
1342
|
run_id: runId2,
|
|
928
|
-
event_type: stringValue(attributes?.["type"]) ??
|
|
1343
|
+
event_type: stringValue(attributes?.["type"]) ?? message.slice("emit.".length),
|
|
929
1344
|
content: attributes?.["content"],
|
|
930
|
-
at
|
|
1345
|
+
at
|
|
931
1346
|
};
|
|
932
1347
|
}
|
|
933
|
-
if (
|
|
1348
|
+
if (message === "run.completed") {
|
|
934
1349
|
return {
|
|
935
|
-
type: "
|
|
1350
|
+
type: "task_result",
|
|
936
1351
|
run_id: runId2,
|
|
937
1352
|
exit_code: numberValue(attributes?.["exit_code"]) ?? 0,
|
|
938
|
-
at
|
|
1353
|
+
at
|
|
939
1354
|
};
|
|
940
1355
|
}
|
|
941
|
-
if (
|
|
1356
|
+
if (message === "run.failed") {
|
|
942
1357
|
return {
|
|
943
1358
|
type: "run_failed",
|
|
944
1359
|
run_id: runId2,
|
|
945
1360
|
failure_kind: stringValue(attributes?.["failure_kind"]) ?? "task_failed",
|
|
946
1361
|
detail: attributes?.["detail"],
|
|
947
|
-
at
|
|
1362
|
+
at
|
|
948
1363
|
};
|
|
949
1364
|
}
|
|
950
|
-
if (
|
|
1365
|
+
if (message === "run.cancelled") {
|
|
951
1366
|
return {
|
|
952
|
-
type: "
|
|
1367
|
+
type: "run_cancelled",
|
|
953
1368
|
run_id: runId2,
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
at: event.at
|
|
1369
|
+
...optionalString("reason", attributes?.["reason"]),
|
|
1370
|
+
at
|
|
957
1371
|
};
|
|
958
1372
|
}
|
|
959
|
-
if (
|
|
1373
|
+
if (message === "run.expired") {
|
|
960
1374
|
return {
|
|
961
|
-
type: "
|
|
1375
|
+
type: "run_expired",
|
|
962
1376
|
run_id: runId2,
|
|
963
|
-
...optionalString("
|
|
964
|
-
|
|
1377
|
+
...optionalString("ttl", attributes?.["ttl"]),
|
|
1378
|
+
...optionalString("message", attributes?.["message"]),
|
|
1379
|
+
at
|
|
965
1380
|
};
|
|
966
1381
|
}
|
|
967
1382
|
return;
|
|
@@ -1039,11 +1454,6 @@ function defineConfig(config) {
|
|
|
1039
1454
|
});
|
|
1040
1455
|
}
|
|
1041
1456
|
|
|
1042
|
-
// sdk/typescript/src/task.ts
|
|
1043
|
-
function task(config) {
|
|
1044
|
-
return markTask(config);
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
1457
|
// sdk/typescript/src/trigger.ts
|
|
1048
1458
|
var defaultClient;
|
|
1049
1459
|
function getDefaultClient() {
|
|
@@ -1051,10 +1461,102 @@ function getDefaultClient() {
|
|
|
1051
1461
|
return defaultClient;
|
|
1052
1462
|
}
|
|
1053
1463
|
var tasks = {
|
|
1054
|
-
trigger(
|
|
1055
|
-
return getDefaultClient().tasks.trigger(
|
|
1464
|
+
trigger(...args) {
|
|
1465
|
+
return getDefaultClient().tasks.trigger(...args);
|
|
1056
1466
|
}
|
|
1057
1467
|
};
|
|
1468
|
+
function triggerTask(task, ...args) {
|
|
1469
|
+
return getDefaultClient()[triggerTaskClientMethod](task, ...args);
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
// sdk/typescript/src/task.ts
|
|
1473
|
+
function task(config) {
|
|
1474
|
+
const marked = markTask(config);
|
|
1475
|
+
Object.defineProperty(marked, "trigger", {
|
|
1476
|
+
value: (...args) => triggerTask(marked, ...args)
|
|
1477
|
+
});
|
|
1478
|
+
return marked;
|
|
1479
|
+
}
|
|
1480
|
+
function queue(config) {
|
|
1481
|
+
return Object.freeze({ ...config });
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
// sdk/typescript/src/schedules.ts
|
|
1485
|
+
function task2(config) {
|
|
1486
|
+
const { cron, workspace, secretBindings, ...taskConfig } = config;
|
|
1487
|
+
const taskId = config.id;
|
|
1488
|
+
if (cron !== undefined && workspace === undefined) {
|
|
1489
|
+
throw new Error(`task ${JSON.stringify(taskId)} schedule workspace is required`);
|
|
1490
|
+
}
|
|
1491
|
+
const schedule = cron === undefined ? undefined : {
|
|
1492
|
+
cron: typeof cron === "string" ? cron : cron.pattern,
|
|
1493
|
+
...typeof cron === "string" || cron.timezone === undefined ? {} : { timezone: cron.timezone },
|
|
1494
|
+
workspace,
|
|
1495
|
+
...secretBindings === undefined ? {} : { secretBindings }
|
|
1496
|
+
};
|
|
1497
|
+
const marked = markScheduledTask({ ...taskConfig, payload: scheduledTaskPayloadSchema }, schedule);
|
|
1498
|
+
Object.defineProperty(marked, "trigger", {
|
|
1499
|
+
value: (...args) => triggerTask(marked, ...args)
|
|
1500
|
+
});
|
|
1501
|
+
return marked;
|
|
1502
|
+
}
|
|
1503
|
+
var scheduledTaskPayloadSchema = {
|
|
1504
|
+
"~standard": {
|
|
1505
|
+
version: 1,
|
|
1506
|
+
vendor: "helmr",
|
|
1507
|
+
validate(value) {
|
|
1508
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1509
|
+
return { issues: [{ message: "expected scheduled task payload object" }] };
|
|
1510
|
+
}
|
|
1511
|
+
const input = value;
|
|
1512
|
+
const timestamp = parseDateField(input["timestamp"], "timestamp");
|
|
1513
|
+
const lastTimestamp = parseOptionalDateField(input["lastTimestamp"], "lastTimestamp");
|
|
1514
|
+
const timezone = input["timezone"];
|
|
1515
|
+
const scheduleId = input["scheduleId"];
|
|
1516
|
+
const externalId = input["externalId"];
|
|
1517
|
+
const upcoming = input["upcoming"];
|
|
1518
|
+
const issues = [
|
|
1519
|
+
...timestamp.issues,
|
|
1520
|
+
...lastTimestamp.issues,
|
|
1521
|
+
...typeof timezone === "string" && timezone.trim() !== "" ? [] : [{ message: "expected string", path: ["timezone"] }],
|
|
1522
|
+
...typeof scheduleId === "string" && scheduleId.trim() !== "" ? [] : [{ message: "expected string", path: ["scheduleId"] }],
|
|
1523
|
+
...externalId === undefined || typeof externalId === "string" ? [] : [{ message: "expected string", path: ["externalId"] }],
|
|
1524
|
+
...Array.isArray(upcoming) ? [] : [{ message: "expected array", path: ["upcoming"] }]
|
|
1525
|
+
];
|
|
1526
|
+
const upcomingDates = Array.isArray(upcoming) ? upcoming.map((item, index) => parseDateField(item, `upcoming.${index}`)) : [];
|
|
1527
|
+
issues.push(...upcomingDates.flatMap((item) => item.issues));
|
|
1528
|
+
if (issues.length > 0) {
|
|
1529
|
+
return { issues };
|
|
1530
|
+
}
|
|
1531
|
+
return {
|
|
1532
|
+
value: {
|
|
1533
|
+
timestamp: timestamp.value,
|
|
1534
|
+
...lastTimestamp.value === undefined ? {} : { lastTimestamp: lastTimestamp.value },
|
|
1535
|
+
timezone,
|
|
1536
|
+
scheduleId,
|
|
1537
|
+
...externalId === undefined ? {} : { externalId },
|
|
1538
|
+
upcoming: upcomingDates.map((item) => item.value)
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
};
|
|
1544
|
+
function parseDateField(value, path) {
|
|
1545
|
+
if (typeof value !== "string") {
|
|
1546
|
+
return { value: new Date(0), issues: [{ message: "expected ISO timestamp", path: path.split(".") }] };
|
|
1547
|
+
}
|
|
1548
|
+
const date = new Date(value);
|
|
1549
|
+
if (Number.isNaN(date.getTime())) {
|
|
1550
|
+
return { value: new Date(0), issues: [{ message: "expected ISO timestamp", path: path.split(".") }] };
|
|
1551
|
+
}
|
|
1552
|
+
return { value: date, issues: [] };
|
|
1553
|
+
}
|
|
1554
|
+
function parseOptionalDateField(value, path) {
|
|
1555
|
+
if (value === undefined || value === null) {
|
|
1556
|
+
return { issues: [] };
|
|
1557
|
+
}
|
|
1558
|
+
return parseDateField(value, path);
|
|
1559
|
+
}
|
|
1058
1560
|
// sdk/typescript/src/index.ts
|
|
1059
1561
|
var image = (id) => new ImageBuilderImpl(id);
|
|
1060
1562
|
var cache = (id) => ({ id });
|
|
@@ -1097,6 +1599,16 @@ var waitpoints = new Proxy({}, {
|
|
|
1097
1599
|
return Reflect.get(getDefaultClient().waitpoints, property, receiver);
|
|
1098
1600
|
}
|
|
1099
1601
|
});
|
|
1602
|
+
var schedules = Object.freeze({
|
|
1603
|
+
task: task2,
|
|
1604
|
+
create: (...args) => getDefaultClient().schedules.create(...args),
|
|
1605
|
+
update: (...args) => getDefaultClient().schedules.update(...args),
|
|
1606
|
+
list: (...args) => getDefaultClient().schedules.list(...args),
|
|
1607
|
+
retrieve: (...args) => getDefaultClient().schedules.retrieve(...args),
|
|
1608
|
+
activate: (...args) => getDefaultClient().schedules.activate(...args),
|
|
1609
|
+
deactivate: (...args) => getDefaultClient().schedules.deactivate(...args),
|
|
1610
|
+
delete: (...args) => getDefaultClient().schedules.delete(...args)
|
|
1611
|
+
});
|
|
1100
1612
|
export {
|
|
1101
1613
|
workspace,
|
|
1102
1614
|
waitpoints,
|
|
@@ -1104,17 +1616,19 @@ export {
|
|
|
1104
1616
|
tasks,
|
|
1105
1617
|
task,
|
|
1106
1618
|
source,
|
|
1619
|
+
schedules,
|
|
1107
1620
|
sandbox,
|
|
1108
1621
|
runs,
|
|
1622
|
+
queue,
|
|
1109
1623
|
image,
|
|
1624
|
+
idempotencyKeys,
|
|
1110
1625
|
defineConfig,
|
|
1111
1626
|
cache,
|
|
1112
1627
|
UnsupportedTransportError,
|
|
1113
1628
|
TimeoutError,
|
|
1114
1629
|
RunNotFoundError,
|
|
1115
|
-
|
|
1630
|
+
PayloadSchemaValidationError,
|
|
1116
1631
|
HelmrClient,
|
|
1117
1632
|
ConcurrentWaitError,
|
|
1118
|
-
AuthError
|
|
1119
|
-
ApprovalTimeoutError
|
|
1633
|
+
AuthError
|
|
1120
1634
|
};
|