dsh-zen-remote 2.2.9 → 2.3.2

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 (149) hide show
  1. package/README.md +2 -2
  2. package/README.zh-CN.md +2 -2
  3. package/docs/interface.md +1 -1
  4. package/docs/remote-access.md +20 -0
  5. package/lan-gate.mjs +11 -1
  6. package/lib/adapters/dsh-better-sidebar/index.js +6 -2
  7. package/lib/adapters/dsh-better-sidebar/index.js.map +1 -1
  8. package/lib/adapters/dsh-better-sidebar/methods.js +3 -0
  9. package/lib/adapters/dsh-better-sidebar/methods.js.map +1 -1
  10. package/lib/admin-routes.js +33 -2
  11. package/lib/admin-routes.js.map +1 -1
  12. package/lib/client.js +3293 -6
  13. package/lib/config.js +70 -0
  14. package/lib/config.js.map +1 -1
  15. package/lib/index.js +232 -2
  16. package/lib/index.js.map +1 -1
  17. package/lib/intercept.js +43 -3
  18. package/lib/intercept.js.map +1 -1
  19. package/lib/lan-gate-server.cjs +58 -3
  20. package/lib/merge-streams.js +42 -12
  21. package/lib/merge-streams.js.map +1 -1
  22. package/lib/types/adapters/dsh-better-sidebar/index.d.ts +1 -1
  23. package/lib/types/adapters/dsh-better-sidebar/index.d.ts.map +1 -1
  24. package/lib/types/adapters/dsh-better-sidebar/methods.d.ts.map +1 -1
  25. package/lib/types/admin-routes.d.ts +13 -0
  26. package/lib/types/admin-routes.d.ts.map +1 -1
  27. package/lib/types/client/index.d.ts.map +1 -1
  28. package/lib/types/client/locales.d.ts +204 -1
  29. package/lib/types/client/locales.d.ts.map +1 -1
  30. package/lib/types/client/wizard/GatewayWizard.d.ts +29 -0
  31. package/lib/types/client/wizard/GatewayWizard.d.ts.map +1 -0
  32. package/lib/types/client/wizard/WizardCard.d.ts +30 -0
  33. package/lib/types/client/wizard/WizardCard.d.ts.map +1 -0
  34. package/lib/types/client/wizard/cards/MigrateCard.d.ts +15 -0
  35. package/lib/types/client/wizard/cards/MigrateCard.d.ts.map +1 -0
  36. package/lib/types/client/wizard/cards/PortForwardCard.d.ts +15 -0
  37. package/lib/types/client/wizard/cards/PortForwardCard.d.ts.map +1 -0
  38. package/lib/types/client/wizard/cards/ProxyCard.d.ts +19 -0
  39. package/lib/types/client/wizard/cards/ProxyCard.d.ts.map +1 -0
  40. package/lib/types/client/wizard/cards/TokenCard.d.ts +20 -0
  41. package/lib/types/client/wizard/cards/TokenCard.d.ts.map +1 -0
  42. package/lib/types/client/wizard/register-wizard.d.ts +51 -0
  43. package/lib/types/client/wizard/register-wizard.d.ts.map +1 -0
  44. package/lib/types/client/wizard/wizard-css.d.ts +10 -0
  45. package/lib/types/client/wizard/wizard-css.d.ts.map +1 -0
  46. package/lib/types/client-data/qr.d.ts +20 -0
  47. package/lib/types/client-data/qr.d.ts.map +1 -0
  48. package/lib/types/client-data/settings-form.d.ts +7 -2
  49. package/lib/types/client-data/settings-form.d.ts.map +1 -1
  50. package/lib/types/client-data/wizard.d.ts +247 -0
  51. package/lib/types/client-data/wizard.d.ts.map +1 -0
  52. package/lib/types/config.d.ts +27 -0
  53. package/lib/types/config.d.ts.map +1 -1
  54. package/lib/types/index.d.ts.map +1 -1
  55. package/lib/types/intercept.d.ts.map +1 -1
  56. package/lib/types/merge-streams.d.ts.map +1 -1
  57. package/lib/types/wizard/binary-io.d.ts +111 -0
  58. package/lib/types/wizard/binary-io.d.ts.map +1 -0
  59. package/lib/types/wizard/binary.d.ts +118 -0
  60. package/lib/types/wizard/binary.d.ts.map +1 -0
  61. package/lib/types/wizard/caddy-process.d.ts +101 -0
  62. package/lib/types/wizard/caddy-process.d.ts.map +1 -0
  63. package/lib/types/wizard/caddy.d.ts +53 -0
  64. package/lib/types/wizard/caddy.d.ts.map +1 -0
  65. package/lib/types/wizard/cloudflare.d.ts +112 -0
  66. package/lib/types/wizard/cloudflare.d.ts.map +1 -0
  67. package/lib/types/wizard/cloudflared.d.ts +71 -0
  68. package/lib/types/wizard/cloudflared.d.ts.map +1 -0
  69. package/lib/types/wizard/probe.d.ts +66 -0
  70. package/lib/types/wizard/probe.d.ts.map +1 -0
  71. package/lib/types/wizard/proxy-snippets.d.ts +60 -0
  72. package/lib/types/wizard/proxy-snippets.d.ts.map +1 -0
  73. package/lib/types/wizard/routes.d.ts +152 -0
  74. package/lib/types/wizard/routes.d.ts.map +1 -0
  75. package/lib/types/wizard/source-detect.d.ts +53 -0
  76. package/lib/types/wizard/source-detect.d.ts.map +1 -0
  77. package/lib/types/wizard/status.d.ts +115 -0
  78. package/lib/types/wizard/status.d.ts.map +1 -0
  79. package/lib/types/wizard/token-store.d.ts +27 -0
  80. package/lib/types/wizard/token-store.d.ts.map +1 -0
  81. package/lib/types/wizard/tunnel-orchestrator.d.ts +90 -0
  82. package/lib/types/wizard/tunnel-orchestrator.d.ts.map +1 -0
  83. package/lib/types/wizard/verify.d.ts +97 -0
  84. package/lib/types/wizard/verify.d.ts.map +1 -0
  85. package/lib/wizard/binary-io.js +386 -0
  86. package/lib/wizard/binary-io.js.map +1 -0
  87. package/lib/wizard/binary.js +138 -0
  88. package/lib/wizard/binary.js.map +1 -0
  89. package/lib/wizard/caddy-process.js +162 -0
  90. package/lib/wizard/caddy-process.js.map +1 -0
  91. package/lib/wizard/caddy.js +80 -0
  92. package/lib/wizard/caddy.js.map +1 -0
  93. package/lib/wizard/cloudflare.js +334 -0
  94. package/lib/wizard/cloudflare.js.map +1 -0
  95. package/lib/wizard/cloudflared.js +158 -0
  96. package/lib/wizard/cloudflared.js.map +1 -0
  97. package/lib/wizard/probe.js +118 -0
  98. package/lib/wizard/probe.js.map +1 -0
  99. package/lib/wizard/proxy-snippets.js +112 -0
  100. package/lib/wizard/proxy-snippets.js.map +1 -0
  101. package/lib/wizard/routes.js +462 -0
  102. package/lib/wizard/routes.js.map +1 -0
  103. package/lib/wizard/source-detect.js +77 -0
  104. package/lib/wizard/source-detect.js.map +1 -0
  105. package/lib/wizard/status.js +94 -0
  106. package/lib/wizard/status.js.map +1 -0
  107. package/lib/wizard/token-store.js +71 -0
  108. package/lib/wizard/token-store.js.map +1 -0
  109. package/lib/wizard/tunnel-orchestrator.js +155 -0
  110. package/lib/wizard/tunnel-orchestrator.js.map +1 -0
  111. package/lib/wizard/verify.js +171 -0
  112. package/lib/wizard/verify.js.map +1 -0
  113. package/package.json +37 -37
  114. package/scripts/check-settings-form.mjs +5 -4
  115. package/scripts/check-wizard-ui.mjs +690 -0
  116. package/src/adapters/dsh-better-sidebar/index.ts +6 -2
  117. package/src/adapters/dsh-better-sidebar/methods.ts +3 -0
  118. package/src/admin-routes.ts +44 -2
  119. package/src/client/index.tsx +9 -0
  120. package/src/client/locales.ts +410 -2
  121. package/src/client/wizard/GatewayWizard.tsx +2224 -0
  122. package/src/client/wizard/WizardCard.tsx +81 -0
  123. package/src/client/wizard/cards/MigrateCard.tsx +42 -0
  124. package/src/client/wizard/cards/PortForwardCard.tsx +37 -0
  125. package/src/client/wizard/cards/ProxyCard.tsx +56 -0
  126. package/src/client/wizard/cards/TokenCard.tsx +81 -0
  127. package/src/client/wizard/register-wizard.ts +99 -0
  128. package/src/client/wizard/wizard-css.ts +948 -0
  129. package/src/client-data/qr.ts +284 -0
  130. package/src/client-data/settings-form.ts +12 -3
  131. package/src/client-data/wizard.ts +432 -0
  132. package/src/config.ts +87 -0
  133. package/src/index.ts +227 -2
  134. package/src/intercept.ts +43 -3
  135. package/src/merge-streams.ts +41 -12
  136. package/src/wizard/binary-io.ts +449 -0
  137. package/src/wizard/binary.ts +177 -0
  138. package/src/wizard/caddy-process.ts +228 -0
  139. package/src/wizard/caddy.ts +97 -0
  140. package/src/wizard/cloudflare.ts +413 -0
  141. package/src/wizard/cloudflared.ts +196 -0
  142. package/src/wizard/probe.ts +161 -0
  143. package/src/wizard/proxy-snippets.ts +128 -0
  144. package/src/wizard/routes.ts +537 -0
  145. package/src/wizard/source-detect.ts +87 -0
  146. package/src/wizard/status.ts +180 -0
  147. package/src/wizard/token-store.ts +73 -0
  148. package/src/wizard/tunnel-orchestrator.ts +197 -0
  149. package/src/wizard/verify.ts +236 -0
