@stage-labs/metro 0.1.0-beta.4 → 0.1.0-beta.40
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 +91 -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
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { and, eq } from 'drizzle-orm';
|
|
2
|
+
import { oauthExpired, refreshOAuth } from '../daemon/connector-oauth.js';
|
|
3
|
+
import {
|
|
4
|
+
parseConnectorUrl,
|
|
5
|
+
type ConnectorAuth,
|
|
6
|
+
type OAuthAuth,
|
|
7
|
+
} from '../daemon/connector-verify.js';
|
|
8
|
+
import { errMsg, log } from '../daemon/log.js';
|
|
9
|
+
import { readConfig, type ConnectorConfig } from './connector-config.js';
|
|
10
|
+
import { getDb } from './client.js';
|
|
11
|
+
import { agentConnectors, connectors } from './schema.js';
|
|
12
|
+
|
|
13
|
+
export type RelayTarget =
|
|
14
|
+
| { kind: 'ok'; url: string; headers: Record<string, string> }
|
|
15
|
+
| { kind: 'missing' }
|
|
16
|
+
| { kind: 'signin' };
|
|
17
|
+
|
|
18
|
+
type ConnectorRow = typeof connectors.$inferSelect;
|
|
19
|
+
|
|
20
|
+
export const bearerHeaders = (token: string): Record<string, string> => ({
|
|
21
|
+
Authorization: `Bearer ${token}`,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export function staleUsable(auth: OAuthAuth, now = Date.now()): boolean {
|
|
25
|
+
return auth.expiresAt === undefined || auth.expiresAt > now;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function headerAuthHeaders(
|
|
29
|
+
auth: ConnectorAuth,
|
|
30
|
+
): Record<string, string> | null {
|
|
31
|
+
return auth.kind === 'header' ? { [auth.name]: auth.value } : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function memberRow(
|
|
35
|
+
agentId: string,
|
|
36
|
+
connectorId: string,
|
|
37
|
+
): Promise<ConnectorRow | undefined> {
|
|
38
|
+
const rows = await getDb()
|
|
39
|
+
.select({ connector: connectors })
|
|
40
|
+
.from(agentConnectors)
|
|
41
|
+
.innerJoin(connectors, eq(agentConnectors.connectorId, connectors.id))
|
|
42
|
+
.where(
|
|
43
|
+
and(
|
|
44
|
+
eq(agentConnectors.agentId, agentId),
|
|
45
|
+
eq(agentConnectors.connectorId, connectorId),
|
|
46
|
+
),
|
|
47
|
+
);
|
|
48
|
+
return rows[0]?.connector;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const inflight = new Map<string, Promise<OAuthAuth>>();
|
|
52
|
+
|
|
53
|
+
function refreshOnce(
|
|
54
|
+
row: ConnectorRow,
|
|
55
|
+
config: ConnectorConfig,
|
|
56
|
+
auth: OAuthAuth,
|
|
57
|
+
): Promise<OAuthAuth> {
|
|
58
|
+
const running = inflight.get(row.id);
|
|
59
|
+
if (running !== undefined) return running;
|
|
60
|
+
const resource = parseConnectorUrl(row.url).toString();
|
|
61
|
+
const job = refreshOAuth(auth, resource)
|
|
62
|
+
.then(async (fresh) => {
|
|
63
|
+
await getDb()
|
|
64
|
+
.update(connectors)
|
|
65
|
+
.set({ config: { ...config, auth: fresh } })
|
|
66
|
+
.where(eq(connectors.id, row.id));
|
|
67
|
+
return fresh;
|
|
68
|
+
})
|
|
69
|
+
.finally(() => {
|
|
70
|
+
inflight.delete(row.id);
|
|
71
|
+
});
|
|
72
|
+
inflight.set(row.id, job);
|
|
73
|
+
return job;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function unrefreshedTarget(
|
|
77
|
+
url: string,
|
|
78
|
+
auth: OAuthAuth,
|
|
79
|
+
force: boolean,
|
|
80
|
+
now = Date.now(),
|
|
81
|
+
): RelayTarget {
|
|
82
|
+
if (force) return { kind: 'signin' };
|
|
83
|
+
if (staleUsable(auth, now))
|
|
84
|
+
return { kind: 'ok', url, headers: bearerHeaders(auth.accessToken) };
|
|
85
|
+
return { kind: 'ok', url, headers: {} };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function oauthTarget(
|
|
89
|
+
row: ConnectorRow,
|
|
90
|
+
config: ConnectorConfig,
|
|
91
|
+
auth: OAuthAuth,
|
|
92
|
+
force: boolean,
|
|
93
|
+
): Promise<RelayTarget> {
|
|
94
|
+
if (!force && !oauthExpired(auth))
|
|
95
|
+
return { kind: 'ok', url: row.url, headers: bearerHeaders(auth.accessToken) };
|
|
96
|
+
try {
|
|
97
|
+
const fresh = await refreshOnce(row, config, auth);
|
|
98
|
+
return { kind: 'ok', url: row.url, headers: bearerHeaders(fresh.accessToken) };
|
|
99
|
+
} catch (err) {
|
|
100
|
+
log.warn({ id: row.id, err: errMsg(err) }, 'relay: token refresh failed');
|
|
101
|
+
return unrefreshedTarget(row.url, auth, force);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function fixedTarget(
|
|
106
|
+
url: string,
|
|
107
|
+
auth: Exclude<ConnectorAuth, OAuthAuth>,
|
|
108
|
+
force: boolean,
|
|
109
|
+
): RelayTarget {
|
|
110
|
+
if (force) return { kind: 'signin' };
|
|
111
|
+
return { kind: 'ok', url, headers: headerAuthHeaders(auth) ?? {} };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function relayTarget(
|
|
115
|
+
agentId: string,
|
|
116
|
+
connectorId: string,
|
|
117
|
+
force: boolean,
|
|
118
|
+
): Promise<RelayTarget> {
|
|
119
|
+
const row = await memberRow(agentId, connectorId);
|
|
120
|
+
if (row === undefined) return { kind: 'missing' };
|
|
121
|
+
parseConnectorUrl(row.url);
|
|
122
|
+
const config = readConfig(row.config);
|
|
123
|
+
const auth = config.auth;
|
|
124
|
+
if (auth.kind === 'oauth') return oauthTarget(row, config, auth, force);
|
|
125
|
+
return fixedTarget(row.url, auth, force);
|
|
126
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { assertRenameFreeOfClash } from './agent-connectors.js';
|
|
2
|
+
import { projectIdOrThrow } from './projects.js';
|
|
3
|
+
import { and, asc, eq, inArray } from 'drizzle-orm';
|
|
4
|
+
import {
|
|
5
|
+
connectorUrlText,
|
|
6
|
+
ConnectorVerifyError,
|
|
7
|
+
parseConnectorUrl,
|
|
8
|
+
verifyRemoteMcp,
|
|
9
|
+
type ConnectorAuth,
|
|
10
|
+
type OAuthAuth,
|
|
11
|
+
type VerifiedRecord,
|
|
12
|
+
} from '../daemon/connector-verify.js';
|
|
13
|
+
import { oauthExpired, refreshOAuth } from '../daemon/connector-oauth.js';
|
|
14
|
+
import { advertisesOAuth } from '../daemon/oauth-discovery.js';
|
|
15
|
+
import {
|
|
16
|
+
ConnectorError,
|
|
17
|
+
connectorAuth,
|
|
18
|
+
connectorName,
|
|
19
|
+
readConfig,
|
|
20
|
+
signInState,
|
|
21
|
+
stamp,
|
|
22
|
+
type ConnectorConfig,
|
|
23
|
+
type ConnectorSignIn,
|
|
24
|
+
} from './connector-config.js';
|
|
25
|
+
import { newId } from './ids.js';
|
|
26
|
+
import { getDb } from './client.js';
|
|
27
|
+
import { connectors, type ConnectorTransport } from './schema.js';
|
|
28
|
+
|
|
29
|
+
export interface Connector {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
url: string;
|
|
33
|
+
transport: ConnectorTransport;
|
|
34
|
+
auth: ConnectorAuth['kind'];
|
|
35
|
+
header: string | null;
|
|
36
|
+
secret: string | null;
|
|
37
|
+
bearer: string | null;
|
|
38
|
+
expiresAt: number | null;
|
|
39
|
+
signIn: ConnectorSignIn;
|
|
40
|
+
verified: VerifiedRecord;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ConnectorInput {
|
|
44
|
+
name: unknown;
|
|
45
|
+
url: unknown;
|
|
46
|
+
header: unknown;
|
|
47
|
+
value: unknown;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type ConnectorCheck =
|
|
51
|
+
| { id: string; name: string; ok: true; verified: VerifiedRecord }
|
|
52
|
+
| { id: string; name: string; ok: false; reason: string };
|
|
53
|
+
|
|
54
|
+
export interface DeletedConnector {
|
|
55
|
+
id: string;
|
|
56
|
+
name: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type ConnectorRow = typeof connectors.$inferSelect;
|
|
60
|
+
|
|
61
|
+
function toConnector(row: ConnectorRow): Connector {
|
|
62
|
+
const config = readConfig(row.config);
|
|
63
|
+
const auth = config.auth;
|
|
64
|
+
return {
|
|
65
|
+
id: row.id,
|
|
66
|
+
name: row.name,
|
|
67
|
+
url: row.url,
|
|
68
|
+
transport: row.transport,
|
|
69
|
+
auth: auth.kind,
|
|
70
|
+
header: auth.kind === 'header' ? auth.name : null,
|
|
71
|
+
secret: auth.kind === 'header' ? auth.value : null,
|
|
72
|
+
bearer: auth.kind === 'oauth' ? auth.accessToken : null,
|
|
73
|
+
expiresAt: auth.kind === 'oauth' ? (auth.expiresAt ?? null) : null,
|
|
74
|
+
signIn: signInState(config),
|
|
75
|
+
verified: config.verified,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const missing = (): ConnectorError =>
|
|
80
|
+
new ConnectorError('no such connector', 404);
|
|
81
|
+
|
|
82
|
+
async function ownedConnectorOrThrow(
|
|
83
|
+
subject: string,
|
|
84
|
+
id: string,
|
|
85
|
+
): Promise<ConnectorRow> {
|
|
86
|
+
const rows = await getDb()
|
|
87
|
+
.select()
|
|
88
|
+
.from(connectors)
|
|
89
|
+
.where(eq(connectors.id, id));
|
|
90
|
+
const row = rows[0];
|
|
91
|
+
if (row === undefined) throw missing();
|
|
92
|
+
try {
|
|
93
|
+
await projectIdOrThrow(subject, row.projectId);
|
|
94
|
+
} catch {
|
|
95
|
+
throw missing();
|
|
96
|
+
}
|
|
97
|
+
return row;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function saveConfig(
|
|
101
|
+
row: ConnectorRow,
|
|
102
|
+
config: ConnectorConfig,
|
|
103
|
+
): Promise<Connector> {
|
|
104
|
+
const rows = await getDb()
|
|
105
|
+
.update(connectors)
|
|
106
|
+
.set({ config })
|
|
107
|
+
.where(and(eq(connectors.id, row.id), eq(connectors.projectId, row.projectId)))
|
|
108
|
+
.returning();
|
|
109
|
+
const saved = rows[0];
|
|
110
|
+
if (saved === undefined) throw missing();
|
|
111
|
+
return toConnector(saved);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function connectorRowsFor(
|
|
115
|
+
subject: string,
|
|
116
|
+
project: string,
|
|
117
|
+
): Promise<ConnectorRow[]> {
|
|
118
|
+
const projectId = await projectIdOrThrow(subject, project);
|
|
119
|
+
return getDb()
|
|
120
|
+
.select()
|
|
121
|
+
.from(connectors)
|
|
122
|
+
.where(eq(connectors.projectId, projectId))
|
|
123
|
+
.orderBy(asc(connectors.id));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export async function listConnectorsForUser(
|
|
127
|
+
subject: string,
|
|
128
|
+
project: string,
|
|
129
|
+
): Promise<Connector[]> {
|
|
130
|
+
return (await connectorRowsFor(subject, project)).map(toConnector);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function rowsByIds(ids: string[]): Promise<ConnectorRow[]> {
|
|
134
|
+
if (ids.length === 0) return [];
|
|
135
|
+
return getDb()
|
|
136
|
+
.select()
|
|
137
|
+
.from(connectors)
|
|
138
|
+
.where(inArray(connectors.id, ids))
|
|
139
|
+
.orderBy(asc(connectors.id));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface ConnectorSummary {
|
|
143
|
+
id: string;
|
|
144
|
+
name: string;
|
|
145
|
+
url: string;
|
|
146
|
+
transport: string;
|
|
147
|
+
signIn: ConnectorSignIn;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export async function connectorSummariesByIds(ids: string[]): Promise<ConnectorSummary[]> {
|
|
151
|
+
return (await rowsByIds(ids)).map((row) => ({
|
|
152
|
+
id: row.id,
|
|
153
|
+
name: row.name,
|
|
154
|
+
url: row.url,
|
|
155
|
+
transport: row.transport,
|
|
156
|
+
signIn: signInState(readConfig(row.config)),
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export async function connectorNamesByIds(
|
|
161
|
+
ids: string[],
|
|
162
|
+
): Promise<{ id: string; name: string }[]> {
|
|
163
|
+
return (await rowsByIds(ids)).map((row) => ({ id: row.id, name: row.name }));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export async function getConnectorForUser(
|
|
167
|
+
subject: string,
|
|
168
|
+
id: string,
|
|
169
|
+
): Promise<Connector> {
|
|
170
|
+
return toConnector(await ownedConnectorOrThrow(subject, id));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function insertConnector(
|
|
174
|
+
projectId: string,
|
|
175
|
+
name: string,
|
|
176
|
+
url: URL,
|
|
177
|
+
config: ConnectorConfig,
|
|
178
|
+
): Promise<Connector> {
|
|
179
|
+
const rows = await getDb()
|
|
180
|
+
.insert(connectors)
|
|
181
|
+
.values({
|
|
182
|
+
id: newId(),
|
|
183
|
+
projectId,
|
|
184
|
+
name,
|
|
185
|
+
url: connectorUrlText(url),
|
|
186
|
+
transport: 'http',
|
|
187
|
+
config,
|
|
188
|
+
})
|
|
189
|
+
.returning();
|
|
190
|
+
const row = rows[0];
|
|
191
|
+
if (row === undefined)
|
|
192
|
+
throw new ConnectorError('connector insert returned no row', 500);
|
|
193
|
+
return toConnector(row);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const UNVERIFIED = {
|
|
197
|
+
at: '',
|
|
198
|
+
server: '',
|
|
199
|
+
version: '',
|
|
200
|
+
protocol: '',
|
|
201
|
+
icon: '',
|
|
202
|
+
tools: 0,
|
|
203
|
+
catalog: [],
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export async function createPendingConnectorForUser(
|
|
207
|
+
subject: string,
|
|
208
|
+
project: string,
|
|
209
|
+
input: { name: unknown; url: unknown },
|
|
210
|
+
): Promise<Connector> {
|
|
211
|
+
const name = connectorName(input.name);
|
|
212
|
+
const url = parseConnectorUrl(input.url);
|
|
213
|
+
const projectId = await projectIdOrThrow(subject, project);
|
|
214
|
+
return insertConnector(projectId, name, url, {
|
|
215
|
+
auth: { kind: 'none' },
|
|
216
|
+
createdAt: new Date().toISOString(),
|
|
217
|
+
verified: UNVERIFIED,
|
|
218
|
+
oauth: true,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export async function createConnectorForUser(
|
|
223
|
+
subject: string,
|
|
224
|
+
project: string,
|
|
225
|
+
input: ConnectorInput,
|
|
226
|
+
): Promise<Connector> {
|
|
227
|
+
const name = connectorName(input.name);
|
|
228
|
+
const url = parseConnectorUrl(input.url);
|
|
229
|
+
const auth = connectorAuth(input.header, input.value);
|
|
230
|
+
const projectId = await projectIdOrThrow(subject, project);
|
|
231
|
+
const verified = stamp(await verifyRemoteMcp(url, auth));
|
|
232
|
+
return insertConnector(projectId, name, url, {
|
|
233
|
+
auth,
|
|
234
|
+
createdAt: new Date().toISOString(),
|
|
235
|
+
verified,
|
|
236
|
+
oauth: await oauthCapable(url, auth),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function oauthCapable(url: URL, auth: ConnectorAuth): Promise<boolean> {
|
|
241
|
+
if (auth.kind === 'oauth') return true;
|
|
242
|
+
if (auth.kind === 'header') return false;
|
|
243
|
+
return advertisesOAuth(url);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async function freshAuth(
|
|
247
|
+
auth: ConnectorAuth,
|
|
248
|
+
resource: string,
|
|
249
|
+
): Promise<ConnectorAuth> {
|
|
250
|
+
if (auth.kind !== 'oauth' || !oauthExpired(auth)) return auth;
|
|
251
|
+
return refreshOAuth(auth, resource);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export async function reconnectConnectorForUser(
|
|
255
|
+
subject: string,
|
|
256
|
+
id: string,
|
|
257
|
+
auth: OAuthAuth,
|
|
258
|
+
): Promise<Connector> {
|
|
259
|
+
const row = await ownedConnectorOrThrow(subject, id);
|
|
260
|
+
const config = readConfig(row.config);
|
|
261
|
+
const url = parseConnectorUrl(row.url);
|
|
262
|
+
const verified = stamp(await verifyRemoteMcp(url, auth));
|
|
263
|
+
return saveConfig(row, { ...config, auth, verified, oauth: true });
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export async function disconnectConnectorForUser(
|
|
267
|
+
subject: string,
|
|
268
|
+
id: string,
|
|
269
|
+
): Promise<Connector> {
|
|
270
|
+
const row = await ownedConnectorOrThrow(subject, id);
|
|
271
|
+
const config = readConfig(row.config);
|
|
272
|
+
if (config.auth.kind !== 'oauth')
|
|
273
|
+
throw new ConnectorError('that connector is not signed in', 400);
|
|
274
|
+
return saveConfig(row, { ...config, auth: { kind: 'none' }, oauth: true });
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export async function verifyConnectorForUser(
|
|
278
|
+
subject: string,
|
|
279
|
+
id: string,
|
|
280
|
+
): Promise<ConnectorCheck> {
|
|
281
|
+
const row = await ownedConnectorOrThrow(subject, id);
|
|
282
|
+
const config = readConfig(row.config);
|
|
283
|
+
try {
|
|
284
|
+
const url = parseConnectorUrl(row.url);
|
|
285
|
+
const auth = await freshAuth(config.auth, url.toString());
|
|
286
|
+
const verified = stamp(await verifyRemoteMcp(url, auth));
|
|
287
|
+
const oauth = config.oauth || (await oauthCapable(url, auth));
|
|
288
|
+
await saveConfig(row, { ...config, auth, verified, oauth });
|
|
289
|
+
return { id: row.id, name: row.name, ok: true, verified };
|
|
290
|
+
} catch (err) {
|
|
291
|
+
if (!(err instanceof ConnectorVerifyError)) throw err;
|
|
292
|
+
return { id: row.id, name: row.name, ok: false, reason: err.message };
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export async function renameConnectorForUser(
|
|
297
|
+
subject: string,
|
|
298
|
+
id: string,
|
|
299
|
+
raw: string,
|
|
300
|
+
): Promise<Connector> {
|
|
301
|
+
const name = connectorName(raw);
|
|
302
|
+
const row = await ownedConnectorOrThrow(subject, id);
|
|
303
|
+
await assertRenameFreeOfClash(row.id, name);
|
|
304
|
+
const rows = await getDb()
|
|
305
|
+
.update(connectors)
|
|
306
|
+
.set({ name })
|
|
307
|
+
.where(and(eq(connectors.id, row.id), eq(connectors.projectId, row.projectId)))
|
|
308
|
+
.returning();
|
|
309
|
+
const saved = rows[0];
|
|
310
|
+
if (saved === undefined) throw missing();
|
|
311
|
+
return toConnector(saved);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export async function deleteConnectorForUser(
|
|
315
|
+
subject: string,
|
|
316
|
+
id: string,
|
|
317
|
+
): Promise<DeletedConnector> {
|
|
318
|
+
const row = await ownedConnectorOrThrow(subject, id);
|
|
319
|
+
const gone = await getDb()
|
|
320
|
+
.delete(connectors)
|
|
321
|
+
.where(eq(connectors.id, row.id))
|
|
322
|
+
.returning({ id: connectors.id, name: connectors.name });
|
|
323
|
+
const deleted = gone[0];
|
|
324
|
+
if (deleted === undefined) throw missing();
|
|
325
|
+
return deleted;
|
|
326
|
+
}
|