@hwip/cli 0.0.1 → 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 (245) hide show
  1. package/LICENSE +190 -0
  2. package/NOTICE +11 -0
  3. package/README.md +1979 -0
  4. package/dist/agent-list.d.ts +76 -0
  5. package/dist/agent-list.js +102 -0
  6. package/dist/agent-read.d.ts +105 -0
  7. package/dist/agent-read.js +309 -0
  8. package/dist/agent-rules-fetch.d.ts +51 -0
  9. package/dist/agent-rules-fetch.js +275 -0
  10. package/dist/agent-rules.default.d.ts +30 -0
  11. package/dist/agent-rules.default.js +292 -0
  12. package/dist/agent-session.d.ts +23 -0
  13. package/dist/agent-session.js +44 -0
  14. package/dist/agent-state.d.ts +84 -0
  15. package/dist/agent-state.js +188 -0
  16. package/dist/agent-target.d.ts +60 -0
  17. package/dist/agent-target.js +81 -0
  18. package/dist/agents.d.ts +31 -0
  19. package/dist/agents.js +75 -0
  20. package/dist/api.d.ts +28 -0
  21. package/dist/api.js +76 -0
  22. package/dist/args.d.ts +74 -0
  23. package/dist/args.js +254 -0
  24. package/dist/ask-context-attach.d.ts +73 -0
  25. package/dist/ask-context-attach.js +151 -0
  26. package/dist/ask-context-screen.d.ts +21 -0
  27. package/dist/ask-context-screen.js +49 -0
  28. package/dist/ask-context-switch.d.ts +31 -0
  29. package/dist/ask-context-switch.js +74 -0
  30. package/dist/ask-context.d.ts +84 -0
  31. package/dist/ask-context.js +172 -0
  32. package/dist/ask-pending.d.ts +47 -0
  33. package/dist/ask-pending.js +221 -0
  34. package/dist/ask-seal.d.ts +120 -0
  35. package/dist/ask-seal.js +179 -0
  36. package/dist/ask.d.ts +107 -0
  37. package/dist/ask.js +551 -0
  38. package/dist/blocked-prompt-seal.d.ts +35 -0
  39. package/dist/blocked-prompt-seal.js +89 -0
  40. package/dist/blocked-prompt.d.ts +57 -0
  41. package/dist/blocked-prompt.js +181 -0
  42. package/dist/channel.d.ts +67 -0
  43. package/dist/channel.js +136 -0
  44. package/dist/check-update.d.ts +28 -0
  45. package/dist/check-update.js +153 -0
  46. package/dist/cli.d.ts +3 -0
  47. package/dist/cli.js +813 -0
  48. package/dist/codex-config.d.ts +24 -0
  49. package/dist/codex-config.js +169 -0
  50. package/dist/codex-transcript.d.ts +59 -0
  51. package/dist/codex-transcript.js +373 -0
  52. package/dist/command-refusal.d.ts +56 -0
  53. package/dist/command-refusal.js +127 -0
  54. package/dist/command-scan.d.ts +46 -0
  55. package/dist/command-scan.js +154 -0
  56. package/dist/command-seal.d.ts +57 -0
  57. package/dist/command-seal.js +88 -0
  58. package/dist/command-seq.d.ts +30 -0
  59. package/dist/command-seq.js +150 -0
  60. package/dist/config.d.ts +156 -0
  61. package/dist/config.js +392 -0
  62. package/dist/core-sections.d.ts +18 -0
  63. package/dist/core-sections.js +28 -0
  64. package/dist/crypto.d.ts +278 -0
  65. package/dist/crypto.js +698 -0
  66. package/dist/desktop-notify.d.ts +33 -0
  67. package/dist/desktop-notify.js +49 -0
  68. package/dist/device-directory.d.ts +36 -0
  69. package/dist/device-directory.js +97 -0
  70. package/dist/device-presence.d.ts +17 -0
  71. package/dist/device-presence.js +62 -0
  72. package/dist/downloads.d.ts +29 -0
  73. package/dist/downloads.js +106 -0
  74. package/dist/errors.d.ts +12 -0
  75. package/dist/errors.js +28 -0
  76. package/dist/file-crypto.d.ts +94 -0
  77. package/dist/file-crypto.js +272 -0
  78. package/dist/file-view-budget.d.ts +15 -0
  79. package/dist/file-view-budget.js +34 -0
  80. package/dist/file-view-switch.d.ts +28 -0
  81. package/dist/file-view-switch.js +122 -0
  82. package/dist/file-view.d.ts +108 -0
  83. package/dist/file-view.js +327 -0
  84. package/dist/gate.d.ts +141 -0
  85. package/dist/gate.js +336 -0
  86. package/dist/gemini-transcript.d.ts +32 -0
  87. package/dist/gemini-transcript.js +189 -0
  88. package/dist/http-failure.d.ts +98 -0
  89. package/dist/http-failure.js +349 -0
  90. package/dist/hwip-core.generated.d.ts +7 -0
  91. package/dist/hwip-core.generated.js +14 -0
  92. package/dist/hwip-hook.d.ts +77 -0
  93. package/dist/hwip-hook.js +340 -0
  94. package/dist/index.d.ts +7 -0
  95. package/dist/index.js +14 -0
  96. package/dist/input-sequencer.d.ts +65 -0
  97. package/dist/input-sequencer.js +142 -0
  98. package/dist/insecure-url.d.ts +16 -0
  99. package/dist/insecure-url.js +76 -0
  100. package/dist/installer.d.ts +96 -0
  101. package/dist/installer.js +850 -0
  102. package/dist/inventory-offload.d.ts +49 -0
  103. package/dist/inventory-offload.js +209 -0
  104. package/dist/inventory-worker.d.ts +2 -0
  105. package/dist/inventory-worker.js +27 -0
  106. package/dist/jsonc-doc.d.ts +106 -0
  107. package/dist/jsonc-doc.js +354 -0
  108. package/dist/keep-awake.d.ts +32 -0
  109. package/dist/keep-awake.js +46 -0
  110. package/dist/key-changes.d.ts +48 -0
  111. package/dist/key-changes.js +140 -0
  112. package/dist/lan-auth.d.ts +140 -0
  113. package/dist/lan-auth.js +217 -0
  114. package/dist/lan-endpoint.d.ts +41 -0
  115. package/dist/lan-endpoint.js +115 -0
  116. package/dist/lan-receiver.d.ts +122 -0
  117. package/dist/lan-receiver.js +510 -0
  118. package/dist/lan-sender.d.ts +76 -0
  119. package/dist/lan-sender.js +184 -0
  120. package/dist/lan-transfer.d.ts +36 -0
  121. package/dist/lan-transfer.js +54 -0
  122. package/dist/listener-device-id.d.ts +20 -0
  123. package/dist/listener-device-id.js +133 -0
  124. package/dist/listener-log.d.ts +4 -0
  125. package/dist/listener-log.js +23 -0
  126. package/dist/listener-process.d.ts +200 -0
  127. package/dist/listener-process.js +470 -0
  128. package/dist/listener-service.d.ts +225 -0
  129. package/dist/listener-service.js +682 -0
  130. package/dist/listener-worktree-cleanup.d.ts +27 -0
  131. package/dist/listener-worktree-cleanup.js +62 -0
  132. package/dist/listener.d.ts +1325 -0
  133. package/dist/listener.js +5680 -0
  134. package/dist/login-address.d.ts +25 -0
  135. package/dist/login-address.js +97 -0
  136. package/dist/login-page.d.ts +29 -0
  137. package/dist/login-page.js +93 -0
  138. package/dist/login.d.ts +98 -0
  139. package/dist/login.js +388 -0
  140. package/dist/mcp-command.d.ts +45 -0
  141. package/dist/mcp-command.js +50 -0
  142. package/dist/mcp.d.ts +19 -0
  143. package/dist/mcp.js +29 -0
  144. package/dist/node-support.d.ts +19 -0
  145. package/dist/node-support.js +38 -0
  146. package/dist/notify-failure.d.ts +57 -0
  147. package/dist/notify-failure.js +150 -0
  148. package/dist/opencode-transcript.d.ts +139 -0
  149. package/dist/opencode-transcript.js +390 -0
  150. package/dist/optional-features.d.ts +6 -0
  151. package/dist/optional-features.js +19 -0
  152. package/dist/pane-fit.d.ts +40 -0
  153. package/dist/pane-fit.js +119 -0
  154. package/dist/peer-key.d.ts +33 -0
  155. package/dist/peer-key.js +34 -0
  156. package/dist/pi-transcript.d.ts +26 -0
  157. package/dist/pi-transcript.js +255 -0
  158. package/dist/presence.d.ts +14 -0
  159. package/dist/presence.js +124 -0
  160. package/dist/private-files.d.ts +51 -0
  161. package/dist/private-files.js +218 -0
  162. package/dist/project-switch.d.ts +19 -0
  163. package/dist/project-switch.js +60 -0
  164. package/dist/redact.d.ts +10 -0
  165. package/dist/redact.js +21 -0
  166. package/dist/remote-agents.d.ts +410 -0
  167. package/dist/remote-agents.js +1218 -0
  168. package/dist/remote-hook.d.ts +12 -0
  169. package/dist/remote-hook.js +196 -0
  170. package/dist/replace-file.d.ts +50 -0
  171. package/dist/replace-file.js +328 -0
  172. package/dist/schedule-command.d.ts +35 -0
  173. package/dist/schedule-command.js +260 -0
  174. package/dist/schedule-launch.d.ts +15 -0
  175. package/dist/schedule-launch.js +24 -0
  176. package/dist/schedule-ready.d.ts +44 -0
  177. package/dist/schedule-ready.js +71 -0
  178. package/dist/schedule-remote.d.ts +77 -0
  179. package/dist/schedule-remote.js +167 -0
  180. package/dist/schedule-runner.d.ts +89 -0
  181. package/dist/schedule-runner.js +452 -0
  182. package/dist/schedule.d.ts +149 -0
  183. package/dist/schedule.js +428 -0
  184. package/dist/secret-mask.d.ts +29 -0
  185. package/dist/secret-mask.js +245 -0
  186. package/dist/self-register.d.ts +45 -0
  187. package/dist/self-register.js +67 -0
  188. package/dist/send.d.ts +56 -0
  189. package/dist/send.js +104 -0
  190. package/dist/session-diff.d.ts +161 -0
  191. package/dist/session-diff.js +445 -0
  192. package/dist/session-registry.d.ts +143 -0
  193. package/dist/session-registry.js +216 -0
  194. package/dist/sessions-report.d.ts +59 -0
  195. package/dist/sessions-report.js +99 -0
  196. package/dist/shell-audit.d.ts +34 -0
  197. package/dist/shell-audit.js +47 -0
  198. package/dist/subagent-transcripts.d.ts +167 -0
  199. package/dist/subagent-transcripts.js +437 -0
  200. package/dist/subscriber-trust.d.ts +128 -0
  201. package/dist/subscriber-trust.js +397 -0
  202. package/dist/templates.d.ts +105 -0
  203. package/dist/templates.js +871 -0
  204. package/dist/tmux-install.d.ts +44 -0
  205. package/dist/tmux-install.js +65 -0
  206. package/dist/tmux-liveness.d.ts +45 -0
  207. package/dist/tmux-liveness.js +71 -0
  208. package/dist/tmux-pane.d.ts +55 -0
  209. package/dist/tmux-pane.js +126 -0
  210. package/dist/tmux-socket.d.ts +20 -0
  211. package/dist/tmux-socket.js +292 -0
  212. package/dist/todo-fold.d.ts +31 -0
  213. package/dist/todo-fold.js +102 -0
  214. package/dist/transcript-tail.d.ts +312 -0
  215. package/dist/transcript-tail.js +718 -0
  216. package/dist/trust-command.d.ts +49 -0
  217. package/dist/trust-command.js +200 -0
  218. package/dist/trust-lock.d.ts +26 -0
  219. package/dist/trust-lock.js +94 -0
  220. package/dist/trust-pins.d.ts +37 -0
  221. package/dist/trust-pins.js +82 -0
  222. package/dist/turn-recorder-wire.d.ts +70 -0
  223. package/dist/turn-recorder-wire.js +165 -0
  224. package/dist/turn-recorder.d.ts +190 -0
  225. package/dist/turn-recorder.js +695 -0
  226. package/dist/turn-ring.d.ts +93 -0
  227. package/dist/turn-ring.js +418 -0
  228. package/dist/turn-watch.d.ts +177 -0
  229. package/dist/turn-watch.js +630 -0
  230. package/dist/types.d.ts +63 -0
  231. package/dist/types.js +2 -0
  232. package/dist/uninstall.d.ts +55 -0
  233. package/dist/uninstall.js +507 -0
  234. package/dist/usage.d.ts +17 -0
  235. package/dist/usage.js +421 -0
  236. package/dist/verify.d.ts +47 -0
  237. package/dist/verify.js +396 -0
  238. package/dist/worktree.d.ts +144 -0
  239. package/dist/worktree.js +310 -0
  240. package/dist/wrapper.d.ts +254 -0
  241. package/dist/wrapper.js +704 -0
  242. package/dist/ws-url.d.ts +109 -0
  243. package/dist/ws-url.js +202 -0
  244. package/package.json +78 -10
  245. package/bin/hwip.js +0 -3
