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.
- package/dist/cli/doctor.js +3 -1
- package/dist/cli/init.js +4 -1
- package/dist/cli/paths.js +1 -1
- package/dist/cli.js +13 -4
- package/dist/config/gateway.d.ts +1 -0
- package/dist/config/gateway.js +2 -1
- package/dist/config/paths.d.ts +2 -0
- package/dist/config/paths.js +5 -1
- package/dist/cron/runtime.d.ts +24 -5
- package/dist/cron/runtime.js +178 -13
- package/dist/delivery/text.js +1 -1
- package/dist/gateway.js +41 -35
- package/dist/index.js +9 -5
- package/dist/opencode/adapter.d.ts +2 -0
- package/dist/opencode/adapter.js +56 -7
- package/dist/runtime/conversation-coordinator.d.ts +4 -0
- package/dist/runtime/conversation-coordinator.js +22 -0
- package/dist/runtime/executor.d.ts +33 -5
- package/dist/runtime/executor.js +229 -22
- package/dist/runtime/runtime-singleton.d.ts +2 -0
- package/dist/runtime/runtime-singleton.js +28 -0
- package/dist/session/context.js +6 -0
- package/dist/store/migrations.js +15 -1
- package/dist/store/sqlite.d.ts +19 -2
- package/dist/store/sqlite.js +81 -4
- package/dist/tools/channel-target.d.ts +5 -0
- package/dist/tools/channel-target.js +6 -0
- package/dist/tools/cron-run.js +1 -1
- package/dist/tools/cron-upsert.d.ts +2 -1
- package/dist/tools/cron-upsert.js +20 -6
- package/dist/tools/{cron-remove.d.ts → schedule-cancel.d.ts} +1 -1
- package/dist/tools/schedule-cancel.js +12 -0
- package/dist/tools/schedule-format.d.ts +4 -0
- package/dist/tools/schedule-format.js +48 -0
- package/dist/tools/{cron-list.d.ts → schedule-list.d.ts} +1 -1
- package/dist/tools/schedule-list.js +17 -0
- package/dist/tools/schedule-once.d.ts +4 -0
- package/dist/tools/schedule-once.js +43 -0
- package/dist/tools/schedule-status.d.ts +3 -0
- package/dist/tools/schedule-status.js +23 -0
- package/generated/wasm/pkg/opencode_gateway_ffi_bg.wasm +0 -0
- package/package.json +4 -4
- package/dist/tools/cron-list.js +0 -34
- package/dist/tools/cron-remove.js +0 -12
package/dist/store/sqlite.js
CHANGED
|
@@ -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, ?
|
|
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
|
-
|
|
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`);
|
package/dist/tools/cron-run.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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:
|
|
23
|
-
deliveryTarget:
|
|
24
|
-
deliveryTopic:
|
|
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
|
|
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
|
|
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,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
|
+
}
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-gateway",
|
|
3
|
-
"version": "0.1.
|
|
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/
|
|
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/
|
|
48
|
+
"url": "https://github.com/M4n5ter/opencode-gateway/issues"
|
|
49
49
|
},
|
|
50
|
-
"homepage": "https://github.com/M4n5ter/
|
|
50
|
+
"homepage": "https://github.com/M4n5ter/opencode-gateway",
|
|
51
51
|
"publishConfig": {
|
|
52
52
|
"access": "public"
|
|
53
53
|
},
|
package/dist/tools/cron-list.js
DELETED
|
@@ -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
|
-
}
|