@stage-labs/metro 0.1.0-beta.201 → 0.1.0-beta.203

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 (171) hide show
  1. package/dist/claude.js +4 -15
  2. package/dist/cli.js +5 -3
  3. package/dist/hold.js +1 -2
  4. package/dist/runtime-install.js +4 -5
  5. package/dist/runtime.js +1 -4
  6. package/dist/serve.js +4 -5
  7. package/dist/service.js +82 -21
  8. package/dist/update.js +1 -1
  9. package/package.json +2 -2
  10. package/runtime/marketplace/plugin/.claude-plugin/plugin.json +1 -1
  11. package/runtime/marketplace/plugin/orchestrator.md +1 -1
  12. package/runtime/node_modules/@metro-labs/core/src/events.ts +4 -2
  13. package/runtime/node_modules/@metro-labs/core/src/lines.ts +12 -25
  14. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +45 -22
  15. package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +0 -1
  16. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +8 -80
  17. package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +3 -5
  18. package/runtime/node_modules/@metro-labs/core/src/stations/train-events.ts +4 -9
  19. package/runtime/node_modules/@metro-labs/core/src/stations/types.ts +3 -1
  20. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +9 -39
  21. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/job-env.ts +1 -1
  22. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +9 -28
  23. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +0 -1
  24. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules-api.ts +3 -3
  25. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +8 -116
  26. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +11 -35
  27. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +7 -9
  28. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +1 -1
  29. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-lookups.ts +1 -1
  30. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +1 -1
  31. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +1 -1
  32. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +3 -3
  33. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +2 -2
  34. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +3 -29
  35. package/runtime/node_modules/@metro-labs/daemon/src/approvals/pending.ts +5 -8
  36. package/runtime/node_modules/@metro-labs/daemon/src/approvals/preview.ts +1 -1
  37. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +5 -8
  38. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +3 -6
  39. package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +25 -3
  40. package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +2 -3
  41. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +2 -2
  42. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +1 -1
  43. package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +6 -56
  44. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +3 -3
  45. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +1 -1
  46. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +7 -6
  47. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +1 -1
  48. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +8 -7
  49. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +4 -4
  50. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +1 -1
  51. package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +2 -4
  52. package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +19 -22
  53. package/runtime/node_modules/@metro-labs/daemon/src/connectors/gates.ts +1 -2
  54. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +6 -8
  55. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +3 -6
  56. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +1 -1
  57. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +2 -3
  58. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +28 -1
  59. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +1 -1
  60. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +4 -4
  61. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +1 -1
  62. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +1 -1
  63. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +1 -1
  64. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +3 -2
  65. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +1 -1
  66. package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +2 -2
  67. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +1 -1
  68. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +3 -3
  69. package/runtime/node_modules/@metro-labs/daemon/src/gateway/frames.ts +1 -1
  70. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +3 -3
  71. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +6 -6
  72. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +7 -7
  73. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +1 -1
  74. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +3 -2
  75. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +1 -1
  76. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +10 -31
  77. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +1 -1
  78. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +1 -1
  79. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +2 -2
  80. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +3 -3
  81. package/runtime/node_modules/@metro-labs/daemon/src/gateway/text.ts +1 -2
  82. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +2 -2
  83. package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +19 -1
  84. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +5 -6
  85. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-snapshot.ts +3 -3
  86. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +2 -2
  87. package/runtime/node_modules/@metro-labs/daemon/src/mcp/read-tool.ts +2 -2
  88. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +14 -0
  89. package/runtime/node_modules/@metro-labs/daemon/src/mcp/typing.ts +46 -0
  90. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +2 -2
  91. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/install.ts +37 -0
  92. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +2 -2
  93. package/runtime/node_modules/@metro-labs/daemon/src/policy/policy.ts +1 -1
  94. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +1 -4
  95. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +3 -8
  96. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +2 -2
  97. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +2 -2
  98. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +2 -2
  99. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +6 -7
  100. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +1 -1
  101. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +2 -2
  102. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +2 -2
  103. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +1 -1
  104. package/runtime/node_modules/@metro-labs/daemon/src/vault/firewall.ts +5 -44
  105. package/runtime/node_modules/@metro-labs/daemon/src/vault/index.ts +4 -4
  106. package/runtime/node_modules/@metro-labs/daemon/src/vault/proxy.ts +6 -7
  107. package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +12 -13
  108. package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +5 -22
  109. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +1 -19
  110. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +7 -35
  111. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +5 -10
  112. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +1 -1
  113. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +1 -2
  114. package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +1 -1
  115. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +11 -12
  116. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +2 -5
  117. package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +0 -1
  118. package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +1 -15
  119. package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +0 -6
  120. package/runtime/node_modules/@metro-labs/outlook/src/format.ts +0 -1
  121. package/runtime/node_modules/@metro-labs/outlook/src/state.ts +1 -1
  122. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +4 -22
  123. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +2 -2
  124. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +1 -3
  125. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +1 -1
  126. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +1 -2
  127. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +1 -1
  128. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +4 -37
  129. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +19 -23
  130. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +1 -4
  131. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +4 -9
  132. package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +3 -3
  133. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +2 -0
  134. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +0 -11
  135. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +2 -12
  136. package/runtime/node_modules/@metro-labs/threema/src/files.ts +4 -15
  137. package/runtime/node_modules/@metro-labs/threema/src/format.ts +2 -7
  138. package/runtime/node_modules/@metro-labs/whatsapp/package.json +1 -2
  139. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +5 -26
  140. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +14 -6
  141. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +5 -0
  142. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +1 -3
  143. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +1 -1
  144. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +2 -0
  145. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +1 -12
  146. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +1 -1
  147. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +14 -5
  148. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +1 -1
  149. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +31 -78
  150. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +3 -4
  151. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +1 -6
  152. package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +1 -1
  153. package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +1 -1
  154. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +1 -1
  155. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +2 -2
  156. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +0 -2
  157. package/runtime/runtime.json +1 -1
  158. package/runtime/node_modules/@metro-labs/core/src/identity.ts +0 -7
  159. package/runtime/node_modules/@metro-labs/daemon/src/approvals/needs.ts +0 -2
  160. package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +0 -12
  161. package/runtime/node_modules/@metro-labs/daemon/src/channels/react-note.ts +0 -13
  162. package/runtime/node_modules/@metro-labs/daemon/src/channels/reply-id.ts +0 -1
  163. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +0 -29
  164. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/api.ts +0 -43
  165. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/move.ts +0 -165
  166. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +0 -1
  167. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +0 -22
  168. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +0 -1
  169. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +0 -1
  170. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +0 -22
  171. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +0 -1
