@stage-labs/metro 0.1.0-beta.45 → 0.1.0-beta.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/api.js +5 -5
  2. package/dist/bedrock-proxy.js +1 -1
  3. package/dist/cli.js +7 -7
  4. package/dist/plugin.js +1 -1
  5. package/dist/prompt.js +1 -1
  6. package/dist/runtime.js +1 -1
  7. package/dist/serve.js +35 -5
  8. package/package.json +1 -1
  9. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +0 -1
  10. package/runtime/node_modules/@metro-labs/mcp/package.json +1 -5
  11. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-import.ts +2 -2
  12. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-grant.ts +1 -1
  13. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-serve.ts +2 -5
  14. package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +13 -26
  15. package/runtime/node_modules/@metro-labs/mcp/src/daemon/claude-files.ts +1 -3
  16. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +2 -4
  17. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connect-hint.ts +11 -10
  18. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth.ts +2 -2
  19. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +3 -5
  20. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +1 -4
  21. package/runtime/node_modules/@metro-labs/mcp/src/daemon/events.ts +2 -2
  22. package/runtime/node_modules/@metro-labs/mcp/src/daemon/http.ts +1 -2
  23. package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +4 -83
  24. package/runtime/node_modules/@metro-labs/mcp/src/daemon/is-record.ts +2 -0
  25. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-cli-api.ts +2 -2
  26. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-mode.ts +3 -39
  27. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-owner.ts +14 -0
  28. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-client.ts +1 -4
  29. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-discovery.ts +1 -4
  30. package/runtime/node_modules/@metro-labs/mcp/src/daemon/paths.ts +0 -33
  31. package/runtime/node_modules/@metro-labs/mcp/src/daemon/protocol.ts +3 -3
  32. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +2 -2
  33. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-config.ts +1 -6
  34. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +1 -42
  35. package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-auth.ts +2 -11
  36. package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-routes.ts +2 -2
  37. package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +8 -90
  38. package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-api.ts +1 -1
  39. package/runtime/node_modules/@metro-labs/mcp/src/daemon/version.ts +1 -0
  40. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-connectors.ts +1 -1
  41. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-config.ts +1 -4
  42. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-relay.ts +1 -1
  43. package/runtime/node_modules/@metro-labs/mcp/src/db/file-admin.ts +20 -34
  44. package/runtime/node_modules/@metro-labs/mcp/src/db/file-source.ts +1 -3
  45. package/runtime/node_modules/@metro-labs/mcp/src/db/local-connectors.ts +11 -22
  46. package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +3 -13
  47. package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +3 -3
  48. package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +1 -1
  49. package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +2 -4
  50. package/runtime/node_modules/@metro-labs/mcp/src/mcp/event-store.ts +1 -1
  51. package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +1 -1
  52. package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +1 -21
  53. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-resolve.ts +1 -1
  54. package/runtime/node_modules/@metro-labs/mcp/src/stations/lines.ts +1 -1
  55. package/runtime/node_modules/@metro-labs/telegram/package.json +0 -2
  56. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +0 -1
  57. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +0 -1
  58. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +2 -5
  59. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +0 -1
  60. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +0 -1
  61. package/runtime/runtime.json +1 -1
  62. package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +0 -45
@@ -5,6 +5,7 @@ import type {
5
5
  VerifiedServer,
6
6
  } from '../daemon/connector-verify.js';
7
7
  import { readStoredTools } from '../daemon/connector-tools.js';
8
+ import { isRecord } from '../daemon/is-record.js';
8
9
 
9
10
  const CONNECTOR_NAME_MAX = 64;
