@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
@@ -0,0 +1,67 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs/promises';
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
+ import { DEFAULT_API_BASE_URL, ensureApiBaseUrlConfigured, getCliConfig, getCliConfigPath, setApiBaseUrlOverride, } from './config.js';
6
+ const originalEnv = { ...process.env };
7
+ let tempHomeDir = '';
8
+ beforeEach(async () => {
9
+ tempHomeDir = await fs.mkdtemp(path.join(os.tmpdir(), 'rs-tunnel-cli-config-'));
10
+ vi.spyOn(os, 'homedir').mockReturnValue(tempHomeDir);
11
+ });
12
+ afterEach(async () => {
13
+ process.env = { ...originalEnv };
14
+ setApiBaseUrlOverride(undefined);
15
+ vi.restoreAllMocks();
16
+ if (tempHomeDir) {
17
+ await fs.rm(tempHomeDir, { recursive: true, force: true });
18
+ }
19
+ });
20
+ describe('cli config', () => {
21
+ it('uses RS_TUNNEL_API_URL when configured', () => {
22
+ process.env.RS_TUNNEL_API_URL = 'https://api.selfhosted.example.com/';
23
+ process.env.RS_TUNNEL_API_BASE_URL = 'https://legacy.example.com';
24
+ const config = getCliConfig();
25
+ expect(config.apiBaseUrl).toBe('https://api.selfhosted.example.com');
26
+ });
27
+ it('falls back to RS_TUNNEL_API_BASE_URL for backwards compatibility', () => {
28
+ delete process.env.RS_TUNNEL_API_URL;
29
+ process.env.RS_TUNNEL_API_BASE_URL = 'https://legacy.example.com/';
30
+ const config = getCliConfig();
31
+ expect(config.apiBaseUrl).toBe('https://legacy.example.com');
32
+ });
33
+ it('uses the default API URL when env vars are absent', () => {
34
+ delete process.env.RS_TUNNEL_API_URL;
35
+ delete process.env.RS_TUNNEL_API_BASE_URL;
36
+ const config = getCliConfig();
37
+ expect(config.apiBaseUrl).toBe(DEFAULT_API_BASE_URL);
38
+ });
39
+ it('supports command-level domain overrides', () => {
40
+ process.env.RS_TUNNEL_API_URL = 'https://api.default.example.com';
41
+ setApiBaseUrlOverride('https://api.override.example.com/');
42
+ const config = getCliConfig();
43
+ expect(config.apiBaseUrl).toBe('https://api.override.example.com');
44
+ });
45
+ it('rejects invalid command-level domain overrides', () => {
46
+ expect(() => setApiBaseUrlOverride('not-a-valid-url')).toThrow(/Invalid API base URL/);
47
+ });
48
+ it('loads API URL from persisted local config when env vars are absent', async () => {
49
+ delete process.env.RS_TUNNEL_API_URL;
50
+ delete process.env.RS_TUNNEL_API_BASE_URL;
51
+ await fs.mkdir(path.dirname(getCliConfigPath()), { recursive: true });
52
+ await fs.writeFile(getCliConfigPath(), JSON.stringify({
53
+ apiBaseUrl: 'https://persisted.example.com/',
54
+ }, null, 2));
55
+ const config = getCliConfig();
56
+ expect(config.apiBaseUrl).toBe('https://persisted.example.com');
57
+ });
58
+ it('persists command-level --domain for future commands', async () => {
59
+ delete process.env.RS_TUNNEL_API_URL;
60
+ delete process.env.RS_TUNNEL_API_BASE_URL;
61
+ await ensureApiBaseUrlConfigured('https://saved.example.com/');
62
+ setApiBaseUrlOverride(undefined);
63
+ const config = getCliConfig();
64
+ expect(config.apiBaseUrl).toBe('https://saved.example.com');
65
+ });
66
+ });
67
+ //# sourceMappingURL=config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.js","sourceRoot":"","sources":["../src/config.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { doctorCommand } from './commands/doctor.js';
4
+ import { listCommand } from './commands/list.js';
5
+ import { loginCommand } from './commands/login.js';
6
+ import { logoutCommand } from './commands/logout.js';
7
+ import { stopCommand } from './commands/stop.js';
8
+ import { upCommand } from './commands/up.js';
9
+ import { ensureApiBaseUrlConfigured } from './config.js';
10
+ const program = new Command();
11
+ const domainOptionDescription = 'Infisical-style domain override for this command (example: https://api.example.com). Saved for future commands.';
12
+ async function applyDomainOption(domain) {
13
+ await ensureApiBaseUrlConfigured(domain);
14
+ }
15
+ program
16
+ .name('rs-tunnel')
17
+ .description('Self-hostable Cloudflare tunnel CLI')
18
+ .version('0.1.0');
19
+ program
20
+ .command('login')
21
+ .requiredOption('--email <email>', 'Email for Slack OAuth')
22
+ .option('--domain <domain-url>', domainOptionDescription)
23
+ .action(async (options) => {
24
+ await applyDomainOption(options.domain);
25
+ await loginCommand(options.email);
26
+ });
27
+ program
28
+ .command('up')
29
+ .requiredOption('--port <port>', 'Local port to expose', (value) => Number.parseInt(value, 10))
30
+ .option('--url <slug>', 'Optional URL slug (single label only)')
31
+ .option('--domain <domain-url>', domainOptionDescription)
32
+ .option('--verbose', 'Show raw cloudflared logs')
33
+ .action(async (options) => {
34
+ await applyDomainOption(options.domain);
35
+ await upCommand({
36
+ port: options.port,
37
+ url: options.url,
38
+ verbose: options.verbose,
39
+ });
40
+ });
41
+ program
42
+ .command('list')
43
+ .option('--domain <domain-url>', domainOptionDescription)
44
+ .action(async (options) => {
45
+ await applyDomainOption(options.domain);
46
+ await listCommand();
47
+ });
48
+ program
49
+ .command('stop')
50
+ .argument('<tunnel-id-or-hostname>', 'Tunnel ID or hostname')
51
+ .option('--domain <domain-url>', domainOptionDescription)
52
+ .action(async (tunnelIdentifier, options) => {
53
+ await applyDomainOption(options.domain);
54
+ await stopCommand(tunnelIdentifier);
55
+ });
56
+ program
57
+ .command('logout')
58
+ .option('--domain <domain-url>', domainOptionDescription)
59
+ .action(async (options) => {
60
+ await applyDomainOption(options.domain);
61
+ await logoutCommand();
62
+ });
63
+ program
64
+ .command('doctor')
65
+ .option('--domain <domain-url>', domainOptionDescription)
66
+ .action(async (options) => {
67
+ await applyDomainOption(options.domain);
68
+ await doctorCommand();
69
+ });
70
+ program.showHelpAfterError();
71
+ program.parseAsync(process.argv).catch((error) => {
72
+ console.error(error instanceof Error ? error.message : String(error));
73
+ process.exit(1);
74
+ });
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import type { TunnelTelemetryIngestRequest } from '@ripeseed/shared';
2
+ export declare class ApiClientError extends Error {
3
+ readonly status: number;
4
+ readonly code: string;
5
+ readonly details?: unknown | undefined;
6
+ constructor(status: number, code: string, message: string, details?: unknown | undefined);
7
+ }
8
+ export declare class ApiClient {
9
+ private readonly baseUrl;
10
+ constructor(baseUrl: string);
11
+ health(): Promise<boolean>;
12
+ startSlackAuth(input: {
13
+ email: string;
14
+ codeChallenge: string;
15
+ cliCallbackUrl: string;
16
+ }): Promise<{
17
+ authorizeUrl: string;
18
+ state: string;
19
+ }>;
20
+ exchangeLoginCode(input: {
21
+ loginCode: string;
22
+ codeVerifier: string;
23
+ }): Promise<{
24
+ accessToken: string;
25
+ refreshToken: string;
26
+ expiresInSec: number;
27
+ profile: {
28
+ email: string;
29
+ slackUserId: string;
30
+ slackTeamId: string;
31
+ };
32
+ }>;
33
+ refreshTokens(refreshToken: string): Promise<{
34
+ accessToken: string;
35
+ refreshToken: string;
36
+ expiresInSec: number;
37
+ profile: {
38
+ email: string;
39
+ slackUserId: string;
40
+ slackTeamId: string;
41
+ };
42
+ }>;
43
+ logout(accessToken: string, refreshToken?: string): Promise<void>;
44
+ createTunnel(accessToken: string, input: {
45
+ port: number;
46
+ requestedSlug?: string;
47
+ }): Promise<{
48
+ tunnelId: string;
49
+ hostname: string;
50
+ cloudflaredToken: string;
51
+ heartbeatIntervalSec: 20;
52
+ }>;
53
+ listTunnels(accessToken: string): Promise<{
54
+ status: "creating" | "active" | "stopping" | "stopped" | "failed";
55
+ hostname: string;
56
+ id: string;
57
+ slug: string;
58
+ requestedPort: number;
59
+ createdAt: string;
60
+ lease: {
61
+ expiresAt: string;
62
+ lastHeartbeatAt: string;
63
+ } | null;
64
+ stoppedAt: string | null;
65
+ lastError: string | null;
66
+ }[]>;
67
+ heartbeat(accessToken: string, tunnelIdOrHostname: string): Promise<{
68
+ expiresAt: string;
69
+ }>;
70
+ ingestTelemetry(accessToken: string, tunnelIdOrHostname: string, input: TunnelTelemetryIngestRequest): Promise<void>;
71
+ stopTunnel(accessToken: string, tunnelIdOrHostname: string): Promise<void>;
72
+ private request;
73
+ }
@@ -0,0 +1,146 @@
1
+ import { apiErrorSchema, authExchangeRequestSchema, authStartRequestSchema, authStartResponseSchema, heartbeatResponseSchema, refreshRequestSchema, tokenPairSchema, tunnelCreateRequestSchema, tunnelCreateResponseSchema, tunnelListResponseSchema, tunnelTelemetryIngestRequestSchema, tunnelTelemetryIngestResponseSchema, } from '@ripeseed/shared';
2
+ function formatNetworkFailure(error) {
3
+ if (!(error instanceof Error)) {
4
+ return 'unknown network error';
5
+ }
6
+ const cause = error.cause;
7
+ if (!cause || typeof cause !== 'object') {
8
+ return error.message;
9
+ }
10
+ const code = typeof cause.code === 'string' ? cause.code : undefined;
11
+ const message = typeof cause.message === 'string'
12
+ ? cause.message
13
+ : error.message;
14
+ return code ? `${code}: ${message}` : message;
15
+ }
16
+ export class ApiClientError extends Error {
17
+ status;
18
+ code;
19
+ details;
20
+ constructor(status, code, message, details) {
21
+ super(message);
22
+ this.status = status;
23
+ this.code = code;
24
+ this.details = details;
25
+ }
26
+ }
27
+ export class ApiClient {
28
+ baseUrl;
29
+ constructor(baseUrl) {
30
+ this.baseUrl = baseUrl;
31
+ }
32
+ async health() {
33
+ try {
34
+ const response = await fetch(`${this.baseUrl}/healthz`);
35
+ return response.ok;
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ }
41
+ async startSlackAuth(input) {
42
+ const body = authStartRequestSchema.parse(input);
43
+ const response = await this.request('/v1/auth/slack/start', {
44
+ method: 'POST',
45
+ body,
46
+ });
47
+ return authStartResponseSchema.parse(response);
48
+ }
49
+ async exchangeLoginCode(input) {
50
+ const body = authExchangeRequestSchema.parse(input);
51
+ const response = await this.request('/v1/auth/exchange', {
52
+ method: 'POST',
53
+ body,
54
+ });
55
+ return tokenPairSchema.parse(response);
56
+ }
57
+ async refreshTokens(refreshToken) {
58
+ const body = refreshRequestSchema.parse({ refreshToken });
59
+ const response = await this.request('/v1/auth/refresh', {
60
+ method: 'POST',
61
+ body,
62
+ });
63
+ return tokenPairSchema.parse(response);
64
+ }
65
+ async logout(accessToken, refreshToken) {
66
+ await this.request('/v1/auth/logout', {
67
+ method: 'POST',
68
+ accessToken,
69
+ body: {
70
+ refreshToken,
71
+ },
72
+ });
73
+ }
74
+ async createTunnel(accessToken, input) {
75
+ const body = tunnelCreateRequestSchema.parse(input);
76
+ const response = await this.request('/v1/tunnels', {
77
+ method: 'POST',
78
+ accessToken,
79
+ body,
80
+ });
81
+ return tunnelCreateResponseSchema.parse(response);
82
+ }
83
+ async listTunnels(accessToken) {
84
+ const response = await this.request('/v1/tunnels', {
85
+ method: 'GET',
86
+ accessToken,
87
+ });
88
+ return tunnelListResponseSchema.parse(response);
89
+ }
90
+ async heartbeat(accessToken, tunnelIdOrHostname) {
91
+ const response = await this.request(`/v1/tunnels/${encodeURIComponent(tunnelIdOrHostname)}/heartbeat`, {
92
+ method: 'POST',
93
+ accessToken,
94
+ });
95
+ const parsed = heartbeatResponseSchema.parse(response);
96
+ return { expiresAt: parsed.expiresAt };
97
+ }
98
+ async ingestTelemetry(accessToken, tunnelIdOrHostname, input) {
99
+ const body = tunnelTelemetryIngestRequestSchema.parse(input);
100
+ const response = await this.request(`/v1/tunnels/${encodeURIComponent(tunnelIdOrHostname)}/telemetry`, {
101
+ method: 'POST',
102
+ accessToken,
103
+ body,
104
+ });
105
+ tunnelTelemetryIngestResponseSchema.parse(response);
106
+ }
107
+ async stopTunnel(accessToken, tunnelIdOrHostname) {
108
+ await this.request(`/v1/tunnels/${encodeURIComponent(tunnelIdOrHostname)}`, {
109
+ method: 'DELETE',
110
+ accessToken,
111
+ });
112
+ }
113
+ async request(path, init) {
114
+ let response;
115
+ try {
116
+ response = await fetch(`${this.baseUrl}${path}`, {
117
+ method: init.method,
118
+ headers: {
119
+ 'Content-Type': 'application/json',
120
+ ...(init.accessToken ? { Authorization: `Bearer ${init.accessToken}` } : {}),
121
+ },
122
+ body: init.body !== undefined ? JSON.stringify(init.body) : undefined,
123
+ });
124
+ }
125
+ catch (error) {
126
+ const networkFailure = formatNetworkFailure(error);
127
+ throw new ApiClientError(0, 'NETWORK_ERROR', `Unable to reach API at ${this.baseUrl} (${networkFailure}). Verify /healthz from this shell, or run the command with --domain <url>.`);
128
+ }
129
+ if (response.status === 204) {
130
+ return {};
131
+ }
132
+ const contentType = response.headers.get('content-type') ?? '';
133
+ const payload = contentType.includes('application/json')
134
+ ? await response.json()
135
+ : { message: await response.text() };
136
+ if (!response.ok) {
137
+ const parsed = apiErrorSchema.safeParse(payload);
138
+ if (parsed.success) {
139
+ throw new ApiClientError(response.status, parsed.data.code, parsed.data.message, parsed.data.details);
140
+ }
141
+ throw new ApiClientError(response.status, 'HTTP_ERROR', `Request failed with status ${response.status}`);
142
+ }
143
+ return payload;
144
+ }
145
+ }
146
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function findCloudflaredBinary(): Promise<string | null>;
2
+ export declare function ensureCloudflaredInstalled(): Promise<string>;
3
+ export declare function getCloudflaredVersion(binaryPath: string): string | null;
@@ -0,0 +1,165 @@
1
+ import { createHash } from 'node:crypto';
2
+ import fs from 'node:fs/promises';
3
+ import { constants as fsConstants } from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { spawnSync } from 'node:child_process';
7
+ import { getBundledBinDir } from '../config.js';
8
+ function commandExists(command) {
9
+ const result = spawnSync(command, ['--version'], { stdio: 'ignore' });
10
+ return !result.error;
11
+ }
12
+ function getCloudflaredAsset() {
13
+ const platform = os.platform();
14
+ const arch = os.arch();
15
+ if (platform === 'darwin' && arch === 'arm64') {
16
+ return {
17
+ assetName: 'cloudflared-darwin-arm64',
18
+ binaryName: 'cloudflared',
19
+ };
20
+ }
21
+ if (platform === 'darwin' && arch === 'x64') {
22
+ return {
23
+ assetName: 'cloudflared-darwin-amd64',
24
+ binaryName: 'cloudflared',
25
+ };
26
+ }
27
+ if (platform === 'linux' && arch === 'arm64') {
28
+ return {
29
+ assetName: 'cloudflared-linux-arm64',
30
+ binaryName: 'cloudflared',
31
+ };
32
+ }
33
+ if (platform === 'linux' && arch === 'x64') {
34
+ return {
35
+ assetName: 'cloudflared-linux-amd64',
36
+ binaryName: 'cloudflared',
37
+ };
38
+ }
39
+ if (platform === 'win32' && arch === 'x64') {
40
+ return {
41
+ assetName: 'cloudflared-windows-amd64.exe',
42
+ binaryName: 'cloudflared.exe',
43
+ };
44
+ }
45
+ if (platform === 'win32' && arch === 'arm64') {
46
+ return {
47
+ assetName: 'cloudflared-windows-arm64.exe',
48
+ binaryName: 'cloudflared.exe',
49
+ };
50
+ }
51
+ throw new Error(`Unsupported OS/architecture combination: ${platform}/${arch}`);
52
+ }
53
+ export async function findCloudflaredBinary() {
54
+ const envOverride = process.env.RS_TUNNEL_CLOUDFLARED_PATH;
55
+ if (envOverride) {
56
+ return envOverride;
57
+ }
58
+ if (commandExists('cloudflared')) {
59
+ return 'cloudflared';
60
+ }
61
+ const fallback = path.join(getBundledBinDir(), getCloudflaredAsset().binaryName);
62
+ try {
63
+ await fs.access(fallback, fsConstants.X_OK);
64
+ return fallback;
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ async function runInstallCommand(command, args) {
71
+ const result = spawnSync(command, args, {
72
+ stdio: 'inherit',
73
+ });
74
+ return result.status === 0;
75
+ }
76
+ async function tryPackageManagerInstall() {
77
+ const platform = os.platform();
78
+ if (platform === 'darwin') {
79
+ if (await runInstallCommand('brew', ['install', 'cloudflared'])) {
80
+ return true;
81
+ }
82
+ }
83
+ if (platform === 'linux') {
84
+ const installers = [
85
+ ['apt-get', ['install', '-y', 'cloudflared']],
86
+ ['dnf', ['install', '-y', 'cloudflared']],
87
+ ['pacman', ['-S', '--noconfirm', 'cloudflared']],
88
+ ];
89
+ for (const [command, args] of installers) {
90
+ if (!commandExists(command)) {
91
+ continue;
92
+ }
93
+ if (await runInstallCommand('sudo', [command, ...args])) {
94
+ return true;
95
+ }
96
+ }
97
+ }
98
+ if (platform === 'win32') {
99
+ if (await runInstallCommand('winget', ['install', '--id', 'Cloudflare.cloudflared', '--silent'])) {
100
+ return true;
101
+ }
102
+ }
103
+ return false;
104
+ }
105
+ function parseSha256Line(line) {
106
+ const first = line.trim().split(/\s+/)[0];
107
+ if (!first) {
108
+ throw new Error('Invalid cloudflared checksum format.');
109
+ }
110
+ return first;
111
+ }
112
+ async function downloadWithChecksum(assetName, destination) {
113
+ const baseUrl = 'https://github.com/cloudflare/cloudflared/releases/latest/download';
114
+ const binaryUrl = `${baseUrl}/${assetName}`;
115
+ const checksumUrl = `${binaryUrl}.sha256`;
116
+ const [binaryResponse, checksumResponse] = await Promise.all([fetch(binaryUrl), fetch(checksumUrl)]);
117
+ if (!binaryResponse.ok) {
118
+ throw new Error(`Failed downloading cloudflared binary: ${binaryResponse.status}`);
119
+ }
120
+ if (!checksumResponse.ok) {
121
+ throw new Error(`Failed downloading cloudflared checksum: ${checksumResponse.status}`);
122
+ }
123
+ const [binaryBuffer, checksumText] = await Promise.all([
124
+ binaryResponse.arrayBuffer(),
125
+ checksumResponse.text(),
126
+ ]);
127
+ const expected = parseSha256Line(checksumText);
128
+ const actual = createHash('sha256').update(Buffer.from(binaryBuffer)).digest('hex');
129
+ if (expected.toLowerCase() !== actual.toLowerCase()) {
130
+ throw new Error('cloudflared checksum verification failed.');
131
+ }
132
+ await fs.mkdir(path.dirname(destination), { recursive: true, mode: 0o755 });
133
+ await fs.writeFile(destination, Buffer.from(binaryBuffer));
134
+ if (os.platform() !== 'win32') {
135
+ await fs.chmod(destination, 0o755);
136
+ }
137
+ }
138
+ async function installFromDownload() {
139
+ const asset = getCloudflaredAsset();
140
+ const destination = path.join(getBundledBinDir(), asset.binaryName);
141
+ await downloadWithChecksum(asset.assetName, destination);
142
+ return destination;
143
+ }
144
+ export async function ensureCloudflaredInstalled() {
145
+ const existing = await findCloudflaredBinary();
146
+ if (existing) {
147
+ return existing;
148
+ }
149
+ const packageInstallSuccess = await tryPackageManagerInstall();
150
+ if (packageInstallSuccess) {
151
+ const afterInstall = await findCloudflaredBinary();
152
+ if (afterInstall) {
153
+ return afterInstall;
154
+ }
155
+ }
156
+ return installFromDownload();
157
+ }
158
+ export function getCloudflaredVersion(binaryPath) {
159
+ const result = spawnSync(binaryPath, ['--version'], { encoding: 'utf8' });
160
+ if (result.error || result.status !== 0) {
161
+ return null;
162
+ }
163
+ return result.stdout.trim() || result.stderr.trim() || null;
164
+ }
165
+ //# sourceMappingURL=cloudflared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflared.js","sourceRoot":"","sources":["../../src/lib/cloudflared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export type CallbackServer = {
2
+ callbackUrl: string;
3
+ waitForCode: () => Promise<{
4
+ code: string;
5
+ state: string;
6
+ }>;
7
+ close: () => Promise<void>;
8
+ };
9
+ export declare function startCallbackServer(): Promise<CallbackServer>;
@@ -0,0 +1,68 @@
1
+ import http from 'node:http';
2
+ import { URL } from 'node:url';
3
+ export async function startCallbackServer() {
4
+ let resolveHandler;
5
+ let rejectHandler;
6
+ const waitForCodePromise = new Promise((resolve, reject) => {
7
+ resolveHandler = resolve;
8
+ rejectHandler = reject;
9
+ });
10
+ const server = http.createServer((req, res) => {
11
+ const requestUrl = new URL(req.url ?? '/', 'http://127.0.0.1');
12
+ if (requestUrl.pathname !== '/callback') {
13
+ res.statusCode = 404;
14
+ res.end('Not found');
15
+ return;
16
+ }
17
+ const code = requestUrl.searchParams.get('code');
18
+ const state = requestUrl.searchParams.get('state');
19
+ if (!code || !state) {
20
+ res.statusCode = 400;
21
+ res.end('Missing code or state');
22
+ rejectHandler?.(new Error('OAuth callback missing code or state'));
23
+ return;
24
+ }
25
+ res.statusCode = 200;
26
+ res.setHeader('Content-Type', 'text/plain');
27
+ res.end('Login complete. You can close this tab.');
28
+ resolveHandler?.({ code, state });
29
+ });
30
+ await new Promise((resolve, reject) => {
31
+ server.once('error', reject);
32
+ server.listen(0, '127.0.0.1', () => {
33
+ server.off('error', reject);
34
+ resolve();
35
+ });
36
+ });
37
+ const address = server.address();
38
+ if (!address || typeof address === 'string') {
39
+ throw new Error('Unable to bind local callback server.');
40
+ }
41
+ const timeout = setTimeout(() => {
42
+ rejectHandler?.(new Error('Timed out waiting for Slack OAuth callback.'));
43
+ }, 120_000);
44
+ return {
45
+ callbackUrl: `http://127.0.0.1:${address.port}/callback`,
46
+ waitForCode: async () => {
47
+ try {
48
+ return await waitForCodePromise;
49
+ }
50
+ finally {
51
+ clearTimeout(timeout);
52
+ }
53
+ },
54
+ close: async () => {
55
+ await new Promise((resolve, reject) => {
56
+ server.close((error) => {
57
+ if (error) {
58
+ reject(error);
59
+ return;
60
+ }
61
+ resolve();
62
+ });
63
+ });
64
+ clearTimeout(timeout);
65
+ },
66
+ };
67
+ }
68
+ //# sourceMappingURL=local-callback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-callback.js","sourceRoot":"","sources":["../../src/lib/local-callback.ts"],"names":[],"mappings":"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"}