@n24q02m/hull 0.0.0-stage → 0.4.0-beta.9

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 (231) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +166 -2
  3. package/build/auth/credential-form.d.ts +163 -0
  4. package/build/auth/credential-form.d.ts.map +1 -0
  5. package/build/auth/credential-form.js +1869 -0
  6. package/build/auth/credential-form.js.map +1 -0
  7. package/build/auth/delegated-oauth-app.d.ts +90 -0
  8. package/build/auth/delegated-oauth-app.d.ts.map +1 -0
  9. package/build/auth/delegated-oauth-app.js +881 -0
  10. package/build/auth/delegated-oauth-app.js.map +1 -0
  11. package/build/auth/index.d.ts +12 -0
  12. package/build/auth/index.d.ts.map +1 -0
  13. package/build/auth/index.js +12 -0
  14. package/build/auth/index.js.map +1 -0
  15. package/build/auth/local-oauth-app.d.ts +152 -0
  16. package/build/auth/local-oauth-app.d.ts.map +1 -0
  17. package/build/auth/local-oauth-app.js +975 -0
  18. package/build/auth/local-oauth-app.js.map +1 -0
  19. package/build/auth/relay-login.d.ts +46 -0
  20. package/build/auth/relay-login.d.ts.map +1 -0
  21. package/build/auth/relay-login.js +197 -0
  22. package/build/auth/relay-login.js.map +1 -0
  23. package/build/auth/router.d.ts +18 -0
  24. package/build/auth/router.d.ts.map +1 -0
  25. package/build/auth/router.js +62 -0
  26. package/build/auth/router.js.map +1 -0
  27. package/build/auth/session-store.d.ts +49 -0
  28. package/build/auth/session-store.d.ts.map +1 -0
  29. package/build/auth/session-store.js +94 -0
  30. package/build/auth/session-store.js.map +1 -0
  31. package/build/auth/stable-sub.d.ts +27 -0
  32. package/build/auth/stable-sub.d.ts.map +1 -0
  33. package/build/auth/stable-sub.js +38 -0
  34. package/build/auth/stable-sub.js.map +1 -0
  35. package/build/auth/well-known.d.ts +4 -0
  36. package/build/auth/well-known.d.ts.map +1 -0
  37. package/build/auth/well-known.js +26 -0
  38. package/build/auth/well-known.js.map +1 -0
  39. package/build/cli/build-cli.d.ts +97 -0
  40. package/build/cli/build-cli.d.ts.map +1 -0
  41. package/build/cli/build-cli.js +309 -0
  42. package/build/cli/build-cli.js.map +1 -0
  43. package/build/cli/index.d.ts +2 -0
  44. package/build/cli/index.d.ts.map +1 -0
  45. package/build/cli/index.js +2 -0
  46. package/build/cli/index.js.map +1 -0
  47. package/build/crypto/aes.d.ts +8 -0
  48. package/build/crypto/aes.d.ts.map +1 -0
  49. package/build/crypto/aes.js +27 -0
  50. package/build/crypto/aes.js.map +1 -0
  51. package/build/crypto/ecdh.d.ts +5 -0
  52. package/build/crypto/ecdh.d.ts.map +1 -0
  53. package/build/crypto/ecdh.js +16 -0
  54. package/build/crypto/ecdh.js.map +1 -0
  55. package/build/crypto/index.d.ts +4 -0
  56. package/build/crypto/index.d.ts.map +1 -0
  57. package/build/crypto/index.js +4 -0
  58. package/build/crypto/index.js.map +1 -0
  59. package/build/crypto/kdf.d.ts +15 -0
  60. package/build/crypto/kdf.d.ts.map +1 -0
  61. package/build/crypto/kdf.js +26 -0
  62. package/build/crypto/kdf.js.map +1 -0
  63. package/build/index.d.ts +19 -0
  64. package/build/index.d.ts.map +1 -0
  65. package/build/index.js +28 -0
  66. package/build/index.js.map +1 -0
  67. package/build/lifecycle/lock.d.ts +68 -0
  68. package/build/lifecycle/lock.d.ts.map +1 -0
  69. package/build/lifecycle/lock.js +173 -0
  70. package/build/lifecycle/lock.js.map +1 -0
  71. package/build/oauth/cache.d.ts +22 -0
  72. package/build/oauth/cache.d.ts.map +1 -0
  73. package/build/oauth/cache.js +24 -0
  74. package/build/oauth/cache.js.map +1 -0
  75. package/build/oauth/index.d.ts +6 -0
  76. package/build/oauth/index.d.ts.map +1 -0
  77. package/build/oauth/index.js +6 -0
  78. package/build/oauth/index.js.map +1 -0
  79. package/build/oauth/jwt-issuer.d.ts +87 -0
  80. package/build/oauth/jwt-issuer.d.ts.map +1 -0
  81. package/build/oauth/jwt-issuer.js +205 -0
  82. package/build/oauth/jwt-issuer.js.map +1 -0
  83. package/build/oauth/pkce.d.ts +10 -0
  84. package/build/oauth/pkce.d.ts.map +1 -0
  85. package/build/oauth/pkce.js +34 -0
  86. package/build/oauth/pkce.js.map +1 -0
  87. package/build/oauth/provider.d.ts +52 -0
  88. package/build/oauth/provider.d.ts.map +1 -0
  89. package/build/oauth/provider.js +98 -0
  90. package/build/oauth/provider.js.map +1 -0
  91. package/build/oauth/user-store.d.ts +25 -0
  92. package/build/oauth/user-store.d.ts.map +1 -0
  93. package/build/oauth/user-store.js +85 -0
  94. package/build/oauth/user-store.js.map +1 -0
  95. package/build/relay/browser.d.ts +15 -0
  96. package/build/relay/browser.d.ts.map +1 -0
  97. package/build/relay/browser.js +121 -0
  98. package/build/relay/browser.js.map +1 -0
  99. package/build/relay/client.d.ts +36 -0
  100. package/build/relay/client.d.ts.map +1 -0
  101. package/build/relay/client.js +135 -0
  102. package/build/relay/client.js.map +1 -0
  103. package/build/relay/session.d.ts +12 -0
  104. package/build/relay/session.d.ts.map +1 -0
  105. package/build/relay/session.js +55 -0
  106. package/build/relay/session.js.map +1 -0
  107. package/build/relay/tool-helpers.d.ts +85 -0
  108. package/build/relay/tool-helpers.d.ts.map +1 -0
  109. package/build/relay/tool-helpers.js +103 -0
  110. package/build/relay/tool-helpers.js.map +1 -0
  111. package/build/relay/wordlist.d.ts +2 -0
  112. package/build/relay/wordlist.d.ts.map +1 -0
  113. package/build/relay/wordlist.js +7782 -0
  114. package/build/relay/wordlist.js.map +1 -0
  115. package/build/schema/types.d.ts +67 -0
  116. package/build/schema/types.d.ts.map +1 -0
  117. package/build/schema/types.js +2 -0
  118. package/build/schema/types.js.map +1 -0
  119. package/build/storage/atomic-write.d.ts +8 -0
  120. package/build/storage/atomic-write.d.ts.map +1 -0
  121. package/build/storage/atomic-write.js +26 -0
  122. package/build/storage/atomic-write.js.map +1 -0
  123. package/build/storage/backends.d.ts +74 -0
  124. package/build/storage/backends.d.ts.map +1 -0
  125. package/build/storage/backends.js +193 -0
  126. package/build/storage/backends.js.map +1 -0
  127. package/build/storage/config-file.d.ts +35 -0
  128. package/build/storage/config-file.d.ts.map +1 -0
  129. package/build/storage/config-file.js +259 -0
  130. package/build/storage/config-file.js.map +1 -0
  131. package/build/storage/credential-store.d.ts +71 -0
  132. package/build/storage/credential-store.d.ts.map +1 -0
  133. package/build/storage/credential-store.js +91 -0
  134. package/build/storage/credential-store.js.map +1 -0
  135. package/build/storage/data-dir.d.ts +58 -0
  136. package/build/storage/data-dir.d.ts.map +1 -0
  137. package/build/storage/data-dir.js +79 -0
  138. package/build/storage/data-dir.js.map +1 -0
  139. package/build/storage/encryption.d.ts +9 -0
  140. package/build/storage/encryption.d.ts.map +1 -0
  141. package/build/storage/encryption.js +27 -0
  142. package/build/storage/encryption.js.map +1 -0
  143. package/build/storage/home-dir.d.ts +11 -0
  144. package/build/storage/home-dir.d.ts.map +1 -0
  145. package/build/storage/home-dir.js +18 -0
  146. package/build/storage/home-dir.js.map +1 -0
  147. package/build/storage/index.d.ts +12 -0
  148. package/build/storage/index.d.ts.map +1 -0
  149. package/build/storage/index.js +15 -0
  150. package/build/storage/index.js.map +1 -0
  151. package/build/storage/machine-id.d.ts +4 -0
  152. package/build/storage/machine-id.d.ts.map +1 -0
  153. package/build/storage/machine-id.js +73 -0
  154. package/build/storage/machine-id.js.map +1 -0
  155. package/build/storage/mode.d.ts +23 -0
  156. package/build/storage/mode.d.ts.map +1 -0
  157. package/build/storage/mode.js +46 -0
  158. package/build/storage/mode.js.map +1 -0
  159. package/build/storage/per-plugin-store.d.ts +65 -0
  160. package/build/storage/per-plugin-store.d.ts.map +1 -0
  161. package/build/storage/per-plugin-store.js +194 -0
  162. package/build/storage/per-plugin-store.js.map +1 -0
  163. package/build/storage/resolver.d.ts +7 -0
  164. package/build/storage/resolver.d.ts.map +1 -0
  165. package/build/storage/resolver.js +38 -0
  166. package/build/storage/resolver.js.map +1 -0
  167. package/build/storage/session-lock.d.ts +19 -0
  168. package/build/storage/session-lock.d.ts.map +1 -0
  169. package/build/storage/session-lock.js +105 -0
  170. package/build/storage/session-lock.js.map +1 -0
  171. package/build/token-auth/authenticator.d.ts +70 -0
  172. package/build/token-auth/authenticator.d.ts.map +1 -0
  173. package/build/token-auth/authenticator.js +146 -0
  174. package/build/token-auth/authenticator.js.map +1 -0
  175. package/build/token-auth/cli.d.ts +38 -0
  176. package/build/token-auth/cli.d.ts.map +1 -0
  177. package/build/token-auth/cli.js +192 -0
  178. package/build/token-auth/cli.js.map +1 -0
  179. package/build/token-auth/context.d.ts +30 -0
  180. package/build/token-auth/context.d.ts.map +1 -0
  181. package/build/token-auth/context.js +32 -0
  182. package/build/token-auth/context.js.map +1 -0
  183. package/build/token-auth/http-guard.d.ts +26 -0
  184. package/build/token-auth/http-guard.d.ts.map +1 -0
  185. package/build/token-auth/http-guard.js +51 -0
  186. package/build/token-auth/http-guard.js.map +1 -0
  187. package/build/token-auth/index.d.ts +14 -0
  188. package/build/token-auth/index.d.ts.map +1 -0
  189. package/build/token-auth/index.js +14 -0
  190. package/build/token-auth/index.js.map +1 -0
  191. package/build/token-auth/limiter.d.ts +20 -0
  192. package/build/token-auth/limiter.d.ts.map +1 -0
  193. package/build/token-auth/limiter.js +41 -0
  194. package/build/token-auth/limiter.js.map +1 -0
  195. package/build/token-auth/loopback.d.ts +17 -0
  196. package/build/token-auth/loopback.d.ts.map +1 -0
  197. package/build/token-auth/loopback.js +62 -0
  198. package/build/token-auth/loopback.js.map +1 -0
  199. package/build/token-auth/py-compat.d.ts +24 -0
  200. package/build/token-auth/py-compat.d.ts.map +1 -0
  201. package/build/token-auth/py-compat.js +76 -0
  202. package/build/token-auth/py-compat.js.map +1 -0
  203. package/build/token-auth/tokens.d.ts +36 -0
  204. package/build/token-auth/tokens.d.ts.map +1 -0
  205. package/build/token-auth/tokens.js +115 -0
  206. package/build/token-auth/tokens.js.map +1 -0
  207. package/build/token-auth/users.d.ts +62 -0
  208. package/build/token-auth/users.d.ts.map +1 -0
  209. package/build/token-auth/users.js +174 -0
  210. package/build/token-auth/users.js.map +1 -0
  211. package/build/transport/cache.d.ts +19 -0
  212. package/build/transport/cache.d.ts.map +1 -0
  213. package/build/transport/cache.js +76 -0
  214. package/build/transport/cache.js.map +1 -0
  215. package/build/transport/index.d.ts +5 -0
  216. package/build/transport/index.d.ts.map +1 -0
  217. package/build/transport/index.js +4 -0
  218. package/build/transport/index.js.map +1 -0
  219. package/build/transport/local-server.d.ts +173 -0
  220. package/build/transport/local-server.d.ts.map +1 -0
  221. package/build/transport/local-server.js +457 -0
  222. package/build/transport/local-server.js.map +1 -0
  223. package/build/transport/oauth-middleware.d.ts +45 -0
  224. package/build/transport/oauth-middleware.d.ts.map +1 -0
  225. package/build/transport/oauth-middleware.js +86 -0
  226. package/build/transport/oauth-middleware.js.map +1 -0
  227. package/build/transport/streamable-http.d.ts +54 -0
  228. package/build/transport/streamable-http.d.ts.map +1 -0
  229. package/build/transport/streamable-http.js +57 -0
  230. package/build/transport/streamable-http.js.map +1 -0
  231. package/package.json +107 -4
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Cross-platform browser opening with WSL detection.
3
+ */
4
+ import { execFile } from 'node:child_process';
5
+ import { readFile } from 'node:fs/promises';
6
+ import { promisify } from 'node:util';
7
+ const execFileAsync = promisify(execFile);
8
+ // Dedupe repeated tryOpenBrowser calls for the same URL. OAuth verification
9
+ // URLs are stable so a retry loop would otherwise spawn a new tab per attempt.
10
+ // Keep a 5-minute window per URL.
11
+ const BROWSER_OPEN_DEDUPE_WINDOW_MS = 5 * 60 * 1000;
12
+ const recentBrowserOpens = new Map();
13
+ /**
14
+ * Read an on/off environment flag: set, and not `''` / `'false'` / `'0'`.
15
+ *
16
+ * One rule for all three variables the guard below reads, deliberately. `CI` has to
17
+ * follow this rule because it is a variable we READ from someone else's environment and
18
+ * `CI=false` is a real idiom for "do not apply CI behavior" (Create React App, Netlify) —
19
+ * the `ci-info` package uses the same rule. Our own two then follow it as well, for two
20
+ * reasons: a plain truthy-string check makes `MCP_NO_BROWSER=false` SUPPRESS the browser,
21
+ * which is wrong under every reading of a negative variable name (writing `=false` means
22
+ * "no, don't no-browser" — that person is asking for auto-open and would be blocked
23
+ * silently); and two rules inside one `if` is a trap for whoever reads it next.
24
+ */
25
+ function envFlag(name) {
26
+ const value = process.env[name];
27
+ return value !== undefined && value !== '' && value !== 'false' && value !== '0';
28
+ }
29
+ async function isWsl() {
30
+ try {
31
+ const version = await readFile('/proc/version', 'utf-8');
32
+ const lower = version.toLowerCase();
33
+ return lower.includes('microsoft') || lower.includes('wsl');
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ function encodePowerShellCommand(command) {
40
+ return Buffer.from(command, 'utf16le').toString('base64');
41
+ }
42
+ async function openInPowerShell(url) {
43
+ try {
44
+ const base64Url = Buffer.from(url, 'utf8').toString('base64');
45
+ const command = `$url = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${base64Url}')); Start-Process $url`;
46
+ const encodedCommand = encodePowerShellCommand(command);
47
+ await execFileAsync('powershell.exe', ['-NoProfile', '-EncodedCommand', encodedCommand]);
48
+ return true;
49
+ }
50
+ catch {
51
+ return false;
52
+ }
53
+ }
54
+ async function openInWsl(url) {
55
+ // Try powershell.exe -EncodedCommand first (safer due to Base64 encoding)
56
+ const result = await openInPowerShell(url);
57
+ if (result) {
58
+ return true;
59
+ }
60
+ // Fallback to wslview (from wslu package, commonly available)
61
+ try {
62
+ await execFileAsync('wslview', [url]);
63
+ return true;
64
+ }
65
+ catch {
66
+ /* fall through */
67
+ }
68
+ return false;
69
+ }
70
+ /**
71
+ * Try to open URL in default browser. Returns true if likely succeeded.
72
+ *
73
+ * Detection order:
74
+ * 1. win32: powershell.exe -EncodedCommand
75
+ * 2. darwin: `open` command
76
+ * 3. linux: check WSL then `xdg-open`
77
+ *
78
+ * Never throws. Returns false on failure.
79
+ */
80
+ export async function tryOpenBrowser(url) {
81
+ try {
82
+ // Env-guard: suppress auto-open in headless / CI / autonomous-test contexts so a
83
+ // relay/clean-state server never hijacks the user's real browser with /authorize?nonce
84
+ // or 127.0.0.1 tabs. Set MCP_NO_BROWSER=1 or NO_BROWSER=1 to disable it explicitly;
85
+ // `CI` counts too because every CI provider sets it (GitHub Actions: CI=true) and a
86
+ // build agent has no browser to hijack.
87
+ if (envFlag('MCP_NO_BROWSER') || envFlag('NO_BROWSER') || envFlag('CI')) {
88
+ return false;
89
+ }
90
+ // Validate URL
91
+ if (!/^https?:\/\/[a-zA-Z0-9-._~:/?#[\]@!&'*+,;=%]+$/i.test(url)) {
92
+ return false;
93
+ }
94
+ const lastOpened = recentBrowserOpens.get(url);
95
+ if (lastOpened !== undefined && Date.now() - lastOpened < BROWSER_OPEN_DEDUPE_WINDOW_MS) {
96
+ return true;
97
+ }
98
+ recentBrowserOpens.set(url, Date.now());
99
+ const platform = process.platform;
100
+ if (platform === 'win32') {
101
+ return openInPowerShell(url);
102
+ }
103
+ if (platform === 'darwin') {
104
+ await execFileAsync('open', [url]);
105
+ return true;
106
+ }
107
+ // linux
108
+ if (await isWsl()) {
109
+ const result = await openInWsl(url);
110
+ if (result)
111
+ return true;
112
+ // Fall through to xdg-open
113
+ }
114
+ await execFileAsync('xdg-open', [url]);
115
+ return true;
116
+ }
117
+ catch {
118
+ return false;
119
+ }
120
+ }
121
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/relay/browser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { RelayConfigSchema } from '../schema/types.js';
2
+ export declare function generatePassphrase(wordCount?: number): string;
3
+ export interface RelaySession {
4
+ sessionId: string;
5
+ keyPair: CryptoKeyPair;
6
+ passphrase: string;
7
+ relayUrl: string;
8
+ }
9
+ export declare function createSession(relayBaseUrl: string, serverName: string, schema: RelayConfigSchema): Promise<RelaySession>;
10
+ export declare function pollForResult(relayBaseUrl: string, session: RelaySession, intervalMs?: number, timeoutMs?: number): Promise<Record<string, string>>;
11
+ export declare function sendMessage(relayBaseUrl: string, sessionId: string, message: {
12
+ type: string;
13
+ text: string;
14
+ data?: Record<string, unknown>;
15
+ }): Promise<string>;
16
+ export declare function pollForResponses(relayBaseUrl: string, sessionId: string, messageId: string, intervalMs?: number, timeoutMs?: number): Promise<string>;
17
+ /**
18
+ * Notify the browser that relay setup is complete, then clean up the session.
19
+ *
20
+ * The browser polls ``/api/sessions/:id/messages`` every 2s and stops on the
21
+ * first ``type:'complete'`` message. If the caller deletes the session before
22
+ * that poll lands, the browser sees 404 on its next fetch and the UI stalls
23
+ * on "Waiting for server...". This helper sends the ``complete`` message,
24
+ * then schedules the DELETE after a grace period long enough to cover a few
25
+ * poll cycles, and unref's the cleanup timer so it doesn't block process
26
+ * shutdown. If the process exits before the timer fires, the relay server's
27
+ * 10-minute TTL reclaims the session.
28
+ *
29
+ * Errors from ``sendMessage`` are logged via ``console.error`` and swallowed
30
+ * — the helper is a best-effort post-setup notification and must not fail
31
+ * the caller's primary flow.
32
+ */
33
+ export declare function notifyComplete(relayBaseUrl: string, sessionId: string, text?: string, options?: {
34
+ gracePeriodMs?: number;
35
+ }): Promise<void>;
36
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/relay/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAM3D,wBAAgB,kBAAkB,CAAC,SAAS,SAAI,GAAG,MAAM,CAkBxD;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,aAAa,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,YAAY,CAAC,CAgBvB;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,YAAY,EACrB,UAAU,SAAO,EACjB,SAAS,SAAU,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAwCjC;AAED,wBAAsB,WAAW,CAC/B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACtE,OAAO,CAAC,MAAM,CAAC,CASjB;AAED,wBAAsB,gBAAgB,CACpC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,UAAU,SAAO,EACjB,SAAS,SAAU,GAClB,OAAO,CAAC,MAAM,CAAC,CA2BjB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,cAAc,CAClC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,MAA0B,EAChC,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GACvC,OAAO,CAAC,IAAI,CAAC,CAcf"}
@@ -0,0 +1,135 @@
1
+ import { decrypt } from '../crypto/aes.js';
2
+ import { deriveSharedSecret, exportPublicKey, generateKeyPair, importPublicKey } from '../crypto/ecdh.js';
3
+ import { deriveAesKey } from '../crypto/kdf.js';
4
+ import { WORDLIST } from './wordlist.js';
5
+ // Single fallback buffer for rejection sampling, reused to minimize GC
6
+ const fallbackBuffer = new Uint16Array(1);
7
+ export function generatePassphrase(wordCount = 4) {
8
+ const words = [];
9
+ const max = Math.floor(0x10000 / WORDLIST.length) * WORDLIST.length; // rejection threshold
10
+ // Optimization: Batch random generation for all words at once
11
+ const buffer = new Uint16Array(wordCount);
12
+ crypto.getRandomValues(buffer);
13
+ for (let i = 0; i < wordCount; i++) {
14
+ let index = buffer[i];
15
+ while (index >= max) {
16
+ // Reject biased values and resample
17
+ crypto.getRandomValues(fallbackBuffer);
18
+ index = fallbackBuffer[0];
19
+ }
20
+ words.push(WORDLIST[index % WORDLIST.length]);
21
+ }
22
+ return words.join('-');
23
+ }
24
+ export async function createSession(relayBaseUrl, serverName, schema) {
25
+ const sessionId = Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString('hex');
26
+ const keyPair = await generateKeyPair();
27
+ const passphrase = generatePassphrase();
28
+ const response = await fetch(`${relayBaseUrl}/api/sessions`, {
29
+ method: 'POST',
30
+ headers: { 'Content-Type': 'application/json' },
31
+ body: JSON.stringify({ sessionId, serverName, schema })
32
+ });
33
+ if (!response.ok)
34
+ throw new Error(`Relay session creation failed: ${response.status}`);
35
+ const pubKeyBase64 = await exportPublicKey(keyPair.publicKey);
36
+ const relayUrl = `${relayBaseUrl}/authorize?s=${sessionId}#k=${pubKeyBase64}&p=${encodeURIComponent(passphrase)}`;
37
+ return { sessionId, keyPair, passphrase, relayUrl };
38
+ }
39
+ export async function pollForResult(relayBaseUrl, session, intervalMs = 2000, timeoutMs = 600_000) {
40
+ const deadline = Date.now() + timeoutMs;
41
+ while (Date.now() < deadline) {
42
+ const response = await fetch(`${relayBaseUrl}/api/sessions/${session.sessionId}`);
43
+ if (response.status === 200) {
44
+ const body = await response.json();
45
+ if (body.status === 'skipped') {
46
+ // Cleanup session
47
+ await fetch(`${relayBaseUrl}/api/sessions/${session.sessionId}`, {
48
+ method: 'DELETE'
49
+ }).catch(() => { });
50
+ throw new Error('RELAY_SKIPPED');
51
+ }
52
+ const { browserPub, ciphertext, iv, tag } = body.result ?? body;
53
+ const browserKey = await importPublicKey(browserPub);
54
+ const sharedSecret = await deriveSharedSecret(session.keyPair.privateKey, browserKey);
55
+ const aesKey = await deriveAesKey(sharedSecret, session.passphrase);
56
+ const plaintext = await decrypt(aesKey, new Uint8Array(Buffer.from(ciphertext, 'base64')), new Uint8Array(Buffer.from(iv, 'base64')), new Uint8Array(Buffer.from(tag, 'base64')));
57
+ // Don't delete session here — keep alive for bidirectional messaging.
58
+ // Session auto-expires via TTL (10 min). Caller can delete manually if needed.
59
+ return JSON.parse(plaintext);
60
+ }
61
+ if (response.status === 404)
62
+ throw new Error('Session expired or not found');
63
+ if (response.status !== 202)
64
+ throw new Error(`Unexpected status: ${response.status}`);
65
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
66
+ }
67
+ throw new Error('Relay setup timed out');
68
+ }
69
+ export async function sendMessage(relayBaseUrl, sessionId, message) {
70
+ const response = await fetch(`${relayBaseUrl}/api/sessions/${sessionId}/messages`, {
71
+ method: 'POST',
72
+ headers: { 'Content-Type': 'application/json' },
73
+ body: JSON.stringify(message)
74
+ });
75
+ if (!response.ok)
76
+ throw new Error(`Failed to send message: ${response.status}`);
77
+ const body = await response.json();
78
+ return body.id;
79
+ }
80
+ export async function pollForResponses(relayBaseUrl, sessionId, messageId, intervalMs = 2000, timeoutMs = 300_000) {
81
+ const deadline = Date.now() + timeoutMs;
82
+ let lastSeenCount = 0;
83
+ while (Date.now() < deadline) {
84
+ const response = await fetch(`${relayBaseUrl}/api/sessions/${sessionId}/responses`);
85
+ if (!response.ok)
86
+ throw new Error(`Failed to poll responses: ${response.status}`);
87
+ const body = await response.json();
88
+ const responses = body.responses;
89
+ // Optimization: The responses array is append-only.
90
+ // Instead of doing O(N) array.find() scanning from the beginning,
91
+ // manually loop backwards stopping at the last seen count.
92
+ if (responses) {
93
+ for (let i = responses.length - 1; i >= lastSeenCount; i--) {
94
+ if (responses[i].messageId === messageId) {
95
+ return responses[i].value;
96
+ }
97
+ }
98
+ lastSeenCount = responses.length;
99
+ }
100
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
101
+ }
102
+ throw new Error('Timed out waiting for response');
103
+ }
104
+ /**
105
+ * Notify the browser that relay setup is complete, then clean up the session.
106
+ *
107
+ * The browser polls ``/api/sessions/:id/messages`` every 2s and stops on the
108
+ * first ``type:'complete'`` message. If the caller deletes the session before
109
+ * that poll lands, the browser sees 404 on its next fetch and the UI stalls
110
+ * on "Waiting for server...". This helper sends the ``complete`` message,
111
+ * then schedules the DELETE after a grace period long enough to cover a few
112
+ * poll cycles, and unref's the cleanup timer so it doesn't block process
113
+ * shutdown. If the process exits before the timer fires, the relay server's
114
+ * 10-minute TTL reclaims the session.
115
+ *
116
+ * Errors from ``sendMessage`` are logged via ``console.error`` and swallowed
117
+ * — the helper is a best-effort post-setup notification and must not fail
118
+ * the caller's primary flow.
119
+ */
120
+ export async function notifyComplete(relayBaseUrl, sessionId, text = 'Setup complete!', options = {}) {
121
+ try {
122
+ await sendMessage(relayBaseUrl, sessionId, { type: 'complete', text });
123
+ }
124
+ catch (err) {
125
+ const msg = err instanceof Error ? err.message : String(err);
126
+ console.error(`[hull] Failed to send relay 'complete' message: ${msg}`);
127
+ return;
128
+ }
129
+ const gracePeriodMs = options.gracePeriodMs ?? 5000;
130
+ const timer = setTimeout(() => {
131
+ fetch(`${relayBaseUrl}/api/sessions/${sessionId}`, { method: 'DELETE' }).catch(() => { });
132
+ }, gracePeriodMs);
133
+ timer.unref?.();
134
+ }
135
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/relay/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export interface ActiveFormSession {
2
+ token: string;
3
+ clientId: string;
4
+ startedAt: Date;
5
+ expiresAt: Date;
6
+ }
7
+ export declare function claimSession(clientId: string): ActiveFormSession;
8
+ export declare function releaseSession(): void;
9
+ export declare function isSessionActive(): boolean;
10
+ export declare function validateSessionToken(token: string): boolean;
11
+ export declare function getActiveSession(): ActiveFormSession | null;
12
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/relay/session.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,IAAI,CAAA;IACf,SAAS,EAAE,IAAI,CAAA;CAChB;AAQD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAYhE;AAED,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,wBAAgB,eAAe,IAAI,OAAO,CAOzC;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAW3D;AAED,wBAAgB,gBAAgB,IAAI,iBAAiB,GAAG,IAAI,CAO3D"}
@@ -0,0 +1,55 @@
1
+ import { randomBytes, timingSafeEqual } from 'node:crypto';
2
+ const SESSION_TTL_MS = 30 * 60 * 1000;
3
+ const TOKEN_BYTES = 32;
4
+ let state = null;
5
+ function now() {
6
+ return new Date();
7
+ }
8
+ export function claimSession(clientId) {
9
+ if (state !== null && state.expiresAt > now()) {
10
+ return state;
11
+ }
12
+ const startedAt = now();
13
+ state = {
14
+ token: randomBytes(TOKEN_BYTES).toString('base64url'),
15
+ clientId,
16
+ startedAt,
17
+ expiresAt: new Date(startedAt.getTime() + SESSION_TTL_MS)
18
+ };
19
+ return state;
20
+ }
21
+ export function releaseSession() {
22
+ state = null;
23
+ }
24
+ export function isSessionActive() {
25
+ if (state === null)
26
+ return false;
27
+ if (state.expiresAt <= now()) {
28
+ state = null;
29
+ return false;
30
+ }
31
+ return true;
32
+ }
33
+ export function validateSessionToken(token) {
34
+ if (state === null)
35
+ return false;
36
+ if (state.expiresAt <= now()) {
37
+ state = null;
38
+ return false;
39
+ }
40
+ const a = Buffer.from(state.token);
41
+ const b = Buffer.from(token);
42
+ const isLengthEqual = a.length === b.length;
43
+ const compareB = isLengthEqual ? b : a;
44
+ return timingSafeEqual(a, compareB) && isLengthEqual;
45
+ }
46
+ export function getActiveSession() {
47
+ if (state === null)
48
+ return null;
49
+ if (state.expiresAt <= now()) {
50
+ state = null;
51
+ return null;
52
+ }
53
+ return state;
54
+ }
55
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/relay/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE1D,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AACrC,MAAM,WAAW,GAAG,EAAE,CAAA;AAStB,IAAI,KAAK,GAA6B,IAAI,CAAA;AAE1C,SAAS,GAAG;IACV,OAAO,IAAI,IAAI,EAAE,CAAA;AACnB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,SAAS,GAAG,GAAG,EAAE,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAA;IACd,CAAC;IACD,MAAM,SAAS,GAAG,GAAG,EAAE,CAAA;IACvB,KAAK,GAAG;QACN,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;QACrD,QAAQ;QACR,SAAS;QACT,SAAS,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC;KAC1D,CAAA;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,KAAK,GAAG,IAAI,CAAA;AACd,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAChC,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,EAAE,CAAC;QAC7B,KAAK,GAAG,IAAI,CAAA;QACZ,OAAO,KAAK,CAAA;IACd,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAChC,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,EAAE,CAAC;QAC7B,KAAK,GAAG,IAAI,CAAA;QACZ,OAAO,KAAK,CAAA;IACd,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAClC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC5B,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAAA;IAC3C,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACtC,OAAO,eAAe,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,aAAa,CAAA;AACtD,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC/B,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,EAAE,CAAC;QAC7B,KAAK,GAAG,IAAI,CAAA;QACZ,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Helper to register `config__open_relay` MCP tool — HTTP-only mode.
3
+ *
4
+ * After the stdio-pure + http-multi-user split, the relay config form is
5
+ * served by the HTTP server itself at ``<PUBLIC_URL>/authorize``. The
6
+ * ``config__open_relay`` tool directs the user to that URL.
7
+ *
8
+ * When the connected client declares URL-mode elicitation
9
+ * (``capabilities.elicitation.url``, SEP-1036), the tool asks the client to
10
+ * present a consent prompt and open the URL via ``Server.elicitInput`` so the
11
+ * credential form is opened out-of-band without the LLM in the loop. Clients
12
+ * that do NOT declare the capability fall back to the legacy behaviour: the
13
+ * tool returns the URL and best-effort opens the user's default browser
14
+ * server-side. There is no daemon-bridge discovery, no respawn, and no
15
+ * session deduplication — the HTTP server's own session tracking handles
16
+ * concurrent setup attempts.
17
+ *
18
+ * In stdio mode, the relay form does not exist; the tool returns
19
+ * ``stdio_unsupported`` so plugin code can render a "switch to HTTP mode"
20
+ * message instead of misleading the user with an unreachable URL.
21
+ */
22
+ import type { ClientCapabilities, ElicitRequestURLParams, ElicitResult } from '@modelcontextprotocol/sdk/types.js';
23
+ export interface OpenRelayResult {
24
+ url: string;
25
+ browserOpened: boolean;
26
+ status: 'configured' | 'unconfigured' | 'expired' | 'session_active' | 'stdio_unsupported';
27
+ elicitation?: 'accepted' | 'declined' | 'cancelled';
28
+ }
29
+ /**
30
+ * Minimal surface required to drive URL-mode elicitation. Satisfied by the
31
+ * low-level `Server` from `@modelcontextprotocol/sdk` (`mcp.server`), which
32
+ * exposes both `getClientCapabilities` and `elicitInput`.
33
+ */
34
+ export interface ElicitationServer {
35
+ getClientCapabilities: () => ClientCapabilities | undefined;
36
+ elicitInput: (params: ElicitRequestURLParams) => Promise<ElicitResult>;
37
+ }
38
+ export interface OpenRelayHandlerOptions {
39
+ serverName: string;
40
+ publicUrl: string | null;
41
+ elicitation?: ElicitationServer;
42
+ }
43
+ /**
44
+ * Build the `open_relay` handler closure. Tests can call
45
+ * ``buildOpenRelayHandler({ serverName, publicUrl })`` directly without
46
+ * needing a real MCP server instance.
47
+ *
48
+ * When `elicitation` is supplied AND the client declared URL-mode
49
+ * elicitation, the handler drives `elicitInput` and returns the accept /
50
+ * decline / cancel outcome. Otherwise it returns the legacy dict
51
+ * (`{ url, browserOpened, status }`) unchanged.
52
+ */
53
+ export declare function buildOpenRelayHandler(options: OpenRelayHandlerOptions): () => Promise<OpenRelayResult>;
54
+ /**
55
+ * Minimal shape we require from the MCP server instance to register the
56
+ * tool. Both `@modelcontextprotocol/sdk`'s `McpServer.tool` and the
57
+ * FastMCP-style helpers used in consumer servers conform to this surface.
58
+ */
59
+ export interface ToolRegistrar {
60
+ tool: (config: {
61
+ name: string;
62
+ description?: string;
63
+ }, handler: () => Promise<OpenRelayResult>) => void;
64
+ }
65
+ /**
66
+ * Register `config__open_relay` on the given MCP server instance.
67
+ *
68
+ * Consumer servers add this single line in their tool registry after the
69
+ * other `config__*` registrations:
70
+ *
71
+ * ```ts
72
+ * import { registerOpenRelayTool } from '@n24q02m/hull'
73
+ * registerOpenRelayTool(mcp, SERVER_NAME, PUBLIC_URL, mcp.server)
74
+ * ```
75
+ *
76
+ * Pass ``null`` for ``publicUrl`` when the server is running in stdio mode;
77
+ * the tool will return ``status: 'stdio_unsupported'`` so the caller can
78
+ * surface a clear "switch to HTTP mode" message.
79
+ *
80
+ * Pass the low-level `Server` (`mcp.server`) as ``elicitation`` to enable
81
+ * URL-mode elicitation for capable clients. When omitted, the tool keeps
82
+ * the legacy browser-open behaviour for every client.
83
+ */
84
+ export declare function registerOpenRelayTool(mcp: ToolRegistrar, serverName: string, publicUrl: string | null, elicitation?: ElicitationServer): void;
85
+ //# sourceMappingURL=tool-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-helpers.d.ts","sourceRoot":"","sources":["../../src/relay/tool-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AAGlH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,OAAO,CAAA;IACtB,MAAM,EAAE,YAAY,GAAG,cAAc,GAAG,SAAS,GAAG,gBAAgB,GAAG,mBAAmB,CAAA;IAC1F,WAAW,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,WAAW,CAAA;CACpD;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,qBAAqB,EAAE,MAAM,kBAAkB,GAAG,SAAS,CAAA;IAC3D,WAAW,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAA;CACvE;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,CAAC,EAAE,iBAAiB,CAAA;CAChC;AAaD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC,CAyBtG;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,KAAK,IAAI,CAAA;CACxG;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,aAAa,EAClB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,WAAW,CAAC,EAAE,iBAAiB,GAC9B,IAAI,CAiBN"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Helper to register `config__open_relay` MCP tool — HTTP-only mode.
3
+ *
4
+ * After the stdio-pure + http-multi-user split, the relay config form is
5
+ * served by the HTTP server itself at ``<PUBLIC_URL>/authorize``. The
6
+ * ``config__open_relay`` tool directs the user to that URL.
7
+ *
8
+ * When the connected client declares URL-mode elicitation
9
+ * (``capabilities.elicitation.url``, SEP-1036), the tool asks the client to
10
+ * present a consent prompt and open the URL via ``Server.elicitInput`` so the
11
+ * credential form is opened out-of-band without the LLM in the loop. Clients
12
+ * that do NOT declare the capability fall back to the legacy behaviour: the
13
+ * tool returns the URL and best-effort opens the user's default browser
14
+ * server-side. There is no daemon-bridge discovery, no respawn, and no
15
+ * session deduplication — the HTTP server's own session tracking handles
16
+ * concurrent setup attempts.
17
+ *
18
+ * In stdio mode, the relay form does not exist; the tool returns
19
+ * ``stdio_unsupported`` so plugin code can render a "switch to HTTP mode"
20
+ * message instead of misleading the user with an unreachable URL.
21
+ */
22
+ import { randomUUID } from 'node:crypto';
23
+ import { tryOpenBrowser } from './browser.js';
24
+ // Maps the client's ElicitResult.action to the tool's outcome field.
25
+ const ELICIT_ACTION_MAP = {
26
+ accept: 'accepted',
27
+ decline: 'declined',
28
+ cancel: 'cancelled'
29
+ };
30
+ function clientSupportsUrlElicitation(server) {
31
+ return server.getClientCapabilities()?.elicitation?.url != null;
32
+ }
33
+ /**
34
+ * Build the `open_relay` handler closure. Tests can call
35
+ * ``buildOpenRelayHandler({ serverName, publicUrl })`` directly without
36
+ * needing a real MCP server instance.
37
+ *
38
+ * When `elicitation` is supplied AND the client declared URL-mode
39
+ * elicitation, the handler drives `elicitInput` and returns the accept /
40
+ * decline / cancel outcome. Otherwise it returns the legacy dict
41
+ * (`{ url, browserOpened, status }`) unchanged.
42
+ */
43
+ export function buildOpenRelayHandler(options) {
44
+ return async function openRelay() {
45
+ if (!options.publicUrl) {
46
+ return { url: '', browserOpened: false, status: 'stdio_unsupported' };
47
+ }
48
+ const url = `${options.publicUrl.replace(/\/$/, '')}/authorize`;
49
+ const { elicitation } = options;
50
+ if (elicitation && clientSupportsUrlElicitation(elicitation)) {
51
+ const message = `Open the ${options.serverName} configuration page to enter your credentials securely in your browser.`;
52
+ const result = await elicitation.elicitInput({
53
+ mode: 'url',
54
+ message,
55
+ url,
56
+ elicitationId: randomUUID()
57
+ });
58
+ return {
59
+ url,
60
+ browserOpened: false,
61
+ status: 'unconfigured',
62
+ elicitation: ELICIT_ACTION_MAP[result.action] ?? 'accepted'
63
+ };
64
+ }
65
+ const opened = await tryOpenBrowser(url);
66
+ return { url, browserOpened: !!opened, status: 'unconfigured' };
67
+ };
68
+ }
69
+ /**
70
+ * Register `config__open_relay` on the given MCP server instance.
71
+ *
72
+ * Consumer servers add this single line in their tool registry after the
73
+ * other `config__*` registrations:
74
+ *
75
+ * ```ts
76
+ * import { registerOpenRelayTool } from '@n24q02m/hull'
77
+ * registerOpenRelayTool(mcp, SERVER_NAME, PUBLIC_URL, mcp.server)
78
+ * ```
79
+ *
80
+ * Pass ``null`` for ``publicUrl`` when the server is running in stdio mode;
81
+ * the tool will return ``status: 'stdio_unsupported'`` so the caller can
82
+ * surface a clear "switch to HTTP mode" message.
83
+ *
84
+ * Pass the low-level `Server` (`mcp.server`) as ``elicitation`` to enable
85
+ * URL-mode elicitation for capable clients. When omitted, the tool keeps
86
+ * the legacy browser-open behaviour for every client.
87
+ */
88
+ export function registerOpenRelayTool(mcp, serverName, publicUrl, elicitation) {
89
+ const handler = buildOpenRelayHandler({ serverName, publicUrl, elicitation });
90
+ mcp.tool({
91
+ name: 'config__open_relay',
92
+ // Deliberately conditional. The handler returns `browserOpened: false`
93
+ // whenever no browser could be launched (headless, CI, the env-guard, no
94
+ // desktop session), and this text is read by a model that then speaks for
95
+ // the tool. "Open the ... form in the user's browser" reads as a
96
+ // guarantee, so a model can report "opened it in your browser" while the
97
+ // result says otherwise -- the user is then waiting on a tab that will
98
+ // never appear. The URL is what the tool always delivers; the browser is
99
+ // a convenience on top, and the wording says so in that order.
100
+ description: `Get the relay configuration URL for ${serverName}, opening it in the user's browser when possible.`
101
+ }, handler);
102
+ }
103
+ //# sourceMappingURL=tool-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-helpers.js","sourceRoot":"","sources":["../../src/relay/tool-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAyB7C,qEAAqE;AACrE,MAAM,iBAAiB,GAA0D;IAC/E,MAAM,EAAE,UAAU;IAClB,OAAO,EAAE,UAAU;IACnB,MAAM,EAAE,WAAW;CACpB,CAAA;AAED,SAAS,4BAA4B,CAAC,MAAyB;IAC7D,OAAO,MAAM,CAAC,qBAAqB,EAAE,EAAE,WAAW,EAAE,GAAG,IAAI,IAAI,CAAA;AACjE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgC;IACpE,OAAO,KAAK,UAAU,SAAS;QAC7B,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YACvB,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAA;QACvE,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAA;QAC/D,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAA;QAC/B,IAAI,WAAW,IAAI,4BAA4B,CAAC,WAAW,CAAC,EAAE,CAAC;YAC7D,MAAM,OAAO,GAAG,YAAY,OAAO,CAAC,UAAU,yEAAyE,CAAA;YACvH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,WAAW,CAAC;gBAC3C,IAAI,EAAE,KAAK;gBACX,OAAO;gBACP,GAAG;gBACH,aAAa,EAAE,UAAU,EAAE;aAC5B,CAAC,CAAA;YACF,OAAO;gBACL,GAAG;gBACH,aAAa,EAAE,KAAK;gBACpB,MAAM,EAAE,cAAc;gBACtB,WAAW,EAAE,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,UAAU;aAC5D,CAAA;QACH,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAA;QACxC,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA;IACjE,CAAC,CAAA;AACH,CAAC;AAWD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,qBAAqB,CACnC,GAAkB,EAClB,UAAkB,EAClB,SAAwB,EACxB,WAA+B;IAE/B,MAAM,OAAO,GAAG,qBAAqB,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAA;IAC7E,GAAG,CAAC,IAAI,CACN;QACE,IAAI,EAAE,oBAAoB;QAC1B,uEAAuE;QACvE,yEAAyE;QACzE,0EAA0E;QAC1E,iEAAiE;QACjE,yEAAyE;QACzE,uEAAuE;QACvE,yEAAyE;QACzE,+DAA+D;QAC/D,WAAW,EAAE,uCAAuC,UAAU,mDAAmD;KAClH,EACD,OAAO,CACR,CAAA;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const WORDLIST: string[];
2
+ //# sourceMappingURL=wordlist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wordlist.d.ts","sourceRoot":"","sources":["../../src/relay/wordlist.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,QAAQ,EAAE,MAAM,EAimP5B,CAAA"}