@mh-gg/cli 0.1.1-alpha.20260613T085325975Z

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 (71) hide show
  1. package/README.md +5 -0
  2. package/bin/matterhorn.cjs +57 -0
  3. package/package.json +49 -0
  4. package/runtime/bin/appFrontend/artifacts.cjs +25 -0
  5. package/runtime/bin/appFrontend/buildServers.cjs +176 -0
  6. package/runtime/bin/appFrontend/commandEnv.cjs +74 -0
  7. package/runtime/bin/appFrontend/commandPolicy.cjs +23 -0
  8. package/runtime/bin/appFrontend/devServers.cjs +150 -0
  9. package/runtime/bin/appFrontend/httpServers.cjs +221 -0
  10. package/runtime/bin/appFrontend/paths.cjs +103 -0
  11. package/runtime/bin/appFrontend/ports.cjs +36 -0
  12. package/runtime/bin/appFrontend/processes.cjs +127 -0
  13. package/runtime/bin/appFrontend.cjs +45 -0
  14. package/runtime/bin/appHostCommand.cjs +381 -0
  15. package/runtime/bin/matterhorn.cjs +501 -0
  16. package/runtime/bin/matterhornAppLoader.cjs +588 -0
  17. package/runtime/bin/matterhornApps.cjs +223 -0
  18. package/runtime/bin/matterhornDeploy.cjs +108 -0
  19. package/runtime/bin/matterhornEmitAppBundle.cjs +20 -0
  20. package/runtime/bin/matterhornInstall.cjs +609 -0
  21. package/runtime/host/callAuth.cjs +76 -0
  22. package/runtime/host/host.cjs +103 -0
  23. package/runtime/host/hostAnnouncement.cjs +70 -0
  24. package/runtime/host/hostClients/constants.cjs +7 -0
  25. package/runtime/host/hostClients/frontendBundleRefresh.cjs +158 -0
  26. package/runtime/host/hostClients/frontendRequests.cjs +166 -0
  27. package/runtime/host/hostClients/index.cjs +68 -0
  28. package/runtime/host/hostClients/rejections.cjs +37 -0
  29. package/runtime/host/hostSession.cjs +160 -0
  30. package/runtime/host/inlineProgressBar.cjs +128 -0
  31. package/runtime/host/localPeerServer.cjs +114 -0
  32. package/runtime/host/localRelayClient.cjs +151 -0
  33. package/runtime/host/matterhornrc.cjs +75 -0
  34. package/runtime/host/memberRootRegistry.cjs +132 -0
  35. package/runtime/host/nodePeer.cjs +127 -0
  36. package/runtime/host/nodePeerRacePatch.cjs +106 -0
  37. package/runtime/host/peerJsConfig.cjs +26 -0
  38. package/runtime/host/pushEgress.cjs +48 -0
  39. package/runtime/host/pushStorage.cjs +233 -0
  40. package/runtime/host/relay/config.cjs +179 -0
  41. package/runtime/host/relay/connectionCleanup.cjs +34 -0
  42. package/runtime/host/relay/connectionDispatcher.cjs +140 -0
  43. package/runtime/host/relay/matterhornOperationEvents.cjs +100 -0
  44. package/runtime/host/relay/matterhornRuntimeEventBridge.cjs +182 -0
  45. package/runtime/host/relay/nostrRelay.cjs +30 -0
  46. package/runtime/host/relay/peerStartup.cjs +81 -0
  47. package/runtime/host/relay.cjs +653 -0
  48. package/runtime/host/relayClientRouting.cjs +1054 -0
  49. package/runtime/host/relayConfig.cjs +156 -0
  50. package/runtime/host/relayHostAuth.cjs +39 -0
  51. package/runtime/host/relayHostMessages.cjs +367 -0
  52. package/runtime/host/relayHttp.cjs +48 -0
  53. package/runtime/host/relayIdentity.cjs +496 -0
  54. package/runtime/host/relayIncomingGate.cjs +153 -0
  55. package/runtime/host/relayMeshEnvelopes.cjs +522 -0
  56. package/runtime/host/relayPeerLifecycle.cjs +96 -0
  57. package/runtime/host/relayPeerSignals.cjs +175 -0
  58. package/runtime/host/relayRoomRuntimePersistence.cjs +129 -0
  59. package/runtime/host/relayStatus.cjs +160 -0
  60. package/runtime/host/sfuRelay.cjs +553 -0
  61. package/runtime/host/sqliteRelayStorage.cjs +352 -0
  62. package/runtime/host/wireValidation/client.cjs +213 -0
  63. package/runtime/host/wireValidation/host.cjs +33 -0
  64. package/runtime/host/wireValidation/index.cjs +13 -0
  65. package/runtime/host/wireValidation/peerSignal.cjs +35 -0
  66. package/runtime/host/wireValidation/presenceEvent.cjs +49 -0
  67. package/runtime/host/wireValidation/push.cjs +49 -0
  68. package/runtime/host/wireValidation/relay.cjs +131 -0
  69. package/runtime/host/wireValidation/shared.cjs +49 -0
  70. package/runtime/scripts/ensureWorkspaceSdkBuild.cjs +148 -0
  71. package/runtime/scripts/killChildTree.cjs +18 -0
