opencode-gateway 0.1.0 → 0.1.1

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 (44) hide show
  1. package/dist/cli/doctor.js +3 -1
  2. package/dist/cli/init.js +4 -1
  3. package/dist/cli/paths.js +1 -1
  4. package/dist/cli.js +13 -4
  5. package/dist/config/gateway.d.ts +1 -0
  6. package/dist/config/gateway.js +2 -1
  7. package/dist/config/paths.d.ts +2 -0
  8. package/dist/config/paths.js +5 -1
  9. package/dist/cron/runtime.d.ts +24 -5
  10. package/dist/cron/runtime.js +178 -13
  11. package/dist/delivery/text.js +1 -1
  12. package/dist/gateway.js +41 -35
  13. package/dist/index.js +9 -5
  14. package/dist/opencode/adapter.d.ts +2 -0
  15. package/dist/opencode/adapter.js +56 -7
  16. package/dist/runtime/conversation-coordinator.d.ts +4 -0
  17. package/dist/runtime/conversation-coordinator.js +22 -0
  18. package/dist/runtime/executor.d.ts +33 -5
  19. package/dist/runtime/executor.js +229 -22
  20. package/dist/runtime/runtime-singleton.d.ts +2 -0
  21. package/dist/runtime/runtime-singleton.js +28 -0
  22. package/dist/session/context.js +6 -0
  23. package/dist/store/migrations.js +15 -1
  24. package/dist/store/sqlite.d.ts +19 -2
  25. package/dist/store/sqlite.js +81 -4
  26. package/dist/tools/channel-target.d.ts +5 -0
  27. package/dist/tools/channel-target.js +6 -0
  28. package/dist/tools/cron-run.js +1 -1
  29. package/dist/tools/cron-upsert.d.ts +2 -1
  30. package/dist/tools/cron-upsert.js +20 -6
  31. package/dist/tools/{cron-remove.d.ts → schedule-cancel.d.ts} +1 -1
  32. package/dist/tools/schedule-cancel.js +12 -0
  33. package/dist/tools/schedule-format.d.ts +4 -0
  34. package/dist/tools/schedule-format.js +48 -0
  35. package/dist/tools/{cron-list.d.ts → schedule-list.d.ts} +1 -1
  36. package/dist/tools/schedule-list.js +17 -0
  37. package/dist/tools/schedule-once.d.ts +4 -0
  38. package/dist/tools/schedule-once.js +43 -0
  39. package/dist/tools/schedule-status.d.ts +3 -0
  40. package/dist/tools/schedule-status.js +23 -0
  41. package/generated/wasm/pkg/opencode_gateway_ffi_bg.wasm +0 -0
  42. package/package.json +4 -4
  43. package/dist/tools/cron-list.js +0 -34
  44. package/dist/tools/cron-remove.js +0 -12
