@ahpd/web 0.0.0-stage → 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 (176) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +204 -2
  3. package/dist/app/ahpw.svg +8 -0
  4. package/dist/app/assets/ActionList-Bawu-Lja.css +1 -0
  5. package/dist/app/assets/ActionList-DZ9lLGRe.js +1 -0
  6. package/dist/app/assets/ActivityBarItem-Cyes10aK.css +1 -0
  7. package/dist/app/assets/ActivityBarItem-DVzJQ1uh.js +1 -0
  8. package/dist/app/assets/Alert-CHZkhmDE.js +1 -0
  9. package/dist/app/assets/Alert-D6FTUHBY.css +1 -0
  10. package/dist/app/assets/Audio-C0yEXr8X.js +1 -0
  11. package/dist/app/assets/Audio-DZmBckTU.css +1 -0
  12. package/dist/app/assets/Badge-BDEW5PFb.js +1 -0
  13. package/dist/app/assets/Badge-B_GtDNjv.css +1 -0
  14. package/dist/app/assets/BarChart-29IHwYQG.css +1 -0
  15. package/dist/app/assets/BarChart-BX8Dbz7S.js +1 -0
  16. package/dist/app/assets/BarLayout-CkfGX1E0.js +1 -0
  17. package/dist/app/assets/Button-CRZ1_3KW.css +1 -0
  18. package/dist/app/assets/Button-_R1oktDz.js +1 -0
  19. package/dist/app/assets/ButtonBar-CpWwS0s7.js +1 -0
  20. package/dist/app/assets/ButtonBar-DfHeNg1i.css +1 -0
  21. package/dist/app/assets/Card-BAqJm0ZO.js +1 -0
  22. package/dist/app/assets/Card-BZxOMaUR.css +1 -0
  23. package/dist/app/assets/CheckBox-Br18O83H.css +1 -0
  24. package/dist/app/assets/CheckBox-Dkn59EbO.js +1 -0
  25. package/dist/app/assets/ChoicePicker-BX-KNSVk.css +1 -0
  26. package/dist/app/assets/ChoicePicker-Cg4Cj6B2.js +1 -0
  27. package/dist/app/assets/Column-BQyN2Mnc.js +1 -0
  28. package/dist/app/assets/Column-Df5lN6aE.css +1 -0
  29. package/dist/app/assets/Composer-C54KOyHb.js +2 -0
  30. package/dist/app/assets/ContextMenu-D3PBPRRl.js +1 -0
  31. package/dist/app/assets/DataTable-D5MTEmlc.css +1 -0
  32. package/dist/app/assets/DataTable-DsRcsDHs.js +1 -0
  33. package/dist/app/assets/DateTimeInput-Ccmu6vbY.js +1 -0
  34. package/dist/app/assets/DateTimeInput-x1XGuNl0.css +1 -0
  35. package/dist/app/assets/DetailHeader-D4ulpmi_.css +1 -0
  36. package/dist/app/assets/DetailHeader-DvDOX8aJ.js +1 -0
  37. package/dist/app/assets/DetailList-BeikDUyt.css +1 -0
  38. package/dist/app/assets/DetailList-Yalgw0YM.js +1 -0
  39. package/dist/app/assets/Details-Bgo6JIBm.js +1 -0
  40. package/dist/app/assets/DiffPage-BFnOf1p9.js +1 -0
  41. package/dist/app/assets/Divider-Bp3vn6nH.js +1 -0
  42. package/dist/app/assets/Divider-MNeVl6_n.css +1 -0
  43. package/dist/app/assets/Divisor-D4gUgDSw.css +1 -0
  44. package/dist/app/assets/Divisor-hrrJK3bw.js +1 -0
  45. package/dist/app/assets/Donut-BJmeC9Mu.js +1 -0
  46. package/dist/app/assets/Donut-BwdpL6WL.css +1 -0
  47. package/dist/app/assets/Edit-DuGIlEIV.js +1 -0
  48. package/dist/app/assets/Explorer-BkMOyBlc.js +1 -0
  49. package/dist/app/assets/Explorer-CjLkoKLM.js +1 -0
  50. package/dist/app/assets/Explorer-CwDdWpNn.js +1 -0
  51. package/dist/app/assets/Explorer-DXZ2epSi.js +1 -0
  52. package/dist/app/assets/ExplorerList-Cecr_ZQS.js +1 -0
  53. package/dist/app/assets/Field-CJtqXD7H.js +1 -0
  54. package/dist/app/assets/Field-DpS1bv_y.css +1 -0
  55. package/dist/app/assets/FieldGroup-2u9EmHRU.js +1 -0
  56. package/dist/app/assets/FieldGroup-B_DfbBby.css +1 -0
  57. package/dist/app/assets/FilePage-Bo_9WZiQ.js +1 -0
  58. package/dist/app/assets/Filter-C9Z9GcBH.js +1 -0
  59. package/dist/app/assets/FloatingLayout-8Wke63p3.js +1 -0
  60. package/dist/app/assets/Form-BKda3NBV.css +1 -0
  61. package/dist/app/assets/Form-D7L1aboH.js +1 -0
  62. package/dist/app/assets/FormActions-DK5QnUct.css +1 -0
  63. package/dist/app/assets/FormActions-DTJXRhkf.js +1 -0
  64. package/dist/app/assets/FormSection-BmWfSm92.js +1 -0
  65. package/dist/app/assets/FormSection-DSe5kU1s.css +1 -0
  66. package/dist/app/assets/Grid-BSSvEO5L.css +1 -0
  67. package/dist/app/assets/Grid-BmJ1BSH9.js +1 -0
  68. package/dist/app/assets/Icon-BFMW_Ijn.css +1 -0
  69. package/dist/app/assets/Icon-CIQ7doud.js +1 -0
  70. package/dist/app/assets/Image-dnPnniEL.css +1 -0
  71. package/dist/app/assets/Image-krGhoZYB.js +1 -0
  72. package/dist/app/assets/Info-1oWO89--.js +1 -0
  73. package/dist/app/assets/InlineLayout-Cnpt0vI0.js +1 -0
  74. package/dist/app/assets/LineChart-DnF4pqRu.css +1 -0
  75. package/dist/app/assets/LineChart-DxnWTKNr.js +1 -0
  76. package/dist/app/assets/Link-Bmovln50.css +1 -0
  77. package/dist/app/assets/Link-DbBfZpn0.js +1 -0
  78. package/dist/app/assets/List-BCNZ5z-i.css +1 -0
  79. package/dist/app/assets/List-BqbZ9K9S.js +1 -0
  80. package/dist/app/assets/LocaleToggle-BVab8iNa.css +1 -0
  81. package/dist/app/assets/LocaleToggle-CBvJEvFm.js +1 -0
  82. package/dist/app/assets/LogPage-DXOU5UzZ.js +1 -0
  83. package/dist/app/assets/LoginForm-BJhoxJyJ.css +1 -0
  84. package/dist/app/assets/LoginForm-C7ZmFVLz.js +1 -0
  85. package/dist/app/assets/Markdown-BaxWPZc1.js +4 -0
  86. package/dist/app/assets/Markdown-D4ts0B_q.css +1 -0
  87. package/dist/app/assets/MarkdownPage-8oL61A0c.js +1 -0
  88. package/dist/app/assets/Modal-BPnVnP2R.js +1 -0
  89. package/dist/app/assets/MountTitle-DXPuKAFo.js +1 -0
  90. package/dist/app/assets/New-DHihaOkV.js +3 -0
  91. package/dist/app/assets/Page-BkOqYXfd.js +1 -0
  92. package/dist/app/assets/Page-D7hXrdQj.js +2 -0
  93. package/dist/app/assets/Page-DOVek_br.js +1 -0
  94. package/dist/app/assets/Pagination-CmWBkMlC.js +1 -0
  95. package/dist/app/assets/Palette-CEQLzNFd.js +1 -0
  96. package/dist/app/assets/PortaLock-BME0nNtM.css +1 -0
  97. package/dist/app/assets/PortaLock-CC2pIbs8.js +2 -0
  98. package/dist/app/assets/RailLayout-MV_LU2y0.js +1 -0
  99. package/dist/app/assets/Row-BpA7AC-I.js +1 -0
  100. package/dist/app/assets/Row-DtfZU_4a.css +1 -0
  101. package/dist/app/assets/ScenaProvider-6A3TJumQ.js +1 -0
  102. package/dist/app/assets/SchemaForm-B_SWmx0L.css +1 -0
  103. package/dist/app/assets/SchemaForm-D6mGj6Xh.js +1 -0
  104. package/dist/app/assets/Settings-wnbeCcl1.js +1 -0
  105. package/dist/app/assets/SigillumGate-BVebugTV.js +1 -0
  106. package/dist/app/assets/SingleLayout-BNVy245Q.js +1 -0
  107. package/dist/app/assets/Slider-0-pV25N6.css +1 -0
  108. package/dist/app/assets/Slider-BW21RHZe.js +1 -0
  109. package/dist/app/assets/SpatialLayout-Bm8uhLD2.js +1 -0
  110. package/dist/app/assets/SpatialLayout-DzIS1m9Q.css +1 -0
  111. package/dist/app/assets/Spinner-B-iOhBZq.js +1 -0
  112. package/dist/app/assets/Spinner-B56wMtzv.css +1 -0
  113. package/dist/app/assets/SplitLayout-BuZpjDyP.js +1 -0
  114. package/dist/app/assets/Splitter-M7YGn5I8.js +1 -0
  115. package/dist/app/assets/StackLayout-CbP6W0Er.js +1 -0
  116. package/dist/app/assets/StatusDot-BwqhMthP.js +1 -0
  117. package/dist/app/assets/StatusDot-DzzNqTRg.css +1 -0
  118. package/dist/app/assets/TabLayout-AUuJvVjp.js +1 -0
  119. package/dist/app/assets/TabPanelLayout-BDz30u7O.js +1 -0
  120. package/dist/app/assets/Tabs-BYb2LuJK.css +1 -0
  121. package/dist/app/assets/Tabs-uqIKAux6.js +1 -0
  122. package/dist/app/assets/TerminalPage-BrP-ENHg.css +1 -0
  123. package/dist/app/assets/TerminalPage-DbF4N79y.js +18 -0
  124. package/dist/app/assets/Text-D2KlerVw.css +1 -0
  125. package/dist/app/assets/Text-MPtQvYE-.js +1 -0
  126. package/dist/app/assets/TextField-DnSHrmJM.js +1 -0
  127. package/dist/app/assets/ThemeModeToggle-B0EIvYRF.js +1 -0
  128. package/dist/app/assets/ThemeModeToggle-D_xGqGsG.css +1 -0
  129. package/dist/app/assets/ThemePicker-BxbcsOmU.css +1 -0
  130. package/dist/app/assets/ThemePicker-DHw8lqsi.js +1 -0
  131. package/dist/app/assets/Video-5WNSpY2W.js +1 -0
  132. package/dist/app/assets/Video-Dj-rxrve.css +1 -0
  133. package/dist/app/assets/_field-parts-BuPjFCuf.js +1 -0
  134. package/dist/app/assets/_field-parts-CfQcSJYh.css +1 -0
  135. package/dist/app/assets/_field-parts-DannIEUW.css +1 -0
  136. package/dist/app/assets/_utils-DZVsX11M.js +1 -0
  137. package/dist/app/assets/actions-DTKyI167.js +1 -0
  138. package/dist/app/assets/channel-BScxZDq2.js +1 -0
  139. package/dist/app/assets/colors-BieSUSt7.js +1 -0
  140. package/dist/app/assets/data-BvTv-4oH.js +2 -0
  141. package/dist/app/assets/diff-eyYjrmkY.js +3 -0
  142. package/dist/app/assets/emojis-XibTG-gy.js +1 -0
  143. package/dist/app/assets/index-Cu7ySznN.js +15 -0
  144. package/dist/app/assets/index-DgTMhxdP.css +1 -0
  145. package/dist/app/assets/jsx-runtime-BguD1OXD.js +1 -0
  146. package/dist/app/assets/link-DK7OzQXZ.js +1 -0
  147. package/dist/app/assets/mount-context-DZgEFVFG.js +1 -0
  148. package/dist/app/assets/preload-helper-BaNbYf_w.js +1 -0
  149. package/dist/app/assets/react-CybPckBe.js +1 -0
  150. package/dist/app/assets/sigillum-Bev0l9LS.js +1 -0
  151. package/dist/app/assets/styles-yrzHRgom.js +1 -0
  152. package/dist/app/assets/surface-presentation-CT86pw6J.js +1 -0
  153. package/dist/app/assets/terminal-BSweq0Uj.js +1 -0
  154. package/dist/app/assets/text-DJDdwKmv.js +1 -0
  155. package/dist/app/assets/turn-BoF7W1h6.js +3 -0
  156. package/dist/app/assets/useI18n-BTsfCYkN.js +1 -0
  157. package/dist/app/assets/useStore-BQkzWMs-.js +2 -0
  158. package/dist/app/assets/useStoreLabel-CK2AU1rh.js +1 -0
  159. package/dist/app/assets/words-BMjqrsOT.js +1 -0
  160. package/dist/app/assets/words-CrqnRMW4.js +1 -0
  161. package/dist/app/index.html +50 -0
  162. package/dist/cli/main.js +21 -0
  163. package/dist/cli/main.js.map +1 -0
  164. package/dist/cli/proxy.js +101 -0
  165. package/dist/cli/proxy.js.map +1 -0
  166. package/dist/cli/serve.js +207 -0
  167. package/dist/cli/serve.js.map +1 -0
  168. package/dist/cli/server.js +32 -0
  169. package/dist/cli/server.js.map +1 -0
  170. package/dist/plugin/index.d.ts +6 -0
  171. package/dist/plugin/index.js +12 -0
  172. package/dist/plugin/index.js.map +1 -0
  173. package/dist/plugin/static.d.ts +9 -0
  174. package/dist/plugin/static.js +77 -0
  175. package/dist/plugin/static.js.map +1 -0
  176. package/package.json +85 -4