@@ -0,0 +1,501 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require("node:child_process");
4
+ const fs = require("node:fs");
5
+ const os = require("node:os");
6
+ const path = require("node:path");
7
+
8
+ let appModules;
9
+ let appHostCommand;
10
+ let deployCommand;
11
+ let installCommand;
12
+ let matterhornrc;
13
+ let relayConfig;
14
+ let relayIdentityModules;
15
+
16
+ function matterhornApps() {
17
+ if (!appModules) appModules = require("./matterhornApps.cjs");
18
+ return appModules;
19
+ }
20
+
21
+ function appHost() {
22
+ if (!appHostCommand) appHostCommand = require("./appHostCommand.cjs");
23
+ return appHostCommand;
24
+ }
25
+
26
+ function deploy() {
27
+ if (!deployCommand) deployCommand = require("./matterhornDeploy.cjs");
28
+ return deployCommand;
29
+ }
30
+
31
+ function installs() {
32
+ if (!installCommand) installCommand = require("./matterhornInstall.cjs");
33
+ return installCommand;
34
+ }
35
+
36
+ function rc() {
37
+ if (!matterhornrc) matterhornrc = require("../host/matterhornrc.cjs");
38
+ return matterhornrc;
39
+ }
40
+
41
+ function relayDefaults() {
42
+ if (!relayConfig) relayConfig = require("../host/relayConfig.cjs");
43
+ return relayConfig;
44
+ }
45
+
46
+ function relayIdentity() {
47
+ if (!relayIdentityModules) relayIdentityModules = require("../host/relayIdentity.cjs");
48
+ return relayIdentityModules;
49
+ }
50
+
51
+ function dataDir() {
52
+ return process.env.MATTERHORN_DATA_DIR || rc().userDataDir();
53
+ }
54
+
55
+ function configFileForDisplay() {
56
+ try { return rc().usermatterhornrcFile(); } catch { return "~/.matterhornrc"; }
57
+ }
58
+
59
+ function defaultAppUrl() {
60
+ return process.env.APP_URL || process.env.HOST_URL || "https://launch.matterhorn.gg/";
61
+ }
62
+
63
+ function usage() {
64
+ console.log("Usage: npx --package matterhorn-sdk matterhorn <app-ref> <room-slug> [--serve [dev|built] [--frontend-port PORT]] [--local-peerjs] -- [host options]");
65
+ console.log(" npx --package matterhorn-sdk matterhorn launch <app-ref> [--room ROOM] [--relay RELAY] [--user USER]");
66
+ console.log(" npx --package matterhorn-sdk matterhorn run <install-id> [--room ROOM] [--relay RELAY] [--user USER]");
67
+ console.log(" npx --package matterhorn-sdk matterhorn deploy <app-ref> <room-slug> [--serve [dev|built] [--frontend-port PORT]] [--relay-peer ADDRESS]");
68
+ console.log(" npx --package matterhorn-sdk matterhorn host SESSION-ID --pack <app-pack-ref> [--player <player-pack-ref>]");
69
+ console.log(" npx --package matterhorn-sdk matterhorn launcher add-player --url <player-pack-ref>");
70
+ console.log(" npx --package matterhorn-sdk matterhorn relay [start] [--name NAME] [--addRelay matterhorn-relay:TOKEN]");
71
+ console.log(" npx --package matterhorn-sdk matterhorn relay invite [--name NAME]");
72
+ console.log(" npx --package matterhorn-sdk matterhorn relay add <matterhorn-relay:TOKEN|peerjs:ADDRESS|PINNED_ALIAS#0000> [--ipc-port 42777]");
73
+ console.log(" npx --package matterhorn-sdk matterhorn relay trust remove NAME");
74
+ console.log(" npx --package matterhorn-sdk matterhorn configure set host https://example.com");
75
+ console.log("Example: npx --package matterhorn-sdk matterhorn @app.matterhorn.events rooftop-disco --serve built");
76
+ console.log("Example: npx --package matterhorn-sdk matterhorn launch @app.matterhorn.events --room rooftop-disco --relay peerjs:other-relay --user alice");
77
+ console.log("Example: npx --package matterhorn-sdk matterhorn apps");
78
+ console.log("Example: npx --package matterhorn-sdk matterhorn doctor");
79
+ console.log("Example: npx --package matterhorn-sdk matterhorn deploy @app.matterhorn.events rooftop-disco --serve built --relay-peer peerjs:other-relay");
80
+ console.log("Example: npx --package matterhorn-sdk matterhorn ./examples/events rooftop-disco --serve built");
81
+ console.log("Example: npx --package matterhorn-sdk matterhorn ./examples/events test-slug --local-peerjs -- --app-url http://localhost:42729/");
82
+ console.log("Example: npx --package matterhorn-sdk matterhorn host rooftop-disco --pack workspace:@mh-gg/example-events#eventAppPack");
83
+ console.log("Example: npx --package matterhorn-sdk matterhorn relay start --name relay-a");
84
+ console.log("Example: npx --package matterhorn-sdk matterhorn relay start --name relay-b --addRelay matterhorn-relay:TOKEN_FROM_RELAY_A");
85
+ console.log("Example: npx --package matterhorn-sdk matterhorn relay add relay-a#0000");
86
+ console.log(`Config: ${configFileForDisplay()}`);
87
+ console.log(`Data: ${dataDir()}\\<room-slug>.json`);
88
+ }
89
+
90
+ function parseRelayAddArgs(args) {
91
+ const { DEFAULT_RELAY_IPC_HOST, DEFAULT_RELAY_IPC_PORT } = relayDefaults();
92
+ const parsed = {
93
+ ipcHost: process.env.MATTERHORN_RELAY_IPC_HOST || DEFAULT_RELAY_IPC_HOST,
94
+ ipcPort: Number(process.env.MATTERHORN_RELAY_IPC_PORT || DEFAULT_RELAY_IPC_PORT),
95
+ relayRef: ""
96
+ };
97
+
98
+ for (let index = 0; index < args.length; index += 1) {
99
+ const token = args[index];
100
+ if (token === "--host" || token === "--ipc-host") {
101
+ parsed.ipcHost = args[index + 1];
102
+ index += 1;
103
+ continue;
104
+ }
105
+ if (token === "--port" || token === "--ipc-port") {
106
+ parsed.ipcPort = Number(args[index + 1]);
107
+ index += 1;
108
+ continue;
109
+ }
110
+ if (!token.startsWith("--") && !parsed.relayRef) parsed.relayRef = token;
111
+ }
112
+
113
+ return parsed;
114
+ }
115
+
116
+ function resolveRelayReferenceForCli(relayRef) {
117
+ const store = relayIdentity().createRelayTrustStore({ dataDir: dataDir() });
118
+ const resolved = store.resolveReference(relayRef);
119
+ if (!resolved.ok) throw new Error(resolved.message);
120
+ return resolved;
121
+ }
122
+
123
+ function relayIpcAuthForCli(parsed) {
124
+ const config = relayConfig().ensureRelayConfig({
125
+ dataDir: dataDir(),
126
+ ipcHost: parsed.ipcHost,
127
+ ipcPort: parsed.ipcPort
128
+ });
129
+ return { type: "relay-ipc", secret: config.ipcSecret };
130
+ }
131
+
132
+ function addRelay(args) {
133
+ const WebSocket = require("ws");
134
+ const parsed = parseRelayAddArgs(args);
135
+ if (!parsed.relayRef) {
136
+ usage();
137
+ process.exit(1);
138
+ }
139
+ let resolved;
140
+ try {
141
+ resolved = resolveRelayReferenceForCli(parsed.relayRef);
142
+ } catch (error) {
143
+ console.error(error.message || error);
144
+ process.exit(1);
145
+ }
146
+
147
+ const ipcUrl = `ws://${parsed.ipcHost}:${parsed.ipcPort}/room`;
148
+ console.log(`Connecting to relay IPC ${ipcUrl}`);
149
+ const socket = new WebSocket(ipcUrl);
150
+ const requestId = `relay_add_${Date.now()}`;
151
+ const timer = setTimeout(() => {
152
+ console.error(`Timed out adding relay ${parsed.relayRef}`);
153
+ socket.close();
154
+ process.exit(1);
155
+ }, 10000);
156
+ socket.on("open", () => {
157
+ console.log(`Adding relay ${parsed.relayRef}`);
158
+ socket.send(JSON.stringify({
159
+ type: "relay.add",
160
+ requestId,
161
+ auth: relayIpcAuthForCli(parsed),
162
+ relayRef: parsed.relayRef,
163
+ relayAddress: resolved.relayAddress,
164
+ relayClaim: resolved.relayClaim
165
+ }));
166
+ });
167
+ socket.on("message", (data) => {
168
+ const message = JSON.parse(data.toString("utf8"));
169
+ if (message.requestId !== requestId) return;
170
+ if (message.type === "relay.add.ok") {
171
+ clearTimeout(timer);
172
+ console.log(`Added relay ${message.relayAddress}`);
173
+ socket.close();
174
+ return;
175
+ }
176
+ clearTimeout(timer);
177
+ console.error(message.message || "Failed to add relay");
178
+ socket.close();
179
+ process.exitCode = 1;
180
+ });
181
+ socket.on("error", (error) => {
182
+ clearTimeout(timer);
183
+ console.error(`Failed to connect to relay IPC ${ipcUrl}: ${error.message}`);
184
+ process.exit(1);
185
+ });
186
+ }
187
+
188
+ function parseRelayIdentityArgs(args) {
189
+ const parsed = {
190
+ relayName: undefined,
191
+ relayPeerId: undefined
192
+ };
193
+ for (let index = 0; index < args.length; index += 1) {
194
+ const token = args[index];
195
+ if (token === "--name" || token === "--relay-name") {
196
+ parsed.relayName = args[index + 1];
197
+ index += 1;
198
+ continue;
199
+ }
200
+ if (token === "--relay-peer-id") {
201
+ parsed.relayPeerId = args[index + 1];
202
+ index += 1;
203
+ }
204
+ }
205
+ return parsed;
206
+ }
207
+
208
+ function printRelayInvite(args, io = console) {
209
+ const parsed = parseRelayIdentityArgs(args);
210
+ const config = relayDefaults().ensureRelayConfig({
211
+ dataDir: dataDir(),
212
+ relayName: parsed.relayName,
213
+ relayPeerId: parsed.relayPeerId
214
+ });
215
+ const claim = relayIdentity().signRelayClaim(config.relayIdentity, {
216
+ relayAddress: config.relayAddress,
217
+ roomPeerId: config.relayPeerId,
218
+ relayMeshPeerId: config.relayMeshPeerId
219
+ });
220
+ const invite = relayIdentity().encodeRelayInvite(claim);
221
+ io.log(invite);
222
+ return invite;
223
+ }
224
+
225
+ function relayTrust(args, io = console) {
226
+ const [command, name] = args;
227
+ if (command !== "remove" || !name) {
228
+ usage();
229
+ process.exit(1);
230
+ }
231
+ const removed = relayIdentity().createRelayTrustStore({ dataDir: dataDir() }).remove(name);
232
+ io.log(removed ? `Removed relay trust pin for ${name}` : `No relay trust pin for ${name}`);
233
+ return removed;
234
+ }
235
+
236
+ function relayStartArgs(args) {
237
+ return args[0] === "start" ? args.slice(1) : args;
238
+ }
239
+
240
+ function listApps(io = console) {
241
+ const fallback = {
242
+ "@app.matterhorn.budget": "Budget",
243
+ "@app.matterhorn.crm": "CRM",
244
+ "@app.matterhorn.events": "Events",
245
+ "@app.matterhorn.kanban": "Kanban",
246
+ "@app.matterhorn.polls": "Polls",
247
+ "@app.matterhorn.wiki": "Wiki"
248
+ };
249
+ let registry;
250
+ try {
251
+ registry = matterhornApps().localWorkspaceRegistry();
252
+ } catch {
253
+ registry = Object.fromEntries(Object.entries(fallback).map(([alias, name]) => [alias, { name }]));
254
+ }
255
+ const aliases = Object.keys(registry).filter((name) => name.startsWith("@app.matterhorn.")).sort();
256
+ io.log("Available Matterhorn app aliases:");
257
+ for (const alias of aliases) {
258
+ const app = registry[alias];
259
+ const pack = app?.appPack || app?.eventAppPack || app?.kanbanAppPack || app?.wikiAppPack || app?.pollsAppPack || app?.budgetAppPack || app?.crmAppPack;
260
+ const label = pack?.name || app?.name || fallback[alias] || alias;
261
+ io.log(` ${alias} - ${label}`);
262
+ }
263
+ return aliases;
264
+ }
265
+
266
+ function doctor(io = console) {
267
+ const checks = [];
268
+ const nodeMajor = Number(process.versions.node.split(".")[0]);
269
+ checks.push({ name: "Node", ok: nodeMajor >= 22, detail: process.versions.node });
270
+ checks.push({ name: "Config", ok: true, detail: rc().usermatterhornrcFile() });
271
+ checks.push({ name: "Data directory", ok: true, detail: dataDir() });
272
+ try {
273
+ require.resolve("ws");
274
+ checks.push({ name: "WebSocket dependency", ok: true, detail: "ws resolved" });
275
+ } catch (error) {
276
+ checks.push({ name: "WebSocket dependency", ok: false, detail: error.message });
277
+ }
278
+ try {
279
+ const manifest = require("../package.json");
280
+ const exports = manifest.exports || {};
281
+ const missing = Object.values(exports)
282
+ .flatMap((entry) => typeof entry === "string" ? [entry] : Object.values(entry || {}))
283
+ .filter((target) => typeof target === "string" && !target.includes("*"))
284
+ .filter((target) => !fs.existsSync(path.join(__dirname, "..", target)));
285
+ checks.push({ name: "Package exports", ok: missing.length === 0, detail: missing.length === 0 ? "all exported files exist" : `missing ${missing.join(", ")}` });
286
+ checks.push({ name: "Retired browser client export", ok: !Object.prototype.hasOwnProperty.call(exports, "./client/matterhorn-client.js"), detail: "matterhorn-sdk/client/matterhorn-client.js is not exported" });
287
+ } catch (error) {
288
+ checks.push({ name: "Package exports", ok: false, detail: error.message });
289
+ }
290
+ for (const key of ["MATTERHORN_TRUSTED_APP_CODE", "MATTERHORN_UNSAFE_APP_FULL_ENV", "MATTERHORN_UNSAFE_GIT_APP_REF"]) {
291
+ checks.push({ name: `Unsafe env ${key}`, ok: process.env[key] !== "1", detail: process.env[key] === "1" ? "set for this process" : "unset" });
292
+ }
293
+ try {
294
+ const { buildMatterhornCommandEnv } = require("./appFrontend/commandEnv.cjs");
295
+ const previous = process.env.NPM_TOKEN;
296
+ process.env.NPM_TOKEN = "doctor-secret";
297
+ const env = buildMatterhornCommandEnv({ trust: "trusted-installed" });
298
+ if (previous === undefined) delete process.env.NPM_TOKEN;
299
+ else process.env.NPM_TOKEN = previous;
300
+ checks.push({ name: "App command env policy", ok: env.NPM_TOKEN === undefined, detail: "full env inheritance disabled by default" });
301
+ } catch (error) {
302
+ checks.push({ name: "App command env policy", ok: false, detail: error.message });
303
+ }
304
+ try {
305
+ const { loadMatterhornApp } = require("./matterhornAppLoader.cjs");
306
+ const previous = process.env.MATTERHORN_TRUSTED_APP_CODE;
307
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "matterhorn-doctor-app-"));
308
+ const packageDir = path.join(tmp, "node_modules", "doctor-js-app");
309
+ fs.mkdirSync(packageDir, { recursive: true });
310
+ fs.writeFileSync(path.join(packageDir, "package.json"), JSON.stringify({ name: "doctor-js-app", version: "1.0.0", main: "index.cjs" }));
311
+ fs.writeFileSync(path.join(packageDir, "index.cjs"), "throw new Error('doctor package JavaScript executed');\n");
312
+ let blocked = false;
313
+ try {
314
+ process.env.MATTERHORN_TRUSTED_APP_CODE = "1";
315
+ loadMatterhornApp("doctor-js-app", { cwd: tmp });
316
+ } catch {
317
+ blocked = true;
318
+ } finally {
319
+ if (previous === undefined) delete process.env.MATTERHORN_TRUSTED_APP_CODE;
320
+ else process.env.MATTERHORN_TRUSTED_APP_CODE = previous;
321
+ fs.rmSync(tmp, { recursive: true, force: true });
322
+ }
323
+ checks.push({ name: "Remote package JS descriptor policy", ok: blocked, detail: "package refs require static JSON or per-source trust" });
324
+ } catch (error) {
325
+ checks.push({ name: "Remote package JS descriptor policy", ok: false, detail: error.message });
326
+ }
327
+ try {
328
+ const { DEFAULT_RELAY_IPC_HOST } = relayDefaults();
329
+ checks.push({ name: "Relay IPC default binding", ok: DEFAULT_RELAY_IPC_HOST === "127.0.0.1", detail: `default host ${DEFAULT_RELAY_IPC_HOST}` });
330
+ } catch (error) {
331
+ checks.push({ name: "Relay IPC default binding", ok: false, detail: error.message });
332
+ }
333
+ try {
334
+ const { HostPluginRuntime, createOperationRoleKeyGrant, createRoleKeyAuthenticator, generateOperationRoleKeyPair } = require("@mh-gg/host-runtime");
335
+ let atomicBlocked = false;
336
+ try {
337
+ new HostPluginRuntime({
338
+ room: { id: "doctor", appPack: { id: "app", hash: "sha256-app" } },
339
+ plugins: [{ id: "plugin", version: "1", schemas: { state: {}, operations: {} }, createInitialState: () => ({}), authorize: () => ({ ok: false }), reduce: (_ctx, state) => state }],
340
+ productionRuntime: true
341
+ });
342
+ } catch (error) {
343
+ atomicBlocked = error.code === "ATOMIC_COMMIT_REQUIRED";
344
+ }
345
+ checks.push({ name: "Production atomic store policy", ok: atomicBlocked, detail: "production runtime requires commitStateAndOperation" });
346
+
347
+ const key = generateOperationRoleKeyPair({ role: "user" });
348
+ const grant = createOperationRoleKeyGrant({ roomId: "doctor", appPackId: "app", appPackHash: "sha256-app", credentialId: "cred", role: "user", publicKeyPem: key.publicKeyPem, issuedAt: 1 });
349
+ const auth = createRoleKeyAuthenticator({ roomId: "doctor", appPackId: "app", appPackHash: "sha256-app", grants: [grant], productionRuntime: true });
350
+ checks.push({ name: "Production role-key grant policy", ok: auth.matterhornRoleKeyAuthenticator === true && auth.requireSignedGrants === true, detail: "production authenticators require signed grants" });
351
+ } catch (error) {
352
+ checks.push({ name: "Production runtime policy", ok: false, detail: error.message });
353
+ }
354
+ for (const check of checks) io.log(`${check.ok ? "✓" : "✗"} ${check.name}: ${check.detail}`);
355
+ return checks;
356
+ }
357
+
358
+ function configure(args) {
359
+ const [command, key, value] = args;
360
+ const file = rc().usermatterhornrcFile();
361
+ const current = rc().readmatterhornrc(file);
362
+
363
+ if (command === "set" && key === "host" && value) {
364
+ rc().writematterhornrc({ ...current, host: value }, file);
365
+ console.log(`Saved host ${value} to ${file}`);
366
+ return;
367
+ }
368
+
369
+ if (command === "get" && key === "host") {
370
+ console.log(current.host || "");
371
+ return;
372
+ }
373
+
374
+ if (command === "list") {
375
+ console.log(JSON.stringify(current, null, 2));
376
+ return;
377
+ }
378
+
379
+ usage();
380
+ process.exit(1);
381
+ }
382
+
383
+ async function main() {
384
+ const args = process.argv.slice(2);
385
+ if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
386
+ usage();
387
+ process.exit(args.length === 0 ? 1 : 0);
388
+ }
389
+ if (args[0] === "apps") {
390
+ listApps(console);
391
+ return;
392
+ }
393
+ if (args[0] === "doctor") {
394
+ const checks = doctor(console);
395
+ process.exitCode = checks.every((check) => check.ok) ? 0 : 1;
396
+ return;
397
+ }
398
+ if (args[0] === "configure") {
399
+ rc().applymatterhornrcDefaults();
400
+ configure(args.slice(1));
401
+ return;
402
+ }
403
+ rc().applymatterhornrcDefaults();
404
+ if (args[0] === "relay") {
405
+ if (args[1] === "add") {
406
+ addRelay(args.slice(2));
407
+ return;
408
+ }
409
+ if (args[1] === "invite") {
410
+ printRelayInvite(args.slice(2));
411
+ return;
412
+ }
413
+ if (args[1] === "trust") {
414
+ relayTrust(args.slice(2));
415
+ return;
416
+ }
417
+ const relayPath = path.join(__dirname, "..", "host", "relay.cjs");
418
+ const child = spawn(process.execPath, [relayPath, ...relayStartArgs(args.slice(1))], {
419
+ stdio: "inherit"
420
+ });
421
+
422
+ child.on("exit", (code, signal) => {
423
+ if (signal) {
424
+ process.kill(process.pid, signal);
425
+ return;
426
+ }
427
+ process.exit(code ?? 0);
428
+ });
429
+ return;
430
+ }
431
+ if (args[0] === "host") {
432
+ await matterhornApps().handleAppHostCommand(args.slice(1));
433
+ return;
434
+ }
435
+ if (args[0] === "launcher") {
436
+ matterhornApps().handleLauncherCommand(args.slice(1));
437
+ return;
438
+ }
439
+ if (args[0] === "launch") {
440
+ const code = await installs().runAppLaunchCommand(args.slice(1), console, {
441
+ matterhornHome: dataDir(),
442
+ dataDir: dataDir(),
443
+ defaultAppUrl: defaultAppUrl()
444
+ });
445
+ process.exitCode = code;
446
+ return;
447
+ }
448
+ if (args[0] === "install") {
449
+ const code = await installs().runAppInstallCommand(args.slice(1), console, {
450
+ matterhornHome: dataDir(),
451
+ dataDir: dataDir(),
452
+ defaultAppUrl: defaultAppUrl()
453
+ });
454
+ process.exitCode = code;
455
+ return;
456
+ }
457
+ if (args[0] === "run") {
458
+ const code = await installs().runInstalledAppCommand(args.slice(1), console, {
459
+ matterhornHome: dataDir(),
460
+ dataDir: dataDir(),
461
+ defaultAppUrl: defaultAppUrl()
462
+ });
463
+ process.exitCode = code;
464
+ return;
465
+ }
466
+ if (args[0] === "deploy") {
467
+ const code = await deploy().runAppDeployCommand(args.slice(1), console, {
468
+ dataDir: dataDir(),
469
+ defaultAppUrl: defaultAppUrl()
470
+ });
471
+ process.exitCode = code;
472
+ return;
473
+ }
474
+
475
+ const code = await appHost().runAppHostCommand(args, console, {
476
+ dataDir: dataDir(),
477
+ defaultAppUrl: defaultAppUrl()
478
+ });
479
+ process.exitCode = code;
480
+ }
481
+
482
+ if (require.main === module) {
483
+ main().catch((error) => {
484
+ console.error(error.message || error);
485
+ process.exit(1);
486
+ });
487
+ }
488
+
489
+ module.exports = {
490
+ addRelay,
491
+ configure,
492
+ doctor,
493
+ listApps,
494
+ main,
495
+ parseRelayAddArgs,
496
+ printRelayInvite,
497
+ relayStartArgs,
498
+ relayTrust,
499
+ resolveRelayReferenceForCli,
500
+ usage
501
+ };