@stage-labs/metro 0.1.0-beta.38 → 0.1.0-beta.39
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 +1 -1
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-import.ts +13 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/paths.ts +2 -1
- package/runtime/node_modules/@metro-labs/mcp/src/db/agent-map.ts +13 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +0 -18
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/accounts.ts +5 -9
- package/runtime/runtime.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stage-labs/metro",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.39",
|
|
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,4 +1,8 @@
|
|
|
1
|
+
import { mkdirSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
1
3
|
import { ApiError } from './api-error.js';
|
|
4
|
+
import { configDir } from './paths.js';
|
|
5
|
+
import { writeSecure } from './secure-fs.js';
|
|
2
6
|
import { carriesSecretsSafely, httpSource } from './runtime-source.js';
|
|
3
7
|
import type { LoadedAgent } from '../db/materialize.js';
|
|
4
8
|
|
|
@@ -49,6 +53,14 @@ async function claim(base: string, code: string, label: string): Promise<string>
|
|
|
49
53
|
return token;
|
|
50
54
|
}
|
|
51
55
|
|
|
56
|
+
export function storeRunToken(agentId: string, token: string, url: string): string {
|
|
57
|
+
const dir = configDir();
|
|
58
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
59
|
+
const path = join(dir, `runtime-${agentId}.json`);
|
|
60
|
+
writeSecure(path, `${JSON.stringify({ token, url }, null, 2)}\n`);
|
|
61
|
+
return path;
|
|
62
|
+
}
|
|
63
|
+
|
|
52
64
|
export async function fetchAgentWithCode(
|
|
53
65
|
code: string,
|
|
54
66
|
label: string,
|
|
@@ -59,5 +71,6 @@ export async function fetchAgentWithCode(
|
|
|
59
71
|
const agent = agents[0];
|
|
60
72
|
if (agent === undefined || agents.length !== 1)
|
|
61
73
|
throw new ApiError('metro returned no agent for that code', 502);
|
|
74
|
+
storeRunToken(agent.id, token, base);
|
|
62
75
|
return agent;
|
|
63
76
|
}
|
|
@@ -17,9 +17,10 @@ export const STATE_DIR =
|
|
|
17
17
|
process.env.METRO_STATE_DIR ?? join(homedir(), '.cache', 'metro');
|
|
18
18
|
mkdirSync(STATE_DIR, { recursive: true });
|
|
19
19
|
|
|
20
|
-
export const
|
|
20
|
+
export const configDir = (): string =>
|
|
21
21
|
process.env.METRO_CONFIG_DIR ??
|
|
22
22
|
join(process.env.XDG_CONFIG_HOME ?? join(homedir(), '.config'), 'metro');
|
|
23
|
+
export const CONFIG_DIR = configDir();
|
|
23
24
|
export const CONFIG_ENV_FILE = join(CONFIG_DIR, '.env');
|
|
24
25
|
|
|
25
26
|
export const TRAINS_ENV_FILE = join(homedir(), '.metro', '.env');
|
|
@@ -45,6 +45,19 @@ export function agentForLine(line: string): string | undefined {
|
|
|
45
45
|
return id === undefined ? undefined : agentNames[id];
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
export interface KnownAccount {
|
|
49
|
+
station: string;
|
|
50
|
+
id: string;
|
|
51
|
+
agentId: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function knownAccounts(): KnownAccount[] {
|
|
55
|
+
return Object.entries(agentMap).flatMap(([key, agentId]) => {
|
|
56
|
+
const cut = key.indexOf('/');
|
|
57
|
+
return cut === -1 ? [] : [{ station: key.slice(0, cut), id: key.slice(cut + 1), agentId }];
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
48
61
|
export function stationAgentIds(station: string): string[] {
|
|
49
62
|
const prefix = `${station}/`;
|
|
50
63
|
return Object.entries(agentMap)
|
|
@@ -108,24 +108,6 @@ export function unmovableStations(list: LoadedAccount[]): StationName[] {
|
|
|
108
108
|
];
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
export interface StationRecord {
|
|
112
|
-
id: string;
|
|
113
|
-
agentId: string;
|
|
114
|
-
station: StationName;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export async function listAllStations(): Promise<StationRecord[]> {
|
|
118
|
-
if (!databaseUrl()) return [];
|
|
119
|
-
const rows = await getDb()
|
|
120
|
-
.select({
|
|
121
|
-
id: stations.id,
|
|
122
|
-
agentId: stations.agentId,
|
|
123
|
-
station: stations.station,
|
|
124
|
-
})
|
|
125
|
-
.from(stations);
|
|
126
|
-
return rows;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
111
|
export async function loadAllStationsFor(
|
|
130
112
|
agentId: string,
|
|
131
113
|
): Promise<LoadedAccount[]> {
|
|
@@ -5,12 +5,8 @@ import {
|
|
|
5
5
|
} from '../stations/registry.js';
|
|
6
6
|
import { listEndpoints } from '../daemon/tunnel.js';
|
|
7
7
|
import { hookUrl } from '../stations/attach.js';
|
|
8
|
-
import { agentIdForAccount } from '../db/agent-map.js';
|
|
9
|
-
import {
|
|
10
|
-
listAllStations,
|
|
11
|
-
stationRunsHere,
|
|
12
|
-
type StationRecord,
|
|
13
|
-
} from '../db/materialize.js';
|
|
8
|
+
import { agentIdForAccount, knownAccounts, type KnownAccount } from '../db/agent-map.js';
|
|
9
|
+
import { stationRunsHere } from '../db/materialize.js';
|
|
14
10
|
import { type StationName } from '../db/schema.js';
|
|
15
11
|
|
|
16
12
|
const accountId = (acc: unknown): string | undefined => {
|
|
@@ -99,7 +95,7 @@ async function liveAccounts(
|
|
|
99
95
|
function mergeKnown(
|
|
100
96
|
station: string,
|
|
101
97
|
live: unknown[],
|
|
102
|
-
known:
|
|
98
|
+
known: KnownAccount[],
|
|
103
99
|
): unknown[] {
|
|
104
100
|
const seen = new Set(
|
|
105
101
|
live.flatMap((row) => {
|
|
@@ -116,12 +112,12 @@ function mergeKnown(
|
|
|
116
112
|
async function loadStations(): Promise<ScopedAccounts> {
|
|
117
113
|
const unavailable: string[] = [];
|
|
118
114
|
const accounts: Record<string, unknown[]> = {};
|
|
119
|
-
const known =
|
|
115
|
+
const known = knownAccounts();
|
|
120
116
|
await Promise.all(
|
|
121
117
|
accountStationNames().map(async (station) => {
|
|
122
118
|
const { rows, reachable } = await liveAccounts(station);
|
|
123
119
|
if (!reachable) unavailable.push(station);
|
|
124
|
-
accounts[station] = mergeKnown(station, rows, known);
|
|
120
|
+
accounts[station] = hasTrain(station) ? mergeKnown(station, rows, known) : rows;
|
|
125
121
|
}),
|
|
126
122
|
);
|
|
127
123
|
return { accounts, unavailable };
|
package/runtime/runtime.json
CHANGED