@beignet/cli 0.0.43 → 0.0.45

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 (81) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +61 -8
  3. package/dist/choices.d.ts +4 -0
  4. package/dist/choices.d.ts.map +1 -1
  5. package/dist/choices.js +9 -0
  6. package/dist/choices.js.map +1 -1
  7. package/dist/db.d.ts.map +1 -1
  8. package/dist/db.js +8 -4
  9. package/dist/db.js.map +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +13 -13
  12. package/dist/index.js.map +1 -1
  13. package/dist/inspect.js +78 -21
  14. package/dist/inspect.js.map +1 -1
  15. package/dist/lib.d.ts +4 -1
  16. package/dist/lib.d.ts.map +1 -1
  17. package/dist/lib.js +1 -1
  18. package/dist/lib.js.map +1 -1
  19. package/dist/mcp.d.ts +2 -1
  20. package/dist/mcp.d.ts.map +1 -1
  21. package/dist/mcp.js +354 -3
  22. package/dist/mcp.js.map +1 -1
  23. package/dist/operational-lifecycle.d.ts +12 -0
  24. package/dist/operational-lifecycle.d.ts.map +1 -0
  25. package/dist/operational-lifecycle.js +39 -0
  26. package/dist/operational-lifecycle.js.map +1 -0
  27. package/dist/operational-path.d.ts +3 -0
  28. package/dist/operational-path.d.ts.map +1 -0
  29. package/dist/operational-path.js +26 -0
  30. package/dist/operational-path.js.map +1 -0
  31. package/dist/operational-process.d.ts +78 -0
  32. package/dist/operational-process.d.ts.map +1 -0
  33. package/dist/operational-process.js +228 -0
  34. package/dist/operational-process.js.map +1 -0
  35. package/dist/operational-runner.d.ts +2 -0
  36. package/dist/operational-runner.d.ts.map +1 -0
  37. package/dist/operational-runner.js +168 -0
  38. package/dist/operational-runner.js.map +1 -0
  39. package/dist/outbox.d.ts +14 -6
  40. package/dist/outbox.d.ts.map +1 -1
  41. package/dist/outbox.js +140 -124
  42. package/dist/outbox.js.map +1 -1
  43. package/dist/schedule.d.ts +2 -1
  44. package/dist/schedule.d.ts.map +1 -1
  45. package/dist/schedule.js +79 -81
  46. package/dist/schedule.js.map +1 -1
  47. package/dist/task.d.ts +2 -1
  48. package/dist/task.d.ts.map +1 -1
  49. package/dist/task.js +57 -61
  50. package/dist/task.js.map +1 -1
  51. package/dist/templates/agents.d.ts.map +1 -1
  52. package/dist/templates/agents.js +21 -8
  53. package/dist/templates/agents.js.map +1 -1
  54. package/dist/templates/base.d.ts.map +1 -1
  55. package/dist/templates/base.js +2 -0
  56. package/dist/templates/base.js.map +1 -1
  57. package/dist/templates/server.d.ts.map +1 -1
  58. package/dist/templates/server.js +9 -2
  59. package/dist/templates/server.js.map +1 -1
  60. package/dist/templates/shadcn.d.ts.map +1 -1
  61. package/dist/templates/shadcn.js +3 -1
  62. package/dist/templates/shadcn.js.map +1 -1
  63. package/package.json +2 -2
  64. package/skills/app-structure/SKILL.md +19 -6
  65. package/src/choices.ts +19 -0
  66. package/src/db.ts +8 -4
  67. package/src/index.ts +19 -16
  68. package/src/inspect.ts +114 -26
  69. package/src/lib.ts +27 -1
  70. package/src/mcp.ts +457 -2
  71. package/src/operational-lifecycle.ts +56 -0
  72. package/src/operational-path.ts +35 -0
  73. package/src/operational-process.ts +454 -0
  74. package/src/operational-runner.ts +221 -0
  75. package/src/outbox.ts +154 -128
  76. package/src/schedule.ts +84 -89
  77. package/src/task.ts +58 -62
  78. package/src/templates/agents.ts +21 -8
  79. package/src/templates/base.ts +2 -0
  80. package/src/templates/server.ts +9 -2
  81. package/src/templates/shadcn.ts +3 -1
