@ripeseed/rs-tunnel 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -0
  3. package/dist/commands/doctor.d.ts +1 -0
  4. package/dist/commands/doctor.js +20 -0
  5. package/dist/commands/doctor.js.map +1 -0
  6. package/dist/commands/list.d.ts +1 -0
  7. package/dist/commands/list.js +18 -0
  8. package/dist/commands/list.js.map +1 -0
  9. package/dist/commands/login.d.ts +1 -0
  10. package/dist/commands/login.js +45 -0
  11. package/dist/commands/login.js.map +1 -0
  12. package/dist/commands/logout.d.ts +1 -0
  13. package/dist/commands/logout.js +19 -0
  14. package/dist/commands/logout.js.map +1 -0
  15. package/dist/commands/stop.d.ts +1 -0
  16. package/dist/commands/stop.js +15 -0
  17. package/dist/commands/stop.js.map +1 -0
  18. package/dist/commands/up.d.ts +32 -0
  19. package/dist/commands/up.js +319 -0
  20. package/dist/commands/up.js.map +1 -0
  21. package/dist/commands/up.test.d.ts +1 -0
  22. package/dist/commands/up.test.js +467 -0
  23. package/dist/commands/up.test.js.map +1 -0
  24. package/dist/config.d.ts +10 -0
  25. package/dist/config.js +135 -0
  26. package/dist/config.js.map +1 -0
  27. package/dist/config.test.d.ts +1 -0
  28. package/dist/config.test.js +67 -0
  29. package/dist/config.test.js.map +1 -0
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.js +75 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/lib/api-client.d.ts +73 -0
  34. package/dist/lib/api-client.js +146 -0
  35. package/dist/lib/api-client.js.map +1 -0
  36. package/dist/lib/cloudflared.d.ts +3 -0
  37. package/dist/lib/cloudflared.js +165 -0
  38. package/dist/lib/cloudflared.js.map +1 -0
  39. package/dist/lib/local-callback.d.ts +9 -0
  40. package/dist/lib/local-callback.js +68 -0
  41. package/dist/lib/local-callback.js.map +1 -0
  42. package/dist/lib/local-proxy.d.ts +27 -0
  43. package/dist/lib/local-proxy.js +200 -0
  44. package/dist/lib/local-proxy.js.map +1 -0
  45. package/dist/lib/local-proxy.test.d.ts +1 -0
  46. package/dist/lib/local-proxy.test.js +181 -0
  47. package/dist/lib/local-proxy.test.js.map +1 -0
  48. package/dist/lib/pkce.d.ts +4 -0
  49. package/dist/lib/pkce.js +10 -0
  50. package/dist/lib/pkce.js.map +1 -0
  51. package/dist/lib/session.d.ts +4 -0
  52. package/dist/lib/session.js +47 -0
  53. package/dist/lib/session.js.map +1 -0
  54. package/dist/lib/tunnel-stats.d.ts +19 -0
  55. package/dist/lib/tunnel-stats.js +75 -0
  56. package/dist/lib/tunnel-stats.js.map +1 -0
  57. package/dist/lib/tunnel-stats.test.d.ts +1 -0
  58. package/dist/lib/tunnel-stats.test.js +44 -0
  59. package/dist/lib/tunnel-stats.test.js.map +1 -0
  60. package/dist/lib/up-dashboard.d.ts +19 -0
  61. package/dist/lib/up-dashboard.js +170 -0
  62. package/dist/lib/up-dashboard.js.map +1 -0
  63. package/dist/lib/version.d.ts +1 -0
  64. package/dist/lib/version.js +18 -0
  65. package/dist/lib/version.js.map +1 -0
  66. package/dist/store/credentials.d.ts +4 -0
  67. package/dist/store/credentials.js +99 -0
  68. package/dist/store/credentials.js.map +1 -0
  69. package/dist/types.d.ts +10 -0
  70. package/dist/types.js +2 -0
  71. package/dist/types.js.map +1 -0
  72. package/package.json +51 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 RipeSeed
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @ripeseed/rs-tunnel
2
+
3
+ CLI for exposing your local HTTP service through a self-hosted `rs-tunnel` API and Cloudflare Tunnel.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i -g @ripeseed/rs-tunnel
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ # point CLI to your deployed API
15
+ export RS_TUNNEL_API_URL=https://api.your-domain.com
16
+
17
+ # authenticate
18
+ rs-tunnel login --email you@your-company.com
19
+
20
+ # expose local app on port 3000
21
+ rs-tunnel up --port 3000
22
+ ```
23
+
24
+ If no API URL is configured, the CLI prompts once and stores it in `~/.rs-tunnel/config.json`.
25
+
26
+ ## Commands
27
+
28
+ ```bash
29
+ rs-tunnel login --email <email> [--domain <api-url>]
30
+ rs-tunnel up --port <port> [--url <slug>] [--verbose] [--domain <api-url>]
31
+ rs-tunnel list [--domain <api-url>]
32
+ rs-tunnel stop <tunnel-id-or-hostname> [--domain <api-url>]
33
+ rs-tunnel logout [--domain <api-url>]
34
+ rs-tunnel doctor [--domain <api-url>]
35
+ ```
36
+
37
+ ## Configuration
38
+
39
+ - `RS_TUNNEL_API_URL`: preferred API base URL override.
40
+ - `RS_TUNNEL_API_BASE_URL`: legacy alias (still supported).
41
+ - `--domain`: command-level API override; also persists for future commands.
42
+
43
+ ## Notes
44
+
45
+ - This package is only the CLI. The API must be running separately.
46
+ - Cloudflare credentials stay on the API side; the CLI only receives short-lived tunnel tokens.
47
+
48
+ ## Troubleshooting
49
+
50
+ - Run `rs-tunnel doctor` to verify API reachability and local setup.
51
+ - Run `rs-tunnel up --verbose` to include raw `cloudflared` lines.
52
+
53
+ ## Repository
54
+
55
+ - Monorepo: https://github.com/RipeSeed/rs-tunnel
56
+ - Full project docs: https://github.com/RipeSeed/rs-tunnel/blob/main/README.md
57
+
@@ -0,0 +1 @@
1
+ export declare function doctorCommand(): Promise<void>;
@@ -0,0 +1,20 @@
1
+ import os from 'node:os';
2
+ import { getCliConfig } from '../config.js';
3
+ import { ApiClient } from '../lib/api-client.js';
4
+ import { findCloudflaredBinary, getCloudflaredVersion } from '../lib/cloudflared.js';
5
+ import { loadSession } from '../store/credentials.js';
6
+ export async function doctorCommand() {
7
+ const config = getCliConfig();
8
+ const apiClient = new ApiClient(config.apiBaseUrl);
9
+ const platform = `${os.platform()} ${os.arch()}`;
10
+ const osSupported = ['darwin', 'linux', 'win32'].includes(os.platform());
11
+ const apiHealthy = await apiClient.health();
12
+ const session = await loadSession();
13
+ const cloudflared = await findCloudflaredBinary();
14
+ const cloudflaredVersion = cloudflared ? getCloudflaredVersion(cloudflared) : null;
15
+ console.log(`OS: ${platform} (${osSupported ? 'supported' : 'unsupported'})`);
16
+ console.log(`API (${config.apiBaseUrl}): ${apiHealthy ? 'reachable' : 'unreachable'}`);
17
+ console.log(`Auth session: ${session ? `present (${session.profile.email})` : 'missing'}`);
18
+ console.log(`cloudflared: ${cloudflared ? `found at ${cloudflared}${cloudflaredVersion ? ` (${cloudflaredVersion})` : ''}` : 'not found'}`);
19
+ }
20
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACrF,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAEnD,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;IACjD,MAAM,WAAW,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;IAEzE,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,CAAC;IAC5C,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IACpC,MAAM,WAAW,GAAG,MAAM,qBAAqB,EAAE,CAAC;IAClD,MAAM,kBAAkB,GAAG,WAAW,CAAC,CAAC,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEnF,OAAO,CAAC,GAAG,CAAC,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC;IAC9E,OAAO,CAAC,GAAG,CAAC,QAAQ,MAAM,CAAC,UAAU,MAAM,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC;IACvF,OAAO,CAAC,GAAG,CAAC,iBAAiB,OAAO,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IAC3F,OAAO,CAAC,GAAG,CACT,gBAAgB,WAAW,CAAC,CAAC,CAAC,YAAY,WAAW,GAAG,kBAAkB,CAAC,CAAC,CAAC,KAAK,kBAAkB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAC/H,CAAC;AACJ,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function listCommand(): Promise<void>;
@@ -0,0 +1,18 @@
1
+ import { getCliConfig } from '../config.js';
2
+ import { ApiClient } from '../lib/api-client.js';
3
+ import { withAuthenticatedSession } from '../lib/session.js';
4
+ export async function listCommand() {
5
+ const config = getCliConfig();
6
+ const apiClient = new ApiClient(config.apiBaseUrl);
7
+ await withAuthenticatedSession(apiClient, async (session) => {
8
+ const tunnels = await apiClient.listTunnels(session.accessToken);
9
+ if (tunnels.length === 0) {
10
+ console.log('No active tunnels.');
11
+ return;
12
+ }
13
+ for (const tunnel of tunnels) {
14
+ console.log(`${tunnel.id} | ${tunnel.hostname} | status=${tunnel.status} | port=${tunnel.requestedPort} | created=${tunnel.createdAt}`);
15
+ }
16
+ });
17
+ }
18
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAEnD,MAAM,wBAAwB,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC1D,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAEjE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;YAClC,OAAO;QACT,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CACT,GAAG,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC,QAAQ,aAAa,MAAM,CAAC,MAAM,WAAW,MAAM,CAAC,aAAa,cAAc,MAAM,CAAC,SAAS,EAAE,CAC3H,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function loginCommand(email: string): Promise<void>;
@@ -0,0 +1,45 @@
1
+ import open from 'open';
2
+ import { getCliConfig } from '../config.js';
3
+ import { ApiClient } from '../lib/api-client.js';
4
+ import { startCallbackServer } from '../lib/local-callback.js';
5
+ import { createPkcePair } from '../lib/pkce.js';
6
+ import { saveSession } from '../store/credentials.js';
7
+ export async function loginCommand(email) {
8
+ const config = getCliConfig();
9
+ const apiClient = new ApiClient(config.apiBaseUrl);
10
+ const callbackServer = await startCallbackServer();
11
+ const pkce = createPkcePair();
12
+ try {
13
+ const auth = await apiClient.startSlackAuth({
14
+ email,
15
+ codeChallenge: pkce.challenge,
16
+ cliCallbackUrl: callbackServer.callbackUrl,
17
+ });
18
+ try {
19
+ await open(auth.authorizeUrl);
20
+ }
21
+ catch {
22
+ console.log(`Open this URL in your browser:\n${auth.authorizeUrl}`);
23
+ }
24
+ console.log('Waiting for Slack OAuth callback...');
25
+ const callback = await callbackServer.waitForCode();
26
+ if (callback.state !== auth.state) {
27
+ throw new Error('OAuth state mismatch. Aborting login.');
28
+ }
29
+ const tokenPair = await apiClient.exchangeLoginCode({
30
+ loginCode: callback.code,
31
+ codeVerifier: pkce.verifier,
32
+ });
33
+ await saveSession({
34
+ accessToken: tokenPair.accessToken,
35
+ refreshToken: tokenPair.refreshToken,
36
+ expiresAtEpochSec: Math.floor(Date.now() / 1000) + tokenPair.expiresInSec,
37
+ profile: tokenPair.profile,
38
+ });
39
+ console.log(`Logged in as ${tokenPair.profile.email}`);
40
+ }
41
+ finally {
42
+ await callbackServer.close();
43
+ }
44
+ }
45
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAa;IAC9C,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAE9B,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACnD,MAAM,cAAc,GAAG,MAAM,mBAAmB,EAAE,CAAC;IACnD,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAE9B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,cAAc,CAAC;YAC1C,KAAK;YACL,aAAa,EAAE,IAAI,CAAC,SAAS;YAC7B,cAAc,EAAE,cAAc,CAAC,WAAW;SAC3C,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,mCAAmC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;QACtE,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QAEnD,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,WAAW,EAAE,CAAC;QAEpD,IAAI,QAAQ,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,iBAAiB,CAAC;YAClD,SAAS,EAAE,QAAQ,CAAC,IAAI;YACxB,YAAY,EAAE,IAAI,CAAC,QAAQ;SAC5B,CAAC,CAAC;QAEH,MAAM,WAAW,CAAC;YAChB,WAAW,EAAE,SAAS,CAAC,WAAW;YAClC,YAAY,EAAE,SAAS,CAAC,YAAY;YACpC,iBAAiB,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,SAAS,CAAC,YAAY;YACzE,OAAO,EAAE,SAAS,CAAC,OAAO;SAC3B,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,gBAAgB,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACzD,CAAC;YAAS,CAAC;QACT,MAAM,cAAc,CAAC,KAAK,EAAE,CAAC;IAC/B,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function logoutCommand(): Promise<void>;
@@ -0,0 +1,19 @@
1
+ import { getCliConfig } from '../config.js';
2
+ import { ApiClient } from '../lib/api-client.js';
3
+ import { clearSession, loadSession } from '../store/credentials.js';
4
+ export async function logoutCommand() {
5
+ const config = getCliConfig();
6
+ const apiClient = new ApiClient(config.apiBaseUrl);
7
+ const session = await loadSession();
8
+ if (session) {
9
+ try {
10
+ await apiClient.logout(session.accessToken, session.refreshToken);
11
+ }
12
+ catch {
13
+ // Session cleanup should continue even if remote revocation fails.
14
+ }
15
+ }
16
+ await clearSession();
17
+ console.log('Logged out.');
18
+ }
19
+ //# sourceMappingURL=logout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEpE,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IAEpC,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;QACpE,CAAC;QAAC,MAAM,CAAC;YACP,mEAAmE;QACrE,CAAC;IACH,CAAC;IAED,MAAM,YAAY,EAAE,CAAC;IACrB,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function stopCommand(tunnelIdentifier: string): Promise<void>;
@@ -0,0 +1,15 @@
1
+ import { getCliConfig } from '../config.js';
2
+ import { ApiClient } from '../lib/api-client.js';
3
+ import { withAuthenticatedSession } from '../lib/session.js';
4
+ export async function stopCommand(tunnelIdentifier) {
5
+ if (!tunnelIdentifier) {
6
+ throw new Error('Tunnel identifier is required.');
7
+ }
8
+ const config = getCliConfig();
9
+ const apiClient = new ApiClient(config.apiBaseUrl);
10
+ await withAuthenticatedSession(apiClient, async (session) => {
11
+ await apiClient.stopTunnel(session.accessToken, tunnelIdentifier);
12
+ });
13
+ console.log(`Stopped tunnel ${tunnelIdentifier}`);
14
+ }
15
+ //# sourceMappingURL=stop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.js","sourceRoot":"","sources":["../../src/commands/stop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,gBAAwB;IACxD,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAEnD,MAAM,wBAAwB,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC1D,MAAM,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,kBAAkB,gBAAgB,EAAE,CAAC,CAAC;AACpD,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { spawn } from 'node:child_process';
2
+ import type { StoredSession } from '../types.js';
3
+ import { ApiClient } from '../lib/api-client.js';
4
+ import { startLocalProxy } from '../lib/local-proxy.js';
5
+ import { createUpDashboard } from '../lib/up-dashboard.js';
6
+ type UpInput = {
7
+ port: number;
8
+ url?: string;
9
+ verbose?: boolean;
10
+ };
11
+ type UpCommandDependencies = {
12
+ createApiClient: (baseUrl: string) => ApiClient;
13
+ requireSession: (apiClient: ApiClient) => Promise<StoredSession>;
14
+ saveSession: (session: StoredSession) => Promise<void>;
15
+ ensureCloudflaredInstalled: () => Promise<string>;
16
+ startLocalProxy: typeof startLocalProxy;
17
+ createUpDashboard: typeof createUpDashboard;
18
+ getCliVersion: () => string;
19
+ spawn: typeof spawn;
20
+ processRef: {
21
+ once: NodeJS.Process['once'];
22
+ removeListener: NodeJS.Process['removeListener'];
23
+ exit: (code?: number) => never;
24
+ stderr: NodeJS.WriteStream;
25
+ };
26
+ setInterval: typeof setInterval;
27
+ clearInterval: typeof clearInterval;
28
+ };
29
+ export declare function extractRegionFromCloudflaredLine(line: string): string | null;
30
+ export declare function sanitizeTelemetryPath(rawPath: string): string;
31
+ export declare function upCommand(input: UpInput, dependencies?: UpCommandDependencies): Promise<void>;
32
+ export {};
@@ -0,0 +1,319 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { createInterface } from 'node:readline';
3
+ import { getCliConfig } from '../config.js';
4
+ import { ApiClient, ApiClientError } from '../lib/api-client.js';
5
+ import { ensureCloudflaredInstalled } from '../lib/cloudflared.js';
6
+ import { startLocalProxy } from '../lib/local-proxy.js';
7
+ import { requireSession } from '../lib/session.js';
8
+ import { TunnelStats } from '../lib/tunnel-stats.js';
9
+ import { createUpDashboard } from '../lib/up-dashboard.js';
10
+ import { getCliVersion } from '../lib/version.js';
11
+ import { saveSession } from '../store/credentials.js';
12
+ function formatApiClientError(context, error) {
13
+ if (error instanceof ApiClientError) {
14
+ return `${context} (status=${error.status}, code=${error.code}): ${error.message}`;
15
+ }
16
+ return `${context}: ${error instanceof Error ? error.message : String(error)}`;
17
+ }
18
+ const defaultDependencies = {
19
+ createApiClient: (baseUrl) => new ApiClient(baseUrl),
20
+ requireSession,
21
+ saveSession,
22
+ ensureCloudflaredInstalled,
23
+ startLocalProxy,
24
+ createUpDashboard,
25
+ getCliVersion,
26
+ spawn,
27
+ processRef: process,
28
+ setInterval,
29
+ clearInterval,
30
+ };
31
+ async function refreshSession(apiClient, session, saveSessionFn) {
32
+ const refreshed = await apiClient.refreshTokens(session.refreshToken);
33
+ const next = {
34
+ accessToken: refreshed.accessToken,
35
+ refreshToken: refreshed.refreshToken,
36
+ expiresAtEpochSec: Math.floor(Date.now() / 1000) + refreshed.expiresInSec,
37
+ profile: refreshed.profile,
38
+ };
39
+ await saveSessionFn(next);
40
+ return next;
41
+ }
42
+ async function withTokenRetry(apiClient, sessionRef, saveSessionFn, run) {
43
+ try {
44
+ return await run(sessionRef.value.accessToken);
45
+ }
46
+ catch (error) {
47
+ if (!(error instanceof ApiClientError) || error.status !== 401) {
48
+ throw error;
49
+ }
50
+ sessionRef.value = await refreshSession(apiClient, sessionRef.value, saveSessionFn);
51
+ return run(sessionRef.value.accessToken);
52
+ }
53
+ }
54
+ export function extractRegionFromCloudflaredLine(line) {
55
+ const patterns = [/\blocation=([a-z0-9-]+)/i, /\bregion=([a-z0-9-]+)/i, /\bcolo=([a-z0-9-]+)/i];
56
+ for (const pattern of patterns) {
57
+ const match = line.match(pattern);
58
+ if (!match?.[1]) {
59
+ continue;
60
+ }
61
+ return match[1].toUpperCase();
62
+ }
63
+ return null;
64
+ }
65
+ export function sanitizeTelemetryPath(rawPath) {
66
+ const trimmed = rawPath.trim();
67
+ const base = trimmed.split('?')[0]?.split('#')[0] ?? '';
68
+ const normalized = base.length === 0 ? '/' : base.startsWith('/') ? base : `/${base}`;
69
+ return normalized.slice(0, 256);
70
+ }
71
+ function attachLineReader(stream, onLine) {
72
+ if (!stream) {
73
+ return () => { };
74
+ }
75
+ const reader = createInterface({
76
+ input: stream,
77
+ crlfDelay: Infinity,
78
+ });
79
+ reader.on('line', onLine);
80
+ return () => reader.close();
81
+ }
82
+ async function stopProxy(proxy) {
83
+ if (!proxy) {
84
+ return;
85
+ }
86
+ try {
87
+ await proxy.stop();
88
+ }
89
+ catch (error) {
90
+ console.error('Failed to stop local proxy:', error instanceof Error ? error.message : String(error));
91
+ }
92
+ }
93
+ export async function upCommand(input, dependencies = defaultDependencies) {
94
+ if (!Number.isInteger(input.port) || input.port < 1 || input.port > 65535) {
95
+ throw new Error('Port must be an integer between 1 and 65535.');
96
+ }
97
+ const config = getCliConfig();
98
+ const apiClient = dependencies.createApiClient(config.apiBaseUrl);
99
+ const sessionRef = {
100
+ value: await dependencies.requireSession(apiClient),
101
+ };
102
+ const cloudflaredPath = await dependencies.ensureCloudflaredInstalled();
103
+ const stats = new TunnelStats();
104
+ let dashboard = null;
105
+ let proxy = null;
106
+ let tunnel = null;
107
+ let child = null;
108
+ let closeStdoutReader = () => { };
109
+ let closeStderrReader = () => { };
110
+ let heartbeatTimer = null;
111
+ let metricsTimer = null;
112
+ let telemetryTimer = null;
113
+ let stopRemoteTunnelPromise = null;
114
+ let remoteTunnelStopped = false;
115
+ const cloudflaredLogBuffer = [];
116
+ let currentRegion = null;
117
+ const telemetryQueue = [];
118
+ let telemetryDisabled = false;
119
+ let lastTelemetryErrorAt = 0;
120
+ const rememberCloudflaredLine = (line) => {
121
+ if (line.trim().length === 0) {
122
+ return;
123
+ }
124
+ cloudflaredLogBuffer.push(line);
125
+ if (cloudflaredLogBuffer.length > 80) {
126
+ cloudflaredLogBuffer.shift();
127
+ }
128
+ };
129
+ const stopRemoteTunnel = async () => {
130
+ if (!tunnel || remoteTunnelStopped) {
131
+ return;
132
+ }
133
+ if (stopRemoteTunnelPromise) {
134
+ await stopRemoteTunnelPromise;
135
+ return;
136
+ }
137
+ const tunnelId = tunnel.tunnelId;
138
+ stopRemoteTunnelPromise = (async () => {
139
+ try {
140
+ await withTokenRetry(apiClient, sessionRef, dependencies.saveSession, (accessToken) => apiClient.stopTunnel(accessToken, tunnelId));
141
+ remoteTunnelStopped = true;
142
+ }
143
+ catch (error) {
144
+ console.error('Failed to stop tunnel cleanly:', error instanceof Error ? error.message : String(error));
145
+ }
146
+ finally {
147
+ stopRemoteTunnelPromise = null;
148
+ }
149
+ })();
150
+ await stopRemoteTunnelPromise;
151
+ };
152
+ const handleCloudflaredLine = (line) => {
153
+ rememberCloudflaredLine(line);
154
+ const region = extractRegionFromCloudflaredLine(line);
155
+ if (region && dashboard) {
156
+ currentRegion = region;
157
+ dashboard.setRegion(region);
158
+ }
159
+ if (dashboard && input.verbose) {
160
+ dashboard.addCloudflaredLine(line);
161
+ }
162
+ };
163
+ const handleSignal = (signal) => {
164
+ void (async () => {
165
+ await stopRemoteTunnel();
166
+ child?.kill(signal);
167
+ })();
168
+ };
169
+ const onSigInt = () => handleSignal('SIGINT');
170
+ const onSigTerm = () => handleSignal('SIGTERM');
171
+ try {
172
+ const startedProxy = await dependencies.startLocalProxy({
173
+ targetPort: input.port,
174
+ onRequest: (event) => {
175
+ stats.recordRequest(event);
176
+ dashboard?.addRequest(event);
177
+ if (telemetryDisabled) {
178
+ return;
179
+ }
180
+ telemetryQueue.push({
181
+ startedAtEpochMs: event.startedAtEpochMs,
182
+ method: event.method.toUpperCase().slice(0, 16),
183
+ path: sanitizeTelemetryPath(event.path),
184
+ statusCode: event.statusCode,
185
+ durationMs: event.durationMs,
186
+ responseBytes: event.responseBytes ?? null,
187
+ error: event.error,
188
+ protocol: event.protocol,
189
+ });
190
+ if (telemetryQueue.length > 2000) {
191
+ telemetryQueue.shift();
192
+ }
193
+ },
194
+ onConnectionChange: (connectionSnapshot) => {
195
+ stats.updateConnections(connectionSnapshot);
196
+ },
197
+ });
198
+ proxy = startedProxy;
199
+ tunnel = await withTokenRetry(apiClient, sessionRef, dependencies.saveSession, (accessToken) => apiClient.createTunnel(accessToken, {
200
+ port: startedProxy.port,
201
+ requestedSlug: input.url,
202
+ }));
203
+ const tunnelId = tunnel.tunnelId;
204
+ dashboard = dependencies.createUpDashboard({
205
+ account: sessionRef.value.profile.email,
206
+ version: dependencies.getCliVersion(),
207
+ forwarding: `https://${tunnel.hostname} -> http://localhost:${input.port}`,
208
+ verbose: Boolean(input.verbose),
209
+ });
210
+ dashboard.setMetrics(stats.getSnapshot());
211
+ metricsTimer = dependencies.setInterval(() => {
212
+ dashboard?.setMetrics(stats.getSnapshot());
213
+ }, 1_000);
214
+ child = dependencies.spawn(cloudflaredPath, ['tunnel', '--no-autoupdate', 'run', '--token', tunnel.cloudflaredToken], {
215
+ stdio: ['ignore', 'pipe', 'pipe'],
216
+ });
217
+ closeStdoutReader = attachLineReader(child.stdout, handleCloudflaredLine);
218
+ closeStderrReader = attachLineReader(child.stderr, handleCloudflaredLine);
219
+ heartbeatTimer = dependencies.setInterval(() => {
220
+ void withTokenRetry(apiClient, sessionRef, dependencies.saveSession, (accessToken) => apiClient.heartbeat(accessToken, tunnelId)).catch((error) => {
221
+ console.error(formatApiClientError('Heartbeat failed', error));
222
+ });
223
+ }, tunnel.heartbeatIntervalSec * 1000);
224
+ telemetryTimer = dependencies.setInterval(() => {
225
+ const snapshot = stats.getSnapshot();
226
+ const drained = telemetryQueue.splice(0, 200);
227
+ let errors = 0;
228
+ let bytes = 0;
229
+ for (const event of drained) {
230
+ if (event.error) {
231
+ errors += 1;
232
+ }
233
+ bytes += event.responseBytes ?? 0;
234
+ }
235
+ const payload = {
236
+ region: currentRegion,
237
+ metrics: {
238
+ ttl: snapshot.ttl,
239
+ opn: snapshot.opn,
240
+ rt1Ms: snapshot.rt1Ms,
241
+ rt5Ms: snapshot.rt5Ms,
242
+ p50Ms: snapshot.p50Ms,
243
+ p90Ms: snapshot.p90Ms,
244
+ requests: drained.length,
245
+ errors,
246
+ bytes,
247
+ },
248
+ requests: drained,
249
+ };
250
+ void withTokenRetry(apiClient, sessionRef, dependencies.saveSession, (accessToken) => apiClient.ingestTelemetry(accessToken, tunnelId, payload)).catch((error) => {
251
+ if (error instanceof ApiClientError && (error.status === 404 || error.status === 405)) {
252
+ telemetryDisabled = true;
253
+ if (telemetryTimer) {
254
+ dependencies.clearInterval(telemetryTimer);
255
+ telemetryTimer = null;
256
+ }
257
+ dashboard?.addMessage('Portal telemetry disabled (server does not support it).');
258
+ return;
259
+ }
260
+ const now = Date.now();
261
+ if (now - lastTelemetryErrorAt >= 30_000) {
262
+ lastTelemetryErrorAt = now;
263
+ console.error('Telemetry upload failed:', error instanceof Error ? error.message : String(error));
264
+ }
265
+ });
266
+ }, 2_000);
267
+ dependencies.processRef.once('SIGINT', onSigInt);
268
+ dependencies.processRef.once('SIGTERM', onSigTerm);
269
+ const exitCode = await new Promise((resolve, reject) => {
270
+ child.once('error', reject);
271
+ child.once('exit', (code) => resolve(code ?? 0));
272
+ });
273
+ if (exitCode !== 0 && !input.verbose && cloudflaredLogBuffer.length > 0) {
274
+ dependencies.processRef.stderr.write('cloudflared exited unexpectedly. Last logs:\n');
275
+ for (const line of cloudflaredLogBuffer.slice(-10)) {
276
+ dependencies.processRef.stderr.write(`${line}\n`);
277
+ }
278
+ }
279
+ if (heartbeatTimer) {
280
+ dependencies.clearInterval(heartbeatTimer);
281
+ }
282
+ if (metricsTimer) {
283
+ dependencies.clearInterval(metricsTimer);
284
+ }
285
+ if (telemetryTimer) {
286
+ dependencies.clearInterval(telemetryTimer);
287
+ }
288
+ closeStdoutReader();
289
+ closeStderrReader();
290
+ dashboard?.stop();
291
+ dependencies.processRef.removeListener('SIGINT', onSigInt);
292
+ dependencies.processRef.removeListener('SIGTERM', onSigTerm);
293
+ await stopRemoteTunnel();
294
+ await stopProxy(proxy);
295
+ dependencies.processRef.exit(exitCode);
296
+ }
297
+ catch (error) {
298
+ if (heartbeatTimer) {
299
+ dependencies.clearInterval(heartbeatTimer);
300
+ }
301
+ if (metricsTimer) {
302
+ dependencies.clearInterval(metricsTimer);
303
+ }
304
+ if (telemetryTimer) {
305
+ dependencies.clearInterval(telemetryTimer);
306
+ }
307
+ closeStdoutReader();
308
+ closeStderrReader();
309
+ if (dashboard) {
310
+ dashboard.stop();
311
+ }
312
+ dependencies.processRef.removeListener('SIGINT', onSigInt);
313
+ dependencies.processRef.removeListener('SIGTERM', onSigTerm);
314
+ await stopRemoteTunnel();
315
+ await stopProxy(proxy);
316
+ throw error;
317
+ }
318
+ }
319
+ //# sourceMappingURL=up.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"up.js","sourceRoot":"","sources":["../../src/commands/up.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};