@@ -0,0 +1,207 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { readFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { ArgumentError, ConfigurationError } from '@cofold/commands';
5
+ import { resolveConfig } from '@cofold/config';
6
+ import { createWebServer } from './server.js';
7
+ /** The port the page is served on unless one is named. */
8
+ export const DEFAULT_PORT = 5190;
9
+ /** The address bound unless one is named. */
10
+ export const DEFAULT_HOST = '127.0.0.1';
11
+ /**
12
+ * `serve`'s flags. Each is also a key in `~/.config/ahpw/config.json`, spelled
13
+ * the same without the dashes. None has a `default`, so a flag can be told
14
+ * apart from the file; the defaults are in `settingsOf`.
15
+ */
16
+ export const serveFields = {
17
+ connect: {
18
+ type: 'string',
19
+ description: 'The AHP server\'s socket, as ws://HOST:PORT or wss://HOST:PORT. A tkn in it is the token. Default AHPD_URL, then AHPD_HOST.',
20
+ cli: { value: 'URL' },
21
+ },
22
+ token: {
23
+ type: 'string',
24
+ description: 'Add this token to the socket, so the page asks for none. Default the tkn in the socket, then AHPD_TOKEN.',
25
+ cli: { value: 'SECRET' },
26
+ },
27
+ tokenFile: {
28
+ type: 'string',
29
+ description: 'Read the token to add from this file.',
30
+ cli: { value: 'PATH' },
31
+ },
32
+ host: {
33
+ type: 'string',
34
+ description: `Bind here. Default ${DEFAULT_HOST}.`,
35
+ cli: { value: 'ADDR' },
36
+ },
37
+ port: {
38
+ type: 'integer',
39
+ description: `Listen here. Default ${DEFAULT_PORT}; 0 picks a free one.`,
40
+ cli: { value: 'N' },
41
+ },
42
+ open: {
43
+ type: 'boolean',
44
+ description: 'Open the page in a browser once it is served.',
45
+ },
46
+ };
47
+ const KINDS = {
48
+ connect: 'string', token: 'string', tokenFile: 'string', host: 'string', port: 'number', open: 'boolean',
49
+ };
50
+ /** `~` at the start of a path, as a shell would read it. A config file has no shell. */
51
+ const expand = (path) => (path === '~' || path.startsWith('~/') ? homedir() + path.slice(1) : path);
52
+ /** The variables `serve` reads, below the file. */
53
+ export const ENVIRONMENT = { url: 'AHPD_URL', host: 'AHPD_HOST', token: 'AHPD_TOKEN' };
54
+ /**
55
+ * A daemon's socket from what a person wrote. `http` and `https` name the same
56
+ * port as `ws` and `wss`, and a bare `HOST:PORT` is taken as `ws://HOST:PORT`.
57
+ */
58
+ function socketOf(text, from) {
59
+ let url;
60
+ try {
61
+ url = new URL(/^[a-z][a-z0-9+.-]*:\/\//i.test(text) ? text : `ws://${text}`);
62
+ }
63
+ catch {
64
+ throw new ArgumentError(`${from} is not a URL: ${text}.`);
65
+ }
66
+ if (url.protocol === 'http:')
67
+ url.protocol = 'ws:';
68
+ else if (url.protocol === 'https:')
69
+ url.protocol = 'wss:';
70
+ if (url.protocol !== 'ws:' && url.protocol !== 'wss:')
71
+ throw new ArgumentError(`${from} takes ws://, wss://, http:// or https://, not ${url.protocol}//.`);
72
+ return url;
73
+ }
74
+ /** A layer with its socket read, and the socket's `tkn` taken out as the layer's token. */
75
+ function lifted(layer, from) {
76
+ if (typeof layer.connect !== 'string')
77
+ return layer;
78
+ const url = socketOf(layer.connect, from);
79
+ const tkn = url.searchParams.get('tkn');
80
+ if (tkn === null)
81
+ return { ...layer, connect: url };
82
+ if (layer.token !== undefined || layer.tokenFile !== undefined)
83
+ throw new ArgumentError(`${from} has a tkn, and a token is also given. Pass one.`);
84
+ url.searchParams.delete('tkn');
85
+ return { ...layer, connect: url, token: tkn };
86
+ }
87
+ /** `AHPD_URL` or else `AHPD_HOST`, with the token in its `tkn` or else `AHPD_TOKEN`. */
88
+ function environmentLayer(env) {
89
+ const named = [ENVIRONMENT.url, ENVIRONMENT.host].find((name) => (env[name] ?? '') !== '');
90
+ const layer = named === undefined ? {} : lifted({ connect: env[named] }, named);
91
+ const token = env[ENVIRONMENT.token] ?? '';
92
+ return layer.token === undefined && token !== '' ? { ...layer, token } : layer;
93
+ }
94
+ const holdsSecret = (layer) => layer.token !== undefined || layer.tokenFile !== undefined;
95
+ /**
96
+ * The flags over the file over the environment, checked, with the defaults filled in.
97
+ *
98
+ * A flag beats the file, and the file beats the environment. `token`,
99
+ * `tokenFile` and a `tkn` in the socket are one secret: two from the same place
100
+ * are refused, and the highest place that gives one replaces the others.
101
+ */
102
+ export function settingsOf(flags, file, env, read = (path) => readFileSync(path, 'utf8')) {
103
+ for (const [key, value] of Object.entries(file)) {
104
+ const kind = KINDS[key];
105
+ if (kind === undefined)
106
+ throw new ConfigurationError(`The configuration has a key serve does not take: ${key}.`);
107
+ if (typeof value !== kind || (key === 'port' && !Number.isInteger(value)))
108
+ throw new ConfigurationError(`The configuration's ${key} is not a${kind === 'number' ? 'n integer' : ` ${kind}`}.`);
109
+ }
110
+ if (flags['token'] !== undefined && flags['tokenFile'] !== undefined)
111
+ throw new ArgumentError('Pass --token or --token-file, not both.');
112
+ if (file['token'] !== undefined && file['tokenFile'] !== undefined)
113
+ throw new ConfigurationError('The configuration has token and tokenFile. Keep one.');
114
+ const layers = [environmentLayer(env), lifted(file, 'The configuration\'s connect'), lifted(flags, '--connect')];
115
+ const secretFrom = layers.findLast(holdsSecret) ?? {};
116
+ const merged = { ...layers[1], ...layers[2], connect: layers.findLast((layer) => layer.connect !== undefined)?.connect, token: secretFrom.token, tokenFile: secretFrom.tokenFile };
117
+ const connect = merged.connect;
118
+ if (!(connect instanceof URL))
119
+ throw new ArgumentError(`Pass --connect with the AHP server's socket, as ws://127.0.0.1:PORT, or set ${ENVIRONMENT.url}.`);
120
+ let held;
121
+ if (typeof merged.tokenFile === 'string') {
122
+ try {
123
+ held = read(expand(merged.tokenFile));
124
+ }
125
+ catch {
126
+ throw new ArgumentError(`No token file at ${merged.tokenFile}.`);
127
+ }
128
+ }
129
+ else if (typeof merged.token === 'string')
130
+ held = merged.token;
131
+ const token = held?.trim();
132
+ if (token === '')
133
+ throw new ArgumentError('The token is empty.');
134
+ const port = merged.port ?? DEFAULT_PORT;
135
+ if (port < 0 || port > 65_535)
136
+ throw new ArgumentError(`--port is not a port: ${port}.`);
137
+ return {
138
+ connect,
139
+ ...(token === undefined ? {} : { token }),
140
+ host: merged.host ?? DEFAULT_HOST,
141
+ port,
142
+ open: merged.open === true,
143
+ };
144
+ }
145
+ /** Whether an address is this machine only. */
146
+ const loopback = (host) => host === 'localhost' || host === '::1' || host.startsWith('127.');
147
+ /** What to say before serving, when something is reachable that a person may not expect. */
148
+ export function warningsOf(settings) {
149
+ const said = [];
150
+ if (!loopback(settings.host)) {
151
+ said.push(`ahpw: ${settings.host} is reached over plain http and ws.${settings.token === undefined ? '' : ' Anyone who reaches it uses the AHP server with the token ahpw adds.'}`);
152
+ }
153
+ if (settings.token !== undefined && settings.connect.protocol === 'ws:' && !loopback(settings.connect.hostname.replace(/^\[|\]$/g, ''))) {
154
+ said.push(`ahpw: the token travels in cleartext to ${settings.connect.host}.`);
155
+ }
156
+ return said;
157
+ }
158
+ /** The page's address as a browser would type it. */
159
+ const pageUrl = (host, port) => {
160
+ const shown = host === '0.0.0.0' || host === '::' ? '127.0.0.1' : host;
161
+ return `http://${shown.includes(':') ? `[${shown}]` : shown}:${port}/`;
162
+ };
163
+ /** Opens `url` in the desktop's browser, and says nothing when there is none. */
164
+ function openBrowser(url) {
165
+ const [command, ...args] = process.platform === 'darwin' ? ['open', url] : process.platform === 'win32' ? ['cmd', '/c', 'start', '', url] : ['xdg-open', url];
166
+ try {
167
+ const child = spawn(command, args, { detached: true, stdio: 'ignore' });
168
+ child.on('error', () => undefined);
169
+ child.unref();
170
+ }
171
+ catch {
172
+ // No browser to open: the address is printed either way.
173
+ }
174
+ }
175
+ /** `ahpw serve`: the page, and its socket carried to a daemon. Runs until stopped. */
176
+ export const declareServe = (registry, app) => registry.action({
177
+ id: 'serve',
178
+ summary: 'Serve the page, connected to an AHP server',
179
+ description: 'Serves the web UI and carries its socket to the AHP server --connect names. With a token, the page asks for none.',
180
+ surfaces: { cli: { pattern: ['serve'] } },
181
+ input: serveFields,
182
+ run: async (context) => {
183
+ const named = context.globals['config'];
184
+ const file = resolveConfig(typeof named === 'string'
185
+ ? { name: 'ahpw', path: named, user: false, environment: false }
186
+ : { name: 'ahpw' });
187
+ const settings = settingsOf(context.input, file.values, process.env);
188
+ for (const line of warningsOf(settings))
189
+ process.stderr.write(`${line}\n`);
190
+ const { server, stop } = createWebServer({ app, upstream: settings.connect, token: settings.token });
191
+ await new Promise((resolve, reject) => {
192
+ server.once('error', reject);
193
+ server.listen(settings.port, settings.host, () => resolve());
194
+ });
195
+ const bound = server.address();
196
+ const url = pageUrl(settings.host, typeof bound === 'object' && bound !== null ? bound.port : settings.port);
197
+ process.stderr.write(`ahpw: serving ${url}, connected to ${settings.connect.origin}${settings.connect.pathname === '/' ? '' : settings.connect.pathname}.\n`);
198
+ if (settings.open)
199
+ openBrowser(url);
200
+ await new Promise((resolve) => {
201
+ const end = () => void stop().then(resolve);
202
+ process.once('SIGINT', end);
203
+ process.once('SIGTERM', end);
204
+ });
205
+ },
206
+ });
207
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../cli/serve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { createServer } from 'node:http';
2
+ import { toNodeListener } from '@cofold/remote';
3
+ import { staticRoute } from '../plugin/static.js';
4
+ import { socketProxy } from './proxy.js';
5
+ /** What the page reads to learn that this server adds the token, so it asks for none. */
6
+ export const SERVER_FILE = '/ahpw.json';
7
+ /** The page at `/`, and its socket carried to the daemon. Not listening yet. */
8
+ export function createWebServer(options) {
9
+ const files = staticRoute(options.app, '/');
10
+ const about = JSON.stringify({ tokenHeld: options.token !== undefined });
11
+ const route = async (request) => {
12
+ if (new URL(request.url).pathname !== SERVER_FILE)
13
+ return await files(request);
14
+ return new Response(about, { headers: { 'content-type': 'application/json', 'cache-control': 'no-store', 'x-content-type-options': 'nosniff' } });
15
+ };
16
+ const server = createServer(toNodeListener(route));
17
+ const proxy = socketProxy(options.upstream, options.token);
18
+ const carried = new Set();
19
+ server.on('upgrade', (request, socket, head) => {
20
+ carried.add(socket);
21
+ socket.on('close', () => carried.delete(socket));
22
+ proxy(request, socket, head);
23
+ });
24
+ const stop = () => new Promise((resolve) => {
25
+ server.close(() => resolve());
26
+ server.closeAllConnections();
27
+ for (const socket of carried)
28
+ socket.destroy();
29
+ });
30
+ return { server, stop };
31
+ }
32
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../cli/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAqC,MAAM,WAAW,CAAC;AAE5E,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,yFAAyF;AACzF,MAAM,CAAC,MAAM,WAAW,GAAG,YAAY,CAAC;AAmBxC,gFAAgF;AAChF,MAAM,UAAU,eAAe,CAAC,OAAyB;IACvD,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC,CAAC;IACzE,MAAM,KAAK,GAAG,KAAK,EAAE,OAAgB,EAAqB,EAAE;QAC1D,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,WAAW;YAAE,OAAO,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/E,OAAO,IAAI,QAAQ,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,eAAe,EAAE,UAAU,EAAE,wBAAwB,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;IACpJ,CAAC,CAAC;IACF,MAAM,MAAM,GAAG,YAAY,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAwB,EAAE,MAAc,EAAE,IAAY,EAAE,EAAE;QAC9E,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QACjD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,GAAkB,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QACxD,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9B,MAAM,CAAC,mBAAmB,EAAE,CAAC;QAC7B,KAAK,MAAM,MAAM,IAAI,OAAO;YAAE,MAAM,CAAC,OAAO,EAAE,CAAC;IACjD,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Plugin } from '@ahpd/sdk';
2
+ /** The plugin's id, and the path it is served under: `/plugins/ahpd-web/`. */
3
+ export declare const name = "ahpd-web";
4
+ /** What a listing prints. */
5
+ export declare const title = "Web";
6
+ export declare const apply: Plugin['apply'];
@@ -0,0 +1,12 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { staticRoute } from './static.js';
3
+ /** The plugin's id, and the path it is served under: `/plugins/ahpd-web/`. */
4
+ export const name = 'ahpd-web';
5
+ /** What a listing prints. */
6
+ export const title = 'Web';
7
+ /** The built page, beside this file in the package. */
8
+ const APP = fileURLToPath(new URL('../app/', import.meta.url));
9
+ export const apply = (host) => {
10
+ host.registerRoute(staticRoute(APP, `/plugins/${name}/`));
11
+ };
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../plugin/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,8EAA8E;AAC9E,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAAC;AAE/B,6BAA6B;AAC7B,MAAM,CAAC,MAAM,KAAK,GAAG,KAAK,CAAC;AAE3B,uDAAuD;AACvD,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE/D,MAAM,CAAC,MAAM,KAAK,GAAoB,CAAC,IAAI,EAAE,EAAE;IAC7C,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,GAAG,EAAE,YAAY,IAAI,GAAG,CAAC,CAAC,CAAC;AAC5D,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A route that serves the files under `root`, mounted at `prefix`.
3
+ *
4
+ * `prefix` is the path the daemon serves the route at, ending in `/`. The bare
5
+ * prefix without its slash is redirected to it, because the page loads its
6
+ * assets relative to its own address. A path that leaves `root`, by `..` or an
7
+ * encoded separator, is answered 404 like a missing file.
8
+ */
9
+ export declare function staticRoute(root: string, prefix: string): (request: Request) => Promise<Response>;
@@ -0,0 +1,77 @@
1
+ import { readFile, stat } from 'node:fs/promises';
2
+ import { extname, resolve, sep } from 'node:path';
3
+ /** What a file is served as, by extension. */
4
+ const TYPES = {
5
+ '.html': 'text/html; charset=utf-8',
6
+ '.js': 'text/javascript; charset=utf-8',
7
+ '.css': 'text/css; charset=utf-8',
8
+ '.json': 'application/json',
9
+ '.svg': 'image/svg+xml',
10
+ '.png': 'image/png',
11
+ '.ico': 'image/x-icon',
12
+ '.woff2': 'font/woff2',
13
+ '.map': 'application/json',
14
+ '.txt': 'text/plain; charset=utf-8',
15
+ };
16
+ /**
17
+ * Headers on every answer.
18
+ *
19
+ * The token sits in this origin's localStorage, so the page loads nothing from
20
+ * anywhere else and cannot be framed. Inline styles stay allowed because the
21
+ * theme sets custom properties on elements.
22
+ */
23
+ const SECURITY = {
24
+ 'content-security-policy': "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'",
25
+ 'x-content-type-options': 'nosniff',
26
+ 'referrer-policy': 'no-referrer',
27
+ };
28
+ const answer = (status, body, headers = {}) => new Response(body, { status, headers: { ...SECURITY, 'content-type': 'text/plain; charset=utf-8', ...headers } });
29
+ /**
30
+ * A route that serves the files under `root`, mounted at `prefix`.
31
+ *
32
+ * `prefix` is the path the daemon serves the route at, ending in `/`. The bare
33
+ * prefix without its slash is redirected to it, because the page loads its
34
+ * assets relative to its own address. A path that leaves `root`, by `..` or an
35
+ * encoded separator, is answered 404 like a missing file.
36
+ */
37
+ export function staticRoute(root, prefix) {
38
+ const base = resolve(root);
39
+ return async (request) => {
40
+ if (request.method !== 'GET' && request.method !== 'HEAD')
41
+ return answer(405, 'Method not allowed', { allow: 'GET, HEAD' });
42
+ const url = new URL(request.url);
43
+ if (url.pathname === prefix.slice(0, -1))
44
+ return answer(308, '', { location: prefix });
45
+ if (!url.pathname.startsWith(prefix))
46
+ return answer(404, 'Not found');
47
+ let parts;
48
+ try {
49
+ parts = url.pathname.slice(prefix.length).split('/').filter((part) => part !== '').map(decodeURIComponent);
50
+ }
51
+ catch {
52
+ return answer(400, 'Bad path');
53
+ }
54
+ if (parts.some((part) => part === '.' || part === '..' || part.includes('/') || part.includes('\\') || part.includes('\0'))) {
55
+ return answer(404, 'Not found');
56
+ }
57
+ let file = resolve(base, ...parts);
58
+ if (file !== base && !file.startsWith(base + sep))
59
+ return answer(404, 'Not found');
60
+ try {
61
+ if ((await stat(file)).isDirectory())
62
+ file = resolve(file, 'index.html');
63
+ const body = await readFile(file);
64
+ const type = TYPES[extname(file).toLowerCase()] ?? 'application/octet-stream';
65
+ // Built assets carry a content hash in their name; the page itself does not.
66
+ const cache = parts[0] === 'assets' ? 'public, max-age=31536000, immutable' : 'no-cache';
67
+ return new Response(request.method === 'HEAD' ? null : body, {
68
+ status: 200,
69
+ headers: { ...SECURITY, 'content-type': type, 'cache-control': cache, 'content-length': String(body.byteLength) },
70
+ });
71
+ }
72
+ catch {
73
+ return answer(404, 'Not found');
74
+ }
75
+ };
76
+ }
77
+ //# sourceMappingURL=static.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static.js","sourceRoot":"","sources":["../../plugin/static.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,87 @@
1
1
  {
2
2
  "name": "@ahpd/web",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "packageManager": "pnpm@11.21.0",
6
+ "description": "A web UI for an AHP server, as an ahpd plugin or on its own with ahpw serve",
7
+ "keywords": [
8
+ "ahp",
9
+ "agent-host-protocol",
10
+ "ahpd",
11
+ "plugin",
12
+ "web",
13
+ "admin"
14
+ ],
15
+ "author": "Luiz Fernando Softov <lfs@softov.dev>",
16
+ "license": "MIT",
17
+ "homepage": "https://github.com/softov/ahpw#readme",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/softov/ahpw.git"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/softov/ahpw/issues"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "engines": {
29
+ "node": ">=22"
30
+ },
31
+ "bin": {
32
+ "ahpw": "./dist/cli/main.js"
33
+ },
34
+ "main": "./dist/plugin/index.js",
35
+ "types": "./dist/plugin/index.d.ts",
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/plugin/index.d.ts",
39
+ "default": "./dist/plugin/index.js"
40
+ }
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "README.md"
45
+ ],
46
+ "scripts": {
47
+ "dev": "vite",
48
+ "build": "tsc -b && vite build && tsc -p tsconfig.plugin.json && tsc -p tsconfig.cli.json && chmod +x dist/cli/main.js",
49
+ "typecheck": "tsc -b",
50
+ "test": "vitest run"
51
+ },
52
+ "peerDependencies": {
53
+ "@ahpd/sdk": ">=0.9"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "@ahpd/sdk": {
57
+ "optional": true
58
+ }
59
+ },
60
+ "devDependencies": {
61
+ "@ahpd/sdk": "^0.9.0",
62
+ "@microsoft/agent-host-protocol": "^1.0.0",
63
+ "@softov/scena": "^0.5.0",
64
+ "@types/node": "^22.10.2",
65
+ "@types/react": "^19.2.18",
66
+ "@types/react-dom": "^19.2.3",
67
+ "@vitejs/plugin-react": "^6.0.5",
68
+ "@xterm/addon-fit": "^0.11.0",
69
+ "@xterm/addon-web-links": "^0.12.0",
70
+ "@xterm/xterm": "^6.0.0",
71
+ "react": "^19.2.8",
72
+ "react-dom": "^19.2.8",
73
+ "typescript": "^5.9.3",
74
+ "vite": "^8.2.1",
75
+ "vitest": "^4.1.0"
76
+ },
77
+ "ahpd": {
78
+ "entry": "./dist/plugin/index.js",
79
+ "title": "Web"
80
+ },
81
+ "dependencies": {
82
+ "@cofold/commands": "^0.3.0",
83
+ "@cofold/config": "^0.3.1",
84
+ "@cofold/remote": "^0.5.0",
85
+ "@cofold/terminal": "^0.3.0"
86
+ }
87
+ }