@stage-labs/metro 0.1.0-beta.60 → 0.1.0-beta.61
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/cli.js +6 -6
- package/dist/serve.js +11 -33
- package/package.json +1 -1
- package/runtime/runtime.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -10,13 +10,13 @@ import { serve } from './serve.js';
|
|
|
10
10
|
import { currentVersion } from './version.js';
|
|
11
11
|
const USAGE = `metro — run your agent on this machine
|
|
12
12
|
|
|
13
|
-
metro serve [--port <n>] [--
|
|
13
|
+
metro serve [--port <n>] [--owner <address>]
|
|
14
14
|
run the daemon: the agent, its channels and its connectors live in
|
|
15
|
-
~/.metro/agents here, and
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
~/.metro/agents here, and it is published through Tailscale Funnel at a
|
|
16
|
+
permanent https://<machine>.<tailnet>.ts.net (Tailscale installed and
|
|
17
|
+
signed in on this machine); the page at metro.box manages it through the
|
|
18
|
+
link it prints; --owner names the one wallet that may sign in, needed on
|
|
19
|
+
the first start only
|
|
20
20
|
metro stop stop the metro daemon on this machine
|
|
21
21
|
metro tail <agent-id>
|
|
22
22
|
follow this machine's inbound events, one JSON line each
|
package/dist/serve.js
CHANGED
|
@@ -10,8 +10,8 @@ const SCRUBBED = new Set(['METRO_RUN_TOKEN', 'METRO_AGENT', 'DATABASE_URL']);
|
|
|
10
10
|
const PORT_FLAG = /^--port=(.*)$/;
|
|
11
11
|
const OWNER_FLAG = /^--owner=(.*)$/;
|
|
12
12
|
const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
13
|
-
const USAGE = 'usage: metro serve [--port <n>] [--
|
|
14
|
-
const TUNNEL_FLAG = /^--tunnel
|
|
13
|
+
const USAGE = 'usage: metro serve [--port <n>] [--owner <address>]';
|
|
14
|
+
const TUNNEL_FLAG = /^--(no-)?tunnel(=.*)?$/;
|
|
15
15
|
const TAILSCALE_APP = '/Applications/Tailscale.app/Contents/MacOS/Tailscale';
|
|
16
16
|
function portOf(raw) {
|
|
17
17
|
const n = Number(raw);
|
|
@@ -19,23 +19,6 @@ function portOf(raw) {
|
|
|
19
19
|
throw new Error(`'${raw ?? ''}' is not a port — ${USAGE}`);
|
|
20
20
|
return n;
|
|
21
21
|
}
|
|
22
|
-
function tunnelOf(raw) {
|
|
23
|
-
if (raw === undefined || raw === '' || raw === 'tailscale')
|
|
24
|
-
return true;
|
|
25
|
-
throw new Error(`'${raw}' is not a tunnel metro knows; the public address is Tailscale Funnel — ${USAGE}`);
|
|
26
|
-
}
|
|
27
|
-
function tunnelFlag(argv, i) {
|
|
28
|
-
const arg = argv[i] ?? '';
|
|
29
|
-
const inline = TUNNEL_FLAG.exec(arg);
|
|
30
|
-
if (inline)
|
|
31
|
-
return { consumed: Number(tunnelOf(inline[1])) - 1 };
|
|
32
|
-
if (arg !== '--tunnel')
|
|
33
|
-
return null;
|
|
34
|
-
const next = argv[i + 1];
|
|
35
|
-
const takesValue = next !== undefined && !next.startsWith('-');
|
|
36
|
-
tunnelOf(takesValue ? next : undefined);
|
|
37
|
-
return { consumed: takesValue ? 1 : 0 };
|
|
38
|
-
}
|
|
39
22
|
function ownerOf(raw) {
|
|
40
23
|
if (raw === undefined || !ADDRESS.test(raw))
|
|
41
24
|
throw new Error(`'${raw ?? ''}' is not an Ethereum address — ${USAGE}`);
|
|
@@ -43,16 +26,11 @@ function ownerOf(raw) {
|
|
|
43
26
|
}
|
|
44
27
|
export function parseServeArgs(argv) {
|
|
45
28
|
let port = localPort();
|
|
46
|
-
let tunnel = false;
|
|
47
29
|
let owner = null;
|
|
48
30
|
for (let i = 0; i < argv.length; i += 1) {
|
|
49
31
|
const arg = argv[i] ?? '';
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
tunnel = true;
|
|
53
|
-
i += tunnelArg.consumed;
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
32
|
+
if (TUNNEL_FLAG.test(arg))
|
|
33
|
+
throw new Error('--tunnel is gone: metro serve always publishes through Tailscale Funnel — ' + USAGE);
|
|
56
34
|
const inlineOwner = OWNER_FLAG.exec(arg);
|
|
57
35
|
if (inlineOwner) {
|
|
58
36
|
owner = ownerOf(inlineOwner[1]);
|
|
@@ -75,7 +53,7 @@ export function parseServeArgs(argv) {
|
|
|
75
53
|
}
|
|
76
54
|
throw new Error(`unknown argument '${arg}' — ${USAGE}`);
|
|
77
55
|
}
|
|
78
|
-
return { port,
|
|
56
|
+
return { port, owner };
|
|
79
57
|
}
|
|
80
58
|
export function requireOwner(owner, dir = agentsDir()) {
|
|
81
59
|
if (owner !== null || existsSync(join(dir, '.owner')))
|
|
@@ -100,7 +78,7 @@ function tailscaleStatus(bin) {
|
|
|
100
78
|
export function findTailscale(candidates = ['tailscale', TAILSCALE_APP]) {
|
|
101
79
|
const bin = candidates.find((c) => spawnSync(c, ['version'], { stdio: 'ignore' }).status === 0);
|
|
102
80
|
if (bin === undefined)
|
|
103
|
-
throw new Error('metro serve
|
|
81
|
+
throw new Error('metro serve needs Tailscale on this machine: the daemon is published through Tailscale Funnel.\n' +
|
|
104
82
|
'macOS: brew install --cask tailscale (or the App Store app)\n' +
|
|
105
83
|
'Linux: curl -fsSL https://tailscale.com/install.sh | sh\n' +
|
|
106
84
|
'Then sign the machine in: tailscale up');
|
|
@@ -128,20 +106,20 @@ export function servePlan(opts) {
|
|
|
128
106
|
? {}
|
|
129
107
|
: { METRO_RUNTIME_STORE: opts.runtime.dir, METRO_RUNTIME_MANIFEST: opts.runtime.manifest }),
|
|
130
108
|
METRO_STATE_DIR: process.env.METRO_STATE_DIR ?? serveStateDir(),
|
|
131
|
-
|
|
132
|
-
|
|
109
|
+
METRO_TUNNEL: 'tailscale',
|
|
110
|
+
METRO_TAILSCALE_BIN: opts.tailscaleBin,
|
|
133
111
|
...(opts.owner === null ? {} : { METRO_OWNER: opts.owner }),
|
|
134
112
|
},
|
|
135
113
|
};
|
|
136
114
|
}
|
|
137
115
|
export function serve(argv) {
|
|
138
|
-
const { port,
|
|
116
|
+
const { port, owner } = parseServeArgs(argv);
|
|
139
117
|
const running = serveLockedBy();
|
|
140
118
|
if (running !== null)
|
|
141
119
|
throw new Error(`a metro serve daemon is already running on this machine (pid ${String(running)}). ` +
|
|
142
120
|
'Stop it first: metro stop');
|
|
143
121
|
requireOwner(owner);
|
|
144
|
-
const tailscaleBin =
|
|
122
|
+
const tailscaleBin = findTailscale();
|
|
145
123
|
process.stderr.write(`Starting a metro daemon of your own on http://127.0.0.1:${String(port)}\n`);
|
|
146
|
-
return spawnPlan(() => servePlan({ runtime: prepareRuntime(), port,
|
|
124
|
+
return spawnPlan(() => servePlan({ runtime: prepareRuntime(), port, owner, tailscaleBin }));
|
|
147
125
|
}
|
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.61",
|
|
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": {
|
package/runtime/runtime.json
CHANGED