@xopcai/xopc 0.0.77 → 0.0.78

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 (126) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{agents-DN3vr8pb.js → agents-Bh_9-1KB.js} +2 -2
  4. package/dist/gateway/static/root/assets/{agents-DN3vr8pb.js.map → agents-Bh_9-1KB.js.map} +1 -1
  5. package/dist/gateway/static/root/assets/{apps-page-BUn41aPi.js → apps-page-CB5anZpc.js} +2 -2
  6. package/dist/gateway/static/root/assets/{apps-page-BUn41aPi.js.map → apps-page-CB5anZpc.js.map} +1 -1
  7. package/dist/gateway/static/root/assets/{channels-settings-CYMmWDtP.js → channels-settings-Bt1sprhC.js} +2 -2
  8. package/dist/gateway/static/root/assets/{channels-settings-CYMmWDtP.js.map → channels-settings-Bt1sprhC.js.map} +1 -1
  9. package/dist/gateway/static/root/assets/{channels-status-swr-sJj4ueTp.js → channels-status-swr-Crgak3fg.js} +2 -2
  10. package/dist/gateway/static/root/assets/{channels-status-swr-sJj4ueTp.js.map → channels-status-swr-Crgak3fg.js.map} +1 -1
  11. package/dist/gateway/static/root/assets/{cron-api-CLxnaHdq.js → cron-api-CzJGvQQ7.js} +2 -2
  12. package/dist/gateway/static/root/assets/{cron-api-CLxnaHdq.js.map → cron-api-CzJGvQQ7.js.map} +1 -1
  13. package/dist/gateway/static/root/assets/{cron-page-BAQ8xSnJ.js → cron-page-BoNRJNVV.js} +2 -2
  14. package/dist/gateway/static/root/assets/{cron-page-BAQ8xSnJ.js.map → cron-page-BoNRJNVV.js.map} +1 -1
  15. package/dist/gateway/static/root/assets/{dist-BfJYxiK5.js → dist-a-eaOUvs.js} +2 -2
  16. package/dist/gateway/static/root/assets/{dist-BfJYxiK5.js.map → dist-a-eaOUvs.js.map} +1 -1
  17. package/dist/gateway/static/root/assets/{extension-debug-page-bgvVs-Sy.js → extension-debug-page-D-O1XjAa.js} +2 -2
  18. package/dist/gateway/static/root/assets/{extension-debug-page-bgvVs-Sy.js.map → extension-debug-page-D-O1XjAa.js.map} +1 -1
  19. package/dist/gateway/static/root/assets/{extension-page-SG4TVv-u.js → extension-page-B2VpqBTH.js} +2 -2
  20. package/dist/gateway/static/root/assets/{extension-page-SG4TVv-u.js.map → extension-page-B2VpqBTH.js.map} +1 -1
  21. package/dist/gateway/static/root/assets/{extension-settings-page-CJZRTsjF.js → extension-settings-page-CmBcQfeO.js} +2 -2
  22. package/dist/gateway/static/root/assets/{extension-settings-page-CJZRTsjF.js.map → extension-settings-page-CmBcQfeO.js.map} +1 -1
  23. package/dist/gateway/static/root/assets/{fetch-K_0JRCXU.js → fetch-EGO9T3MN.js} +3 -3
  24. package/dist/gateway/static/root/assets/{fetch-K_0JRCXU.js.map → fetch-EGO9T3MN.js.map} +1 -1
  25. package/dist/gateway/static/root/assets/{field-primitives-Z76hyBYS.js → field-primitives-Bh7G1y4D.js} +2 -2
  26. package/dist/gateway/static/root/assets/{field-primitives-Z76hyBYS.js.map → field-primitives-Bh7G1y4D.js.map} +1 -1
  27. package/dist/gateway/static/root/assets/{heartbeat-config-api-BqfDabSI.js → heartbeat-config-api-DxpIEZNs.js} +2 -2
  28. package/dist/gateway/static/root/assets/{heartbeat-config-api-BqfDabSI.js.map → heartbeat-config-api-DxpIEZNs.js.map} +1 -1
  29. package/dist/gateway/static/root/assets/{index-ChiUhJAs.js → index-Dxy9ZCtC.js} +5 -5
  30. package/dist/gateway/static/root/assets/{index-ChiUhJAs.js.map → index-Dxy9ZCtC.js.map} +1 -1
  31. package/dist/gateway/static/root/assets/{logs-page-DrIMhDE2.js → logs-page-Dw58E2GE.js} +2 -2
  32. package/dist/gateway/static/root/assets/{logs-page-DrIMhDE2.js.map → logs-page-Dw58E2GE.js.map} +1 -1
  33. package/dist/gateway/static/root/assets/{sessions-page-B-RGO3N0.js → sessions-page-CPkhCy57.js} +2 -2
  34. package/dist/gateway/static/root/assets/{sessions-page-B-RGO3N0.js.map → sessions-page-CPkhCy57.js.map} +1 -1
  35. package/dist/gateway/static/root/assets/{settings-form-section-Csvl1iL6.js → settings-form-section-DLZDVMEf.js} +2 -2
  36. package/dist/gateway/static/root/assets/{settings-form-section-Csvl1iL6.js.map → settings-form-section-DLZDVMEf.js.map} +1 -1
  37. package/dist/gateway/static/root/assets/settings-page-CVPCa0PE.js +4 -0
  38. package/dist/gateway/static/root/assets/settings-page-CVPCa0PE.js.map +1 -0
  39. package/dist/gateway/static/root/assets/{skills-page-dHwx2vh0.js → skills-page-DueZ9Qfg.js} +2 -2
  40. package/dist/gateway/static/root/assets/{skills-page-dHwx2vh0.js.map → skills-page-DueZ9Qfg.js.map} +1 -1
  41. package/dist/gateway/static/root/assets/{theme-store-Bl5A2Fd_.js → theme-store-CWPq9gW1.js} +2 -2
  42. package/dist/gateway/static/root/assets/{theme-store-Bl5A2Fd_.js.map → theme-store-CWPq9gW1.js.map} +1 -1
  43. package/dist/gateway/static/root/assets/{utils-COYrNFF7.js → utils-Cnix55r9.js} +2 -2
  44. package/dist/gateway/static/root/assets/{utils-COYrNFF7.js.map → utils-Cnix55r9.js.map} +1 -1
  45. package/dist/gateway/static/root/assets/{voice-api-key-field-5WZZaxH3.js → voice-api-key-field-BR3Ut06g.js} +2 -2
  46. package/dist/gateway/static/root/assets/{voice-api-key-field-5WZZaxH3.js.map → voice-api-key-field-BR3Ut06g.js.map} +1 -1
  47. package/dist/gateway/static/root/index.html +3 -3
  48. package/dist/package.js +1 -1
  49. package/dist/src/browser/providers/browser-ext-install.js +23 -4
  50. package/dist/src/browser/providers/browser-ext-install.js.map +1 -1
  51. package/dist/src/cli/commands/tunnel.js +4 -5
  52. package/dist/src/cli/commands/tunnel.js.map +1 -1
  53. package/dist/src/config/index.js +2 -2
  54. package/dist/src/config/rules.js +0 -5
  55. package/dist/src/config/rules.js.map +1 -1
  56. package/dist/src/config/schema.d.ts +0 -27
  57. package/dist/src/config/schema.js +4 -18
  58. package/dist/src/config/schema.js.map +1 -1
  59. package/dist/src/gateway/auth-rate-limit.d.ts +2 -0
  60. package/dist/src/gateway/auth-rate-limit.js +9 -3
  61. package/dist/src/gateway/auth-rate-limit.js.map +1 -1
  62. package/dist/src/gateway/hono/app.js +19 -13
  63. package/dist/src/gateway/hono/app.js.map +1 -1
  64. package/dist/src/gateway/hono/lib/config-payload.d.ts +3 -1
  65. package/dist/src/gateway/hono/lib/config-payload.js +1 -2
  66. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  67. package/dist/src/gateway/hono/routes/tunnel.js +32 -30
  68. package/dist/src/gateway/hono/routes/tunnel.js.map +1 -1
  69. package/dist/src/gateway/host.d.ts +24 -0
  70. package/dist/src/gateway/host.js +33 -1
  71. package/dist/src/gateway/host.js.map +1 -1
  72. package/dist/src/gateway/index.d.ts +1 -1
  73. package/dist/src/gateway/index.js +2 -2
  74. package/dist/src/gateway/runtime-config.js +1 -8
  75. package/dist/src/gateway/runtime-config.js.map +1 -1
  76. package/dist/src/gateway/security/audit.js +4 -4
  77. package/dist/src/gateway/security/audit.js.map +1 -1
  78. package/dist/src/gateway/server.js +2 -3
  79. package/dist/src/gateway/server.js.map +1 -1
  80. package/dist/src/gateway/service/types.d.ts +2 -0
  81. package/dist/src/gateway/service.d.ts +2 -0
  82. package/dist/src/gateway/service.js +7 -2
  83. package/dist/src/gateway/service.js.map +1 -1
  84. package/dist/src/tunnel/frp-subdomain-host.d.ts +2 -0
  85. package/dist/src/tunnel/frp-subdomain-host.js +15 -0
  86. package/dist/src/tunnel/frp-subdomain-host.js.map +1 -0
  87. package/dist/src/tunnel/gateway-lifecycle.d.ts +0 -4
  88. package/dist/src/tunnel/gateway-lifecycle.js +9 -11
  89. package/dist/src/tunnel/gateway-lifecycle.js.map +1 -1
  90. package/dist/src/tunnel/index.d.ts +2 -4
  91. package/dist/src/tunnel/index.js +2 -4
  92. package/dist/src/tunnel/pair-url.js +7 -1
  93. package/dist/src/tunnel/pair-url.js.map +1 -1
  94. package/dist/src/tunnel/pairing.d.ts +13 -0
  95. package/dist/src/tunnel/pairing.js +48 -1
  96. package/dist/src/tunnel/pairing.js.map +1 -1
  97. package/dist/src/tunnel/tunnel-config.js +2 -16
  98. package/dist/src/tunnel/tunnel-config.js.map +1 -1
  99. package/dist/src/tunnel/tunnel-service.d.ts +1 -10
  100. package/dist/src/tunnel/tunnel-service.js +7 -60
  101. package/dist/src/tunnel/tunnel-service.js.map +1 -1
  102. package/dist/src/tunnel/tunnel-types.d.ts +3 -18
  103. package/dist/src/tunnel/well-known.d.ts +5 -0
  104. package/dist/src/tunnel/well-known.js +2 -1
  105. package/dist/src/tunnel/well-known.js.map +1 -1
  106. package/package.json +2 -2
  107. package/dist/gateway/static/root/assets/settings-page-nxAc0ta1.js +0 -4
  108. package/dist/gateway/static/root/assets/settings-page-nxAc0ta1.js.map +0 -1
  109. package/dist/src/tunnel/acme-cert-store.d.ts +0 -34
  110. package/dist/src/tunnel/acme-cert-store.js +0 -184
  111. package/dist/src/tunnel/acme-cert-store.js.map +0 -1
  112. package/dist/src/tunnel/acme-client.d.ts +0 -50
  113. package/dist/src/tunnel/acme-client.js +0 -473
  114. package/dist/src/tunnel/acme-client.js.map +0 -1
  115. package/dist/src/tunnel/acme-crypto.d.ts +0 -25
  116. package/dist/src/tunnel/acme-crypto.js +0 -58
  117. package/dist/src/tunnel/acme-crypto.js.map +0 -1
  118. package/dist/src/tunnel/acme-csr.d.ts +0 -5
  119. package/dist/src/tunnel/acme-csr.js +0 -48
  120. package/dist/src/tunnel/acme-csr.js.map +0 -1
  121. package/dist/src/tunnel/tls-server.d.ts +0 -14
  122. package/dist/src/tunnel/tls-server.js +0 -126
  123. package/dist/src/tunnel/tls-server.js.map +0 -1
  124. package/dist/src/tunnel/tunnel-e2e-config.d.ts +0 -11
  125. package/dist/src/tunnel/tunnel-e2e-config.js +0 -29
  126. package/dist/src/tunnel/tunnel-e2e-config.js.map +0 -1
