@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,223 @@
1
+ const fs = require("node:fs");
2
+ const path = require("node:path");
3
+ const {
4
+ manifestHash,
5
+ validatePlayerPackManifest
6
+ } = require("@mh-gg/base");
7
+ const {
8
+ createMemoryRoomStore,
9
+ startRoomHost
10
+ } = require("@mh-gg/host-runtime");
11
+ const {
12
+ readmatterhornrc,
13
+ usermatterhornrcFile,
14
+ writematterhornrc
15
+ } = require("../host/matterhornrc.cjs");
16
+
17
+ function localWorkspaceRegistry() {
18
+ const events = require("@mh-gg/example-events");
19
+ const kanban = require("@mh-gg/example-kanban");
20
+ const wiki = require("@mh-gg/example-wiki");
21
+ const polls = require("@mh-gg/example-polls");
22
+ const budget = require("@mh-gg/example-budget");
23
+ const crm = require("@mh-gg/example-crm");
24
+ const plugins = require("@mh-gg/base-plugins");
25
+ const react = require("@mh-gg/example-react");
26
+ const svelte = require("@mh-gg/example-svelte");
27
+ const vue = require("@mh-gg/example-vue");
28
+ const lit = require("@mh-gg/example-lit");
29
+ const vanilla = require("@mh-gg/example-vanilla");
30
+ return {
31
+ "@app.matterhorn.events": events,
32
+ "@app.matterhorn.kanban": kanban,
33
+ "@app.matterhorn.wiki": wiki,
34
+ "@app.matterhorn.polls": polls,
35
+ "@app.matterhorn.budget": budget,
36
+ "@app.matterhorn.crm": crm,
37
+ "@mh-gg/example-events": events,
38
+ "@mh-gg/example-kanban": kanban,
39
+ "@mh-gg/example-wiki": wiki,
40
+ "@mh-gg/example-polls": polls,
41
+ "@mh-gg/example-budget": budget,
42
+ "@mh-gg/example-crm": crm,
43
+ "@app.matterhorn.react": react,
44
+ "@app.matterhorn.svelte": svelte,
45
+ "@app.matterhorn.vue": vue,
46
+ "@app.matterhorn.lit": lit,
47
+ "@app.matterhorn.vanilla": vanilla,
48
+ "@mh-gg/example-react": react,
49
+ "@mh-gg/example-svelte": svelte,
50
+ "@mh-gg/example-vue": vue,
51
+ "@mh-gg/example-lit": lit,
52
+ "@mh-gg/example-vanilla": vanilla,
53
+ "@mh-gg/base-plugins": plugins
54
+ };
55
+ }
56
+
57
+ function parseRefUrl(url) {
58
+ const text = String(url || "");
59
+ const hashIndex = text.indexOf("#");
60
+ return {
61
+ base: hashIndex === -1 ? text : text.slice(0, hashIndex),
62
+ exportName: hashIndex === -1 ? undefined : text.slice(hashIndex + 1)
63
+ };
64
+ }
65
+
66
+ function loadLocalPack(url, workspace = localWorkspaceRegistry()) {
67
+ const { base, exportName } = parseRefUrl(url);
68
+ let value;
69
+ if (base.startsWith("workspace:")) {
70
+ const packageName = base.slice("workspace:".length);
71
+ value = workspace[packageName];
72
+ if (value === undefined) throw new Error(`Workspace pack ${packageName} is not registered`);
73
+ } else if (base.startsWith("file:")) {
74
+ const filePath = path.resolve(base.slice("file:".length));
75
+ value = JSON.parse(fs.readFileSync(filePath, "utf8"));
76
+ } else {
77
+ throw new Error(`Unsupported local pack ref ${url}`);
78
+ }
79
+
80
+ if (exportName) {
81
+ if (value[exportName] === undefined) throw new Error(`Pack ref ${url} does not export ${exportName}`);
82
+ value = value[exportName];
83
+ }
84
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`Pack ref ${url} did not resolve to a manifest`);
85
+ return value;
86
+ }
87
+
88
+ function packRefWithIntegrity(url, workspace) {
89
+ const manifest = loadLocalPack(url, workspace);
90
+ return {
91
+ manifest,
92
+ ref: {
93
+ url,
94
+ integrity: manifestHash(manifest)
95
+ }
96
+ };
97
+ }
98
+
99
+ function buildPluginRegistry(workspace) {
100
+ const registry = {};
101
+ for (const [packageName, exports] of Object.entries(workspace)) {
102
+ for (const [exportName, value] of Object.entries(exports)) {
103
+ if (!value || typeof value !== "object" || Array.isArray(value)) continue;
104
+ if (!value.id || !value.version || (!value.schemas && !value.createInitialState && !value.reduce)) continue;
105
+ registry[value.id] = value;
106
+ registry[`workspace:${packageName}#${exportName}`] = value;
107
+ }
108
+ }
109
+ return registry;
110
+ }
111
+
112
+ function parseOptionArgs(args) {
113
+ const parsed = {
114
+ positional: []
115
+ };
116
+ for (let index = 0; index < args.length; index += 1) {
117
+ const token = args[index];
118
+ if (!token.startsWith("--")) {
119
+ parsed.positional.push(token);
120
+ continue;
121
+ }
122
+ const key = token.slice(2);
123
+ const value = args[index + 1];
124
+ if (value === undefined || value.startsWith("--")) {
125
+ parsed[key] = true;
126
+ continue;
127
+ }
128
+ parsed[key] = value;
129
+ index += 1;
130
+ }
131
+ return parsed;
132
+ }
133
+
134
+ function formatList(values) {
135
+ return values && values.length ? values.join(", ") : "none";
136
+ }
137
+
138
+ function createInviteUrl(packUrl) {
139
+ return ({ room }) => {
140
+ const params = new URLSearchParams({
141
+ roomId: room.id,
142
+ appPackId: room.appPack.id,
143
+ appPackHash: room.appPack.hash,
144
+ appPackUrl: packUrl
145
+ });
146
+ return `matterhorn://room/${encodeURIComponent(room.id)}?${params.toString()}`;
147
+ };
148
+ }
149
+
150
+ async function handleAppHostCommand(args, io = console, options = {}) {
151
+ const parsed = parseOptionArgs(args);
152
+ const roomName = parsed.positional[0];
153
+ if (!roomName || !parsed.pack) throw new Error("Usage: matterhorn host <roomName> --pack <app-pack-ref> [--player <player-pack-ref>]");
154
+
155
+ const workspace = options.workspace || localWorkspaceRegistry();
156
+ const app = packRefWithIntegrity(parsed.pack, workspace);
157
+ const player = parsed.player ? packRefWithIntegrity(parsed.player, workspace) : undefined;
158
+ const playerPacks = player ? [validatePlayerPackManifest(player.manifest)] : [];
159
+ const started = await startRoomHost({
160
+ roomName,
161
+ appPackRef: app.ref,
162
+ workspace,
163
+ pluginRegistry: buildPluginRegistry(workspace),
164
+ playerPacks,
165
+ store: options.store || createMemoryRoomStore(),
166
+ createInviteUrl: createInviteUrl(parsed.pack)
167
+ });
168
+
169
+ io.log("Matterhorn host ready");
170
+ io.log(`Room: ${started.room.id}`);
171
+ io.log(`App: ${app.manifest.name} (${app.manifest.id}@${app.manifest.version})`);
172
+ io.log(`App hash: ${started.room.appPack.hash}`);
173
+ io.log(`Publisher: ${app.manifest.publisher.name || app.manifest.publisher.id}`);
174
+ io.log(`Required capabilities: ${formatList(app.manifest.capabilities.required)}`);
175
+ io.log(`Optional capabilities: ${formatList(app.manifest.capabilities.optional || [])}`);
176
+ io.log(`Host sandbox: ${started.installed.hostPack.runtime.sandbox || "none"}`);
177
+ if (player) io.log(`Player override: ${player.manifest.name} (${player.manifest.id}@${player.manifest.version})`);
178
+ io.log(`Invite: ${started.inviteUrl}`);
179
+ return started;
180
+ }
181
+
182
+ function addPlayerToConfig(config, player, now = () => Date.now()) {
183
+ const players = Array.isArray(config.players) ? [...config.players] : [];
184
+ const next = {
185
+ url: player.ref.url,
186
+ integrity: player.ref.integrity,
187
+ id: player.manifest.id,
188
+ name: player.manifest.name,
189
+ version: player.manifest.version,
190
+ addedAt: new Date(now()).toISOString()
191
+ };
192
+ const index = players.findIndex((item) => item.url === next.url);
193
+ if (index === -1) players.push(next);
194
+ else players[index] = { ...players[index], ...next };
195
+ return { ...config, players };
196
+ }
197
+
198
+ function handleLauncherCommand(args, io = console, options = {}) {
199
+ if (args[0] !== "add-player") throw new Error("Usage: matterhorn launcher add-player --url <player-pack-ref>");
200
+ const parsed = parseOptionArgs(args.slice(1));
201
+ if (!parsed.url) throw new Error("Usage: matterhorn launcher add-player --url <player-pack-ref>");
202
+
203
+ const workspace = options.workspace || localWorkspaceRegistry();
204
+ const player = packRefWithIntegrity(parsed.url, workspace);
205
+ validatePlayerPackManifest(player.manifest);
206
+ const file = options.configFile || usermatterhornrcFile();
207
+ const next = addPlayerToConfig(readmatterhornrc(file), player, options.now);
208
+ writematterhornrc(next, file);
209
+ io.log(`Added player ${player.manifest.name} (${player.manifest.id})`);
210
+ io.log(`Config: ${file}`);
211
+ return next;
212
+ }
213
+
214
+ module.exports = {
215
+ addPlayerToConfig,
216
+ buildPluginRegistry,
217
+ handleAppHostCommand,
218
+ handleLauncherCommand,
219
+ loadLocalPack,
220
+ localWorkspaceRegistry,
221
+ packRefWithIntegrity,
222
+ parseOptionArgs
223
+ };
@@ -0,0 +1,108 @@
1
+ const { slugifyRoom } = require("@mh-gg/room-link");
2
+ const { parseServeMode } = require("./appFrontend.cjs");
3
+ const { runAppHostCommand } = require("./appHostCommand.cjs");
4
+ const { loadMatterhornDeployment } = require("./matterhornAppLoader.cjs");
5
+
6
+ const DEPLOY_OPTIONS = new Set(["--serve", "--frontend-port"]);
7
+
8
+ function splitAtSeparator(args) {
9
+ const index = args.indexOf("--");
10
+ if (index === -1) return { deployArgs: args, hostArgs: [] };
11
+ return {
12
+ deployArgs: args.slice(0, index),
13
+ hostArgs: args.slice(index + 1)
14
+ };
15
+ }
16
+
17
+ function optionHasValue(args, index) {
18
+ return args[index + 1] !== undefined && !args[index + 1].startsWith("--");
19
+ }
20
+
21
+ function appendHostOption(hostArgs, args, index) {
22
+ hostArgs.push(args[index]);
23
+ if (optionHasValue(args, index)) {
24
+ hostArgs.push(args[index + 1]);
25
+ return index + 1;
26
+ }
27
+ return index;
28
+ }
29
+
30
+ function parseAppDeployArgs(args) {
31
+ const { deployArgs, hostArgs: separatedHostArgs } = splitAtSeparator(args);
32
+ const [appRef, roomName, ...rest] = deployArgs;
33
+ if (!appRef || !roomName) {
34
+ throw new Error("Usage: matterhorn deploy <app-ref> <room-slug> [--serve [dev|built]] [--frontend-port PORT] [host options]");
35
+ }
36
+
37
+ const parsed = {
38
+ appRef,
39
+ roomName: slugifyRoom(roomName),
40
+ serveMode: undefined,
41
+ frontendPort: undefined,
42
+ hostArgs: [...separatedHostArgs]
43
+ };
44
+
45
+ for (let index = 0; index < rest.length; index += 1) {
46
+ const token = rest[index];
47
+ if (token === "--serve") {
48
+ if (optionHasValue(rest, index)) {
49
+ parsed.serveMode = parseServeMode(rest[index + 1]);
50
+ index += 1;
51
+ } else {
52
+ parsed.serveMode = "dev";
53
+ }
54
+ continue;
55
+ }
56
+ if (token === "--frontend-port") {
57
+ if (!optionHasValue(rest, index)) throw new Error("--frontend-port requires a port");
58
+ parsed.frontendPort = Number(rest[index + 1]);
59
+ if (!Number.isInteger(parsed.frontendPort) || parsed.frontendPort <= 0) throw new Error("--frontend-port must be a positive integer");
60
+ index += 1;
61
+ continue;
62
+ }
63
+ if (token.startsWith("--") && !DEPLOY_OPTIONS.has(token)) {
64
+ index = appendHostOption(parsed.hostArgs, rest, index);
65
+ continue;
66
+ }
67
+ throw new Error(`Unexpected Matterhorn deploy argument ${token}`);
68
+ }
69
+
70
+ return parsed;
71
+ }
72
+
73
+ function appHostArgsForDeployment(plan) {
74
+ const args = [plan.appRef, plan.roomName];
75
+ if (plan.serveMode) args.push("--serve", plan.serveMode);
76
+ if (plan.frontendPort) args.push("--frontend-port", String(plan.frontendPort));
77
+ if (plan.hostArgs.length > 0) args.push("--", ...plan.hostArgs);
78
+ return args;
79
+ }
80
+
81
+ function assertDeployable(deployment) {
82
+ if (deployment.kind !== "matterhorn.self-contained-app") throw new Error("Matterhorn deployment kind must be matterhorn.self-contained-app");
83
+ if (deployment.relay?.mode !== "embedded" || deployment.relay?.autoStart !== true) {
84
+ throw new Error("Matterhorn deploy requires an embedded auto-start relay");
85
+ }
86
+ if (deployment.host?.runner !== "matterhorn-example-host") {
87
+ throw new Error(`Unsupported Matterhorn deploy host runner ${deployment.host?.runner || "unknown"}`);
88
+ }
89
+ }
90
+
91
+ async function runAppDeployCommand(args, io = console, options = {}) {
92
+ const plan = parseAppDeployArgs(args);
93
+ const deployment = (options.loadMatterhornDeployment || loadMatterhornDeployment)(plan.appRef, {
94
+ cwd: options.cwd || process.cwd()
95
+ });
96
+ assertDeployable(deployment);
97
+ io.log(`Matterhorn deployment: ${deployment.name} (${deployment.id}@${deployment.version || "unknown"})`);
98
+ io.log("Relay: embedded p2p relay with host runtime registration");
99
+ if (deployment.relay.acceptsPeerRelays) io.log("Relay peers: enabled with --relay-peer <relay-address>");
100
+ return await (options.runAppHostCommand || runAppHostCommand)(appHostArgsForDeployment(plan), io, options);
101
+ }
102
+
103
+ module.exports = {
104
+ appHostArgsForDeployment,
105
+ assertDeployable,
106
+ parseAppDeployArgs,
107
+ runAppDeployCommand
108
+ };
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ const { emitMatterhornBundleForRef } = require("./matterhornAppLoader.cjs");
3
+
4
+ function main(argv = process.argv.slice(2)) {
5
+ const [appRef, cwd] = argv;
6
+ if (!appRef) throw new Error("Usage: matterhornEmitAppBundle <app-ref> [cwd]");
7
+ const result = emitMatterhornBundleForRef(appRef, { cwd: cwd || process.cwd() });
8
+ console.log(result.bundlePath);
9
+ }
10
+
11
+ if (require.main === module) {
12
+ try {
13
+ main();
14
+ } catch (error) {
15
+ console.error(error?.message || error);
16
+ process.exit(1);
17
+ }
18
+ }
19
+
20
+ module.exports = { main };