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,90 @@
1
+ /**
2
+ * Tunnel orchestration (GW-13b, 路 C's autopilot): one call drives the
3
+ * whole Cloudflare-side setup the design promises — zone/account lookup
4
+ * (through the ancestor-chain check), tunnel create-or-adopt, ingress
5
+ * configuration against the gateway's ACTUAL port, the proxied CNAME,
6
+ * and the cloudflared child.
7
+ *
8
+ * Rules the review and the design pin down:
9
+ *
10
+ * - **已有同名隧道**:the first setup pass answers `tunnel-conflict`
11
+ * with the same-name list; the UI lets the user pick one and re-runs
12
+ * with `tunnelId`, which adopts the existing tunnel through the token
13
+ * endpoint instead of creating anything.
14
+ * - **模式切换**:starting the tunnel stops the managed Caddy first
15
+ * (`stopCaddy`) — never two ingress processes at once.
16
+ * - The connector token travels from the Cloudflare answer into the
17
+ * 0600 token file and the child's `--token-file`; it never reaches a
18
+ * response or a log.
19
+ * - Every failure is a VALUE with a stable `reason` the UI maps to
20
+ * locale copy; CloudflareError codes map losslessly (`cf-*` → the
21
+ * orchestration's own reasons), unknown ones degrade to `api`.
22
+ */
23
+ import { type CloudflareApi, type CloudflareTunnelSummary } from './cloudflare.js';
24
+ /** The tunnel name the wizard mints and looks for. */
25
+ export declare const TUNNEL_NAME = "dsh-zen-remote";
26
+ export interface TunnelOrchestratorOptions {
27
+ dshHome: string;
28
+ /** The stored Cloudflare token (token-store); absent → `no-token`. */
29
+ readToken: () => string | undefined;
30
+ /** The gateway's ACTUAL port (the ingress service target); may probe
31
+ * asynchronously (the wiring re-reads /lan-gate/status per setup). */
32
+ gatewayPort: () => number | Promise<number>;
33
+ /** The client factory; injectable so the tests drive the orchestration
34
+ * against a scripted CloudflareApi without any HTTP. Defaults to
35
+ * createCloudflareApi over the injected fetch. */
36
+ apiFactory?: (token: string) => CloudflareApi;
37
+ cloudflared: {
38
+ start: (input: {
39
+ tunnelToken: string;
40
+ }) => Promise<{
41
+ ok: true;
42
+ } | {
43
+ ok: false;
44
+ error: string;
45
+ }>;
46
+ /** 模式切换:starting the tunnel stops the managed Caddy first. */
47
+ stopCaddy: () => void;
48
+ };
49
+ fetchImpl?: typeof fetch;
50
+ }
51
+ export interface TunnelSetupInput {
52
+ /** The public domain (bare hostname; may be a subdomain of the zone). */
53
+ domain: string;
54
+ /** Adopt this existing tunnel instead of creating one (the conflict path). */
55
+ tunnelId?: string;
56
+ }
57
+ export type TunnelSetupResult = {
58
+ ok: true;
59
+ tunnelId: string;
60
+ publicUrl: string;
61
+ tokenFile: string;
62
+ } | {
63
+ ok: false;
64
+ reason: 'no-token' | 'bad-domain' | 'zone-not-found' | 'tunnel-conflict' | 'dns-conflict' | 'auth' | 'network' | 'api' | 'spawn-failed';
65
+ detail?: string;
66
+ /** The same-name tunnels, for `tunnel-conflict`. */
67
+ existing?: CloudflareTunnelSummary[];
68
+ /** The manual-install page, for `spawn-failed`. */
69
+ installUrl?: string;
70
+ };
71
+ /**
72
+ * Drive the whole tunnel setup. Throws nothing — every outcome is a value.
73
+ */
74
+ export declare function setupTunnel(options: TunnelOrchestratorOptions, input: TunnelSetupInput): Promise<TunnelSetupResult>;
75
+ /**
76
+ * The check-domain probe the migration card's 重新检查 drives: does any
77
+ * ancestor of the domain name a zone on the token's accounts?
78
+ */
79
+ export type TunnelCheckResult = {
80
+ ok: true;
81
+ onCloudflare: boolean;
82
+ zoneId: string | null;
83
+ accountId: string | null;
84
+ } | {
85
+ ok: false;
86
+ reason: 'no-token' | 'bad-domain' | 'auth' | 'network' | 'api';
87
+ detail?: string;
88
+ };
89
+ export declare function checkDomainOnCloudflare(options: Pick<TunnelOrchestratorOptions, 'readToken' | 'fetchImpl' | 'apiFactory'>, domain: string): Promise<TunnelCheckResult>;
90
+ //# sourceMappingURL=tunnel-orchestrator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel-orchestrator.d.ts","sourceRoot":"","sources":["../../../src/wizard/tunnel-orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EAAuB,KAAK,aAAa,EAAE,KAAK,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAGvG,sDAAsD;AACtD,eAAO,MAAM,WAAW,mBAAmB,CAAA;AAE3C,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,SAAS,EAAE,MAAM,MAAM,GAAG,SAAS,CAAA;IACnC;0EACsE;IACtE,WAAW,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3C;;sDAEkD;IAClD,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,aAAa,CAAA;IAC7C,WAAW,EAAE;QACX,KAAK,EAAE,CAAC,KAAK,EAAE;YAAE,WAAW,EAAE,MAAM,CAAA;SAAE,KAAK,OAAO,CAAC;YAAE,EAAE,EAAE,IAAI,CAAA;SAAE,GAAG;YAAE,EAAE,EAAE,KAAK,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC/F,8DAA8D;QAC9D,SAAS,EAAE,MAAM,IAAI,CAAA;KACtB,CAAA;IACD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACpE;IACA,EAAE,EAAE,KAAK,CAAA;IACT,MAAM,EAAE,UAAU,GAAG,YAAY,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,cAAc,CAAA;IACvI,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oDAAoD;IACpD,QAAQ,CAAC,EAAE,uBAAuB,EAAE,CAAA;IACpC,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAcH;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,yBAAyB,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAwFzH;AAED;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACpF;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,UAAU,GAAG,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAElG,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,yBAAyB,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAe5K"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Final-verification orchestration (GW-9): the wizard's last mile, where
3
+ * 「完成」 means a real phone really paired through the real public URL —
4
+ * never a green light from internal checks alone (设计: 不做已联通的假绿灯).
5
+ *
6
+ * The flow, spread over two entry points the UI drives:
7
+ *
8
+ * start({ publicUrl }): runtime ready? → walk the public URL
9
+ * (GW-8's probe; BOTH the pairing page and the admin-403 shape must be
10
+ * right) → snapshot the gateway's device ids (the baseline) → mint a WEB
11
+ * pairing code → remember the session IN MEMORY and answer with the
12
+ * code, the URL and the expiry. The code never touches the row config,
13
+ * and minting it restarts nothing — trustedProxies changes are what
14
+ * restart the gateway, and any such change invalidates the session
15
+ * instead (the config fingerprint below).
16
+ *
17
+ * check(): re-read the gateway status. A device id the baseline did not
18
+ * have is a freshly paired phone; the gateway's lastWebClaim record must
19
+ * name THAT device with secure === true (the proxy passed
20
+ * X-Forwarded-Proto: https through, so the issued cookie carries the
21
+ * Secure flag). All green → pass. The claim recorded an insecure cookie →
22
+ * cookie-insecure. Past the code's expiry → code-expired. Anything else →
23
+ * pending, keep polling.
24
+ *
25
+ * The session binds to a CONFIG FINGERPRINT (ingressMode, domain,
26
+ * httpsPort, publicUrl, the gateway's ACTUAL port, host, trustedProxies):
27
+ * any of those moving between start and check answers stale and discards
28
+ * the session, because the verification proved a URL that no longer exists.
29
+ * The session is deliberately memory-only — a row reload (which a
30
+ * trustedProxies change causes) builds a fresh manager and the session is
31
+ * gone, exactly the same verdict the fingerprint would have produced.
32
+ */
33
+ import type { ZenRemoteConfig } from '../config.js';
34
+ import { type HostPathOutcome } from './probe.js';
35
+ export interface VerificationManagerOptions {
36
+ /** Loopback base of the gateway child, `http://127.0.0.1:<port>`. */
37
+ gatewayBase: string;
38
+ /** Fresh resolved config values per call (volatile discipline). */
39
+ getValues: () => ZenRemoteConfig;
40
+ /** The hostPath probe; injectable for tests (defaults to GW-8's real one). */
41
+ probe?: (publicUrl: string) => Promise<HostPathOutcome>;
42
+ /** Whether the ingress processes are alive (Caddy / cloudflared). The
43
+ * wiring supplies this once the process managers are mounted; absent
44
+ * means "checked elsewhere" and only the gateway is probed. */
45
+ runtimeReady?: () => boolean | Promise<boolean>;
46
+ fetchImpl?: typeof fetch;
47
+ /** Per-gateway-call ceiling. Default 5000 ms. */
48
+ timeoutMs?: number;
49
+ /** Clock (tests drive expiry). */
50
+ now?: () => number;
51
+ }
52
+ export type VerificationStartResult = {
53
+ ok: true;
54
+ pairingCode: string;
55
+ publicUrl: string;
56
+ expiresAt: number;
57
+ } | {
58
+ ok: false;
59
+ reason: 'no-public-url' | 'bad-public-url' | 'runtime-not-ready' | 'gateway-unreachable' | 'probe-failed' | 'pair-refused';
60
+ /** The probe's classifications for `probe-failed`; the gateway's status
61
+ * code for `pair-refused`; the normalize failure reason for
62
+ * `bad-public-url`. */
63
+ detail?: unknown;
64
+ };
65
+ export type VerificationCheckResult = {
66
+ result: 'not-started';
67
+ } | {
68
+ result: 'stale';
69
+ } | {
70
+ result: 'code-expired';
71
+ } | {
72
+ result: 'pending';
73
+ } | {
74
+ result: 'cookie-insecure';
75
+ deviceId: string;
76
+ } | {
77
+ result: 'pass';
78
+ deviceId: string;
79
+ at: number;
80
+ };
81
+ /**
82
+ * The config fingerprint a session binds to: any drift between start and
83
+ * check means the verified URL is no longer the deployed URL.
84
+ */
85
+ export declare function verificationFingerprint(values: ZenRemoteConfig, gatewayPort: number | null): string;
86
+ export interface VerificationManager {
87
+ start(input: {
88
+ publicUrl: unknown;
89
+ probeOnly?: boolean;
90
+ }): Promise<VerificationStartResult>;
91
+ check(): Promise<VerificationCheckResult>;
92
+ /** Whether a verification session is open (the route surfaces it for the
93
+ * UI's 「待外部验证」 state). */
94
+ hasSession(): boolean;
95
+ }
96
+ export declare function createVerificationManager(options: VerificationManagerOptions): VerificationManager;
97
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/wizard/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAEnD,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,YAAY,CAAA;AAEhE,MAAM,WAAW,0BAA0B;IACzC,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAA;IACnB,mEAAmE;IACnE,SAAS,EAAE,MAAM,eAAe,CAAA;IAChC,8EAA8E;IAC9E,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,eAAe,CAAC,CAAA;IACvD;;mEAE+D;IAC/D,YAAY,CAAC,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/C,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,MAAM,MAAM,uBAAuB,GAC/B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACvE;IACA,EAAE,EAAE,KAAK,CAAA;IACT,MAAM,EAAE,eAAe,GAAG,gBAAgB,GAAG,mBAAmB,GAAG,qBAAqB,GAAG,cAAc,GAAG,cAAc,CAAA;IAC1H;;2BAEuB;IACvB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAEH,MAAM,MAAM,uBAAuB,GAC/B;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,GACzB;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GACnB;IAAE,MAAM,EAAE,cAAc,CAAA;CAAE,GAC1B;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,iBAAiB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAA;AASpD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAUnG;AAWD,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,KAAK,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAA;IAC3F,KAAK,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAA;IACzC;8BAC0B;IAC1B,UAAU,IAAI,OAAO,CAAA;CACtB;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,GAAG,mBAAmB,CAyHlG"}
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Ingress binary I/O (GW-2, revised by GW-fix-1/H1): download, unpack,
3
+ * install and cache-probe for the two wizard-managed binaries. The
4
+ * arithmetic lives in binary.ts; this file is the only one allowed to touch
5
+ * the filesystem or the network for it.
6
+ *
7
+ * House rules the tasks pin down:
8
+ *
9
+ * - No shell, no `child_process` at all. Downloads ride `fetch`, the
10
+ * body streams straight to a `.part` file, and the install is a POSIX
11
+ * rename — a crash mid-download can never leave a half binary at the
12
+ * install path.
13
+ * - The archive wrapper comes from binary.ts's METADATA (`options.archive`,
14
+ * what binaryUrls() models per platform), never from sniffing the URL:
15
+ * the Caddy build API's URL is a query string whose ending says nothing
16
+ * about the tar.gz / zip payload it answers with (GW-fix-1/H1).
17
+ * - Windows' Caddy asset is a zip and there is no zip reader here yet:
18
+ * refused up front with the typed `windows-zip-unsupported` code, the
19
+ * manual-install link stays the fallback (GW-fix-1/H1 item 3).
20
+ * - After unpacking (or for a bare download), a unix image must carry an
21
+ * ELF or Mach-O magic; anything else is refused as `bad-binary-image`
22
+ * and leaves nothing on disk (GW-fix-1/H1 item 4). Windows keeps no
23
+ * magic check — PE inspection is not this module's job.
24
+ * - The darwin cloudflared asset is a `.tgz`; it is unpacked with
25
+ * `zlib.gunzipSync` plus a minimal ustar reader (the archive carries a
26
+ * single entry — no PAX/GNU long-name handling needed, but those
27
+ * header types are skipped correctly if an upstream rebuild adds one).
28
+ * - Installed images get 0755; `ensureBinary` is idempotent (an existing
29
+ * executable short-circuits before any network traffic).
30
+ *
31
+ * `fetch` is injectable so the tests drive real downloads against a local
32
+ * mock server without touching the network.
33
+ */
34
+ import { accessSync, chmodSync, closeSync, constants as fsConstants, createWriteStream, mkdirSync, openSync, readFileSync, readSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
35
+ import { Readable, Transform } from 'node:stream';
36
+ import { pipeline } from 'node:stream/promises';
37
+ import { gunzipSync } from 'node:zlib';
38
+ import { MANUAL_INSTALL_LINKS, binaryDownloadCandidates, binaryInstallPath, binDir, binaryUrls, currentPlatform, } from './binary.js';
39
+ /** Generous ceiling on one download; a mirror gone wrong must not be able
40
+ * to fill the disk. Both real binaries sit far below 200 MB. */
41
+ const MAX_DOWNLOAD_BYTES = 512 * 1024 * 1024;
42
+ /** Connection / response-headers timeout before a mirror is skipped. */
43
+ const CONNECT_TIMEOUT_MS = 10_000;
44
+ /** Maximum idle gap between body chunks before a stalled mirror is aborted. */
45
+ const STALL_TIMEOUT_MS = 15_000;
46
+ /** ELF: `\x7fELF`. */
47
+ const ELF_MAGIC = Buffer.from([0x7f, 0x45, 0x4c, 0x46]);
48
+ /** Mach-O 32 big-endian, Mach-O 64 big-endian, Mach-O 64 little-endian —
49
+ * the three shapes Apple toolchains emit for this codebase's targets. */
50
+ const MACH_O_MAGICS = [
51
+ Buffer.from([0xfe, 0xed, 0xfa, 0xce]),
52
+ Buffer.from([0xfe, 0xed, 0xfa, 0xcf]),
53
+ Buffer.from([0xcf, 0xfa, 0xed, 0xfe]),
54
+ ];
55
+ /** Whether the first bytes of an image look like an executable this module
56
+ * is willing to install on a unix host (GW-fix-1/H1 item 4). */
57
+ function looksLikeUnixImage(head) {
58
+ return ELF_MAGIC.equals(head) || MACH_O_MAGICS.some((magic) => magic.equals(head));
59
+ }
60
+ /** The first four bytes of a file (fewer when the file is shorter). */
61
+ function headBytes(path) {
62
+ const head = Buffer.alloc(4);
63
+ const fd = openSync(path, 'r');
64
+ try {
65
+ return head.subarray(0, readSync(fd, head, 0, 4, 0));
66
+ }
67
+ finally {
68
+ closeSync(fd);
69
+ }
70
+ }
71
+ /**
72
+ * One failed or refused download/install. `code` is the wire-stable marker
73
+ * the UI classifies on — `'download-failed'` for transport/shaping
74
+ * failures, `'windows-zip-unsupported'` for the zip refusal, and
75
+ * `'bad-binary-image'` when the payload did not carry an executable
76
+ * image's magic. `installUrl` is the official manual-install page the
77
+ * failure card links to. Fields are assigned in the constructor body rather
78
+ * than declared as parameter properties (the check scripts import modules
79
+ * through Node's strip-only type stripping, which rejects that syntax).
80
+ */
81
+ export class BinaryDownloadError extends Error {
82
+ code;
83
+ tool;
84
+ url;
85
+ status;
86
+ installUrl;
87
+ constructor(tool, url, status, message, options) {
88
+ super(message, options);
89
+ this.name = 'BinaryDownloadError';
90
+ this.code = options?.code ?? 'download-failed';
91
+ this.tool = tool;
92
+ this.url = url;
93
+ this.status = status;
94
+ this.installUrl = MANUAL_INSTALL_LINKS[tool];
95
+ }
96
+ }
97
+ /** Whether `path` exists and is executable (the "already installed" test). */
98
+ function isExecutableFile(path) {
99
+ try {
100
+ if (!statSync(path).isFile())
101
+ return false;
102
+ accessSync(path, fsConstants.X_OK);
103
+ return true;
104
+ }
105
+ catch {
106
+ return false;
107
+ }
108
+ }
109
+ /**
110
+ * The installed binary's absolute path, or undefined when it is not (yet)
111
+ * on disk in executable shape — the read-only probe; see binary.ts's
112
+ * `binaryInstallPath` for the pure arithmetic.
113
+ */
114
+ export function binaryPath(dshHome, tool, platform = currentPlatform()) {
115
+ const path = binaryInstallPath(dshHome, tool, platform);
116
+ return isExecutableFile(path) ? path : undefined;
117
+ }
118
+ /** Write one fetched response body to `partPath` under the size cap and
119
+ * per-chunk stall timeout so a throttled or hung mirror fails over quickly. */
120
+ async function streamToFile(tool, url, body, partPath, onProgress) {
121
+ let received = 0;
122
+ let stallTimer;
123
+ const counter = new Transform({
124
+ transform(chunk, _encoding, done) {
125
+ received += chunk.length;
126
+ if (received > MAX_DOWNLOAD_BYTES) {
127
+ done(new BinaryDownloadError(tool, url, null, `the download of ${tool} exceeds the ${MAX_DOWNLOAD_BYTES}-byte cap`));
128
+ return;
129
+ }
130
+ if (stallTimer !== undefined)
131
+ clearTimeout(stallTimer);
132
+ stallTimer = setTimeout(() => {
133
+ counter.destroy(new BinaryDownloadError(tool, url, null, `the download of ${tool} stalled after ${received} bytes`));
134
+ }, STALL_TIMEOUT_MS);
135
+ if (typeof stallTimer.unref === 'function')
136
+ stallTimer.unref();
137
+ onProgress?.(received, null);
138
+ done(null, chunk);
139
+ },
140
+ });
141
+ stallTimer = setTimeout(() => {
142
+ counter.destroy(new BinaryDownloadError(tool, url, null, `the download of ${tool} stalled before first byte`));
143
+ }, STALL_TIMEOUT_MS);
144
+ if (typeof stallTimer.unref === 'function')
145
+ stallTimer.unref();
146
+ try {
147
+ await pipeline(Readable.fromWeb(body), counter, createWriteStream(partPath, { mode: 0o644 }));
148
+ }
149
+ catch (error) {
150
+ rmSync(partPath, { force: true });
151
+ throw error;
152
+ }
153
+ finally {
154
+ if (stallTimer !== undefined)
155
+ clearTimeout(stallTimer);
156
+ }
157
+ }
158
+ /** Size bytes of a ustar header's octal size field, 0 when unparsable. */
159
+ function tarEntrySize(header) {
160
+ const raw = header.subarray(124, 136).toString('ascii').replace(/[\0 ]+$/g, '').trim();
161
+ const size = parseInt(raw === '' ? '0' : raw, 8);
162
+ return Number.isFinite(size) && size > 0 ? size : 0;
163
+ }
164
+ /**
165
+ * The first regular-file payload of a ustar archive as one buffer slice.
166
+ * The cloudflared `.tgz` carries exactly one (`cloudflared`); anything else
167
+ * (PAX/GNU metadata, directories) is skipped by advancing over its data
168
+ * blocks, so a future archive layout change degrades to "not found" instead
169
+ * of corrupt bytes.
170
+ */
171
+ function firstTarFileEntry(archive) {
172
+ let offset = 0;
173
+ while (offset + 512 <= archive.length) {
174
+ const header = archive.subarray(offset, offset + 512);
175
+ if (header.every((byte) => byte === 0))
176
+ return undefined;
177
+ const size = tarEntrySize(header);
178
+ const typeflag = String.fromCharCode(header[156] ?? 0x20);
179
+ const dataStart = offset + 512;
180
+ if ((typeflag === '0' || typeflag === '\0') && size > 0 && dataStart + size <= archive.length) {
181
+ return archive.subarray(dataStart, dataStart + size);
182
+ }
183
+ offset = dataStart + Math.ceil(size / 512) * 512;
184
+ }
185
+ return undefined;
186
+ }
187
+ function chmodExec(path) {
188
+ try {
189
+ chmodSync(path, 0o755);
190
+ }
191
+ catch {
192
+ // Windows: no executable bit — the extension does that job.
193
+ }
194
+ }
195
+ /** Install `bytes` at `installPath`: `.part` sibling, chmod, atomic rename. */
196
+ function installBytes(bytes, installPath) {
197
+ const partPath = `${installPath}.part`;
198
+ try {
199
+ writeFileSync(partPath, bytes, { mode: 0o644 });
200
+ chmodExec(partPath);
201
+ renameSync(partPath, installPath);
202
+ }
203
+ catch (error) {
204
+ rmSync(partPath, { force: true });
205
+ throw error;
206
+ }
207
+ }
208
+ /** The unix image-magic gate (GW-fix-1/H1 item 4): a payload that does not
209
+ * open with an ELF or Mach-O magic is refused, nothing lands on disk. */
210
+ function assertUnixImageMagic(tool, url, status, head) {
211
+ if (looksLikeUnixImage(head))
212
+ return;
213
+ throw new BinaryDownloadError(tool, url, status, `the downloaded ${tool} does not carry an ELF or Mach-O image magic`, { code: 'bad-binary-image' });
214
+ }
215
+ /**
216
+ * Download one binary from an explicit URL and install it. The archive
217
+ * wrapper is the CALLER'S metadata (`options.archive`) — `ensureBinary`
218
+ * feeds it from binaryUrls(); tests pin it per fixture. `platform` only
219
+ * matters for the install name and the magic-check gate; it defaults to
220
+ * this process's.
221
+ * @returns the absolute install path.
222
+ */
223
+ export async function downloadBinary(dshHome, tool, url, options = {}) {
224
+ const platform = options.platform ?? currentPlatform();
225
+ const archive = options.archive ?? 'none';
226
+ const installPath = binaryInstallPath(dshHome, tool, platform);
227
+ if (archive === 'zip') {
228
+ // Refused BEFORE any network traffic: there is no zip reader in this
229
+ // codebase yet (GW-fix-1/H1 item 3), and half-supporting the format
230
+ // would only install a zip's bytes as an image.
231
+ throw new BinaryDownloadError(tool, url, null, `the ${tool} asset for Windows ships as a zip, which cannot be unpacked here yet`, { code: 'windows-zip-unsupported' });
232
+ }
233
+ const fetchImpl = options.fetchImpl ?? fetch;
234
+ let response;
235
+ try {
236
+ response = await fetchImpl(url, {
237
+ redirect: 'follow',
238
+ signal: AbortSignal.timeout(CONNECT_TIMEOUT_MS),
239
+ });
240
+ }
241
+ catch (cause) {
242
+ throw new BinaryDownloadError(tool, url, null, `the download of ${tool} failed before an answer arrived`, { cause });
243
+ }
244
+ if (!response.ok || response.body === null) {
245
+ throw new BinaryDownloadError(tool, url, response.status, `the download of ${tool} answered ${response.status}`);
246
+ }
247
+ mkdirSync(binDir(dshHome), { recursive: true });
248
+ const declared = Number(response.headers.get('content-length'));
249
+ const total = Number.isFinite(declared) && declared > 0 ? declared : null;
250
+ const onProgress = options.onProgress
251
+ ? (received) => options.onProgress?.(received, total)
252
+ : undefined;
253
+ if (archive === 'tgz') {
254
+ // Archive (or auto-detected bare binary if an upstream endpoint serves a
255
+ // raw ELF/Mach-O image despite tgz metadata): land the bytes in a sibling,
256
+ // then either install directly when the payload already starts with a
257
+ // unix image magic or gunzip + extract the tar entry.
258
+ const tgzPath = `${installPath}.download.tgz`;
259
+ try {
260
+ await streamToFile(tool, url, response.body, tgzPath, onProgress);
261
+ const rawBytes = readFileSync(tgzPath);
262
+ const gzipMagic = rawBytes.length >= 2 && rawBytes[0] === 0x1f && rawBytes[1] === 0x8b;
263
+ const entry = (!gzipMagic && platform.os !== 'windows' && looksLikeUnixImage(rawBytes.subarray(0, 4)))
264
+ ? rawBytes
265
+ : firstTarFileEntry(gunzipSync(rawBytes));
266
+ if (entry === undefined) {
267
+ throw new BinaryDownloadError(tool, url, response.status, `the ${tool} archive carried no binary entry`);
268
+ }
269
+ if (platform.os !== 'windows')
270
+ assertUnixImageMagic(tool, url, response.status, entry.subarray(0, 4));
271
+ installBytes(entry, installPath);
272
+ }
273
+ catch (error) {
274
+ if (error instanceof BinaryDownloadError)
275
+ throw error;
276
+ throw new BinaryDownloadError(tool, url, response.status, `the ${tool} archive could not be unpacked`, { cause: error });
277
+ }
278
+ finally {
279
+ rmSync(tgzPath, { force: true });
280
+ }
281
+ return installPath;
282
+ }
283
+ // Bare executable: stream to the .part, gate the magic, then rename.
284
+ const partPath = `${installPath}.part`;
285
+ try {
286
+ await streamToFile(tool, url, response.body, partPath, onProgress);
287
+ if (platform.os !== 'windows')
288
+ assertUnixImageMagic(tool, url, response.status, headBytes(partPath));
289
+ chmodExec(partPath);
290
+ renameSync(partPath, installPath);
291
+ }
292
+ catch (error) {
293
+ rmSync(partPath, { force: true });
294
+ if (error instanceof BinaryDownloadError)
295
+ throw error;
296
+ throw new BinaryDownloadError(tool, url, response.status, `the download of ${tool} could not be written to disk`, { cause: error });
297
+ }
298
+ return installPath;
299
+ }
300
+ /** Cached mainland-China network detection so repeated downloads do not
301
+ * re-probe within the same process. */
302
+ let cachedCnNetwork;
303
+ /**
304
+ * Detect whether the current host is in mainland China:
305
+ * 1. Fast local check: system timezone is `Asia/Shanghai`, `Asia/Chongqing`,
306
+ * `Asia/Harbin`, or `Asia/Urumqi`, or `LANG`/`LC_ALL` carries `zh_CN`.
307
+ * 2. Quick 1.5s probe to Cloudflare's `/cdn-cgi/trace` (`loc=CN`), or if
308
+ * direct `github.com` HEAD fails/times out within 2s.
309
+ */
310
+ export async function detectCnNetwork(fetchImpl = fetch) {
311
+ if (cachedCnNetwork !== undefined)
312
+ return cachedCnNetwork;
313
+ try {
314
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone ?? '';
315
+ if (/^Asia\/(Shanghai|Chongqing|Harbin|Urumqi)$/i.test(tz)) {
316
+ cachedCnNetwork = true;
317
+ return true;
318
+ }
319
+ }
320
+ catch { /* ignore */ }
321
+ const envLang = `${process.env.LANG ?? ''} ${process.env.LC_ALL ?? ''}`;
322
+ if (/zh_CN/i.test(envLang)) {
323
+ cachedCnNetwork = true;
324
+ return true;
325
+ }
326
+ try {
327
+ const trace = await fetchImpl('https://www.cloudflare.com/cdn-cgi/trace', {
328
+ signal: AbortSignal.timeout(1500),
329
+ });
330
+ if (trace.ok) {
331
+ const text = await trace.text();
332
+ if (/\bloc=CN\b/i.test(text)) {
333
+ cachedCnNetwork = true;
334
+ return true;
335
+ }
336
+ cachedCnNetwork = false;
337
+ return false;
338
+ }
339
+ }
340
+ catch {
341
+ // ignore trace timeout
342
+ }
343
+ cachedCnNetwork = false;
344
+ return false;
345
+ }
346
+ /**
347
+ * The idempotent front door: an already-installed executable short-circuits
348
+ * (no network), anything else downloads from the official distribution
349
+ * point for the tool, archive shape included.
350
+ *
351
+ * When using the default `fetch` transport, automatically detects mainland
352
+ * China network environments and tries prebuilt GitHub Release mirrors
353
+ * (`gh-proxy.com` / `ghfast.top`) + direct GitHub Release before falling back
354
+ * to `caddyserver.com/api/download`. When a custom `fetchImpl` is injected
355
+ * (unit tests), calls the canonical `binaryUrls(platform)` endpoint directly.
356
+ * @returns the absolute install path and whether THIS call downloaded.
357
+ */
358
+ export async function ensureBinary(dshHome, tool, options = {}) {
359
+ const platform = options.platform ?? currentPlatform();
360
+ const existing = binaryPath(dshHome, tool, platform);
361
+ if (existing !== undefined)
362
+ return { path: existing, downloaded: false };
363
+ if (options.fetchImpl !== undefined) {
364
+ const urls = binaryUrls(platform);
365
+ const [url, archive] = tool === 'caddy' ? [urls.caddy, urls.caddyArchive] : [urls.cloudflared, urls.cloudflaredArchive];
366
+ const path = await downloadBinary(dshHome, tool, url, { ...options, archive });
367
+ return { path, downloaded: true };
368
+ }
369
+ const preferCnMirror = await detectCnNetwork();
370
+ const candidates = binaryDownloadCandidates(tool, platform, preferCnMirror);
371
+ let lastError;
372
+ for (const candidate of candidates) {
373
+ try {
374
+ const path = await downloadBinary(dshHome, tool, candidate.url, {
375
+ ...options,
376
+ archive: candidate.archive,
377
+ });
378
+ return { path, downloaded: true };
379
+ }
380
+ catch (error) {
381
+ lastError = error;
382
+ }
383
+ }
384
+ throw lastError;
385
+ }
386
+ //# sourceMappingURL=binary-io.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binary-io.js","sourceRoot":"","sources":["../../src/wizard/binary-io.ts"],"names":[],"mappings":"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"}