10
11
  const HEADER_NAME_RE = /^[A-Za-z0-9!#$%&'*+.^_`|~-]{1,64}$/;
@@ -22,10 +23,6 @@ export interface ConnectorConfig {
22
23
  oauth: boolean;
23
24
  }
24
25
 
25
- function isRecord(value: unknown): value is Record<string, unknown> {
26
- return typeof value === 'object' && value !== null && !Array.isArray(value);
27
- }
28
-
29
26
  function text(value: unknown): string {
30
27
  return typeof value === 'string' ? value : '';
31
28
  }
@@ -25,7 +25,7 @@ export function staleUsable(auth: OAuthAuth, now = Date.now()): boolean {
25
25
  return auth.expiresAt === undefined || auth.expiresAt > now;
26
26
  }
27
27
 
28
- export function headerAuthHeaders(
28
+ function headerAuthHeaders(
29
29
  auth: ConnectorAuth,
30
30
  ): Record<string, string> | null {
31
31
  return auth.kind === 'header' ? { [auth.name]: auth.value } : null;
@@ -1,12 +1,4 @@
1
- import {
2
- closeSync,
3
- existsSync,
4
- openSync,
5
- readFileSync,
6
- rmdirSync,
7
- rmSync,
8
- writeSync,
9
- } from 'node:fs';
1
+ import { existsSync, readFileSync, rmdirSync, rmSync } from 'node:fs';
10
2
  import { join } from 'node:path';
11
3
  import { ApiError } from '../daemon/api-error.js';
12
4
  import { ensureSecureDir, writeSecure } from '../daemon/secure-fs.js';
@@ -54,31 +46,23 @@ export function localOwner(dir = agentsDir()): string | null {
54
46
  }
55
47
  }
56
48
 
57
- function writeOwnerOnce(path: string, address: string): boolean {
58
- let fd: number;
59
- try {
60
- fd = openSync(path, 'wx', 0o600);
61
- } catch (err) {
62
- if ((err as NodeJS.ErrnoException).code === 'EEXIST') return false;
63
- throw err;
64
- }
65
- writeSync(fd, `${address}\n`);
66
- closeSync(fd);
67
- return true;
68
- }
69
-
70
- export function claimLocalOwner(raw: string, dir = agentsDir()): Promise<string> {
49
+ export function setLocalOwner(raw: string, dir = agentsDir()): string {
71
50
  const address = normalizeAddress(raw);
72
- if (address === null)
73
- return Promise.reject(new ApiError('an Ethereum address is required', 400));
51
+ if (address === null) throw new ApiError(`'${raw}' is not an Ethereum address`, 400);
74
52
  ensureSecureDir(dir);
75
- if (writeOwnerOnce(join(dir, OWNER_FILE), address))
76
- return Promise.resolve(address);
77
- if (localOwner(dir) !== address)
53
+ writeSecure(join(dir, OWNER_FILE), `${address}\n`);
54
+ return address;
55
+ }
56
+
57
+ export function ownerSignIn(raw: string, dir = agentsDir()): Promise<string> {
58
+ const owner = localOwner(dir);
59
+ if (owner === null)
78
60
  return Promise.reject(
79
- new ApiError('this machine belongs to another wallet', 403),
61
+ new ApiError('this machine has no owner yet: start it with metro serve --owner <address>', 403),
80
62
  );
81
- return Promise.resolve(address);
63
+ if (owner !== normalizeAddress(raw))
64
+ return Promise.reject(new ApiError('this machine belongs to another wallet', 403));
65
+ return Promise.resolve(owner);
82
66
  }
83
67
 
84
68
  function isOwner(subject: string, dir: string): boolean {
@@ -278,10 +262,12 @@ export function localSetAgentConnectors(
278
262
  save(stored);
279
263
  }
280
264
 
281
- export function localAgentsWith(connectorId: string, dir = agentsDir()): { id: string; name: string }[] {
282
- return storedAgents(dir)
283
- .filter((s) => s.file.connectors.includes(connectorId))
284
- .map((s) => ({ id: s.file.id, name: s.file.name }));
265
+ export function localHoldEverywhere(connectorId: string, dir = agentsDir()): void {
266
+ for (const stored of storedAgents(dir)) {
267
+ if (stored.file.connectors.includes(connectorId)) continue;
268
+ stored.file.connectors = [...stored.file.connectors, connectorId];
269
+ save(stored);
270
+ }
285
271
  }
286
272
 
287
273
  export function localDropConnectorEverywhere(connectorId: string, dir = agentsDir()): void {
@@ -4,6 +4,7 @@ import { join } from 'node:path';
4
4
  import { AGENT_NAME_RE, ID_RE } from './ids.js';
5
5
  import { STATIONS, type StationName } from './schema.js';
6
6
  import type { LoadedAccount, LoadedAgent, StationSource } from './materialize.js';
7
+ import { isRecord } from '../daemon/is-record.js';
7
8
 
8
9
  export const AGENT_FILE = 'agent.json';
9
10
 
@@ -30,9 +31,6 @@ export function agentsDir(): string {
30
31
  : join(homedir(), '.metro', 'agents');
31
32
  }
32
33
 
33
- const isRecord = (v: unknown): v is Record<string, unknown> =>
34
- typeof v === 'object' && v !== null && !Array.isArray(v);
35
-
36
34
  function fail(path: string, reason: string): never {
37
35
  throw new AgentFileError(`${path}: ${reason}`);
38
36
  }
@@ -41,8 +41,8 @@ import {
41
41
  assertLocalOwner,
42
42
  connectorIdsOfLocalAgent,
43
43
  localAgentConnectorIds,
44
- localAgentsWith,
45
44
  localDropConnectorEverywhere,
45
+ localHoldEverywhere,
46
46
  localOwnedAgentOrThrow,
47
47
  localSetAgentConnectors,
48
48
  LOCAL_PROJECT_ID,
@@ -70,7 +70,7 @@ const isRow = (v: unknown): v is { id: string; name: string; url: string; config
70
70
  const asList = (value: unknown): unknown[] =>
71
71
  Array.isArray(value) ? value.map((item: unknown) => item) : [];
72
72
 
73
- export function readLocalConnectors(dir = agentsDir()): LocalConnectorRow[] {
73
+ function readLocalConnectors(dir = agentsDir()): LocalConnectorRow[] {
74
74
  const raw = readJson<{ connectors?: unknown }>(filePath(dir), {}, {
75
75
  warn: 'connectors.json: malformed, ignoring',
76
76
  });
@@ -125,9 +125,16 @@ export async function localGetConnector(subject: string, id: string, dir = agent
125
125
  return Promise.resolve(connectorFromRow(rowOrThrow(subject, id, dir)));
126
126
  }
127
127
 
128
+ function assertNameFree(name: string, exceptId: string | null, dir: string): void {
129
+ if (readLocalConnectors(dir).some((r) => r.id !== exceptId && r.name === name))
130
+ throw new ConnectorError(`a connector named '${name}' already exists on this daemon`, 409);
131
+ }
132
+
128
133
  function insert(dir: string, name: string, url: URL, config: ConnectorConfig): Connector {
134
+ assertNameFree(name, null, dir);
129
135
  const row: LocalConnectorRow = { id: newId(), name, url: connectorUrlText(url), transport: 'http', config };
130
136
  writeRows(dir, [...readLocalConnectors(dir), row]);
137
+ localHoldEverywhere(row.id, dir);
131
138
  return connectorFromRow(row);
132
139
  }
133
140
 
@@ -201,24 +208,10 @@ export async function localVerifyConnector(subject: string, id: string, dir = ag
201
208
  }
202
209
  }
203
210
 
204
- const nameClash = (name: string, agent: string): ConnectorError =>
205
- new ConnectorError(`the agent '${agent}' already has a connector named '${name}'`, 409);
206
-
207
- function clashFor(name: string, exceptId: string, agentIds: string[], dir: string): string | null {
208
- const rows = readLocalConnectors(dir);
209
- for (const agentId of agentIds) {
210
- const held = connectorIdsOfLocalAgent(agentId, dir) ?? [];
211
- const other = rows.find((r) => r.id !== exceptId && held.includes(r.id) && r.name === name);
212
- if (other !== undefined) return localAgentsWith(other.id, dir).find((a) => a.id === agentId)?.name ?? agentId;
213
- }
214
- return null;
215
- }
216
-
217
211
  export async function localRenameConnector(subject: string, id: string, raw: string, dir = agentsDir()): Promise<Connector> {
218
212
  const name = connectorName(raw);
219
213
  const row = rowOrThrow(subject, id, dir);
220
- const clash = clashFor(name, id, localAgentsWith(id, dir).map((a) => a.id), dir);
221
- if (clash !== null) throw nameClash(name, clash);
214
+ assertNameFree(name, id, dir);
222
215
  return Promise.resolve(replace(dir, { ...row, name }));
223
216
  }
224
217
 
@@ -252,11 +245,7 @@ export async function localAddConnector(subject: string, agentId: string, connec
252
245
  const row = readLocalConnectors(dir).find((r) => r.id === connectorId);
253
246
  if (row === undefined) throw missing();
254
247
  const held = localAgentConnectorIds(subject, agentId, dir);
255
- if (!held.includes(connectorId)) {
256
- const clash = clashFor(row.name, connectorId, [agentId], dir);
257
- if (clash !== null) throw nameClash(row.name, clash);
258
- localSetAgentConnectors(subject, agentId, [...held, connectorId], dir);
259
- }
248
+ if (!held.includes(connectorId)) localSetAgentConnectors(subject, agentId, [...held, connectorId], dir);
260
249
  return { ...agent, connectorIds: localAgentConnectorIds(subject, agentId, dir) };
261
250
  }
262
251
 
@@ -21,7 +21,6 @@ import {
21
21
  type AllowlistMap,
22
22
  } from './agent-map.js';
23
23
  import { setKeyMap } from './key-map.js';
24
- import { carryTokenStores, PREVIOUS_ACCOUNT_ID } from './token-carry.js';
25
24
  import { STATIONS, stations, agents, type StationName } from './schema.js';
26
25
 
27
26
  interface StationTarget {
@@ -149,7 +148,7 @@ export async function loadAgentForRuntime(
149
148
  };
150
149
  }
151
150
 
152
- export const pgSource: StationSource = () => loadAgents();
151
+ const pgSource: StationSource = () => loadAgents();
153
152
 
154
153
  async function loadAgents(): Promise<LoadedAgent[]> {
155
154
  const db = getDb();
@@ -182,14 +181,6 @@ async function loadAgents(): Promise<LoadedAgent[]> {
182
181
  return out;
183
182
  }
184
183
 
185
- function trainConfig(
186
- config: Record<string, unknown>,
187
- ): Record<string, unknown> {
188
- return Object.fromEntries(
189
- Object.entries(config).filter(([key]) => key !== PREVIOUS_ACCOUNT_ID),
190
- );
191
- }
192
-
193
184
  function trainStubPath(station: StationName): string {
194
185
  return join(trainsDir(), `${station}.ts`);
195
186
  }
@@ -208,7 +199,7 @@ export function writeIfChanged(path: string, content: string): boolean {
208
199
  return true;
209
200
  }
210
201
 
211
- export const isLocalRuntime = (): boolean =>
202
+ const isLocalRuntime = (): boolean =>
212
203
  (process.env.METRO_RUN_TOKEN?.trim() ?? '') !== '';
213
204
 
214
205
  export const stationRunsHere = (station: StationName): boolean =>
@@ -248,12 +239,11 @@ function writeStations(list: LoadedAgent[]): WrittenStations {
248
239
  }
249
240
  setAgentMap(map, names);
250
241
  setAllowlistMap(allow);
251
- carryTokenStores(list.flatMap((agent) => agent.accounts));
252
242
 
253
243
  const active = new Map<StationName, number>();
254
244
  const changed: StationName[] = [];
255
245
  for (const [station, accts] of byStation) {
256
- const records = accts.map((a) => ({ id: a.id, ...trainConfig(a.config) }));
246
+ const records = accts.map((a) => ({ id: a.id, ...a.config }));
257
247
  const path = accountFilePath(station);
258
248
  const content = JSON.stringify(records, null, 2);
259
249
  if (currentText(path) !== content) {
@@ -33,7 +33,7 @@ const CONTROL = /[\u0000-\u001f\u007f]/;
33
33
  const missing = (): ProjectError => new ProjectError('no such project', 404);
34
34
  const forbidden = (why: string): ProjectError => new ProjectError(why, 403);
35
35
 
36
- export function projectName(raw: unknown): string {
36
+ function projectName(raw: unknown): string {
37
37
  const name = typeof raw === 'string' ? raw.trim() : '';
38
38
  if (name === '' || name.length > NAME_MAX)
39
39
  throw new ProjectError(
@@ -54,7 +54,7 @@ export interface Access {
54
54
  role: ProjectRole;
55
55
  }
56
56
 
57
- export async function memberAccessOrThrow(
57
+ async function memberAccessOrThrow(
58
58
  subject: string,
59
59
  projectId: string,
60
60
  ): Promise<Access> {
@@ -101,7 +101,7 @@ async function addMemberRow(
101
101
  .onConflictDoNothing();
102
102
  }
103
103
 
104
- export async function ensureDefaultProject(userId: string): Promise<string> {
104
+ async function ensureDefaultProject(userId: string): Promise<string> {
105
105
  const existing = await getDb()
106
106
  .select({ id: projects.id })
107
107
  .from(projects)
@@ -91,7 +91,7 @@ export async function runtimeLabels(
91
91
  return out;
92
92
  }
93
93
 
94
- export async function runtimeFor(agentId: string): Promise<RuntimeView | null> {
94
+ async function runtimeFor(agentId: string): Promise<RuntimeView | null> {
95
95
  const rows = await getDb()
96
96
  .select({
97
97
  id: runtimes.id,
@@ -23,9 +23,8 @@ export const users = pgTable('users', {
23
23
  address: text('address').unique(),
24
24
  });
25
25
 
26
- export const PROJECT_ROLES = ['admin', 'member'] as const;
27
26
 
28
- export type ProjectRole = (typeof PROJECT_ROLES)[number];
27
+ export type ProjectRole = 'admin' | 'member';
29
28
 
30
29
  export const projects = pgTable('projects', {
31
30
  id: text('id').primaryKey(),
@@ -80,9 +79,8 @@ export const stations = pgTable('stations', {
80
79
  config: jsonb('config').notNull(),
81
80
  });
82
81
 
83
- export const CONNECTOR_TRANSPORTS = ['http', 'sse'] as const;
84
82
 
85
- export type ConnectorTransport = (typeof CONNECTOR_TRANSPORTS)[number];
83
+ export type ConnectorTransport = 'http' | 'sse';
86
84
 
87
85
  export const connectors = pgTable(
88
86
  'connectors',
@@ -6,7 +6,7 @@ import type {
6
6
  import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
7
7
  import { frameInScope, frameLine } from './frame-scope.js';
8
8
 
9
- export const EVENT_STORE_MAX = 500;
9
+ const EVENT_STORE_MAX = 500;
10
10
 
11
11
  interface StoredEvent {
12
12
  eventId: EventId;
@@ -9,6 +9,7 @@ import {
9
9
  authConfigFromEnv,
10
10
  authenticate,
11
11
  } from '../mcp/request-identity.js';
12
+ import { METRO_VERSION } from '../daemon/version.js';
12
13
 
13
14
  export type MonitorCall = (
14
15
  train: string,
@@ -18,7 +19,6 @@ export type MonitorCall = (
18
19
 
19
20
  const KEEPALIVE_MS = 25_000;
20
21
  const CALL_BODY_MAX = 256 * 1024;
21
- const METRO_VERSION = process.env.npm_package_version ?? '0.1.0-beta.15';
22
22
 
23
23
  function monitorEnabled(): boolean {
24
24
  return hasAnyKey() || authConfigFromEnv().sessionSecret !== '';
@@ -7,7 +7,6 @@ export type Die = (msg: string) => never;
7
7
  export interface MakeLoaderOpts<T> {
8
8
  prefix: string;
9
9
  file: string;
10
- allowlistEnv: string[];
11
10
  validate: (raw: T[], die: Die) => void;
12
11
  }
13
12
 
@@ -24,17 +23,6 @@ export function makeAccountStore<T extends { id: string }>(
24
23
  process.exit(2);
25
24
  };
26
25
 
27
- let allowlistRaw: string | undefined;
28
- for (const k of opts.allowlistEnv) {
29
- allowlistRaw ??= process.env[k];
30
- }
31
- const allowlist = new Set(
32
- (allowlistRaw ?? '')
33
- .split(',')
34
- .map((s) => s.trim())
35
- .filter(Boolean),
36
- );
37
-
38
26
  function loadAccounts(): T[] {
39
27
  if (!existsSync(opts.file))
40
28
  return die(
@@ -50,15 +38,7 @@ export function makeAccountStore<T extends { id: string }>(
50
38
  if (!Array.isArray(raw) || raw.length === 0)
51
39
  die(`${opts.file} must be a non-empty array`);
52
40
  opts.validate(raw, die);
53
- const selected = allowlist.size
54
- ? raw.filter((a) => allowlist.has(a.id))
55
- : raw;
56
- if (selected.length === 0) {
57
- die(
58
- `no accounts match ${opts.allowlistEnv[0]} (${[...allowlist].join(', ')})`,
59
- );
60
- }
61
- return selected;
41
+ return raw;
62
42
  }
63
43
 
64
44
  return { die, loadAccounts };
@@ -151,7 +151,7 @@ const sourcesOf = (a: CanonicalAttachment): string[] =>
151
151
  return typeof v === 'string' && v.length > 0;
152
152
  });
153
153
 
154
- export async function resolveAttachment(
154
+ async function resolveAttachment(
155
155
  a: CanonicalAttachment,
156
156
  budget: InlineBudget = { used: 0 },
157
157
  opts: ResolveOptions = {},
@@ -5,7 +5,7 @@ const PREFIX = 'metro://';
5
5
  const build = (station: string, ...seg: (string | number)[]): Line =>
6
6
  asLine(`${PREFIX}${station}/${seg.map(String).join('/')}`);
7
7
 
8
- export function parseAccountScoped(
8
+ function parseAccountScoped(
9
9
  line: Line | string,
10
10
  station: string,
11
11
  validate?: (resource: string) => boolean,
@@ -18,11 +18,9 @@
18
18
  },
19
19
  "devDependencies": {
20
20
  "@stage-labs/config": "0.1.0-beta.2",
21
- "@types/qrcode-terminal": "^0.12.2",
22
21
  "eslint": "^10.3.0",
23
22
  "knip": "^6.15.0",
24
23
  "madge": "^8.0.0",
25
- "qrcode-terminal": "^0.12.0",
26
24
  "typescript": "^5"
27
25
  }
28
26
  }
@@ -29,7 +29,6 @@ function validateAccount(a: UserAccount, seen: Set<string>, die: Die): void {
29
29
  export const { loadAccounts } = makeAccountStore<UserAccount>({
30
30
  prefix: 'telegram',
31
31
  file: ACCOUNTS_FILE,
32
- allowlistEnv: ['TELEGRAM_ONLY_ACCOUNTS', 'TELEGRAM_ACCOUNTS'],
33
32
  validate(raw, die) {
34
33
  const seen = new Set<string>();
35
34
  for (const a of raw) validateAccount(a, seen, die);
@@ -19,7 +19,6 @@ export interface AccountConfig {
19
19
  export const { loadAccounts } = makeAccountStore<AccountConfig>({
20
20
  prefix: 'telegram-bot',
21
21
  file: ACCOUNTS_FILE,
22
- allowlistEnv: ['TELEGRAM_BOT_ONLY_ACCOUNTS', 'TELEGRAM_BOT_ACCOUNTS'],
23
22
  validate(raw, die) {
24
23
  const seenId = new Set<string>();
25
24
  const seenTok = new Set<string>();
@@ -1,12 +1,9 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { Line, asLine } from '@metro-labs/mcp/lines';
2
+ import { Line } from '@metro-labs/mcp/lines';
3
3
  import { mintId, type MetroEvent } from '@metro-labs/mcp/events';
4
4
  import type { Endpoint } from '@metro-labs/mcp/endpoints';
5
5
  import type { Station, Verb } from '@metro-labs/mcp/stations/types';
6
6
 
7
- const sessionOwner = (sessionId: string): Line =>
8
- asLine(`metro://session/${sessionId}`);
9
-
10
7
  export const webhookStation: Station = {
11
8
  name: 'webhook',
12
9
  hasAccounts: true,
@@ -31,7 +28,7 @@ export function webhookEntry(
31
28
  line,
32
29
  lineName: endpoint.label,
33
30
  from: line,
34
- to: endpoint.session ? sessionOwner(endpoint.session) : line,
31
+ to: line,
35
32
  messageId:
36
33
  headers['x-github-delivery'] ?? headers['x-request-id'] ?? randomUUID(),
37
34
  text: `${headers['x-github-event'] ?? headers['x-intercom-topic'] ?? 'event'} ${method} ${url}`,
@@ -28,7 +28,6 @@ function validateAccount(
28
28
  export const { loadAccounts } = makeAccountStore<WhatsAppAccount>({
29
29
  prefix: 'whatsapp',
30
30
  file: ACCOUNTS_FILE,
31
- allowlistEnv: ['WHATSAPP_ONLY_ACCOUNTS', 'WHATSAPP_ACCOUNTS'],
32
31
  validate(raw, die) {
33
32
  const seen = new Set<string>();
34
33
  for (const a of raw) validateAccount(a, seen, die);
@@ -28,7 +28,6 @@ export interface AccountConfig {
28
28
  export const { loadAccounts } = makeAccountStore<AccountConfig>({
29
29
  prefix: 'xmtp',
30
30
  file: ACCOUNTS_FILE,
31
- allowlistEnv: ['XMTP_ONLY_ACCOUNTS', 'XMTP_ACCOUNTS'],
32
31
  validate(raw, die) {
33
32
  const seen = new Set<string>();
34
33
  for (const a of raw) {
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.1.0-beta.45"
2
+ "version": "0.1.0-beta.47"
3
3
  }
@@ -1,45 +0,0 @@
1
- import { existsSync, readFileSync } from 'node:fs';
2
- import { tokenStorePath } from '@metro-labs/whatsapp/tokens';
3
- import { errMsg, log } from '../daemon/log.js';
4
- import { writeSecure } from '../daemon/secure-fs.js';
5
-
6
- export const PREVIOUS_ACCOUNT_ID = 'previousAccountId';
7
-
8
- export interface CarriedStation {
9
- station: string;
10
- id: string;
11
- config: Record<string, unknown>;
12
- }
13
-
14
- export function previousAccountId(
15
- config: Record<string, unknown>,
16
- ): string | undefined {
17
- const raw = config[PREVIOUS_ACCOUNT_ID];
18
- return typeof raw === 'string' && raw !== '' ? raw : undefined;
19
- }
20
-
21
- export function carryTokenStores(list: CarriedStation[]): number {
22
- let carried = 0;
23
- for (const s of list) {
24
- if (s.station !== 'whatsapp') continue;
25
- const previous = previousAccountId(s.config);
26
- if (previous === undefined || previous === s.id) continue;
27
- const from = tokenStorePath(previous);
28
- const to = tokenStorePath(s.id);
29
- if (!existsSync(from) || existsSync(to)) continue;
30
- try {
31
- writeSecure(to, readFileSync(from, 'utf8'));
32
- carried += 1;
33
- log.info(
34
- { from: previous, to: s.id },
35
- 'db: carried the whatsapp token store to the station id',
36
- );
37
- } catch (err) {
38
- log.error(
39
- { from: previous, to: s.id, err: errMsg(err) },
40
- 'db: could not carry the whatsapp token store',
41
- );
42
- }
43
- }
44
- return carried;
45
- }