@stage-labs/metro 0.1.0-beta.62 → 0.1.0-beta.63

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.62",
3
+ "version": "0.1.0-beta.63",
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": {
@@ -25,7 +25,7 @@ function authorized(req: IncomingMessage, name: string): boolean {
25
25
  );
26
26
  }
27
27
 
28
- const DEFAULT_PUBLIC_BASE = 'https://mcp.metro.box';
28
+ const DEFAULT_PUBLIC_BASE = 'https://api.metro.box';
29
29
 
30
30
  export function publicBaseUrl(): string | null {
31
31
  const env = process.env.METRO_PUBLIC_URL?.trim();
@@ -190,6 +190,7 @@ export function localSessionApis(deps: LocalModeDeps): SessionApis {
190
190
  localCli,
191
191
  claudeApi: { authorize: (subject) => { assertLocalOwner(subject); } },
192
192
  updateApi: { authorize: (subject) => { assertLocalOwner(subject); }, restart: deps.restart },
193
+ machineApi: { authorize: (subject) => { assertLocalOwner(subject); } },
193
194
  identity: { owner: localOwner },
194
195
  mode: localModeInfo,
195
196
  };
@@ -0,0 +1,61 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { hostname } from 'node:os';
3
+ import { ApiError } from './api-error.js';
4
+ import { apiFailure, apiSession, cors, sendJson } from './api-http.js';
5
+ import { publicBaseUrl } from './attach-serve.js';
6
+ import { claudeDir } from './claude-files.js';
7
+ import { webhookPort } from './tunnel.js';
8
+ import { METRO_VERSION } from './version.js';
9
+ import { agentsDir } from '../db/file-source.js';
10
+ import { localOwner } from '../db/file-admin.js';
11
+
12
+ const PATH = '/api/server';
13
+
14
+ export interface MachineApiDeps {
15
+ authorize: (subject: string) => void;
16
+ startedAt?: string;
17
+ }
18
+
19
+ const bootedAt = new Date(Date.now() - process.uptime() * 1000).toISOString();
20
+
21
+ export function machineInfo(startedAt = bootedAt): Record<string, unknown> {
22
+ const store = process.env.METRO_RUNTIME_STORE?.trim() ?? '';
23
+ return {
24
+ version: METRO_VERSION,
25
+ owner: localOwner(),
26
+ hostname: hostname(),
27
+ platform: process.platform,
28
+ arch: process.arch,
29
+ port: webhookPort(),
30
+ publicUrl: publicBaseUrl(),
31
+ uptimeSeconds: Math.round(process.uptime()),
32
+ startedAt,
33
+ bun: process.versions.bun ?? null,
34
+ agentsDir: agentsDir(),
35
+ claudeDir: claudeDir(),
36
+ runtimeStore: store === '' ? null : store,
37
+ };
38
+ }
39
+
40
+ export function handleMachineRequest(req: IncomingMessage, res: ServerResponse, deps: MachineApiDeps): boolean {
41
+ const path = (req.url ?? '').split('?')[0] ?? '';
42
+ if (path !== PATH) return false;
43
+ if (req.method === 'OPTIONS') {
44
+ res.writeHead(204, cors(req)).end();
45
+ return true;
46
+ }
47
+ if (req.method !== 'GET') {
48
+ sendJson(req, res, 405, { error: 'method not allowed' });
49
+ return true;
50
+ }
51
+ apiSession(req)
52
+ .then((session) => {
53
+ if (!session) throw new ApiError('unauthorized', 401);
54
+ deps.authorize(session.subject);
55
+ sendJson(req, res, 200, machineInfo(deps.startedAt));
56
+ })
57
+ .catch((err: unknown) => {
58
+ apiFailure(req, res, err, 'machine-api');
59
+ });
60
+ return true;
61
+ }
@@ -18,6 +18,7 @@ import { handleVaultApiRequest, type VaultApiDeps } from './vault-api.js';
18
18
  import { handleServersApiRequest, type ServersApiDeps } from './servers-api.js';
19
19
  import { handleBundleRequest, type BundleApiDeps } from './bundle-api.js';
20
20
  import { handleUpdateRequest, type UpdateApiDeps } from './update-api.js';
21
+ import { handleMachineRequest, type MachineApiDeps } from './machine-api.js';
21
22
 
22
23
  export interface SessionApis {
23
24
  agentApi?: AgentApiDeps;
@@ -26,6 +27,7 @@ export interface SessionApis {
26
27
  vaultApi?: VaultApiDeps;
27
28
  serversApi?: ServersApiDeps;
28
29
  updateApi?: UpdateApiDeps;
30
+ machineApi?: MachineApiDeps;
29
31
  localCli?: LocalCliDeps;
30
32
  claudeApi?: ClaudeApiDeps;
31
33
  connectorApi?: ConnectorApiDeps;
@@ -50,6 +52,7 @@ export function handleSessionApis(
50
52
  ...when(apis.vaultApi, (d) => handleVaultApiRequest(req, res, d)),
51
53
  ...when(apis.serversApi, (d) => handleServersApiRequest(req, res, d)),
52
54
  ...when(apis.updateApi, (d) => handleUpdateRequest(req, res, d)),
55
+ ...when(apis.machineApi, (d) => handleMachineRequest(req, res, d)),
53
56
  ...when(apis.claudeApi, (d) => handleClaudeRequest(req, res, d)),
54
57
  ...when(apis.agentConnectorApi, (d) => handleAgentConnectorRequest(req, res, d)),
55
58
  ...when(apis.agentApi, (d) => handleAgentApiRequest(req, res, d)),
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.1.0-beta.62"
2
+ "version": "0.1.0-beta.63"
3
3
  }