@opengeni/api-router 0.5.3 → 0.5.4

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.
Files changed (48) hide show
  1. package/dist/app.js +1 -1
  2. package/dist/{chunk-3HIA43CC.js → chunk-DO2G3JSB.js} +5184 -2195
  3. package/dist/chunk-DO2G3JSB.js.map +1 -0
  4. package/dist/index.d.ts +2 -1
  5. package/dist/index.js +297 -54
  6. package/dist/index.js.map +1 -1
  7. package/package.json +20 -20
  8. package/src/app.ts +415 -147
  9. package/src/auth/managed-auth.ts +32 -16
  10. package/src/http/auth.ts +8 -1
  11. package/src/http/common.ts +6 -2
  12. package/src/http/sse.ts +27 -6
  13. package/src/index.ts +196 -74
  14. package/src/integrations/oauth-client.ts +403 -120
  15. package/src/integrations/provider-domain.ts +4 -1
  16. package/src/mcp/documents.ts +173 -94
  17. package/src/mcp/server.ts +1517 -692
  18. package/src/mcp/session-view.ts +8 -2
  19. package/src/mcp/toolspace.ts +175 -84
  20. package/src/observability.ts +7 -1
  21. package/src/routes/api-keys.ts +39 -23
  22. package/src/routes/billing.ts +180 -65
  23. package/src/routes/capabilities.ts +17 -8
  24. package/src/routes/catalog-assets.ts +5 -2
  25. package/src/routes/codex.ts +244 -63
  26. package/src/routes/connections.ts +71 -33
  27. package/src/routes/documents.ts +242 -92
  28. package/src/routes/enrollments.ts +100 -70
  29. package/src/routes/environments.ts +205 -136
  30. package/src/routes/files.ts +164 -39
  31. package/src/routes/github.ts +123 -50
  32. package/src/routes/install.ts +9 -2
  33. package/src/routes/machines.ts +9 -8
  34. package/src/routes/packs.ts +141 -89
  35. package/src/routes/rigs.ts +189 -0
  36. package/src/routes/scheduled-tasks.ts +51 -9
  37. package/src/routes/sessions.ts +839 -329
  38. package/src/routes/social.ts +50 -38
  39. package/src/routes/workspace-capture.ts +238 -0
  40. package/src/routes/workspaces.ts +159 -13
  41. package/src/sandbox/access.ts +11 -3
  42. package/src/sandbox/auth-callout.ts +5 -1
  43. package/src/sandbox/channel-a.ts +104 -27
  44. package/src/sandbox/enrollment.ts +13 -3
  45. package/src/sandbox/machines.ts +68 -59
  46. package/src/sandbox/metrics-ingestion.ts +238 -17
  47. package/src/sandbox/viewer.ts +172 -46
  48. package/dist/chunk-3HIA43CC.js.map +0 -1
@@ -27,12 +27,14 @@ export function createManagedAuth(settings: Settings, db: Database): ManagedAuth
27
27
  trustedOrigins: betterAuthTrustedOrigins(settings),
28
28
  advanced: {
29
29
  useSecureCookies: settings.publicBaseUrl?.startsWith("https://") ?? false,
30
- ...(settings.betterAuthCookieDomain ? {
31
- crossSubDomainCookies: {
32
- enabled: true,
33
- domain: settings.betterAuthCookieDomain,
34
- },
35
- } : {}),
30
+ ...(settings.betterAuthCookieDomain
31
+ ? {
32
+ crossSubDomainCookies: {
33
+ enabled: true,
34
+ domain: settings.betterAuthCookieDomain,
35
+ },
36
+ }
37
+ : {}),
36
38
  database: {
37
39
  generateId: () => crypto.randomUUID(),
38
40
  },
@@ -149,7 +151,11 @@ export function createManagedAuth(settings: Settings, db: Database): ManagedAuth
149
151
  }) as ManagedAuth;
150
152
  }
151
153
 