@@ -0,0 +1,25 @@
1
+ /** Hosts a callback may name without the user having said so. */
2
+ export declare const BUILT_IN_HOSTS: readonly string[];
3
+ /**
4
+ * Why `url` may not be saved as the `field` the console sent, or null. The reason names the host at most —
5
+ * never the userinfo, path or query — because it is printed.
6
+ */
7
+ export declare const callbackAddressProblem: (field: "baseUrl" | "wsUrl", url: string, extraHosts?: readonly string[]) => string | null;
8
+ /**
9
+ * The hosts `--allow-host a.example,b.example` names (C132), or why the value is no list of them. A bare host
10
+ * name: a scheme, port, path or `*` is refused rather than trimmed, and so is a name with one label — it would
11
+ * trust a whole top-level domain, and a callback host is trusted with its subdomains (a public suffix like `co.uk`
12
+ * passes the same way — the host is the user's to name). An IP address is trusted as itself only. The value
13
+ * not given is no hosts.
14
+ */
15
+ export declare const parseAllowHosts: (raw: string | boolean | undefined) => {
16
+ hosts: string[];
17
+ } | {
18
+ error: string;
19
+ };
20
+ /** The hosts besides `hwip.app` that a callback may name: the console's own, and those `config.json` already holds. */
21
+ export declare const trustedCallbackHosts: (webUrl: string, config: {
22
+ baseUrl?: unknown;
23
+ wsUrl?: unknown;
24
+ }) => string[];
25
+ //# sourceMappingURL=login-address.d.ts.map
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.trustedCallbackHosts = exports.parseAllowHosts = exports.callbackAddressProblem = exports.BUILT_IN_HOSTS = void 0;
4
+ /**
5
+ * The addresses a sign-in callback may hand to `config.json` (C131). The console sends the key together with
6
+ * the `baseUrl` and `wsUrl` it belongs to, and the config then sends the key wherever they point — so a callback
7
+ * that names an address the user never chose is refused, not saved.
8
+ *
9
+ * Allowed: https (wss) on a host under `hwip.app`, on the console's own host (a self-hosted console and its
10
+ * subdomains), on a host the config file already names, or on one the user named with `--allow-host` (C132); and this machine, over http or https, for local
11
+ * development. No userinfo, query or fragment, and a socket URL that can carry the key's query.
12
+ */
13
+ const insecure_url_js_1 = require("./insecure-url.js");
14
+ const ws_url_js_1 = require("./ws-url.js");
15
+ /** Hosts a callback may name without the user having said so. */
16
+ exports.BUILT_IN_HOSTS = ['hwip.app'];
17
+ const MAX_ADDRESS_CHARS = 2048;
18
+ const hostOf = (url) => {
19
+ if (typeof url !== 'string')
20
+ return null;
21
+ try {
22
+ return new URL(url).hostname.toLowerCase().replace(/\.$/, '');
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ };
28
+ /** `host` is `trusted` or one of its subdomains (an IP address has none). */
29
+ const IPV4 = /^\d{1,3}(\.\d{1,3}){3}$/;
30
+ const NUMERIC = /^[\d.]+$/;
31
+ const under = (host, trusted) => host === trusted || (!NUMERIC.test(trusted) && host.endsWith(`.${trusted}`));
32
+ /**
33
+ * Why `url` may not be saved as the `field` the console sent, or null. The reason names the host at most —
34
+ * never the userinfo, path or query — because it is printed.
35
+ */
36
+ const callbackAddressProblem = (field, url, extraHosts = []) => {
37
+ if (url.length > MAX_ADDRESS_CHARS)
38
+ return `the ${field} is too long`;
39
+ if (field === 'wsUrl') {
40
+ const problem = (0, ws_url_js_1.wsUrlProblem)(url);
41
+ if (problem)
42
+ return problem;
43
+ }
44
+ let parsed;
45
+ try {
46
+ parsed = new URL(url);
47
+ }
48
+ catch {
49
+ return `the ${field} is not a URL`;
50
+ }
51
+ const secure = field === 'baseUrl' ? 'https:' : 'wss:';
52
+ const plain = field === 'baseUrl' ? 'http:' : 'ws:';
53
+ const local = (0, insecure_url_js_1.isLoopbackHost)(parsed.hostname);
54
+ if (parsed.protocol !== secure && !(parsed.protocol === plain && local)) {
55
+ return `the ${field} is not ${field === 'baseUrl' ? 'https' : 'wss'} (${parsed.protocol.replace(':', '')}://${parsed.host})`;
56
+ }
57
+ if (parsed.username || parsed.password)
58
+ return `the ${field} has a user name or password in it`;
59
+ if (parsed.search || parsed.hash)
60
+ return `the ${field} has a query or fragment, which the key's address cannot carry`;
61
+ if (local)
62
+ return null;
63
+ const host = hostOf(url);
64
+ const trusted = [...exports.BUILT_IN_HOSTS, ...extraHosts].map((h) => h.toLowerCase());
65
+ if (!trusted.some((t) => under(host, t)))
66
+ return `the ${field} host ${host} is not one hwip knows (hwip.app, the console's host, one already in config.json, or one named with --allow-host)`;
67
+ return null;
68
+ };
69
+ exports.callbackAddressProblem = callbackAddressProblem;
70
+ const HOST_NAME = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/;
71
+ /**
72
+ * The hosts `--allow-host a.example,b.example` names (C132), or why the value is no list of them. A bare host
73
+ * name: a scheme, port, path or `*` is refused rather than trimmed, and so is a name with one label — it would
74
+ * trust a whole top-level domain, and a callback host is trusted with its subdomains (a public suffix like `co.uk`
75
+ * passes the same way — the host is the user's to name). An IP address is trusted as itself only. The value
76
+ * not given is no hosts.
77
+ */
78
+ const parseAllowHosts = (raw) => {
79
+ if (raw === undefined)
80
+ return { hosts: [] };
81
+ if (typeof raw !== 'string')
82
+ return { error: '--allow-host needs a host name (--allow-host api.example.com)' };
83
+ const hosts = [];
84
+ for (const entry of raw.split(',')) {
85
+ const host = entry.trim().toLowerCase().replace(/\.$/, '');
86
+ // A dotted name of digits only is an IPv4 address, whole or not at all: `0.5` is no host, and as a suffix it would match every `*.0.5`.
87
+ if (!HOST_NAME.test(host) || (NUMERIC.test(host) && !IPV4.test(host))) {
88
+ return { error: `--allow-host takes bare host names like api.example.com, comma-separated — not "${entry.trim()}"` };
89
+ }
90
+ hosts.push(host);
91
+ }
92
+ return { hosts };
93
+ };
94
+ exports.parseAllowHosts = parseAllowHosts;
95
+ /** The hosts besides `hwip.app` that a callback may name: the console's own, and those `config.json` already holds. */
96
+ const trustedCallbackHosts = (webUrl, config) => [hostOf(webUrl), hostOf(config.baseUrl), hostOf(config.wsUrl)].filter((h) => h !== null && h !== '');
97
+ exports.trustedCallbackHosts = trustedCallbackHosts;
@@ -0,0 +1,29 @@
1
+ /** design/tokens.json colours this page uses, as of hwip 011525e. `logo` is the lockup wordmark ink (kit `lockup-horizontal[-on-dark].svg`). */
2
+ export declare const PAGE_PALETTE: {
3
+ readonly light: {
4
+ readonly grouped: "#F3EEE6";
5
+ readonly card: "#FFFDF9";
6
+ readonly raised: "#F1ECE3";
7
+ readonly ink: "#1E1D1B";
8
+ readonly muted: "#6B665E";
9
+ readonly danger: "#B3261E";
10
+ readonly separator: "#E6DFD3";
11
+ readonly logo: "#1E1D1B";
12
+ };
13
+ readonly dark: {
14
+ readonly grouped: "#100F0D";
15
+ readonly card: "#221F1C";
16
+ readonly raised: "#2C2925";
17
+ readonly ink: "#F2F1EE";
18
+ readonly muted: "#A9A39A";
19
+ readonly danger: "#F2A8A0";
20
+ readonly separator: "#36322D";
21
+ readonly logo: "#E9E4DA";
22
+ };
23
+ };
24
+ type PageVariant = 'ok' | 'error';
25
+ export declare const renderPage: (variant: PageVariant, title: string, message: string, hint?: string) => string;
26
+ export declare const okPage: () => string;
27
+ export declare const errorPage: (reason: string) => string;
28
+ export {};
29
+ //# sourceMappingURL=login-page.d.ts.map
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ // The page the browser lands on after `hwip login`, served from the loopback
3
+ // callback server (login.ts). The user arrives from the web console's connect
4
+ // screen, so it speaks the console's language — warm paper background, one
5
+ // card, the logo lockup, Pretendard when installed — with the colours copied
6
+ // from hwip `design/tokens.json` (color.grouped · card · raised · ink · muted ·
7
+ // danger · separator, light and dark). The server has no network, so this is
8
+ // one self-contained document: inline styles, inline SVG, no script, no font
9
+ // or image fetch (the console loads Pretendard from a CDN; here the same
10
+ // stack falls back to the system face). It is on screen for a few seconds
11
+ // before the user returns to the terminal, so its job is to confirm state and
12
+ // point back.
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.errorPage = exports.okPage = exports.renderPage = exports.PAGE_PALETTE = void 0;
15
+ /** design/tokens.json colours this page uses, as of hwip 011525e. `logo` is the lockup wordmark ink (kit `lockup-horizontal[-on-dark].svg`). */
16
+ exports.PAGE_PALETTE = {
17
+ light: {
18
+ grouped: '#F3EEE6', card: '#FFFDF9', raised: '#F1ECE3', ink: '#1E1D1B', muted: '#6B665E',
19
+ danger: '#B3261E', separator: '#E6DFD3', logo: '#1E1D1B',
20
+ },
21
+ dark: {
22
+ grouped: '#100F0D', card: '#221F1C', raised: '#2C2925', ink: '#F2F1EE', muted: '#A9A39A',
23
+ danger: '#F2A8A0', separator: '#36322D', logo: '#E9E4DA',
24
+ },
25
+ };
26
+ const esc = (s) => s.replace(/[&<>"]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]));
27
+ const schemeVars = (p) => `--grouped:${p.grouped};--card:${p.card};--raised:${p.raised};--ink:${p.ink};--muted:${p.muted};` +
28
+ `--danger:${p.danger};--separator:${p.separator};--logo:${p.logo}`;
29
+ const PAGE_STYLE = `*,*::before,*::after{box-sizing:border-box}
30
+ :root{color-scheme:light dark;${schemeVars(exports.PAGE_PALETTE.light)};--shadow:0 3px 14px rgb(30 29 27/.05);--edge:transparent;
31
+ --font:Pretendard Variable,Pretendard,-apple-system,system-ui,"Apple SD Gothic Neo",sans-serif}
32
+ @media (prefers-color-scheme:dark){:root{${schemeVars(exports.PAGE_PALETTE.dark)};--shadow:none;--edge:var(--separator)}}
33
+ body{margin:0;background:var(--grouped);color:var(--ink);font:400 15px/1.5 var(--font)}
34
+ .center{min-height:100vh;min-height:100dvh;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:24px 16px}
35
+ .lockup{width:120px;height:auto;color:var(--logo)}
36
+ .card{width:100%;max-width:360px;display:flex;flex-direction:column;gap:12px;padding:24px;background:var(--card);
37
+ border:1px solid var(--edge);border-radius:24px;box-shadow:var(--shadow)}
38
+ .status{width:40px;height:40px;border-radius:999px;background:var(--raised);color:var(--ink);display:grid;place-items:center}
39
+ .status.error{color:var(--danger)}
40
+ .status svg{width:40px;height:40px;fill:none;stroke:currentColor;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
41
+ h1{margin:0;font-size:20px;font-weight:600;line-height:1.3;overflow-wrap:anywhere}
42
+ p{margin:0;color:var(--muted);font-size:14px;overflow-wrap:anywhere}
43
+ .chip{align-self:flex-start;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;color:var(--ink);
44
+ background:var(--raised);border-radius:12px;padding:6px 10px}`;
45
+ // Drawn in a 48×48 box inside the 40px disc: a check, or an exclamation
46
+ // (the `h.01` path renders as a dot through the round line cap).
47
+ const GLYPH = {
48
+ ok: '<path d="M14 25l7 7 13-14"/>',
49
+ error: '<path d="M24 14v13"/><path d="M24 34h.01"/>',
50
+ };
51
+ // The kit's horizontal lockup (`design/logo/kit/svg/lockup-horizontal.svg`)
52
+ // with the ink as `currentColor`, so one drawing serves light and dark; the
53
+ // persimmon dot is the kit's in both.
54
+ const LOCKUP = '<svg class="lockup" viewBox="0 0 506 256" aria-hidden="true"><g transform="translate(0 8) scale(0.94)">' +
55
+ '<g transform="translate(5.1 42.4) scale(0.917)"><g transform="translate(0 -12)">' +
56
+ '<path fill="currentColor" fill-rule="evenodd" d="M14 122 C22 104 56 94 96 92 C130 90 152 90 166 86 C170 80 178 80 184 86 C196 82 206 72 212 60 C210 44 214 26 222 12 C230 26 232 42 228 56 C238 46 248 40 254 38 C250 54 238 68 222 78 C204 100 184 120 156 134 C126 148 84 158 50 152 C32 148 16 138 14 122 Z M30 136 C50 146 74 152 100 154 C74 150 50 144 30 134 Z M38 146 C56 152 74 156 94 158 C74 154 56 150 38 144 Z"/>' +
57
+ '<path fill="currentColor" transform="matrix(0.6587 -0.1926 -0.1926 0.8913 58.93 33.26)" d="M82 144 C112 150 150 170 180 192 C194 202 204 212 214 222 C202 216 188 210 172 202 C140 188 110 172 94 160 C88 156 84 150 82 144 Z"/>' +
58
+ '<circle fill="#D9623B" cx="100" cy="56" r="17"/></g></g></g>' +
59
+ '<g transform="translate(262 60) scale(0.62)">' +
60
+ '<path d="M0 190 V30 M0 190 V136 A36 36 0 0 1 72 136 V190 M102 96 L128 190 L154 104 L180 190 L206 96 M232 96 V190 M262 96 V238 M262 143 A47 47 0 1 1 356 143 A47 47 0 1 1 262 143" fill="none" stroke="currentColor" stroke-width="13" stroke-linecap="round" stroke-linejoin="round"/>' +
61
+ '<circle cx="232" cy="56" r="9.8" fill="currentColor"/></g></svg>';
62
+ const renderPage = (variant, title, message, hint) => `<!doctype html><html lang="en"><head><meta charset="utf-8">` +
63
+ `<meta name="viewport" content="width=device-width,initial-scale=1">` +
64
+ `<title>Hwip · ${esc(title)}</title><style>${PAGE_STYLE}</style></head>` +
65
+ `<body><div class="center">${LOCKUP}<main class="card">` +
66
+ `<span class="status ${variant}" aria-hidden="true"><svg viewBox="0 0 48 48">${GLYPH[variant]}</svg></span>` +
67
+ `<h1>${esc(title)}</h1><p>${esc(message)}</p>` +
68
+ (hint ? `<code class="chip">${esc(hint)}</code>` : '') +
69
+ `</main></div></body></html>`;
70
+ exports.renderPage = renderPage;
71
+ const okPage = () => (0, exports.renderPage)('ok', "You're connected", 'Close this tab and head back to your terminal.', 'hwip is ready');
72
+ exports.okPage = okPage;
73
+ // Turn an internal callback failure reason into end-user guidance: name what
74
+ // happened and how to recover, in the interface's voice — never an apology.
75
+ const errorPage = (reason) => {
76
+ const copy = {
77
+ 'state mismatch': [
78
+ "Sign-in didn't match",
79
+ "This link didn't match the request that started it. Return to your terminal and run login again.",
80
+ ],
81
+ 'missing key': [
82
+ 'No key came back',
83
+ 'The sign-in finished without a key. Return to your terminal and run login again.',
84
+ ],
85
+ 'not found': [
86
+ 'Nothing to see here',
87
+ 'This page only handles the Hwip login callback.',
88
+ ],
89
+ };
90
+ const [title, message] = copy[reason] ?? ['Something went wrong', reason];
91
+ return (0, exports.renderPage)('error', title, message);
92
+ };
93
+ exports.errorPage = errorPage;
@@ -0,0 +1,98 @@
1
+ /** Credentials returned by a completed login. apiKey is guaranteed present
2
+ * (parseCallback rejects a missing key with 400). Shared by handleLogin and
3
+ * handleInstall via runLoginFlow. */
4
+ export type LoginFlowResult = {
5
+ apiKey: string;
6
+ hookId?: string;
7
+ baseUrl?: string;
8
+ wsUrl?: string;
9
+ };
10
+ type CallbackResult = {
11
+ ok: true;
12
+ config: LoginFlowResult;
13
+ }
14
+ /** `endsLogin`: what the terminal says when this refusal ends the login. */
15
+ | {
16
+ ok: false;
17
+ status: number;
18
+ reason: string;
19
+ endsLogin?: string;
20
+ };
21
+ export declare const stripUndefined: <T extends Record<string, unknown>>(obj: T) => Partial<T>;
22
+ /**
23
+ * `post=1` tells the console this listener takes the callback as a form POST:
24
+ * the key then travels in the request body and never in a URL (browser history,
25
+ * autofill and session restore keep URLs). `/cb` takes no key any other way.
26
+ */
27
+ export declare const buildBridgeUrl: (webUrl: string, port: number, state: string, host: string) => string;
28
+ export declare const persistConfig: (next: LoginFlowResult) => void;
29
+ /**
30
+ * The callback's fields, from a form body (the only place the key may be). The `baseUrl` and `wsUrl` it names are
31
+ * where `config.json` will send the key, so one that is not https/wss on a host hwip knows (`login-address.ts`) is
32
+ * refused whole — nothing is saved — and ends the login saying which address and why (C131).
33
+ * `extraHosts`: hosts besides `hwip.app` the callback may name (the console's, those already in the config).
34
+ */
35
+ export declare const parseCallbackParams: (params: URLSearchParams, expectedState: string, extraHosts?: readonly string[]) => CallbackResult;
36
+ /** Requests with a wrong state one login answers 403 and waits through, before it gives up. */
37
+ export declare const MAX_STATE_MISMATCHES = 5;
38
+ /**
39
+ * A GET callback. It carries no key: the console's "cancel" is one (`?error=denied&state=…`) and answers "missing
40
+ * key". A key in the query is refused unread (C102) and ends the login with `KEY_IN_URL`; its value is not in the
41
+ * result, the page or the log.
42
+ */
43
+ export declare const parseCallback: (reqUrl: string, expectedState: string) => CallbackResult;
44
+ /** A form with five short fields is well under this; the cap is for whatever else can reach a loopback port. */
45
+ export declare const MAX_CALLBACK_BODY_BYTES: number;
46
+ /**
47
+ * Where the sign-in browser can run. `open`: here. `ssh`: on the user's own
48
+ * machine, with the callback port forwarded — a browser opened here would
49
+ * land on the remote box. `none`: nowhere this login can reach.
50
+ */
51
+ export type BrowserPlan = {
52
+ kind: 'open';
53
+ } | {
54
+ kind: 'ssh';
55
+ } | {
56
+ kind: 'none';
57
+ reason: string;
58
+ };
59
+ export declare const browserPlan: (env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform) => BrowserPlan;
60
+ /**
61
+ * Opens `url`; resolves to why it could not, or undefined once the opener
62
+ * started. An opener that exits non-zero inside the grace window failed —
63
+ * `xdg-open` with no handler exits 3 — and one still running after it, or
64
+ * done with 0, handed the URL on.
65
+ */
66
+ export declare const openBrowser: (url: string, graceMs?: number) => Promise<string | undefined>;
67
+ /** A key typed while the login waited (inside install), instead of the browser's answer. */
68
+ export type PastedKey = {
69
+ pastedKey: string;
70
+ };
71
+ export declare const resolveWebUrl: (raw: string | boolean | undefined) => string;
72
+ /** The hosts `--allow-host` adds to the callback's trusted ones. A bad value stops the command — it is not read as "none". */
73
+ export declare const resolveAllowHosts: (raw: string | boolean | undefined) => string[];
74
+ export declare const resolveTimeoutSec: (raw: string | boolean | undefined) => number;
75
+ /**
76
+ * Core login flow shared by `handleLogin` (CLI command) and `handleInstall`
77
+ * (auto-trigger on missing credentials). Returns the issued credentials, or
78
+ * null when the browser can't open (headless) or the callback never arrives.
79
+ * `deps.open` is injectable for tests; defaults to the real browser launcher.
80
+ */
81
+ export declare const runLoginFlow: (
82
+ /** `pasteNext`: the caller (`hwip install`) prompts for the key when this returns null. */
83
+ opts: {
84
+ webUrl: string;
85
+ timeoutSec: number;
86
+ pasteNext?: boolean;
87
+ allowHosts?: readonly string[];
88
+ }, deps?: {
89
+ open?: (url: string) => Promise<string | undefined>;
90
+ plan?: BrowserPlan;
91
+ skip?: () => {
92
+ pressed: Promise<string>;
93
+ close: () => void;
94
+ } | null;
95
+ }) => Promise<LoginFlowResult | PastedKey | null>;
96
+ export declare const handleLogin: (args: Record<string, string | boolean>, deps?: Parameters<typeof runLoginFlow>[1]) => Promise<number>;
97
+ export {};
98
+ //# sourceMappingURL=login.d.ts.map