@kdonev/termscape 0.1.0

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 (139) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +288 -0
  3. package/dist/agents/env.d.ts +36 -0
  4. package/dist/agents/env.d.ts.map +1 -0
  5. package/dist/agents/env.js +61 -0
  6. package/dist/agents/env.js.map +1 -0
  7. package/dist/agents/profiles.d.ts +45 -0
  8. package/dist/agents/profiles.d.ts.map +1 -0
  9. package/dist/agents/profiles.js +123 -0
  10. package/dist/agents/profiles.js.map +1 -0
  11. package/dist/agents/resolve.d.ts +16 -0
  12. package/dist/agents/resolve.d.ts.map +1 -0
  13. package/dist/agents/resolve.js +71 -0
  14. package/dist/agents/resolve.js.map +1 -0
  15. package/dist/agents/router.d.ts +29 -0
  16. package/dist/agents/router.d.ts.map +1 -0
  17. package/dist/agents/router.js +83 -0
  18. package/dist/agents/router.js.map +1 -0
  19. package/dist/agents/tokens.d.ts +21 -0
  20. package/dist/agents/tokens.d.ts.map +1 -0
  21. package/dist/agents/tokens.js +39 -0
  22. package/dist/agents/tokens.js.map +1 -0
  23. package/dist/agents/wiring.d.ts +30 -0
  24. package/dist/agents/wiring.d.ts.map +1 -0
  25. package/dist/agents/wiring.js +110 -0
  26. package/dist/agents/wiring.js.map +1 -0
  27. package/dist/browser.d.ts +13 -0
  28. package/dist/browser.d.ts.map +1 -0
  29. package/dist/browser.js +34 -0
  30. package/dist/browser.js.map +1 -0
  31. package/dist/cli.d.ts +3 -0
  32. package/dist/cli.d.ts.map +1 -0
  33. package/dist/cli.js +200 -0
  34. package/dist/cli.js.map +1 -0
  35. package/dist/db/index.d.ts +14 -0
  36. package/dist/db/index.d.ts.map +1 -0
  37. package/dist/db/index.js +24 -0
  38. package/dist/db/index.js.map +1 -0
  39. package/dist/db/migrations.d.ts +15 -0
  40. package/dist/db/migrations.d.ts.map +1 -0
  41. package/dist/db/migrations.js +176 -0
  42. package/dist/db/migrations.js.map +1 -0
  43. package/dist/db/store.d.ts +71 -0
  44. package/dist/db/store.d.ts.map +1 -0
  45. package/dist/db/store.js +341 -0
  46. package/dist/db/store.js.map +1 -0
  47. package/dist/hub.d.ts +188 -0
  48. package/dist/hub.d.ts.map +1 -0
  49. package/dist/hub.js +636 -0
  50. package/dist/hub.js.map +1 -0
  51. package/dist/hub.tgz +0 -0
  52. package/dist/mcp/server.d.ts +31 -0
  53. package/dist/mcp/server.d.ts.map +1 -0
  54. package/dist/mcp/server.js +66 -0
  55. package/dist/mcp/server.js.map +1 -0
  56. package/dist/paths.d.ts +27 -0
  57. package/dist/paths.d.ts.map +1 -0
  58. package/dist/paths.js +31 -0
  59. package/dist/paths.js.map +1 -0
  60. package/dist/protocol/address.d.ts +15 -0
  61. package/dist/protocol/address.d.ts.map +1 -0
  62. package/dist/protocol/address.js +39 -0
  63. package/dist/protocol/address.js.map +1 -0
  64. package/dist/protocol/domain.d.ts +151 -0
  65. package/dist/protocol/domain.d.ts.map +1 -0
  66. package/dist/protocol/domain.js +104 -0
  67. package/dist/protocol/domain.js.map +1 -0
  68. package/dist/protocol/index.d.ts +7 -0
  69. package/dist/protocol/index.d.ts.map +1 -0
  70. package/dist/protocol/index.js +7 -0
  71. package/dist/protocol/index.js.map +1 -0
  72. package/dist/protocol/inject.d.ts +29 -0
  73. package/dist/protocol/inject.d.ts.map +1 -0
  74. package/dist/protocol/inject.js +40 -0
  75. package/dist/protocol/inject.js.map +1 -0
  76. package/dist/protocol/mcp-tools.d.ts +45 -0
  77. package/dist/protocol/mcp-tools.d.ts.map +1 -0
  78. package/dist/protocol/mcp-tools.js +46 -0
  79. package/dist/protocol/mcp-tools.js.map +1 -0
  80. package/dist/protocol/peer.d.ts +304 -0
  81. package/dist/protocol/peer.d.ts.map +1 -0
  82. package/dist/protocol/peer.js +183 -0
  83. package/dist/protocol/peer.js.map +1 -0
  84. package/dist/protocol/ws.d.ts +416 -0
  85. package/dist/protocol/ws.d.ts.map +1 -0
  86. package/dist/protocol/ws.js +119 -0
  87. package/dist/protocol/ws.js.map +1 -0
  88. package/dist/remote/deployer.d.ts +78 -0
  89. package/dist/remote/deployer.d.ts.map +1 -0
  90. package/dist/remote/deployer.js +216 -0
  91. package/dist/remote/deployer.js.map +1 -0
  92. package/dist/remote/enroll.d.ts +14 -0
  93. package/dist/remote/enroll.d.ts.map +1 -0
  94. package/dist/remote/enroll.js +244 -0
  95. package/dist/remote/enroll.js.map +1 -0
  96. package/dist/remote/join-script.d.ts +29 -0
  97. package/dist/remote/join-script.d.ts.map +1 -0
  98. package/dist/remote/join-script.js +709 -0
  99. package/dist/remote/join-script.js.map +1 -0
  100. package/dist/remote/join.d.ts +19 -0
  101. package/dist/remote/join.d.ts.map +1 -0
  102. package/dist/remote/join.js +195 -0
  103. package/dist/remote/join.js.map +1 -0
  104. package/dist/remote/lan.d.ts +45 -0
  105. package/dist/remote/lan.d.ts.map +1 -0
  106. package/dist/remote/lan.js +105 -0
  107. package/dist/remote/lan.js.map +1 -0
  108. package/dist/remote/peer-serve.d.ts +27 -0
  109. package/dist/remote/peer-serve.d.ts.map +1 -0
  110. package/dist/remote/peer-serve.js +274 -0
  111. package/dist/remote/peer-serve.js.map +1 -0
  112. package/dist/remote/peer.d.ts +62 -0
  113. package/dist/remote/peer.d.ts.map +1 -0
  114. package/dist/remote/peer.js +250 -0
  115. package/dist/remote/peer.js.map +1 -0
  116. package/dist/remote/registry.d.ts +104 -0
  117. package/dist/remote/registry.d.ts.map +1 -0
  118. package/dist/remote/registry.js +359 -0
  119. package/dist/remote/registry.js.map +1 -0
  120. package/dist/remote/tarball.d.ts +9 -0
  121. package/dist/remote/tarball.d.ts.map +1 -0
  122. package/dist/remote/tarball.js +22 -0
  123. package/dist/remote/tarball.js.map +1 -0
  124. package/dist/server.d.ts +54 -0
  125. package/dist/server.d.ts.map +1 -0
  126. package/dist/server.js +438 -0
  127. package/dist/server.js.map +1 -0
  128. package/dist/session/manager.d.ts +104 -0
  129. package/dist/session/manager.d.ts.map +1 -0
  130. package/dist/session/manager.js +421 -0
  131. package/dist/session/manager.js.map +1 -0
  132. package/dist/session/pty.d.ts +114 -0
  133. package/dist/session/pty.d.ts.map +1 -0
  134. package/dist/session/pty.js +300 -0
  135. package/dist/session/pty.js.map +1 -0
  136. package/package.json +49 -0
  137. package/web/assets/index-B2JtCtHa.css +1 -0
  138. package/web/assets/index-BOqZOoep.js +107 -0
  139. package/web/index.html +13 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"join-script.js","sourceRoot":"","sources":["../../src/remote/join-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,kFAAkF;AAClF,SAAS,EAAE,CAAC,CAAS;IACnB,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AACzC,CAAC;AAED,uEAAuE;AACvE,MAAM,SAAS,GAAG,sCAAsC,CAAC;AAEzD,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,KAAa;IAC3D,OAAO;kDACyC,MAAM;;;UAG9C,EAAE,CAAC,MAAM,CAAC;aACP,EAAE,CAAC,KAAK,CAAC;;YAEV,EAAE,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqSxB,CAAC;AACF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,KAAa;IAChE,OAAO,mDAAmD,MAAM;;;;eAInD,EAAE,CAAC,MAAM,CAAC;eACV,EAAE,CAAC,KAAK,CAAC;;eAET,EAAE,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2W3B,CAAC;AACF,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { Hub } from '../hub.js';
2
+ import type { PeerServer } from './peer-serve.js';
3
+ export interface JoinOptions {
4
+ hub: Hub;
5
+ peerServer: PeerServer;
6
+ /** The canvas hub's origin, e.g. http://192.168.1.40:7333 */
7
+ hubUrl: string;
8
+ /** Single-use enrollment token; ignored once we hold a durable host token. */
9
+ joinToken?: string;
10
+ label?: string;
11
+ /** Where to keep the durable host token. Defaults to the hub home. */
12
+ tokenFile?: string;
13
+ log?: (line: string) => void;
14
+ }
15
+ export interface JoinLink {
16
+ stop(): void;
17
+ }
18
+ export declare function joinCanvas(opts: JoinOptions): JoinLink;
19
+ //# sourceMappingURL=join.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"join.d.ts","sourceRoot":"","sources":["../../src/remote/join.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAGrC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAuBlD,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,GAAG,CAAC;IACT,UAAU,EAAE,UAAU,CAAC;IACvB,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,IAAI,IAAI,CAAC;CACd;AAkCD,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ,CAgJtD"}
@@ -0,0 +1,195 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, rmSync } from 'node:fs';
2
+ import { hostname, platform, arch, homedir } from 'node:os';
3
+ import { dirname } from 'node:path';
4
+ import { WebSocket } from 'ws';
5
+ import { PEER_SCHEMA_VERSION, PeerResponse } from '../protocol/index.js';
6
+ import { HUB_VERSION } from '../hub.js';
7
+ import { paths } from '../paths.js';
8
+ /**
9
+ * The joining machine's side of enrollment.
10
+ *
11
+ * It dials the canvas hub and then answers its requests — the same role a hub
12
+ * deployed over SSH plays on its `/peer` endpoint, just over a socket opened
13
+ * from this end. That is why this file is thin: `peer-serve.ts` does the work,
14
+ * and all that is left here is getting a socket to hand it and keeping one.
15
+ *
16
+ * The enrollment token is spent once. What comes back in `welcome` is a
17
+ * durable host token written to disk, so every later start — a reboot, a
18
+ * crash, a dropped link — rejoins silently with no second trip to the
19
+ * download page.
20
+ *
21
+ * The two TERMSCAPE_JOIN* markers below exist for the installer to grep. A hub
22
+ * that has started is not a hub that has joined, and reporting success on the
23
+ * former is how you end up telling someone they are connected when the canvas
24
+ * refused them.
25
+ */
26
+ const MAX_BACKOFF_MS = 15_000;
27
+ function readHostToken(file) {
28
+ try {
29
+ const t = readFileSync(file, 'utf8').trim();
30
+ return t || null;
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ function writeHostToken(file, token) {
37
+ mkdirSync(dirname(file), { recursive: true });
38
+ writeFileSync(file, token, { mode: 0o600 });
39
+ }
40
+ /** Drop a credential the canvas has told us it does not recognise. */
41
+ function clearHostToken(file) {
42
+ try {
43
+ rmSync(file, { force: true });
44
+ }
45
+ catch {
46
+ // Nothing here depends on it being gone; the retry ignores it anyway.
47
+ }
48
+ }
49
+ /** http://host:port -> ws://host:port/peer-in */
50
+ function peerInUrl(hubUrl) {
51
+ const u = new URL(hubUrl);
52
+ u.protocol = u.protocol === 'https:' ? 'wss:' : 'ws:';
53
+ u.pathname = '/peer-in';
54
+ u.search = '';
55
+ return u.toString();
56
+ }
57
+ export function joinCanvas(opts) {
58
+ const log = opts.log ?? ((line) => console.log(`[join] ${line}`));
59
+ /**
60
+ * A refusal we cannot retry our way out of. The hub was started only to
61
+ * join, so there is nothing left for it to do; whoever owns the process
62
+ * listens for this and stops, which also frees the install for a re-run.
63
+ */
64
+ const fatal = (message) => {
65
+ stopped = true;
66
+ log(`TERMSCAPE_JOIN_FAILED=${message}`);
67
+ opts.hub.emit('joinFailed', message);
68
+ };
69
+ const tokenFile = opts.tokenFile ?? paths.hostTokenFile();
70
+ const url = peerInUrl(opts.hubUrl);
71
+ let backoff = 500;
72
+ let stopped = false;
73
+ let socket = null;
74
+ let timer = null;
75
+ /**
76
+ * Whether the stored host token has already been tried and rejected. Once
77
+ * it has, the enrollment key the installer supplied is the only thing left
78
+ * worth trying, and it is tried exactly once.
79
+ */
80
+ let hostTokenSpent = false;
81
+ function dial() {
82
+ if (stopped)
83
+ return;
84
+ const hostToken = hostTokenSpent ? null : readHostToken(tokenFile);
85
+ if (!hostToken && !opts.joinToken) {
86
+ fatal('no enrollment token and no stored host token; nothing to join with');
87
+ return;
88
+ }
89
+ const ws = new WebSocket(url);
90
+ socket = ws;
91
+ const usedHostToken = hostToken !== null;
92
+ // Set once the canvas hub accepts us, so a rejection is not retried
93
+ // forever as if it were a network blip.
94
+ let welcomed = false;
95
+ ws.on('open', () => {
96
+ const hello = {
97
+ t: 'hello',
98
+ token: hostToken ?? opts.joinToken,
99
+ hubVersion: HUB_VERSION,
100
+ schemaVersion: PEER_SCHEMA_VERSION,
101
+ // Only on first contact: after that the host token identifies us and
102
+ // the canvas hub already has a row describing this machine.
103
+ ...(hostToken
104
+ ? {}
105
+ : {
106
+ enroll: {
107
+ label: opts.label || hostname(),
108
+ platform: platform(),
109
+ arch: arch(),
110
+ homeDir: homedir(),
111
+ },
112
+ }),
113
+ };
114
+ ws.send(JSON.stringify(hello));
115
+ });
116
+ const onHandshake = (raw) => {
117
+ if (welcomed)
118
+ return;
119
+ let parsed;
120
+ try {
121
+ parsed = PeerResponse.safeParse(JSON.parse(raw.toString('utf8')));
122
+ }
123
+ catch {
124
+ return;
125
+ }
126
+ if (!parsed.success)
127
+ return;
128
+ const msg = parsed.data;
129
+ if (msg.t === 'err') {
130
+ // A stored host token that the canvas no longer knows means this
131
+ // machine was removed from it — the common case being someone
132
+ // dropping the host and then re-running the join command. The fresh
133
+ // key they were just handed is exactly the answer, so use it rather
134
+ // than failing while holding an unused one.
135
+ if (usedHostToken && opts.joinToken && !hostTokenSpent) {
136
+ hostTokenSpent = true;
137
+ clearHostToken(tokenFile);
138
+ log('this canvas no longer knows this machine; enrolling again');
139
+ ws.close(); // The close handler redials, now without a host token.
140
+ return;
141
+ }
142
+ // Otherwise a bad token or a version gap, which retrying will not fix.
143
+ fatal(msg.message);
144
+ ws.close();
145
+ return;
146
+ }
147
+ if (msg.t !== 'welcome')
148
+ return;
149
+ if (msg.schemaVersion !== PEER_SCHEMA_VERSION) {
150
+ fatal(`schema mismatch: the canvas hub ${msg.hubVersion} speaks v${msg.schemaVersion}, ` +
151
+ `this hub speaks v${PEER_SCHEMA_VERSION}. Update whichever is older.`);
152
+ ws.close();
153
+ return;
154
+ }
155
+ if (msg.hostToken) {
156
+ writeHostToken(tokenFile, msg.hostToken);
157
+ log('enrolled; this machine will rejoin on its own from now on');
158
+ }
159
+ welcomed = true;
160
+ backoff = 500;
161
+ ws.off('message', onHandshake);
162
+ // Hand the socket to the same code that serves an accepted /peer link.
163
+ // The canvas hub drives from here; we answer.
164
+ opts.peerServer.serve(ws, { preAuthed: true });
165
+ // Machine-readable: the installer waits for this, not for the port.
166
+ log(`TERMSCAPE_JOINED=1 joined ${opts.hubUrl}`);
167
+ };
168
+ ws.on('message', onHandshake);
169
+ ws.on('error', (err) => {
170
+ if (!welcomed)
171
+ log(`cannot reach ${opts.hubUrl}: ${err.message}`);
172
+ });
173
+ ws.on('close', () => {
174
+ socket = null;
175
+ if (stopped)
176
+ return;
177
+ if (welcomed)
178
+ log('link dropped; reconnecting');
179
+ timer = setTimeout(dial, backoff);
180
+ timer.unref?.();
181
+ backoff = Math.min(backoff * 2, MAX_BACKOFF_MS);
182
+ });
183
+ }
184
+ dial();
185
+ return {
186
+ stop() {
187
+ stopped = true;
188
+ if (timer)
189
+ clearTimeout(timer);
190
+ socket?.close();
191
+ socket = null;
192
+ },
193
+ };
194
+ }
195
+ //# sourceMappingURL=join.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"join.js","sourceRoot":"","sources":["../../src/remote/join.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Picking the address another machine can reach us on.
3
+ *
4
+ * `--listen lan` exists because the alternative is asking someone to work out
5
+ * their own IPv4 address before they can attach a second machine, which is
6
+ * exactly the kind of step that makes a feature go unused.
7
+ */
8
+ /** First non-internal IPv4 address, preferring real NICs over virtual ones. */
9
+ export declare function lanAddress(): string | null;
10
+ /**
11
+ * Turn a `--listen` value into a bind address.
12
+ *
13
+ * `lan` binds the wildcard rather than the LAN address alone, because a
14
+ * process can only bind one address and loopback has to keep working: it is
15
+ * where the browser opens the canvas, and where every agent's generated MCP
16
+ * config points. Binding the LAN address by itself would take the hub off
17
+ * 127.0.0.1 and break both. The LAN address is still what gets *advertised* —
18
+ * see advertisedHost.
19
+ */
20
+ export declare function resolveBindHost(spec: string): string;
21
+ /** Whether a bind on this address also answers on 127.0.0.1. */
22
+ export declare function coversLoopback(host: string): boolean;
23
+ /** Bracket a bare IPv6 literal so it can sit in a URL. */
24
+ export declare function urlHost(host: string): string;
25
+ /** Whether a bound address is reachable from anywhere but this machine. */
26
+ export declare function isLoopback(host: string): boolean;
27
+ /**
28
+ * The address to advertise for a given bind. A wildcard bind is reachable on
29
+ * every interface, so advertise the LAN one; anything else advertises itself.
30
+ */
31
+ export declare function advertisedHost(host: string): string | null;
32
+ /**
33
+ * The machine's own name, when it is actually usable as a URL host.
34
+ *
35
+ * `http://studio:7777/join` is far easier to carry to another machine than
36
+ * `http://192.168.88.79:7777/join`, but only if that machine can resolve it —
37
+ * Windows does it over LLMNR/NetBIOS, macOS and Linux over mDNS, and neither
38
+ * is guaranteed. So the name is only offered when it resolves *here* to the
39
+ * very address we bound. That is a proxy, not a promise, which is why the
40
+ * caller keeps the numeric URL as a stated fallback rather than dropping it.
41
+ *
42
+ * Returns null when nothing resolves, and the caller falls back to the IP.
43
+ */
44
+ export declare function preferredHostname(ip: string, resolve?: (name: string) => Promise<string[]>): Promise<string | null>;
45
+ //# sourceMappingURL=lan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lan.d.ts","sourceRoot":"","sources":["../../src/remote/lan.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AAEH,+EAA+E;AAC/E,wBAAgB,UAAU,IAAI,MAAM,GAAG,IAAI,CAa1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWpD;AAED,gEAAgE;AAChE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,0DAA0D;AAC1D,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,2EAA2E;AAC3E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI1D;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,MAAM,EACV,OAAO,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAkB,GAC5D,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBxB"}
@@ -0,0 +1,105 @@
1
+ import { hostname, networkInterfaces } from 'node:os';
2
+ import { lookup } from 'node:dns/promises';
3
+ /**
4
+ * Picking the address another machine can reach us on.
5
+ *
6
+ * `--listen lan` exists because the alternative is asking someone to work out
7
+ * their own IPv4 address before they can attach a second machine, which is
8
+ * exactly the kind of step that makes a feature go unused.
9
+ */
10
+ /** First non-internal IPv4 address, preferring real NICs over virtual ones. */
11
+ export function lanAddress() {
12
+ const candidates = [];
13
+ for (const [name, addrs] of Object.entries(networkInterfaces())) {
14
+ for (const a of addrs ?? []) {
15
+ if (a.family !== 'IPv4' || a.internal)
16
+ continue;
17
+ // Docker bridges, WSL adapters and VM host-only networks are reachable
18
+ // from almost nothing, so they lose to a real interface.
19
+ const virtual = /^(docker|br-|veth|vEthernet|virbr|utun|tailscale)/i.test(name);
20
+ if (virtual)
21
+ candidates.push(a.address);
22
+ else
23
+ candidates.unshift(a.address);
24
+ }
25
+ }
26
+ return candidates[0] ?? null;
27
+ }
28
+ /**
29
+ * Turn a `--listen` value into a bind address.
30
+ *
31
+ * `lan` binds the wildcard rather than the LAN address alone, because a
32
+ * process can only bind one address and loopback has to keep working: it is
33
+ * where the browser opens the canvas, and where every agent's generated MCP
34
+ * config points. Binding the LAN address by itself would take the hub off
35
+ * 127.0.0.1 and break both. The LAN address is still what gets *advertised* —
36
+ * see advertisedHost.
37
+ */
38
+ export function resolveBindHost(spec) {
39
+ if (spec === 'lan') {
40
+ if (!lanAddress()) {
41
+ throw new Error('--listen lan: no non-loopback IPv4 address on this machine. ' +
42
+ 'Pass an explicit address instead.');
43
+ }
44
+ return '0.0.0.0';
45
+ }
46
+ return spec;
47
+ }
48
+ /** Whether a bind on this address also answers on 127.0.0.1. */
49
+ export function coversLoopback(host) {
50
+ return host === '0.0.0.0' || host === '::' || isLoopback(host);
51
+ }
52
+ /** Bracket a bare IPv6 literal so it can sit in a URL. */
53
+ export function urlHost(host) {
54
+ return host.includes(':') ? `[${host}]` : host;
55
+ }
56
+ /** Whether a bound address is reachable from anywhere but this machine. */
57
+ export function isLoopback(host) {
58
+ return host === '127.0.0.1' || host === 'localhost' || host === '::1';
59
+ }
60
+ /**
61
+ * The address to advertise for a given bind. A wildcard bind is reachable on
62
+ * every interface, so advertise the LAN one; anything else advertises itself.
63
+ */
64
+ export function advertisedHost(host) {
65
+ if (host === '0.0.0.0' || host === '::')
66
+ return lanAddress();
67
+ if (isLoopback(host))
68
+ return null;
69
+ return host.includes(':') ? `[${host}]` : host;
70
+ }
71
+ /**
72
+ * The machine's own name, when it is actually usable as a URL host.
73
+ *
74
+ * `http://studio:7777/join` is far easier to carry to another machine than
75
+ * `http://192.168.88.79:7777/join`, but only if that machine can resolve it —
76
+ * Windows does it over LLMNR/NetBIOS, macOS and Linux over mDNS, and neither
77
+ * is guaranteed. So the name is only offered when it resolves *here* to the
78
+ * very address we bound. That is a proxy, not a promise, which is why the
79
+ * caller keeps the numeric URL as a stated fallback rather than dropping it.
80
+ *
81
+ * Returns null when nothing resolves, and the caller falls back to the IP.
82
+ */
83
+ export async function preferredHostname(ip, resolve = defaultResolve) {
84
+ const name = hostname().trim();
85
+ if (!name || isLoopback(name))
86
+ return null;
87
+ // Plain name first: a bare host is nicer to type than one with a suffix.
88
+ // `.local` is the mDNS form, which is what a Mac or a Linux box will answer.
89
+ const candidates = name.endsWith('.local') ? [name] : [name, `${name}.local`];
90
+ for (const candidate of candidates) {
91
+ try {
92
+ if ((await resolve(candidate)).includes(ip))
93
+ return candidate;
94
+ }
95
+ catch {
96
+ // Does not resolve here; try the next shape.
97
+ }
98
+ }
99
+ return null;
100
+ }
101
+ async function defaultResolve(name) {
102
+ const found = await lookup(name, { all: true, family: 4 });
103
+ return found.map((a) => a.address);
104
+ }
105
+ //# sourceMappingURL=lan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lan.js","sourceRoot":"","sources":["../../src/remote/lan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C;;;;;;GAMG;AAEH,+EAA+E;AAC/E,MAAM,UAAU,UAAU;IACxB,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,EAAE,CAAC;QAChE,KAAK,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;YAC5B,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,QAAQ;gBAAE,SAAS;YAChD,uEAAuE;YACvE,yDAAyD;YACzD,MAAM,OAAO,GAAG,oDAAoD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChF,IAAI,OAAO;gBAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;;gBACnC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACb,8DAA8D;gBAC5D,mCAAmC,CACtC,CAAC;QACJ,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;AACjE,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,KAAK,CAAC;AACxE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,UAAU,EAAE,CAAC;IAC7D,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAU,EACV,UAA+C,cAAc;IAE7D,MAAM,IAAI,GAAG,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAE3C,yEAAyE;IACzE,6EAA6E;IAC7E,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,IAAI,QAAQ,CAAC,CAAC;IAE9E,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAAE,OAAO,SAAS,CAAC;QAChE,CAAC;QAAC,MAAM,CAAC;YACP,6CAA6C;QAC/C,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;IAC3D,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { type PeerAgent, type PeerRelayAsk } from '../protocol/index.js';
2
+ import type { WebSocket } from 'ws';
3
+ import type { Hub } from '../hub.js';
4
+ /**
5
+ * The link back to the canvas, as the rest of this hub needs it. Separated
6
+ * from the serving machinery because a hub that owns a canvas has no uplink at
7
+ * all, and the difference should be a null check rather than a special case.
8
+ */
9
+ export interface Uplink {
10
+ /** True while a canvas is attached to this hub. */
11
+ readonly attached: boolean;
12
+ /** Agents the canvas has told us about. Never this hub's own. */
13
+ agents(): PeerAgent[];
14
+ /** Ask the canvas to act on an address we cannot resolve ourselves. */
15
+ ask<T>(ask: PeerRelayAsk): Promise<T>;
16
+ }
17
+ export interface PeerServer extends Uplink {
18
+ /** Wire one accepted or dialled socket into this hub. */
19
+ serve(socket: WebSocket, opts?: {
20
+ preAuthed?: boolean;
21
+ }): void;
22
+ /** Number of peers currently attached. */
23
+ readonly size: number;
24
+ closeAll(): void;
25
+ }
26
+ export declare function createPeerServer(hub: Hub, clientToken: string): PeerServer;
27
+ //# sourceMappingURL=peer-serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-serve.d.ts","sourceRoot":"","sources":["../../src/remote/peer-serve.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,YAAY,EAGlB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAuBrC;;;;GAIG;AACH,MAAM,WAAW,MAAM;IACrB,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,iEAAiE;IACjE,MAAM,IAAI,SAAS,EAAE,CAAC;IACtB,uEAAuE;IACvE,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,UAAW,SAAQ,MAAM;IACxC,yDAAyD;IACzD,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/D,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,IAAI,IAAI,CAAC;CAClB;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,GAAG,UAAU,CAgQ1E"}