enpilink 1.0.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.
- package/LICENSE +21 -0
- package/README.md +289 -0
- package/bin/run.js +5 -0
- package/dist/cli/build-helpers.d.ts +8 -0
- package/dist/cli/build-helpers.js +105 -0
- package/dist/cli/build-helpers.js.map +1 -0
- package/dist/cli/build-helpers.test.d.ts +1 -0
- package/dist/cli/build-helpers.test.js +100 -0
- package/dist/cli/build-helpers.test.js.map +1 -0
- package/dist/cli/detect-port.d.ts +18 -0
- package/dist/cli/detect-port.js +50 -0
- package/dist/cli/detect-port.js.map +1 -0
- package/dist/cli/ensure-ssh-key.d.ts +17 -0
- package/dist/cli/ensure-ssh-key.js +45 -0
- package/dist/cli/ensure-ssh-key.js.map +1 -0
- package/dist/cli/ensure-ssh-key.test.d.ts +1 -0
- package/dist/cli/ensure-ssh-key.test.js +68 -0
- package/dist/cli/ensure-ssh-key.test.js.map +1 -0
- package/dist/cli/header.d.ts +4 -0
- package/dist/cli/header.js +6 -0
- package/dist/cli/header.js.map +1 -0
- package/dist/cli/resolve-views-dir.d.ts +1 -0
- package/dist/cli/resolve-views-dir.js +17 -0
- package/dist/cli/resolve-views-dir.js.map +1 -0
- package/dist/cli/run-command.d.ts +2 -0
- package/dist/cli/run-command.js +43 -0
- package/dist/cli/run-command.js.map +1 -0
- package/dist/cli/telemetry.d.ts +14 -0
- package/dist/cli/telemetry.js +24 -0
- package/dist/cli/telemetry.js.map +1 -0
- package/dist/cli/tunnel-control-server.d.ts +11 -0
- package/dist/cli/tunnel-control-server.js +35 -0
- package/dist/cli/tunnel-control-server.js.map +1 -0
- package/dist/cli/tunnel-control-server.test.d.ts +1 -0
- package/dist/cli/tunnel-control-server.test.js +39 -0
- package/dist/cli/tunnel-control-server.test.js.map +1 -0
- package/dist/cli/tunnel-handler.d.ts +3 -0
- package/dist/cli/tunnel-handler.js +48 -0
- package/dist/cli/tunnel-handler.js.map +1 -0
- package/dist/cli/tunnel-handler.test.d.ts +1 -0
- package/dist/cli/tunnel-handler.test.js +107 -0
- package/dist/cli/tunnel-handler.test.js.map +1 -0
- package/dist/cli/tunnel-providers/index.d.ts +5 -0
- package/dist/cli/tunnel-providers/index.js +5 -0
- package/dist/cli/tunnel-providers/index.js.map +1 -0
- package/dist/cli/tunnel-providers/srv-us.d.ts +18 -0
- package/dist/cli/tunnel-providers/srv-us.js +66 -0
- package/dist/cli/tunnel-providers/srv-us.js.map +1 -0
- package/dist/cli/tunnel-providers/srv-us.test.d.ts +1 -0
- package/dist/cli/tunnel-providers/srv-us.test.js +74 -0
- package/dist/cli/tunnel-providers/srv-us.test.js.map +1 -0
- package/dist/cli/tunnel-providers/types.d.ts +49 -0
- package/dist/cli/tunnel-providers/types.js +2 -0
- package/dist/cli/tunnel-providers/types.js.map +1 -0
- package/dist/cli/tunnel.d.ts +75 -0
- package/dist/cli/tunnel.js +254 -0
- package/dist/cli/tunnel.js.map +1 -0
- package/dist/cli/tunnel.test.d.ts +1 -0
- package/dist/cli/tunnel.test.js +255 -0
- package/dist/cli/tunnel.test.js.map +1 -0
- package/dist/cli/types.d.ts +5 -0
- package/dist/cli/types.js +2 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli/use-execute-steps.d.ts +11 -0
- package/dist/cli/use-execute-steps.js +36 -0
- package/dist/cli/use-execute-steps.js.map +1 -0
- package/dist/cli/use-messages.d.ts +3 -0
- package/dist/cli/use-messages.js +11 -0
- package/dist/cli/use-messages.js.map +1 -0
- package/dist/cli/use-nodemon.d.ts +2 -0
- package/dist/cli/use-nodemon.js +73 -0
- package/dist/cli/use-nodemon.js.map +1 -0
- package/dist/cli/use-open-browser.d.ts +1 -0
- package/dist/cli/use-open-browser.js +44 -0
- package/dist/cli/use-open-browser.js.map +1 -0
- package/dist/cli/use-open-tunnel-browser.d.ts +6 -0
- package/dist/cli/use-open-tunnel-browser.js +19 -0
- package/dist/cli/use-open-tunnel-browser.js.map +1 -0
- package/dist/cli/use-tunnel.d.ts +17 -0
- package/dist/cli/use-tunnel.js +131 -0
- package/dist/cli/use-tunnel.js.map +1 -0
- package/dist/cli/use-typescript-check.d.ts +9 -0
- package/dist/cli/use-typescript-check.js +94 -0
- package/dist/cli/use-typescript-check.js.map +1 -0
- package/dist/commands/build.d.ts +8 -0
- package/dist/commands/build.js +97 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/create.d.ts +9 -0
- package/dist/commands/create.js +30 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dev.d.ts +13 -0
- package/dist/commands/dev.js +112 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/start.d.ts +10 -0
- package/dist/commands/start.js +76 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/telemetry/disable.d.ts +5 -0
- package/dist/commands/telemetry/disable.js +12 -0
- package/dist/commands/telemetry/disable.js.map +1 -0
- package/dist/commands/telemetry/enable.d.ts +5 -0
- package/dist/commands/telemetry/enable.js +12 -0
- package/dist/commands/telemetry/enable.js.map +1 -0
- package/dist/commands/telemetry/status.d.ts +5 -0
- package/dist/commands/telemetry/status.js +12 -0
- package/dist/commands/telemetry/status.js.map +1 -0
- package/dist/server/admin.d.ts +79 -0
- package/dist/server/admin.js +239 -0
- package/dist/server/admin.js.map +1 -0
- package/dist/server/admin.test.d.ts +1 -0
- package/dist/server/admin.test.js +226 -0
- package/dist/server/admin.test.js.map +1 -0
- package/dist/server/analytics.d.ts +60 -0
- package/dist/server/analytics.js +168 -0
- package/dist/server/analytics.js.map +1 -0
- package/dist/server/analytics.test.d.ts +1 -0
- package/dist/server/analytics.test.js +179 -0
- package/dist/server/analytics.test.js.map +1 -0
- package/dist/server/asset-base-url-transform-plugin.d.ts +11 -0
- package/dist/server/asset-base-url-transform-plugin.js +48 -0
- package/dist/server/asset-base-url-transform-plugin.js.map +1 -0
- package/dist/server/asset-base-url-transform-plugin.test.d.ts +1 -0
- package/dist/server/asset-base-url-transform-plugin.test.js +134 -0
- package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -0
- package/dist/server/auth.d.ts +20 -0
- package/dist/server/auth.js +28 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/build-manifest.test.d.ts +1 -0
- package/dist/server/build-manifest.test.js +27 -0
- package/dist/server/build-manifest.test.js.map +1 -0
- package/dist/server/config/config.test.d.ts +1 -0
- package/dist/server/config/config.test.js +214 -0
- package/dist/server/config/config.test.js.map +1 -0
- package/dist/server/config/index.d.ts +3 -0
- package/dist/server/config/index.js +4 -0
- package/dist/server/config/index.js.map +1 -0
- package/dist/server/config/resolve.d.ts +73 -0
- package/dist/server/config/resolve.js +167 -0
- package/dist/server/config/resolve.js.map +1 -0
- package/dist/server/config/router.d.ts +23 -0
- package/dist/server/config/router.js +119 -0
- package/dist/server/config/router.js.map +1 -0
- package/dist/server/config/schema.d.ts +78 -0
- package/dist/server/config/schema.js +158 -0
- package/dist/server/config/schema.js.map +1 -0
- package/dist/server/content-helpers.d.ts +67 -0
- package/dist/server/content-helpers.js +79 -0
- package/dist/server/content-helpers.js.map +1 -0
- package/dist/server/content-helpers.test.d.ts +1 -0
- package/dist/server/content-helpers.test.js +70 -0
- package/dist/server/content-helpers.test.js.map +1 -0
- package/dist/server/express.d.ts +11 -0
- package/dist/server/express.js +129 -0
- package/dist/server/express.js.map +1 -0
- package/dist/server/express.test.d.ts +1 -0
- package/dist/server/express.test.js +464 -0
- package/dist/server/express.test.js.map +1 -0
- package/dist/server/file-ref.d.ts +28 -0
- package/dist/server/file-ref.js +27 -0
- package/dist/server/file-ref.js.map +1 -0
- package/dist/server/index.d.ts +17 -0
- package/dist/server/index.js +14 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/inferUtilityTypes.d.ts +64 -0
- package/dist/server/inferUtilityTypes.js +2 -0
- package/dist/server/inferUtilityTypes.js.map +1 -0
- package/dist/server/log-sink.d.ts +16 -0
- package/dist/server/log-sink.js +66 -0
- package/dist/server/log-sink.js.map +1 -0
- package/dist/server/metric.d.ts +12 -0
- package/dist/server/metric.js +13 -0
- package/dist/server/metric.js.map +1 -0
- package/dist/server/middleware.d.ts +137 -0
- package/dist/server/middleware.js +93 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/middleware.test-d.d.ts +1 -0
- package/dist/server/middleware.test-d.js +75 -0
- package/dist/server/middleware.test-d.js.map +1 -0
- package/dist/server/middleware.test.d.ts +1 -0
- package/dist/server/middleware.test.js +493 -0
- package/dist/server/middleware.test.js.map +1 -0
- package/dist/server/mock-seed.d.ts +62 -0
- package/dist/server/mock-seed.js +251 -0
- package/dist/server/mock-seed.js.map +1 -0
- package/dist/server/mock-seed.test.d.ts +1 -0
- package/dist/server/mock-seed.test.js +122 -0
- package/dist/server/mock-seed.test.js.map +1 -0
- package/dist/server/observability.d.ts +149 -0
- package/dist/server/observability.js +340 -0
- package/dist/server/observability.js.map +1 -0
- package/dist/server/observability.test.d.ts +1 -0
- package/dist/server/observability.test.js +251 -0
- package/dist/server/observability.test.js.map +1 -0
- package/dist/server/otel.d.ts +45 -0
- package/dist/server/otel.js +117 -0
- package/dist/server/otel.js.map +1 -0
- package/dist/server/otel.test.d.ts +1 -0
- package/dist/server/otel.test.js +122 -0
- package/dist/server/otel.test.js.map +1 -0
- package/dist/server/server.d.ts +422 -0
- package/dist/server/server.js +684 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/storage/index.d.ts +23 -0
- package/dist/server/storage/index.js +46 -0
- package/dist/server/storage/index.js.map +1 -0
- package/dist/server/storage/memory.d.ts +30 -0
- package/dist/server/storage/memory.js +98 -0
- package/dist/server/storage/memory.js.map +1 -0
- package/dist/server/storage/memory.test.d.ts +1 -0
- package/dist/server/storage/memory.test.js +81 -0
- package/dist/server/storage/memory.test.js.map +1 -0
- package/dist/server/storage/postgres.d.ts +65 -0
- package/dist/server/storage/postgres.js +242 -0
- package/dist/server/storage/postgres.js.map +1 -0
- package/dist/server/storage/postgres.test.d.ts +1 -0
- package/dist/server/storage/postgres.test.js +182 -0
- package/dist/server/storage/postgres.test.js.map +1 -0
- package/dist/server/storage/sqlite.d.ts +33 -0
- package/dist/server/storage/sqlite.js +250 -0
- package/dist/server/storage/sqlite.js.map +1 -0
- package/dist/server/storage/sqlite.test.d.ts +1 -0
- package/dist/server/storage/sqlite.test.js +133 -0
- package/dist/server/storage/sqlite.test.js.map +1 -0
- package/dist/server/storage/types.d.ts +119 -0
- package/dist/server/storage/types.js +11 -0
- package/dist/server/storage/types.js.map +1 -0
- package/dist/server/templateHelper.d.ts +16 -0
- package/dist/server/templateHelper.js +11 -0
- package/dist/server/templateHelper.js.map +1 -0
- package/dist/server/templates.generated.d.ts +4 -0
- package/dist/server/templates.generated.js +47 -0
- package/dist/server/templates.generated.js.map +1 -0
- package/dist/server/tunnel-proxy-router.d.ts +7 -0
- package/dist/server/tunnel-proxy-router.js +110 -0
- package/dist/server/tunnel-proxy-router.js.map +1 -0
- package/dist/server/tunnel-proxy-router.test.d.ts +1 -0
- package/dist/server/tunnel-proxy-router.test.js +229 -0
- package/dist/server/tunnel-proxy-router.test.js.map +1 -0
- package/dist/server/viewsDevServer.d.ts +14 -0
- package/dist/server/viewsDevServer.js +45 -0
- package/dist/server/viewsDevServer.js.map +1 -0
- package/dist/test/utils.d.ts +127 -0
- package/dist/test/utils.js +247 -0
- package/dist/test/utils.js.map +1 -0
- package/dist/test/view.test.d.ts +1 -0
- package/dist/test/view.test.js +568 -0
- package/dist/test/view.test.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +54 -0
- package/dist/web/bridges/apps-sdk/adaptor.js +164 -0
- package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -0
- package/dist/web/bridges/apps-sdk/bridge.d.ts +11 -0
- package/dist/web/bridges/apps-sdk/bridge.js +47 -0
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -0
- package/dist/web/bridges/apps-sdk/index.d.ts +5 -0
- package/dist/web/bridges/apps-sdk/index.js +5 -0
- package/dist/web/bridges/apps-sdk/index.js.map +1 -0
- package/dist/web/bridges/apps-sdk/types.d.ts +147 -0
- package/dist/web/bridges/apps-sdk/types.js +10 -0
- package/dist/web/bridges/apps-sdk/types.js.map +1 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +13 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +18 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -0
- package/dist/web/bridges/get-adaptor.d.ts +9 -0
- package/dist/web/bridges/get-adaptor.js +15 -0
- package/dist/web/bridges/get-adaptor.js.map +1 -0
- package/dist/web/bridges/index.d.ts +5 -0
- package/dist/web/bridges/index.js +6 -0
- package/dist/web/bridges/index.js.map +1 -0
- package/dist/web/bridges/mcp-app/adaptor.d.ts +81 -0
- package/dist/web/bridges/mcp-app/adaptor.js +346 -0
- package/dist/web/bridges/mcp-app/adaptor.js.map +1 -0
- package/dist/web/bridges/mcp-app/bridge.d.ts +28 -0
- package/dist/web/bridges/mcp-app/bridge.js +124 -0
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -0
- package/dist/web/bridges/mcp-app/index.d.ts +4 -0
- package/dist/web/bridges/mcp-app/index.js +4 -0
- package/dist/web/bridges/mcp-app/index.js.map +1 -0
- package/dist/web/bridges/mcp-app/types.d.ts +8 -0
- package/dist/web/bridges/mcp-app/types.js +2 -0
- package/dist/web/bridges/mcp-app/types.js.map +1 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +19 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +19 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.d.ts +1 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +26 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -0
- package/dist/web/bridges/mcp-app/view-tools.test.d.ts +1 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js +144 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -0
- package/dist/web/bridges/types.d.ts +243 -0
- package/dist/web/bridges/types.js +2 -0
- package/dist/web/bridges/types.js.map +1 -0
- package/dist/web/bridges/use-host-context.d.ts +7 -0
- package/dist/web/bridges/use-host-context.js +13 -0
- package/dist/web/bridges/use-host-context.js.map +1 -0
- package/dist/web/components/modal-provider.d.ts +4 -0
- package/dist/web/components/modal-provider.js +45 -0
- package/dist/web/components/modal-provider.js.map +1 -0
- package/dist/web/create-store.d.ts +29 -0
- package/dist/web/create-store.js +64 -0
- package/dist/web/create-store.js.map +1 -0
- package/dist/web/create-store.test.d.ts +1 -0
- package/dist/web/create-store.test.js +129 -0
- package/dist/web/create-store.test.js.map +1 -0
- package/dist/web/data-llm.d.ts +47 -0
- package/dist/web/data-llm.js +100 -0
- package/dist/web/data-llm.js.map +1 -0
- package/dist/web/data-llm.test.d.ts +1 -0
- package/dist/web/data-llm.test.js +142 -0
- package/dist/web/data-llm.test.js.map +1 -0
- package/dist/web/generate-helpers.d.ts +120 -0
- package/dist/web/generate-helpers.js +115 -0
- package/dist/web/generate-helpers.js.map +1 -0
- package/dist/web/generate-helpers.test-d.d.ts +1 -0
- package/dist/web/generate-helpers.test-d.js +211 -0
- package/dist/web/generate-helpers.test-d.js.map +1 -0
- package/dist/web/generate-helpers.test.d.ts +1 -0
- package/dist/web/generate-helpers.test.js +17 -0
- package/dist/web/generate-helpers.test.js.map +1 -0
- package/dist/web/helpers/state.d.ts +7 -0
- package/dist/web/helpers/state.js +45 -0
- package/dist/web/helpers/state.js.map +1 -0
- package/dist/web/helpers/state.test.d.ts +1 -0
- package/dist/web/helpers/state.test.js +53 -0
- package/dist/web/helpers/state.test.js.map +1 -0
- package/dist/web/hooks/index.d.ts +17 -0
- package/dist/web/hooks/index.js +18 -0
- package/dist/web/hooks/index.js.map +1 -0
- package/dist/web/hooks/test/utils.d.ts +20 -0
- package/dist/web/hooks/test/utils.js +75 -0
- package/dist/web/hooks/test/utils.js.map +1 -0
- package/dist/web/hooks/use-call-tool.d.ts +146 -0
- package/dist/web/hooks/use-call-tool.js +96 -0
- package/dist/web/hooks/use-call-tool.js.map +1 -0
- package/dist/web/hooks/use-call-tool.test-d.d.ts +1 -0
- package/dist/web/hooks/use-call-tool.test-d.js +104 -0
- package/dist/web/hooks/use-call-tool.test-d.js.map +1 -0
- package/dist/web/hooks/use-call-tool.test.d.ts +1 -0
- package/dist/web/hooks/use-call-tool.test.js +211 -0
- package/dist/web/hooks/use-call-tool.test.js.map +1 -0
- package/dist/web/hooks/use-display-mode.d.ts +24 -0
- package/dist/web/hooks/use-display-mode.js +29 -0
- package/dist/web/hooks/use-display-mode.js.map +1 -0
- package/dist/web/hooks/use-display-mode.test-d.d.ts +1 -0
- package/dist/web/hooks/use-display-mode.test-d.js +8 -0
- package/dist/web/hooks/use-display-mode.test-d.js.map +1 -0
- package/dist/web/hooks/use-display-mode.test.d.ts +1 -0
- package/dist/web/hooks/use-display-mode.test.js +41 -0
- package/dist/web/hooks/use-display-mode.test.js.map +1 -0
- package/dist/web/hooks/use-download.d.ts +5 -0
- package/dist/web/hooks/use-download.js +8 -0
- package/dist/web/hooks/use-download.js.map +1 -0
- package/dist/web/hooks/use-download.test.d.ts +1 -0
- package/dist/web/hooks/use-download.test.js +95 -0
- package/dist/web/hooks/use-download.test.js.map +1 -0
- package/dist/web/hooks/use-files.d.ts +39 -0
- package/dist/web/hooks/use-files.js +42 -0
- package/dist/web/hooks/use-files.js.map +1 -0
- package/dist/web/hooks/use-files.test.d.ts +1 -0
- package/dist/web/hooks/use-files.test.js +54 -0
- package/dist/web/hooks/use-files.test.js.map +1 -0
- package/dist/web/hooks/use-intent.d.ts +30 -0
- package/dist/web/hooks/use-intent.js +34 -0
- package/dist/web/hooks/use-intent.js.map +1 -0
- package/dist/web/hooks/use-intent.test.d.ts +1 -0
- package/dist/web/hooks/use-intent.test.js +85 -0
- package/dist/web/hooks/use-intent.test.js.map +1 -0
- package/dist/web/hooks/use-layout.d.ts +24 -0
- package/dist/web/hooks/use-layout.js +25 -0
- package/dist/web/hooks/use-layout.js.map +1 -0
- package/dist/web/hooks/use-layout.test.d.ts +1 -0
- package/dist/web/hooks/use-layout.test.js +96 -0
- package/dist/web/hooks/use-layout.test.js.map +1 -0
- package/dist/web/hooks/use-notify.d.ts +29 -0
- package/dist/web/hooks/use-notify.js +33 -0
- package/dist/web/hooks/use-notify.js.map +1 -0
- package/dist/web/hooks/use-notify.test.d.ts +1 -0
- package/dist/web/hooks/use-notify.test.js +105 -0
- package/dist/web/hooks/use-notify.test.js.map +1 -0
- package/dist/web/hooks/use-open-external.d.ts +20 -0
- package/dist/web/hooks/use-open-external.js +24 -0
- package/dist/web/hooks/use-open-external.js.map +1 -0
- package/dist/web/hooks/use-open-external.test.d.ts +1 -0
- package/dist/web/hooks/use-open-external.test.js +65 -0
- package/dist/web/hooks/use-open-external.test.js.map +1 -0
- package/dist/web/hooks/use-register-view-tool.d.ts +38 -0
- package/dist/web/hooks/use-register-view-tool.js +50 -0
- package/dist/web/hooks/use-register-view-tool.js.map +1 -0
- package/dist/web/hooks/use-request-close.d.ts +16 -0
- package/dist/web/hooks/use-request-close.js +21 -0
- package/dist/web/hooks/use-request-close.js.map +1 -0
- package/dist/web/hooks/use-request-close.test.d.ts +1 -0
- package/dist/web/hooks/use-request-close.test.js +52 -0
- package/dist/web/hooks/use-request-close.test.js.map +1 -0
- package/dist/web/hooks/use-request-modal.d.ts +24 -0
- package/dist/web/hooks/use-request-modal.js +31 -0
- package/dist/web/hooks/use-request-modal.js.map +1 -0
- package/dist/web/hooks/use-request-modal.test.d.ts +1 -0
- package/dist/web/hooks/use-request-modal.test.js +61 -0
- package/dist/web/hooks/use-request-modal.test.js.map +1 -0
- package/dist/web/hooks/use-request-size.d.ts +20 -0
- package/dist/web/hooks/use-request-size.js +24 -0
- package/dist/web/hooks/use-request-size.js.map +1 -0
- package/dist/web/hooks/use-request-size.test.d.ts +1 -0
- package/dist/web/hooks/use-request-size.test.js +65 -0
- package/dist/web/hooks/use-request-size.test.js.map +1 -0
- package/dist/web/hooks/use-send-follow-up-message.d.ts +19 -0
- package/dist/web/hooks/use-send-follow-up-message.js +25 -0
- package/dist/web/hooks/use-send-follow-up-message.js.map +1 -0
- package/dist/web/hooks/use-set-open-in-app-url.d.ts +18 -0
- package/dist/web/hooks/use-set-open-in-app-url.js +25 -0
- package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -0
- package/dist/web/hooks/use-set-open-in-app-url.test.d.ts +1 -0
- package/dist/web/hooks/use-set-open-in-app-url.test.js +43 -0
- package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -0
- package/dist/web/hooks/use-tool-info.d.ts +87 -0
- package/dist/web/hooks/use-tool-info.js +49 -0
- package/dist/web/hooks/use-tool-info.js.map +1 -0
- package/dist/web/hooks/use-tool-info.test-d.d.ts +1 -0
- package/dist/web/hooks/use-tool-info.test-d.js +91 -0
- package/dist/web/hooks/use-tool-info.test-d.js.map +1 -0
- package/dist/web/hooks/use-tool-info.test.d.ts +1 -0
- package/dist/web/hooks/use-tool-info.test.js +130 -0
- package/dist/web/hooks/use-tool-info.test.js.map +1 -0
- package/dist/web/hooks/use-user.d.ts +20 -0
- package/dist/web/hooks/use-user.js +37 -0
- package/dist/web/hooks/use-user.js.map +1 -0
- package/dist/web/hooks/use-user.test.d.ts +1 -0
- package/dist/web/hooks/use-user.test.js +122 -0
- package/dist/web/hooks/use-user.test.js.map +1 -0
- package/dist/web/hooks/use-view-state.d.ts +25 -0
- package/dist/web/hooks/use-view-state.js +32 -0
- package/dist/web/hooks/use-view-state.js.map +1 -0
- package/dist/web/hooks/use-view-state.test.d.ts +1 -0
- package/dist/web/hooks/use-view-state.test.js +177 -0
- package/dist/web/hooks/use-view-state.test.js.map +1 -0
- package/dist/web/index.d.ts +7 -0
- package/dist/web/index.js +8 -0
- package/dist/web/index.js.map +1 -0
- package/dist/web/mount-view.d.ts +20 -0
- package/dist/web/mount-view.js +46 -0
- package/dist/web/mount-view.js.map +1 -0
- package/dist/web/plugin/data-llm.test.d.ts +1 -0
- package/dist/web/plugin/data-llm.test.js +81 -0
- package/dist/web/plugin/data-llm.test.js.map +1 -0
- package/dist/web/plugin/plugin.d.ts +33 -0
- package/dist/web/plugin/plugin.js +189 -0
- package/dist/web/plugin/plugin.js.map +1 -0
- package/dist/web/plugin/scan-views.d.ts +16 -0
- package/dist/web/plugin/scan-views.js +88 -0
- package/dist/web/plugin/scan-views.js.map +1 -0
- package/dist/web/plugin/scan-views.test.d.ts +1 -0
- package/dist/web/plugin/scan-views.test.js +99 -0
- package/dist/web/plugin/scan-views.test.js.map +1 -0
- package/dist/web/plugin/transform-data-llm.d.ts +12 -0
- package/dist/web/plugin/transform-data-llm.js +96 -0
- package/dist/web/plugin/transform-data-llm.js.map +1 -0
- package/dist/web/plugin/transform-data-llm.test.d.ts +1 -0
- package/dist/web/plugin/transform-data-llm.test.js +81 -0
- package/dist/web/plugin/transform-data-llm.test.js.map +1 -0
- package/dist/web/plugin/validate-view.d.ts +1 -0
- package/dist/web/plugin/validate-view.js +9 -0
- package/dist/web/plugin/validate-view.js.map +1 -0
- package/dist/web/plugin/validate-view.test.d.ts +1 -0
- package/dist/web/plugin/validate-view.test.js +24 -0
- package/dist/web/plugin/validate-view.test.js.map +1 -0
- package/dist/web/proxy.d.ts +1 -0
- package/dist/web/proxy.js +52 -0
- package/dist/web/proxy.js.map +1 -0
- package/dist/web/types.d.ts +20 -0
- package/dist/web/types.js +2 -0
- package/dist/web/types.js.map +1 -0
- package/package.json +125 -0
- package/scripts/postinstall.mjs +45 -0
- package/tsconfig.base.json +36 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import { Readable } from "node:stream";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { TunnelManager } from "./tunnel.js";
|
|
6
|
+
import { createTunnelHandler } from "./tunnel-handler.js";
|
|
7
|
+
let openServer;
|
|
8
|
+
afterEach(() => openServer?.close());
|
|
9
|
+
function makeFakeChild() {
|
|
10
|
+
const child = new EventEmitter();
|
|
11
|
+
child.stdout = new Readable({ read() { } });
|
|
12
|
+
child.stderr = new Readable({ read() { } });
|
|
13
|
+
child.kill = vi.fn(() => true);
|
|
14
|
+
return child;
|
|
15
|
+
}
|
|
16
|
+
async function listen(handler) {
|
|
17
|
+
const server = http.createServer(handler);
|
|
18
|
+
await new Promise((resolve) => server.listen(0, resolve));
|
|
19
|
+
const port = server.address().port;
|
|
20
|
+
return { port, server };
|
|
21
|
+
}
|
|
22
|
+
async function listenWithHandler() {
|
|
23
|
+
const child = makeFakeChild();
|
|
24
|
+
const manager = new TunnelManager({
|
|
25
|
+
getPort: () => 3000,
|
|
26
|
+
spawn: () => child,
|
|
27
|
+
});
|
|
28
|
+
const { port, server } = await listen(createTunnelHandler(manager));
|
|
29
|
+
openServer = server;
|
|
30
|
+
return { port, child, manager };
|
|
31
|
+
}
|
|
32
|
+
describe("createTunnelHandler", () => {
|
|
33
|
+
it("POST /__enpilink/tunnel starts the tunnel and returns the current state", async () => {
|
|
34
|
+
const { port, child } = await listenWithHandler();
|
|
35
|
+
const res = await fetch(`http://localhost:${port}/__enpilink/tunnel`, {
|
|
36
|
+
method: "POST",
|
|
37
|
+
});
|
|
38
|
+
expect(res.status).toBe(200);
|
|
39
|
+
expect(await res.json()).toEqual({
|
|
40
|
+
status: "starting",
|
|
41
|
+
message: "Starting tunnel…",
|
|
42
|
+
});
|
|
43
|
+
expect(child.kill).not.toHaveBeenCalled();
|
|
44
|
+
});
|
|
45
|
+
it("POST /__enpilink/tunnel is idempotent — second call does not respawn", async () => {
|
|
46
|
+
const { port, manager } = await listenWithHandler();
|
|
47
|
+
const startSpy = vi.spyOn(manager, "start");
|
|
48
|
+
await fetch(`http://localhost:${port}/__enpilink/tunnel`, {
|
|
49
|
+
method: "POST",
|
|
50
|
+
});
|
|
51
|
+
await fetch(`http://localhost:${port}/__enpilink/tunnel`, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
});
|
|
54
|
+
expect(startSpy).toHaveBeenCalledTimes(2);
|
|
55
|
+
// Manager.start() is internally idempotent (verified in tunnel.test.ts).
|
|
56
|
+
});
|
|
57
|
+
it("DELETE /__enpilink/tunnel stops the tunnel", async () => {
|
|
58
|
+
const { port, child } = await listenWithHandler();
|
|
59
|
+
await fetch(`http://localhost:${port}/__enpilink/tunnel`, {
|
|
60
|
+
method: "POST",
|
|
61
|
+
});
|
|
62
|
+
const res = await fetch(`http://localhost:${port}/__enpilink/tunnel`, {
|
|
63
|
+
method: "DELETE",
|
|
64
|
+
});
|
|
65
|
+
expect(res.status).toBe(200);
|
|
66
|
+
expect(await res.json()).toEqual({ status: "idle" });
|
|
67
|
+
expect(child.kill).toHaveBeenCalled();
|
|
68
|
+
});
|
|
69
|
+
it("GET /__enpilink/tunnel/events streams the current state on connect", async () => {
|
|
70
|
+
const { port, child } = await listenWithHandler();
|
|
71
|
+
await fetch(`http://localhost:${port}/__enpilink/tunnel`, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
});
|
|
74
|
+
child.stdout.emit("data", Buffer.from("https://abc123.srv.us/\n"));
|
|
75
|
+
const res = await fetch(`http://localhost:${port}/__enpilink/tunnel/events`);
|
|
76
|
+
expect(res.headers.get("content-type")).toMatch(/text\/event-stream/);
|
|
77
|
+
expect(res.body).toBeTruthy();
|
|
78
|
+
const reader = res.body.getReader();
|
|
79
|
+
const { value } = await reader.read();
|
|
80
|
+
const chunk = new TextDecoder().decode(value);
|
|
81
|
+
expect(chunk).toContain("event: state");
|
|
82
|
+
expect(chunk).toContain('"status":"connected"');
|
|
83
|
+
expect(chunk).toContain('"url":"https://abc123.srv.us"');
|
|
84
|
+
await reader.cancel();
|
|
85
|
+
});
|
|
86
|
+
it("GET /__enpilink/tunnel/events surfaces a drop as reconnecting state with the stderr detail", async () => {
|
|
87
|
+
const { port, child } = await listenWithHandler();
|
|
88
|
+
await fetch(`http://localhost:${port}/__enpilink/tunnel`, {
|
|
89
|
+
method: "POST",
|
|
90
|
+
});
|
|
91
|
+
child.stderr.emit("data", Buffer.from("boom: tunnel auth failed\n"));
|
|
92
|
+
// While the tunnel should be up, an unexpected close triggers auto-reconnect
|
|
93
|
+
// (it does NOT terminate as a hard error).
|
|
94
|
+
child.emit("close", 1);
|
|
95
|
+
const res = await fetch(`http://localhost:${port}/__enpilink/tunnel/events`);
|
|
96
|
+
expect(res.headers.get("content-type")).toMatch(/text\/event-stream/);
|
|
97
|
+
expect(res.body).toBeTruthy();
|
|
98
|
+
const reader = res.body.getReader();
|
|
99
|
+
const { value } = await reader.read();
|
|
100
|
+
const chunk = new TextDecoder().decode(value);
|
|
101
|
+
expect(chunk).toContain("event: state");
|
|
102
|
+
expect(chunk).toContain('"status":"reconnecting"');
|
|
103
|
+
expect(chunk).toContain("boom: tunnel auth failed");
|
|
104
|
+
await reader.cancel();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
//# sourceMappingURL=tunnel-handler.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-handler.test.js","sourceRoot":"","sources":["../../src/cli/tunnel-handler.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,IAAI,UAAmC,CAAC;AACxC,SAAS,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;AAQrC,SAAS,aAAa;IACpB,MAAM,KAAK,GAAG,IAAI,YAAY,EAAe,CAAC;IAC9C,KAAK,CAAC,MAAM,GAAG,IAAI,QAAQ,CAAC,EAAE,IAAI,KAAI,CAAC,EAAE,CAAC,CAAC;IAC3C,KAAK,CAAC,MAAM,GAAG,IAAI,QAAQ,CAAC,EAAE,IAAI,KAAI,CAAC,EAAE,CAAC,CAAC;IAC3C,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,EAAE,CAAgB,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC9C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,OAA6B;IACjD,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAChE,MAAM,IAAI,GAAI,MAAM,CAAC,OAAO,EAAuB,CAAC,IAAI,CAAC;IACzD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1B,CAAC;AAED,KAAK,UAAU,iBAAiB;IAC9B,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC;QAChC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI;QACnB,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK;KACnB,CAAC,CAAC;IACH,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,UAAU,GAAG,MAAM,CAAC;IACpB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAClC,CAAC;AAED,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;QACvF,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAElD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,oBAAoB,IAAI,oBAAoB,EAAE;YACpE,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC;YAC/B,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,kBAAkB;SAC5B,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAC;QACpD,MAAM,QAAQ,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAE5C,MAAM,KAAK,CAAC,oBAAoB,IAAI,oBAAoB,EAAE;YACxD,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;QACH,MAAM,KAAK,CAAC,oBAAoB,IAAI,oBAAoB,EAAE;YACxD,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;QAEH,MAAM,CAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC1C,yEAAyE;IAC3E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAClD,MAAM,KAAK,CAAC,oBAAoB,IAAI,oBAAoB,EAAE;YACxD,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,oBAAoB,IAAI,oBAAoB,EAAE;YACpE,MAAM,EAAE,QAAQ;SACjB,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAClD,MAAM,KAAK,CAAC,oBAAoB,IAAI,oBAAoB,EAAE;YACxD,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC;QAEnE,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,oBAAoB,IAAI,2BAA2B,CACpD,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;QACtE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;QAE9B,MAAM,MAAM,GAAI,GAAG,CAAC,IAAmC,CAAC,SAAS,EAAE,CAAC;QACpE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAE9C,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC;QAEzD,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4FAA4F,EAAE,KAAK,IAAI,EAAE;QAC1G,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAClD,MAAM,KAAK,CAAC,oBAAoB,IAAI,oBAAoB,EAAE;YACxD,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,CAAC;QACrE,6EAA6E;QAC7E,2CAA2C;QAC3C,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAEvB,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,oBAAoB,IAAI,2BAA2B,CACpD,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;QACtE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;QAE9B,MAAM,MAAM,GAAI,GAAG,CAAC,IAAmC,CAAC,SAAS,EAAE,CAAC;QACpE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAE9C,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;QACnD,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;QAEpD,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { EventEmitter } from \"node:events\";\nimport http from \"node:http\";\nimport { Readable } from \"node:stream\";\nimport { afterEach, describe, expect, it, vi } from \"vitest\";\nimport { TunnelManager } from \"./tunnel.js\";\nimport { createTunnelHandler } from \"./tunnel-handler.js\";\n\nlet openServer: http.Server | undefined;\nafterEach(() => openServer?.close());\n\ntype FakeChild = EventEmitter & {\n stdout: Readable;\n stderr: Readable;\n kill: ReturnType<typeof vi.fn<() => boolean>>;\n};\n\nfunction makeFakeChild(): FakeChild {\n const child = new EventEmitter() as FakeChild;\n child.stdout = new Readable({ read() {} });\n child.stderr = new Readable({ read() {} });\n child.kill = vi.fn<() => boolean>(() => true);\n return child;\n}\n\nasync function listen(handler: http.RequestListener) {\n const server = http.createServer(handler);\n await new Promise<void>((resolve) => server.listen(0, resolve));\n const port = (server.address() as { port: number }).port;\n return { port, server };\n}\n\nasync function listenWithHandler() {\n const child = makeFakeChild();\n const manager = new TunnelManager({\n getPort: () => 3000,\n spawn: () => child,\n });\n const { port, server } = await listen(createTunnelHandler(manager));\n openServer = server;\n return { port, child, manager };\n}\n\ndescribe(\"createTunnelHandler\", () => {\n it(\"POST /__enpilink/tunnel starts the tunnel and returns the current state\", async () => {\n const { port, child } = await listenWithHandler();\n\n const res = await fetch(`http://localhost:${port}/__enpilink/tunnel`, {\n method: \"POST\",\n });\n expect(res.status).toBe(200);\n expect(await res.json()).toEqual({\n status: \"starting\",\n message: \"Starting tunnel…\",\n });\n expect(child.kill).not.toHaveBeenCalled();\n });\n\n it(\"POST /__enpilink/tunnel is idempotent — second call does not respawn\", async () => {\n const { port, manager } = await listenWithHandler();\n const startSpy = vi.spyOn(manager, \"start\");\n\n await fetch(`http://localhost:${port}/__enpilink/tunnel`, {\n method: \"POST\",\n });\n await fetch(`http://localhost:${port}/__enpilink/tunnel`, {\n method: \"POST\",\n });\n\n expect(startSpy).toHaveBeenCalledTimes(2);\n // Manager.start() is internally idempotent (verified in tunnel.test.ts).\n });\n\n it(\"DELETE /__enpilink/tunnel stops the tunnel\", async () => {\n const { port, child } = await listenWithHandler();\n await fetch(`http://localhost:${port}/__enpilink/tunnel`, {\n method: \"POST\",\n });\n\n const res = await fetch(`http://localhost:${port}/__enpilink/tunnel`, {\n method: \"DELETE\",\n });\n expect(res.status).toBe(200);\n expect(await res.json()).toEqual({ status: \"idle\" });\n expect(child.kill).toHaveBeenCalled();\n });\n\n it(\"GET /__enpilink/tunnel/events streams the current state on connect\", async () => {\n const { port, child } = await listenWithHandler();\n await fetch(`http://localhost:${port}/__enpilink/tunnel`, {\n method: \"POST\",\n });\n child.stdout.emit(\"data\", Buffer.from(\"https://abc123.srv.us/\\n\"));\n\n const res = await fetch(\n `http://localhost:${port}/__enpilink/tunnel/events`,\n );\n expect(res.headers.get(\"content-type\")).toMatch(/text\\/event-stream/);\n expect(res.body).toBeTruthy();\n\n const reader = (res.body as ReadableStream<Uint8Array>).getReader();\n const { value } = await reader.read();\n const chunk = new TextDecoder().decode(value);\n\n expect(chunk).toContain(\"event: state\");\n expect(chunk).toContain('\"status\":\"connected\"');\n expect(chunk).toContain('\"url\":\"https://abc123.srv.us\"');\n\n await reader.cancel();\n });\n\n it(\"GET /__enpilink/tunnel/events surfaces a drop as reconnecting state with the stderr detail\", async () => {\n const { port, child } = await listenWithHandler();\n await fetch(`http://localhost:${port}/__enpilink/tunnel`, {\n method: \"POST\",\n });\n child.stderr.emit(\"data\", Buffer.from(\"boom: tunnel auth failed\\n\"));\n // While the tunnel should be up, an unexpected close triggers auto-reconnect\n // (it does NOT terminate as a hard error).\n child.emit(\"close\", 1);\n\n const res = await fetch(\n `http://localhost:${port}/__enpilink/tunnel/events`,\n );\n expect(res.headers.get(\"content-type\")).toMatch(/text\\/event-stream/);\n expect(res.body).toBeTruthy();\n\n const reader = (res.body as ReadableStream<Uint8Array>).getReader();\n const { value } = await reader.read();\n const chunk = new TextDecoder().decode(value);\n\n expect(chunk).toContain(\"event: state\");\n expect(chunk).toContain('\"status\":\"reconnecting\"');\n expect(chunk).toContain(\"boom: tunnel auth failed\");\n\n await reader.cancel();\n });\n});\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TunnelProvider } from "./types.js";
|
|
2
|
+
export { parseSrvUsLine, srvUsProvider, srvUsSshArgs } from "./srv-us.js";
|
|
3
|
+
export type { ParsedStdoutEvent, TunnelChildProcess, TunnelProvider, } from "./types.js";
|
|
4
|
+
/** The default tunnel provider. Account-free, only needs `ssh`. */
|
|
5
|
+
export declare const defaultProvider: TunnelProvider;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { srvUsProvider } from "./srv-us.js";
|
|
2
|
+
export { parseSrvUsLine, srvUsProvider, srvUsSshArgs } from "./srv-us.js";
|
|
3
|
+
/** The default tunnel provider. Account-free, only needs `ssh`. */
|
|
4
|
+
export const defaultProvider = srvUsProvider;
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/tunnel-providers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAO1E,mEAAmE;AACnE,MAAM,CAAC,MAAM,eAAe,GAAmB,aAAa,CAAC","sourcesContent":["import { srvUsProvider } from \"./srv-us.js\";\nimport type { TunnelProvider } from \"./types.js\";\n\nexport { parseSrvUsLine, srvUsProvider, srvUsSshArgs } from \"./srv-us.js\";\nexport type {\n ParsedStdoutEvent,\n TunnelChildProcess,\n TunnelProvider,\n} from \"./types.js\";\n\n/** The default tunnel provider. Account-free, only needs `ssh`. */\nexport const defaultProvider: TunnelProvider = srvUsProvider;\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ParsedStdoutEvent, TunnelProvider } from "./types.js";
|
|
2
|
+
export declare function parseSrvUsLine(line: string): ParsedStdoutEvent | null;
|
|
3
|
+
/**
|
|
4
|
+
* Build the ssh argv for a srv.us reverse tunnel.
|
|
5
|
+
* - `-i <keyPath>`: use enpilink's dedicated key (stable URL).
|
|
6
|
+
* - `StrictHostKeyChecking=accept-new`: trust srv.us on first connect, no prompt.
|
|
7
|
+
* - `ServerAliveInterval=30` / `ServerAliveCountMax=3`: detect dead links fast
|
|
8
|
+
* so auto-reconnect kicks in.
|
|
9
|
+
* - `ExitOnForwardFailure=yes`: fail (and let us reconnect) instead of hanging
|
|
10
|
+
* if the remote forward can't be set up.
|
|
11
|
+
* - `-R 1:localhost:<port>`: reverse-forward srv.us tunnel #1 to the dev server.
|
|
12
|
+
*/
|
|
13
|
+
export declare function srvUsSshArgs(port: number, keyPath: string): string[];
|
|
14
|
+
/**
|
|
15
|
+
* The default, account-free provider. Spawns `ssh … srv.us -R 1:localhost:<port>`
|
|
16
|
+
* after lazily ensuring the ed25519 key exists.
|
|
17
|
+
*/
|
|
18
|
+
export declare const srvUsProvider: TunnelProvider;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import spawn from "cross-spawn";
|
|
2
|
+
import { ensureSshKey } from "../ensure-ssh-key.js";
|
|
3
|
+
/**
|
|
4
|
+
* Matches a srv.us public URL anywhere in a stdout line. srv.us announces each
|
|
5
|
+
* forwarded port as `<n>: https://<hash>.srv.us/` (live-confirmed 2026-06-14),
|
|
6
|
+
* with NO `Forwarding:` prefix — so we scan for the URL itself anywhere in the
|
|
7
|
+
* line. Tolerant of GitHub/GitLab-style friendly subdomains (any host ending in
|
|
8
|
+
* `.srv.us`) and an optional trailing slash.
|
|
9
|
+
*/
|
|
10
|
+
const SRV_US_URL_RE = /(https?:\/\/[a-z0-9.-]+\.srv\.us)\/?/i;
|
|
11
|
+
export function parseSrvUsLine(line) {
|
|
12
|
+
const trimmed = line.trim();
|
|
13
|
+
if (!trimmed) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const match = trimmed.match(SRV_US_URL_RE);
|
|
17
|
+
if (match?.[1]) {
|
|
18
|
+
return { kind: "connected", url: match[1].replace(/\/$/, "") };
|
|
19
|
+
}
|
|
20
|
+
return { kind: "starting", message: trimmed };
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build the ssh argv for a srv.us reverse tunnel.
|
|
24
|
+
* - `-i <keyPath>`: use enpilink's dedicated key (stable URL).
|
|
25
|
+
* - `StrictHostKeyChecking=accept-new`: trust srv.us on first connect, no prompt.
|
|
26
|
+
* - `ServerAliveInterval=30` / `ServerAliveCountMax=3`: detect dead links fast
|
|
27
|
+
* so auto-reconnect kicks in.
|
|
28
|
+
* - `ExitOnForwardFailure=yes`: fail (and let us reconnect) instead of hanging
|
|
29
|
+
* if the remote forward can't be set up.
|
|
30
|
+
* - `-R 1:localhost:<port>`: reverse-forward srv.us tunnel #1 to the dev server.
|
|
31
|
+
*/
|
|
32
|
+
export function srvUsSshArgs(port, keyPath) {
|
|
33
|
+
return [
|
|
34
|
+
"-i",
|
|
35
|
+
keyPath,
|
|
36
|
+
"-o",
|
|
37
|
+
"StrictHostKeyChecking=accept-new",
|
|
38
|
+
"-o",
|
|
39
|
+
"ServerAliveInterval=30",
|
|
40
|
+
"-o",
|
|
41
|
+
"ServerAliveCountMax=3",
|
|
42
|
+
"-o",
|
|
43
|
+
"ExitOnForwardFailure=yes",
|
|
44
|
+
"srv.us",
|
|
45
|
+
"-R",
|
|
46
|
+
`1:localhost:${port}`,
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The default, account-free provider. Spawns `ssh … srv.us -R 1:localhost:<port>`
|
|
51
|
+
* after lazily ensuring the ed25519 key exists.
|
|
52
|
+
*/
|
|
53
|
+
export const srvUsProvider = {
|
|
54
|
+
name: "srv.us",
|
|
55
|
+
ensure() {
|
|
56
|
+
ensureSshKey();
|
|
57
|
+
},
|
|
58
|
+
spawn(port) {
|
|
59
|
+
const keyPath = ensureSshKey();
|
|
60
|
+
return spawn("ssh", srvUsSshArgs(port, keyPath), {
|
|
61
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
parseLine: parseSrvUsLine,
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=srv-us.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"srv-us.js","sourceRoot":"","sources":["../../../src/cli/tunnel-providers/srv-us.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,aAAa,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAOpD;;;;;;GAMG;AACH,MAAM,aAAa,GAAG,uCAAuC,CAAC;AAE9D,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC3C,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC;IACjE,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAChD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,OAAe;IACxD,OAAO;QACL,IAAI;QACJ,OAAO;QACP,IAAI;QACJ,kCAAkC;QAClC,IAAI;QACJ,wBAAwB;QACxB,IAAI;QACJ,uBAAuB;QACvB,IAAI;QACJ,0BAA0B;QAC1B,QAAQ;QACR,IAAI;QACJ,eAAe,IAAI,EAAE;KACtB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAmB;IAC3C,IAAI,EAAE,QAAQ;IACd,MAAM;QACJ,YAAY,EAAE,CAAC;IACjB,CAAC;IACD,KAAK,CAAC,IAAY;QAChB,MAAM,OAAO,GAAG,YAAY,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE;YAC/C,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;IACL,CAAC;IACD,SAAS,EAAE,cAAc;CAC1B,CAAC","sourcesContent":["import spawn from \"cross-spawn\";\nimport { ensureSshKey } from \"../ensure-ssh-key.js\";\nimport type {\n ParsedStdoutEvent,\n TunnelChildProcess,\n TunnelProvider,\n} from \"./types.js\";\n\n/**\n * Matches a srv.us public URL anywhere in a stdout line. srv.us announces each\n * forwarded port as `<n>: https://<hash>.srv.us/` (live-confirmed 2026-06-14),\n * with NO `Forwarding:` prefix — so we scan for the URL itself anywhere in the\n * line. Tolerant of GitHub/GitLab-style friendly subdomains (any host ending in\n * `.srv.us`) and an optional trailing slash.\n */\nconst SRV_US_URL_RE = /(https?:\\/\\/[a-z0-9.-]+\\.srv\\.us)\\/?/i;\n\nexport function parseSrvUsLine(line: string): ParsedStdoutEvent | null {\n const trimmed = line.trim();\n if (!trimmed) {\n return null;\n }\n\n const match = trimmed.match(SRV_US_URL_RE);\n if (match?.[1]) {\n return { kind: \"connected\", url: match[1].replace(/\\/$/, \"\") };\n }\n return { kind: \"starting\", message: trimmed };\n}\n\n/**\n * Build the ssh argv for a srv.us reverse tunnel.\n * - `-i <keyPath>`: use enpilink's dedicated key (stable URL).\n * - `StrictHostKeyChecking=accept-new`: trust srv.us on first connect, no prompt.\n * - `ServerAliveInterval=30` / `ServerAliveCountMax=3`: detect dead links fast\n * so auto-reconnect kicks in.\n * - `ExitOnForwardFailure=yes`: fail (and let us reconnect) instead of hanging\n * if the remote forward can't be set up.\n * - `-R 1:localhost:<port>`: reverse-forward srv.us tunnel #1 to the dev server.\n */\nexport function srvUsSshArgs(port: number, keyPath: string): string[] {\n return [\n \"-i\",\n keyPath,\n \"-o\",\n \"StrictHostKeyChecking=accept-new\",\n \"-o\",\n \"ServerAliveInterval=30\",\n \"-o\",\n \"ServerAliveCountMax=3\",\n \"-o\",\n \"ExitOnForwardFailure=yes\",\n \"srv.us\",\n \"-R\",\n `1:localhost:${port}`,\n ];\n}\n\n/**\n * The default, account-free provider. Spawns `ssh … srv.us -R 1:localhost:<port>`\n * after lazily ensuring the ed25519 key exists.\n */\nexport const srvUsProvider: TunnelProvider = {\n name: \"srv.us\",\n ensure() {\n ensureSshKey();\n },\n spawn(port: number): TunnelChildProcess {\n const keyPath = ensureSshKey();\n return spawn(\"ssh\", srvUsSshArgs(port, keyPath), {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n },\n parseLine: parseSrvUsLine,\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { parseSrvUsLine, srvUsProvider, srvUsSshArgs } from "./srv-us.js";
|
|
3
|
+
describe("parseSrvUsLine", () => {
|
|
4
|
+
it("extracts the URL from srv.us's real on-connect line (live-confirmed format)", () => {
|
|
5
|
+
// Confirmed against a live srv.us tunnel on 2026-06-14: each forwarded port
|
|
6
|
+
// is announced as `<n>: https://<hash>.srv.us/`.
|
|
7
|
+
expect(parseSrvUsLine("1: https://65xgxz2werq5kx7rj6fjndeol4.srv.us/")).toEqual({
|
|
8
|
+
kind: "connected",
|
|
9
|
+
url: "https://65xgxz2werq5kx7rj6fjndeol4.srv.us",
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
it("extracts a hash subdomain srv.us URL printed bare", () => {
|
|
13
|
+
expect(parseSrvUsLine("https://qp556ma755ktlag5b2xyt334ae.srv.us/")).toEqual({
|
|
14
|
+
kind: "connected",
|
|
15
|
+
url: "https://qp556ma755ktlag5b2xyt334ae.srv.us",
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
it("extracts the URL even when embedded in surrounding text", () => {
|
|
19
|
+
expect(parseSrvUsLine("Your service is available at https://abc.srv.us")).toEqual({
|
|
20
|
+
kind: "connected",
|
|
21
|
+
url: "https://abc.srv.us",
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
it("accepts friendly github/gitlab-style subdomains", () => {
|
|
25
|
+
expect(parseSrvUsLine("https://my-user.srv.us/")).toEqual({
|
|
26
|
+
kind: "connected",
|
|
27
|
+
url: "https://my-user.srv.us",
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
it("strips a trailing slash", () => {
|
|
31
|
+
expect(parseSrvUsLine("https://abc.srv.us/")).toEqual({
|
|
32
|
+
kind: "connected",
|
|
33
|
+
url: "https://abc.srv.us",
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
it("treats non-URL output as a starting/progress message", () => {
|
|
37
|
+
expect(parseSrvUsLine("Warning: Permanently added 'srv.us' to known hosts.")).toEqual({
|
|
38
|
+
kind: "starting",
|
|
39
|
+
message: "Warning: Permanently added 'srv.us' to known hosts.",
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
it("returns null for blank lines", () => {
|
|
43
|
+
expect(parseSrvUsLine("")).toBeNull();
|
|
44
|
+
expect(parseSrvUsLine(" ")).toBeNull();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe("srvUsSshArgs", () => {
|
|
48
|
+
it("builds the reverse-tunnel ssh argv with hardening options", () => {
|
|
49
|
+
expect(srvUsSshArgs(63189, "/home/u/.enpilink/id_ed25519")).toEqual([
|
|
50
|
+
"-i",
|
|
51
|
+
"/home/u/.enpilink/id_ed25519",
|
|
52
|
+
"-o",
|
|
53
|
+
"StrictHostKeyChecking=accept-new",
|
|
54
|
+
"-o",
|
|
55
|
+
"ServerAliveInterval=30",
|
|
56
|
+
"-o",
|
|
57
|
+
"ServerAliveCountMax=3",
|
|
58
|
+
"-o",
|
|
59
|
+
"ExitOnForwardFailure=yes",
|
|
60
|
+
"srv.us",
|
|
61
|
+
"-R",
|
|
62
|
+
"1:localhost:63189",
|
|
63
|
+
]);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe("srvUsProvider", () => {
|
|
67
|
+
it("is named srv.us and exposes ensure + spawn + parseLine", () => {
|
|
68
|
+
expect(srvUsProvider.name).toBe("srv.us");
|
|
69
|
+
expect(typeof srvUsProvider.ensure).toBe("function");
|
|
70
|
+
expect(typeof srvUsProvider.spawn).toBe("function");
|
|
71
|
+
expect(srvUsProvider.parseLine).toBe(parseSrvUsLine);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=srv-us.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"srv-us.test.js","sourceRoot":"","sources":["../../../src/cli/tunnel-providers/srv-us.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { parseSrvUsLine, srvUsProvider, srvUsSshArgs } from \"./srv-us.js\";\n\ndescribe(\"parseSrvUsLine\", () => {\n it(\"extracts the URL from srv.us's real on-connect line (live-confirmed format)\", () => {\n // Confirmed against a live srv.us tunnel on 2026-06-14: each forwarded port\n // is announced as `<n>: https://<hash>.srv.us/`.\n expect(\n parseSrvUsLine(\"1: https://65xgxz2werq5kx7rj6fjndeol4.srv.us/\"),\n ).toEqual({\n kind: \"connected\",\n url: \"https://65xgxz2werq5kx7rj6fjndeol4.srv.us\",\n });\n });\n\n it(\"extracts a hash subdomain srv.us URL printed bare\", () => {\n expect(\n parseSrvUsLine(\"https://qp556ma755ktlag5b2xyt334ae.srv.us/\"),\n ).toEqual({\n kind: \"connected\",\n url: \"https://qp556ma755ktlag5b2xyt334ae.srv.us\",\n });\n });\n\n it(\"extracts the URL even when embedded in surrounding text\", () => {\n expect(\n parseSrvUsLine(\"Your service is available at https://abc.srv.us\"),\n ).toEqual({\n kind: \"connected\",\n url: \"https://abc.srv.us\",\n });\n });\n\n it(\"accepts friendly github/gitlab-style subdomains\", () => {\n expect(parseSrvUsLine(\"https://my-user.srv.us/\")).toEqual({\n kind: \"connected\",\n url: \"https://my-user.srv.us\",\n });\n });\n\n it(\"strips a trailing slash\", () => {\n expect(parseSrvUsLine(\"https://abc.srv.us/\")).toEqual({\n kind: \"connected\",\n url: \"https://abc.srv.us\",\n });\n });\n\n it(\"treats non-URL output as a starting/progress message\", () => {\n expect(\n parseSrvUsLine(\"Warning: Permanently added 'srv.us' to known hosts.\"),\n ).toEqual({\n kind: \"starting\",\n message: \"Warning: Permanently added 'srv.us' to known hosts.\",\n });\n });\n\n it(\"returns null for blank lines\", () => {\n expect(parseSrvUsLine(\"\")).toBeNull();\n expect(parseSrvUsLine(\" \")).toBeNull();\n });\n});\n\ndescribe(\"srvUsSshArgs\", () => {\n it(\"builds the reverse-tunnel ssh argv with hardening options\", () => {\n expect(srvUsSshArgs(63189, \"/home/u/.enpilink/id_ed25519\")).toEqual([\n \"-i\",\n \"/home/u/.enpilink/id_ed25519\",\n \"-o\",\n \"StrictHostKeyChecking=accept-new\",\n \"-o\",\n \"ServerAliveInterval=30\",\n \"-o\",\n \"ServerAliveCountMax=3\",\n \"-o\",\n \"ExitOnForwardFailure=yes\",\n \"srv.us\",\n \"-R\",\n \"1:localhost:63189\",\n ]);\n });\n});\n\ndescribe(\"srvUsProvider\", () => {\n it(\"is named srv.us and exposes ensure + spawn + parseLine\", () => {\n expect(srvUsProvider.name).toBe(\"srv.us\");\n expect(typeof srvUsProvider.ensure).toBe(\"function\");\n expect(typeof srvUsProvider.spawn).toBe(\"function\");\n expect(srvUsProvider.parseLine).toBe(parseSrvUsLine);\n });\n});\n"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Readable } from "node:stream";
|
|
2
|
+
/**
|
|
3
|
+
* A spawned tunnel child process. Intentionally a structural subset of
|
|
4
|
+
* `ChildProcess` so it can be faked in tests without a real subprocess.
|
|
5
|
+
*/
|
|
6
|
+
export type TunnelChildProcess = {
|
|
7
|
+
stdout: Pick<Readable, "on"> | null;
|
|
8
|
+
stderr: Pick<Readable, "on"> | null;
|
|
9
|
+
kill: (signal?: NodeJS.Signals | number) => boolean;
|
|
10
|
+
on(event: "error", listener: (err: Error) => void): unknown;
|
|
11
|
+
on(event: "close", listener: (code: number | null) => void): unknown;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The result of parsing a single line of a provider's stdout.
|
|
15
|
+
* - `connected`: the public URL is now known and the tunnel is live.
|
|
16
|
+
* - `starting`: progress text emitted before the URL is known.
|
|
17
|
+
*/
|
|
18
|
+
export type ParsedStdoutEvent = {
|
|
19
|
+
kind: "connected";
|
|
20
|
+
url: string;
|
|
21
|
+
} | {
|
|
22
|
+
kind: "starting";
|
|
23
|
+
message: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* A pluggable tunnel backend. Encapsulates BOTH how the tunnel process is
|
|
27
|
+
* spawned AND how its stdout is parsed for the public URL, since each provider
|
|
28
|
+
* (srv.us, localhost.run, bore, cloudflared…) prints a different format.
|
|
29
|
+
*
|
|
30
|
+
* `TunnelManager` is provider-agnostic: it owns lifecycle/state/reconnect and
|
|
31
|
+
* delegates spawning + line parsing to the provider. Adding a provider therefore
|
|
32
|
+
* never touches the manager or any caller.
|
|
33
|
+
*/
|
|
34
|
+
export interface TunnelProvider {
|
|
35
|
+
/** Stable identifier, e.g. "srv.us". */
|
|
36
|
+
readonly name: string;
|
|
37
|
+
/**
|
|
38
|
+
* Ensure any prerequisites exist (e.g. an SSH key). Best-effort and lazy;
|
|
39
|
+
* called once before the first spawn. Optional — providers with no setup omit it.
|
|
40
|
+
*/
|
|
41
|
+
ensure?(): void | Promise<void>;
|
|
42
|
+
/** Spawn the tunnel for a local port and return the child process. */
|
|
43
|
+
spawn(port: number): TunnelChildProcess;
|
|
44
|
+
/**
|
|
45
|
+
* Parse one stdout line. Return `null` for lines that carry no signal so they
|
|
46
|
+
* can be ignored (or surfaced only under `--verbose`).
|
|
47
|
+
*/
|
|
48
|
+
parseLine(line: string): ParsedStdoutEvent | null;
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/cli/tunnel-providers/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Readable } from \"node:stream\";\n\n/**\n * A spawned tunnel child process. Intentionally a structural subset of\n * `ChildProcess` so it can be faked in tests without a real subprocess.\n */\nexport type TunnelChildProcess = {\n stdout: Pick<Readable, \"on\"> | null;\n stderr: Pick<Readable, \"on\"> | null;\n kill: (signal?: NodeJS.Signals | number) => boolean;\n on(event: \"error\", listener: (err: Error) => void): unknown;\n on(event: \"close\", listener: (code: number | null) => void): unknown;\n};\n\n/**\n * The result of parsing a single line of a provider's stdout.\n * - `connected`: the public URL is now known and the tunnel is live.\n * - `starting`: progress text emitted before the URL is known.\n */\nexport type ParsedStdoutEvent =\n | { kind: \"connected\"; url: string }\n | { kind: \"starting\"; message: string };\n\n/**\n * A pluggable tunnel backend. Encapsulates BOTH how the tunnel process is\n * spawned AND how its stdout is parsed for the public URL, since each provider\n * (srv.us, localhost.run, bore, cloudflared…) prints a different format.\n *\n * `TunnelManager` is provider-agnostic: it owns lifecycle/state/reconnect and\n * delegates spawning + line parsing to the provider. Adding a provider therefore\n * never touches the manager or any caller.\n */\nexport interface TunnelProvider {\n /** Stable identifier, e.g. \"srv.us\". */\n readonly name: string;\n /**\n * Ensure any prerequisites exist (e.g. an SSH key). Best-effort and lazy;\n * called once before the first spawn. Optional — providers with no setup omit it.\n */\n ensure?(): void | Promise<void>;\n /** Spawn the tunnel for a local port and return the child process. */\n spawn(port: number): TunnelChildProcess;\n /**\n * Parse one stdout line. Return `null` for lines that carry no signal so they\n * can be ignored (or surfaced only under `--verbose`).\n */\n parseLine(line: string): ParsedStdoutEvent | null;\n}\n"]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import { type ParsedStdoutEvent, type TunnelChildProcess, type TunnelProvider } from "./tunnel-providers/index.js";
|
|
3
|
+
export type { ParsedStdoutEvent, TunnelChildProcess, TunnelProvider, } from "./tunnel-providers/index.js";
|
|
4
|
+
export type TunnelState = {
|
|
5
|
+
status: "idle";
|
|
6
|
+
} | {
|
|
7
|
+
status: "starting";
|
|
8
|
+
message: string;
|
|
9
|
+
} | {
|
|
10
|
+
status: "connected";
|
|
11
|
+
url: string;
|
|
12
|
+
} | {
|
|
13
|
+
status: "reconnecting";
|
|
14
|
+
message: string;
|
|
15
|
+
} | {
|
|
16
|
+
status: "error";
|
|
17
|
+
message: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Parse a single stdout line using the default (srv.us) provider. Kept as a
|
|
21
|
+
* standalone export for tests and callers that want the default parser.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseStdoutLine(line: string): ParsedStdoutEvent | null;
|
|
24
|
+
export type TunnelActivity = {
|
|
25
|
+
time: string;
|
|
26
|
+
text: string;
|
|
27
|
+
level: "log" | "error";
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Legacy spawn injection. A bare `(port) => child` function; when provided it is
|
|
31
|
+
* adapted into a provider that uses the default (srv.us) line parser. New code
|
|
32
|
+
* should pass a full `provider` instead.
|
|
33
|
+
*/
|
|
34
|
+
export type SpawnFn = (port: number) => TunnelChildProcess;
|
|
35
|
+
export declare class TunnelManager extends EventEmitter {
|
|
36
|
+
private state;
|
|
37
|
+
private child;
|
|
38
|
+
private timeout;
|
|
39
|
+
private reconnectTimer;
|
|
40
|
+
private stderrBuffer;
|
|
41
|
+
private connected;
|
|
42
|
+
/** True between start() and stop(): governs whether drops trigger reconnect. */
|
|
43
|
+
private wantUp;
|
|
44
|
+
private reconnectAttempts;
|
|
45
|
+
private ensured;
|
|
46
|
+
private readonly getPort;
|
|
47
|
+
private readonly provider;
|
|
48
|
+
constructor(opts: {
|
|
49
|
+
getPort: () => number;
|
|
50
|
+
provider?: TunnelProvider;
|
|
51
|
+
/** @deprecated pass `provider` instead. */
|
|
52
|
+
spawn?: SpawnFn;
|
|
53
|
+
});
|
|
54
|
+
getState(): TunnelState;
|
|
55
|
+
subscribe(listener: (state: TunnelState) => void): () => void;
|
|
56
|
+
start(): void;
|
|
57
|
+
/**
|
|
58
|
+
* Spawn (or respawn) the tunnel child. `initialState` is what we broadcast
|
|
59
|
+
* while waiting for the URL — "starting" on first launch, "reconnecting" after
|
|
60
|
+
* a drop. Ensures provider prerequisites (e.g. SSH key) once, lazily.
|
|
61
|
+
*/
|
|
62
|
+
private spawnChild;
|
|
63
|
+
/**
|
|
64
|
+
* After an unexpected drop, broadcast "reconnecting" and respawn after an
|
|
65
|
+
* exponential backoff (capped). Reset attempts once a connection succeeds.
|
|
66
|
+
*/
|
|
67
|
+
private scheduleReconnect;
|
|
68
|
+
stop(): void;
|
|
69
|
+
private handleStdout;
|
|
70
|
+
private handleStderr;
|
|
71
|
+
private setState;
|
|
72
|
+
private emitActivity;
|
|
73
|
+
private clearConnectTimeout;
|
|
74
|
+
private clearReconnectTimer;
|
|
75
|
+
}
|