@@ -315,11 +315,16 @@ export class SqliteStore {
315
315
  upsertCronJob(input) {
316
316
  assertSafeInteger(input.nextRunAtMs, "cron next_run_at_ms");
317
317
  assertSafeInteger(input.recordedAtMs, "cron recordedAtMs");
318
+ if (input.runAtMs !== null) {
319
+ assertSafeInteger(input.runAtMs, "cron run_at_ms");
320
+ }
318
321
  this.db
319
322
  .query(`
320
323
  INSERT INTO cron_jobs (
321
324
  id,
325
+ kind,
322
326
  schedule,
327
+ run_at_ms,
323
328
  prompt,
324
329
  delivery_channel,
325
330
  delivery_target,
@@ -329,9 +334,11 @@ export class SqliteStore {
329
334
  created_at_ms,
330
335
  updated_at_ms
331
336
  )
332
- VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?9)
337
+ VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?11)
333
338
  ON CONFLICT(id) DO UPDATE SET
339
+ kind = excluded.kind,
334
340
  schedule = excluded.schedule,
341
+ run_at_ms = excluded.run_at_ms,
335
342
  prompt = excluded.prompt,
336
343
  delivery_channel = excluded.delivery_channel,
337
344
  delivery_target = excluded.delivery_target,
@@ -340,14 +347,16 @@ export class SqliteStore {
340
347
  next_run_at_ms = excluded.next_run_at_ms,
341
348
  updated_at_ms = excluded.updated_at_ms;
342
349
  `)
343
- .run(input.id, input.schedule, input.prompt, input.deliveryChannel, input.deliveryTarget, input.deliveryTopic, input.enabled ? 1 : 0, input.nextRunAtMs, input.recordedAtMs);
350
+ .run(input.id, input.kind, encodeStoredSchedule(input.kind, input.schedule), input.runAtMs, input.prompt, input.deliveryChannel, input.deliveryTarget, input.deliveryTopic, input.enabled ? 1 : 0, input.nextRunAtMs, input.recordedAtMs);
344
351
  }
345
352
  getCronJob(id) {
346
353
  const row = this.db
347
354
  .query(`
348
355
  SELECT
349
356
  id,
357
+ kind,
350
358
  schedule,
359
+ run_at_ms,
351
360
  prompt,
352
361
  delivery_channel,
353
362
  delivery_target,
@@ -367,7 +376,9 @@ export class SqliteStore {
367
376
  .query(`
368
377
  SELECT
369
378
  id,
379
+ kind,
370
380
  schedule,
381
+ run_at_ms,
371
382
  prompt,
372
383
  delivery_channel,
373
384
  delivery_target,
@@ -388,7 +399,9 @@ export class SqliteStore {
388
399
  .query(`
389
400
  SELECT
390
401
  id,
402
+ kind,
391
403
  schedule,
404
+ run_at_ms,
392
405
  prompt,
393
406
  delivery_channel,
394
407
  delivery_target,
@@ -398,7 +411,7 @@ export class SqliteStore {
398
411
  created_at_ms,
399
412
  updated_at_ms
400
413
  FROM cron_jobs
401
- WHERE enabled = 1 AND next_run_at_ms <= ?1
414
+ WHERE kind = 'cron' AND enabled = 1 AND next_run_at_ms <= ?1
402
415
  ORDER BY next_run_at_ms ASC, id ASC;
403
416
  `)
404
417
  .all(nowMs);
@@ -411,7 +424,9 @@ export class SqliteStore {
411
424
  .query(`
412
425
  SELECT
413
426
  id,
427
+ kind,
414
428
  schedule,
429
+ run_at_ms,
415
430
  prompt,
416
431
  delivery_channel,
417
432
  delivery_target,
@@ -443,6 +458,47 @@ export class SqliteStore {
443
458
  `)
444
459
  .run(id, nextRunAtMs, recordedAtMs);
445
460
  }
461
+ setCronJobEnabled(id, enabled, recordedAtMs) {
462
+ assertSafeInteger(recordedAtMs, "cron recordedAtMs");
463
+ this.db
464
+ .query(`
465
+ UPDATE cron_jobs
466
+ SET enabled = ?2,
467
+ updated_at_ms = ?3
468
+ WHERE id = ?1;
469
+ `)
470
+ .run(id, enabled ? 1 : 0, recordedAtMs);
471
+ }
472
+ listCronRuns(jobId, limit) {
473
+ assertSafeInteger(limit, "cron run limit");
474
+ const rows = this.db
475
+ .query(`
476
+ SELECT
477
+ id,
478
+ job_id,
479
+ scheduled_for_ms,
480
+ started_at_ms,
481
+ finished_at_ms,
482
+ status,
483
+ response_text,
484
+ error_message
485
+ FROM cron_runs
486
+ WHERE job_id = ?1
487
+ ORDER BY started_at_ms DESC, id DESC
488
+ LIMIT ?2;
489
+ `)
490
+ .all(jobId, limit);
491
+ return rows.map((row) => ({
492
+ id: row.id,
493
+ jobId: row.job_id,
494
+ scheduledForMs: row.scheduled_for_ms,
495
+ startedAtMs: row.started_at_ms,
496
+ finishedAtMs: row.finished_at_ms,
497
+ status: row.status,
498
+ responseText: row.response_text,
499
+ errorMessage: row.error_message,
500
+ }));
501
+ }
446
502
  insertCronRun(jobId, scheduledForMs, startedAtMs) {
447
503
  assertSafeInteger(scheduledForMs, "cron scheduled_for_ms");
448
504
  assertSafeInteger(startedAtMs, "cron started_at_ms");
@@ -496,9 +552,12 @@ export class SqliteStore {
496
552
  }
497
553
  }
498
554
  function mapCronJobRow(row) {
555
+ const kind = parseScheduleJobKind(row.kind);
499
556
  return {
500
557
  id: row.id,
501
- schedule: row.schedule,
558
+ kind,
559
+ schedule: kind === "cron" ? row.schedule : null,
560
+ runAtMs: row.run_at_ms,
502
561
  prompt: row.prompt,
503
562
  deliveryChannel: row.delivery_channel,
504
563
  deliveryTarget: row.delivery_target,
@@ -509,6 +568,24 @@ function mapCronJobRow(row) {
509
568
  updatedAtMs: row.updated_at_ms,
510
569
  };
511
570
  }
571
+ function parseScheduleJobKind(value) {
572
+ switch (value) {
573
+ case "cron":
574
+ case "once":
575
+ return value;
576
+ default:
577
+ throw new Error(`stored schedule job kind is invalid: ${value}`);
578
+ }
579
+ }
580
+ function encodeStoredSchedule(kind, schedule) {
581
+ if (kind === "once") {
582
+ return "@once";
583
+ }
584
+ if (schedule === null) {
585
+ throw new Error("cron schedule must not be null");
586
+ }
587
+ return schedule;
588
+ }
512
589
  function mapMailboxEntryRow(row, attachments) {
513
590
  return {
514
591
  id: row.id,
@@ -5,3 +5,8 @@ export declare function resolveToolDeliveryTarget(args: {
5
5
  target?: string;
6
6
  topic?: string;
7
7
  }, sessionId: string | null | undefined, sessions: GatewaySessionContext): BindingDeliveryTarget;
8
+ export declare function resolveOptionalToolDeliveryTarget(args: {
9
+ channel?: string;
10
+ target?: string;
11
+ topic?: string;
12
+ }, sessionId: string | null | undefined, sessions: GatewaySessionContext): BindingDeliveryTarget | null;
@@ -9,6 +9,12 @@ export function resolveToolDeliveryTarget(args, sessionId, sessions) {
9
9
  topic,
10
10
  };
11
11
  }
12
+ export function resolveOptionalToolDeliveryTarget(args, sessionId, sessions) {
13
+ if (args.channel === undefined && args.target === undefined && args.topic === undefined) {
14
+ return sessionId ? sessions.getDefaultReplyTarget(sessionId) : null;
15
+ }
16
+ return resolveToolDeliveryTarget(args, sessionId, sessions);
17
+ }
12
18
  function normalizeRequired(value, field) {
13
19
  if (value === null) {
14
20
  throw new Error(`${field} is required when the current session has no default reply target`);
@@ -1,7 +1,7 @@
1
1
  import { tool } from "@opencode-ai/plugin";
2
2
  export function createCronRunTool(runtime) {
3
3
  return tool({
4
- description: "Run one persisted gateway cron job immediately without changing its schedule",
4
+ description: "Run one persisted gateway schedule job immediately without changing its schedule metadata.",
5
5
  args: {
6
6
  id: tool.schema.string().min(1),
7
7
  },
@@ -1,3 +1,4 @@
1
1
  import type { ToolDefinition } from "@opencode-ai/plugin";
2
2
  import type { GatewayCronRuntime } from "../cron/runtime";
3
- export declare function createCronUpsertTool(runtime: GatewayCronRuntime): ToolDefinition;
3
+ import type { GatewaySessionContext } from "../session/context";
4
+ export declare function createCronUpsertTool(runtime: GatewayCronRuntime, sessions: GatewaySessionContext): ToolDefinition;
@@ -1,8 +1,9 @@
1
1
  import { tool } from "@opencode-ai/plugin";
2
+ import { resolveOptionalToolDeliveryTarget } from "./channel-target";
2
3
  import { formatUnixMsAsUtc, formatUnixMsInTimeZone } from "./time";
3
- export function createCronUpsertTool(runtime) {
4
+ export function createCronUpsertTool(runtime, sessions) {
4
5
  return tool({
5
- description: "Create or replace a persisted gateway cron job. The schedule uses cron.timezone or the runtime local time zone.",
6
+ description: "Create or replace a recurring gateway cron job. When called from a channel-backed session, delivery defaults to the current reply target.",
6
7
  args: {
7
8
  id: tool.schema.string().min(1),
8
9
  schedule: tool.schema.string().min(1),
@@ -12,26 +13,39 @@ export function createCronUpsertTool(runtime) {
12
13
  delivery_target: tool.schema.string().optional(),
13
14
  delivery_topic: tool.schema.string().optional(),
14
15
  },
15
- async execute(args) {
16
+ async execute(args, context) {
17
+ const deliveryTarget = resolveOptionalToolDeliveryTarget({
18
+ channel: args.delivery_channel,
19
+ target: args.delivery_target,
20
+ topic: args.delivery_topic,
21
+ }, context.sessionID, sessions);
16
22
  const timeZone = runtime.timeZone();
17
23
  const job = runtime.upsertJob({
18
24
  id: args.id,
19
25
  schedule: args.schedule,
20
26
  prompt: args.prompt,
21
27
  enabled: args.enabled ?? true,
22
- deliveryChannel: args.delivery_channel ?? null,
23
- deliveryTarget: args.delivery_target ?? null,
24
- deliveryTopic: args.delivery_topic ?? null,
28
+ deliveryChannel: deliveryTarget?.channel ?? null,
29
+ deliveryTarget: deliveryTarget?.target ?? null,
30
+ deliveryTopic: deliveryTarget?.topic ?? null,
25
31
  });
26
32
  return [
27
33
  `id=${job.id}`,
34
+ `kind=${job.kind}`,
28
35
  `enabled=${job.enabled}`,
29
36
  `schedule=${job.schedule}`,
30
37
  `timezone=${timeZone}`,
31
38
  `next_run_at_ms=${job.nextRunAtMs}`,
32
39
  `next_run_at_local=${formatUnixMsInTimeZone(job.nextRunAtMs, timeZone)}`,
33
40
  `next_run_at_utc=${formatUnixMsAsUtc(job.nextRunAtMs)}`,
41
+ `delivery=${formatDelivery(job.deliveryChannel, job.deliveryTarget, job.deliveryTopic)}`,
34
42
  ].join("\n");
35
43
  },
36
44
  });
37
45
  }
46
+ function formatDelivery(channel, target, topic) {
47
+ if (channel === null || target === null) {
48
+ return "none";
49
+ }
50
+ return topic === null ? `${channel}:${target}` : `${channel}:${target}:topic:${topic}`;
51
+ }
@@ -1,3 +1,3 @@
1
1
  import type { ToolDefinition } from "@opencode-ai/plugin";
2
2
  import type { GatewayCronRuntime } from "../cron/runtime";
3
- export declare function createCronRemoveTool(runtime: GatewayCronRuntime): ToolDefinition;
3
+ export declare function createScheduleCancelTool(runtime: GatewayCronRuntime): ToolDefinition;
@@ -0,0 +1,12 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ export function createScheduleCancelTool(runtime) {
3
+ return tool({
4
+ description: "Cancel a persisted gateway schedule job without deleting its run history.",
5
+ args: {
6
+ id: tool.schema.string().min(1),
7
+ },
8
+ async execute(args) {
9
+ return runtime.cancelJob(args.id) ? `canceled=${args.id}` : `inactive=${args.id}`;
10
+ },
11
+ });
12
+ }
@@ -0,0 +1,4 @@
1
+ import type { ScheduleJobStatus } from "../cron/runtime";
2
+ import type { CronJobRecord } from "../store/sqlite";
3
+ export declare function formatScheduleJob(job: CronJobRecord, timeZone: string): string;
4
+ export declare function formatScheduleStatus(status: ScheduleJobStatus, timeZone: string): string;
@@ -0,0 +1,48 @@
1
+ import { formatUnixMsAsUtc, formatUnixMsInTimeZone } from "./time";
2
+ export function formatScheduleJob(job, timeZone) {
3
+ const lines = [`id=${job.id}`, `kind=${job.kind}`, `enabled=${job.enabled}`];
4
+ if (job.kind === "cron") {
5
+ lines.push(`schedule=${job.schedule}`);
6
+ lines.push(`timezone=${timeZone}`);
7
+ lines.push(`next_run_at_ms=${job.nextRunAtMs}`);
8
+ lines.push(`next_run_at_local=${formatUnixMsInTimeZone(job.nextRunAtMs, timeZone)}`);
9
+ lines.push(`next_run_at_utc=${formatUnixMsAsUtc(job.nextRunAtMs)}`);
10
+ }
11
+ else {
12
+ lines.push(`run_at_ms=${job.runAtMs ?? "none"}`);
13
+ if (job.runAtMs !== null) {
14
+ lines.push(`run_at_local=${formatUnixMsInTimeZone(job.runAtMs, timeZone)}`);
15
+ lines.push(`run_at_utc=${formatUnixMsAsUtc(job.runAtMs)}`);
16
+ }
17
+ }
18
+ lines.push(`delivery=${formatDelivery(job.deliveryChannel, job.deliveryTarget, job.deliveryTopic)}`);
19
+ lines.push(`prompt=${job.prompt}`);
20
+ return lines.join("\n");
21
+ }
22
+ export function formatScheduleStatus(status, timeZone) {
23
+ const lines = [formatScheduleJob(status.job, timeZone), `state=${status.state}`];
24
+ if (status.runs.length === 0) {
25
+ lines.push("runs=none");
26
+ return lines.join("\n");
27
+ }
28
+ lines.push("");
29
+ lines.push(...status.runs.map((run, index) => formatRun(run, index + 1)));
30
+ return lines.join("\n");
31
+ }
32
+ function formatRun(run, ordinal) {
33
+ return [
34
+ `run[${ordinal}].id=${run.id}`,
35
+ `run[${ordinal}].status=${run.status}`,
36
+ `run[${ordinal}].scheduled_for_ms=${run.scheduledForMs}`,
37
+ `run[${ordinal}].started_at_ms=${run.startedAtMs}`,
38
+ `run[${ordinal}].finished_at_ms=${run.finishedAtMs ?? "none"}`,
39
+ `run[${ordinal}].response_text=${run.responseText ?? "none"}`,
40
+ `run[${ordinal}].error_message=${run.errorMessage ?? "none"}`,
41
+ ].join("\n");
42
+ }
43
+ function formatDelivery(channel, target, topic) {
44
+ if (channel === null || target === null) {
45
+ return "none";
46
+ }
47
+ return topic === null ? `${channel}:${target}` : `${channel}:${target}:topic:${topic}`;
48
+ }
@@ -1,3 +1,3 @@
1
1
  import type { ToolDefinition } from "@opencode-ai/plugin";
2
2
  import type { GatewayCronRuntime } from "../cron/runtime";
3
- export declare function createCronListTool(runtime: GatewayCronRuntime): ToolDefinition;
3
+ export declare function createScheduleListTool(runtime: GatewayCronRuntime): ToolDefinition;
@@ -0,0 +1,17 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { formatScheduleJob } from "./schedule-format";
3
+ export function createScheduleListTool(runtime) {
4
+ return tool({
5
+ description: "List persisted gateway schedule jobs, including recurring cron jobs and one-shot timers.",
6
+ args: {
7
+ include_terminal: tool.schema.boolean().optional(),
8
+ },
9
+ async execute(args) {
10
+ const jobs = runtime.listJobs(args.include_terminal ?? false);
11
+ if (jobs.length === 0) {
12
+ return "no scheduled jobs";
13
+ }
14
+ return jobs.map((job) => formatScheduleJob(job, runtime.timeZone())).join("\n\n");
15
+ },
16
+ });
17
+ }
@@ -0,0 +1,4 @@
1
+ import type { ToolDefinition } from "@opencode-ai/plugin";
2
+ import type { GatewayCronRuntime } from "../cron/runtime";
3
+ import type { GatewaySessionContext } from "../session/context";
4
+ export declare function createScheduleOnceTool(runtime: GatewayCronRuntime, sessions: GatewaySessionContext): ToolDefinition;
@@ -0,0 +1,43 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { resolveOptionalToolDeliveryTarget } from "./channel-target";
3
+ import { formatScheduleJob } from "./schedule-format";
4
+ export function createScheduleOnceTool(runtime, sessions) {
5
+ return tool({
6
+ description: "Schedule a one-shot gateway job. When called from a channel-backed session, delivery defaults to the current reply target.",
7
+ args: {
8
+ id: tool.schema.string().min(1),
9
+ prompt: tool.schema.string().min(1),
10
+ delay_seconds: tool.schema.number().optional(),
11
+ run_at_ms: tool.schema.number().optional(),
12
+ delivery_channel: tool.schema.string().optional(),
13
+ delivery_target: tool.schema.string().optional(),
14
+ delivery_topic: tool.schema.string().optional(),
15
+ },
16
+ async execute(args, context) {
17
+ const deliveryTarget = resolveOptionalToolDeliveryTarget({
18
+ channel: args.delivery_channel,
19
+ target: args.delivery_target,
20
+ topic: args.delivery_topic,
21
+ }, context.sessionID, sessions);
22
+ const job = runtime.scheduleOnce({
23
+ id: args.id,
24
+ prompt: args.prompt,
25
+ delaySeconds: normalizeOptionalInteger(args.delay_seconds, "delay_seconds"),
26
+ runAtMs: normalizeOptionalInteger(args.run_at_ms, "run_at_ms"),
27
+ deliveryChannel: deliveryTarget?.channel ?? null,
28
+ deliveryTarget: deliveryTarget?.target ?? null,
29
+ deliveryTopic: deliveryTarget?.topic ?? null,
30
+ });
31
+ return formatScheduleJob(job, runtime.timeZone());
32
+ },
33
+ });
34
+ }
35
+ function normalizeOptionalInteger(value, field) {
36
+ if (value === undefined) {
37
+ return null;
38
+ }
39
+ if (!Number.isSafeInteger(value)) {
40
+ throw new Error(`${field} must be an integer`);
41
+ }
42
+ return value;
43
+ }
@@ -0,0 +1,3 @@
1
+ import type { ToolDefinition } from "@opencode-ai/plugin";
2
+ import type { GatewayCronRuntime } from "../cron/runtime";
3
+ export declare function createScheduleStatusTool(runtime: GatewayCronRuntime): ToolDefinition;
@@ -0,0 +1,23 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { formatScheduleStatus } from "./schedule-format";
3
+ export function createScheduleStatusTool(runtime) {
4
+ return tool({
5
+ description: "Inspect one persisted gateway schedule job and its recent run history.",
6
+ args: {
7
+ id: tool.schema.string().min(1),
8
+ limit: tool.schema.number().optional(),
9
+ },
10
+ async execute(args) {
11
+ return formatScheduleStatus(runtime.getJobStatus(args.id, normalizeOptionalLimit(args.limit)), runtime.timeZone());
12
+ },
13
+ });
14
+ }
15
+ function normalizeOptionalLimit(value) {
16
+ if (value === undefined) {
17
+ return undefined;
18
+ }
19
+ if (!Number.isSafeInteger(value)) {
20
+ throw new Error("limit must be an integer");
21
+ }
22
+ return value;
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-gateway",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Gateway plugin for OpenCode",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -41,13 +41,13 @@
41
41
  },
42
42
  "repository": {
43
43
  "type": "git",
44
- "url": "git+https://github.com/M4n5ter/aI.git",
44
+ "url": "git+https://github.com/M4n5ter/opencode-gateway.git",
45
45
  "directory": "packages/opencode-plugin"
46
46
  },
47
47
  "bugs": {
48
- "url": "https://github.com/M4n5ter/aI/issues"
48
+ "url": "https://github.com/M4n5ter/opencode-gateway/issues"
49
49
  },
50
- "homepage": "https://github.com/M4n5ter/aI",
50
+ "homepage": "https://github.com/M4n5ter/opencode-gateway",
51
51
  "publishConfig": {
52
52
  "access": "public"
53
53
  },
@@ -1,34 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin";
2
- import { formatUnixMsAsUtc, formatUnixMsInTimeZone } from "./time";
3
- export function createCronListTool(runtime) {
4
- return tool({
5
- description: "List persisted gateway cron jobs. Schedules use cron.timezone or the runtime local time zone.",
6
- args: {},
7
- async execute() {
8
- const jobs = runtime.listJobs();
9
- if (jobs.length === 0) {
10
- return "no cron jobs";
11
- }
12
- return jobs.map((job) => formatCronJob(job, runtime.timeZone())).join("\n\n");
13
- },
14
- });
15
- }
16
- function formatCronJob(job, timeZone) {
17
- return [
18
- `id=${job.id}`,
19
- `enabled=${job.enabled}`,
20
- `schedule=${job.schedule}`,
21
- `timezone=${timeZone}`,
22
- `next_run_at_ms=${job.nextRunAtMs}`,
23
- `next_run_at_local=${formatUnixMsInTimeZone(job.nextRunAtMs, timeZone)}`,
24
- `next_run_at_utc=${formatUnixMsAsUtc(job.nextRunAtMs)}`,
25
- `delivery=${formatDelivery(job.deliveryChannel, job.deliveryTarget, job.deliveryTopic)}`,
26
- `prompt=${job.prompt}`,
27
- ].join("\n");
28
- }
29
- function formatDelivery(channel, target, topic) {
30
- if (channel === null || target === null) {
31
- return "none";
32
- }
33
- return topic === null ? `${channel}:${target}` : `${channel}:${target}:topic:${topic}`;
34
- }
@@ -1,12 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin";
2
- export function createCronRemoveTool(runtime) {
3
- return tool({
4
- description: "Remove a persisted gateway cron job",
5
- args: {
6
- id: tool.schema.string().min(1),
7
- },
8
- async execute(args) {
9
- return runtime.removeJob(args.id) ? `removed=${args.id}` : `missing=${args.id}`;
10
- },
11
- });
12
- }