package/src/outbox.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { readFile, stat } from "node:fs/promises";
1
+ import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import type { ErrorReporterPort } from "@beignet/core/error-reporting";
4
4
  import type {
@@ -16,6 +16,8 @@ import {
16
16
  flushOperationalErrorReporter,
17
17
  reportOperationalFailure,
18
18
  } from "./operational-error-reporting.js";
19
+ import { runWithOperationalCleanup } from "./operational-lifecycle.js";
20
+ import { resolveAppOperationalModulePath } from "./operational-path.js";
19
21
 
20
22
  /**
21
23
  * Options for running one bounded outbox drain pass.
@@ -126,10 +128,19 @@ export type RunOutboxRequeueResult = {
126
128
  durationMs: number;
127
129
  };
128
130
 
129
- /**
130
- * Result returned by destructive outbox admin commands.
131
- */
132
- export type RunOutboxDeleteResult = {
131
+ /** Result returned by `beignet outbox purge`. */
132
+ export type RunOutboxPurgeResult = {
133
+ schemaVersion: 1;
134
+ cwd: string;
135
+ modulePath: string;
136
+ dryRun: boolean;
137
+ matched: number;
138
+ deleted: number;
139
+ durationMs: number;
140
+ };
141
+
142
+ /** Result returned by `beignet outbox prune`. */
143
+ export type RunOutboxPruneResult = {
133
144
  schemaVersion: 1;
134
145
  cwd: string;
135
146
  modulePath: string;
@@ -222,115 +233,122 @@ export async function runOutboxDrain(
222
233
  : undefined;
223
234
  const ctx = assertOutboxDrainContext(rawContext, modulePath);
224
235
 
225
- try {
226
- // Loaded lazily so CLI commands that never touch the app runtime, such as
227
- // `beignet lint`, work without a built @beignet/core.
228
- const { drainOutbox } = await import("@beignet/core/outbox");
229
- const result = await drainOutbox({
230
- outbox: ctx.ports.outbox,
231
- registry,
232
- eventBus: ctx.ports.eventBus,
233
- jobs: ctx.ports.jobs,
234
- batchSize,
235
- instrumentation: ctx.ports.instrumentation ?? ctx.ports.devtools,
236
- instrumentationContext: {
237
- requestId: ctx.requestId,
238
- traceId: ctx.traceId,
239
- },
240
- onError(error, message) {
241
- ctx.ports.logger?.error("Outbox delivery failed", {
242
- error,
243
- outboxMessageId: message.id,
244
- kind: message.kind,
245
- name: message.name,
246
- attempts: message.attempts,
247
- });
248
- },
249
- onDeadLetter(error, message) {
250
- return reportOperationalFailure({
251
- ctx,
252
- error,
253
- reportOptions: {
254
- level: "error",
255
- mechanism: "beignet.outbox.cli",
256
- handled: false,
257
- tags: {
258
- "beignet.kind": "outbox",
259
- "beignet.outbox.kind": message.kind,
260
- "beignet.outbox.name": message.name,
261
- "beignet.outbox.outcome": "deadLettered",
262
- },
263
- contexts: {
264
- outbox: {
265
- messageId: message.id,
266
- kind: message.kind,
267
- name: message.name,
268
- attempts: message.attempts,
269
- maxAttempts: message.maxAttempts,
270
- outcome: "deadLettered",
236
+ return runWithOperationalCleanup({
237
+ operation: "Outbox drain",
238
+ cleanupLabel: "stopOutboxDrainContext",
239
+ run: async () => {
240
+ try {
241
+ // Loaded lazily so CLI commands that never touch the app runtime, such
242
+ // as `beignet lint`, work without a built @beignet/core.
243
+ const { drainOutbox } = await import("@beignet/core/outbox");
244
+ const result = await drainOutbox({
245
+ outbox: ctx.ports.outbox,
246
+ registry,
247
+ eventBus: ctx.ports.eventBus,
248
+ jobs: ctx.ports.jobs,
249
+ batchSize,
250
+ instrumentation: ctx.ports.instrumentation ?? ctx.ports.devtools,
251
+ instrumentationContext: {
252
+ requestId: ctx.requestId,
253
+ traceId: ctx.traceId,
254
+ },
255
+ onError(error, message) {
256
+ ctx.ports.logger?.error("Outbox delivery failed", {
257
+ error,
258
+ outboxMessageId: message.id,
259
+ kind: message.kind,
260
+ name: message.name,
261
+ attempts: message.attempts,
262
+ });
263
+ },
264
+ onDeadLetter(error, message) {
265
+ return reportOperationalFailure({
266
+ ctx,
267
+ error,
268
+ reportOptions: {
269
+ level: "error",
270
+ mechanism: "beignet.outbox.cli",
271
+ handled: false,
272
+ tags: {
273
+ "beignet.kind": "outbox",
274
+ "beignet.outbox.kind": message.kind,
275
+ "beignet.outbox.name": message.name,
276
+ "beignet.outbox.outcome": "deadLettered",
277
+ },
278
+ contexts: {
279
+ outbox: {
280
+ messageId: message.id,
281
+ kind: message.kind,
282
+ name: message.name,
283
+ attempts: message.attempts,
284
+ maxAttempts: message.maxAttempts,
285
+ outcome: "deadLettered",
286
+ },
287
+ },
271
288
  },
272
- },
289
+ });
290
+ },
291
+ onSettlementError(settlementError, message) {
292
+ return reportOperationalFailure({
293
+ ctx,
294
+ error: settlementError,
295
+ reportOptions: {
296
+ level: "error",
297
+ mechanism: "beignet.outbox.cli",
298
+ handled: false,
299
+ tags: {
300
+ "beignet.kind": "outbox",
301
+ "beignet.outbox.kind": message.kind,
302
+ "beignet.outbox.name": message.name,
303
+ "beignet.outbox.outcome": "settlementFailed",
304
+ },
305
+ contexts: {
306
+ outbox: {
307
+ messageId: message.id,
308
+ kind: message.kind,
309
+ name: message.name,
310
+ attempts: message.attempts,
311
+ maxAttempts: message.maxAttempts,
312
+ outcome: "settlementFailed",
313
+ },
314
+ },
315
+ },
316
+ });
273
317
  },
274
318
  });
275
- },
276
- onSettlementError(settlementError, message) {
277
- return reportOperationalFailure({
319
+
320
+ await recordOutboxDrain(ctx, result);
321
+
322
+ return {
323
+ schemaVersion: 1,
324
+ cwd,
325
+ modulePath,
326
+ result,
327
+ durationMs: Math.round(performance.now() - startedAt),
328
+ };
329
+ } catch (error) {
330
+ await reportOperationalFailure({
278
331
  ctx,
279
- error: settlementError,
332
+ error,
280
333
  reportOptions: {
281
334
  level: "error",
282
335
  mechanism: "beignet.outbox.cli",
283
336
  handled: false,
284
337
  tags: {
285
338
  "beignet.kind": "outbox",
286
- "beignet.outbox.kind": message.kind,
287
- "beignet.outbox.name": message.name,
288
- "beignet.outbox.outcome": "settlementFailed",
289
- },
290
- contexts: {
291
- outbox: {
292
- messageId: message.id,
293
- kind: message.kind,
294
- name: message.name,
295
- attempts: message.attempts,
296
- maxAttempts: message.maxAttempts,
297
- outcome: "settlementFailed",
298
- },
339
+ "beignet.outbox.outcome": "drainFailed",
299
340
  },
341
+ contexts: { outbox: { outcome: "drainFailed" } },
300
342
  },
301
343
  });
302
- },
303
- });
304
-
305
- await recordOutboxDrain(ctx, result);
306
-
307
- return {
308
- schemaVersion: 1,
309
- cwd,
310
- modulePath,
311
- result,
312
- durationMs: Math.round(performance.now() - startedAt),
313
- };
314
- } catch (error) {
315
- await reportOperationalFailure({
316
- ctx,
317
- error,
318
- reportOptions: {
319
- level: "error",
320
- mechanism: "beignet.outbox.cli",
321
- handled: false,
322
- tags: {
323
- "beignet.kind": "outbox",
324
- "beignet.outbox.outcome": "drainFailed",
325
- },
326
- contexts: { outbox: { outcome: "drainFailed" } },
327
- },
328
- });
329
- throw error;
330
- } finally {
331
- await flushOperationalErrorReporter(ctx);
332
- await outboxModule.stopOutboxDrainContext?.(ctx, contextArgs);
333
- }
344
+ throw error;
345
+ }
346
+ },
347
+ cleanup: async () => {
348
+ await flushOperationalErrorReporter(ctx);
349
+ await outboxModule.stopOutboxDrainContext?.(ctx, contextArgs);
350
+ },
351
+ });
334
352
  }
335
353
 
336
354
  /**
@@ -403,7 +421,7 @@ export async function runOutboxRequeue(
403
421
  */
404
422
  export async function runOutboxPurge(
405
423
  options: RunOutboxPurgeOptions = {},
406
- ): Promise<RunOutboxDeleteResult> {
424
+ ): Promise<RunOutboxPurgeResult> {
407
425
  if (!options.before && !options.all) {
408
426
  throw new Error(
409
427
  "Pass --before <date> to purge old dead-lettered messages, or pass --all to purge every dead-lettered message.",
@@ -437,7 +455,7 @@ export async function runOutboxPurge(
437
455
  */
438
456
  export async function runOutboxPrune(
439
457
  options: RunOutboxPruneOptions,
440
- ): Promise<RunOutboxDeleteResult> {
458
+ ): Promise<RunOutboxPruneResult> {
441
459
  return withOutboxAdminContext(options, "prune", async ({ admin, base }) => {
442
460
  const query = {
443
461
  status: "delivered" as const,
@@ -482,30 +500,37 @@ async function withOutboxAdminContext<T extends object>(
482
500
  ? await outboxModule.createOutboxAdminContext(contextArgs)
483
501
  : await createFallbackOutboxAdminContext(outboxModule);
484
502
  const ctx = assertOutboxAdminContext(rawContext, modulePath);
485
- const admin = resolveOutboxAdminPort(ctx, modulePath);
486
503
 
487
- try {
488
- const result = await run({
489
- admin,
490
- base: {
491
- schemaVersion: 1,
492
- cwd,
493
- modulePath,
494
- },
495
- });
496
- return {
497
- ...result,
498
- durationMs: Math.round(performance.now() - startedAt),
499
- };
500
- } finally {
501
- if (outboxModule.stopOutboxAdminContext) {
502
- await outboxModule.stopOutboxAdminContext(ctx, contextArgs);
503
- } else {
504
- await outboxModule.stopOutboxDrainContext?.(ctx, {
505
- registry: maybeOutboxRegistry(outboxModule.outboxRegistry),
504
+ return runWithOperationalCleanup({
505
+ operation: `Outbox ${operation}`,
506
+ cleanupLabel: outboxModule.stopOutboxAdminContext
507
+ ? "stopOutboxAdminContext"
508
+ : "stopOutboxDrainContext",
509
+ run: async () => {
510
+ const admin = resolveOutboxAdminPort(ctx, modulePath);
511
+ const result = await run({
512
+ admin,
513
+ base: {
514
+ schemaVersion: 1,
515
+ cwd,
516
+ modulePath,
517
+ },
506
518
  });
507
- }
508
- }
519
+ return {
520
+ ...result,
521
+ durationMs: Math.round(performance.now() - startedAt),
522
+ };
523
+ },
524
+ cleanup: async () => {
525
+ if (outboxModule.stopOutboxAdminContext) {
526
+ await outboxModule.stopOutboxAdminContext(ctx, contextArgs);
527
+ } else {
528
+ await outboxModule.stopOutboxDrainContext?.(ctx, {
529
+ registry: maybeOutboxRegistry(outboxModule.outboxRegistry),
530
+ });
531
+ }
532
+ },
533
+ });
509
534
  }
510
535
 
511
536
  async function createFallbackOutboxAdminContext(
@@ -521,10 +546,11 @@ async function loadOutboxModule(
521
546
  cwd: string,
522
547
  modulePath: string,
523
548
  ): Promise<OutboxModule> {
524
- const absolutePath = path.join(cwd, modulePath);
549
+ let absolutePath: string;
525
550
  try {
526
- await stat(absolutePath);
527
- } catch {
551
+ absolutePath = await resolveAppOperationalModulePath(cwd, modulePath);
552
+ } catch (error) {
553
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
528
554
  throw new Error(
529
555
  `Could not find app outbox registry at ${modulePath}. Create server/outbox.ts or configure paths.outbox in beignet.config.ts.`,
530
556
  );
package/src/schedule.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { readFile, stat } from "node:fs/promises";
1
+ import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import type { ErrorReporterPort } from "@beignet/core/error-reporting";
4
4
  import type { ProviderInstrumentationPort } from "@beignet/core/providers";
@@ -13,6 +13,8 @@ import {
13
13
  flushOperationalErrorReporter,
14
14
  reportOperationalFailure,
15
15
  } from "./operational-error-reporting.js";
16
+ import { runWithOperationalCleanup } from "./operational-lifecycle.js";
17
+ import { resolveAppOperationalModulePath } from "./operational-path.js";
16
18
 
17
19
  /**
18
20
  * Options for running one app-owned schedule.
@@ -21,7 +23,8 @@ export type RunAppScheduleOptions = {
21
23
  name: string;
22
24
  cwd?: string;
23
25
  modulePath?: string;
24
- payload?: string | Record<string, unknown>;
26
+ /** Pre-parsed payload validated by the registered schedule schema. */
27
+ payload?: unknown;
25
28
  id?: string;
26
29
  attempt?: number;
27
30
  scheduledAt?: string;
@@ -106,91 +109,97 @@ export async function runAppSchedule(
106
109
  : {};
107
110
  const scheduleContext = toScheduleContext(ctx);
108
111
 
109
- try {
110
- // Loaded lazily so CLI commands that never touch the app runtime, such as
111
- // `beignet lint`, work without a built @beignet/core.
112
- const { createInlineScheduleRunner } = await import(
113
- "@beignet/core/schedules"
114
- );
115
- const runner = createInlineScheduleRunner<unknown>({
116
- ctx,
117
- instrumentation: scheduleContext.ports,
118
- instrumentationContext: {
119
- requestId: scheduleContext.requestId,
120
- traceId: scheduleContext.traceId,
121
- },
122
- onError({ error }) {
123
- scheduleContext.ports?.logger?.error("Schedule failed", {
124
- error,
125
- scheduleName: schedule.name,
126
- });
127
- },
128
- onHookError({ error, hook }) {
129
- scheduleContext.ports?.logger?.warn?.(
130
- "Schedule lifecycle hook failed",
131
- {
112
+ return runWithOperationalCleanup({
113
+ operation: `Schedule "${schedule.name}"`,
114
+ cleanupLabel: "stopScheduleContext",
115
+ run: async () => {
116
+ // Loaded lazily so CLI commands that never touch the app runtime, such as
117
+ // `beignet lint`, work without a built @beignet/core.
118
+ const { createInlineScheduleRunner } = await import(
119
+ "@beignet/core/schedules"
120
+ );
121
+ const runner = createInlineScheduleRunner<unknown>({
122
+ ctx,
123
+ instrumentation: scheduleContext.ports,
124
+ instrumentationContext: {
125
+ requestId: scheduleContext.requestId,
126
+ traceId: scheduleContext.traceId,
127
+ },
128
+ onError({ error }) {
129
+ scheduleContext.ports?.logger?.error("Schedule failed", {
132
130
  error,
133
- hook,
134
131
  scheduleName: schedule.name,
135
- },
136
- );
137
- },
138
- });
139
-
140
- try {
141
- await runner.run(schedule, runOptions);
142
- } catch (error) {
143
- await reportOperationalFailure({
144
- ctx: scheduleContext,
145
- error,
146
- reportOptions: {
147
- level: "error",
148
- mechanism: "beignet.schedule.cli",
149
- handled: false,
150
- tags: {
151
- "beignet.kind": "schedule",
152
- "beignet.schedule": schedule.name,
153
- },
154
- contexts: {
155
- schedule: {
156
- name: schedule.name,
157
- source: runOptions.source ?? "beignet-cli",
158
- attempt: runOptions.attempt ?? null,
132
+ });
133
+ },
134
+ onHookError({ error, hook }) {
135
+ scheduleContext.ports?.logger?.warn?.(
136
+ "Schedule lifecycle hook failed",
137
+ {
138
+ error,
139
+ hook,
140
+ scheduleName: schedule.name,
159
141
  },
160
- },
142
+ );
161
143
  },
162
144
  });
163
- throw error;
164
- }
165
145
 
166
- return {
167
- schemaVersion: 1,
168
- name: schedule.name,
169
- cwd,
170
- modulePath,
171
- run: {
172
- id: runOptions.id,
173
- attempt: runOptions.attempt,
174
- scheduledAt: stringifyDateInput(runOptions.scheduledAt),
175
- triggeredAt: stringifyDateInput(runOptions.triggeredAt),
176
- source: runOptions.source ?? "beignet-cli",
177
- },
178
- durationMs: Math.round(performance.now() - startedAt),
179
- };
180
- } finally {
181
- await flushOperationalErrorReporter(scheduleContext);
182
- await scheduleModule.stopScheduleContext?.(ctx, contextArgs);
183
- }
146
+ try {
147
+ await runner.run(schedule, runOptions);
148
+ } catch (error) {
149
+ await reportOperationalFailure({
150
+ ctx: scheduleContext,
151
+ error,
152
+ reportOptions: {
153
+ level: "error",
154
+ mechanism: "beignet.schedule.cli",
155
+ handled: false,
156
+ tags: {
157
+ "beignet.kind": "schedule",
158
+ "beignet.schedule": schedule.name,
159
+ },
160
+ contexts: {
161
+ schedule: {
162
+ name: schedule.name,
163
+ source: runOptions.source ?? "beignet-cli",
164
+ attempt: runOptions.attempt ?? null,
165
+ },
166
+ },
167
+ },
168
+ });
169
+ throw error;
170
+ }
171
+
172
+ return {
173
+ schemaVersion: 1,
174
+ name: schedule.name,
175
+ cwd,
176
+ modulePath,
177
+ run: {
178
+ id: runOptions.id,
179
+ attempt: runOptions.attempt,
180
+ scheduledAt: stringifyDateInput(runOptions.scheduledAt),
181
+ triggeredAt: stringifyDateInput(runOptions.triggeredAt),
182
+ source: runOptions.source ?? "beignet-cli",
183
+ },
184
+ durationMs: Math.round(performance.now() - startedAt),
185
+ };
186
+ },
187
+ cleanup: async () => {
188
+ await flushOperationalErrorReporter(scheduleContext);
189
+ await scheduleModule.stopScheduleContext?.(ctx, contextArgs);
190
+ },
191
+ });
184
192
  }
185
193
 
186
194
  async function loadScheduleModule(
187
195
  cwd: string,
188
196
  modulePath: string,
189
197
  ): Promise<ScheduleModule> {
190
- const absolutePath = path.join(cwd, modulePath);
198
+ let absolutePath: string;
191
199
  try {
192
- await stat(absolutePath);
193
- } catch {
200
+ absolutePath = await resolveAppOperationalModulePath(cwd, modulePath);
201
+ } catch (error) {
202
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
194
203
  throw new Error(
195
204
  `Could not find app schedule registry at ${modulePath}. Create server/schedules.ts or configure paths.schedules in beignet.config.ts.`,
196
205
  );
@@ -213,7 +222,7 @@ function buildScheduleRunOptions(
213
222
  };
214
223
 
215
224
  if (options.payload !== undefined) {
216
- runOptions.payload = parseSchedulePayloadFlag(options.payload);
225
+ runOptions.payload = options.payload;
217
226
  }
218
227
  if (options.id !== undefined) runOptions.id = options.id;
219
228
  if (options.attempt !== undefined) runOptions.attempt = options.attempt;
@@ -223,20 +232,6 @@ function buildScheduleRunOptions(
223
232
  return runOptions;
224
233
  }
225
234
 
226
- function parseSchedulePayloadFlag(
227
- input: NonNullable<RunAppScheduleOptions["payload"]>,
228
- ): unknown {
229
- if (typeof input !== "string") return input;
230
-
231
- try {
232
- return JSON.parse(input) as unknown;
233
- } catch (error) {
234
- throw new Error(
235
- `Invalid --payload JSON: ${error instanceof Error ? error.message : String(error)}`,
236
- );
237
- }
238
- }
239
-
240
235
  function findSchedule(
241
236
  schedules: readonly unknown[] | undefined,
242
237
  name: string,