@stage-labs/metro 0.1.0-beta.4 → 0.1.0-beta.41
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/api.js +131 -0
- package/dist/bedrock-eventstream.js +120 -0
- package/dist/bedrock-proxy.js +260 -0
- package/dist/bedrock-upstream.js +72 -0
- package/dist/bedrock.js +106 -0
- package/dist/claude.js +15 -0
- package/dist/cli.js +181 -207
- package/dist/control.js +173 -0
- package/dist/plugin.js +82 -0
- package/dist/prompt.js +61 -0
- package/dist/runtime.js +117 -0
- package/dist/serve.js +77 -0
- package/dist/store.js +93 -0
- package/dist/tail.js +120 -0
- package/dist/update.js +33 -0
- package/dist/version.js +97 -0
- package/package.json +34 -21
- package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +119 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +210 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
- package/runtime/node_modules/@metro-labs/mcp/package.json +67 -0
- package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +24 -0
- package/runtime/node_modules/@metro-labs/mcp/src/channels/inbound.ts +367 -0
- package/runtime/node_modules/@metro-labs/mcp/src/channels/media-note.ts +85 -0
- package/runtime/node_modules/@metro-labs/mcp/src/channels/pending.ts +75 -0
- package/runtime/node_modules/@metro-labs/mcp/src/channels/relay.ts +129 -0
- package/runtime/node_modules/@metro-labs/mcp/src/channels/webhook-note.ts +51 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +311 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +334 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-connector-api.ts +150 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-import.ts +76 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-pair.ts +20 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-error.ts +8 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +153 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-grant.ts +99 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-owner.ts +32 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-serve.ts +133 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-session.ts +278 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +383 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +143 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connect-hint.ts +33 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-api.ts +315 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +21 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth-routes.ts +157 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth.ts +127 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +100 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +57 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +333 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/cors.ts +29 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/crash-guard.ts +49 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/events.ts +117 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/hosted-connectors.ts +118 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/http.ts +369 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +86 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/import-api.ts +67 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-connectors-api.ts +56 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-mode.ts +187 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-secret.ts +21 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/log.ts +42 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/mode-api.ts +31 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-client.ts +162 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-discovery.ts +120 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +65 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/pair-codes.ts +58 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/paths.ts +145 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/project-api.ts +214 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/protocol.ts +224 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/relay.ts +303 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +139 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-source.ts +105 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/secure-fs.ts +57 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-api.ts +27 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +63 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-config.ts +27 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +218 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-auth.ts +82 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-nonces.ts +43 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-routes.ts +89 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/supervisor-io.ts +97 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/supervisor.ts +320 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/train-call.ts +22 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/train-error.ts +53 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +257 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-api.ts +295 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-store.ts +181 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +91 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +214 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/agent-connectors.ts +143 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/agent-map.ts +84 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/agent-scope.ts +64 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +37 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/connector-config.ts +147 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/connector-relay.ts +126 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +326 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/file-admin.ts +319 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/file-source.ts +152 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/ids.ts +21 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/key-map.ts +45 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +373 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +309 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +24 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +105 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +118 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +45 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +72 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/whatsapp-login.ts +18 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/accounts.ts +141 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/call-tools.ts +203 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +29 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/ctx.ts +62 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/event-store.ts +91 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +21 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/group-tools.ts +129 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/index.ts +192 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/member-tools.ts +72 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +66 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/raw-get-stream.ts +207 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +73 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +172 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +43 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/session.ts +211 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/str.ts +5 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-dispatch.ts +212 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-schemas.ts +397 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/upload-tool.ts +66 -0
- package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +240 -0
- package/runtime/node_modules/@metro-labs/mcp/src/server.ts +2 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +80 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-inline.ts +100 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-interactive.ts +161 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-resolve.ts +200 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-xmtp.ts +111 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/attach.ts +182 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/attachments.ts +196 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/lines.ts +82 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +88 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/registry.ts +35 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/station-runtime.ts +64 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/types.ts +107 -0
- package/runtime/node_modules/@metro-labs/telegram/package.json +28 -0
- package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +81 -0
- package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
- package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
- package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
- package/runtime/node_modules/@metro-labs/telegram/src/format.ts +141 -0
- package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
- package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
- package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
- package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
- package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
- package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
- package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
- package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
- package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
- package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
- package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
- package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +172 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +48 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
- package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
- package/runtime/node_modules/@metro-labs/webhook/src/station.ts +40 -0
- package/runtime/node_modules/@metro-labs/whatsapp/package.json +29 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +67 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +178 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +318 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +132 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +141 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +17 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
- package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +113 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +333 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +78 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +39 -0
- package/runtime/runtime.json +3 -0
- package/runtime/trains/.keep +0 -0
- package/LICENSE +0 -21
- package/README.md +0 -117
- package/dist/agents/claude.js +0 -207
- package/dist/agents/codex.js +0 -207
- package/dist/agents/types.js +0 -2
- package/dist/channels/discord.js +0 -104
- package/dist/channels/telegram.js +0 -189
- package/dist/helpers/scope-cache.js +0 -65
- package/dist/helpers/streaming.js +0 -209
- package/dist/helpers/telegram-format.js +0 -39
- package/dist/helpers/turn.js +0 -40
- package/dist/log.js +0 -10
- package/dist/orchestrator.js +0 -208
- package/dist/paths.js +0 -68
package/dist/agents/codex.js
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
/** Codex adapter: spawns `codex app-server` over UDS WebSocket; one thread/scope, streams turn events. */
|
|
2
|
-
import { spawn } from 'node:child_process';
|
|
3
|
-
import { existsSync, unlinkSync } from 'node:fs';
|
|
4
|
-
import { createConnection } from 'node:net';
|
|
5
|
-
import { join } from 'node:path';
|
|
6
|
-
import { WebSocket } from 'ws';
|
|
7
|
-
import { errMsg, log } from '../log.js';
|
|
8
|
-
import { STATE_DIR } from '../paths.js';
|
|
9
|
-
const SOCKET_PATH = join(STATE_DIR, 'codex-app-server.sock');
|
|
10
|
-
const READY_TIMEOUT_MS = 15_000;
|
|
11
|
-
const READY_POLL_MS = 100;
|
|
12
|
-
export class CodexAgent {
|
|
13
|
-
clientVersion;
|
|
14
|
-
ws = null;
|
|
15
|
-
daemon = null;
|
|
16
|
-
nextId = 1;
|
|
17
|
-
pending = new Map();
|
|
18
|
-
turnCallbacks = new Map();
|
|
19
|
-
constructor(clientVersion) {
|
|
20
|
-
this.clientVersion = clientVersion;
|
|
21
|
-
}
|
|
22
|
-
async start() {
|
|
23
|
-
await this.checkCodexInstalled();
|
|
24
|
-
try {
|
|
25
|
-
unlinkSync(SOCKET_PATH);
|
|
26
|
-
}
|
|
27
|
-
catch { /* missing is fine */ }
|
|
28
|
-
log.info({ socket: SOCKET_PATH }, 'codex agent: starting app-server');
|
|
29
|
-
this.daemon = spawn('codex', ['app-server', '--listen', `unix://${SOCKET_PATH}`], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
30
|
-
let bootStderr = '';
|
|
31
|
-
let daemonExited = false;
|
|
32
|
-
let daemonExitCode = null;
|
|
33
|
-
this.daemon.stdout?.on('data', d => log.trace({ src: 'codex-stdout' }, String(d).trim()));
|
|
34
|
-
this.daemon.stderr?.on('data', (d) => { bootStderr += String(d); log.trace({ src: 'codex-stderr' }, String(d).trim()); });
|
|
35
|
-
this.daemon.on('exit', code => { daemonExited = true; daemonExitCode = code; log.warn({ code }, 'codex daemon exited'); });
|
|
36
|
-
try {
|
|
37
|
-
await this.waitForSocket(() => daemonExited, () => bootStderr.trim() || `exit code ${daemonExitCode}`);
|
|
38
|
-
await this.connect();
|
|
39
|
-
}
|
|
40
|
-
catch (err) {
|
|
41
|
-
const detail = bootStderr.trim() ? ` (codex stderr: ${bootStderr.trim().slice(0, 200)})` : '';
|
|
42
|
-
throw new Error(`${errMsg(err)}${detail}`);
|
|
43
|
-
}
|
|
44
|
-
log.info('codex agent: ready');
|
|
45
|
-
}
|
|
46
|
-
async checkCodexInstalled() {
|
|
47
|
-
await new Promise((resolve, reject) => {
|
|
48
|
-
const c = spawn('codex', ['--version'], { stdio: ['ignore', 'ignore', 'pipe'] });
|
|
49
|
-
let stderr = '';
|
|
50
|
-
c.stderr?.on('data', d => { stderr += String(d); });
|
|
51
|
-
c.on('error', err => reject(new Error(`codex CLI not found on PATH: ${errMsg(err)}`)));
|
|
52
|
-
c.on('exit', code => code === 0 ? resolve() : reject(new Error(`codex --version exited ${code}: ${stderr.trim()}`)));
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
async stop() {
|
|
56
|
-
this.ws?.close();
|
|
57
|
-
this.ws = null;
|
|
58
|
-
this.daemon?.kill('SIGTERM');
|
|
59
|
-
this.daemon = null;
|
|
60
|
-
}
|
|
61
|
-
async createThread() {
|
|
62
|
-
const result = await this.call('thread/start', {});
|
|
63
|
-
log.info({ thread: result.thread.id }, 'codex agent: thread created');
|
|
64
|
-
return result.thread.id;
|
|
65
|
-
}
|
|
66
|
-
async sendTurn(threadId, text, callbacks) {
|
|
67
|
-
this.turnCallbacks.set(threadId, callbacks);
|
|
68
|
-
try {
|
|
69
|
-
/** `text_elements` is snake_case per codex's generated bindings. */
|
|
70
|
-
await this.call('turn/start', { threadId, input: [{ type: 'text', text, text_elements: [] }] });
|
|
71
|
-
}
|
|
72
|
-
catch (err) {
|
|
73
|
-
this.turnCallbacks.delete(threadId);
|
|
74
|
-
callbacks.onError(err instanceof Error ? err : new Error(String(err)));
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
async waitForSocket(exited, exitReason) {
|
|
78
|
-
const deadline = Date.now() + READY_TIMEOUT_MS;
|
|
79
|
-
while (Date.now() < deadline) {
|
|
80
|
-
if (exited())
|
|
81
|
-
throw new Error(`codex app-server exited before listening: ${exitReason()}`);
|
|
82
|
-
if (existsSync(SOCKET_PATH))
|
|
83
|
-
return;
|
|
84
|
-
await new Promise(r => setTimeout(r, READY_POLL_MS));
|
|
85
|
-
}
|
|
86
|
-
throw new Error(`codex app-server didn't appear at ${SOCKET_PATH} within ${READY_TIMEOUT_MS}ms`);
|
|
87
|
-
}
|
|
88
|
-
/** perMessageDeflate disabled: codex 0.130 closes connections offering it. */
|
|
89
|
-
async connect() {
|
|
90
|
-
const ws = new WebSocket('ws://localhost/', {
|
|
91
|
-
perMessageDeflate: false,
|
|
92
|
-
createConnection: () => {
|
|
93
|
-
const sock = createConnection({ path: SOCKET_PATH });
|
|
94
|
-
sock.on('error', err => log.warn({ err: errMsg(err) }, 'codex agent: socket error'));
|
|
95
|
-
return sock;
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
this.ws = ws;
|
|
99
|
-
ws.on('error', err => log.warn({ err: errMsg(err) }, 'codex agent: websocket error'));
|
|
100
|
-
await new Promise((resolve, reject) => { ws.once('open', () => resolve()); ws.once('error', reject); });
|
|
101
|
-
ws.on('message', data => this.onMessage(data));
|
|
102
|
-
ws.on('close', () => { log.warn('codex agent: websocket closed'); this.ws = null; this.drainPending('codex websocket closed'); });
|
|
103
|
-
await this.call('initialize', { clientInfo: { name: 'metro', version: this.clientVersion, title: null } });
|
|
104
|
-
}
|
|
105
|
-
onMessage(raw) {
|
|
106
|
-
let msg;
|
|
107
|
-
try {
|
|
108
|
-
msg = JSON.parse(raw.toString());
|
|
109
|
-
}
|
|
110
|
-
catch (err) {
|
|
111
|
-
log.warn({ err: errMsg(err) }, 'codex agent: malformed message');
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (msg.id !== undefined && this.pending.has(msg.id)) {
|
|
115
|
-
const p = this.pending.get(msg.id);
|
|
116
|
-
this.pending.delete(msg.id);
|
|
117
|
-
if (msg.error)
|
|
118
|
-
p.reject(new Error(msg.error.message ?? 'rpc error'));
|
|
119
|
-
else
|
|
120
|
-
p.resolve(msg.result);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
if (!msg.method)
|
|
124
|
-
return;
|
|
125
|
-
log.trace({ method: msg.method }, 'codex agent: notification');
|
|
126
|
-
const threadId = msg.params?.threadId;
|
|
127
|
-
if (!threadId)
|
|
128
|
-
return;
|
|
129
|
-
const cb = this.turnCallbacks.get(threadId);
|
|
130
|
-
if (!cb)
|
|
131
|
-
return;
|
|
132
|
-
if (msg.method === 'item/agentMessage/delta') {
|
|
133
|
-
cb.onDelta(msg.params.delta);
|
|
134
|
-
}
|
|
135
|
-
else if (msg.method === 'item/started') {
|
|
136
|
-
const a = summarizeItem(msg.params.item);
|
|
137
|
-
if (a)
|
|
138
|
-
cb.onToolStart(a);
|
|
139
|
-
}
|
|
140
|
-
else if (msg.method === 'item/completed') {
|
|
141
|
-
const item = msg.params.item;
|
|
142
|
-
if (item.type !== 'agentMessage' && item.type !== 'userMessage')
|
|
143
|
-
cb.onToolEnd(item.id, itemOutput(item));
|
|
144
|
-
}
|
|
145
|
-
else if (msg.method === 'thread/status/changed') {
|
|
146
|
-
/** codex 0.130: `thread/status=idle` is the dependable completion signal. */
|
|
147
|
-
const status = msg.params.status?.type;
|
|
148
|
-
if (status === 'idle') {
|
|
149
|
-
this.turnCallbacks.delete(threadId);
|
|
150
|
-
cb.onComplete();
|
|
151
|
-
}
|
|
152
|
-
else if (status === 'systemError') {
|
|
153
|
-
this.turnCallbacks.delete(threadId);
|
|
154
|
-
cb.onError(new Error('codex thread entered systemError'));
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
else if (msg.method === 'turn/completed') {
|
|
158
|
-
this.turnCallbacks.delete(threadId);
|
|
159
|
-
cb.onComplete();
|
|
160
|
-
}
|
|
161
|
-
else if (msg.method === 'error') {
|
|
162
|
-
this.turnCallbacks.delete(threadId);
|
|
163
|
-
cb.onError(new Error(msg.params.error?.message ?? 'codex error notification'));
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
call(method, params) {
|
|
167
|
-
if (!this.ws)
|
|
168
|
-
return Promise.reject(new Error('codex agent: not connected'));
|
|
169
|
-
const id = this.nextId++;
|
|
170
|
-
const ws = this.ws;
|
|
171
|
-
return new Promise((resolve, reject) => {
|
|
172
|
-
this.pending.set(id, { resolve: resolve, reject });
|
|
173
|
-
ws.send(JSON.stringify({ jsonrpc: '2.0', id, method, params }));
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
drainPending(reason) {
|
|
177
|
-
const err = new Error(reason);
|
|
178
|
-
for (const cb of this.turnCallbacks.values()) {
|
|
179
|
-
try {
|
|
180
|
-
cb.onError(err);
|
|
181
|
-
}
|
|
182
|
-
catch (e) {
|
|
183
|
-
log.warn({ err: errMsg(e) }, 'codex agent: drain callback threw');
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
this.turnCallbacks.clear();
|
|
187
|
-
for (const p of this.pending.values())
|
|
188
|
-
p.reject(err);
|
|
189
|
-
this.pending.clear();
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
function summarizeItem(item) {
|
|
193
|
-
const id = item.id;
|
|
194
|
-
if (item.type === 'commandExecution' && 'command' in item) {
|
|
195
|
-
return { id, kind: 'commandExecution', name: 'Bash', detail: item.command || undefined };
|
|
196
|
-
}
|
|
197
|
-
if (item.type === 'fileChange' && 'changes' in item) {
|
|
198
|
-
const paths = (item.changes ?? []).map(c => c.path).filter((p) => !!p);
|
|
199
|
-
return { id, kind: 'fileChange', name: 'Edit', detail: paths.length === 1 ? paths[0] : paths.length ? `${paths.length} files` : undefined };
|
|
200
|
-
}
|
|
201
|
-
if (item.type === 'reasoning')
|
|
202
|
-
return { id, kind: 'reasoning', name: 'Thinking…', transient: true };
|
|
203
|
-
if (item.type === 'agentMessage' || item.type === 'userMessage')
|
|
204
|
-
return null;
|
|
205
|
-
return { id, kind: item.type, name: item.type };
|
|
206
|
-
}
|
|
207
|
-
const itemOutput = (item) => item.type === 'commandExecution' && 'output' in item ? item.output?.trim() || undefined : undefined;
|
package/dist/agents/types.js
DELETED
package/dist/channels/discord.js
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { Client, Events, GatewayIntentBits, Partials } from 'discord.js';
|
|
2
|
-
import { errMsg, log } from '../log.js';
|
|
3
|
-
/** Receive: discord.js gateway. Send: raw REST so non-orchestrator callers stay one-shot. */
|
|
4
|
-
const API_BASE = 'https://discord.com/api/v10';
|
|
5
|
-
function token() {
|
|
6
|
-
const t = process.env.DISCORD_BOT_TOKEN;
|
|
7
|
-
if (!t)
|
|
8
|
-
throw new Error('DISCORD_BOT_TOKEN is not set');
|
|
9
|
-
return t;
|
|
10
|
-
}
|
|
11
|
-
async function rest(method, path, body, timeoutMs = 30_000, retriesLeft = 2) {
|
|
12
|
-
const res = await fetch(`${API_BASE}${path}`, {
|
|
13
|
-
method,
|
|
14
|
-
headers: {
|
|
15
|
-
'Authorization': `Bot ${token()}`,
|
|
16
|
-
'User-Agent': 'metro (https://github.com/bonustrack/metro, dev)',
|
|
17
|
-
...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),
|
|
18
|
-
},
|
|
19
|
-
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
20
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
21
|
-
});
|
|
22
|
-
/** 429: honor `retry_after` (seconds) and retry, up to a few hops. */
|
|
23
|
-
if (res.status === 429 && retriesLeft > 0) {
|
|
24
|
-
const retryAfter = Number(res.headers.get('retry-after')) || 1;
|
|
25
|
-
log.debug({ path, retryAfter }, 'discord 429; backing off');
|
|
26
|
-
await new Promise(r => setTimeout(r, Math.max(retryAfter * 1000, 250)));
|
|
27
|
-
return rest(method, path, body, timeoutMs, retriesLeft - 1);
|
|
28
|
-
}
|
|
29
|
-
if (!res.ok) {
|
|
30
|
-
const text = await res.text().catch(() => '');
|
|
31
|
-
throw new Error(`discord ${method} ${path}: ${res.status} ${text}`);
|
|
32
|
-
}
|
|
33
|
-
if (res.status === 204)
|
|
34
|
-
return undefined;
|
|
35
|
-
return (await res.json());
|
|
36
|
-
}
|
|
37
|
-
let client = null;
|
|
38
|
-
function getClient() {
|
|
39
|
-
if (client)
|
|
40
|
-
return client;
|
|
41
|
-
client = new Client({
|
|
42
|
-
intents: [GatewayIntentBits.DirectMessages, GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent],
|
|
43
|
-
/** DM channels arrive partial; without this messageCreate never fires. */
|
|
44
|
-
partials: [Partials.Channel],
|
|
45
|
-
});
|
|
46
|
-
return client;
|
|
47
|
-
}
|
|
48
|
-
let onInboundHandler = () => { };
|
|
49
|
-
export const onInbound = (handler) => { onInboundHandler = handler; };
|
|
50
|
-
export async function shutdownGateway() {
|
|
51
|
-
if (!client)
|
|
52
|
-
return;
|
|
53
|
-
await client.destroy();
|
|
54
|
-
client = null;
|
|
55
|
-
}
|
|
56
|
-
export async function startGateway() {
|
|
57
|
-
const c = getClient();
|
|
58
|
-
c.on(Events.MessageCreate, m => {
|
|
59
|
-
if (m.author.bot)
|
|
60
|
-
return;
|
|
61
|
-
/** Forward every human message; orchestrator decides routing. @-mention stays in content. */
|
|
62
|
-
const tags = [...m.attachments.values()].map(a => a.contentType?.startsWith('image/') ? '[image]'
|
|
63
|
-
: a.contentType?.startsWith('audio/') ? `[audio: ${a.name}]`
|
|
64
|
-
: `[file: ${a.name}]`).join(' ');
|
|
65
|
-
const text = [m.content, tags].filter(Boolean).join(' ').trim();
|
|
66
|
-
if (!text)
|
|
67
|
-
return;
|
|
68
|
-
onInboundHandler({
|
|
69
|
-
channel_id: m.channelId,
|
|
70
|
-
message_id: m.id,
|
|
71
|
-
text,
|
|
72
|
-
in_guild: !!m.guildId,
|
|
73
|
-
mentions_bot: c.user ? m.mentions.has(c.user.id) : false,
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
c.on(Events.Error, err => log.error({ err: errMsg(err) }, 'discord error'));
|
|
77
|
-
await c.login(process.env.DISCORD_BOT_TOKEN);
|
|
78
|
-
await new Promise(r => c.once(Events.ClientReady, () => r()));
|
|
79
|
-
}
|
|
80
|
-
export async function getMe() {
|
|
81
|
-
const me = await rest('GET', '/users/@me');
|
|
82
|
-
return { username: me.username };
|
|
83
|
-
}
|
|
84
|
-
/** SUPPRESS_EMBEDS (1 << 2) — strip link unfurls from every bot message. */
|
|
85
|
-
const SUPPRESS_EMBEDS = 1 << 2;
|
|
86
|
-
export async function sendMessage(channelId, text) {
|
|
87
|
-
const sent = await rest('POST', `/channels/${channelId}/messages`, { content: text, flags: SUPPRESS_EMBEDS });
|
|
88
|
-
return sent.id;
|
|
89
|
-
}
|
|
90
|
-
/** Public thread anchored on `messageId`. Returns the new thread channel id. */
|
|
91
|
-
export async function createThreadFromMessage(channelId, messageId, name) {
|
|
92
|
-
const created = await rest('POST', `/channels/${channelId}/messages/${messageId}/threads`, { name, auto_archive_duration: 1440 });
|
|
93
|
-
return created.id;
|
|
94
|
-
}
|
|
95
|
-
export async function editMessage(channelId, messageId, text) {
|
|
96
|
-
const sent = await rest('PATCH', `/channels/${channelId}/messages/${messageId}`, { content: text, flags: SUPPRESS_EMBEDS });
|
|
97
|
-
return sent.id;
|
|
98
|
-
}
|
|
99
|
-
/** Catchup-on-restart: fetch messages newer than `afterMessageId` (cap 100). */
|
|
100
|
-
export async function fetchMessagesSince(channelId, afterMessageId) {
|
|
101
|
-
const msgs = await rest('GET', `/channels/${channelId}/messages?after=${afterMessageId}&limit=100`);
|
|
102
|
-
/** Discord returns newest-first; flip to chronological for replay. */
|
|
103
|
-
return [...msgs].reverse().map(m => ({ message_id: m.id, text: m.content, author_id: m.author.id, author_is_bot: !!m.author.bot }));
|
|
104
|
-
}
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { STATE_DIR } from '../paths.js';
|
|
4
|
-
import { errMsg, log } from '../log.js';
|
|
5
|
-
import { mdToTelegramHtml } from '../helpers/telegram-format.js';
|
|
6
|
-
const API_BASE = 'https://api.telegram.org';
|
|
7
|
-
function token() {
|
|
8
|
-
const t = process.env.TELEGRAM_BOT_TOKEN;
|
|
9
|
-
if (!t)
|
|
10
|
-
throw new Error('TELEGRAM_BOT_TOKEN is not set');
|
|
11
|
-
return t;
|
|
12
|
-
}
|
|
13
|
-
async function tg(method, body, opts = {}) {
|
|
14
|
-
const signals = [AbortSignal.timeout(opts.timeoutMs ?? 30_000)];
|
|
15
|
-
if (opts.signal)
|
|
16
|
-
signals.push(opts.signal);
|
|
17
|
-
const res = await fetch(`${API_BASE}/bot${token()}/${method}`, {
|
|
18
|
-
method: 'POST',
|
|
19
|
-
headers: { 'Content-Type': 'application/json' },
|
|
20
|
-
body: JSON.stringify(body),
|
|
21
|
-
signal: AbortSignal.any(signals),
|
|
22
|
-
});
|
|
23
|
-
const json = (await res.json());
|
|
24
|
-
if (!json.ok)
|
|
25
|
-
throw new Error(`telegram ${method}: ${json.description ?? 'unknown error'}`);
|
|
26
|
-
return json.result;
|
|
27
|
-
}
|
|
28
|
-
let botUsername = null;
|
|
29
|
-
let botUserId = null;
|
|
30
|
-
export async function getMe() {
|
|
31
|
-
const me = await tg('getMe', {});
|
|
32
|
-
botUsername = me.username;
|
|
33
|
-
botUserId = me.id;
|
|
34
|
-
return me;
|
|
35
|
-
}
|
|
36
|
-
let onInboundHandler = () => { };
|
|
37
|
-
export const onInbound = (handler) => { onInboundHandler = handler; };
|
|
38
|
-
const offsetFile = join(STATE_DIR, 'telegram-offset.json');
|
|
39
|
-
let pollOffset = 0;
|
|
40
|
-
let pollAbort = null;
|
|
41
|
-
function loadOffset() {
|
|
42
|
-
try {
|
|
43
|
-
return existsSync(offsetFile) ? Number(readFileSync(offsetFile, 'utf8').trim()) || 0 : 0;
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
return 0;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
function saveOffset(o) {
|
|
50
|
-
try {
|
|
51
|
-
writeFileSync(offsetFile, String(o));
|
|
52
|
-
}
|
|
53
|
-
catch (err) {
|
|
54
|
-
log.warn({ err: errMsg(err) }, 'telegram offset save failed');
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
export async function startPolling() {
|
|
58
|
-
await tg('deleteWebhook', { drop_pending_updates: false }).catch(() => { });
|
|
59
|
-
const persisted = loadOffset();
|
|
60
|
-
if (persisted > 0) {
|
|
61
|
-
pollOffset = persisted;
|
|
62
|
-
log.info({ offset: pollOffset }, 'telegram polling: resuming from persisted offset');
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
/** First run: anchor on latest update id (-1 returns most recent without consuming). */
|
|
66
|
-
const initial = await tg('getUpdates', { offset: -1, timeout: 0 });
|
|
67
|
-
pollOffset = initial.length ? initial[0].update_id + 1 : 0;
|
|
68
|
-
saveOffset(pollOffset);
|
|
69
|
-
log.info({ offset: pollOffset }, 'telegram polling: starting fresh');
|
|
70
|
-
}
|
|
71
|
-
pollAbort = new AbortController();
|
|
72
|
-
void pollLoop();
|
|
73
|
-
}
|
|
74
|
-
async function pollLoop() {
|
|
75
|
-
const abortSignal = pollAbort?.signal;
|
|
76
|
-
while (pollAbort && !pollAbort.signal.aborted) {
|
|
77
|
-
try {
|
|
78
|
-
const updates = await tg('getUpdates', { offset: pollOffset, timeout: 25 }, { timeoutMs: 60_000, signal: abortSignal });
|
|
79
|
-
for (const u of updates) {
|
|
80
|
-
pollOffset = u.update_id + 1;
|
|
81
|
-
await dispatchUpdate(u);
|
|
82
|
-
}
|
|
83
|
-
if (updates.length)
|
|
84
|
-
saveOffset(pollOffset);
|
|
85
|
-
}
|
|
86
|
-
catch (err) {
|
|
87
|
-
if (pollAbort?.signal.aborted)
|
|
88
|
-
break;
|
|
89
|
-
log.warn({ err: errMsg(err) }, 'telegram poll error; backing off');
|
|
90
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
log.info('telegram polling stopped');
|
|
94
|
-
}
|
|
95
|
-
export async function shutdownPolling() { pollAbort?.abort(); pollAbort = null; }
|
|
96
|
-
async function dispatchUpdate(u) {
|
|
97
|
-
const m = u.message;
|
|
98
|
-
if (!m?.chat?.id || typeof m.message_id !== 'number' || m.from?.is_bot)
|
|
99
|
-
return;
|
|
100
|
-
const text = messageToText(m);
|
|
101
|
-
if (!text)
|
|
102
|
-
return;
|
|
103
|
-
onInboundHandler({
|
|
104
|
-
chat_id: m.chat.id,
|
|
105
|
-
message_id: m.message_id,
|
|
106
|
-
message_thread_id: m.is_topic_message ? m.message_thread_id : undefined,
|
|
107
|
-
text,
|
|
108
|
-
is_private: m.chat.type === 'private',
|
|
109
|
-
is_forum_topic: !!m.is_topic_message,
|
|
110
|
-
in_forum: !!m.chat.is_forum,
|
|
111
|
-
mentions_bot: detectMentionsBot(m),
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
function detectMentionsBot(m) {
|
|
115
|
-
if (m.chat?.type === 'private')
|
|
116
|
-
return true;
|
|
117
|
-
const text = m.text ?? m.caption ?? '';
|
|
118
|
-
for (const e of m.entities ?? m.caption_entities ?? []) {
|
|
119
|
-
if (e.type === 'mention' && botUsername) {
|
|
120
|
-
if (text.substring(e.offset, e.offset + e.length).toLowerCase() === `@${botUsername.toLowerCase()}`)
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
else if (e.type === 'text_mention' && e.user?.id === botUserId)
|
|
124
|
-
return true;
|
|
125
|
-
}
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
function messageToText(m) {
|
|
129
|
-
if (m.text)
|
|
130
|
-
return m.text;
|
|
131
|
-
const caption = m.caption ?? '';
|
|
132
|
-
if (m.photo?.length || m.document?.mime_type?.startsWith('image/'))
|
|
133
|
-
return [caption, '[image]'].filter(Boolean).join(' ');
|
|
134
|
-
if (m.voice)
|
|
135
|
-
return [caption, '[voice]'].filter(Boolean).join(' ');
|
|
136
|
-
if (m.audio)
|
|
137
|
-
return [caption, '[audio]'].filter(Boolean).join(' ');
|
|
138
|
-
return caption || null;
|
|
139
|
-
}
|
|
140
|
-
/** Create a forum topic; requires `can_manage_topics` admin permission. */
|
|
141
|
-
export async function createForumTopic(chatId, name) {
|
|
142
|
-
const r = await tg('createForumTopic', { chat_id: chatId, name: name.slice(0, 128) || 'metro' });
|
|
143
|
-
return r.message_thread_id;
|
|
144
|
-
}
|
|
145
|
-
/** Deep link `t.me/c/<id>/<topic>`; strips supergroup's `-100` prefix from chat id. */
|
|
146
|
-
export function topicLink(chatId, topicId) {
|
|
147
|
-
const id = String(Math.abs(chatId)).replace(/^100/, '');
|
|
148
|
-
return `https://t.me/c/${id}/${topicId}`;
|
|
149
|
-
}
|
|
150
|
-
const isParseError = (err) => errMsg(err).includes("can't parse entities");
|
|
151
|
-
const NO_PREVIEW = { link_preview_options: { is_disabled: true } };
|
|
152
|
-
/** Send agent-style markdown as Telegram HTML, falling back to plain text on parse errors. */
|
|
153
|
-
export async function sendMessage(chatId, threadId, text, replyToMessageId) {
|
|
154
|
-
const base = { chat_id: chatId, ...NO_PREVIEW };
|
|
155
|
-
if (threadId !== undefined)
|
|
156
|
-
base.message_thread_id = threadId;
|
|
157
|
-
if (replyToMessageId !== undefined)
|
|
158
|
-
base.reply_parameters = { message_id: replyToMessageId };
|
|
159
|
-
try {
|
|
160
|
-
return (await tg('sendMessage', { ...base, text: mdToTelegramHtml(text), parse_mode: 'HTML' })).message_id;
|
|
161
|
-
}
|
|
162
|
-
catch (err) {
|
|
163
|
-
if (!isParseError(err))
|
|
164
|
-
throw err;
|
|
165
|
-
log.warn({ err: errMsg(err) }, 'telegram: HTML rejected, sending plain');
|
|
166
|
-
return (await tg('sendMessage', { ...base, text })).message_id;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
export async function editMessageText(chatId, messageId, text) {
|
|
170
|
-
const base = { chat_id: chatId, message_id: messageId, ...NO_PREVIEW };
|
|
171
|
-
const swallow = (err) => { if (!errMsg(err).includes('message is not modified'))
|
|
172
|
-
throw err; };
|
|
173
|
-
try {
|
|
174
|
-
await tg('editMessageText', { ...base, text: mdToTelegramHtml(text), parse_mode: 'HTML' });
|
|
175
|
-
}
|
|
176
|
-
catch (err) {
|
|
177
|
-
if (errMsg(err).includes('message is not modified'))
|
|
178
|
-
return;
|
|
179
|
-
if (!isParseError(err))
|
|
180
|
-
throw err;
|
|
181
|
-
log.warn({ err: errMsg(err) }, 'telegram: HTML edit rejected, retrying plain');
|
|
182
|
-
try {
|
|
183
|
-
await tg('editMessageText', { ...base, text });
|
|
184
|
-
}
|
|
185
|
-
catch (e) {
|
|
186
|
-
swallow(e);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/** Per-machine scope→{thread ids,last-used} cache. Keys: `discord:<id>` / `telegram:<chat>:<topic>`. */
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { errMsg, log } from '../log.js';
|
|
5
|
-
import { STATE_DIR } from '../paths.js';
|
|
6
|
-
const cacheFile = join(STATE_DIR, 'scopes.json');
|
|
7
|
-
function read() {
|
|
8
|
-
if (!existsSync(cacheFile))
|
|
9
|
-
return {};
|
|
10
|
-
try {
|
|
11
|
-
return JSON.parse(readFileSync(cacheFile, 'utf8'));
|
|
12
|
-
}
|
|
13
|
-
catch (err) {
|
|
14
|
-
log.warn({ err: errMsg(err), path: cacheFile }, 'scope cache read failed; treating as empty');
|
|
15
|
-
return {};
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
function write(cache) {
|
|
19
|
-
try {
|
|
20
|
-
writeFileSync(cacheFile, JSON.stringify(cache, null, 2));
|
|
21
|
-
}
|
|
22
|
-
catch (err) {
|
|
23
|
-
log.warn({ err: errMsg(err), path: cacheFile }, 'scope cache write failed');
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function ensure(cache, scopeKey) {
|
|
27
|
-
if (!cache[scopeKey])
|
|
28
|
-
cache[scopeKey] = { createdAt: new Date().toISOString(), agents: {} };
|
|
29
|
-
if (!cache[scopeKey].agents)
|
|
30
|
-
cache[scopeKey].agents = {};
|
|
31
|
-
return cache[scopeKey];
|
|
32
|
-
}
|
|
33
|
-
export function getAgentThread(scopeKey, kind) {
|
|
34
|
-
return read()[scopeKey]?.agents?.[kind];
|
|
35
|
-
}
|
|
36
|
-
export function setAgentThread(scopeKey, kind, threadId) {
|
|
37
|
-
const cache = read();
|
|
38
|
-
const entry = ensure(cache, scopeKey);
|
|
39
|
-
entry.agents[kind] = threadId;
|
|
40
|
-
entry.lastAgent = kind;
|
|
41
|
-
write(cache);
|
|
42
|
-
}
|
|
43
|
-
export function getLastAgent(scopeKey) {
|
|
44
|
-
return read()[scopeKey]?.lastAgent;
|
|
45
|
-
}
|
|
46
|
-
export function setLastAgent(scopeKey, kind) {
|
|
47
|
-
const cache = read();
|
|
48
|
-
if (!cache[scopeKey])
|
|
49
|
-
return;
|
|
50
|
-
cache[scopeKey].lastAgent = kind;
|
|
51
|
-
write(cache);
|
|
52
|
-
}
|
|
53
|
-
export function setLastSeen(scopeKey, messageId) {
|
|
54
|
-
const cache = read();
|
|
55
|
-
if (!cache[scopeKey])
|
|
56
|
-
return;
|
|
57
|
-
cache[scopeKey].lastSeenMessageId = messageId;
|
|
58
|
-
write(cache);
|
|
59
|
-
}
|
|
60
|
-
export function listScopes() {
|
|
61
|
-
return Object.entries(read()).map(([scopeKey, entry]) => ({ scopeKey, entry }));
|
|
62
|
-
}
|
|
63
|
-
export const discordScopeKey = (threadChannelId) => `discord:${threadChannelId}`;
|
|
64
|
-
export const discordChannelFromScopeKey = (scopeKey) => scopeKey.startsWith('discord:') ? scopeKey.slice('discord:'.length) : null;
|
|
65
|
-
export const telegramScopeKey = (chatId, topicId) => `telegram:${chatId}:${topicId ?? 'main'}`;
|