@@ -0,0 +1,334 @@
1
+ /**
2
+ * Cloudflare API v4 client (GW-7) — the whole tunnel path (路 C) talks to
3
+ * this module: account lookup, zone lookup, tunnel create/list, ingress
4
+ * configuration, the CNAME that points the public hostname at the tunnel,
5
+ * and the composed "is this domain on Cloudflare already" check.
6
+ *
7
+ * Transport rules the ticket pins down:
8
+ *
9
+ * - Plain `fetch`, no third-party dependency; the token rides the
10
+ * `Authorization: Bearer` header and is NEVER logged, echoed into an
11
+ * error, or written into any returned structure.
12
+ * - Every failure is a typed {@link CloudflareError} whose `code` the UI
13
+ * classifies on (`cf-auth-failed`, `cf-zone-not-found`,
14
+ * `cf-tunnel-conflict`, `cf-dns-conflict`, `cf-network`, and the
15
+ * catch-all `cf-api`). Raw response bodies never pass through — the
16
+ * message carries only the API's short error summary, truncated,
17
+ * which is what the design's 「兜底显示原始错误码」 wants.
18
+ * - The client is stateless: a factory binds the token (plus injectable
19
+ * `fetchImpl` / `baseUrl` for the mock-server tests); every method
20
+ * takes the ids it needs. The module-level `cf*` wrappers are one-call
21
+ * conveniences over the factory.
22
+ *
23
+ * Conflict classification matches the well-known numeric codes where they
24
+ * exist and falls back to the API's error text (`already exists`
25
+ * wordings), so an upstream renumber degrades to the generic envelope
26
+ * instead of a wrong verdict.
27
+ */
28
+ import { hostOf } from '../config.js';
29
+ export const CLOUDFLARE_API_BASE = 'https://api.cloudflare.com/client/v4';
30
+ /**
31
+ * One refused API call. Fields are assigned in the constructor body (the
32
+ * check scripts' strip-only type stripping rejects parameter properties).
33
+ */
34
+ export class CloudflareError extends Error {
35
+ code;
36
+ status;
37
+ /** The API's own numeric error code(s), when the envelope carried any. */
38
+ apiCodes;
39
+ constructor(code, message, status = null, apiCodes = [], options) {
40
+ super(message, options);
41
+ this.name = 'CloudflareError';
42
+ this.code = code;
43
+ this.status = status;
44
+ this.apiCodes = apiCodes;
45
+ }
46
+ }
47
+ /** Tunnel name conflict: Cloudflare rejects duplicate names (documented
48
+ * code 10405; deployments also word it in the message text). The text
49
+ * fallback names its SUBJECT — "already exists" alone matches the DNS
50
+ * wording just as well, so the two classifiers must stay scoped. */
51
+ function isTunnelConflict(apiCodes, text) {
52
+ return apiCodes.includes(10405) || (/tunnel/i.test(text) && /already exists/i.test(text));
53
+ }
54
+ /** DNS record conflict: the documented codes are 81053 / 81057. */
55
+ function isDnsConflict(apiCodes, text) {
56
+ return apiCodes.includes(81053) || apiCodes.includes(81057) || (/record/i.test(text) && /already exists/i.test(text));
57
+ }
58
+ /** The API's error summary: message strings only, joined, truncated —
59
+ * never the raw body, never the request's token. */
60
+ function summarize(errors, fallback) {
61
+ const codes = [];
62
+ const parts = [];
63
+ for (const entry of errors) {
64
+ if (entry !== null && typeof entry === 'object') {
65
+ const record = entry;
66
+ if (typeof record.code === 'number')
67
+ codes.push(record.code);
68
+ if (typeof record.message === 'string' && record.message !== '')
69
+ parts.push(record.message);
70
+ }
71
+ }
72
+ return { text: (parts.join('; ') || fallback).slice(0, 200), codes };
73
+ }
74
+ /** Tunnel names we mint: short, safe in a URL path. */
75
+ const TUNNEL_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9-]{0,62}$/;
76
+ /** A `service:` URL inside a tunnel ingress: http(s) to host[:port]. */
77
+ const SERVICE_URL_PATTERN = /^https?:\/\/[A-Za-z0-9.[\]:-]+$/;
78
+ /** Path segments are ids we received from the API or names we minted —
79
+ * encoded anyway, so a hostile value cannot change the request shape. */
80
+ const seg = (value) => encodeURIComponent(value);
81
+ /** Page-size + page ceiling for the two list endpoints: 5 pages of 50 is
82
+ * 250 accounts/tunnels — far past anything a wizard user has, and the hard
83
+ * stop means a broken pagination answer can never loop forever. */
84
+ const PAGE_SIZE = 50;
85
+ const PAGE_LIMIT = 5;
86
+ /**
87
+ * The ancestor chain of one hostname, itself first: `dsh.example.com` →
88
+ * `dsh.example.com`, `example.com`, `com`. Cloudflare zones are apex
89
+ * domains, so a wizard lookup for `dsh.example.com` must find the zone
90
+ * named `example.com` (GW-fix-2/H1) — exact-name matching walks this chain
91
+ * level by level.
92
+ */
93
+ export function ancestorHosts(domain) {
94
+ const labels = domain.split('.');
95
+ const chain = [];
96
+ for (let index = 0; index < labels.length; index++)
97
+ chain.push(labels.slice(index).join('.'));
98
+ return chain;
99
+ }
100
+ export function createCloudflareApi(options) {
101
+ const baseUrl = options.baseUrl ?? CLOUDFLARE_API_BASE;
102
+ const fetchImpl = options.fetchImpl ?? fetch;
103
+ const token = options.token;
104
+ if (typeof token !== 'string' || token === '') {
105
+ throw new CloudflareError('cf-auth-failed', 'no Cloudflare API token');
106
+ }
107
+ /** One authenticated v4 call: Bearer header, envelope unwrapping, typed
108
+ * failures. The token never appears in any error path. Returns the
109
+ * result PLUS the pagination envelope (the list endpoints page). */
110
+ const call = async (method, path, body) => {
111
+ let response;
112
+ try {
113
+ response = await fetchImpl(`${baseUrl}${path}`, {
114
+ method,
115
+ headers: {
116
+ Authorization: `Bearer ${token}`,
117
+ ...(body === undefined ? {} : { 'Content-Type': 'application/json' }),
118
+ },
119
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
120
+ signal: AbortSignal.timeout(15_000),
121
+ });
122
+ }
123
+ catch (cause) {
124
+ throw new CloudflareError('cf-network', `the Cloudflare API call failed before an answer arrived (${path})`, null, [], { cause });
125
+ }
126
+ let envelope;
127
+ try {
128
+ envelope = await response.json();
129
+ }
130
+ catch {
131
+ throw new CloudflareError('cf-network', `the Cloudflare API answered unparsable JSON (${path})`, response.status);
132
+ }
133
+ const errors = Array.isArray(envelope?.errors) ? envelope.errors : [];
134
+ const summary = summarize(errors, `HTTP ${response.status}`);
135
+ if (response.status === 401 || response.status === 403) {
136
+ throw new CloudflareError('cf-auth-failed', summary.text, response.status, summary.codes);
137
+ }
138
+ if (envelope?.success !== true) {
139
+ let code = 'cf-api';
140
+ if (isTunnelConflict(summary.codes, summary.text))
141
+ code = 'cf-tunnel-conflict';
142
+ else if (isDnsConflict(summary.codes, summary.text))
143
+ code = 'cf-dns-conflict';
144
+ throw new CloudflareError(code, summary.text, response.status, summary.codes);
145
+ }
146
+ // A missing or junk total_pages reads as a single page — old mocks and
147
+ // tolerant callers keep working, pagination simply never continues.
148
+ const totalPages = typeof envelope.result_info?.total_pages === 'number' && envelope.result_info.total_pages >= 1
149
+ ? envelope.result_info.total_pages
150
+ : 1;
151
+ return { result: envelope.result, totalPages };
152
+ };
153
+ const requireString = (value, what) => {
154
+ if (typeof value !== 'string' || value === '')
155
+ throw new CloudflareError('cf-api', `the Cloudflare API answered without ${what}`);
156
+ return value;
157
+ };
158
+ const requireId = (value, what) => {
159
+ if (typeof value !== 'string' || value === '')
160
+ throw new CloudflareError('cf-api', `${what} is required`);
161
+ return value;
162
+ };
163
+ return {
164
+ async listAccounts() {
165
+ const accounts = [];
166
+ for (let page = 1; page <= PAGE_LIMIT; page++) {
167
+ const { result, totalPages } = await call('GET', `/accounts?per_page=${PAGE_SIZE}&page=${page}`);
168
+ if (!Array.isArray(result))
169
+ throw new CloudflareError('cf-api', 'the accounts answer had no list');
170
+ accounts.push(...result.map((account) => requireString(account?.id, 'an account id')));
171
+ if (page >= totalPages || result.length === 0)
172
+ break;
173
+ }
174
+ return accounts;
175
+ },
176
+ async getZone(accountId, domain) {
177
+ const host = hostOf(domain);
178
+ if (host === undefined)
179
+ throw new CloudflareError('cf-api', 'the domain is not a bare hostname');
180
+ const id = requireId(accountId, 'the account id');
181
+ const { result } = await call('GET', `/zones?name=${seg(host)}&account.id=${seg(id)}`);
182
+ // Exact-match on the zone NAME client-side: the query filter is
183
+ // advisory, the verdict is ours.
184
+ const zones = Array.isArray(result) ? result : [];
185
+ const zone = zones.find((candidate) => candidate?.name === host);
186
+ if (zone === undefined)
187
+ throw new CloudflareError('cf-zone-not-found', `no Cloudflare zone named ${host}`);
188
+ return requireString(zone.id, 'a zone id');
189
+ },
190
+ async createTunnel(accountId, name) {
191
+ const id = requireId(accountId, 'the account id');
192
+ if (typeof name !== 'string' || !TUNNEL_NAME_PATTERN.test(name)) {
193
+ throw new CloudflareError('cf-api', 'the tunnel name is not a safe label');
194
+ }
195
+ // config_src cloudflare makes the create response carry the connector
196
+ // token directly — the second round-trip (GET .../token) is spared.
197
+ const { result } = await call('POST', `/accounts/${seg(id)}/cfd_tunnel`, { name, config_src: 'cloudflare' });
198
+ const record = result;
199
+ return { id: requireString(record?.id, 'a tunnel id'), token: requireString(record?.token, 'a tunnel token') };
200
+ },
201
+ async getTunnelToken(accountId, tunnelId) {
202
+ const id = requireId(accountId, 'the account id');
203
+ const tunnel = requireId(tunnelId, 'the tunnel id');
204
+ const { result } = await call('GET', `/accounts/${seg(id)}/cfd_tunnel/${seg(tunnel)}/token`);
205
+ const tokenValue = typeof result === 'string'
206
+ ? result
207
+ : result?.token;
208
+ return requireString(tokenValue, 'a tunnel token');
209
+ },
210
+ async listTunnels(accountId) {
211
+ const id = requireId(accountId, 'the account id');
212
+ const tunnels = [];
213
+ for (let page = 1; page <= PAGE_LIMIT; page++) {
214
+ const { result, totalPages } = await call('GET', `/accounts/${seg(id)}/cfd_tunnel?is_deleted=false&per_page=${PAGE_SIZE}&page=${page}`);
215
+ if (!Array.isArray(result))
216
+ throw new CloudflareError('cf-api', 'the tunnels answer had no list');
217
+ tunnels.push(...result.map((tunnel) => {
218
+ const record = tunnel;
219
+ return {
220
+ id: requireString(record?.id, 'a tunnel id'),
221
+ name: typeof record?.name === 'string' ? record.name : '',
222
+ status: typeof record?.status === 'string' ? record.status : '',
223
+ };
224
+ }));
225
+ if (page >= totalPages || result.length === 0)
226
+ break;
227
+ }
228
+ return tunnels;
229
+ },
230
+ async configureIngress(accountId, tunnelId, domain, serviceUrl) {
231
+ const accountIdSafe = requireId(accountId, 'the account id');
232
+ const tunnelIdSafe = requireId(tunnelId, 'the tunnel id');
233
+ const host = hostOf(domain);
234
+ if (host === undefined)
235
+ throw new CloudflareError('cf-api', 'the domain is not a bare hostname');
236
+ if (typeof serviceUrl !== 'string' || !SERVICE_URL_PATTERN.test(serviceUrl)) {
237
+ throw new CloudflareError('cf-api', 'the service URL is not a plain http(s) origin');
238
+ }
239
+ await call('PUT', `/accounts/${seg(accountIdSafe)}/cfd_tunnel/${seg(tunnelIdSafe)}/configurations`, {
240
+ config: {
241
+ ingress: [
242
+ { hostname: host, service: serviceUrl },
243
+ { service: 'http_status:404' },
244
+ ],
245
+ },
246
+ });
247
+ },
248
+ async listDnsRecords(zoneId, name) {
249
+ const zone = requireId(zoneId, 'the zone id');
250
+ const recordName = hostOf(name);
251
+ if (recordName === undefined)
252
+ throw new CloudflareError('cf-api', 'the record name is not a bare hostname');
253
+ const { result } = await call('GET', `/zones/${seg(zone)}/dns_records?type=CNAME&name=${seg(recordName)}&per_page=${50}&page=${1}`);
254
+ if (!Array.isArray(result))
255
+ throw new CloudflareError('cf-api', 'the DNS records answer had no list');
256
+ return result.map((record) => {
257
+ const entry = record;
258
+ return {
259
+ id: requireString(entry?.id, 'a DNS record id'),
260
+ name: typeof entry?.name === 'string' ? entry.name : '',
261
+ content: typeof entry?.content === 'string' ? entry.content : '',
262
+ };
263
+ });
264
+ },
265
+ async createDnsRecord(zoneId, subdomain, target) {
266
+ const zone = requireId(zoneId, 'the zone id');
267
+ const name = hostOf(subdomain);
268
+ if (name === undefined)
269
+ throw new CloudflareError('cf-api', 'the record name is not a bare hostname');
270
+ if (typeof target !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9-]{0,62}\.cfargotunnel\.com$/.test(target)) {
271
+ throw new CloudflareError('cf-api', 'the CNAME target is not a tunnel hostname');
272
+ }
273
+ const { result } = await call('POST', `/zones/${seg(zone)}/dns_records`, {
274
+ type: 'CNAME',
275
+ name,
276
+ content: target,
277
+ proxied: true,
278
+ ttl: 1,
279
+ });
280
+ return requireString(result?.id, 'a DNS record id');
281
+ },
282
+ async checkDomainOnCloudflare(domain) {
283
+ const host = hostOf(domain);
284
+ if (host === undefined)
285
+ throw new CloudflareError('cf-api', 'the domain is not a bare hostname');
286
+ // Zones are APEX domains: the user's `dsh.example.com` lives in the
287
+ // zone named `example.com`, so the exact-name lookup walks the
288
+ // ancestor chain (GW-fix-2/H1). First hit wins.
289
+ const chain = ancestorHosts(host);
290
+ for (const accountId of await this.listAccounts()) {
291
+ for (const candidate of chain) {
292
+ try {
293
+ const zoneId = await this.getZone(accountId, candidate);
294
+ return { onCloudflare: true, zoneId, accountId };
295
+ }
296
+ catch (error) {
297
+ if (error instanceof CloudflareError && error.code === 'cf-zone-not-found')
298
+ continue;
299
+ throw error;
300
+ }
301
+ }
302
+ }
303
+ return { onCloudflare: false, zoneId: null, accountId: null };
304
+ },
305
+ };
306
+ }
307
+ export function cfListAccounts(token, opts) {
308
+ return createCloudflareApi({ token, ...opts }).listAccounts();
309
+ }
310
+ export function cfGetZone(token, accountId, domain, opts) {
311
+ return createCloudflareApi({ token, ...opts }).getZone(accountId, domain);
312
+ }
313
+ export function cfCreateTunnel(token, accountId, name, opts) {
314
+ return createCloudflareApi({ token, ...opts }).createTunnel(accountId, name);
315
+ }
316
+ export function cfListTunnels(token, accountId, opts) {
317
+ return createCloudflareApi({ token, ...opts }).listTunnels(accountId);
318
+ }
319
+ export function cfGetTunnelToken(token, accountId, tunnelId, opts) {
320
+ return createCloudflareApi({ token, ...opts }).getTunnelToken(accountId, tunnelId);
321
+ }
322
+ export function cfConfigureIngress(token, accountId, tunnelId, domain, serviceUrl, opts) {
323
+ return createCloudflareApi({ token, ...opts }).configureIngress(accountId, tunnelId, domain, serviceUrl);
324
+ }
325
+ export function cfCreateDnsRecord(token, zoneId, subdomain, target, opts) {
326
+ return createCloudflareApi({ token, ...opts }).createDnsRecord(zoneId, subdomain, target);
327
+ }
328
+ export function cfListDnsRecords(token, zoneId, name, opts) {
329
+ return createCloudflareApi({ token, ...opts }).listDnsRecords(zoneId, name);
330
+ }
331
+ export function cfCheckDomainOnCloudflare(token, domain, opts) {
332
+ return createCloudflareApi({ token, ...opts }).checkDomainOnCloudflare(domain);
333
+ }
334
+ //# sourceMappingURL=cloudflare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../src/wizard/cloudflare.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * cloudflared process lifecycle (GW-7, 路 C's last mile). The tunnel's
3
+ * connector runs in the foreground (`tunnel run`) as a child of the DSH
4
+ * host, managed through the same subprocess service as the gateway child
5
+ * and the managed Caddy.
6
+ *
7
+ * The connector secret NEVER touches argv (visible to every process on
8
+ * the machine via ps) and is never an environment variable either — it is
9
+ * written to `<DSH_HOME>/zen-remote-ingress/tunnel-token`, mode 0600,
10
+ * through a temp-file-then-rename so a crash mid-write can never leave a
11
+ * half token at the real path, and handed to cloudflared with
12
+ * `--token-file`.
13
+ *
14
+ * Idempotency and cleanup mirror caddy-process.ts: a live child refuses a
15
+ * second start (stop first to switch tunnels), stop is idempotent, and an
16
+ * exited child clears its own slot. The host wiring hands stop to
17
+ * ctx.effect so the row's disposal kills cloudflared too.
18
+ */
19
+ import { mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
20
+ import { join } from 'node:path';
21
+ import { ensureBinary, BinaryDownloadError } from './binary-io.js';
22
+ import { ingressDir } from './binary.js';
23
+ /** Where the connector secret lives (design.md: 不进 Config 的字段). */
24
+ export function tunnelTokenPath(dshHome) {
25
+ return join(ingressDir(dshHome), 'tunnel-token');
26
+ }
27
+ /** Whether a non-empty tunnel-token file exists (the projection's boolean
28
+ * only — GW-fix-4/M1: the manager's EXISTENCE says nothing about whether a
29
+ * token has been stored). */
30
+ export function tunnelTokenConfigured(dshHome) {
31
+ try {
32
+ return statSync(tunnelTokenPath(dshHome)).isFile() && readFileSync(tunnelTokenPath(dshHome), 'utf8').trim() !== '';
33
+ }
34
+ catch {
35
+ return false;
36
+ }
37
+ }
38
+ /**
39
+ * Persist the tunnel connector secret: 0600, atomic (write the `.part`
40
+ * sibling, then rename over the real path).
41
+ */
42
+ export function writeTunnelTokenFile(dshHome, token) {
43
+ const finalPath = tunnelTokenPath(dshHome);
44
+ const partPath = `${finalPath}.part`;
45
+ try {
46
+ mkdirSync(ingressDir(dshHome), { recursive: true });
47
+ writeFileSync(partPath, token, { mode: 0o600 });
48
+ try {
49
+ renameSync(partPath, finalPath);
50
+ }
51
+ catch {
52
+ writeFileSync(finalPath, token, { mode: 0o600 });
53
+ rmSync(partPath, { force: true });
54
+ }
55
+ }
56
+ catch (error) {
57
+ rmSync(partPath, { force: true });
58
+ throw error;
59
+ }
60
+ return finalPath;
61
+ }
62
+ export function createCloudflaredManager(options) {
63
+ const { subprocess, dshHome } = options;
64
+ const maxLogBytes = options.maxLogBytes ?? 64 * 1024;
65
+ let handle = null;
66
+ let startedOnce = false;
67
+ // GW-fix-2/L3: whitelist env — the host process carries the relay secret
68
+ // and DSH's own credentials, and cloudflared needs none of them (its
69
+ // connector secret comes through the 0600 token file). Include Windows
70
+ // system/profile variables so Go's TLS root-cert store and winsock
71
+ // initialize cleanly on Windows.
72
+ const childEnv = () => {
73
+ const env = {};
74
+ for (const key of [
75
+ 'PATH', 'HOME', 'TMPDIR', 'LANG', 'TZ',
76
+ 'SystemRoot', 'SYSTEMROOT', 'windir', 'WINDIR',
77
+ 'USERPROFILE', 'APPDATA', 'LOCALAPPDATA', 'PROGRAMDATA', 'TEMP', 'TMP',
78
+ ]) {
79
+ if (process.env[key] !== undefined)
80
+ env[key] = process.env[key];
81
+ }
82
+ return env;
83
+ };
84
+ const spawnChild = (argv) => subprocess.spawn({
85
+ argv,
86
+ cwd: dshHome,
87
+ // GW-fix-2/L3: whitelist env — the token rides the 0600 file, so the
88
+ // child needs nothing from the host's environment beyond the basics.
89
+ env: childEnv(),
90
+ stdio: {
91
+ stdin: 'ignore',
92
+ stdout: { maxBytes: maxLogBytes },
93
+ stderr: { maxBytes: maxLogBytes },
94
+ },
95
+ graceMs: 3000,
96
+ });
97
+ const clearSlot = (child) => {
98
+ if (handle === child)
99
+ handle = null;
100
+ };
101
+ return {
102
+ async start(input) {
103
+ if (handle !== null)
104
+ return { ok: false, error: 'already-running' };
105
+ const tunnelToken = typeof input.tunnelToken === 'string' && input.tunnelToken.trim() !== '' ? input.tunnelToken : undefined;
106
+ if (tunnelToken === undefined)
107
+ return { ok: false, error: 'missing-token' };
108
+ let binaryPath;
109
+ try {
110
+ binaryPath = (await ensureBinary(dshHome, 'cloudflared', {
111
+ fetchImpl: options.fetchImpl,
112
+ platform: options.platform,
113
+ onProgress: options.onProgress,
114
+ })).path;
115
+ }
116
+ catch (error) {
117
+ if (error instanceof BinaryDownloadError) {
118
+ return { ok: false, error: 'download-failed', detail: error.message, installUrl: error.installUrl };
119
+ }
120
+ return { ok: false, error: 'download-failed', detail: String(error?.message ?? error) };
121
+ }
122
+ let tokenFile;
123
+ try {
124
+ tokenFile = writeTunnelTokenFile(dshHome, tunnelToken);
125
+ }
126
+ catch (error) {
127
+ return { ok: false, error: 'spawn-failed', detail: String(error?.message ?? error) };
128
+ }
129
+ try {
130
+ const child = spawnChild([binaryPath, 'tunnel', 'run', '--token-file', tokenFile]);
131
+ handle = child;
132
+ startedOnce = true;
133
+ void child.done.then(() => clearSlot(child)).catch(() => clearSlot(child));
134
+ return { ok: true, binaryPath, tokenFile, alreadyRunning: false };
135
+ }
136
+ catch (error) {
137
+ handle = null;
138
+ return { ok: false, error: 'spawn-failed', detail: String(error?.message ?? error) };
139
+ }
140
+ },
141
+ stop() {
142
+ if (handle === null)
143
+ return;
144
+ const child = handle;
145
+ handle = null;
146
+ try {
147
+ void child.terminate();
148
+ }
149
+ catch {
150
+ // A dead child's terminate is a no-op or a throw — either way done.
151
+ }
152
+ },
153
+ isRunning: () => handle !== null,
154
+ /** Whether any start has succeeded in this manager's lifetime. */
155
+ hasStarted: () => startedOnce,
156
+ };
157
+ }
158
+ //# sourceMappingURL=cloudflared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflared.js","sourceRoot":"","sources":["../../src/wizard/cloudflared.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC9F,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAyB,MAAM,gBAAgB,CAAA;AACzF,OAAO,EAAE,UAAU,EAAuB,MAAM,aAAa,CAAA;AAG7D,mEAAmE;AACnE,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC,CAAA;AAClD,CAAC;AAED;;6BAE6B;AAC7B,MAAM,UAAU,qBAAqB,CAAC,OAAe;IACnD,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,YAAY,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAA;IACpH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAe,EAAE,KAAa;IACjE,MAAM,SAAS,GAAG,eAAe,CAAC,OAAO,CAAC,CAAA;IAC1C,MAAM,QAAQ,GAAG,GAAG,SAAS,OAAO,CAAA;IACpC,IAAI,CAAC;QACH,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACnD,aAAa,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QAC/C,IAAI,CAAC;YACH,UAAU,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAA;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,aAAa,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;YAChD,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QACnC,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QACjC,MAAM,KAAK,CAAA;IACb,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAqCD,MAAM,UAAU,wBAAwB,CAAC,OAAkC;IACzE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IACvC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,EAAE,GAAG,IAAI,CAAA;IACpD,IAAI,MAAM,GAAgC,IAAI,CAAA;IAC9C,IAAI,WAAW,GAAG,KAAK,CAAA;IAEvB,yEAAyE;IACzE,qEAAqE;IACrE,uEAAuE;IACvE,mEAAmE;IACnE,iCAAiC;IACjC,MAAM,QAAQ,GAAG,GAAsB,EAAE;QACvC,MAAM,GAAG,GAAsB,EAAE,CAAA;QACjC,KAAK,MAAM,GAAG,IAAI;YAChB,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI;YACtC,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ;YAC9C,aAAa,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK;SACvE,EAAE,CAAC;YACF,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACjE,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC,CAAA;IAED,MAAM,UAAU,GAAG,CAAC,IAAc,EAAwB,EAAE,CAC1D,UAAU,CAAC,KAAK,CAAC;QACf,IAAI;QACJ,GAAG,EAAE,OAAO;QACZ,qEAAqE;QACrE,qEAAqE;QACrE,GAAG,EAAE,QAAQ,EAAE;QACf,KAAK,EAAE;YACL,KAAK,EAAE,QAAQ;YACf,MAAM,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE;YACjC,MAAM,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE;SAClC;QACD,OAAO,EAAE,IAAI;KACd,CAAC,CAAA;IAEJ,MAAM,SAAS,GAAG,CAAC,KAA2B,EAAQ,EAAE;QACtD,IAAI,MAAM,KAAK,KAAK;YAAE,MAAM,GAAG,IAAI,CAAA;IACrC,CAAC,CAAA;IAED,OAAO;QACL,KAAK,CAAC,KAAK,CAAC,KAAK;YACf,IAAI,MAAM,KAAK,IAAI;gBAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAA;YACnE,MAAM,WAAW,GAAG,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,IAAI,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAA;YAC5H,IAAI,WAAW,KAAK,SAAS;gBAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,CAAA;YAE3E,IAAI,UAAkB,CAAA;YACtB,IAAI,CAAC;gBACH,UAAU,GAAG,CAAC,MAAM,YAAY,CAAC,OAAO,EAAE,aAAa,EAAE;oBACvD,SAAS,EAAE,OAAO,CAAC,SAAS;oBAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;oBAC1B,UAAU,EAAE,OAAO,CAAC,UAAU;iBAC/B,CAAC,CAAC,CAAC,IAAI,CAAA;YACV,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,mBAAmB,EAAE,CAAC;oBACzC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAA;gBACrG,CAAC;gBACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,CAAE,KAAe,EAAE,OAAO,IAAI,KAAK,CAAC,EAAE,CAAA;YACpG,CAAC;YAED,IAAI,SAAiB,CAAA;YACrB,IAAI,CAAC;gBACH,SAAS,GAAG,oBAAoB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;YACxD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,CAAE,KAAe,EAAE,OAAO,IAAI,KAAK,CAAC,EAAE,CAAA;YACjG,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC,CAAA;gBAClF,MAAM,GAAG,KAAK,CAAA;gBACd,WAAW,GAAG,IAAI,CAAA;gBAClB,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAA;gBAC1E,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,KAAK,EAAE,CAAA;YACnE,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,GAAG,IAAI,CAAA;gBACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,CAAE,KAAe,EAAE,OAAO,IAAI,KAAK,CAAC,EAAE,CAAA;YACjG,CAAC;QACH,CAAC;QAED,IAAI;YACF,IAAI,MAAM,KAAK,IAAI;gBAAE,OAAM;YAC3B,MAAM,KAAK,GAAG,MAAM,CAAA;YACpB,MAAM,GAAG,IAAI,CAAA;YACb,IAAI,CAAC;gBACH,KAAK,KAAK,CAAC,SAAS,EAAE,CAAA;YACxB,CAAC;YAAC,MAAM,CAAC;gBACP,oEAAoE;YACtE,CAAC;QACH,CAAC;QAED,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,KAAK,IAAI;QAChC,kEAAkE;QAClE,UAAU,EAAE,GAAG,EAAE,CAAC,WAAW;KAC9B,CAAA;AACH,CAAC"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * hostPath probe (GW-8): the server machine walks its OWN public URL and
3
+ * classifies what answers, so the wizard can say something sharper than
4
+ * "it didn't work" — the pairing page showing up means TLS + proxying to
5
+ * the gateway both work; the admin surface answering 200 means the proxy
6
+ * is DROPPING the forwarded headers the gateway trusts; a redirect smells
7
+ * like Cloudflare Access or a proxy rewriting to a login page.
8
+ *
9
+ * Split exactly as the ticket asks: the two `classify*Response` functions
10
+ * are pure (the check scripts can drive them), `probeHostPath` owns the
11
+ * fetch I/O — `redirect: 'manual'` so a proxy's 3xx is SEEN, never
12
+ * followed — with the URL admission reusing client-pairing's
13
+ * `normalizeServerUrl` (https anywhere; plain http only for the explicit
14
+ * private-network allowances) and a 10 s ceiling per request.
15
+ */
16
+ import { normalizeServerUrl } from '../client-pairing.js';
17
+ /**
18
+ * `GET /`: the ONE right answer is the gateway's pairing page — 401,
19
+ * text/html, carrying the claim-endpoint marker the page's own script
20
+ * posts to. A 200 means the URL belongs to something else entirely.
21
+ */
22
+ export function classifyRootResponse(status, contentType, body) {
23
+ if (status === 401) {
24
+ if (contentType.includes('text/html') && body.includes('/lan-gate/pair/claim'))
25
+ return 'gateway-pairing-page';
26
+ return 'unexpected-401';
27
+ }
28
+ if (status >= 200 && status < 300)
29
+ return 'unexpected-200';
30
+ if (status >= 300 && status < 400)
31
+ return 'redirect';
32
+ return 'unexpected';
33
+ }
34
+ /**
35
+ * `GET /lan-gate/admin`: right is the gateway's own 403 JSON — a public
36
+ * visitor must not get in. 200 + HTML is the DANGEROUS shape: something
37
+ * answered with a page, meaning the proxy forwarded without the
38
+ * forwarded headers and the gateway's local-only wall never saw a
39
+ * proxied request (the admin page leaked). 403 + HTML smells like
40
+ * Cloudflare Access interposing its own login.
41
+ */
42
+ export function classifyAdminResponse(status, contentType, body) {
43
+ const isHtml = contentType.includes('text/html');
44
+ if (status === 403) {
45
+ if (isHtml)
46
+ return 'proxy-403';
47
+ if (contentType.includes('application/json')) {
48
+ try {
49
+ const parsed = JSON.parse(body);
50
+ if (parsed !== null && typeof parsed === 'object' && parsed.ok === false) {
51
+ return 'gateway-admin-403';
52
+ }
53
+ }
54
+ catch {
55
+ // JSON-shaped header with a non-JSON body: not our gateway's answer.
56
+ }
57
+ }
58
+ return 'unexpected';
59
+ }
60
+ if (status >= 200 && status < 300 && isHtml)
61
+ return 'admin-exposed';
62
+ return 'unexpected';
63
+ }
64
+ /** One manual-redirect GET, classified. Sends a browser-shaped `Accept:
65
+ * text/html` (never `application/json`, which tells `lan-gate-server.cjs`
66
+ * the caller is an XHR/API client and makes `GET /` answer 401 JSON
67
+ * `{"ok":false,"reason":"unpaired"}` instead of the HTML pairing page;
68
+ * `/lan-gate/admin` answers 403 JSON regardless of `Accept`). */
69
+ async function probeOnce(fetchImpl, url, timeoutMs) {
70
+ const response = await fetchImpl(url, {
71
+ redirect: 'manual',
72
+ signal: AbortSignal.timeout(timeoutMs),
73
+ headers: { Accept: 'text/html' },
74
+ });
75
+ const body = await response.text();
76
+ return { status: response.status, contentType: String(response.headers.get('content-type') ?? ''), body };
77
+ }
78
+ /**
79
+ * Probe the machine's public URL from here: both the root path and the
80
+ * admin surface, never following a redirect. The two requests run
81
+ * concurrently; the answer carries both classifications and the elapsed
82
+ * time. Every failure mode is a VALUE (`kind: 'unreachable'`), never a
83
+ * throw — the wizard routes render whichever verdict arrived.
84
+ */
85
+ export async function probeHostPath(publicUrl, options = {}) {
86
+ const started = (options.now ?? Date.now)();
87
+ const elapsed = () => (options.now ?? Date.now)() - started;
88
+ // Same admission the client pairing uses: https anywhere, plain http
89
+ // only into the explicit private-network allowances.
90
+ const normalized = normalizeServerUrl(typeof publicUrl === 'string' ? publicUrl : '');
91
+ if (!normalized.ok) {
92
+ return { kind: 'unreachable', reason: normalized.reason === 'insecure-http' ? 'insecure-http' : 'invalid-url', ms: 0 };
93
+ }
94
+ const fetchImpl = options.fetchImpl ?? fetch;
95
+ const timeoutMs = options.timeoutMs ?? 10_000;
96
+ try {
97
+ const [root, admin] = await Promise.all([
98
+ probeOnce(fetchImpl, `${normalized.url}/`, timeoutMs),
99
+ probeOnce(fetchImpl, `${normalized.url}/lan-gate/admin`, timeoutMs),
100
+ ]);
101
+ return {
102
+ kind: 'reached',
103
+ root: classifyRootResponse(root.status, root.contentType, root.body),
104
+ admin: classifyAdminResponse(admin.status, admin.contentType, admin.body),
105
+ ms: elapsed(),
106
+ };
107
+ }
108
+ catch (error) {
109
+ const name = error?.name;
110
+ return {
111
+ kind: 'unreachable',
112
+ reason: name === 'TimeoutError' || name === 'AbortError' ? 'timeout' : 'network',
113
+ detail: String(error?.message ?? error),
114
+ ms: elapsed(),
115
+ };
116
+ }
117
+ }
118
+ //# sourceMappingURL=probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe.js","sourceRoot":"","sources":["../../src/wizard/probe.ts"],"names":[],"mappings":"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"}