opencode-gateway 0.2.3 → 0.2.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.
Files changed (79) hide show
  1. package/dist/cli.js +0 -0
  2. package/dist/index.js +36910 -56
  3. package/dist/runtime/delay.d.ts +1 -0
  4. package/dist/store/database.d.ts +22 -0
  5. package/dist/store/migrations.d.ts +2 -2
  6. package/dist/store/sqlite.d.ts +2 -2
  7. package/package.json +9 -3
  8. package/dist/binding/execution.js +0 -1
  9. package/dist/binding/gateway.js +0 -1
  10. package/dist/binding/index.js +0 -4
  11. package/dist/binding/opencode.js +0 -1
  12. package/dist/cli/args.js +0 -53
  13. package/dist/cli/doctor.js +0 -49
  14. package/dist/cli/init.js +0 -40
  15. package/dist/cli/opencode-config-file.js +0 -18
  16. package/dist/cli/opencode-config.js +0 -194
  17. package/dist/cli/paths.js +0 -22
  18. package/dist/cli/templates.js +0 -41
  19. package/dist/config/cron.js +0 -52
  20. package/dist/config/gateway.js +0 -148
  21. package/dist/config/memory.js +0 -105
  22. package/dist/config/paths.js +0 -39
  23. package/dist/config/telegram.js +0 -91
  24. package/dist/cron/runtime.js +0 -402
  25. package/dist/delivery/telegram.js +0 -75
  26. package/dist/delivery/text.js +0 -175
  27. package/dist/gateway.js +0 -117
  28. package/dist/host/file-sender.js +0 -59
  29. package/dist/host/logger.js +0 -53
  30. package/dist/host/transport.js +0 -35
  31. package/dist/mailbox/router.js +0 -16
  32. package/dist/media/mime.js +0 -45
  33. package/dist/memory/prompt.js +0 -122
  34. package/dist/opencode/adapter.js +0 -340
  35. package/dist/opencode/driver-hub.js +0 -82
  36. package/dist/opencode/event-normalize.js +0 -48
  37. package/dist/opencode/event-stream.js +0 -65
  38. package/dist/opencode/events.js +0 -1
  39. package/dist/questions/client.js +0 -36
  40. package/dist/questions/format.js +0 -36
  41. package/dist/questions/normalize.js +0 -45
  42. package/dist/questions/parser.js +0 -96
  43. package/dist/questions/runtime.js +0 -195
  44. package/dist/questions/types.js +0 -1
  45. package/dist/runtime/attachments.js +0 -12
  46. package/dist/runtime/conversation-coordinator.js +0 -22
  47. package/dist/runtime/executor.js +0 -407
  48. package/dist/runtime/mailbox.js +0 -112
  49. package/dist/runtime/opencode-runner.js +0 -79
  50. package/dist/runtime/runtime-singleton.js +0 -28
  51. package/dist/session/context.js +0 -23
  52. package/dist/session/conversation-key.js +0 -3
  53. package/dist/session/switcher.js +0 -59
  54. package/dist/session/system-prompt.js +0 -52
  55. package/dist/store/migrations.js +0 -197
  56. package/dist/store/sqlite.js +0 -777
  57. package/dist/telegram/client.js +0 -180
  58. package/dist/telegram/media.js +0 -65
  59. package/dist/telegram/normalize.js +0 -119
  60. package/dist/telegram/poller.js +0 -166
  61. package/dist/telegram/runtime.js +0 -157
  62. package/dist/telegram/state.js +0 -149
  63. package/dist/telegram/types.js +0 -1
  64. package/dist/tools/channel-new-session.js +0 -27
  65. package/dist/tools/channel-send-file.js +0 -27
  66. package/dist/tools/channel-target.js +0 -34
  67. package/dist/tools/cron-run.js +0 -20
  68. package/dist/tools/cron-upsert.js +0 -51
  69. package/dist/tools/gateway-dispatch-cron.js +0 -33
  70. package/dist/tools/gateway-status.js +0 -25
  71. package/dist/tools/schedule-cancel.js +0 -12
  72. package/dist/tools/schedule-format.js +0 -48
  73. package/dist/tools/schedule-list.js +0 -17
  74. package/dist/tools/schedule-once.js +0 -43
  75. package/dist/tools/schedule-status.js +0 -23
  76. package/dist/tools/telegram-send-test.js +0 -26
  77. package/dist/tools/telegram-status.js +0 -49
  78. package/dist/tools/time.js +0 -25
  79. package/dist/utils/error.js +0 -57
