@bd7pil/ocrc 0.9.0
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/LICENSE +34 -0
- package/README.md +330 -0
- package/dist/cli/host-backends.js +216 -0
- package/dist/cli/host-backends.js.map +1 -0
- package/dist/cli/host.js +185 -0
- package/dist/cli/host.js.map +1 -0
- package/dist/cli/index.js +67 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.js +68 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/install.js +220 -0
- package/dist/cli/install.js.map +1 -0
- package/dist/cli/pair.js +11 -0
- package/dist/cli/pair.js.map +1 -0
- package/dist/cli/uninstall.js +75 -0
- package/dist/cli/uninstall.js.map +1 -0
- package/dist/connectivity/auth/cf-access.js +20 -0
- package/dist/connectivity/auth/cf-access.js.map +1 -0
- package/dist/connectivity/auth/index.js +2 -0
- package/dist/connectivity/auth/index.js.map +1 -0
- package/dist/connectivity/auth/select.js +19 -0
- package/dist/connectivity/auth/select.js.map +1 -0
- package/dist/connectivity/auth/token.js +157 -0
- package/dist/connectivity/auth/token.js.map +1 -0
- package/dist/connectivity/exposure/cloudflared.js +77 -0
- package/dist/connectivity/exposure/cloudflared.js.map +1 -0
- package/dist/connectivity/exposure/index.js +2 -0
- package/dist/connectivity/exposure/index.js.map +1 -0
- package/dist/connectivity/exposure/providers.js +49 -0
- package/dist/connectivity/exposure/providers.js.map +1 -0
- package/dist/connectivity/pairing.js +29 -0
- package/dist/connectivity/pairing.js.map +1 -0
- package/dist/core/agent/acp-backend.js +539 -0
- package/dist/core/agent/acp-backend.js.map +1 -0
- package/dist/core/agent/acp-connect.js +92 -0
- package/dist/core/agent/acp-connect.js.map +1 -0
- package/dist/core/agent/acp-normalizer.js +81 -0
- package/dist/core/agent/acp-normalizer.js.map +1 -0
- package/dist/core/agent/acp-replay.js +89 -0
- package/dist/core/agent/acp-replay.js.map +1 -0
- package/dist/core/agent/acp-store.js +129 -0
- package/dist/core/agent/acp-store.js.map +1 -0
- package/dist/core/agent/backend.js +2 -0
- package/dist/core/agent/backend.js.map +1 -0
- package/dist/core/agent/diff-util.js +70 -0
- package/dist/core/agent/diff-util.js.map +1 -0
- package/dist/core/agent/event.js +12 -0
- package/dist/core/agent/event.js.map +1 -0
- package/dist/core/agent/opencode-backend.js +598 -0
- package/dist/core/agent/opencode-backend.js.map +1 -0
- package/dist/core/agent/opencode-normalizer.js +69 -0
- package/dist/core/agent/opencode-normalizer.js.map +1 -0
- package/dist/core/agent/registry.js +55 -0
- package/dist/core/agent/registry.js.map +1 -0
- package/dist/core/agent/v2-backend.js +176 -0
- package/dist/core/agent/v2-backend.js.map +1 -0
- package/dist/core/card-bus.js +74 -0
- package/dist/core/card-bus.js.map +1 -0
- package/dist/core/channels.js +95 -0
- package/dist/core/channels.js.map +1 -0
- package/dist/core/history.js +91 -0
- package/dist/core/history.js.map +1 -0
- package/dist/core/list-files.js +50 -0
- package/dist/core/list-files.js.map +1 -0
- package/dist/core/opencode-events.js +29 -0
- package/dist/core/opencode-events.js.map +1 -0
- package/dist/core/primary-election.js +91 -0
- package/dist/core/primary-election.js.map +1 -0
- package/dist/core/push.js +152 -0
- package/dist/core/push.js.map +1 -0
- package/dist/core/relay.js +400 -0
- package/dist/core/relay.js.map +1 -0
- package/dist/core/scheduler.js +150 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/core/state.js +222 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/stream-accumulator.js +76 -0
- package/dist/core/stream-accumulator.js.map +1 -0
- package/dist/core/structured-card.js +2 -0
- package/dist/core/structured-card.js.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/opencode/global-events.js +57 -0
- package/dist/opencode/global-events.js.map +1 -0
- package/dist/opencode/list-sessions.js +122 -0
- package/dist/opencode/list-sessions.js.map +1 -0
- package/dist/opencode/submit.js +39 -0
- package/dist/opencode/submit.js.map +1 -0
- package/dist/opencode/workspaces.js +51 -0
- package/dist/opencode/workspaces.js.map +1 -0
- package/dist/plugin/config.js +128 -0
- package/dist/plugin/config.js.map +1 -0
- package/dist/plugin/control-plane.js +133 -0
- package/dist/plugin/control-plane.js.map +1 -0
- package/dist/plugin/entry.js +409 -0
- package/dist/plugin/entry.js.map +1 -0
- package/dist/plugin/v2/event-map.js +193 -0
- package/dist/plugin/v2/event-map.js.map +1 -0
- package/dist/plugin/v2/types.js +15 -0
- package/dist/plugin/v2/types.js.map +1 -0
- package/dist/transport/interface.js +2 -0
- package/dist/transport/interface.js.map +1 -0
- package/dist/transport/telegram/esc.js +9 -0
- package/dist/transport/telegram/esc.js.map +1 -0
- package/dist/transport/telegram/handlers/info-commands.js +101 -0
- package/dist/transport/telegram/handlers/info-commands.js.map +1 -0
- package/dist/transport/telegram/handlers.js +1348 -0
- package/dist/transport/telegram/handlers.js.map +1 -0
- package/dist/transport/telegram/i18n/en.js +538 -0
- package/dist/transport/telegram/i18n/en.js.map +1 -0
- package/dist/transport/telegram/i18n/index.js +83 -0
- package/dist/transport/telegram/i18n/index.js.map +1 -0
- package/dist/transport/telegram/i18n/zh.js +538 -0
- package/dist/transport/telegram/i18n/zh.js.map +1 -0
- package/dist/transport/telegram/index.js +792 -0
- package/dist/transport/telegram/index.js.map +1 -0
- package/dist/transport/telegram/main-keyboard.js +36 -0
- package/dist/transport/telegram/main-keyboard.js.map +1 -0
- package/dist/transport/telegram/managers/abort-suppression-manager.js +33 -0
- package/dist/transport/telegram/managers/abort-suppression-manager.js.map +1 -0
- package/dist/transport/telegram/managers/external-input-suppression-manager.js +51 -0
- package/dist/transport/telegram/managers/external-input-suppression-manager.js.map +1 -0
- package/dist/transport/telegram/managers/interaction-manager.js +254 -0
- package/dist/transport/telegram/managers/interaction-manager.js.map +1 -0
- package/dist/transport/telegram/managers/permission-manager.js +242 -0
- package/dist/transport/telegram/managers/permission-manager.js.map +1 -0
- package/dist/transport/telegram/managers/question-manager.js +191 -0
- package/dist/transport/telegram/managers/question-manager.js.map +1 -0
- package/dist/transport/telegram/managers/rename-manager.js +56 -0
- package/dist/transport/telegram/managers/rename-manager.js.map +1 -0
- package/dist/transport/telegram/menus.js +77 -0
- package/dist/transport/telegram/menus.js.map +1 -0
- package/dist/transport/telegram/permission-flow.js +156 -0
- package/dist/transport/telegram/permission-flow.js.map +1 -0
- package/dist/transport/telegram/question-flow.js +224 -0
- package/dist/transport/telegram/question-flow.js.map +1 -0
- package/dist/transport/telegram/render/block-parser.js +365 -0
- package/dist/transport/telegram/render/block-parser.js.map +1 -0
- package/dist/transport/telegram/render/block-plain-text.js +87 -0
- package/dist/transport/telegram/render/block-plain-text.js.map +1 -0
- package/dist/transport/telegram/render/block-splitter.js +152 -0
- package/dist/transport/telegram/render/block-splitter.js.map +1 -0
- package/dist/transport/telegram/render/chunker.js +71 -0
- package/dist/transport/telegram/render/chunker.js.map +1 -0
- package/dist/transport/telegram/render/limits.js +13 -0
- package/dist/transport/telegram/render/limits.js.map +1 -0
- package/dist/transport/telegram/render/markdown-normalizer.js +102 -0
- package/dist/transport/telegram/render/markdown-normalizer.js.map +1 -0
- package/dist/transport/telegram/render/markdown-to-telegram-v2.js +116 -0
- package/dist/transport/telegram/render/markdown-to-telegram-v2.js.map +1 -0
- package/dist/transport/telegram/render/part-signature.js +14 -0
- package/dist/transport/telegram/render/part-signature.js.map +1 -0
- package/dist/transport/telegram/render/pipeline.js +23 -0
- package/dist/transport/telegram/render/pipeline.js.map +1 -0
- package/dist/transport/telegram/render/rich-blocks.js +311 -0
- package/dist/transport/telegram/render/rich-blocks.js.map +1 -0
- package/dist/transport/telegram/render/text-splitter.js +64 -0
- package/dist/transport/telegram/render/text-splitter.js.map +1 -0
- package/dist/transport/telegram/render/types.js +2 -0
- package/dist/transport/telegram/render/types.js.map +1 -0
- package/dist/transport/telegram/renderer.js +305 -0
- package/dist/transport/telegram/renderer.js.map +1 -0
- package/dist/transport/telegram/streaming/duration-formatter.js +68 -0
- package/dist/transport/telegram/streaming/duration-formatter.js.map +1 -0
- package/dist/transport/telegram/streaming/finalize-assistant-response.js +37 -0
- package/dist/transport/telegram/streaming/finalize-assistant-response.js.map +1 -0
- package/dist/transport/telegram/streaming/response-streamer.js +400 -0
- package/dist/transport/telegram/streaming/response-streamer.js.map +1 -0
- package/dist/transport/telegram/streaming/running-tool-tracker.js +150 -0
- package/dist/transport/telegram/streaming/running-tool-tracker.js.map +1 -0
- package/dist/transport/telegram/streaming/stream-throttle.js +57 -0
- package/dist/transport/telegram/streaming/stream-throttle.js.map +1 -0
- package/dist/transport/telegram/streaming/tool-call-streamer.js +390 -0
- package/dist/transport/telegram/streaming/tool-call-streamer.js.map +1 -0
- package/dist/transport/telegram/streaming-render.js +182 -0
- package/dist/transport/telegram/streaming-render.js.map +1 -0
- package/dist/transport/telegram/types/interaction.js +2 -0
- package/dist/transport/telegram/types/interaction.js.map +1 -0
- package/dist/transport/telegram/types/model.js +25 -0
- package/dist/transport/telegram/types/model.js.map +1 -0
- package/dist/transport/telegram/types/permission.js +2 -0
- package/dist/transport/telegram/types/permission.js.map +1 -0
- package/dist/transport/telegram/types/question.js +2 -0
- package/dist/transport/telegram/types/question.js.map +1 -0
- package/dist/transport/telegram/types/rename.js +2 -0
- package/dist/transport/telegram/types/rename.js.map +1 -0
- package/dist/transport/telegram/types/scheduled-task.js +23 -0
- package/dist/transport/telegram/types/scheduled-task.js.map +1 -0
- package/dist/transport/telegram/types/session.js +2 -0
- package/dist/transport/telegram/types/session.js.map +1 -0
- package/dist/transport/telegram/ui.js +28 -0
- package/dist/transport/telegram/ui.js.map +1 -0
- package/dist/transport/web/index.js +148 -0
- package/dist/transport/web/index.js.map +1 -0
- package/dist/transport/web/middleware/cf-access.js +104 -0
- package/dist/transport/web/middleware/cf-access.js.map +1 -0
- package/dist/transport/web/opencode-config.js +15 -0
- package/dist/transport/web/opencode-config.js.map +1 -0
- package/dist/transport/web/routes/abort.js +12 -0
- package/dist/transport/web/routes/abort.js.map +1 -0
- package/dist/transport/web/routes/approval.js +16 -0
- package/dist/transport/web/routes/approval.js.map +1 -0
- package/dist/transport/web/routes/catalog.js +11 -0
- package/dist/transport/web/routes/catalog.js.map +1 -0
- package/dist/transport/web/routes/channels.js +53 -0
- package/dist/transport/web/routes/channels.js.map +1 -0
- package/dist/transport/web/routes/commands.js +15 -0
- package/dist/transport/web/routes/commands.js.map +1 -0
- package/dist/transport/web/routes/context.js +17 -0
- package/dist/transport/web/routes/context.js.map +1 -0
- package/dist/transport/web/routes/controls.js +34 -0
- package/dist/transport/web/routes/controls.js.map +1 -0
- package/dist/transport/web/routes/create-session.js +21 -0
- package/dist/transport/web/routes/create-session.js.map +1 -0
- package/dist/transport/web/routes/diff.js +7 -0
- package/dist/transport/web/routes/diff.js.map +1 -0
- package/dist/transport/web/routes/files.js +22 -0
- package/dist/transport/web/routes/files.js.map +1 -0
- package/dist/transport/web/routes/logs.js +9 -0
- package/dist/transport/web/routes/logs.js.map +1 -0
- package/dist/transport/web/routes/m8.js +69 -0
- package/dist/transport/web/routes/m8.js.map +1 -0
- package/dist/transport/web/routes/mcp.js +7 -0
- package/dist/transport/web/routes/mcp.js.map +1 -0
- package/dist/transport/web/routes/message.js +32 -0
- package/dist/transport/web/routes/message.js.map +1 -0
- package/dist/transport/web/routes/overrides.js +20 -0
- package/dist/transport/web/routes/overrides.js.map +1 -0
- package/dist/transport/web/routes/question.js +32 -0
- package/dist/transport/web/routes/question.js.map +1 -0
- package/dist/transport/web/routes/rename.js +12 -0
- package/dist/transport/web/routes/rename.js.map +1 -0
- package/dist/transport/web/routes/schedules.js +28 -0
- package/dist/transport/web/routes/schedules.js.map +1 -0
- package/dist/transport/web/routes/session.js +20 -0
- package/dist/transport/web/routes/session.js.map +1 -0
- package/dist/transport/web/routes/sessions.js +25 -0
- package/dist/transport/web/routes/sessions.js.map +1 -0
- package/dist/transport/web/routes/subagents.js +11 -0
- package/dist/transport/web/routes/subagents.js.map +1 -0
- package/dist/transport/web/routes/suggestions.js +7 -0
- package/dist/transport/web/routes/suggestions.js.map +1 -0
- package/dist/transport/web/routes/todo.js +7 -0
- package/dist/transport/web/routes/todo.js.map +1 -0
- package/dist/transport/web/routes/version.js +5 -0
- package/dist/transport/web/routes/version.js.map +1 -0
- package/dist/transport/web/routes/workspaces.js +7 -0
- package/dist/transport/web/routes/workspaces.js.map +1 -0
- package/dist/transport/web/server.js +111 -0
- package/dist/transport/web/server.js.map +1 -0
- package/dist/transport/web/session-summary.js +46 -0
- package/dist/transport/web/session-summary.js.map +1 -0
- package/dist/transport/web/ws-hub.js +83 -0
- package/dist/transport/web/ws-hub.js.map +1 -0
- package/dist/utils/logger.js +105 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/markdown.js +129 -0
- package/dist/utils/markdown.js.map +1 -0
- package/dist/utils/paths.js +18 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/version.js +44 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +91 -0
- package/web/dist/_app/env.js +1 -0
- package/web/dist/_app/immutable/assets/0.DxN3RsuG.css +1 -0
- package/web/dist/_app/immutable/assets/2.BNuGfmZP.css +1 -0
- package/web/dist/_app/immutable/assets/3.CTDprntu.css +1 -0
- package/web/dist/_app/immutable/chunks/BUpwHpMJ.js +1 -0
- package/web/dist/_app/immutable/chunks/BjY4dxcB.js +2 -0
- package/web/dist/_app/immutable/chunks/C2FuqD3s.js +1 -0
- package/web/dist/_app/immutable/chunks/CUHGq08X.js +2 -0
- package/web/dist/_app/immutable/chunks/CWj6FrbW.js +1 -0
- package/web/dist/_app/immutable/chunks/ClvoqtGj.js +1 -0
- package/web/dist/_app/immutable/chunks/DIEDQ_hk.js +1 -0
- package/web/dist/_app/immutable/entry/app.BoRpcVMj.js +2 -0
- package/web/dist/_app/immutable/entry/start.Cj-PQNTq.js +1 -0
- package/web/dist/_app/immutable/nodes/0.y9YEms5p.js +3 -0
- package/web/dist/_app/immutable/nodes/1.CPhhiRM2.js +1 -0
- package/web/dist/_app/immutable/nodes/2.B_c84Mc9.js +1 -0
- package/web/dist/_app/immutable/nodes/3.ClTd0J3D.js +64 -0
- package/web/dist/_app/version.json +1 -0
- package/web/dist/apple-touch-icon.png +0 -0
- package/web/dist/favicon.png +0 -0
- package/web/dist/favicon.svg +17 -0
- package/web/dist/icon-192.png +0 -0
- package/web/dist/icon-512-maskable.png +0 -0
- package/web/dist/icon-512.png +0 -0
- package/web/dist/index.html +73 -0
- package/web/dist/manifest.webmanifest +28 -0
- package/web/dist/service-worker.js +1 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import { createStreamAccumulator } from './stream-accumulator.js';
|
|
2
|
+
import { singleBackendRegistry } from './agent/registry.js';
|
|
3
|
+
import { isEphemeralSession } from '../opencode/submit.js';
|
|
4
|
+
import { createLogger } from '../utils/logger.js';
|
|
5
|
+
const log = createLogger('relay');
|
|
6
|
+
const SUBMIT_MAX_RETRIES = 5;
|
|
7
|
+
const SUBMIT_RETRY_BASE_MS = 2000;
|
|
8
|
+
/** Node/undici transport error codes worth retrying. */
|
|
9
|
+
const NETWORK_ERROR_CODES = new Set([
|
|
10
|
+
'ECONNREFUSED', 'ECONNRESET', 'ENOTFOUND', 'ETIMEDOUT', 'EAI_AGAIN',
|
|
11
|
+
'EPIPE', 'EHOSTUNREACH', 'ENETUNREACH',
|
|
12
|
+
'UND_ERR_CONNECT_TIMEOUT', 'UND_ERR_HEADERS_TIMEOUT', 'UND_ERR_BODY_TIMEOUT', 'UND_ERR_SOCKET',
|
|
13
|
+
]);
|
|
14
|
+
function isNetworkError(err) {
|
|
15
|
+
// Prefer structured error codes (undici fetch failures carry them on cause).
|
|
16
|
+
const code = err.code ?? err.cause?.code;
|
|
17
|
+
if (code)
|
|
18
|
+
return NETWORK_ERROR_CODES.has(code);
|
|
19
|
+
// Message fallback, kept narrow — bare 'network'/'timeout' substrings would
|
|
20
|
+
// misclassify ordinary errors (e.g. "operation timeout from the model") and
|
|
21
|
+
// burn 5 retries on a non-transport failure.
|
|
22
|
+
const msg = err.message.toLowerCase();
|
|
23
|
+
return msg.includes('fetch failed') ||
|
|
24
|
+
msg.includes('econnrefused') ||
|
|
25
|
+
msg.includes('econnreset') ||
|
|
26
|
+
msg.includes('enotfound') ||
|
|
27
|
+
msg.includes('etimedout') ||
|
|
28
|
+
msg.includes('socket hang up') ||
|
|
29
|
+
msg.includes('network timeout');
|
|
30
|
+
}
|
|
31
|
+
async function submitWithRetry(backend, sessionId, opts) {
|
|
32
|
+
for (let i = 0; i < SUBMIT_MAX_RETRIES; i++) {
|
|
33
|
+
try {
|
|
34
|
+
await backend.prompt(sessionId, opts);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
const e = err;
|
|
39
|
+
if (opts.signal?.aborted)
|
|
40
|
+
throw e;
|
|
41
|
+
if (i < SUBMIT_MAX_RETRIES - 1 && isNetworkError(e)) {
|
|
42
|
+
const delay = SUBMIT_RETRY_BASE_MS * Math.pow(2, i);
|
|
43
|
+
log.warn(`submit failed (attempt ${i + 1}/${SUBMIT_MAX_RETRIES}), retry in ${delay}ms: ${e.message}`);
|
|
44
|
+
await delayOrAbort(delay, opts.signal);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
throw e;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function delayOrAbort(ms, signal) {
|
|
53
|
+
if (!signal || signal.aborted)
|
|
54
|
+
return Promise.resolve();
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
const t = setTimeout(() => {
|
|
57
|
+
signal.removeEventListener('abort', onAbort);
|
|
58
|
+
resolve();
|
|
59
|
+
}, ms);
|
|
60
|
+
const onAbort = () => { clearTimeout(t); reject(new Error('aborted')); };
|
|
61
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async function pickSessionFallback(backend) {
|
|
65
|
+
const sessions = await backend.listSessions();
|
|
66
|
+
if (sessions.length === 0)
|
|
67
|
+
throw new Error('No opencode sessions found — use /workspaces + /new in Telegram (or the web UI) to start one in a project');
|
|
68
|
+
// Prefer root sessions (no parentID) over child/subagent sessions, then most
|
|
69
|
+
// recently active (updatedAt over createdAt) — avoids connecting to a completed
|
|
70
|
+
// subagent session.
|
|
71
|
+
const sorted = [...sessions].sort((a, b) => {
|
|
72
|
+
const aIsChild = !!a.parentID;
|
|
73
|
+
const bIsChild = !!b.parentID;
|
|
74
|
+
if (aIsChild !== bIsChild)
|
|
75
|
+
return aIsChild ? 1 : -1;
|
|
76
|
+
return (b.updatedAt ?? b.createdAt ?? 0) - (a.updatedAt ?? a.createdAt ?? 0);
|
|
77
|
+
});
|
|
78
|
+
return sorted[0].id;
|
|
79
|
+
}
|
|
80
|
+
export function createRelay(deps) {
|
|
81
|
+
const registry = deps.registry ?? singleBackendRegistry(deps.backend ?? (() => { throw new Error('createRelay needs registry or backend'); })());
|
|
82
|
+
/** Per-session response contexts, keyed by session id. */
|
|
83
|
+
const pluginSessions = new Map();
|
|
84
|
+
/**
|
|
85
|
+
* Per-session turn queues. Two concurrent messages for the same session used
|
|
86
|
+
* to clobber each other (the second cleanupPluginSession killed the first
|
|
87
|
+
* turn's timer/abort/accumulator mid-stream); now a later message waits until
|
|
88
|
+
* the previous turn's ctx finishes (idle / error / abort / timeout).
|
|
89
|
+
*/
|
|
90
|
+
const turnQueues = new Map();
|
|
91
|
+
/** Last user text per session — feeds Tier2 follow-up generation at finalize. */
|
|
92
|
+
const lastUserText = new Map();
|
|
93
|
+
function cleanupPluginSession(sessionId) {
|
|
94
|
+
const ctx = pluginSessions.get(sessionId);
|
|
95
|
+
if (ctx) {
|
|
96
|
+
clearTimeout(ctx.timer);
|
|
97
|
+
pluginSessions.delete(sessionId);
|
|
98
|
+
if (ctx.abortKey)
|
|
99
|
+
deps.state.setActiveAbort(ctx.abortKey, undefined);
|
|
100
|
+
ctx.releaseTurn?.();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** The session a message will target, normalized — the turn-queue key. */
|
|
104
|
+
function queueKeyFor(msg) {
|
|
105
|
+
const raw = msg.sessionId
|
|
106
|
+
?? deps.state.getPinnedSessionId()
|
|
107
|
+
?? (deps.tuiVisible ? deps.state.getTuiSelectedSession() : undefined)
|
|
108
|
+
?? deps.state.getLastSessionId();
|
|
109
|
+
return raw ? deps.state.normalizeSessionId(raw) : '__default__';
|
|
110
|
+
}
|
|
111
|
+
const relay = async function handleIncoming(msg) {
|
|
112
|
+
// Chain onto the previous turn for this session; errors never block the
|
|
113
|
+
// queue (the gate resolves in a finally) and the map entry is dropped once
|
|
114
|
+
// the chain settles so it can't grow unbounded.
|
|
115
|
+
const key = queueKeyFor(msg);
|
|
116
|
+
const prev = turnQueues.get(key) ?? Promise.resolve();
|
|
117
|
+
let release;
|
|
118
|
+
const gate = new Promise((res) => { release = res; });
|
|
119
|
+
const tail = prev.then(() => gate);
|
|
120
|
+
turnQueues.set(key, tail);
|
|
121
|
+
void tail.then(() => { if (turnQueues.get(key) === tail)
|
|
122
|
+
turnQueues.delete(key); });
|
|
123
|
+
await prev;
|
|
124
|
+
// Released by cleanupPluginSession once a ctx is installed; if the turn
|
|
125
|
+
// ends before that (error path), release here so the queue never wedges.
|
|
126
|
+
let ctxInstalled = false;
|
|
127
|
+
try {
|
|
128
|
+
await runTurn(msg, (ctx) => {
|
|
129
|
+
ctx.releaseTurn = release;
|
|
130
|
+
ctxInstalled = true;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
if (!ctxInstalled)
|
|
135
|
+
release();
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
async function runTurn(msg, onCtxInstalled) {
|
|
139
|
+
const ac = new AbortController();
|
|
140
|
+
const timer = setTimeout(() => ac.abort(), deps.chatTimeoutMs);
|
|
141
|
+
// Registered under a provisional key until the real session id is known.
|
|
142
|
+
// After resolution the controller stays mapped under BOTH keys until the
|
|
143
|
+
// turn ends (cleanupPluginSession / catch below) so an abort by either id
|
|
144
|
+
// lands — clearing the provisional key at re-registration opened a window
|
|
145
|
+
// where /api/abort with the old id found nothing.
|
|
146
|
+
const provisionalKey = deps.state.getPinnedSessionId() ?? deps.state.getLastSessionId() ?? 'pending';
|
|
147
|
+
let sessionId = provisionalKey;
|
|
148
|
+
deps.state.setActiveAbort(provisionalKey, ac);
|
|
149
|
+
try {
|
|
150
|
+
const nextAgent = deps.state.getNextAgent();
|
|
151
|
+
const nextModel = deps.state.getNextModel();
|
|
152
|
+
const pinnedSession = deps.state.getPinnedSessionId();
|
|
153
|
+
const tuiSession = deps.tuiVisible ? deps.state.getTuiSelectedSession() : undefined;
|
|
154
|
+
const lastSession = deps.state.getLastSessionId();
|
|
155
|
+
// A per-message target (msg.sessionId, set by web to the session the UI is
|
|
156
|
+
// viewing) wins over the global pinned/tui/last fallback, so web and
|
|
157
|
+
// Telegram can drive different sessions at once. Any persisted target may
|
|
158
|
+
// point at a deleted session — validate before submitting, otherwise
|
|
159
|
+
// submitWithRetry burns 5 retries against a 404. A fresh
|
|
160
|
+
// pickSessionFallback() result is trusted as-is.
|
|
161
|
+
// Normalize to full ID so the cardBus / pluginSessions use the same key as
|
|
162
|
+
// the opencode event hook (which always delivers full session IDs).
|
|
163
|
+
let resolvedId = (msg.sessionId ? deps.state.normalizeSessionId(msg.sessionId) : undefined)
|
|
164
|
+
?? (pinnedSession ? deps.state.normalizeSessionId(pinnedSession) : undefined)
|
|
165
|
+
?? (tuiSession ? deps.state.normalizeSessionId(tuiSession) : undefined)
|
|
166
|
+
?? (lastSession ? deps.state.normalizeSessionId(lastSession) : undefined);
|
|
167
|
+
// Pick the backend: a known target routes to its owning backend; a brand-new
|
|
168
|
+
// turn (no resolvable target) goes to the active backend.
|
|
169
|
+
let backend = resolvedId ? registry.forSession(resolvedId) : registry.active();
|
|
170
|
+
if (resolvedId) {
|
|
171
|
+
// Key any error from here on to the target session, not the provisional key.
|
|
172
|
+
sessionId = resolvedId;
|
|
173
|
+
// hasSession throws on transport errors — let it propagate to the catch
|
|
174
|
+
// (error card, turn ends) instead of misrouting to another session.
|
|
175
|
+
if (!(await backend.hasSession(resolvedId))) {
|
|
176
|
+
log.warn(`target session ${resolvedId.slice(-8)} no longer exists, falling back to newest`);
|
|
177
|
+
resolvedId = undefined;
|
|
178
|
+
sessionId = provisionalKey;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (!resolvedId) {
|
|
182
|
+
backend = registry.active();
|
|
183
|
+
resolvedId = await pickSessionFallback(backend);
|
|
184
|
+
}
|
|
185
|
+
// Tag ownership so future turns/reads for this session route consistently.
|
|
186
|
+
registry.tag(resolvedId, backend.id);
|
|
187
|
+
log.info(`submitting to session=${resolvedId.slice(-8)} backend=${backend.id}, agent=${nextAgent ?? 'default'}, model=${nextModel ? `${nextModel.providerID}/${nextModel.modelID}` : 'default'}`);
|
|
188
|
+
if (deps.tuiVisible && backend.capabilities.tuiSelect) {
|
|
189
|
+
await backend.selectTuiSession?.(resolvedId, ac.signal);
|
|
190
|
+
}
|
|
191
|
+
await submitWithRetry(backend, resolvedId, {
|
|
192
|
+
text: msg.text,
|
|
193
|
+
agent: nextAgent,
|
|
194
|
+
model: nextModel,
|
|
195
|
+
images: msg.images,
|
|
196
|
+
signal: ac.signal,
|
|
197
|
+
});
|
|
198
|
+
sessionId = resolvedId;
|
|
199
|
+
lastUserText.set(sessionId, msg.text);
|
|
200
|
+
deps.state.setLastSessionId(sessionId);
|
|
201
|
+
deps.state.setActiveAbort(sessionId, ac);
|
|
202
|
+
// Keep the provisional key mapped to the same controller until the turn
|
|
203
|
+
// ends — /api/abort with either id must find it during the whole run.
|
|
204
|
+
// It's cleared in cleanupPluginSession / the catch below.
|
|
205
|
+
// Publish thinking + user cards now that sessionId is known. The user card
|
|
206
|
+
// id is derived from the incoming messageId so a web client's optimistic
|
|
207
|
+
// user card (same id) reconciles in place instead of duplicating.
|
|
208
|
+
deps.cardBus.publish({ kind: 'thinking', sessionId, showStop: true });
|
|
209
|
+
deps.cardBus.publish({ kind: 'user', sessionId, text: msg.text, ts: Date.now(), id: `user:${msg.messageId}`, origin: msg.origin });
|
|
210
|
+
// The response arrives asynchronously via handleEvent() (opencode plugin
|
|
211
|
+
// event hook). Store per-session context and return; finalization happens
|
|
212
|
+
// on session.idle. Clean up the ctx + abort if this run is cancelled.
|
|
213
|
+
cleanupPluginSession(sessionId);
|
|
214
|
+
const ctx = {
|
|
215
|
+
sessionId,
|
|
216
|
+
cardId: `turn:${sessionId}:${Date.now()}`,
|
|
217
|
+
acc: createStreamAccumulator(),
|
|
218
|
+
processedPartIds: new Set(),
|
|
219
|
+
partTextAcc: new Map(),
|
|
220
|
+
signal: ac.signal,
|
|
221
|
+
timer,
|
|
222
|
+
abortKey: sessionId !== provisionalKey ? provisionalKey : undefined,
|
|
223
|
+
};
|
|
224
|
+
onCtxInstalled(ctx);
|
|
225
|
+
pluginSessions.set(sessionId, ctx);
|
|
226
|
+
ac.signal.addEventListener('abort', () => {
|
|
227
|
+
cleanupPluginSession(sessionId);
|
|
228
|
+
deps.state.setActiveAbort(sessionId, undefined);
|
|
229
|
+
}, { once: true });
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
const e = err;
|
|
234
|
+
if (ac.signal.aborted) {
|
|
235
|
+
log.info('relay aborted (timeout or user stop) — push will notify on session idle');
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
log.warn('relay error', e.message);
|
|
239
|
+
deps.cardBus.publish({ kind: 'error', sessionId, message: e.message });
|
|
240
|
+
}
|
|
241
|
+
clearTimeout(timer);
|
|
242
|
+
deps.state.setActiveAbort(sessionId, undefined);
|
|
243
|
+
if (sessionId !== provisionalKey)
|
|
244
|
+
deps.state.setActiveAbort(provisionalKey, undefined);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async function publishAssistantCard(params) {
|
|
248
|
+
const { sessionId, acc, cardId } = params;
|
|
249
|
+
let { assistantMessageId } = params;
|
|
250
|
+
const backend = registry.forSession(sessionId);
|
|
251
|
+
let blocks = acc.finalize();
|
|
252
|
+
if (blocks.length === 0 && assistantMessageId) {
|
|
253
|
+
blocks = await backend.getMessageBlocks(sessionId, assistantMessageId);
|
|
254
|
+
}
|
|
255
|
+
const meta = await backend.getSessionMeta(sessionId);
|
|
256
|
+
if (meta.cost !== undefined)
|
|
257
|
+
deps.state.setSessionCost(sessionId, meta.cost);
|
|
258
|
+
if (blocks.length === 0)
|
|
259
|
+
blocks = [{ type: 'text', text: '(empty response)' }];
|
|
260
|
+
log.info(`relay: publishing assistant card for ${sessionId}, blocks=${blocks.length}`);
|
|
261
|
+
deps.cardBus.publish({ kind: 'assistant', sessionId, blocks, meta, id: cardId });
|
|
262
|
+
// Mark delivery so the push engine doesn't also fire a "Session finished"
|
|
263
|
+
// notification for a session the user just watched complete.
|
|
264
|
+
deps.state.markAssistantDelivered(sessionId);
|
|
265
|
+
// Tier2 suggested follow-ups: generate after the exchange completes, from
|
|
266
|
+
// the last user message + this reply. Fire-and-forget, zero history
|
|
267
|
+
// pollution (runs in a throwaway session), result lands in state for the
|
|
268
|
+
// web client to pick up.
|
|
269
|
+
const userText = lastUserText.get(sessionId);
|
|
270
|
+
lastUserText.delete(sessionId);
|
|
271
|
+
const suggest = backend.suggestFollowUps;
|
|
272
|
+
if (suggest && userText) {
|
|
273
|
+
const assistantText = blocks
|
|
274
|
+
.filter((b) => b.type === 'text')
|
|
275
|
+
.map((b) => b.text ?? '')
|
|
276
|
+
.join('\n')
|
|
277
|
+
.slice(-1500);
|
|
278
|
+
void suggest
|
|
279
|
+
.call(backend, sessionId, { user: userText, assistant: assistantText })
|
|
280
|
+
.then((items) => { if (items.length > 0)
|
|
281
|
+
deps.state.setSessionSuggestions(sessionId, items); })
|
|
282
|
+
.catch(() => { });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* handleEvent — opencode plugin event dispatch.
|
|
287
|
+
* Receives individual events from the opencode plugin event hook and drives
|
|
288
|
+
* streaming/finalization for the in-flight session (message.part.updated,
|
|
289
|
+
* message.part.delta, session.idle, session.error).
|
|
290
|
+
*/
|
|
291
|
+
relay.handleEvent = async function handleEvent(e) {
|
|
292
|
+
const sid = e.sessionId;
|
|
293
|
+
let ctx = pluginSessions.get(sid);
|
|
294
|
+
// Adopt externally-initiated turns: a streaming event arrives for a session
|
|
295
|
+
// we never submitted to (a TUI/command turn), so there's no context — create
|
|
296
|
+
// one so it streams/renders in Web/Telegram like any other turn. The ctx gets
|
|
297
|
+
// a REAL timeout + abort registration: without them a leaked turn (no idle
|
|
298
|
+
// ever arrives) lived forever, wedged hasActiveGeneration(), and the next
|
|
299
|
+
// turn for the session reused its stale accumulator. Aborting only stops our
|
|
300
|
+
// mirroring — the TUI turn itself is owned by opencode; the card's stop
|
|
301
|
+
// button calls the backend's abort directly.
|
|
302
|
+
// Internal side calls (Tier2 suggestion generation) stream through the
|
|
303
|
+
// same hook; never adopt their turns into user-facing feeds.
|
|
304
|
+
if (isEphemeralSession(sid))
|
|
305
|
+
return;
|
|
306
|
+
if (!ctx && (e.kind === 'part' || e.kind === 'delta')) {
|
|
307
|
+
const ac = new AbortController();
|
|
308
|
+
ctx = {
|
|
309
|
+
sessionId: sid,
|
|
310
|
+
cardId: `turn:${sid}:${Date.now()}`,
|
|
311
|
+
acc: createStreamAccumulator(),
|
|
312
|
+
processedPartIds: new Set(),
|
|
313
|
+
partTextAcc: new Map(),
|
|
314
|
+
signal: ac.signal,
|
|
315
|
+
timer: setTimeout(() => ac.abort(), deps.chatTimeoutMs),
|
|
316
|
+
};
|
|
317
|
+
pluginSessions.set(sid, ctx);
|
|
318
|
+
deps.state.setActiveAbort(sid, ac);
|
|
319
|
+
ac.signal.addEventListener('abort', () => {
|
|
320
|
+
cleanupPluginSession(sid);
|
|
321
|
+
deps.state.setActiveAbort(sid, undefined);
|
|
322
|
+
}, { once: true });
|
|
323
|
+
}
|
|
324
|
+
if (e.kind === 'part') {
|
|
325
|
+
if (!ctx)
|
|
326
|
+
return;
|
|
327
|
+
if (!ctx.assistantMessageId && e.messageId)
|
|
328
|
+
ctx.assistantMessageId = e.messageId;
|
|
329
|
+
const part = e.part;
|
|
330
|
+
const isNewPart = !ctx.processedPartIds.has(part.id);
|
|
331
|
+
if (isNewPart)
|
|
332
|
+
ctx.processedPartIds.add(part.id);
|
|
333
|
+
const input = { id: part.id, type: part.type, text: part.text, tool: part.tool, args: part.args, status: part.status };
|
|
334
|
+
const blocks = ctx.acc.update([input]);
|
|
335
|
+
if (part.type === 'text' && isNewPart && typeof part.text === 'string') {
|
|
336
|
+
ctx.partTextAcc.set(part.id, part.text);
|
|
337
|
+
}
|
|
338
|
+
if (!ctx.signal.aborted) {
|
|
339
|
+
deps.cardBus.publish({ kind: 'streaming', sessionId: sid, blocks, id: ctx.cardId });
|
|
340
|
+
}
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (e.kind === 'delta') {
|
|
344
|
+
if (!ctx)
|
|
345
|
+
return;
|
|
346
|
+
const prev = ctx.partTextAcc.get(e.partId) ?? '';
|
|
347
|
+
const fullText = prev + e.text;
|
|
348
|
+
ctx.partTextAcc.set(e.partId, fullText);
|
|
349
|
+
if (!ctx.signal.aborted) {
|
|
350
|
+
const blocks = ctx.acc.update([{ id: e.partId, type: 'text', text: fullText }]);
|
|
351
|
+
deps.cardBus.publish({ kind: 'streaming', sessionId: sid, blocks, id: ctx.cardId });
|
|
352
|
+
}
|
|
353
|
+
if (!ctx.assistantMessageId && e.messageId)
|
|
354
|
+
ctx.assistantMessageId = e.messageId;
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (e.kind === 'idle') {
|
|
358
|
+
if (ctx) {
|
|
359
|
+
log.info(`[plugin] session idle: ${sid.slice(-8)}, finalizing`);
|
|
360
|
+
// Capture acc/msgId locally: cleanupPluginSession() runs synchronously
|
|
361
|
+
// below, and a new message for the same session would install a fresh ctx
|
|
362
|
+
// with a fresh accumulator — so this deferred finalize must read the
|
|
363
|
+
// snapshot it captured, not pluginSessions.get(sid).
|
|
364
|
+
const acc = ctx.acc;
|
|
365
|
+
const msgId = ctx.assistantMessageId;
|
|
366
|
+
const cardId = ctx.cardId;
|
|
367
|
+
setTimeout(async () => {
|
|
368
|
+
try {
|
|
369
|
+
await publishAssistantCard({ sessionId: sid, acc, assistantMessageId: msgId, cardId });
|
|
370
|
+
}
|
|
371
|
+
catch (err) {
|
|
372
|
+
log.error(`[plugin] publishAssistantCard failed`, err);
|
|
373
|
+
}
|
|
374
|
+
}, 0);
|
|
375
|
+
cleanupPluginSession(sid);
|
|
376
|
+
deps.state.setActiveAbort(sid, undefined);
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
log.info(`[plugin] session idle (no ctx): ${sid.slice(-8)}`);
|
|
380
|
+
deps.cardBus.publish({ kind: 'status', sessionId: sid, fields: { status: 'idle' } });
|
|
381
|
+
}
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (e.kind === 'error') {
|
|
385
|
+
log.warn(`[plugin] session error${ctx ? '' : ' (no ctx)'}: ${e.message}`);
|
|
386
|
+
deps.cardBus.publish({ kind: 'error', sessionId: sid, message: e.message });
|
|
387
|
+
if (ctx) {
|
|
388
|
+
cleanupPluginSession(sid);
|
|
389
|
+
deps.state.setActiveAbort(sid, undefined);
|
|
390
|
+
}
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (e.kind === 'notice') {
|
|
394
|
+
deps.cardBus.publish({ kind: 'info', sessionId: sid, title: e.title, sections: [{ body: e.body }] });
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
return relay;
|
|
399
|
+
}
|
|
400
|
+
//# sourceMappingURL=relay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay.js","sourceRoot":"","sources":["../../src/core/relay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// scheduler.ts — cross-channel scheduled prompts (P2b-M7).
|
|
2
|
+
//
|
|
3
|
+
// The engine is deliberately channel-free: it persists schedules to a JSON
|
|
4
|
+
// file, ticks on an interval, and dispatches due prompts as ordinary
|
|
5
|
+
// IncomingMessages through the relay — so Telegram, Web and any future
|
|
6
|
+
// channel are all just front-ends over the same store.
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from 'node:fs';
|
|
8
|
+
import { dirname } from 'node:path';
|
|
9
|
+
import { createLogger } from '../utils/logger.js';
|
|
10
|
+
const log = createLogger('scheduler');
|
|
11
|
+
const TICK_MS = 30_000;
|
|
12
|
+
/** Validate a spec at the boundary (TG wizard / web API both funnel here). */
|
|
13
|
+
export function normalizeSpec(raw) {
|
|
14
|
+
const r = raw;
|
|
15
|
+
if (!r || typeof r !== 'object')
|
|
16
|
+
return null;
|
|
17
|
+
if (r.kind === 'every') {
|
|
18
|
+
const m = Number(r.minutes);
|
|
19
|
+
if (!Number.isFinite(m) || m <= 0)
|
|
20
|
+
return null;
|
|
21
|
+
return { kind: 'every', minutes: Math.max(1, Math.floor(m)) };
|
|
22
|
+
}
|
|
23
|
+
if (r.kind === 'daily') {
|
|
24
|
+
const t = typeof r.time === 'string' ? r.time : '';
|
|
25
|
+
if (!/^([01]\d|2[0-3]):[0-5]\d$/.test(t))
|
|
26
|
+
return null;
|
|
27
|
+
return { kind: 'daily', time: t };
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
function isDue(s, now) {
|
|
32
|
+
if (!s.enabled)
|
|
33
|
+
return false;
|
|
34
|
+
if (s.spec.kind === 'every') {
|
|
35
|
+
if (!s.lastRunAt)
|
|
36
|
+
return true; // first tick after creation fires immediately
|
|
37
|
+
return now.getTime() - s.lastRunAt >= s.spec.minutes * 60_000;
|
|
38
|
+
}
|
|
39
|
+
// daily: due when the current HH:MM matches and we haven't run today yet
|
|
40
|
+
const [hh, mm] = s.spec.time.split(':').map(Number);
|
|
41
|
+
const due = now.getHours() === hh && now.getMinutes() >= mm && now.getMinutes() < mm + TICK_MS / 60_000;
|
|
42
|
+
if (!due)
|
|
43
|
+
return false;
|
|
44
|
+
const last = s.lastRunAt ? new Date(s.lastRunAt) : undefined;
|
|
45
|
+
return !(last && last.toDateString() === now.toDateString());
|
|
46
|
+
}
|
|
47
|
+
export function createScheduler(opts) {
|
|
48
|
+
let store = { schedules: [] };
|
|
49
|
+
// ── persistence (atomic tmp+rename, same pattern as state.ts) ──
|
|
50
|
+
function load() {
|
|
51
|
+
if (!existsSync(opts.path))
|
|
52
|
+
return;
|
|
53
|
+
try {
|
|
54
|
+
const raw = JSON.parse(readFileSync(opts.path, 'utf-8'));
|
|
55
|
+
store.schedules = Array.isArray(raw.schedules) ? raw.schedules : [];
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
log.warn(`failed to load ${opts.path}: ${err.message}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
let writeQueued;
|
|
62
|
+
function persist() {
|
|
63
|
+
if (writeQueued)
|
|
64
|
+
clearTimeout(writeQueued);
|
|
65
|
+
writeQueued = setTimeout(() => {
|
|
66
|
+
try {
|
|
67
|
+
mkdirSync(dirname(opts.path), { recursive: true });
|
|
68
|
+
const tmp = `${opts.path}.tmp`;
|
|
69
|
+
writeFileSync(tmp, JSON.stringify({ schedules: store.schedules }, null, 2));
|
|
70
|
+
renameSync(tmp, opts.path);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
log.warn(`persist failed: ${err.message}`);
|
|
74
|
+
}
|
|
75
|
+
}, 100);
|
|
76
|
+
writeQueued.unref?.();
|
|
77
|
+
}
|
|
78
|
+
load();
|
|
79
|
+
let timer;
|
|
80
|
+
function tick(now = new Date()) {
|
|
81
|
+
const fired = [];
|
|
82
|
+
for (const s of store.schedules) {
|
|
83
|
+
if (!isDue(s, now))
|
|
84
|
+
continue;
|
|
85
|
+
s.lastRunAt = now.getTime();
|
|
86
|
+
fired.push(s);
|
|
87
|
+
const msg = {
|
|
88
|
+
userId: 'ocrc-scheduler',
|
|
89
|
+
chatId: 'ocrc-scheduler',
|
|
90
|
+
text: s.prompt,
|
|
91
|
+
messageId: `sched_${s.id}_${now.getTime()}`,
|
|
92
|
+
origin: 'scheduler',
|
|
93
|
+
};
|
|
94
|
+
void opts.dispatch(msg).catch((err) => log.error(`dispatch failed for ${s.id}`, err));
|
|
95
|
+
}
|
|
96
|
+
if (fired.length > 0)
|
|
97
|
+
persist();
|
|
98
|
+
return fired;
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
start() {
|
|
102
|
+
if (timer)
|
|
103
|
+
return;
|
|
104
|
+
timer = setInterval(() => tick(), opts.tickMs ?? TICK_MS);
|
|
105
|
+
timer.unref?.();
|
|
106
|
+
},
|
|
107
|
+
stop() {
|
|
108
|
+
if (timer)
|
|
109
|
+
clearInterval(timer);
|
|
110
|
+
timer = undefined;
|
|
111
|
+
},
|
|
112
|
+
tick,
|
|
113
|
+
list: () => [...store.schedules].sort((a, b) => b.createdAt - a.createdAt),
|
|
114
|
+
add(input) {
|
|
115
|
+
const spec = normalizeSpec(input.spec);
|
|
116
|
+
if (!spec || !input.prompt.trim())
|
|
117
|
+
return null;
|
|
118
|
+
const s = {
|
|
119
|
+
id: `sch_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`,
|
|
120
|
+
name: input.name?.trim() || input.prompt.trim().slice(0, 24),
|
|
121
|
+
prompt: input.prompt.trim(),
|
|
122
|
+
spec,
|
|
123
|
+
enabled: input.enabled ?? true,
|
|
124
|
+
createdAt: Date.now(),
|
|
125
|
+
};
|
|
126
|
+
store.schedules.push(s);
|
|
127
|
+
persist();
|
|
128
|
+
return s;
|
|
129
|
+
},
|
|
130
|
+
remove(id) {
|
|
131
|
+
const before = store.schedules.length;
|
|
132
|
+
store.schedules = store.schedules.filter((s) => s.id !== id);
|
|
133
|
+
const removed = store.schedules.length < before;
|
|
134
|
+
if (removed)
|
|
135
|
+
persist();
|
|
136
|
+
return removed;
|
|
137
|
+
},
|
|
138
|
+
setEnabled(id, enabled) {
|
|
139
|
+
const s = store.schedules.find((x) => x.id === id);
|
|
140
|
+
if (!s)
|
|
141
|
+
return undefined;
|
|
142
|
+
s.enabled = enabled;
|
|
143
|
+
if (!enabled)
|
|
144
|
+
delete s.lastRunAt;
|
|
145
|
+
persist();
|
|
146
|
+
return s;
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/core/scheduler.ts"],"names":[],"mappings":"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"}
|