@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/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
- // sdk/typescript/src/internal.ts
65
- var approvalTimeoutErrorBrand = Symbol.for("helmr.sdk.ApprovalTimeoutError");
66
- var messageTimeoutErrorBrand = Symbol.for("helmr.sdk.MessageTimeoutError");
67
- var concurrentWaitErrorBrand = Symbol.for("helmr.sdk.ConcurrentWaitError");
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 ApprovalTimeoutError extends Error {
70
- constructor(message) {
71
- super(message);
72
- this.name = "ApprovalTimeoutError";
73
- Object.defineProperty(this, approvalTimeoutErrorBrand, { value: true });
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
- static [Symbol.hasInstance](value) {
76
- return this === ApprovalTimeoutError && typeof value === "object" && value !== null && approvalTimeoutErrorBrand in value;
160
+ }
161
+ function validateQueueName(value) {
162
+ if (!isValidQueueName(value)) {
163
+ throw new TaskQueueNameError(value);
77
164
  }
78
165
  }
79
-
80
- class MessageTimeoutError extends Error {
81
- constructor(message) {
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
- static [Symbol.hasInstance](value) {
87
- return this === MessageTimeoutError && typeof value === "object" && value !== null && messageTimeoutErrorBrand in value;
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: "message",
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 (task, opts) => {
449
- const runWorkspace = runWorkspaceFromSpec(opts.workspace);
450
- const maxDurationSeconds = readOptionalMaxDurationSeconds(task.maxDuration);
451
- const response = await this.#fetch("/api/runs", {
452
- method: "POST",
453
- body: JSON.stringify({
454
- task_id: task.id,
455
- secrets: opts.secrets ?? {},
456
- payload: opts.payload,
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
- approve: async (target, waitpointIdOrOpts, opts = {}) => {
523
- const resolved = resolveWaitpointArgs(target, waitpointIdOrOpts, opts);
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(approvalBody(resolved.opts)),
831
+ body: JSON.stringify(waitpointCreateBody(opts)),
527
832
  headers: { "content-type": "application/json" }
528
833
  });
834
+ return waitpointFromResponse(response);
529
835
  },
530
- deny: async (target, waitpointIdOrOpts, opts = {}) => {
836
+ respond: async (target, waitpointIdOrOpts, opts = {}) => {
531
837
  const resolved = resolveWaitpointArgs(target, waitpointIdOrOpts, opts);
532
- await this.#fetch(`/api/runs/${encodeURIComponent(resolved.runId)}/waitpoints/${encodeURIComponent(resolved.waitpointId)}/deny`, {
838
+ await this.#fetch(`/api/waitpoints/${encodeURIComponent(resolved.waitpointId)}/respond`, {
533
839
  method: "POST",
534
- body: JSON.stringify(approvalBody(resolved.opts)),
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.runId, resolved.waitpointId, resolved.opts)),
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
- complete: async (target, tokenOrOpts, maybeOpts) => {
557
- const resolved = typeof target === "string" ? resolveWaitpointTokenCompleteArgs(target, tokenOrOpts, maybeOpts) : { id: target.id, token: target.token, opts: tokenOrOpts };
558
- await this.#fetch(`/api/waitpoints/tokens/${encodeURIComponent(resolved.id)}/complete`, {
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(waitpointTokenCompleteBody(resolved.token, resolved.opts)),
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.pending_wait),
1021
+ pendingWaitpoint: pendingWaitpointFromResponse(response.id, response.pending_waitpoint),
668
1022
  ..."output" in response ? { output: response.output } : {}
669
1023
  });
670
1024
  }
671
- function approvalBody(opts) {
672
- return opts.reason === undefined ? {} : { reason: opts.reason };
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 waitpointTokenCreateBody(runId2, waitpointId, opts) {
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 waitpointTokenCompleteBody(token, opts) {
1090
+ function waitpointCreateBody(opts) {
685
1091
  return {
686
- token,
687
- action: opts.action,
688
- ..."reason" in opts && opts.reason === undefined ? {} : ("reason" in opts) ? { reason: opts.reason } : {},
689
- ..."text" in opts ? { text: opts.text } : {},
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 resolveWaitpointTokenCompleteArgs(id, token, opts) {
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 completing by token id");
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: resolvedOpts ?? {}
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
- runId: target,
724
- waitpointId: waitpointIdOrOpts,
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["runId"] === "string" && typeof record["waitpointId"] === "string";
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
- return runEventRecordToRunEvent(JSON.parse(data));
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 attributes = objectRecord(event.attributes);
851
- const runId2 = event.run_id ?? stringValue(attributes?.["run_id"]) ?? "";
852
- if (event.message === "log.stdout" || event.message === "log.stderr") {
853
- const stream = event.message === "log.stdout" ? "stdout" : "stderr";
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: event.at
1300
+ at
861
1301
  };
862
1302
  }
863
- if (event.message === "waitpoint.requested" && stringValue(attributes?.["kind"]) === "approval") {
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
- return {
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: "message_request",
1311
+ type: "waitpoint_request",
901
1312
  run_id: runId2,
902
1313
  waitpoint_id: waitpointId,
903
- ...optionalString("prompt", message),
1314
+ kind,
1315
+ displayText: stringValue(attributes?.["display_text"]) ?? "",
1316
+ request: attributes?.["request"] ?? {},
904
1317
  ...optionalNumber("timeout", attributes?.["timeout"]),
905
- at: event.at
1318
+ at
906
1319
  };
907
1320
  }
908
- if (event.message === "waitpoint.resolved" && stringValue(attributes?.["kind"]) === "message") {
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 (stringValue(attributes?.["resolution_kind"]) !== "replied")
1327
+ if (kind === undefined || resolution === undefined)
915
1328
  return;
916
1329
  return {
917
- type: "message_received",
1330
+ type: "waitpoint_resolved",
918
1331
  run_id: runId2,
919
1332
  waitpoint_id: waitpointId,
920
- text: text ?? "",
921
- at: event.at
1333
+ kind,
1334
+ resolutionKind: resolution,
1335
+ value: attributes?.["result"],
1336
+ at
922
1337
  };
923
1338
  }
924
- if (event.message.startsWith("emit.")) {
1339
+ if (message.startsWith("emit.")) {
925
1340
  return {
926
1341
  type: "emit",
927
1342
  run_id: runId2,
928
- event_type: stringValue(attributes?.["type"]) ?? event.message.slice("emit.".length),
1343
+ event_type: stringValue(attributes?.["type"]) ?? message.slice("emit.".length),
929
1344
  content: attributes?.["content"],
930
- at: event.at
1345
+ at
931
1346
  };
932
1347
  }
933
- if (event.message === "run.completed") {
1348
+ if (message === "run.completed") {
934
1349
  return {
935
- type: "task_complete",
1350
+ type: "task_result",
936
1351
  run_id: runId2,
937
1352
  exit_code: numberValue(attributes?.["exit_code"]) ?? 0,
938
- at: event.at
1353
+ at
939
1354
  };
940
1355
  }
941
- if (event.message === "run.failed") {
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: event.at
1362
+ at
948
1363
  };
949
1364
  }
950
- if (event.message === "run.timeout") {
1365
+ if (message === "run.cancelled") {
951
1366
  return {
952
- type: "run_timeout",
1367
+ type: "run_cancelled",
953
1368
  run_id: runId2,
954
- elapsed_secs: numberValue(attributes?.["elapsed_active_secs"]) ?? 0,
955
- limit_secs: numberValue(attributes?.["limit_secs"]) ?? 0,
956
- at: event.at
1369
+ ...optionalString("reason", attributes?.["reason"]),
1370
+ at
957
1371
  };
958
1372
  }
959
- if (event.message === "run.cancelled") {
1373
+ if (message === "run.expired") {
960
1374
  return {
961
- type: "run_cancelled",
1375
+ type: "run_expired",
962
1376
  run_id: runId2,
963
- ...optionalString("reason", attributes?.["reason"]),
964
- at: event.at
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(task2, opts) {
1055
- return getDefaultClient().tasks.trigger(task2, opts);
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
- MessageTimeoutError,
1630
+ PayloadSchemaValidationError,
1116
1631
  HelmrClient,
1117
1632
  ConcurrentWaitError,
1118
- AuthError,
1119
- ApprovalTimeoutError
1633
+ AuthError
1120
1634
  };