@xopcai/xopc 0.0.270 → 0.0.273
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.
- package/README.md +1 -5
- package/README.zh-CN.md +1 -5
- package/dist/browser-ext/dist/assets/{auth-cwEj8rFb.js → auth-DkwOUI6_.js} +132 -33
- package/dist/browser-ext/dist/background.js +2 -2
- package/dist/browser-ext/dist/sidepanel.html +1 -1
- package/dist/browser-ext/dist/sidepanel.js +46 -32
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-D_0FmdRj.js → activity-store-cF7IX2RP.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-IZvEXak_.js → agent-avatar-dicebear-BrgxX8nG.js} +1 -1
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-DvhuRCLA.js +1 -0
- package/dist/gateway/static/root/assets/agents-C0By2FIo.js +1 -0
- package/dist/gateway/static/root/assets/{api-O3TTQ4qN.js → api-CYkdUpkx.js} +1 -1
- package/dist/gateway/static/root/assets/app-link-BiBXVEvp.js +1 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-Cqqsbzri.js → app-management-settings-panel-DfoNzWIa.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-DaJyf32s.js → appearance-settings-czbLdCqj.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-BSJXKWcE.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-kDPnDTJI.js → archive-plugin-D14P13mX.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-CJ3VceKS.js → attachment-load-Dok_B1Gv.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-JwMtU8XB.js → automations-page-DM7ZEewI.js} +1 -1
- package/dist/gateway/static/root/assets/automations-workspace--1TsZf3O.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-DzyZXLBX.js → binary-plugins-CxNg6InF.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-kTt3gYIJ.js → block-editor-D22VcYnC.js} +3 -3
- package/dist/gateway/static/root/assets/{browser-automation-inputs-BKohPYR4.js → browser-automation-inputs-6NWTLEpd.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-ktQfFPk-.js → browser-automations-page-BrqySdbW.js} +2 -2
- package/dist/gateway/static/root/assets/browser-settings-page-DOHgQer_.js +8 -0
- package/dist/gateway/static/root/assets/capabilities-settings-panel-Bpse_5K-.js +8 -0
- package/dist/gateway/static/root/assets/channels-settings-yHsY174W.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-DrqzYmDr.js → channels-status-swr-BUZimNmp.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-CIFGGLGa.js → chat-terminal-dock-CZPBNpMI.js} +2 -2
- package/dist/gateway/static/root/assets/clock-3-DnrAnuyZ.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-B_wLKGnH.js +2 -0
- package/dist/gateway/static/root/assets/cron-expression-C26e13YU.js +2 -0
- package/dist/gateway/static/root/assets/date-picker-Da_0QyLe.js +1 -0
- package/dist/gateway/static/root/assets/{dependency-picker-D3vNIjRI.js → dependency-picker-D_djqzy7.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-fQwRb_3p.js → desktop-pet-Celmbw-T.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-DfOJ-GvT.js → desktop-pet-settings-CqNNgF45.js} +1 -1
- package/dist/gateway/static/root/assets/device-pairing-wizard-B5_w9qlm.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-Cn53jB5-.js → extension-debug-page-ku3kCjvH.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-IDpLCxpC.js → extension-page-C7FpHwL4.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-OMj2tU2_.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-config-swr-B7dcmP7Q.js → gateway-config-swr-6eb1vKE1.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-CH-6vkgN.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-JGhGMhVZ.js → gateway-startup-retry-DUFFCU8H.js} +1 -1
- package/dist/gateway/static/root/assets/git-branch-Dh9uxTot.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-CSFzOACM.js +1 -0
- package/dist/gateway/static/root/assets/home-page-BJebAKB-.js +1 -0
- package/dist/gateway/static/root/assets/{image-generation-api-BJLX7Qrh.js → image-generation-api-C1cHRBRp.js} +1 -1
- package/dist/gateway/static/root/assets/index-C6OWYFtQ.js +74 -0
- package/dist/gateway/static/root/assets/index-DKy1Qn1_.css +2 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CBvMTFFx.js → keyboard-shortcuts-settings-BuvD3iv8.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-CaC4-THD.js +2 -0
- package/dist/gateway/static/root/assets/{local-apps-page-APCMNDvU.js → local-apps-page-DdJ8zwBp.js} +1 -1
- package/dist/gateway/static/root/assets/locale-store-BBSCIl4I.js +1 -0
- package/dist/gateway/static/root/assets/logs-page-BaR2he0K.js +2 -0
- package/dist/gateway/static/root/assets/management-settings-r-iYWZFY.js +1 -0
- package/dist/gateway/static/root/assets/markdown-view-c5vtnYe8.js +201 -0
- package/dist/gateway/static/root/assets/{media-plugins-C10byeaa.js → media-plugins-D_a6GDY6.js} +1 -1
- package/dist/gateway/static/root/assets/message-circle-Badhj-4D.js +1 -0
- package/dist/gateway/static/root/assets/message-square-fVE7Ghj1.js +1 -0
- package/dist/gateway/static/root/assets/{messages-D0V_bgRP.js → messages-DjuBdDCn.js} +3 -3
- package/dist/gateway/static/root/assets/note-detail-panel-BV6nbgac.js +5 -0
- package/dist/gateway/static/root/assets/notes-page-BmLKoWwi.js +8 -0
- package/dist/gateway/static/root/assets/{page-tabs-qQ36zncF.js → page-tabs-C7Le9bSI.js} +1 -1
- package/dist/gateway/static/root/assets/popover-select-Dz_Qis2R.js +1 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-BiAJvvCo.js → preview-open-alternatives-B2pOxJBK.js} +1 -1
- package/dist/gateway/static/root/assets/{proactive-card-CcNBqHoH.js → proactive-card-BlQ1vCtV.js} +1 -1
- package/dist/gateway/static/root/assets/{proactive-page-C3K61jve.js → proactive-page-CghKTfYa.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-CunnDP4w.js → product-open-page-DpjiHgwu.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-CV8Lh296.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-COweOaJP.js +1 -0
- package/dist/gateway/static/root/assets/{registry-api-0JSegbNl.js → registry-api-CyQ4k1Ux.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-ChPPGVU5.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-bu_mwBTr.js → runtime-tools-settings-panel-Bl4ArLHf.js} +2 -2
- package/dist/gateway/static/root/assets/{schema-form-BE0sgZyz.js → schema-form-KQFmfWd4.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DgmHibOK.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-DV7kMco2.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-CBo517lV.js → settings-form-section-Dbnls9ty.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-DBPimx93.js → settings-page-CDKirYRG.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-SQmtWoOs.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-DdV75VTe.js → share-preview-page-DwgLjS1m.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-Cs5bUrYw.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-DyKrjq7F.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-Cn_-nvRo.js → skills-page-Bd0_ED_p.js} +1 -1
- package/dist/gateway/static/root/assets/skills-page.utils-rA-XT-YG.js +1 -0
- package/dist/gateway/static/root/assets/system-settings-panel-CSkMrHLz.js +1 -0
- package/dist/gateway/static/root/assets/task-detail-page-CEQU-L_G.js +1 -0
- package/dist/gateway/static/root/assets/{theme-store-1wryLgBQ.js → theme-store-DWpIEWen.js} +1 -1
- package/dist/gateway/static/root/assets/tunnel-api-COJcGnEi.js +1 -0
- package/dist/gateway/static/root/assets/{use-autosave-N_lm4FdD.js → use-autosave-DkQAIAuB.js} +1 -1
- package/dist/gateway/static/root/assets/user-model-page-BQAU55lf.js +2 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-DMsyVfri.js → voice-api-key-field-BrqcH0P-.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-QWVmiAp3.js → voice-config-api-BJ95Z3pT.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-BC0vAxp1.js → work-discovery-overlay-vfTWKm13.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Cz9jQIlP.js → workflow-run-setup-panel-CHdr_ng9.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-hwRJD7vR.js +6 -0
- package/dist/gateway/static/root/index.html +16 -11
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/device-pairing.js +2 -1
- package/dist/packages/gateway-contract/src/session-identity.js +37 -0
- package/dist/src/agent/memory/compaction-chunks.d.ts +18 -0
- package/dist/src/agent/memory/compaction-chunks.js +53 -0
- package/dist/src/agent/memory/compaction-request.d.ts +12 -0
- package/dist/src/agent/memory/compaction-request.js +72 -0
- package/dist/src/agent/memory/compaction.d.ts +3 -0
- package/dist/src/agent/memory/compaction.js +166 -118
- package/dist/src/agent/service.js +1 -0
- package/dist/src/browser/native-messaging-host.js +3 -1
- package/dist/src/browser/providers/browser-ext-install.d.ts +6 -0
- package/dist/src/browser/providers/browser-ext-install.js +16 -1
- package/dist/src/cli/bin.js +2 -2
- package/dist/src/cli/commands/agent.js +2 -2
- package/dist/src/cli/commands/auth.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/extension-dev.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/extension.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/gateway/run-foreground.js +8 -5
- package/dist/src/cli/commands/gateway/service.js +4 -3
- package/dist/src/cli/commands/gateway/token.js +28 -1
- package/dist/src/cli/commands/gateway-ssh-tunnel.js +1 -1
- package/dist/src/cli/commands/models.js +2 -2
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/cli/commands/platform.js +1 -1
- package/dist/src/cli/commands/profile.js +1 -1
- package/dist/src/cli/commands/tunnel.js +6 -1
- package/dist/src/cli/commands/update.js +1 -1
- package/dist/src/cli/extension-cli-register.js +1 -1
- package/dist/src/config/index.js +1 -1
- package/dist/src/gateway/device-routes.js +4 -3
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/app.js +1 -0
- package/dist/src/gateway/hono/middleware/auth.d.ts +1 -2
- package/dist/src/gateway/hono/middleware/auth.js +49 -31
- package/dist/src/gateway/hono/middleware/strict-rate-limit.js +11 -15
- package/dist/src/gateway/hono/routes/browser-session.d.ts +3 -0
- package/dist/src/gateway/hono/routes/browser-session.js +43 -0
- package/dist/src/gateway/hono/routes/browser.js +26 -0
- package/dist/src/gateway/hono/routes/devices.js +61 -96
- package/dist/src/gateway/hono/routes/exposure.js +3 -7
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/hono/routes/models.js +60 -1
- package/dist/src/gateway/hono/routes/notes.js +3 -7
- package/dist/src/gateway/hono/routes/sessions.js +11 -0
- package/dist/src/gateway/hono/routes/shares.js +8 -20
- package/dist/src/gateway/hono/routes/site-shares.js +4 -8
- package/dist/src/gateway/hono/routes/tunnel.js +13 -13
- package/dist/src/gateway/image-generation-setup.d.ts +17 -0
- package/dist/src/gateway/image-generation-setup.js +30 -21
- package/dist/src/gateway/rate-limit/buckets.d.ts +3 -0
- package/dist/src/gateway/rate-limit/buckets.js +10 -0
- package/dist/src/gateway/run-loop.js +1 -1
- package/dist/src/gateway/runtime-config.js +2 -0
- package/dist/src/gateway/security/browser-session.d.ts +5 -0
- package/dist/src/gateway/security/browser-session.js +45 -0
- package/dist/src/gateway/security/gateway-principal.d.ts +1 -0
- package/dist/src/gateway/server.js +1 -1
- package/dist/src/gateway/service.d.ts +1 -0
- package/dist/src/gateway/service.js +11 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/infra/rate-limit/keyed-store.d.ts +3 -0
- package/dist/src/infra/rate-limit/keyed-store.js +9 -2
- package/dist/src/proactive/policy/service.d.ts +1 -1
- package/dist/src/realtime/runtime.d.ts +2 -1
- package/dist/src/realtime/runtime.js +9 -3
- package/dist/src/realtime/tickets.d.ts +1 -0
- package/dist/src/realtime/tickets.js +3 -0
- package/dist/src/session/store.d.ts +2 -0
- package/dist/src/session/store.js +17 -2
- package/dist/src/session/types.d.ts +2 -1
- package/dist/src/storage/sqlite/browser-session-repository.d.ts +17 -0
- package/dist/src/storage/sqlite/browser-session-repository.js +54 -0
- package/dist/src/storage/sqlite/device-pairing-approval.js +3 -4
- package/dist/src/storage/sqlite/device-pairing-repository.d.ts +4 -10
- package/dist/src/storage/sqlite/device-pairing-repository.js +10 -62
- package/dist/src/storage/sqlite/index.d.ts +1 -1
- package/dist/src/storage/sqlite/index.js +2 -2
- package/dist/src/storage/sqlite/migrations/164_browser_sessions.sql +8 -0
- package/dist/src/storage/sqlite/migrations/165_device_pairing_target.sql +40 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/storage/sqlite/session-identity-sql.d.ts +2 -0
- package/dist/src/storage/sqlite/session-identity-sql.js +18 -0
- package/dist/src/storage/sqlite/session-repository.js +26 -22
- package/dist/src/tunnel/auth-policy.d.ts +3 -0
- package/dist/src/tunnel/auth-policy.js +8 -0
- package/dist/src/tunnel/broker-client.d.ts +5 -15
- package/dist/src/tunnel/broker-client.js +37 -49
- package/dist/src/tunnel/frpc-binary.d.ts +8 -6
- package/dist/src/tunnel/frpc-binary.js +76 -88
- package/dist/src/tunnel/frpc-config.d.ts +2 -1
- package/dist/src/tunnel/frpc-config.js +29 -12
- package/dist/src/tunnel/frpc-release.d.ts +27 -0
- package/dist/src/tunnel/frpc-release.js +30 -0
- package/dist/src/tunnel/gateway-lifecycle.js +8 -2
- package/dist/src/tunnel/index.js +2 -1
- package/dist/src/tunnel/registration-proof.d.ts +19 -0
- package/dist/src/tunnel/registration-proof.js +37 -0
- package/dist/src/tunnel/tunnel-persist.js +3 -1
- package/dist/src/tunnel/tunnel-service.d.ts +1 -0
- package/dist/src/tunnel/tunnel-service.js +35 -8
- package/dist/src/tunnel/tunnel-state.js +11 -5
- package/dist/src/tunnel/tunnel-types.d.ts +2 -0
- package/dist/src/tunnel/well-known.js +5 -1
- package/dist/src/user-context/config.js +2 -2
- package/dist/src/utils/logger/redact.js +4 -0
- package/dist/src/voice/realtime/runtime.d.ts +1 -0
- package/dist/src/voice/realtime/runtime.js +17 -1
- package/dist/src/voice/realtime/runtime.types.d.ts +1 -0
- package/dist/src/workflows/service/workflow-run-service.js +2 -0
- package/dist/src/workflows/service/workflow-session-bridge.d.ts +1 -0
- package/dist/src/workflows/service/workflow-session-bridge.js +1 -0
- package/package.json +3 -3
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-CE3ygr3l.js +0 -1
- package/dist/gateway/static/root/assets/agents-DzG2qw-c.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-B4lX6yoT.js +0 -1
- package/dist/gateway/static/root/assets/automations-workspace-BiNBewg0.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-Dgdt5uVL.js +0 -8
- package/dist/gateway/static/root/assets/capabilities-settings-panel-BpDW-R9f.js +0 -7
- package/dist/gateway/static/root/assets/channels-settings-DU4EQnR0.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-CfJqy284.js +0 -2
- package/dist/gateway/static/root/assets/cron-expression-BNwbnrEb.js +0 -2
- package/dist/gateway/static/root/assets/date-picker-DKyirU4K.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-B1dds2bP.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-pp9jN0wl.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-B8_zTyev.js +0 -1
- package/dist/gateway/static/root/assets/home-page-g1KdAdLb.js +0 -1
- package/dist/gateway/static/root/assets/index-1zRt56Zt.css +0 -2
- package/dist/gateway/static/root/assets/index-OMnem2nr.js +0 -74
- package/dist/gateway/static/root/assets/local-app-workbench-page-bj75v2ZE.js +0 -2
- package/dist/gateway/static/root/assets/locale-store-BThkG0W5.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-BX7n39zV.js +0 -2
- package/dist/gateway/static/root/assets/management-settings-CM62j8ZX.js +0 -1
- package/dist/gateway/static/root/assets/markdown-view-BwjpJVPs.js +0 -201
- package/dist/gateway/static/root/assets/mobile-pairing-wizard-BCWfwVx3.js +0 -1
- package/dist/gateway/static/root/assets/note-detail-panel-koOqb0gX.js +0 -5
- package/dist/gateway/static/root/assets/notes-page-BH54rY3B.js +0 -8
- package/dist/gateway/static/root/assets/popover-select-CKBYnT3c.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-SL2g8MZY.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-BtxazT5J.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BSb7b67A.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-ljcS2KVz.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-CR9Y9LQm.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-J3AnooPW.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-AAm4Iwcp.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-pRCjP5sm.js +0 -1
- package/dist/gateway/static/root/assets/skills-page.utils-CfVAc_lu.js +0 -1
- package/dist/gateway/static/root/assets/system-settings-panel-B-pzalp-.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-CmrLy3PO.js +0 -1
- package/dist/gateway/static/root/assets/tunnel-api-Cgpnm2fL.js +0 -1
- package/dist/gateway/static/root/assets/user-model-page-u3QLDX6L.js +0 -2
- package/dist/gateway/static/root/assets/workflows-page-DA6OOK6X.js +0 -6
|
@@ -2,127 +2,115 @@ import { createLogger } from "../utils/logger/index.js";
|
|
|
2
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
3
|
import { init_paths, resolveBinDir } from "../config/paths.js";
|
|
4
4
|
import { extractFrpcFromReleaseArchive, frpcReleaseArchiveExtension, nodePlatformForFrpTarget } from "./frpc-extract.js";
|
|
5
|
-
import {
|
|
5
|
+
import { FRPC_RELEASES, FRPC_VERSION } from "./frpc-release.js";
|
|
6
6
|
import { join } from "node:path";
|
|
7
|
-
import { chmodSync, createWriteStream, existsSync, mkdirSync, rmSync } from "node:fs";
|
|
8
|
-
import {
|
|
7
|
+
import { chmodSync, createReadStream, createWriteStream, existsSync, mkdirSync, mkdtempSync, renameSync, rmSync } from "node:fs";
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
9
|
import { pipeline } from "node:stream/promises";
|
|
10
|
-
import { Readable } from "node:stream";
|
|
10
|
+
import { Readable, Transform } from "node:stream";
|
|
11
11
|
//#region src/tunnel/frpc-binary.ts
|
|
12
12
|
init_paths();
|
|
13
13
|
init_logger();
|
|
14
14
|
const log = createLogger("TunnelFrpc");
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
linux: "linux",
|
|
19
|
-
win32: "windows"
|
|
20
|
-
};
|
|
21
|
-
const ARCH_MAP = {
|
|
22
|
-
x64: "amd64",
|
|
23
|
-
arm64: "arm64",
|
|
24
|
-
ia32: "386"
|
|
25
|
-
};
|
|
26
|
-
function buildFrpcReleaseBasename(frpPlatform, arch, version = FRPC_VERSION) {
|
|
27
|
-
return `frp_${version}_${frpPlatform}_${arch}`;
|
|
15
|
+
const MAX_ARCHIVE_BYTES = 64 * 1024 * 1024;
|
|
16
|
+
function buildFrpcReleaseBasename(platform, arch, version = FRPC_VERSION) {
|
|
17
|
+
return `frp_${version}_${platform}_${arch}`;
|
|
28
18
|
}
|
|
29
|
-
function frpcDownloadUrlsForTarget(
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
return [
|
|
33
|
-
`https://frp.xopc.ai/bin/${folder}${ext}`,
|
|
34
|
-
`https://github.com/fatedier/frp/releases/download/v${version}/${folder}${ext}`,
|
|
35
|
-
`https://ghfast.top/https://github.com/fatedier/frp/releases/download/v${version}/${folder}${ext}`
|
|
36
|
-
];
|
|
19
|
+
function frpcDownloadUrlsForTarget(platform, arch, version = FRPC_VERSION) {
|
|
20
|
+
const archive = buildFrpcReleaseBasename(platform, arch, version) + frpcReleaseArchiveExtension(platform);
|
|
21
|
+
return [`https://frp.xopc.ai/bin/${archive}`, `https://github.com/fatedier/frp/releases/download/v${version}/${archive}`];
|
|
37
22
|
}
|
|
38
|
-
function
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
platform,
|
|
43
|
-
arch,
|
|
44
|
-
folder: buildFrpcReleaseBasename(platform, arch)
|
|
45
|
-
};
|
|
23
|
+
async function verifyFrpcDigest(path, expected) {
|
|
24
|
+
const hash = createHash("sha256");
|
|
25
|
+
await pipeline(createReadStream(path), hash);
|
|
26
|
+
if (hash.digest("hex") !== expected) throw new Error("COMPONENT_VERIFY_FAILED: frpc checksum mismatch");
|
|
46
27
|
}
|
|
47
|
-
function
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
28
|
+
async function downloadToFile(url, path, onProgress) {
|
|
29
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(6e4) });
|
|
30
|
+
if (!response.ok || !response.body) throw new Error(`frpc download failed: ${response.status}`);
|
|
31
|
+
const length = Number(response.headers.get("content-length"));
|
|
32
|
+
const totalBytes = length > 0 ? length : null;
|
|
33
|
+
if (totalBytes && totalBytes > MAX_ARCHIVE_BYTES) {
|
|
34
|
+
await response.body.cancel();
|
|
35
|
+
throw new Error("frpc archive exceeds size limit");
|
|
36
|
+
}
|
|
56
37
|
let bytesReceived = 0;
|
|
57
|
-
const
|
|
38
|
+
const bounded = new Transform({ transform(chunk, _encoding, callback) {
|
|
39
|
+
bytesReceived += chunk.length;
|
|
40
|
+
if (bytesReceived > MAX_ARCHIVE_BYTES) return callback(/* @__PURE__ */ new Error("frpc archive exceeds size limit"));
|
|
58
41
|
onProgress?.({
|
|
59
42
|
phase: "downloading",
|
|
60
43
|
url,
|
|
61
44
|
bytesReceived,
|
|
62
45
|
totalBytes,
|
|
63
|
-
percent: totalBytes
|
|
46
|
+
percent: totalBytes ? Math.min(100, Math.round(bytesReceived / totalBytes * 100)) : null
|
|
64
47
|
});
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
nodeStream.on("data", (chunk) => {
|
|
69
|
-
bytesReceived += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
|
|
70
|
-
report();
|
|
71
|
-
});
|
|
72
|
-
await pipeline(nodeStream, createWriteStream(destPath));
|
|
73
|
-
report();
|
|
48
|
+
callback(null, chunk);
|
|
49
|
+
} });
|
|
50
|
+
await pipeline(Readable.fromWeb(response.body), bounded, createWriteStream(path, { mode: 384 }));
|
|
74
51
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
process.env.XOPC_FRPC_PATH = binPath;
|
|
52
|
+
function publishFrpcPathForProcess(path) {
|
|
53
|
+
process.env.XOPC_FRPC_PATH = path;
|
|
78
54
|
}
|
|
79
|
-
/** Clear runtime frpc path when the tunnel stops (no bundled Electron binary). */
|
|
80
55
|
function clearFrpcPathForProcess() {
|
|
81
56
|
delete process.env.XOPC_FRPC_PATH;
|
|
82
57
|
}
|
|
83
|
-
async function ensureFrpcBinary(opts) {
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
if (
|
|
88
|
-
const
|
|
89
|
-
|
|
58
|
+
async function ensureFrpcBinary(opts = {}) {
|
|
59
|
+
const platform = opts.platform ?? (process.platform === "win32" ? "windows" : process.platform);
|
|
60
|
+
const arch = opts.arch ?? (process.arch === "x64" ? "amd64" : process.arch);
|
|
61
|
+
const target = `${platform}_${arch}`;
|
|
62
|
+
if (!Object.hasOwn(FRPC_RELEASES, target)) throw new Error(`Unsupported frpc target: ${target}`);
|
|
63
|
+
const release = FRPC_RELEASES[target];
|
|
64
|
+
const fromEnv = !opts.platform && !opts.arch ? process.env.XOPC_FRPC_PATH?.trim() : void 0;
|
|
65
|
+
if (fromEnv) {
|
|
66
|
+
await verifyFrpcDigest(fromEnv, release.binary);
|
|
67
|
+
return fromEnv;
|
|
68
|
+
}
|
|
69
|
+
const cacheDir = join(resolveBinDir(), "frpc", FRPC_VERSION, target);
|
|
70
|
+
mkdirSync(cacheDir, {
|
|
71
|
+
recursive: true,
|
|
72
|
+
mode: 448
|
|
73
|
+
});
|
|
74
|
+
chmodSync(cacheDir, 448);
|
|
75
|
+
const binName = platform === "windows" ? "frpc.exe" : "frpc";
|
|
90
76
|
const cachePath = join(cacheDir, binName);
|
|
91
|
-
if (existsSync(cachePath))
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
77
|
+
if (existsSync(cachePath)) try {
|
|
78
|
+
await verifyFrpcDigest(cachePath, release.binary);
|
|
79
|
+
return cachePath;
|
|
80
|
+
} catch {
|
|
81
|
+
log.warn({ target }, "Discarding unverified frpc cache");
|
|
82
|
+
}
|
|
83
|
+
const temporary = mkdtempSync(join(cacheDir, ".download-"));
|
|
84
|
+
const archivePath = join(temporary, "release" + frpcReleaseArchiveExtension(platform));
|
|
85
|
+
const executablePath = join(temporary, binName);
|
|
86
|
+
let lastError;
|
|
98
87
|
try {
|
|
99
|
-
for (const url of
|
|
100
|
-
|
|
101
|
-
await
|
|
102
|
-
onProgress?.({
|
|
88
|
+
for (const url of frpcDownloadUrlsForTarget(platform, arch)) try {
|
|
89
|
+
await downloadToFile(url, archivePath, opts.onProgress);
|
|
90
|
+
await verifyFrpcDigest(archivePath, release.archive);
|
|
91
|
+
opts.onProgress?.({
|
|
103
92
|
phase: "extracting",
|
|
104
|
-
url
|
|
105
|
-
bytesReceived: 0,
|
|
106
|
-
totalBytes: null,
|
|
107
|
-
percent: null
|
|
93
|
+
url
|
|
108
94
|
});
|
|
109
|
-
await extractFrpcFromReleaseArchive(archivePath,
|
|
110
|
-
|
|
95
|
+
await extractFrpcFromReleaseArchive(archivePath, executablePath, buildFrpcReleaseBasename(platform, arch), nodePlatformForFrpTarget(platform));
|
|
96
|
+
await verifyFrpcDigest(executablePath, release.binary);
|
|
97
|
+
if (platform !== "windows") chmodSync(executablePath, 448);
|
|
98
|
+
renameSync(executablePath, cachePath);
|
|
111
99
|
return cachePath;
|
|
112
|
-
} catch (
|
|
113
|
-
|
|
100
|
+
} catch (error) {
|
|
101
|
+
lastError = error;
|
|
114
102
|
log.warn({
|
|
115
103
|
url,
|
|
116
|
-
err
|
|
117
|
-
}, "frpc download
|
|
104
|
+
err: error
|
|
105
|
+
}, "Verified frpc download failed");
|
|
118
106
|
}
|
|
119
107
|
} finally {
|
|
120
|
-
rmSync(
|
|
108
|
+
rmSync(temporary, {
|
|
121
109
|
recursive: true,
|
|
122
110
|
force: true
|
|
123
111
|
});
|
|
124
112
|
}
|
|
125
|
-
throw new Error(`Failed to
|
|
113
|
+
throw new Error(`Failed to obtain verified frpc for ${target}`, { cause: lastError });
|
|
126
114
|
}
|
|
127
115
|
//#endregion
|
|
128
|
-
export { FRPC_VERSION, buildFrpcReleaseBasename, clearFrpcPathForProcess, ensureFrpcBinary, frpcDownloadUrlsForTarget, publishFrpcPathForProcess };
|
|
116
|
+
export { FRPC_VERSION, buildFrpcReleaseBasename, clearFrpcPathForProcess, ensureFrpcBinary, frpcDownloadUrlsForTarget, publishFrpcPathForProcess, verifyFrpcDigest };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { TunnelRegistration } from './tunnel-types.js';
|
|
2
2
|
export declare function resolveFrpcConfigPath(tunnelId: string): string;
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function removeFrpcConfig(tunnelId: string): void;
|
|
4
|
+
export declare function writeFrpcConfig(registration: TunnelRegistration, localPort: number, localHost?: string): string;
|
|
@@ -1,34 +1,51 @@
|
|
|
1
1
|
import { resolveStateDir } from "../config/paths-state.js";
|
|
2
2
|
import { init_paths } from "../config/paths.js";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { chmodSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { rootCertificates } from "node:tls";
|
|
5
6
|
//#region src/tunnel/frpc-config.ts
|
|
6
7
|
init_paths();
|
|
7
8
|
function resolveFrpcConfigPath(tunnelId) {
|
|
9
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(tunnelId)) throw new Error("Invalid tunnel id");
|
|
8
10
|
const tmpDir = join(resolveStateDir(), "tmp");
|
|
9
|
-
mkdirSync(tmpDir, {
|
|
11
|
+
mkdirSync(tmpDir, {
|
|
12
|
+
recursive: true,
|
|
13
|
+
mode: 448
|
|
14
|
+
});
|
|
15
|
+
chmodSync(tmpDir, 448);
|
|
10
16
|
return join(tmpDir, `frpc-${tunnelId}.toml`);
|
|
11
17
|
}
|
|
12
|
-
function
|
|
18
|
+
function removeFrpcConfig(tunnelId) {
|
|
19
|
+
rmSync(resolveFrpcConfigPath(tunnelId), { force: true });
|
|
20
|
+
}
|
|
21
|
+
function writeFrpcConfig(registration, localPort, localHost = "127.0.0.1") {
|
|
13
22
|
const configPath = resolveFrpcConfigPath(registration.tunnelId);
|
|
23
|
+
const caPath = join(resolveStateDir(), "tmp", "frp-trusted-roots.pem");
|
|
24
|
+
writeFileSync(caPath, rootCertificates.join("\n"), { mode: 384 });
|
|
14
25
|
const { frpc } = registration;
|
|
26
|
+
for (const port of [localPort, frpc.serverPort]) if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error("Invalid tunnel port");
|
|
27
|
+
const quote = JSON.stringify;
|
|
15
28
|
writeFileSync(configPath, `# Auto-generated by xopc — do not edit
|
|
16
|
-
serverAddr =
|
|
29
|
+
serverAddr = ${quote(frpc.serverAddr)}
|
|
17
30
|
serverPort = ${frpc.serverPort}
|
|
18
|
-
metadatas.token =
|
|
19
|
-
|
|
31
|
+
metadatas.token = ${quote(frpc.authToken)}
|
|
20
32
|
loginFailExit = false
|
|
33
|
+
transport.tls.enable = true
|
|
34
|
+
transport.tls.serverName = ${quote(frpc.serverAddr)}
|
|
35
|
+
transport.tls.trustedCaFile = ${quote(caPath)}
|
|
21
36
|
transport.heartbeatInterval = 10
|
|
22
37
|
transport.heartbeatTimeout = 30
|
|
23
38
|
|
|
24
39
|
[[proxies]]
|
|
25
|
-
name =
|
|
26
|
-
type = "
|
|
27
|
-
localIP =
|
|
40
|
+
name = ${quote(frpc.proxyName)}
|
|
41
|
+
type = "http"
|
|
42
|
+
localIP = ${quote(localHost)}
|
|
28
43
|
localPort = ${localPort}
|
|
29
|
-
subdomain =
|
|
30
|
-
|
|
44
|
+
subdomain = ${quote(frpc.subdomain)}
|
|
45
|
+
hostHeaderRewrite = ${quote(new URL(registration.publicUrl).hostname)}
|
|
46
|
+
`, { mode: 384 });
|
|
47
|
+
chmodSync(configPath, 384);
|
|
31
48
|
return configPath;
|
|
32
49
|
}
|
|
33
50
|
//#endregion
|
|
34
|
-
export { resolveFrpcConfigPath, writeFrpcConfig };
|
|
51
|
+
export { removeFrpcConfig, resolveFrpcConfigPath, writeFrpcConfig };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const FRPC_VERSION = "0.62.1";
|
|
2
|
+
export declare const FRPC_RELEASES: {
|
|
3
|
+
readonly darwin_amd64: {
|
|
4
|
+
readonly archive: "f951a5aa727a4880f32753ba3c41ecc9dee38a63658760354011e71ea83db995";
|
|
5
|
+
readonly binary: "5ce5258b6ff1a232e9eb8e29247a55badb127e7fc66b5a58c299b442aba2bcb2";
|
|
6
|
+
};
|
|
7
|
+
readonly darwin_arm64: {
|
|
8
|
+
readonly archive: "f9fc616d994a87d790504da21c2f942cd4224637d9ade9a67482f3c23c7f2432";
|
|
9
|
+
readonly binary: "49afde483f55927c3eeac9141cae82857cb2f15b9e9d55f4ac45378e761eabcc";
|
|
10
|
+
};
|
|
11
|
+
readonly linux_amd64: {
|
|
12
|
+
readonly archive: "dcf1c62e5862543b6686a8e4c2f430ef636ac33306167f377e7272875020c4d3";
|
|
13
|
+
readonly binary: "030bff369048a49fce09e44f96e71fab36522394675ab10b2e684c9e3cc0aa26";
|
|
14
|
+
};
|
|
15
|
+
readonly linux_arm64: {
|
|
16
|
+
readonly archive: "94c98c19ce181f8e09e0d0cb79c278c31e9ec936eb713826a1ddf4995e7c2155";
|
|
17
|
+
readonly binary: "3f900ac9b035aac50b117ce5f7c450ca073d3e453448783979e978dc57bc39a9";
|
|
18
|
+
};
|
|
19
|
+
readonly windows_amd64: {
|
|
20
|
+
readonly archive: "a12e3b36b81232ad9888b43519a6dbcacc5c8def52f3a6c6e95d4fae6a373c5d";
|
|
21
|
+
readonly binary: "14cd52ca87fea607852b3a8e194146eeabe6ce87976cd1fc6327b74caebb6c77";
|
|
22
|
+
};
|
|
23
|
+
readonly windows_arm64: {
|
|
24
|
+
readonly archive: "da9e79e7cacc5570631196fae2f9e50ae5202521aa8abaf2e6de85b1778086b9";
|
|
25
|
+
readonly binary: "14b1b4bdd75228dc29dbc3ca264602911242a0e60cb0acb3311eb4926d50cb4b";
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/tunnel/frpc-release.ts
|
|
2
|
+
const FRPC_VERSION = "0.62.1";
|
|
3
|
+
const FRPC_RELEASES = {
|
|
4
|
+
"darwin_amd64": {
|
|
5
|
+
"archive": "f951a5aa727a4880f32753ba3c41ecc9dee38a63658760354011e71ea83db995",
|
|
6
|
+
"binary": "5ce5258b6ff1a232e9eb8e29247a55badb127e7fc66b5a58c299b442aba2bcb2"
|
|
7
|
+
},
|
|
8
|
+
"darwin_arm64": {
|
|
9
|
+
"archive": "f9fc616d994a87d790504da21c2f942cd4224637d9ade9a67482f3c23c7f2432",
|
|
10
|
+
"binary": "49afde483f55927c3eeac9141cae82857cb2f15b9e9d55f4ac45378e761eabcc"
|
|
11
|
+
},
|
|
12
|
+
"linux_amd64": {
|
|
13
|
+
"archive": "dcf1c62e5862543b6686a8e4c2f430ef636ac33306167f377e7272875020c4d3",
|
|
14
|
+
"binary": "030bff369048a49fce09e44f96e71fab36522394675ab10b2e684c9e3cc0aa26"
|
|
15
|
+
},
|
|
16
|
+
"linux_arm64": {
|
|
17
|
+
"archive": "94c98c19ce181f8e09e0d0cb79c278c31e9ec936eb713826a1ddf4995e7c2155",
|
|
18
|
+
"binary": "3f900ac9b035aac50b117ce5f7c450ca073d3e453448783979e978dc57bc39a9"
|
|
19
|
+
},
|
|
20
|
+
"windows_amd64": {
|
|
21
|
+
"archive": "a12e3b36b81232ad9888b43519a6dbcacc5c8def52f3a6c6e95d4fae6a373c5d",
|
|
22
|
+
"binary": "14cd52ca87fea607852b3a8e194146eeabe6ce87976cd1fc6327b74caebb6c77"
|
|
23
|
+
},
|
|
24
|
+
"windows_arm64": {
|
|
25
|
+
"archive": "da9e79e7cacc5570631196fae2f9e50ae5202521aa8abaf2e6de85b1778086b9",
|
|
26
|
+
"binary": "14b1b4bdd75228dc29dbc3ca264602911242a0e60cb0acb3311eb4926d50cb4b"
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
export { FRPC_RELEASES, FRPC_VERSION };
|
|
@@ -3,6 +3,8 @@ import { init_logger } from "../utils/logger.js";
|
|
|
3
3
|
import { resolveGatewayEffectiveHost } from "../config/gateway-bind.js";
|
|
4
4
|
import { resolveOptionalTunnelRegistrationSecret, resolveTunnelBrokerUrl } from "./env.js";
|
|
5
5
|
import { getTunnelService } from "./tunnel-service.js";
|
|
6
|
+
import { resolveGatewayAuth } from "../gateway/auth.js";
|
|
7
|
+
import { requireTunnelGatewayToken } from "./auth-policy.js";
|
|
6
8
|
import { hasValidTunnelConsent } from "./consent.js";
|
|
7
9
|
import { resolveFrpSubdomainHost } from "./frp-subdomain-host.js";
|
|
8
10
|
import { fetchTunnelWellKnown } from "./well-known.js";
|
|
@@ -99,7 +101,8 @@ async function maybeAutoStartTunnelFromConfig(config, gatewayToken) {
|
|
|
99
101
|
const port = (config.gateway ?? {}).port ?? 18790;
|
|
100
102
|
const host = resolveGatewayEffectiveHost(config);
|
|
101
103
|
await configureTunnelFromGatewayConfig(config);
|
|
102
|
-
|
|
104
|
+
const resolvedAuth = resolveGatewayAuth({ authConfig: config.gateway?.auth });
|
|
105
|
+
if (resolvedAuth.mode !== "token" || !gatewayToken?.trim()) {
|
|
103
106
|
log.warn({ phase: "tunnel_autostart" }, "tunnel.autoStart is enabled but gateway auth token is unavailable (auth mode may be none)");
|
|
104
107
|
return;
|
|
105
108
|
}
|
|
@@ -113,7 +116,10 @@ async function maybeAutoStartTunnelFromConfig(config, gatewayToken) {
|
|
|
113
116
|
return;
|
|
114
117
|
}
|
|
115
118
|
try {
|
|
116
|
-
await tunnel.start(port,
|
|
119
|
+
await tunnel.start(port, requireTunnelGatewayToken({
|
|
120
|
+
mode: resolvedAuth.mode,
|
|
121
|
+
token: gatewayToken
|
|
122
|
+
}));
|
|
117
123
|
log.info({
|
|
118
124
|
phase: "tunnel_autostart",
|
|
119
125
|
host,
|
package/dist/src/tunnel/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TunnelBrokerClient, resolveBrokerApiBase } from "./broker-client.js";
|
|
2
|
-
import { FRPC_VERSION
|
|
2
|
+
import { FRPC_VERSION } from "./frpc-release.js";
|
|
3
|
+
import { clearFrpcPathForProcess, ensureFrpcBinary, publishFrpcPathForProcess } from "./frpc-binary.js";
|
|
3
4
|
import { TUNNEL_REGISTRATION_SECRET_REQUIRED_CODE, TunnelRegistrationSecretError, getTunnelRegistrationSecretMeta, isLocalDevelopmentTunnelBroker, isMaskedTunnelSecretPatchValue, resolveOptionalTunnelRegistrationSecret, resolveTunnelBrokerUrl, resolveTunnelRegistrationSecret, tunnelRegistrationSecretRequiredMessage } from "./env.js";
|
|
4
5
|
import { loadTunnelState, resolveTunnelStatePath, saveTunnelState } from "./tunnel-state.js";
|
|
5
6
|
import { logTunnelAudit } from "./tunnel-audit.js";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Canonical v2 registration proof, shared with the Broker protocol test vectors. */
|
|
2
|
+
export declare function createTunnelRegistrationProof(input: {
|
|
3
|
+
brokerUrl: string;
|
|
4
|
+
platform: string;
|
|
5
|
+
gatewayVersion: string;
|
|
6
|
+
preferredSubdomain?: string;
|
|
7
|
+
recoveryToken?: string;
|
|
8
|
+
}): {
|
|
9
|
+
version: number;
|
|
10
|
+
gatewayId: string;
|
|
11
|
+
publicKey: string;
|
|
12
|
+
platform: string;
|
|
13
|
+
gatewayVersion: string;
|
|
14
|
+
preferredSubdomain: string;
|
|
15
|
+
recoveryToken: string;
|
|
16
|
+
nonce: string;
|
|
17
|
+
expiresAt: number;
|
|
18
|
+
signature: string;
|
|
19
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { getGatewayIdentityPublicKeyRaw, getOrCreateGatewayIdentity, signGatewayPayload } from "../storage/sqlite/gateway-identity-repository.js";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
//#region src/tunnel/registration-proof.ts
|
|
4
|
+
/** Canonical v2 registration proof, shared with the Broker protocol test vectors. */
|
|
5
|
+
function createTunnelRegistrationProof(input) {
|
|
6
|
+
const identity = getOrCreateGatewayIdentity();
|
|
7
|
+
const publicKey = getGatewayIdentityPublicKeyRaw(identity);
|
|
8
|
+
const nonce = randomBytes(24).toString("base64url");
|
|
9
|
+
const expiresAt = Date.now() + 12e4;
|
|
10
|
+
const audience = input.brokerUrl.replace(/\/+$/, "");
|
|
11
|
+
const payload = JSON.stringify([
|
|
12
|
+
"xopc-tunnel-registration-v2",
|
|
13
|
+
audience,
|
|
14
|
+
identity.id,
|
|
15
|
+
publicKey,
|
|
16
|
+
input.platform,
|
|
17
|
+
input.gatewayVersion,
|
|
18
|
+
input.preferredSubdomain ?? "",
|
|
19
|
+
input.recoveryToken ?? "",
|
|
20
|
+
nonce,
|
|
21
|
+
expiresAt
|
|
22
|
+
]);
|
|
23
|
+
return {
|
|
24
|
+
version: 2,
|
|
25
|
+
gatewayId: identity.id,
|
|
26
|
+
publicKey,
|
|
27
|
+
platform: input.platform,
|
|
28
|
+
gatewayVersion: input.gatewayVersion,
|
|
29
|
+
preferredSubdomain: input.preferredSubdomain,
|
|
30
|
+
recoveryToken: input.recoveryToken,
|
|
31
|
+
nonce,
|
|
32
|
+
expiresAt,
|
|
33
|
+
signature: signGatewayPayload(payload)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { createTunnelRegistrationProof };
|
|
@@ -8,6 +8,7 @@ function persistedFromRegistration(registration) {
|
|
|
8
8
|
frpcAuthToken: registration.frpc.authToken,
|
|
9
9
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10
10
|
enabled: true,
|
|
11
|
+
frpcSubdomain: registration.frpc.subdomain,
|
|
11
12
|
frpcServerAddr: registration.frpc.serverAddr,
|
|
12
13
|
frpcServerPort: registration.frpc.serverPort,
|
|
13
14
|
proxyName: registration.frpc.proxyName,
|
|
@@ -16,13 +17,14 @@ function persistedFromRegistration(registration) {
|
|
|
16
17
|
}
|
|
17
18
|
/** Rebuild registration from disk when Broker row is still valid (stop without deregister). */
|
|
18
19
|
function registrationFromPersisted(persisted) {
|
|
19
|
-
if (!persisted.frpcAuthToken || !persisted.frpcServerAddr || !persisted.frpcServerPort || !persisted.proxyName) return null;
|
|
20
|
+
if (!persisted.frpcSubdomain || !persisted.frpcAuthToken || !persisted.frpcServerAddr || !persisted.frpcServerPort || !persisted.proxyName) return null;
|
|
20
21
|
return {
|
|
21
22
|
tunnelId: persisted.tunnelId,
|
|
22
23
|
tunnelToken: persisted.tunnelToken,
|
|
23
24
|
subdomain: persisted.subdomain,
|
|
24
25
|
publicUrl: persisted.publicUrl,
|
|
25
26
|
frpc: {
|
|
27
|
+
subdomain: persisted.frpcSubdomain,
|
|
26
28
|
serverAddr: persisted.frpcServerAddr,
|
|
27
29
|
serverPort: persisted.frpcServerPort,
|
|
28
30
|
authToken: persisted.frpcAuthToken,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { PACKAGE_VERSION, init_package_version } from "../package-version.js";
|
|
2
2
|
import { createLogger } from "../utils/logger/index.js";
|
|
3
3
|
import { init_logger } from "../utils/logger.js";
|
|
4
|
-
import { TunnelBrokerClient, resolveBrokerApiBase } from "./broker-client.js";
|
|
4
|
+
import { TunnelBrokerClient, TunnelBrokerError, resolveBrokerApiBase } from "./broker-client.js";
|
|
5
5
|
import { clearFrpcPathForProcess, ensureFrpcBinary, publishFrpcPathForProcess } from "./frpc-binary.js";
|
|
6
|
-
import { writeFrpcConfig } from "./frpc-config.js";
|
|
6
|
+
import { removeFrpcConfig, writeFrpcConfig } from "./frpc-config.js";
|
|
7
7
|
import { spawnFrpcProcess } from "./frpc-process.js";
|
|
8
8
|
import { TunnelRegistrationSecretError } from "./env.js";
|
|
9
9
|
import { canResumePersistedTunnel, persistedFromRegistration, registrationFromPersisted } from "./tunnel-persist.js";
|
|
@@ -127,7 +127,7 @@ var TunnelService = class extends EventEmitter {
|
|
|
127
127
|
saveTunnelState(state);
|
|
128
128
|
this.setStartPhase("registering", { publicUrl: registration.publicUrl });
|
|
129
129
|
try {
|
|
130
|
-
const configPath = writeFrpcConfig(registration, gatewayPort, "127.0.0.1"
|
|
130
|
+
const configPath = writeFrpcConfig(registration, gatewayPort, "127.0.0.1");
|
|
131
131
|
this.setStartPhase("starting_frpc", { publicUrl: registration.publicUrl });
|
|
132
132
|
await this.spawnAndWait(frpcBin, configPath, broker, state, registration.heartbeatIntervalMs);
|
|
133
133
|
} catch (err) {
|
|
@@ -161,6 +161,7 @@ var TunnelService = class extends EventEmitter {
|
|
|
161
161
|
let released = false;
|
|
162
162
|
const persisted = loadTunnelState();
|
|
163
163
|
const cfg = this.serviceConfig;
|
|
164
|
+
if (persisted) removeFrpcConfig(persisted.tunnelId);
|
|
164
165
|
if (release && persisted && cfg) {
|
|
165
166
|
const broker = new TunnelBrokerClient(resolveBrokerApiBase(cfg.brokerUrl));
|
|
166
167
|
try {
|
|
@@ -206,6 +207,7 @@ var TunnelService = class extends EventEmitter {
|
|
|
206
207
|
return resumed;
|
|
207
208
|
}
|
|
208
209
|
} catch (err) {
|
|
210
|
+
if (!(err instanceof TunnelBrokerError) || err.status !== 410) throw err;
|
|
209
211
|
log.info({
|
|
210
212
|
err,
|
|
211
213
|
tunnelId: persisted.tunnelId,
|
|
@@ -218,7 +220,7 @@ var TunnelService = class extends EventEmitter {
|
|
|
218
220
|
registrationSecret: cfg.registrationSecret,
|
|
219
221
|
gatewayVersion: PACKAGE_VERSION,
|
|
220
222
|
platform: platformLabel(),
|
|
221
|
-
|
|
223
|
+
recoveryToken: persisted?.tunnelToken,
|
|
222
224
|
preferredSubdomain: persisted?.subdomain
|
|
223
225
|
});
|
|
224
226
|
}
|
|
@@ -285,19 +287,37 @@ var TunnelService = class extends EventEmitter {
|
|
|
285
287
|
}
|
|
286
288
|
}
|
|
287
289
|
}
|
|
290
|
+
heartbeatGeneration = 0;
|
|
288
291
|
startHeartbeat(broker, state, intervalMs) {
|
|
289
292
|
this.clearHeartbeat();
|
|
293
|
+
const generation = this.heartbeatGeneration;
|
|
294
|
+
let pending = false;
|
|
290
295
|
const tick = async () => {
|
|
296
|
+
if (pending || this.stopping || generation !== this.heartbeatGeneration) return;
|
|
297
|
+
pending = true;
|
|
291
298
|
try {
|
|
292
299
|
await broker.heartbeat(state.tunnelId, state.tunnelToken);
|
|
293
|
-
this.lastHeartbeatAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
300
|
+
if (generation === this.heartbeatGeneration) this.lastHeartbeatAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
294
301
|
} catch (err) {
|
|
295
302
|
log.warn({
|
|
296
303
|
err,
|
|
297
304
|
tunnelId: state.tunnelId
|
|
298
305
|
}, "Tunnel heartbeat failed");
|
|
299
|
-
if (this.stopping) return;
|
|
306
|
+
if (this.stopping || generation !== this.heartbeatGeneration) return;
|
|
307
|
+
if (!(err instanceof TunnelBrokerError) || ![
|
|
308
|
+
401,
|
|
309
|
+
403,
|
|
310
|
+
410
|
|
311
|
+
].includes(err.status)) return;
|
|
300
312
|
this.clearHeartbeat();
|
|
313
|
+
if (err.status === 401 || err.status === 403) {
|
|
314
|
+
await this.stop();
|
|
315
|
+
this.lastError = "TUNNEL_AUTH_REVOKED";
|
|
316
|
+
this.state = "error";
|
|
317
|
+
this.emit("tunnel:error", this.lastError);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const renewalGeneration = this.heartbeatGeneration;
|
|
301
321
|
const ctx = this.startContext;
|
|
302
322
|
const cfg = this.serviceConfig;
|
|
303
323
|
if (!ctx || !cfg) return;
|
|
@@ -308,25 +328,32 @@ var TunnelService = class extends EventEmitter {
|
|
|
308
328
|
registrationSecret: cfg.registrationSecret,
|
|
309
329
|
gatewayVersion: PACKAGE_VERSION,
|
|
310
330
|
platform: platformLabel(),
|
|
311
|
-
|
|
331
|
+
recoveryToken: state.tunnelToken,
|
|
312
332
|
preferredSubdomain: state.subdomain
|
|
313
333
|
});
|
|
334
|
+
if (this.stopping || renewalGeneration !== this.heartbeatGeneration) return;
|
|
314
335
|
const next = persistedFromRegistration(registration);
|
|
336
|
+
removeFrpcConfig(state.tunnelId);
|
|
315
337
|
saveTunnelState(next);
|
|
316
338
|
const frpcBin = await ensureFrpcBinary();
|
|
317
|
-
const configPath = writeFrpcConfig(registration, ctx.gatewayPort, "127.0.0.1"
|
|
339
|
+
const configPath = writeFrpcConfig(registration, ctx.gatewayPort, "127.0.0.1");
|
|
318
340
|
await this.spawnAndWait(frpcBin, configPath, broker, next, registration.heartbeatIntervalMs);
|
|
319
341
|
} catch (reErr) {
|
|
342
|
+
if (this.stopping || renewalGeneration !== this.heartbeatGeneration) return;
|
|
343
|
+
if (reErr instanceof TunnelBrokerError && [401, 403].includes(reErr.status)) await this.stop();
|
|
320
344
|
this.lastError = reErr instanceof Error ? reErr.message : String(reErr);
|
|
321
345
|
this.state = "error";
|
|
322
346
|
this.emit("tunnel:error", this.lastError);
|
|
323
347
|
}
|
|
348
|
+
} finally {
|
|
349
|
+
pending = false;
|
|
324
350
|
}
|
|
325
351
|
};
|
|
326
352
|
tick();
|
|
327
353
|
this.heartbeatTimer = setInterval(() => void tick(), intervalMs);
|
|
328
354
|
}
|
|
329
355
|
clearHeartbeat() {
|
|
356
|
+
this.heartbeatGeneration += 1;
|
|
330
357
|
if (this.heartbeatTimer) {
|
|
331
358
|
clearInterval(this.heartbeatTimer);
|
|
332
359
|
this.heartbeatTimer = null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolveStateDir } from "../config/paths-state.js";
|
|
2
2
|
import { init_paths } from "../config/paths.js";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
5
5
|
//#region src/tunnel/tunnel-state.ts
|
|
6
6
|
init_paths();
|
|
7
7
|
const TUNNEL_STATE_FILE = "tunnel.json";
|
|
@@ -22,8 +22,15 @@ function loadTunnelState() {
|
|
|
22
22
|
}
|
|
23
23
|
function saveTunnelState(state) {
|
|
24
24
|
const path = resolveTunnelStatePath();
|
|
25
|
-
mkdirSync(resolveStateDir(), {
|
|
26
|
-
|
|
25
|
+
mkdirSync(resolveStateDir(), {
|
|
26
|
+
recursive: true,
|
|
27
|
+
mode: 448
|
|
28
|
+
});
|
|
29
|
+
chmodSync(resolveStateDir(), 448);
|
|
30
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
31
|
+
writeFileSync(temporary, `${JSON.stringify(state, null, 2)}\n`, { mode: 384 });
|
|
32
|
+
chmodSync(temporary, 384);
|
|
33
|
+
renameSync(temporary, path);
|
|
27
34
|
}
|
|
28
35
|
function updateTunnelState(patch) {
|
|
29
36
|
const current = loadTunnelState();
|
|
@@ -36,8 +43,7 @@ function updateTunnelState(patch) {
|
|
|
36
43
|
return next;
|
|
37
44
|
}
|
|
38
45
|
function clearTunnelState() {
|
|
39
|
-
|
|
40
|
-
if (existsSync(path)) writeFileSync(path, "{}\n", "utf8");
|
|
46
|
+
rmSync(resolveTunnelStatePath(), { force: true });
|
|
41
47
|
}
|
|
42
48
|
//#endregion
|
|
43
49
|
export { clearTunnelState, loadTunnelState, resolveTunnelStatePath, saveTunnelState, updateTunnelState };
|
|
@@ -21,6 +21,7 @@ export type TunnelRegistration = {
|
|
|
21
21
|
serverPort: number;
|
|
22
22
|
authToken: string;
|
|
23
23
|
proxyName: string;
|
|
24
|
+
subdomain: string;
|
|
24
25
|
};
|
|
25
26
|
expiresAt: string;
|
|
26
27
|
heartbeatIntervalMs: number;
|
|
@@ -34,6 +35,7 @@ export type PersistedTunnelState = {
|
|
|
34
35
|
registeredAt: string;
|
|
35
36
|
enabled?: boolean;
|
|
36
37
|
/** Saved on register so stop/start can resume without re-registering. */
|
|
38
|
+
frpcSubdomain?: string;
|
|
37
39
|
frpcServerAddr?: string;
|
|
38
40
|
frpcServerPort?: number;
|
|
39
41
|
proxyName?: string;
|