@@ -1,43 +0,0 @@
1
- import type { IncomingMessage, ServerResponse } from 'node:http';
2
- import { readFileSync } from 'node:fs';
3
- import { sessionRoute } from '@metro-labs/http/api-http';
4
- import { MOVE_LOG, moveState, startMove } from './move.js';
5
- import { rootHelper, runningAsMetro, runningAsRoot } from './privilege.js';
6
- import { HELPER_VERSION } from './helper-script.js';
7
-
8
- const PATH = '/api/server/metro-user';
9
- const LOG_TAIL = 40;
10
-
11
- function logTail(): string {
12
- try {
13
- return readFileSync(MOVE_LOG, 'utf8').split('\n').slice(-LOG_TAIL).join('\n');
14
- } catch {
15
- return '';
16
- }
17
- }
18
-
19
- function helperVersion(): number | null {
20
- if (!runningAsMetro()) return null;
21
- const v = Number(rootHelper(['version']).stdout.trim());
22
- return Number.isInteger(v) ? v : null;
23
- }
24
-
25
- function view(): unknown {
26
- const runningAs = runningAsRoot() ? 'root' : runningAsMetro() ? 'metro' : 'other';
27
- const helper = helperVersion();
28
- return {
29
- runningAs,
30
- canMove: runningAs === 'root',
31
- move: moveState(),
32
- log: logTail(),
33
- helper,
34
- helperCurrent: runningAs !== 'metro' || helper === HELPER_VERSION,
35
- };
36
- }
37
-
38
- export function handleMetroUserRequest(req: IncomingMessage, res: ServerResponse): boolean {
39
- return sessionRoute(req, res, { methods: { [PATH]: ['GET', 'POST'] }, admin: true, label: 'metro-user-api' }, () => {
40
- if (req.method === 'POST') startMove();
41
- return Promise.resolve(view());
42
- });
43
- }
@@ -1,165 +0,0 @@
1
- import { spawnSync } from 'node:child_process';
2
- import { chmodSync, existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
3
- import { homedir } from 'node:os';
4
- import { join } from 'node:path';
5
- import { ApiError } from '@metro-labs/http/api-error';
6
- import { METRO_VERSION } from '@metro-labs/core/version';
7
- import { AGENT_NAME } from '../agent-user/user.js';
8
- import { HELPER_PATH, helperScript, METRO_HOME, METRO_USER, SUDOERS_PATH, sudoersText } from './helper-script.js';
9
- import { runningAsRoot } from './privilege.js';
10
-
11
- export const UNIT_FILE = '/etc/systemd/system/metro.service';
12
- export const MOVE_DIR = '/var/lib/metro-move';
13
- export const MOVE_LOG = '/var/log/metro-move.log';
14
- const PREFIX = `${METRO_HOME}/.npm-global`;
15
- const CLI = `${PREFIX}/lib/node_modules/@stage-labs/metro/dist/cli.js`;
16
-
17
- const which = (bin: string): string | null => {
18
- const run = spawnSync('sh', ['-c', `command -v ${bin}`], { encoding: 'utf8' });
19
- const path = run.status === 0 ? run.stdout.trim() : '';
20
- if (path === '') return null;
21
- try {
22
- return realpathSync(path);
23
- } catch {
24
- return null;
25
- }
26
- };
27
-
28
- export interface UnitFacts {
29
- serveArgs: string[];
30
- env: string[];
31
- port: string;
32
- }
33
-
34
- export function unitFacts(unit: string): UnitFacts {
35
- const exec = /^ExecStart=(.*)$/m.exec(unit)?.[1] ?? '';
36
- const words = [...exec.matchAll(/"((?:[^"\\]|\\.)*)"|(\S+)/g)].map((m) => (m[1] ?? m[2] ?? '').replace(/\\(.)/g, '$1'));
37
- const at = words.indexOf('serve');
38
- if (at < 0) throw new ApiError('the metro service does not run `metro serve`; move it by hand', 409);
39
- const env = [...unit.matchAll(/^Environment=(.*)$/gm)].map((m) => m[1] ?? '').filter((e) => /^"?METRO_/.test(e) && !/^"?METRO_(AGENTS_DIR|RUNTIME_STORE)=/.test(e));
40
- const portArg = words[words.indexOf('--port') + 1];
41
- const envPort = /METRO_WEBHOOK_PORT=(\d+)/.exec(env.join(' '))?.[1];
42
- return { serveArgs: words.slice(at + 1), env, port: (words.includes('--port') ? portArg : envPort) ?? '8420' };
43
- }
44
-
45
- const ESCAPED_QUOTE = '\'\\\'\'';
46
- const shellWord = (w: string): string => `'${w.replace(/'/g, ESCAPED_QUOTE)}'`;
47
- const unitWord = (w: string): string => (/^[A-Za-z0-9_/.:=+@,-]+$/.test(w) ? w : `"${w.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`);
48
-
49
- export function metroUnit(node: string, facts: UnitFacts): string {
50
- return [
51
- '[Unit]',
52
- 'Description=Metro daemon (metro serve)',
53
- 'After=network-online.target tailscaled.service',
54
- 'Wants=network-online.target',
55
- '',
56
- '[Service]',
57
- `User=${METRO_USER}`,
58
- `Group=${METRO_USER}`,
59
- `ExecStart=${[node, CLI, 'serve', ...facts.serveArgs].map(unitWord).join(' ')}`,
60
- 'Restart=always',
61
- 'RestartSec=2',
62
- 'OOMPolicy=continue',
63
- 'WorkingDirectory=/',
64
- `Environment=HOME=${METRO_HOME}`,
65
- `Environment=PATH=${PREFIX}/bin:/usr/local/bin:/usr/bin:/bin`,
66
- `Environment=NPM_CONFIG_PREFIX=${PREFIX}`,
67
- ...facts.env.map((e) => `Environment=${e}`),
68
- '',
69
- '[Install]',
70
- 'WantedBy=multi-user.target',
71
- '',
72
- ].join('\n');
73
- }
74
-
75
- export function moveScript(port: string, spec: string, bun: string): string {
76
- return [
77
- '#!/bin/sh',
78
- 'set -u',
79
- `exec >> ${MOVE_LOG} 2>&1`,
80
- 'echo "--- metro move $(date -Is)"',
81
- `OLD=/root/.metro; NEW=${METRO_HOME}/.metro; DIR=${MOVE_DIR}`,
82
- 'fail() { echo "FAILED: $*"; echo failed > "$DIR/state"; exit 1; }',
83
- 'healthy() {',
84
- ' i=0; while [ $i -lt 60 ]; do',
85
- ` if curl -fsS -m 3 http://127.0.0.1:${port}/health >/dev/null 2>&1 && [ "$(ps -o user= -p "$(systemctl show metro -p MainPID --value)")" = "$1" ]; then return 0; fi`,
86
- ' i=$((i+1)); sleep 2; done; return 1; }',
87
- 'echo moving > "$DIR/state"',
88
- `id ${METRO_USER} >/dev/null 2>&1 || useradd --system --create-home --home-dir ${METRO_HOME} --shell /usr/sbin/nologin ${METRO_USER} || fail "useradd"`,
89
- `usermod -aG systemd-journal ${METRO_USER} || true`,
90
- `mkdir -p ${METRO_HOME} && chown ${METRO_USER}:${METRO_USER} ${METRO_HOME} && chmod 711 ${METRO_HOME}`,
91
- `B=${shellWord(bun)}; case "$B" in /root/*) install -m 755 "$B" /usr/local/bin/bun || fail "bun";; esac`,
92
- `su -s /bin/sh ${METRO_USER} -c ${shellWord(`cd / && npm install --global --prefix ${PREFIX} ${spec}`)} || fail "npm install for ${METRO_USER}"`,
93
- `for bin in node bun tailscale npm; do su -s /bin/sh ${METRO_USER} -c ${shellWord(`PATH=${PREFIX}/bin:/usr/local/bin:/usr/bin:/bin command -v "$1" >/dev/null`)} sh "$bin" || fail "${METRO_USER} cannot find $bin"; done`,
94
- `tailscale set --operator=${METRO_USER} || fail "tailscale operator"`,
95
- `cp ${UNIT_FILE} "$DIR/metro.service.root" || fail "unit backup"`,
96
- 'systemctl stop metro',
97
- 'if [ -e "$NEW" ]; then mv "$NEW" "$NEW.old-$(date +%s)"; fi',
98
- 'mv "$OLD" "$NEW" || { systemctl start metro; fail "move"; }',
99
- 'rm -f "$NEW/runtime/runtime.json"',
100
- `chown -R ${METRO_USER}:${METRO_USER} ${METRO_HOME} && chmod 711 ${METRO_HOME} && chmod 700 "$NEW"`,
101
- `install -m 644 "$DIR/metro.service.new" ${UNIT_FILE}`,
102
- 'systemctl daemon-reload && systemctl start metro',
103
- `if healthy ${METRO_USER}; then echo done > "$DIR/state"; echo "metro runs as ${METRO_USER}"; exit 0; fi`,
104
- 'echo "not healthy as metro, putting everything back"',
105
- 'systemctl stop metro',
106
- 'mv "$NEW" "$OLD" && chown -R root:root "$OLD"',
107
- `install -m 644 "$DIR/metro.service.root" ${UNIT_FILE}`,
108
- 'systemctl daemon-reload && systemctl start metro',
109
- 'healthy root && fail "Metro did not start as metro; it runs as root again" || fail "Metro did not start as metro, and the way back did not answer either"',
110
- '',
111
- ].join('\n');
112
- }
113
-
114
- function whichOrFail(bin: string): string {
115
- const found = which(bin);
116
- if (found === null) throw new ApiError(`${bin} is missing on this box`, 409);
117
- return found;
118
- }
119
-
120
- const requireTools = (): void => {
121
- for (const bin of ['sudo', 'visudo', 'tailscale', 'curl', 'npm']) whichOrFail(bin);
122
- };
123
-
124
- function preflight(): { node: string; bun: string; unit: string } {
125
- if (!runningAsRoot()) throw new ApiError('only a Metro running as root can move itself', 409);
126
- if (!existsSync(UNIT_FILE)) throw new ApiError('this box does not run Metro as the systemd service `metro`', 409);
127
- if (process.env.METRO_AGENTS_DIR !== undefined && process.env.METRO_AGENTS_DIR !== '') throw new ApiError('METRO_AGENTS_DIR is set; move this box by hand', 409);
128
- if (homedir() !== '/root') throw new ApiError(`Metro keeps its files in ${homedir()}, not /root; move this box by hand`, 409);
129
- const node = which('node');
130
- if (node === null || node.startsWith('/root/')) throw new ApiError('Node must be installed for every user (not under /root) before the move', 409);
131
- requireTools();
132
- return { node, bun: whichOrFail('bun'), unit: readFileSync(UNIT_FILE, 'utf8') };
133
- }
134
-
135
- function installHelper(): void {
136
- mkdirSync('/usr/local/lib/metro', { recursive: true, mode: 0o755 });
137
- writeFileSync(HELPER_PATH, helperScript(), { mode: 0o755 });
138
- chmodSync(HELPER_PATH, 0o755);
139
- const draft = join(MOVE_DIR, 'sudoers');
140
- writeFileSync(draft, sudoersText(AGENT_NAME), { mode: 0o440 });
141
- const check = spawnSync('visudo', ['-cf', draft], { encoding: 'utf8' });
142
- if (check.status !== 0) throw new ApiError(`the sudo rules did not validate: ${check.stdout}${check.stderr}`, 500);
143
- spawnSync('install', ['-m', '440', draft, SUDOERS_PATH]);
144
- }
145
-
146
- export function startMove(spec = `@stage-labs/metro@${METRO_VERSION}`): { log: string } {
147
- const { node, bun, unit } = preflight();
148
- const facts = unitFacts(unit);
149
- mkdirSync(MOVE_DIR, { recursive: true, mode: 0o700 });
150
- installHelper();
151
- writeFileSync(join(MOVE_DIR, 'metro.service.new'), metroUnit(node, facts), { mode: 0o600 });
152
- writeFileSync(join(MOVE_DIR, 'move.sh'), moveScript(facts.port, spec, bun), { mode: 0o700 });
153
- writeFileSync(join(MOVE_DIR, 'state'), 'starting\n');
154
- const run = spawnSync('systemd-run', ['--unit=metro-move', '--collect', '--quiet', 'sh', join(MOVE_DIR, 'move.sh')], { encoding: 'utf8' });
155
- if (run.status !== 0) throw new ApiError(`could not start the move: ${run.stderr.trim()}`, 500);
156
- return { log: MOVE_LOG };
157
- }
158
-
159
- export function moveState(): string | null {
160
- try {
161
- return readFileSync(join(MOVE_DIR, 'state'), 'utf8').trim();
162
- } catch {
163
- return null;
164
- }
165
- }
@@ -1 +0,0 @@
1
- export { emit, respond, mintId } from '@metro-labs/core/stations/station-runtime';
@@ -1,22 +0,0 @@
1
- import type { Normalized } from '@metro-labs/core/stations/messaging-normalize';
2
-
3
- type Args = Record<string, unknown>;
4
-
5
- export function normalizeTelegramUser(action: string, env: Args): Normalized {
6
- if (action === 'reply') {
7
- return {
8
- action: 'send',
9
- args: {
10
- line: env.line,
11
- text: env.text,
12
- replyTo: env.replyTo,
13
- account: env.account,
14
- attachments: env.attachments,
15
- },
16
- };
17
- }
18
- if (action === 'unreact') {
19
- return { action: 'react', args: { ...env, emoji: '' } };
20
- }
21
- return { action, args: env };
22
- }
@@ -1 +0,0 @@
1
- export { emit, mintId } from '@metro-labs/core/stations/station-runtime';
@@ -1 +0,0 @@
1
- export { emit, respond, mintId } from '@metro-labs/core/stations/station-runtime';
@@ -1,22 +0,0 @@
1
- import type { Normalized } from '@metro-labs/core/stations/messaging-normalize';
2
-
3
- type Args = Record<string, unknown>;
4
-
5
- export function normalizeWhatsApp(action: string, env: Args): Normalized {
6
- if (action === 'reply') {
7
- return {
8
- action: 'send',
9
- args: {
10
- line: env.line,
11
- text: env.text,
12
- replyTo: env.replyTo ?? env.messageId,
13
- account: env.account,
14
- attachments: env.attachments,
15
- },
16
- };
17
- }
18
- if (action === 'unreact') {
19
- return { action: 'react', args: { ...env, emoji: '' } };
20
- }
21
- return { action, args: env };
22
- }
@@ -1 +0,0 @@
1
- export { emit, mintId } from '@metro-labs/core/stations/station-runtime';