@stage-labs/metro 0.1.0-beta.35 → 0.1.0-beta.37

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stage-labs/metro",
3
- "version": "0.1.0-beta.35",
3
+ "version": "0.1.0-beta.37",
4
4
  "description": "The metro command line. Sign in once per machine, then hand your MCP connector list to Claude Code without the credentials touching disk, argv or shell history.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,3 +1,4 @@
1
+ import { webhookPort } from './tunnel.js';
1
2
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
3
  import { errMsg, log } from './log.js';
3
4
  import { publicBaseOrDefault } from './attach-serve.js';
@@ -89,10 +90,10 @@ export function mcpEndpoint(): string {
89
90
  return `${publicBaseOrDefault()}/mcp`;
90
91
  }
91
92
 
92
- const LOCAL_MCP_ENDPOINT = 'http://127.0.0.1:8420/mcp';
93
+ const localMcpEndpoint = (): string => `http://127.0.0.1:${String(webhookPort())}/mcp`;
93
94
 
94
95
  export function mcpAddCommand(key: string): string {
95
- const url = `${LOCAL_MCP_ENDPOINT}?token=${key}`;
96
+ const url = `${localMcpEndpoint()}?token=${key}`;
96
97
  return `claude mcp add --transport http ${SERVER_NAME} "${url}"`;
97
98
  }
98
99
 
@@ -105,7 +106,7 @@ interface KeyPayload {
105
106
  function credentials(key: string): KeyPayload {
106
107
  return {
107
108
  key,
108
- endpoint: `${LOCAL_MCP_ENDPOINT}?token=${key}`,
109
+ endpoint: `${localMcpEndpoint()}?token=${key}`,
109
110
  command: mcpAddCommand(key),
110
111
  };
111
112
  }
@@ -23,6 +23,7 @@ import {
23
23
  import type { AccountRef } from './account-attach.js';
24
24
  import {
25
25
  AGENT_FILE,
26
+ AgentFileError,
26
27
  agentsDir,
27
28
  listAgentFiles,
28
29
  parseAgentFile,
@@ -224,17 +225,7 @@ export async function localImportAgent(
224
225
  409,
225
226
  );
226
227
  assertUnclaimed(storedAgents(dir), agent);
227
- const file = parseAgentFile(
228
- JSON.stringify({
229
- version: 1,
230
- id: agent.id,
231
- name: agent.name,
232
- key: agent.key,
233
- owner: localOwner(dir),
234
- stations: agent.accounts.map((a) => ({ ...a, allowlist: a.allowlist ?? ['*'] })),
235
- }),
236
- path,
237
- );
228
+ const file = fileFor(agent, localOwner(dir), path);
238
229
  ensureSecureDir(folder);
239
230
  save({ path, file });
240
231
  registerKey(agent.key, agent.id);
@@ -246,6 +237,26 @@ export async function localImportAgent(
246
237
  });
247
238
  }
248
239
 
240
+ function fileFor(agent: LoadedAgent, owner: string | null, path: string): AgentFile {
241
+ try {
242
+ return parseAgentFile(
243
+ JSON.stringify({
244
+ version: 1,
245
+ id: agent.id,
246
+ name: agent.name,
247
+ key: agent.key,
248
+ owner,
249
+ stations: agent.accounts.map((a) => ({ ...a, allowlist: a.allowlist ?? ['*'] })),
250
+ }),
251
+ path,
252
+ );
253
+ } catch (err) {
254
+ if (err instanceof AgentFileError)
255
+ throw new AgentAdminError(`metro's copy of this agent cannot be written here: ${err.message}`, 400);
256
+ throw err;
257
+ }
258
+ }
259
+
249
260
  function removeIfEmpty(folder: string): void {
250
261
  try {
251
262
  rmdirSync(folder);
@@ -7,7 +7,7 @@ import type { LoadedAccount, LoadedAgent, StationSource } from './materialize.js
7
7
 
8
8
  export const AGENT_FILE = 'agent.json';
9
9
 
10
- const KEY_RE = /^mk_[A-Za-z0-9_-]{43}$/;
10
+ const KEY_RE = /^mk_[A-Za-z0-9_-]{16,128}$/;
11
11
  const ADDRESS_RE = /^0x[0-9a-f]{40}$/;
12
12
  const STATION_NAMES = new Set<string>(STATIONS);
13
13
 
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.1.0-beta.35"
2
+ "version": "0.1.0-beta.37"
3
3
  }