152
- export async function managedSessionAccessContext(auth: ManagedAuth, db: Database, headers: Headers) {
154
+ export async function managedSessionAccessContext(
155
+ auth: ManagedAuth,
156
+ db: Database,
157
+ headers: Headers,
158
+ ) {
153
159
  const session = await auth.api.getSession({ headers });
154
160
  if (!session?.user) {
155
161
  return null;
@@ -184,15 +190,20 @@ function betterAuthTrustedOrigins(settings: Settings): string[] {
184
190
  return [...origins];
185
191
  }
186
192
 
187
- async function sendEmail(settings: Settings, input: {
188
- to: string;
189
- subject: string;
190
- text: string;
191
- html: string;
192
- }): Promise<void> {
193
+ async function sendEmail(
194
+ settings: Settings,
195
+ input: {
196
+ to: string;
197
+ subject: string;
198
+ text: string;
199
+ html: string;
200
+ },
201
+ ): Promise<void> {
193
202
  if (!settings.resendApiKey) {
194
203
  if (settings.environment === "local" || settings.environment === "test") {
195
- console.warn(`[opengeni] Skipping email to ${input.to}: OPENGENI_RESEND_API_KEY is not configured`);
204
+ console.warn(
205
+ `[opengeni] Skipping email to ${input.to}: OPENGENI_RESEND_API_KEY is not configured`,
206
+ );
196
207
  return;
197
208
  }
198
209
  throw new Error("OPENGENI_RESEND_API_KEY is required to send managed auth email");
@@ -212,7 +223,9 @@ async function sendEmail(settings: Settings, input: {
212
223
 
213
224
  async function verificationUrl(settings: Settings, email: string): Promise<string> {
214
225
  if (!settings.betterAuthSecret) {
215
- throw new Error("OPENGENI_BETTER_AUTH_SECRET is required to send managed auth verification email");
226
+ throw new Error(
227
+ "OPENGENI_BETTER_AUTH_SECRET is required to send managed auth verification email",
228
+ );
216
229
  }
217
230
  if (!settings.publicBaseUrl) {
218
231
  throw new Error("OPENGENI_PUBLIC_BASE_URL is required to send managed auth verification email");
@@ -225,7 +238,10 @@ async function verificationUrl(settings: Settings, email: string): Promise<strin
225
238
  }
226
239
 
227
240
  function splitCsv(raw: string): string[] {
228
- return raw.split(",").map((value) => value.trim()).filter(Boolean);
241
+ return raw
242
+ .split(",")
243
+ .map((value) => value.trim())
244
+ .filter(Boolean);
229
245
  }
230
246
 
231
247
  function escapeHtml(value: string): string {
package/src/http/auth.ts CHANGED
@@ -84,7 +84,14 @@ function isAuthorized(c: Context, expected: string | undefined): boolean {
84
84
  return false;
85
85
  }
86
86
  const explicit = c.req.header("x-opengeni-access-key");
87
- return constantTimeEqual(explicit, expected);
87
+ if (constantTimeEqual(explicit, expected)) {
88
+ return true;
89
+ }
90
+ const authorization = c.req.header("authorization");
91
+ const bearer = authorization?.startsWith("Bearer ")
92
+ ? authorization.slice("Bearer ".length)
93
+ : undefined;
94
+ return constantTimeEqual(bearer, expected);
88
95
  }
89
96
 
90
97
  function constantTimeEqual(actual: string | undefined, expected: string): boolean {
@@ -9,8 +9,12 @@ export function boundedLimit(raw: string | undefined): number {
9
9
  return Math.min(500, Math.max(1, Math.floor(limit)));
10
10
  }
11
11
 
12
- export async function assertSessionExists(db: Database, workspaceId: string, sessionId: string): Promise<void> {
13
- if (!await getSession(db, workspaceId, sessionId)) {
12
+ export async function assertSessionExists(
13
+ db: Database,
14
+ workspaceId: string,
15
+ sessionId: string,
16
+ ): Promise<void> {
17
+ if (!(await getSession(db, workspaceId, sessionId))) {
14
18
  throw new HTTPException(404, { message: "session not found" });
15
19
  }
16
20
  }
package/src/http/sse.ts CHANGED
@@ -2,7 +2,14 @@ import type { SessionEvent } from "@opengeni/contracts";
2
2
  import { listSessionEvents, type Database } from "@opengeni/db";
3
3
  import { formatSse, type EventBus } from "@opengeni/events";
4
4
 
5
- export async function sseSessionStream(db: Database, bus: EventBus, workspaceId: string, sessionId: string, after: number, signal: AbortSignal): Promise<Response> {
5
+ export async function sseSessionStream(
6
+ db: Database,
7
+ bus: EventBus,
8
+ workspaceId: string,
9
+ sessionId: string,
10
+ after: number,
11
+ signal: AbortSignal,
12
+ ): Promise<Response> {
6
13
  const encoder = new TextEncoder();
7
14
  let controller: ReadableStreamDefaultController<Uint8Array>;
8
15
  let lastSent = after;
@@ -18,7 +25,13 @@ export async function sseSessionStream(db: Database, bus: EventBus, workspaceId:
18
25
  return;
19
26
  }
20
27
  if (event.sequence > lastSent + 1) {
21
- const missing = await listSessionEvents(db, workspaceId, sessionId, lastSent, event.sequence - lastSent - 1);
28
+ const missing = await listSessionEvents(
29
+ db,
30
+ workspaceId,
31
+ sessionId,
32
+ lastSent,
33
+ event.sequence - lastSent - 1,
34
+ );
22
35
  for (const missed of missing) {
23
36
  if (missed.sequence > lastSent) {
24
37
  controller.enqueue(encoder.encode(formatSse(missed)));
@@ -40,7 +53,11 @@ export async function sseSessionStream(db: Database, bus: EventBus, workspaceId:
40
53
  }
41
54
  });
42
55
 
43
- await replaySessionEvents((cursor, limit) => listSessionEvents(db, workspaceId, sessionId, cursor, limit), send, after);
56
+ await replaySessionEvents(
57
+ (cursor, limit) => listSessionEvents(db, workspaceId, sessionId, cursor, limit),
58
+ send,
59
+ after,
60
+ );
44
61
  replaying = false;
45
62
  for (const event of buffered.sort((a, b) => a.sequence - b.sequence)) {
46
63
  await send(event);
@@ -53,9 +70,13 @@ export async function sseSessionStream(db: Database, bus: EventBus, workspaceId:
53
70
  },
54
71
  });
55
72
 
56
- signal.addEventListener("abort", () => {
57
- unsubscribe?.();
58
- }, { once: true });
73
+ signal.addEventListener(
74
+ "abort",
75
+ () => {
76
+ unsubscribe?.();
77
+ },
78
+ { once: true },
79
+ );
59
80
 
60
81
  return new Response(stream, {
61
82
  headers: {
package/src/index.ts CHANGED
@@ -1,9 +1,26 @@
1
- import { dbSearchPath, getSettings, resolveNatsCalloutConfig, resolveNatsControlPlaneAuth, retryStartupDependency, startupRetryOptions } from "@opengeni/config";
2
- import type { ScheduledTask, ScheduledTaskOverlapPolicy, ScheduledTaskScheduleSpec } from "@opengeni/contracts";
3
- import { createDb } from "@opengeni/db";
1
+ import {
2
+ dbSearchPath,
3
+ getSettings,
4
+ resolveNatsCalloutConfig,
5
+ resolveNatsControlPlaneAuth,
6
+ retryStartupDependency,
7
+ startupRetryOptions,
8
+ } from "@opengeni/config";
9
+ import type {
10
+ ScheduledTask,
11
+ ScheduledTaskOverlapPolicy,
12
+ ScheduledTaskScheduleSpec,
13
+ } from "@opengeni/contracts";
14
+ import { createDb, markSessionWorkflowWakeDelivered, type Database } from "@opengeni/db";
4
15
  import { createNatsEventBus, type ResponderConnection } from "@opengeni/events";
5
16
  import { createObservability, logStartupDependencyRetry } from "@opengeni/observability";
6
- import { Connection, Client as TemporalClient, ScheduleNotFoundError, ScheduleOverlapPolicy, WorkflowExecutionAlreadyStartedError } from "@temporalio/client";
17
+ import {
18
+ Connection,
19
+ Client as TemporalClient,
20
+ ScheduleNotFoundError,
21
+ ScheduleOverlapPolicy,
22
+ WorkflowExecutionAlreadyStartedError,
23
+ } from "@temporalio/client";
7
24
  import type { ScheduleOptions, ScheduleSpec, ScheduleUpdateOptions } from "@temporalio/client";
8
25
  import { createApp, type DocumentIndexClient, type SessionWorkflowClient } from "./app";
9
26
  import { observabilityEventLogger } from "./observability";
@@ -35,7 +52,10 @@ const TEMPORAL_MONTHS = [
35
52
  "DECEMBER",
36
53
  ] as const;
37
54
 
38
- export async function createTemporalWorkflowClient(settings: ReturnType<typeof getSettings>): Promise<{
55
+ export async function createTemporalWorkflowClient(
56
+ settings: ReturnType<typeof getSettings>,
57
+ db: Database,
58
+ ): Promise<{
39
59
  client: SessionWorkflowClient;
40
60
  documentIndexer: DocumentIndexClient;
41
61
  close: () => Promise<void>;
@@ -49,7 +69,13 @@ export async function createTemporalWorkflowClient(settings: ReturnType<typeof g
49
69
  signalUserMessage: async ({ eventId, workflowId }) => {
50
70
  await temporal.workflow.getHandle(workflowId).signal("userMessage", eventId);
51
71
  },
52
- wakeSessionWorkflow: async ({ accountId, workspaceId, sessionId, workflowId }) => {
72
+ wakeSessionWorkflow: async ({
73
+ accountId,
74
+ workspaceId,
75
+ sessionId,
76
+ workflowId,
77
+ wakeRevision,
78
+ }) => {
53
79
  await temporal.workflow.signalWithStart("sessionWorkflow", {
54
80
  taskQueue: settings.temporalTaskQueue,
55
81
  workflowId,
@@ -57,26 +83,87 @@ export async function createTemporalWorkflowClient(settings: ReturnType<typeof g
57
83
  args: [{ accountId, workspaceId, sessionId }],
58
84
  signal: "queueChanged",
59
85
  });
86
+ await markSessionWorkflowWakeDelivered(db, {
87
+ accountId,
88
+ workspaceId,
89
+ sessionId,
90
+ temporalWorkflowId: workflowId,
91
+ wakeRevision,
92
+ });
93
+ },
94
+ signalCodexCapacity: async ({
95
+ accountId,
96
+ workspaceId,
97
+ sessionId,
98
+ workflowId,
99
+ wakeRevision,
100
+ workflowWakeRevision,
101
+ }) => {
102
+ await temporal.workflow.signalWithStart("sessionWorkflow", {
103
+ taskQueue: settings.temporalTaskQueue,
104
+ workflowId,
105
+ workflowIdReusePolicy: "ALLOW_DUPLICATE",
106
+ args: [{ accountId, workspaceId, sessionId }],
107
+ signal: "codexCapacityChanged",
108
+ signalArgs: [wakeRevision],
109
+ });
110
+ await markSessionWorkflowWakeDelivered(db, {
111
+ accountId,
112
+ workspaceId,
113
+ sessionId,
114
+ temporalWorkflowId: workflowId,
115
+ wakeRevision: workflowWakeRevision,
116
+ });
60
117
  },
61
- signalApprovalDecision: async ({ eventId, workflowId }) => {
62
- await temporal.workflow.getHandle(workflowId).signal("approvalDecision", eventId);
118
+ signalApprovalDecision: async ({
119
+ accountId,
120
+ workspaceId,
121
+ sessionId,
122
+ eventId,
123
+ workflowId,
124
+ workflowWakeRevision,
125
+ }) => {
126
+ await temporal.workflow.signalWithStart("sessionWorkflow", {
127
+ taskQueue: settings.temporalTaskQueue,
128
+ workflowId,
129
+ workflowIdReusePolicy: "ALLOW_DUPLICATE",
130
+ args: [{ accountId, workspaceId, sessionId }],
131
+ signal: "approvalDecision",
132
+ signalArgs: [eventId],
133
+ });
134
+ await markSessionWorkflowWakeDelivered(db, {
135
+ accountId,
136
+ workspaceId,
137
+ sessionId,
138
+ temporalWorkflowId: workflowId,
139
+ wakeRevision: workflowWakeRevision,
140
+ });
63
141
  },
64
- signalInterrupt: async ({ accountId, workspaceId, sessionId, eventId, workflowId }) => {
65
- // Start-or-signal: an interrupt POSTed while the session is idle has no
66
- // running workflow execution to signal, and getHandle().signal() would
67
- // throw WorkflowNotFoundError -> a 500 (the operator-can't-stop bug). Like
68
- // wakeSessionWorkflow, signalWithStart delivers the signal to a live run
69
- // when one exists and otherwise starts a fresh sessionWorkflow that picks
70
- // the buffered `interrupt` up immediately. ALLOW_DUPLICATE matches the
71
- // wake path so a running execution is reused rather than rejected.
142
+ signalSessionControl: async ({
143
+ accountId,
144
+ workspaceId,
145
+ sessionId,
146
+ eventId,
147
+ workflowId,
148
+ workflowWakeRevision,
149
+ }) => {
150
+ // Start-or-signal: a control sent after the prior workflow returned idle
151
+ // starts a fresh run with the durable control already buffered.
72
152
  await temporal.workflow.signalWithStart("sessionWorkflow", {
73
153
  taskQueue: settings.temporalTaskQueue,
74
154
  workflowId,
75
155
  workflowIdReusePolicy: "ALLOW_DUPLICATE",
76
156
  args: [{ accountId, workspaceId, sessionId }],
77
- signal: "interrupt",
157
+ signal: "sessionControl",
78
158
  signalArgs: [eventId],
79
159
  });
160
+ await markSessionWorkflowWakeDelivered(db, {
161
+ accountId,
162
+ workspaceId,
163
+ sessionId,
164
+ temporalWorkflowId: workflowId,
165
+ wakeRevision: workflowWakeRevision,
166
+ });
80
167
  },
81
168
  syncScheduledTask: async ({ task }) => {
82
169
  const schedule = temporal.schedule.getHandle(task.temporalScheduleId);
@@ -91,36 +178,60 @@ export async function createTemporalWorkflowClient(settings: ReturnType<typeof g
91
178
  }
92
179
  },
93
180
  deleteScheduledTaskSchedule: async ({ temporalScheduleId }) => {
94
- await temporal.schedule.getHandle(temporalScheduleId).delete().catch(() => undefined);
181
+ await temporal.schedule
182
+ .getHandle(temporalScheduleId)
183
+ .delete()
184
+ .catch(() => undefined);
95
185
  },
96
186
  triggerScheduledTask: async ({ task, agentRunUsageIdempotencyKey, triggerWorkflowId }) => {
97
- // Deterministic workflowId (derived from the trigger token by the
98
- // caller) + REJECT_DUPLICATE makes a retried manual trigger idempotent:
99
- // the second start collides on the id and is rejected instead of
100
- // spawning a second run. The shared idempotency key dedupes the charge.
101
- const workflowId = triggerWorkflowId ?? `scheduled-task-${task.id}-manual-${crypto.randomUUID()}`;
102
- try {
103
- await temporal.workflow.start("scheduledTaskFireWorkflow", {
104
- taskQueue: settings.temporalTaskQueue,
105
- workflowId,
106
- workflowIdReusePolicy: "REJECT_DUPLICATE",
107
- args: [{
108
- accountId: task.accountId,
109
- workspaceId: task.workspaceId,
110
- taskId: task.id,
111
- triggerType: "manual",
112
- agentRunUsageIdempotencyKey,
113
- }],
114
- });
115
- } catch (error) {
116
- // A duplicate trigger token started this run already; treat the retry
117
- // as a no-op so the (idempotent) usage charge stays the only effect.
118
- if (isWorkflowAlreadyStarted(error)) {
119
- return;
120
- }
187
+ // Deterministic workflowId (derived from the trigger token by the
188
+ // caller) + REJECT_DUPLICATE makes a retried manual trigger idempotent:
189
+ // the second start collides on the id and is rejected instead of
190
+ // spawning a second run. The shared idempotency key dedupes the charge.
191
+ const workflowId =
192
+ triggerWorkflowId ?? `scheduled-task-${task.id}-manual-${crypto.randomUUID()}`;
193
+ try {
194
+ await temporal.workflow.start("scheduledTaskFireWorkflow", {
195
+ taskQueue: settings.temporalTaskQueue,
196
+ workflowId,
197
+ workflowIdReusePolicy: "REJECT_DUPLICATE",
198
+ args: [
199
+ {
200
+ accountId: task.accountId,
201
+ workspaceId: task.workspaceId,
202
+ taskId: task.id,
203
+ triggerType: "manual",
204
+ agentRunUsageIdempotencyKey,
205
+ },
206
+ ],
207
+ });
208
+ } catch (error) {
209
+ // A duplicate trigger token started this run already; treat the retry
210
+ // as a no-op so the (idempotent) usage charge stays the only effect.
211
+ if (isWorkflowAlreadyStarted(error)) {
212
+ return;
213
+ }
121
214
  throw error;
122
215
  }
123
216
  },
217
+ startRigVerification: async ({ workspaceId, changeId, versionId, workflowId }) => {
218
+ const targetId = changeId ?? versionId;
219
+ if (!targetId) {
220
+ throw new Error("rig verification requires changeId or versionId");
221
+ }
222
+ await temporal.workflow.start("rigVerificationWorkflow", {
223
+ taskQueue: settings.temporalTaskQueue,
224
+ workflowId: workflowId ?? `rig-verification-${targetId}-${crypto.randomUUID()}`,
225
+ workflowIdReusePolicy: "ALLOW_DUPLICATE",
226
+ args: [
227
+ {
228
+ workspaceId,
229
+ ...(changeId ? { changeId } : {}),
230
+ ...(versionId ? { versionId } : {}),
231
+ },
232
+ ],
233
+ });
234
+ },
124
235
  check: async () => {
125
236
  await connection.workflowService.getSystemInfo({});
126
237
  },
@@ -158,7 +269,8 @@ export async function startApi() {
158
269
  let bus: Awaited<ReturnType<typeof createNatsEventBus>> | undefined;
159
270
  let workflowClient: Awaited<ReturnType<typeof createTemporalWorkflowClient>> | undefined;
160
271
  const retryOptions = startupRetryOptions(settings);
161
- const onRetry = (event: Parameters<typeof logStartupDependencyRetry>[1]) => logStartupDependencyRetry(observability, event);
272
+ const onRetry = (event: Parameters<typeof logStartupDependencyRetry>[1]) =>
273
+ logStartupDependencyRetry(observability, event);
162
274
  // The PRIVILEGED control-plane NATS login (M-AUTH): when the server runs with
163
275
  // auth_callout, api/worker authenticate as a static account user permitted to
164
276
  // request `agent.*.rpc`. Null in local dev (anonymous connect — the bus default).
@@ -169,7 +281,9 @@ export async function startApi() {
169
281
  () =>
170
282
  createNatsEventBus(
171
283
  settings.natsUrl,
172
- controlPlaneAuth ? { user: controlPlaneAuth.user, pass: controlPlaneAuth.password } : undefined,
284
+ controlPlaneAuth
285
+ ? { user: controlPlaneAuth.user, pass: controlPlaneAuth.password }
286
+ : undefined,
173
287
  { logger: observabilityEventLogger(observability) },
174
288
  ),
175
289
  {
@@ -177,16 +291,16 @@ export async function startApi() {
177
291
  onRetry,
178
292
  },
179
293
  );
180
- workflowClient = await retryStartupDependency("Temporal", () => createTemporalWorkflowClient(settings), {
181
- ...retryOptions,
182
- onRetry,
183
- });
294
+ workflowClient = await retryStartupDependency(
295
+ "Temporal",
296
+ () => createTemporalWorkflowClient(settings, dbClient.db),
297
+ {
298
+ ...retryOptions,
299
+ onRetry,
300
+ },
301
+ );
184
302
  } catch (error) {
185
- await Promise.allSettled([
186
- bus?.close(),
187
- workflowClient?.close(),
188
- dbClient.close(),
189
- ]);
303
+ await Promise.allSettled([bus?.close(), workflowClient?.close(), dbClient.close()]);
190
304
  throw error;
191
305
  }
192
306
  if (!bus || !workflowClient) {
@@ -295,25 +409,29 @@ export function temporalScheduleSpec(schedule: ScheduledTaskScheduleSpec): Sched
295
409
  }
296
410
  if (schedule.type === "calendar") {
297
411
  return {
298
- calendars: [{
299
- hour: schedule.hour,
300
- minute: schedule.minute,
301
- second: 0,
302
- ...(schedule.daysOfWeek ? { dayOfWeek: schedule.daysOfWeek } : {}),
303
- }],
412
+ calendars: [
413
+ {
414
+ hour: schedule.hour,
415
+ minute: schedule.minute,
416
+ second: 0,
417
+ ...(schedule.daysOfWeek ? { dayOfWeek: schedule.daysOfWeek } : {}),
418
+ },
419
+ ],
304
420
  timezone: schedule.timeZone,
305
421
  };
306
422
  }
307
423
  const runAt = new Date(schedule.runAt);
308
424
  return {
309
- calendars: [{
310
- year: runAt.getUTCFullYear(),
311
- month: temporalMonth(runAt.getUTCMonth()),
312
- dayOfMonth: runAt.getUTCDate(),
313
- hour: runAt.getUTCHours(),
314
- minute: runAt.getUTCMinutes(),
315
- second: runAt.getUTCSeconds(),
316
- }],
425
+ calendars: [
426
+ {
427
+ year: runAt.getUTCFullYear(),
428
+ month: temporalMonth(runAt.getUTCMonth()),
429
+ dayOfMonth: runAt.getUTCDate(),
430
+ hour: runAt.getUTCHours(),
431
+ minute: runAt.getUTCMinutes(),
432
+ second: runAt.getUTCSeconds(),
433
+ },
434
+ ],
317
435
  timezone: "UTC",
318
436
  };
319
437
  }
@@ -330,12 +448,14 @@ function temporalScheduleOptions(task: ScheduledTask, taskQueue: string): Schedu
330
448
  type: "startWorkflow",
331
449
  workflowType: "scheduledTaskFireWorkflow",
332
450
  taskQueue,
333
- args: [{
334
- accountId: task.accountId,
335
- workspaceId: task.workspaceId,
336
- taskId: task.id,
337
- triggerType: "scheduled",
338
- }],
451
+ args: [
452
+ {
453
+ accountId: task.accountId,
454
+ workspaceId: task.workspaceId,
455
+ taskId: task.id,
456
+ triggerType: "scheduled",
457
+ },
458
+ ],
339
459
  },
340
460
  policies: {
341
461
  overlap: temporalOverlapPolicy(task.overlapPolicy),
@@ -362,6 +482,8 @@ function temporalScheduleUpdateOptions(options: ScheduleOptions): ScheduleUpdate
362
482
  ...(options.policies ? { policies: options.policies } : {}),
363
483
  state: options.state ?? {},
364
484
  ...(options.searchAttributes ? { searchAttributes: options.searchAttributes } : {}),
365
- ...(options.typedSearchAttributes ? { typedSearchAttributes: options.typedSearchAttributes } : {}),
485
+ ...(options.typedSearchAttributes
486
+ ? { typedSearchAttributes: options.typedSearchAttributes }
487
+ : {}),
366
488
  };
367
489
  }