@@ -1,48 +0,0 @@
1
- import { join } from "node:path";
2
- import { tmpdir } from "node:os";
3
- import { mkdtempSync, readFileSync, rmSync } from "node:fs";
4
- import { execFileSync } from "node:child_process";
5
- //#region src/tunnel/acme-csr.ts
6
- /** Generate EC P-256 CSR + private key PEM via openssl (no extra npm deps). */
7
- function generateDomainCsr(domain) {
8
- const workDir = mkdtempSync(join(tmpdir(), "xopc-acme-csr-"));
9
- const keyPath = join(workDir, "domain.key.pem");
10
- const csrPath = join(workDir, "domain.csr.pem");
11
- try {
12
- execFileSync("openssl", [
13
- "ecparam",
14
- "-name",
15
- "prime256v1",
16
- "-genkey",
17
- "-noout",
18
- "-out",
19
- keyPath
20
- ], { stdio: "ignore" });
21
- execFileSync("openssl", [
22
- "req",
23
- "-new",
24
- "-key",
25
- keyPath,
26
- "-out",
27
- csrPath,
28
- "-subj",
29
- `/CN=${domain}`
30
- ], { stdio: "ignore" });
31
- const keyPem = readFileSync(keyPath, "utf8");
32
- const match = readFileSync(csrPath, "utf8").match(/-----BEGIN CERTIFICATE REQUEST-----[\s\S]+?-----END CERTIFICATE REQUEST-----/);
33
- if (!match) throw new Error("Failed to parse CSR PEM");
34
- return {
35
- csrDer: Buffer.from(match[0].replace(/-----(BEGIN|END) CERTIFICATE REQUEST-----/g, "").replace(/\s+/g, ""), "base64"),
36
- keyPem
37
- };
38
- } finally {
39
- rmSync(workDir, {
40
- recursive: true,
41
- force: true
42
- });
43
- }
44
- }
45
- //#endregion
46
- export { generateDomainCsr };
47
-
48
- //# sourceMappingURL=acme-csr.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"acme-csr.js","names":[],"sources":["../../../src/tunnel/acme-csr.ts"],"sourcesContent":["import { execFileSync } from 'node:child_process';\nimport { mkdtempSync, readFileSync, rmSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\n\n/** Generate EC P-256 CSR + private key PEM via openssl (no extra npm deps). */\nexport function generateDomainCsr(domain: string): { csrDer: Buffer; keyPem: string } {\n const workDir = mkdtempSync(join(tmpdir(), 'xopc-acme-csr-'));\n const keyPath = join(workDir, 'domain.key.pem');\n const csrPath = join(workDir, 'domain.csr.pem');\n try {\n // Two-step CSR: avoid OpenSSL `-newkey ec` embedding explicit EC parameters (LE rejects those).\n execFileSync(\n 'openssl',\n ['ecparam', '-name', 'prime256v1', '-genkey', '-noout', '-out', keyPath],\n { stdio: 'ignore' },\n );\n execFileSync(\n 'openssl',\n ['req', '-new', '-key', keyPath, '-out', csrPath, '-subj', `/CN=${domain}`],\n { stdio: 'ignore' },\n );\n const keyPem = readFileSync(keyPath, 'utf8');\n const csrPem = readFileSync(csrPath, 'utf8');\n const match = csrPem.match(/-----BEGIN CERTIFICATE REQUEST-----[\\s\\S]+?-----END CERTIFICATE REQUEST-----/);\n if (!match) throw new Error('Failed to parse CSR PEM');\n const der = Buffer.from(\n match[0].replace(/-----(BEGIN|END) CERTIFICATE REQUEST-----/g, '').replace(/\\s+/g, ''),\n 'base64',\n );\n return { csrDer: der, keyPem };\n } finally {\n rmSync(workDir, { recursive: true, force: true });\n }\n}\n"],"mappings":";;;;;;AAMA,SAAgB,kBAAkB,QAAoD;CACpF,MAAM,UAAU,YAAY,KAAK,QAAQ,EAAE,iBAAiB,CAAC;CAC7D,MAAM,UAAU,KAAK,SAAS,iBAAiB;CAC/C,MAAM,UAAU,KAAK,SAAS,iBAAiB;AAC/C,KAAI;AAEF,eACE,WACA;GAAC;GAAW;GAAS;GAAc;GAAW;GAAU;GAAQ;GAAQ,EACxE,EAAE,OAAO,UAAU,CACpB;AACD,eACE,WACA;GAAC;GAAO;GAAQ;GAAQ;GAAS;GAAQ;GAAS;GAAS,OAAO;GAAS,EAC3E,EAAE,OAAO,UAAU,CACpB;EACD,MAAM,SAAS,aAAa,SAAS,OAAO;EAE5C,MAAM,QADS,aAAa,SAAS,OACjB,CAAC,MAAM,+EAA+E;AAC1G,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,0BAA0B;AAKtD,SAAO;GAAE,QAJG,OAAO,KACjB,MAAM,GAAG,QAAQ,8CAA8C,GAAG,CAAC,QAAQ,QAAQ,GAAG,EACtF,SAEkB;GAAE;GAAQ;WACtB;AACR,SAAO,SAAS;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC"}
@@ -1,14 +0,0 @@
1
- import { type Server } from 'node:https';
2
- import { type StoredCert } from './acme-cert-store.js';
3
- import type { AcmeConfig } from './acme-client.js';
4
- export type TunnelTlsServerConfig = {
5
- tlsPort: number;
6
- gatewayPort: number;
7
- acmeConfig: AcmeConfig;
8
- fetch?: typeof fetch;
9
- };
10
- export declare function startTunnelTlsServer(config: TunnelTlsServerConfig): Promise<Server>;
11
- export declare function stopTunnelTlsServer(): void;
12
- export declare function getActiveTlsCert(): StoredCert | null;
13
- /** @internal */
14
- export declare function resetTunnelTlsServerForTests(): void;
@@ -1,126 +0,0 @@
1
- import { createLogger } from "../utils/logger/index.js";
2
- import { init_logger } from "../utils/logger.js";
3
- import { ensureValidCert, loadStoredCert, startRenewalScheduler, stopRenewalScheduler } from "./acme-cert-store.js";
4
- import { Readable } from "node:stream";
5
- import { createServer } from "node:https";
6
- //#region src/tunnel/tls-server.ts
7
- init_logger();
8
- const log = createLogger("TunnelTLS");
9
- let httpsNodeServer = null;
10
- function resolveTlsFetch(gatewayPort, customFetch) {
11
- if (customFetch) return (req) => customFetch(req);
12
- return async (req) => {
13
- const url = new URL(req.url);
14
- url.protocol = "http:";
15
- url.hostname = "127.0.0.1";
16
- url.port = String(gatewayPort);
17
- return fetch(url, {
18
- method: req.method,
19
- headers: req.headers,
20
- body: req.body,
21
- redirect: "manual",
22
- duplex: "half"
23
- });
24
- };
25
- }
26
- async function nodeRequestToFetch(req, tlsPort) {
27
- const url = new URL(req.url ?? "/", `https://127.0.0.1:${tlsPort}`);
28
- const headers = new Headers();
29
- for (const [key, value] of Object.entries(req.headers)) {
30
- if (value === void 0) continue;
31
- if (Array.isArray(value)) for (const part of value) headers.append(key, part);
32
- else headers.set(key, value);
33
- }
34
- const hasBody = req.method !== "GET" && req.method !== "HEAD";
35
- const init = {
36
- method: req.method,
37
- headers
38
- };
39
- if (hasBody) {
40
- init.body = Readable.toWeb(req);
41
- init.duplex = "half";
42
- }
43
- return new Request(url, init);
44
- }
45
- async function sendFetchResponse(res, response) {
46
- res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
47
- if (response.body) {
48
- const reader = response.body.getReader();
49
- while (true) {
50
- const { done, value } = await reader.read();
51
- if (done) break;
52
- res.write(value);
53
- }
54
- }
55
- res.end();
56
- }
57
- async function startTunnelTlsServer(config) {
58
- if (httpsNodeServer) return httpsNodeServer;
59
- const cert = await ensureValidCert(config.acmeConfig);
60
- const handler = resolveTlsFetch(config.gatewayPort, config.fetch);
61
- httpsNodeServer = createServer({
62
- key: cert.keyPem,
63
- cert: cert.certPem,
64
- minVersion: "TLSv1.2"
65
- }, (req, res) => {
66
- (async () => {
67
- try {
68
- await sendFetchResponse(res, await handler(await nodeRequestToFetch(req, config.tlsPort)));
69
- } catch (err) {
70
- log.warn({
71
- err,
72
- phase: "tls_proxy"
73
- }, "TLS proxy request failed");
74
- if (!res.headersSent) res.writeHead(502);
75
- res.end("Bad Gateway");
76
- }
77
- })();
78
- });
79
- await new Promise((resolve, reject) => {
80
- httpsNodeServer.once("error", reject);
81
- httpsNodeServer.listen(config.tlsPort, "127.0.0.1", () => resolve());
82
- }).catch((err) => {
83
- httpsNodeServer?.close();
84
- httpsNodeServer = null;
85
- if ((err instanceof Error ? err.message : String(err)).includes("EADDRINUSE")) throw new Error(`Tunnel TLS port ${config.tlsPort} is already in use. Stop other xopc tunnel instances or set tunnel.e2e.tlsPort (try ${config.gatewayPort + 1} for gateway port ${config.gatewayPort}).`);
86
- throw err;
87
- });
88
- log.info({
89
- port: config.tlsPort,
90
- domain: cert.domain,
91
- expiresAt: cert.expiresAt
92
- }, "Tunnel TLS server listening (Let's Encrypt cert)");
93
- startRenewalScheduler(config.acmeConfig, () => reloadTlsCert());
94
- return httpsNodeServer;
95
- }
96
- function reloadTlsCert() {
97
- const cert = loadStoredCert();
98
- if (!cert || !httpsNodeServer) return;
99
- httpsNodeServer.setSecureContext({
100
- key: cert.keyPem,
101
- cert: cert.certPem
102
- });
103
- log.info({
104
- domain: cert.domain,
105
- expiresAt: cert.expiresAt
106
- }, "TLS cert hot-reloaded");
107
- }
108
- function stopTunnelTlsServer() {
109
- stopRenewalScheduler();
110
- if (httpsNodeServer) {
111
- httpsNodeServer.close();
112
- httpsNodeServer = null;
113
- log.info("TLS server stopped");
114
- }
115
- }
116
- function getActiveTlsCert() {
117
- return loadStoredCert();
118
- }
119
- /** @internal */
120
- function resetTunnelTlsServerForTests() {
121
- stopTunnelTlsServer();
122
- }
123
- //#endregion
124
- export { getActiveTlsCert, resetTunnelTlsServerForTests, startTunnelTlsServer, stopTunnelTlsServer };
125
-
126
- //# sourceMappingURL=tls-server.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tls-server.js","names":["createHttpsServer"],"sources":["../../../src/tunnel/tls-server.ts"],"sourcesContent":["import { createServer as createHttpsServer, type Server } from 'node:https';\nimport { Readable } from 'node:stream';\n\nimport { createLogger } from '../utils/logger.js';\nimport {\n ensureValidCert,\n loadStoredCert,\n startRenewalScheduler,\n stopRenewalScheduler,\n type StoredCert,\n} from './acme-cert-store.js';\nimport type { AcmeConfig } from './acme-client.js';\n\nconst log = createLogger('TunnelTLS');\n\nlet httpsNodeServer: Server | null = null;\n\nexport type TunnelTlsServerConfig = {\n tlsPort: number;\n gatewayPort: number;\n acmeConfig: AcmeConfig;\n fetch?: typeof fetch;\n};\n\nfunction resolveTlsFetch(\n gatewayPort: number,\n customFetch?: typeof fetch,\n): (req: Request) => Response | Promise<Response> {\n if (customFetch) {\n return (req: Request) => customFetch(req);\n }\n return async (req: Request) => {\n const url = new URL(req.url);\n url.protocol = 'http:';\n url.hostname = '127.0.0.1';\n url.port = String(gatewayPort);\n return fetch(url, {\n method: req.method,\n headers: req.headers,\n body: req.body,\n redirect: 'manual',\n duplex: 'half',\n } as RequestInit);\n };\n}\n\nasync function nodeRequestToFetch(req: import('node:http').IncomingMessage, tlsPort: number): Promise<Request> {\n const url = new URL(req.url ?? '/', `https://127.0.0.1:${tlsPort}`);\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const part of value) headers.append(key, part);\n } else {\n headers.set(key, value);\n }\n }\n\n const hasBody = req.method !== 'GET' && req.method !== 'HEAD';\n const init: RequestInit = {\n method: req.method,\n headers,\n };\n if (hasBody) {\n init.body = Readable.toWeb(req) as RequestInit['body'];\n (init as RequestInit & { duplex?: 'half' }).duplex = 'half';\n }\n return new Request(url, init);\n}\n\nasync function sendFetchResponse(res: import('node:http').ServerResponse, response: Response): Promise<void> {\n res.writeHead(response.status, Object.fromEntries(response.headers.entries()));\n if (response.body) {\n const reader = response.body.getReader();\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n res.write(value);\n }\n }\n res.end();\n}\n\nexport async function startTunnelTlsServer(config: TunnelTlsServerConfig): Promise<Server> {\n if (httpsNodeServer) return httpsNodeServer;\n\n const cert = await ensureValidCert(config.acmeConfig);\n const handler = resolveTlsFetch(config.gatewayPort, config.fetch);\n\n httpsNodeServer = createHttpsServer(\n {\n key: cert.keyPem,\n cert: cert.certPem,\n minVersion: 'TLSv1.2',\n },\n (req, res) => {\n void (async () => {\n try {\n const request = await nodeRequestToFetch(req, config.tlsPort);\n const response = await handler(request);\n await sendFetchResponse(res, response);\n } catch (err) {\n log.warn({ err, phase: 'tls_proxy' }, 'TLS proxy request failed');\n if (!res.headersSent) res.writeHead(502);\n res.end('Bad Gateway');\n }\n })();\n },\n );\n\n await new Promise<void>((resolve, reject) => {\n httpsNodeServer!.once('error', reject);\n httpsNodeServer!.listen(config.tlsPort, '127.0.0.1', () => resolve());\n }).catch((err: unknown) => {\n httpsNodeServer?.close();\n httpsNodeServer = null;\n const em = err instanceof Error ? err.message : String(err);\n if (em.includes('EADDRINUSE')) {\n throw new Error(\n `Tunnel TLS port ${config.tlsPort} is already in use. ` +\n `Stop other xopc tunnel instances or set tunnel.e2e.tlsPort (try ${config.gatewayPort + 1} for gateway port ${config.gatewayPort}).`,\n );\n }\n throw err;\n });\n\n log.info(\n { port: config.tlsPort, domain: cert.domain, expiresAt: cert.expiresAt },\n \"Tunnel TLS server listening (Let's Encrypt cert)\",\n );\n\n startRenewalScheduler(config.acmeConfig, () => reloadTlsCert());\n\n return httpsNodeServer;\n}\n\nfunction reloadTlsCert(): void {\n const cert = loadStoredCert();\n if (!cert || !httpsNodeServer) return;\n httpsNodeServer.setSecureContext({ key: cert.keyPem, cert: cert.certPem });\n log.info({ domain: cert.domain, expiresAt: cert.expiresAt }, 'TLS cert hot-reloaded');\n}\n\nexport function stopTunnelTlsServer(): void {\n stopRenewalScheduler();\n if (httpsNodeServer) {\n httpsNodeServer.close();\n httpsNodeServer = null;\n log.info('TLS server stopped');\n }\n}\n\nexport function getActiveTlsCert(): StoredCert | null {\n return loadStoredCert();\n}\n\n/** @internal */\nexport function resetTunnelTlsServerForTests(): void {\n stopTunnelTlsServer();\n}\n"],"mappings":"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"}
@@ -1,11 +0,0 @@
1
- import type { TunnelConfig } from '../config/schema.js';
2
- export type ResolvedTunnelE2eConfig = {
3
- enabled: boolean;
4
- tlsPort: number;
5
- staging: boolean;
6
- };
7
- /** Historical schema default — not suitable when gateway.port ≠ 18790 (e.g. Electron 28790). */
8
- export declare const LEGACY_DEFAULT_TUNNEL_TLS_PORT = 18791;
9
- export declare function resolveTunnelTlsPort(e2eTlsPort: number | undefined, gatewayPort: number): number;
10
- export declare function resolveTunnelE2eConfig(tunnel?: TunnelConfig, gatewayPort?: number): ResolvedTunnelE2eConfig;
11
- export declare function resolveFrpSubdomainHost(brokerUrl: string, override?: string): string;
@@ -1,29 +0,0 @@
1
- //#region src/tunnel/tunnel-e2e-config.ts
2
- /** Historical schema default — not suitable when gateway.port ≠ 18790 (e.g. Electron 28790). */
3
- const LEGACY_DEFAULT_TUNNEL_TLS_PORT = 18791;
4
- function resolveTunnelTlsPort(e2eTlsPort, gatewayPort) {
5
- if (e2eTlsPort === void 0) return gatewayPort + 1;
6
- if (e2eTlsPort === 18791 && gatewayPort !== 18790) return gatewayPort + 1;
7
- return e2eTlsPort;
8
- }
9
- function resolveTunnelE2eConfig(tunnel, gatewayPort = 18790) {
10
- const e2e = tunnel?.e2e;
11
- return {
12
- enabled: e2e?.enabled ?? true,
13
- tlsPort: resolveTunnelTlsPort(e2e?.tlsPort, gatewayPort),
14
- staging: e2e?.staging ?? false
15
- };
16
- }
17
- function resolveFrpSubdomainHost(brokerUrl, override) {
18
- if (override?.trim()) return override.trim();
19
- try {
20
- const host = new URL(brokerUrl.replace(/\/api\/?$/, "")).hostname;
21
- if (host === "frp.xopc.ai" || host.endsWith(".frp.xopc.ai")) return "frp.xopc.ai";
22
- if (host.includes(".")) return host;
23
- } catch {}
24
- return "frp.xopc.ai";
25
- }
26
- //#endregion
27
- export { LEGACY_DEFAULT_TUNNEL_TLS_PORT, resolveFrpSubdomainHost, resolveTunnelE2eConfig, resolveTunnelTlsPort };
28
-
29
- //# sourceMappingURL=tunnel-e2e-config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tunnel-e2e-config.js","names":[],"sources":["../../../src/tunnel/tunnel-e2e-config.ts"],"sourcesContent":["import type { TunnelConfig } from '../config/schema.js';\n\nexport type ResolvedTunnelE2eConfig = {\n enabled: boolean;\n tlsPort: number;\n staging: boolean;\n};\n\n/** Historical schema default — not suitable when gateway.port ≠ 18790 (e.g. Electron 28790). */\nexport const LEGACY_DEFAULT_TUNNEL_TLS_PORT = 18791;\n\nexport function resolveTunnelTlsPort(e2eTlsPort: number | undefined, gatewayPort: number): number {\n if (e2eTlsPort === undefined) {\n return gatewayPort + 1;\n }\n if (e2eTlsPort === LEGACY_DEFAULT_TUNNEL_TLS_PORT && gatewayPort !== 18790) {\n return gatewayPort + 1;\n }\n return e2eTlsPort;\n}\n\nexport function resolveTunnelE2eConfig(\n tunnel?: TunnelConfig,\n gatewayPort = 18790,\n): ResolvedTunnelE2eConfig {\n const e2e = tunnel?.e2e;\n return {\n enabled: e2e?.enabled ?? true,\n tlsPort: resolveTunnelTlsPort(e2e?.tlsPort, gatewayPort),\n staging: e2e?.staging ?? false,\n };\n}\n\nexport function resolveFrpSubdomainHost(brokerUrl: string, override?: string): string {\n if (override?.trim()) return override.trim();\n try {\n const host = new URL(brokerUrl.replace(/\\/api\\/?$/, '')).hostname;\n if (host === 'frp.xopc.ai' || host.endsWith('.frp.xopc.ai')) return 'frp.xopc.ai';\n if (host.includes('.')) return host;\n } catch {\n /* fall through */\n }\n return 'frp.xopc.ai';\n}\n"],"mappings":";;AASA,MAAa,iCAAiC;AAE9C,SAAgB,qBAAqB,YAAgC,aAA6B;AAChG,KAAI,eAAe,KAAA,EACjB,QAAO,cAAc;AAEvB,KAAI,eAAA,SAAiD,gBAAgB,MACnE,QAAO,cAAc;AAEvB,QAAO;;AAGT,SAAgB,uBACd,QACA,cAAc,OACW;CACzB,MAAM,MAAM,QAAQ;AACpB,QAAO;EACL,SAAS,KAAK,WAAW;EACzB,SAAS,qBAAqB,KAAK,SAAS,YAAY;EACxD,SAAS,KAAK,WAAW;EAC1B;;AAGH,SAAgB,wBAAwB,WAAmB,UAA2B;AACpF,KAAI,UAAU,MAAM,CAAE,QAAO,SAAS,MAAM;AAC5C,KAAI;EACF,MAAM,OAAO,IAAI,IAAI,UAAU,QAAQ,aAAa,GAAG,CAAC,CAAC;AACzD,MAAI,SAAS,iBAAiB,KAAK,SAAS,eAAe,CAAE,QAAO;AACpE,MAAI,KAAK,SAAS,IAAI,CAAE,QAAO;SACzB;AAGR,QAAO"}