@@ -1,149 +0,0 @@
1
- const TELEGRAM_UPDATE_OFFSET_KEY = "telegram.update_offset";
2
- const TELEGRAM_LAST_POLL_SUCCESS_MS_KEY = "telegram.last_poll_success_ms";
3
- const TELEGRAM_LAST_POLL_STARTED_MS_KEY = "telegram.last_poll_started_ms";
4
- const TELEGRAM_LAST_POLL_COMPLETED_MS_KEY = "telegram.last_poll_completed_ms";
5
- const TELEGRAM_LAST_POLL_ERROR_AT_MS_KEY = "telegram.last_poll_error_at_ms";
6
- const TELEGRAM_LAST_POLL_ERROR_MESSAGE_KEY = "telegram.last_poll_error_message";
7
- const TELEGRAM_LAST_POLL_TIMEOUT_AT_MS_KEY = "telegram.last_poll_timeout_at_ms";
8
- const TELEGRAM_LAST_POLL_TIMEOUT_MESSAGE_KEY = "telegram.last_poll_timeout_message";
9
- const TELEGRAM_LAST_SEND_SUCCESS_MS_KEY = "telegram.last_send_success_ms";
10
- const TELEGRAM_LAST_SEND_ERROR_AT_MS_KEY = "telegram.last_send_error_at_ms";
11
- const TELEGRAM_LAST_SEND_ERROR_MESSAGE_KEY = "telegram.last_send_error_message";
12
- const TELEGRAM_LAST_PROBE_SUCCESS_MS_KEY = "telegram.last_probe_success_ms";
13
- const TELEGRAM_LAST_PROBE_ERROR_AT_MS_KEY = "telegram.last_probe_error_at_ms";
14
- const TELEGRAM_LAST_PROBE_ERROR_MESSAGE_KEY = "telegram.last_probe_error_message";
15
- const TELEGRAM_LAST_BOT_ID_KEY = "telegram.last_bot_id";
16
- const TELEGRAM_LAST_BOT_USERNAME_KEY = "telegram.last_bot_username";
17
- const TELEGRAM_LAST_DRAFT_SUCCESS_MS_KEY = "telegram.last_draft_success_ms";
18
- const TELEGRAM_LAST_DRAFT_ERROR_AT_MS_KEY = "telegram.last_draft_error_at_ms";
19
- const TELEGRAM_LAST_DRAFT_ERROR_MESSAGE_KEY = "telegram.last_draft_error_message";
20
- const TELEGRAM_LAST_PREVIEW_EMIT_MS_KEY = "telegram.last_preview_emit_ms";
21
- const TELEGRAM_LAST_STREAM_FALLBACK_AT_MS_KEY = "telegram.last_stream_fallback_at_ms";
22
- const TELEGRAM_LAST_STREAM_FALLBACK_REASON_KEY = "telegram.last_stream_fallback_reason";
23
- export function readTelegramHealthSnapshot(store) {
24
- return {
25
- updateOffset: readStoredInteger(store, TELEGRAM_UPDATE_OFFSET_KEY),
26
- lastPollSuccessMs: readStoredInteger(store, TELEGRAM_LAST_POLL_SUCCESS_MS_KEY),
27
- lastPollStartedMs: readStoredInteger(store, TELEGRAM_LAST_POLL_STARTED_MS_KEY),
28
- lastPollCompletedMs: readStoredInteger(store, TELEGRAM_LAST_POLL_COMPLETED_MS_KEY),
29
- lastPollErrorAtMs: readStoredInteger(store, TELEGRAM_LAST_POLL_ERROR_AT_MS_KEY),
30
- lastPollErrorMessage: readStoredText(store, TELEGRAM_LAST_POLL_ERROR_MESSAGE_KEY),
31
- lastPollTimeoutAtMs: readStoredInteger(store, TELEGRAM_LAST_POLL_TIMEOUT_AT_MS_KEY),
32
- lastPollTimeoutMessage: readStoredText(store, TELEGRAM_LAST_POLL_TIMEOUT_MESSAGE_KEY),
33
- lastSendSuccessMs: readStoredInteger(store, TELEGRAM_LAST_SEND_SUCCESS_MS_KEY),
34
- lastSendErrorAtMs: readStoredInteger(store, TELEGRAM_LAST_SEND_ERROR_AT_MS_KEY),
35
- lastSendErrorMessage: readStoredText(store, TELEGRAM_LAST_SEND_ERROR_MESSAGE_KEY),
36
- lastProbeSuccessMs: readStoredInteger(store, TELEGRAM_LAST_PROBE_SUCCESS_MS_KEY),
37
- lastProbeErrorAtMs: readStoredInteger(store, TELEGRAM_LAST_PROBE_ERROR_AT_MS_KEY),
38
- lastProbeErrorMessage: readStoredText(store, TELEGRAM_LAST_PROBE_ERROR_MESSAGE_KEY),
39
- lastBotId: readStoredText(store, TELEGRAM_LAST_BOT_ID_KEY),
40
- lastBotUsername: readStoredText(store, TELEGRAM_LAST_BOT_USERNAME_KEY),
41
- lastDraftSuccessMs: readStoredInteger(store, TELEGRAM_LAST_DRAFT_SUCCESS_MS_KEY),
42
- lastDraftErrorAtMs: readStoredInteger(store, TELEGRAM_LAST_DRAFT_ERROR_AT_MS_KEY),
43
- lastDraftErrorMessage: readStoredText(store, TELEGRAM_LAST_DRAFT_ERROR_MESSAGE_KEY),
44
- lastPreviewEmitMs: readStoredInteger(store, TELEGRAM_LAST_PREVIEW_EMIT_MS_KEY),
45
- lastStreamFallbackAtMs: readStoredInteger(store, TELEGRAM_LAST_STREAM_FALLBACK_AT_MS_KEY),
46
- lastStreamFallbackReason: readStoredFallbackReason(store),
47
- };
48
- }
49
- export function recordTelegramPollStarted(store, recordedAtMs) {
50
- store.putStateValue(TELEGRAM_LAST_POLL_STARTED_MS_KEY, String(recordedAtMs), recordedAtMs);
51
- }
52
- export function recordTelegramPollCompleted(store, recordedAtMs) {
53
- store.putStateValue(TELEGRAM_LAST_POLL_COMPLETED_MS_KEY, String(recordedAtMs), recordedAtMs);
54
- }
55
- export function recordTelegramPollSuccess(store, recordedAtMs) {
56
- store.putStateValue(TELEGRAM_LAST_POLL_SUCCESS_MS_KEY, String(recordedAtMs), recordedAtMs);
57
- store.putStateValue(TELEGRAM_LAST_POLL_ERROR_AT_MS_KEY, "", recordedAtMs);
58
- store.putStateValue(TELEGRAM_LAST_POLL_ERROR_MESSAGE_KEY, "", recordedAtMs);
59
- store.putStateValue(TELEGRAM_LAST_POLL_TIMEOUT_AT_MS_KEY, "", recordedAtMs);
60
- store.putStateValue(TELEGRAM_LAST_POLL_TIMEOUT_MESSAGE_KEY, "", recordedAtMs);
61
- }
62
- export function recordTelegramPollFailure(store, message, recordedAtMs) {
63
- store.putStateValue(TELEGRAM_LAST_POLL_ERROR_AT_MS_KEY, String(recordedAtMs), recordedAtMs);
64
- store.putStateValue(TELEGRAM_LAST_POLL_ERROR_MESSAGE_KEY, message, recordedAtMs);
65
- }
66
- export function recordTelegramPollTimeout(store, message, recordedAtMs) {
67
- store.putStateValue(TELEGRAM_LAST_POLL_TIMEOUT_AT_MS_KEY, String(recordedAtMs), recordedAtMs);
68
- store.putStateValue(TELEGRAM_LAST_POLL_TIMEOUT_MESSAGE_KEY, message, recordedAtMs);
69
- recordTelegramPollFailure(store, message, recordedAtMs);
70
- }
71
- export function recordTelegramSendSuccess(store, recordedAtMs) {
72
- store.putStateValue(TELEGRAM_LAST_SEND_SUCCESS_MS_KEY, String(recordedAtMs), recordedAtMs);
73
- store.putStateValue(TELEGRAM_LAST_SEND_ERROR_AT_MS_KEY, "", recordedAtMs);
74
- store.putStateValue(TELEGRAM_LAST_SEND_ERROR_MESSAGE_KEY, "", recordedAtMs);
75
- }
76
- export function recordTelegramSendFailure(store, message, recordedAtMs) {
77
- store.putStateValue(TELEGRAM_LAST_SEND_ERROR_AT_MS_KEY, String(recordedAtMs), recordedAtMs);
78
- store.putStateValue(TELEGRAM_LAST_SEND_ERROR_MESSAGE_KEY, message, recordedAtMs);
79
- }
80
- export function recordTelegramProbeSuccess(store, bot, recordedAtMs) {
81
- store.putStateValue(TELEGRAM_LAST_PROBE_SUCCESS_MS_KEY, String(recordedAtMs), recordedAtMs);
82
- store.putStateValue(TELEGRAM_LAST_PROBE_ERROR_AT_MS_KEY, "", recordedAtMs);
83
- store.putStateValue(TELEGRAM_LAST_PROBE_ERROR_MESSAGE_KEY, "", recordedAtMs);
84
- store.putStateValue(TELEGRAM_LAST_BOT_ID_KEY, String(bot.id), recordedAtMs);
85
- store.putStateValue(TELEGRAM_LAST_BOT_USERNAME_KEY, bot.username ?? "", recordedAtMs);
86
- }
87
- export function recordTelegramProbeFailure(store, message, recordedAtMs) {
88
- store.putStateValue(TELEGRAM_LAST_PROBE_ERROR_AT_MS_KEY, String(recordedAtMs), recordedAtMs);
89
- store.putStateValue(TELEGRAM_LAST_PROBE_ERROR_MESSAGE_KEY, message, recordedAtMs);
90
- }
91
- export function recordTelegramDraftSuccess(store, recordedAtMs) {
92
- store.putStateValue(TELEGRAM_LAST_DRAFT_SUCCESS_MS_KEY, String(recordedAtMs), recordedAtMs);
93
- store.putStateValue(TELEGRAM_LAST_DRAFT_ERROR_AT_MS_KEY, "", recordedAtMs);
94
- store.putStateValue(TELEGRAM_LAST_DRAFT_ERROR_MESSAGE_KEY, "", recordedAtMs);
95
- }
96
- export function recordTelegramDraftFailure(store, message, recordedAtMs) {
97
- store.putStateValue(TELEGRAM_LAST_DRAFT_ERROR_AT_MS_KEY, String(recordedAtMs), recordedAtMs);
98
- store.putStateValue(TELEGRAM_LAST_DRAFT_ERROR_MESSAGE_KEY, message, recordedAtMs);
99
- }
100
- export function recordTelegramPreviewEmit(store, recordedAtMs) {
101
- store.putStateValue(TELEGRAM_LAST_PREVIEW_EMIT_MS_KEY, String(recordedAtMs), recordedAtMs);
102
- }
103
- export function recordTelegramStreamFallback(store, reason, recordedAtMs) {
104
- store.putStateValue(TELEGRAM_LAST_STREAM_FALLBACK_AT_MS_KEY, String(recordedAtMs), recordedAtMs);
105
- store.putStateValue(TELEGRAM_LAST_STREAM_FALLBACK_REASON_KEY, reason, recordedAtMs);
106
- }
107
- export function readTelegramChatType(store, chatId) {
108
- const value = readStoredText(store, telegramChatTypeKey(chatId));
109
- return value === null ? null : value;
110
- }
111
- export function recordTelegramChatType(store, chatId, chatType, recordedAtMs) {
112
- store.putStateValue(telegramChatTypeKey(chatId), chatType, recordedAtMs);
113
- }
114
- function telegramChatTypeKey(chatId) {
115
- return `telegram.chat_type:${chatId}`;
116
- }
117
- function readStoredInteger(store, key) {
118
- const value = store.getStateValue(key);
119
- if (value === null || value.length === 0) {
120
- return null;
121
- }
122
- const parsed = Number.parseInt(value, 10);
123
- if (!Number.isSafeInteger(parsed) || parsed < 0) {
124
- throw new Error(`stored ${key} is invalid: ${value}`);
125
- }
126
- return parsed;
127
- }
128
- function readStoredText(store, key) {
129
- const value = store.getStateValue(key);
130
- if (value === null || value.length === 0) {
131
- return null;
132
- }
133
- return value;
134
- }
135
- function readStoredFallbackReason(store) {
136
- const value = readStoredText(store, TELEGRAM_LAST_STREAM_FALLBACK_REASON_KEY);
137
- if (value === null) {
138
- return null;
139
- }
140
- switch (value) {
141
- case "non_private_chat":
142
- case "progressive_handle_unavailable":
143
- case "draft_send_failed":
144
- case "preview_not_established":
145
- return value;
146
- default:
147
- throw new Error(`stored ${TELEGRAM_LAST_STREAM_FALLBACK_REASON_KEY} is invalid: ${value}`);
148
- }
149
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,27 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin";
2
- import { resolveToolDeliveryTarget } from "./channel-target";
3
- export function createChannelNewSessionTool(switcher, sessions) {
4
- return tool({
5
- description: "Create a fresh OpenCode session for a channel route and switch future inbound messages to it. When called from a channel-backed session, channel, target, and topic default to the current reply target.",
6
- args: {
7
- channel: tool.schema.string().min(1).optional(),
8
- target: tool.schema.string().min(1).optional(),
9
- topic: tool.schema.string().optional(),
10
- title: tool.schema.string().optional(),
11
- },
12
- async execute(args, context) {
13
- return formatChannelSessionSwitchResult(await switcher.createAndSwitchSession(resolveToolDeliveryTarget(args, context.sessionID, sessions), args.title ?? null));
14
- },
15
- });
16
- }
17
- function formatChannelSessionSwitchResult(result) {
18
- return [
19
- `channel=${result.channel}`,
20
- `target=${result.target}`,
21
- `topic=${result.topic ?? "none"}`,
22
- `conversation_key=${result.conversationKey}`,
23
- `previous_session_id=${result.previousSessionId ?? "none"}`,
24
- `new_session_id=${result.newSessionId}`,
25
- `effective_on=${result.effectiveOn}`,
26
- ].join("\n");
27
- }
@@ -1,27 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin";
2
- import { resolveToolDeliveryTarget } from "./channel-target";
3
- export function createChannelSendFileTool(sender, sessions) {
4
- return tool({
5
- description: "Send a local absolute-path file to a channel target. When called from a channel-backed session, channel, target, and topic default to the current reply target.",
6
- args: {
7
- channel: tool.schema.string().min(1).optional(),
8
- target: tool.schema.string().min(1).optional(),
9
- topic: tool.schema.string().optional(),
10
- file_path: tool.schema.string().min(1),
11
- caption: tool.schema.string().optional(),
12
- },
13
- async execute(args, context) {
14
- return formatChannelFileSendResult(await sender.sendFile(resolveToolDeliveryTarget(args, context.sessionID, sessions), args.file_path, args.caption ?? null));
15
- },
16
- });
17
- }
18
- function formatChannelFileSendResult(result) {
19
- return [
20
- `channel=${result.channel}`,
21
- `target=${result.target}`,
22
- `topic=${result.topic ?? "none"}`,
23
- `file_path=${result.filePath}`,
24
- `mime_type=${result.mimeType}`,
25
- `delivery_kind=${result.deliveryKind}`,
26
- ].join("\n");
27
- }
@@ -1,34 +0,0 @@
1
- export function resolveToolDeliveryTarget(args, sessionId, sessions) {
2
- const fallback = sessionId ? sessions.getDefaultReplyTarget(sessionId) : null;
3
- const channel = normalizeRequired(args.channel ?? fallback?.channel ?? null, "channel");
4
- const target = normalizeRequired(args.target ?? fallback?.target ?? null, "target");
5
- const topic = normalizeOptional(args.topic ?? fallback?.topic ?? null);
6
- return {
7
- channel,
8
- target,
9
- topic,
10
- };
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
- }
18
- function normalizeRequired(value, field) {
19
- if (value === null) {
20
- throw new Error(`${field} is required when the current session has no default reply target`);
21
- }
22
- const trimmed = value.trim();
23
- if (trimmed.length === 0) {
24
- throw new Error(`${field} must not be empty`);
25
- }
26
- return trimmed;
27
- }
28
- function normalizeOptional(value) {
29
- if (value === null) {
30
- return null;
31
- }
32
- const trimmed = value.trim();
33
- return trimmed.length === 0 ? null : trimmed;
34
- }
@@ -1,20 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin";
2
- export function createCronRunTool(runtime) {
3
- return tool({
4
- description: "Run one persisted gateway schedule job immediately without changing its schedule metadata.",
5
- args: {
6
- id: tool.schema.string().min(1),
7
- },
8
- async execute(args) {
9
- return formatRuntimeReport(await runtime.runNow(args.id));
10
- },
11
- });
12
- }
13
- function formatRuntimeReport(report) {
14
- return [
15
- `conversation_key=${report.conversationKey}`,
16
- `response_text=${report.responseText}`,
17
- `delivered=${report.delivered}`,
18
- `recorded_at_ms=${report.recordedAtMs}`,
19
- ].join("\n");
20
- }
@@ -1,51 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin";
2
- import { resolveOptionalToolDeliveryTarget } from "./channel-target";
3
- import { formatUnixMsAsUtc, formatUnixMsInTimeZone } from "./time";
4
- export function createCronUpsertTool(runtime, sessions) {
5
- return tool({
6
- description: "Create or replace a recurring gateway cron 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
- schedule: tool.schema.string().min(1),
10
- prompt: tool.schema.string().min(1),
11
- enabled: tool.schema.boolean().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 timeZone = runtime.timeZone();
23
- const job = runtime.upsertJob({
24
- id: args.id,
25
- schedule: args.schedule,
26
- prompt: args.prompt,
27
- enabled: args.enabled ?? true,
28
- deliveryChannel: deliveryTarget?.channel ?? null,
29
- deliveryTarget: deliveryTarget?.target ?? null,
30
- deliveryTopic: deliveryTarget?.topic ?? null,
31
- });
32
- return [
33
- `id=${job.id}`,
34
- `kind=${job.kind}`,
35
- `enabled=${job.enabled}`,
36
- `schedule=${job.schedule}`,
37
- `timezone=${timeZone}`,
38
- `next_run_at_ms=${job.nextRunAtMs}`,
39
- `next_run_at_local=${formatUnixMsInTimeZone(job.nextRunAtMs, timeZone)}`,
40
- `next_run_at_utc=${formatUnixMsAsUtc(job.nextRunAtMs)}`,
41
- `delivery=${formatDelivery(job.deliveryChannel, job.deliveryTarget, job.deliveryTopic)}`,
42
- ].join("\n");
43
- },
44
- });
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,33 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin";
2
- export function createGatewayDispatchCronTool(executor) {
3
- return tool({
4
- description: "Dispatch one gateway cron-style prompt through the gateway runtime",
5
- args: {
6
- id: tool.schema.string().min(1),
7
- schedule: tool.schema.string().min(1),
8
- prompt: tool.schema.string().min(1),
9
- },
10
- async execute(args) {
11
- const report = await executor.dispatchCronJob(toCronJobSpec(args));
12
- return formatRuntimeReport(report);
13
- },
14
- });
15
- }
16
- function toCronJobSpec(args) {
17
- return {
18
- id: args.id,
19
- schedule: args.schedule,
20
- prompt: args.prompt,
21
- deliveryChannel: null,
22
- deliveryTarget: null,
23
- deliveryTopic: null,
24
- };
25
- }
26
- function formatRuntimeReport(report) {
27
- return [
28
- `conversation_key=${report.conversationKey}`,
29
- `response_text=${report.responseText}`,
30
- `delivered=${report.delivered}`,
31
- `recorded_at_ms=${report.recordedAtMs}`,
32
- ].join("\n");
33
- }
@@ -1,25 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin";
2
- export function createGatewayStatusTool(runtime) {
3
- return tool({
4
- description: "Return the current Rust gateway contract status",
5
- args: {},
6
- async execute() {
7
- return formatGatewayStatus(runtime.status());
8
- },
9
- });
10
- }
11
- function formatGatewayStatus(status) {
12
- return [
13
- `runtime_mode=${status.runtimeMode}`,
14
- `supports_telegram=${status.supportsTelegram}`,
15
- `supports_cron=${status.supportsCron}`,
16
- `has_web_ui=${status.hasWebUi}`,
17
- `cron_timezone=${status.cronTimezone}`,
18
- `cron_enabled=${status.cronEnabled}`,
19
- `cron_polling=${status.cronPolling}`,
20
- `cron_running_jobs=${status.cronRunningJobs}`,
21
- `telegram_enabled=${status.telegramEnabled}`,
22
- `telegram_polling=${status.telegramPolling}`,
23
- `telegram_allowlist_mode=${status.telegramAllowlistMode}`,
24
- ].join("\n");
25
- }
@@ -1,12 +0,0 @@
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
- }
@@ -1,48 +0,0 @@
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,17 +0,0 @@
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
- }
@@ -1,43 +0,0 @@
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
- }
@@ -1,23 +0,0 @@
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
- }
@@ -1,26 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin";
2
- import { formatUnixMsAsUtc } from "./time";
3
- export function createTelegramSendTestTool(runtime) {
4
- return tool({
5
- description: "Send a Telegram test message to an explicit chat_id and optional topic",
6
- args: {
7
- chat_id: tool.schema.string().min(1),
8
- topic: tool.schema.string().optional(),
9
- text: tool.schema.string().optional(),
10
- mode: tool.schema.enum(["auto", "oneshot", "stream"]).optional(),
11
- },
12
- async execute(args) {
13
- return formatTelegramSendTestResult(await runtime.sendTest(args.chat_id, args.topic ?? null, args.text ?? null, args.mode ?? "auto"));
14
- },
15
- });
16
- }
17
- function formatTelegramSendTestResult(result) {
18
- return [
19
- `chat_id=${result.chatId}`,
20
- `topic=${result.topic ?? "none"}`,
21
- `mode=${result.mode}`,
22
- `sent_at_ms=${result.sentAtMs}`,
23
- `sent_at_utc=${formatUnixMsAsUtc(result.sentAtMs)}`,
24
- `text=${result.text}`,
25
- ].join("\n");
26
- }
@@ -1,49 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin";
2
- import { formatOptionalUnixMsAsUtc } from "./time";
3
- export function createTelegramStatusTool(runtime) {
4
- return tool({
5
- description: "Return Telegram gateway status, cached health, and a live Bot API probe",
6
- args: {},
7
- async execute() {
8
- return formatTelegramStatus(await runtime.status());
9
- },
10
- });
11
- }
12
- function formatTelegramStatus(status) {
13
- return [
14
- `enabled=${status.enabled}`,
15
- `polling=${status.polling}`,
16
- `allowlist_mode=${status.allowlistMode}`,
17
- `allowed_chats_count=${status.allowedChatsCount}`,
18
- `allowed_users_count=${status.allowedUsersCount}`,
19
- `update_offset=${status.updateOffset ?? "none"}`,
20
- `last_poll_success_ms=${status.lastPollSuccessMs ?? "none"}`,
21
- `last_poll_success_utc=${formatOptionalUnixMsAsUtc(status.lastPollSuccessMs)}`,
22
- `last_poll_error_at_ms=${status.lastPollErrorAtMs ?? "none"}`,
23
- `last_poll_error=${status.lastPollErrorMessage ?? "none"}`,
24
- `last_send_success_ms=${status.lastSendSuccessMs ?? "none"}`,
25
- `last_send_success_utc=${formatOptionalUnixMsAsUtc(status.lastSendSuccessMs)}`,
26
- `last_send_error_at_ms=${status.lastSendErrorAtMs ?? "none"}`,
27
- `last_send_error=${status.lastSendErrorMessage ?? "none"}`,
28
- `last_probe_success_ms=${status.lastProbeSuccessMs ?? "none"}`,
29
- `last_probe_success_utc=${formatOptionalUnixMsAsUtc(status.lastProbeSuccessMs)}`,
30
- `last_probe_error_at_ms=${status.lastProbeErrorAtMs ?? "none"}`,
31
- `last_probe_error=${status.lastProbeErrorMessage ?? "none"}`,
32
- `live_probe=${status.liveProbe}`,
33
- `live_probe_error=${status.liveProbeError ?? "none"}`,
34
- `bot_id=${status.liveBotId ?? status.lastBotId ?? "none"}`,
35
- `bot_username=${status.liveBotUsername ?? status.lastBotUsername ?? "none"}`,
36
- `streaming_enabled=${status.streamingEnabled}`,
37
- `opencode_event_stream_connected=${status.opencodeEventStreamConnected}`,
38
- `last_event_stream_error=${status.lastEventStreamError ?? "none"}`,
39
- `last_draft_success_ms=${status.lastDraftSuccessMs ?? "none"}`,
40
- `last_draft_success_utc=${formatOptionalUnixMsAsUtc(status.lastDraftSuccessMs)}`,
41
- `last_draft_error_at_ms=${status.lastDraftErrorAtMs ?? "none"}`,
42
- `last_draft_error=${status.lastDraftErrorMessage ?? "none"}`,
43
- `last_preview_emit_ms=${status.lastPreviewEmitMs ?? "none"}`,
44
- `last_preview_emit_utc=${formatOptionalUnixMsAsUtc(status.lastPreviewEmitMs)}`,
45
- `last_stream_fallback_at_ms=${status.lastStreamFallbackAtMs ?? "none"}`,
46
- `last_stream_fallback_utc=${formatOptionalUnixMsAsUtc(status.lastStreamFallbackAtMs)}`,
47
- `last_stream_fallback_reason=${status.lastStreamFallbackReason ?? "none"}`,
48
- ].join("\n");
49
- }
@@ -1,25 +0,0 @@
1
- export function formatUnixMsAsUtc(value) {
2
- return new Date(value).toISOString();
3
- }
4
- export function formatOptionalUnixMsAsUtc(value) {
5
- return value === null ? "none" : formatUnixMsAsUtc(value);
6
- }
7
- export function formatUnixMsInTimeZone(value, timeZone) {
8
- const formatter = new Intl.DateTimeFormat("en-CA", {
9
- timeZone,
10
- year: "numeric",
11
- month: "2-digit",
12
- day: "2-digit",
13
- hour: "2-digit",
14
- minute: "2-digit",
15
- second: "2-digit",
16
- hour12: false,
17
- });
18
- const parts = formatter.formatToParts(new Date(value));
19
- const values = new Map(parts.map((part) => [part.type, part.value]));
20
- return [
21
- `${values.get("year")}-${values.get("month")}-${values.get("day")}`,
22
- `${values.get("hour")}:${values.get("minute")}:${values.get("second")}`,
23
- `[${timeZone}]`,
24
- ].join(" ");
25
- }