cyrus-cloudflare-tunnel-client 0.2.0-rc → 0.2.0-rc.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/dist/CloudflareTunnelClient.d.ts +40 -0
- package/dist/CloudflareTunnelClient.d.ts.map +1 -0
- package/dist/CloudflareTunnelClient.js +112 -0
- package/dist/CloudflareTunnelClient.js.map +1 -0
- package/dist/ConfigApiClient.d.ts +29 -0
- package/dist/ConfigApiClient.d.ts.map +1 -0
- package/dist/ConfigApiClient.js +72 -0
- package/dist/ConfigApiClient.js.map +1 -0
- package/dist/handlers/configureMcp.d.ts +7 -0
- package/dist/handlers/configureMcp.d.ts.map +1 -0
- package/dist/handlers/configureMcp.js +104 -0
- package/dist/handlers/configureMcp.js.map +1 -0
- package/dist/handlers/cyrusConfig.d.ts +11 -0
- package/dist/handlers/cyrusConfig.d.ts.map +1 -0
- package/dist/handlers/cyrusConfig.js +161 -0
- package/dist/handlers/cyrusConfig.js.map +1 -0
- package/dist/handlers/cyrusEnv.d.ts +7 -0
- package/dist/handlers/cyrusEnv.d.ts.map +1 -0
- package/dist/handlers/cyrusEnv.js +113 -0
- package/dist/handlers/cyrusEnv.js.map +1 -0
- package/dist/handlers/repository.d.ts +9 -0
- package/dist/handlers/repository.d.ts.map +1 -0
- package/dist/handlers/repository.js +123 -0
- package/dist/handlers/repository.js.map +1 -0
- package/dist/handlers/testMcp.d.ts +10 -0
- package/dist/handlers/testMcp.d.ts.map +1 -0
- package/dist/handlers/testMcp.js +74 -0
- package/dist/handlers/testMcp.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/{src/index.ts → dist/index.js} +1 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +4 -1
- package/src/CloudflareTunnelClient.ts +0 -150
- package/src/ConfigApiClient.ts +0 -94
- package/src/types.ts +0 -10
- package/tsconfig.json +0 -12
- package/vitest.config.ts +0 -12
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Handle Cyrus environment variables update
|
|
5
|
+
* Primarily used to update/provide the Claude API token
|
|
6
|
+
*/
|
|
7
|
+
export async function handleCyrusEnv(payload, cyrusHome) {
|
|
8
|
+
try {
|
|
9
|
+
// Validate payload
|
|
10
|
+
if (!payload || typeof payload !== "object") {
|
|
11
|
+
return {
|
|
12
|
+
success: false,
|
|
13
|
+
error: "Environment variables update requires valid data",
|
|
14
|
+
details: "Payload must be an object containing environment variable key-value pairs.",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
// Extract environment variables from payload
|
|
18
|
+
// The payload may have a 'variables' key containing the env vars,
|
|
19
|
+
// or the env vars may be directly in the payload
|
|
20
|
+
const envVarsSource = payload.variables || payload;
|
|
21
|
+
const envVars = Object.entries(envVarsSource).filter(([key, value]) => value !== undefined &&
|
|
22
|
+
typeof value === "string" &&
|
|
23
|
+
!["variables", "restartCyrus", "backupEnv"].includes(key));
|
|
24
|
+
if (envVars.length === 0) {
|
|
25
|
+
return {
|
|
26
|
+
success: false,
|
|
27
|
+
error: "No environment variables to update",
|
|
28
|
+
details: "At least one environment variable must be provided in the request.",
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const envPath = join(cyrusHome, ".env");
|
|
32
|
+
// Ensure the .cyrus directory exists
|
|
33
|
+
const envDir = dirname(envPath);
|
|
34
|
+
if (!existsSync(envDir)) {
|
|
35
|
+
mkdirSync(envDir, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
// Read existing env file if it exists
|
|
38
|
+
const existingEnv = {};
|
|
39
|
+
if (existsSync(envPath)) {
|
|
40
|
+
try {
|
|
41
|
+
const content = readFileSync(envPath, "utf-8");
|
|
42
|
+
const lines = content.split("\n");
|
|
43
|
+
for (const line of lines) {
|
|
44
|
+
const trimmed = line.trim();
|
|
45
|
+
// Skip empty lines and comments
|
|
46
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const equalIndex = trimmed.indexOf("=");
|
|
50
|
+
if (equalIndex > 0) {
|
|
51
|
+
const key = trimmed.substring(0, equalIndex);
|
|
52
|
+
const value = trimmed.substring(equalIndex + 1);
|
|
53
|
+
existingEnv[key] = value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Ignore errors reading existing file - we'll create a new one
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Merge new variables (new values override existing ones)
|
|
62
|
+
for (const [key, value] of envVars) {
|
|
63
|
+
if (value !== undefined) {
|
|
64
|
+
existingEnv[key] = value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Build new env file content
|
|
68
|
+
const envContent = Object.entries(existingEnv)
|
|
69
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
70
|
+
.join("\n");
|
|
71
|
+
// Backup existing env file if requested
|
|
72
|
+
if (payload.backupEnv && existsSync(envPath)) {
|
|
73
|
+
try {
|
|
74
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
75
|
+
const backupPath = join(cyrusHome, `.env.backup-${timestamp}`);
|
|
76
|
+
const existingEnvFile = readFileSync(envPath, "utf-8");
|
|
77
|
+
writeFileSync(backupPath, existingEnvFile, "utf-8");
|
|
78
|
+
}
|
|
79
|
+
catch (backupError) {
|
|
80
|
+
// Log but don't fail - backup is not critical
|
|
81
|
+
console.warn(`Failed to backup env: ${backupError instanceof Error ? backupError.message : String(backupError)}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Write env file
|
|
85
|
+
try {
|
|
86
|
+
writeFileSync(envPath, `${envContent}\n`, "utf-8");
|
|
87
|
+
return {
|
|
88
|
+
success: true,
|
|
89
|
+
message: "Environment variables updated successfully",
|
|
90
|
+
data: {
|
|
91
|
+
envPath,
|
|
92
|
+
variablesUpdated: envVars.map(([key]) => key),
|
|
93
|
+
restartCyrus: payload.restartCyrus || false,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
return {
|
|
99
|
+
success: false,
|
|
100
|
+
error: "Failed to save environment variables",
|
|
101
|
+
details: `Could not write to ${envPath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
return {
|
|
107
|
+
success: false,
|
|
108
|
+
error: "Environment variables update failed",
|
|
109
|
+
details: error instanceof Error ? error.message : String(error),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=cyrusEnv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cyrusEnv.js","sourceRoot":"","sources":["../../src/handlers/cyrusEnv.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ApiResponse, RepositoryPayload } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Handle repository cloning or verification
|
|
4
|
+
* - Clones repositories to ~/.cyrus/repos/<repo-name>
|
|
5
|
+
* - If repository exists, verify it's a git repo and do nothing
|
|
6
|
+
* - If repository doesn't exist, clone it to ~/.cyrus/repos/<repo-name>
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleRepository(payload: RepositoryPayload, cyrusHome: string): Promise<ApiResponse>;
|
|
9
|
+
//# sourceMappingURL=repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/handlers/repository.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA4BlE;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACrC,OAAO,EAAE,iBAAiB,EAC1B,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,CA+FtB"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { basename, join } from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
const execAsync = promisify(exec);
|
|
6
|
+
/**
|
|
7
|
+
* Check if a directory contains a git repository
|
|
8
|
+
*/
|
|
9
|
+
function isGitRepository(path) {
|
|
10
|
+
try {
|
|
11
|
+
return existsSync(join(path, ".git"));
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Extract repository name from URL
|
|
19
|
+
*/
|
|
20
|
+
function getRepoNameFromUrl(repoUrl) {
|
|
21
|
+
// Handle URLs like: https://github.com/user/repo.git or git@github.com:user/repo.git
|
|
22
|
+
const match = repoUrl.match(/\/([^/]+?)(\.git)?$/);
|
|
23
|
+
if (match?.[1]) {
|
|
24
|
+
return match[1];
|
|
25
|
+
}
|
|
26
|
+
// Fallback: use last part of URL
|
|
27
|
+
return basename(repoUrl, ".git");
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Handle repository cloning or verification
|
|
31
|
+
* - Clones repositories to ~/.cyrus/repos/<repo-name>
|
|
32
|
+
* - If repository exists, verify it's a git repo and do nothing
|
|
33
|
+
* - If repository doesn't exist, clone it to ~/.cyrus/repos/<repo-name>
|
|
34
|
+
*/
|
|
35
|
+
export async function handleRepository(payload, cyrusHome) {
|
|
36
|
+
try {
|
|
37
|
+
// Validate payload
|
|
38
|
+
if (!payload.repository_url || typeof payload.repository_url !== "string") {
|
|
39
|
+
return {
|
|
40
|
+
success: false,
|
|
41
|
+
error: "Repository URL is required",
|
|
42
|
+
details: "Please provide a valid Git repository URL (e.g., https://github.com/user/repo.git)",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Use repository name from payload or extract from URL
|
|
46
|
+
const repoName = payload.repository_name || getRepoNameFromUrl(payload.repository_url);
|
|
47
|
+
// Construct path within ~/.cyrus/repos
|
|
48
|
+
const reposDir = join(cyrusHome, "repos");
|
|
49
|
+
const repoPath = join(reposDir, repoName);
|
|
50
|
+
// Ensure repos directory exists
|
|
51
|
+
if (!existsSync(reposDir)) {
|
|
52
|
+
try {
|
|
53
|
+
mkdirSync(reposDir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
return {
|
|
57
|
+
success: false,
|
|
58
|
+
error: "Failed to create repositories directory",
|
|
59
|
+
details: `Could not create directory at ${reposDir}: ${error instanceof Error ? error.message : String(error)}`,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Check if repository already exists
|
|
64
|
+
if (existsSync(repoPath)) {
|
|
65
|
+
// Verify it's a git repository
|
|
66
|
+
if (isGitRepository(repoPath)) {
|
|
67
|
+
return {
|
|
68
|
+
success: true,
|
|
69
|
+
message: "Repository already exists",
|
|
70
|
+
data: {
|
|
71
|
+
path: repoPath,
|
|
72
|
+
name: repoName,
|
|
73
|
+
action: "verified",
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
success: false,
|
|
79
|
+
error: "Directory exists but is not a Git repository",
|
|
80
|
+
details: `A non-Git directory already exists at ${repoPath}. Please remove it manually or choose a different repository name.`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// Clone the repository
|
|
84
|
+
try {
|
|
85
|
+
const cloneCmd = `git clone "${payload.repository_url}" "${repoPath}"`;
|
|
86
|
+
await execAsync(cloneCmd);
|
|
87
|
+
// Verify the clone was successful
|
|
88
|
+
if (!isGitRepository(repoPath)) {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: "Repository clone verification failed",
|
|
92
|
+
details: `Git clone command completed, but the cloned directory at ${repoPath} does not appear to be a valid Git repository.`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
success: true,
|
|
97
|
+
message: "Repository cloned successfully",
|
|
98
|
+
data: {
|
|
99
|
+
path: repoPath,
|
|
100
|
+
name: repoName,
|
|
101
|
+
repository_url: payload.repository_url,
|
|
102
|
+
action: "cloned",
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
error: "Failed to clone repository",
|
|
111
|
+
details: `Could not clone repository from ${payload.repository_url}: ${errorMessage}. Please verify the URL is correct and you have access to the repository.`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
return {
|
|
117
|
+
success: false,
|
|
118
|
+
error: "Repository operation failed",
|
|
119
|
+
details: error instanceof Error ? error.message : String(error),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/handlers/repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ApiResponse, TestMcpPayload } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Handle MCP connection test
|
|
4
|
+
* Tests connectivity and configuration of an MCP server
|
|
5
|
+
*
|
|
6
|
+
* Note: This is a placeholder implementation. The actual MCP testing logic
|
|
7
|
+
* would require integrating with the MCP SDK to test connections.
|
|
8
|
+
*/
|
|
9
|
+
export declare function handleTestMcp(payload: TestMcpPayload): Promise<ApiResponse>;
|
|
10
|
+
//# sourceMappingURL=testMcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testMcp.d.ts","sourceRoot":"","sources":["../../src/handlers/testMcp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/D;;;;;;GAMG;AACH,wBAAsB,aAAa,CAClC,OAAO,EAAE,cAAc,GACrB,OAAO,CAAC,WAAW,CAAC,CAsEtB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle MCP connection test
|
|
3
|
+
* Tests connectivity and configuration of an MCP server
|
|
4
|
+
*
|
|
5
|
+
* Note: This is a placeholder implementation. The actual MCP testing logic
|
|
6
|
+
* would require integrating with the MCP SDK to test connections.
|
|
7
|
+
*/
|
|
8
|
+
export async function handleTestMcp(payload) {
|
|
9
|
+
try {
|
|
10
|
+
// Validate payload
|
|
11
|
+
if (!payload.transportType) {
|
|
12
|
+
return {
|
|
13
|
+
success: false,
|
|
14
|
+
error: "MCP test requires transport type",
|
|
15
|
+
details: 'The transportType field is required and must be either "stdio" or "sse".',
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (payload.transportType !== "stdio" && payload.transportType !== "sse") {
|
|
19
|
+
return {
|
|
20
|
+
success: false,
|
|
21
|
+
error: "Invalid MCP transport type",
|
|
22
|
+
details: `Transport type "${payload.transportType}" is not supported. Must be either "stdio" or "sse".`,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Validate transport-specific requirements
|
|
26
|
+
if (payload.transportType === "stdio") {
|
|
27
|
+
if (!payload.command) {
|
|
28
|
+
return {
|
|
29
|
+
success: false,
|
|
30
|
+
error: "MCP stdio transport requires command",
|
|
31
|
+
details: "The command field is required when using stdio transport type.",
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else if (payload.transportType === "sse") {
|
|
36
|
+
if (!payload.serverUrl) {
|
|
37
|
+
return {
|
|
38
|
+
success: false,
|
|
39
|
+
error: "MCP SSE transport requires server URL",
|
|
40
|
+
details: "The serverUrl field is required when using SSE transport type.",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// TODO: Implement actual MCP connection testing
|
|
45
|
+
// This would involve:
|
|
46
|
+
// 1. Creating an MCP client with the provided configuration
|
|
47
|
+
// 2. Attempting to connect to the MCP server
|
|
48
|
+
// 3. Listing available tools/resources
|
|
49
|
+
// 4. Getting server info
|
|
50
|
+
// 5. Returning the results
|
|
51
|
+
return {
|
|
52
|
+
success: true,
|
|
53
|
+
message: "MCP connection test completed (placeholder implementation)",
|
|
54
|
+
data: {
|
|
55
|
+
transportType: payload.transportType,
|
|
56
|
+
tools: [],
|
|
57
|
+
serverInfo: {
|
|
58
|
+
name: "placeholder",
|
|
59
|
+
version: "0.0.0",
|
|
60
|
+
protocol: "mcp/1.0",
|
|
61
|
+
},
|
|
62
|
+
note: "This is a placeholder response. Full MCP testing will be implemented in a future update.",
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
return {
|
|
68
|
+
success: false,
|
|
69
|
+
error: "MCP connection test failed",
|
|
70
|
+
details: error instanceof Error ? error.message : String(error),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=testMcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testMcp.js","sourceRoot":"","sources":["../../src/handlers/testMcp.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAClC,OAAuB;IAEvB,IAAI,CAAC;QACJ,mBAAmB;QACnB,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAC5B,OAAO;gBACN,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,kCAAkC;gBACzC,OAAO,EACN,0EAA0E;aAC3E,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,aAAa,KAAK,OAAO,IAAI,OAAO,CAAC,aAAa,KAAK,KAAK,EAAE,CAAC;YAC1E,OAAO;gBACN,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,4BAA4B;gBACnC,OAAO,EAAE,mBAAmB,OAAO,CAAC,aAAa,sDAAsD;aACvG,CAAC;QACH,CAAC;QAED,2CAA2C;QAC3C,IAAI,OAAO,CAAC,aAAa,KAAK,OAAO,EAAE,CAAC;YACvC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBACtB,OAAO;oBACN,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,sCAAsC;oBAC7C,OAAO,EACN,gEAAgE;iBACjE,CAAC;YACH,CAAC;QACF,CAAC;aAAM,IAAI,OAAO,CAAC,aAAa,KAAK,KAAK,EAAE,CAAC;YAC5C,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;gBACxB,OAAO;oBACN,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,uCAAuC;oBAC9C,OAAO,EACN,gEAAgE;iBACjE,CAAC;YACH,CAAC;QACF,CAAC;QAED,gDAAgD;QAChD,sBAAsB;QACtB,4DAA4D;QAC5D,6CAA6C;QAC7C,uCAAuC;QACvC,yBAAyB;QACzB,2BAA2B;QAE3B,OAAO;YACN,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,4DAA4D;YACrE,IAAI,EAAE;gBACL,aAAa,EAAE,OAAO,CAAC,aAAa;gBACpC,KAAK,EAAE,EAAE;gBACT,UAAU,EAAE;oBACX,IAAI,EAAE,aAAa;oBACnB,OAAO,EAAE,OAAO;oBAChB,QAAQ,EAAE,SAAS;iBACnB;gBACD,IAAI,EAAE,0FAA0F;aAChG;SACD,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO;YACN,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,4BAA4B;YACnC,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC/D,CAAC;IACH,CAAC;AACF,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,cAAc,YAAY,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event emitted by CloudflareTunnelClient
|
|
3
|
+
*/
|
|
4
|
+
export interface CloudflareTunnelClientEvents {
|
|
5
|
+
connect: () => void;
|
|
6
|
+
connected: (connection: any) => void;
|
|
7
|
+
disconnect: (reason: string) => void;
|
|
8
|
+
error: (error: Error) => void;
|
|
9
|
+
ready: (tunnelUrl: string) => void;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACrC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAC9B,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cyrus-cloudflare-tunnel-client",
|
|
3
|
-
"version": "0.2.0-rc",
|
|
3
|
+
"version": "0.2.0-rc.2",
|
|
4
4
|
"description": "Cloudflare tunnel client for receiving config updates and webhooks from cyrus-hosted",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"import": "./dist/index.js"
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
14
17
|
"dependencies": {
|
|
15
18
|
"cloudflared": "^0.7.1"
|
|
16
19
|
},
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import type { ChildProcess } from "node:child_process";
|
|
2
|
-
import { EventEmitter } from "node:events";
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
4
|
-
import { bin, install, Tunnel } from "cloudflared";
|
|
5
|
-
import type { CloudflareTunnelClientEvents } from "./types.js";
|
|
6
|
-
|
|
7
|
-
export declare interface CloudflareTunnelClient {
|
|
8
|
-
on<K extends keyof CloudflareTunnelClientEvents>(
|
|
9
|
-
event: K,
|
|
10
|
-
listener: CloudflareTunnelClientEvents[K],
|
|
11
|
-
): this;
|
|
12
|
-
emit<K extends keyof CloudflareTunnelClientEvents>(
|
|
13
|
-
event: K,
|
|
14
|
-
...args: Parameters<CloudflareTunnelClientEvents[K]>
|
|
15
|
-
): boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Cloudflare tunnel client for establishing tunnels to local services
|
|
20
|
-
* Handles ONLY tunnel establishment - HTTP handling is done by SharedApplicationServer
|
|
21
|
-
*/
|
|
22
|
-
export class CloudflareTunnelClient extends EventEmitter {
|
|
23
|
-
private tunnelProcess: ChildProcess | null = null;
|
|
24
|
-
private tunnelUrl: string | null = null;
|
|
25
|
-
private connected = false;
|
|
26
|
-
private connectionCount = 0;
|
|
27
|
-
private cloudflareToken: string;
|
|
28
|
-
private localPort: number;
|
|
29
|
-
|
|
30
|
-
constructor(
|
|
31
|
-
cloudflareToken: string,
|
|
32
|
-
localPort: number,
|
|
33
|
-
onReady?: (tunnelUrl: string) => void,
|
|
34
|
-
) {
|
|
35
|
-
super();
|
|
36
|
-
this.cloudflareToken = cloudflareToken;
|
|
37
|
-
this.localPort = localPort;
|
|
38
|
-
|
|
39
|
-
// Set up onReady callback if provided
|
|
40
|
-
if (onReady) {
|
|
41
|
-
this.on("ready", onReady);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Start the Cloudflare tunnel
|
|
47
|
-
*/
|
|
48
|
-
async startTunnel(): Promise<void> {
|
|
49
|
-
try {
|
|
50
|
-
// Ensure cloudflared binary is installed
|
|
51
|
-
if (!existsSync(bin)) {
|
|
52
|
-
await install(bin);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
console.log(`Starting tunnel to localhost:${this.localPort}`);
|
|
56
|
-
|
|
57
|
-
// Create tunnel with token-based authentication (no URL needed for remotely-managed tunnels)
|
|
58
|
-
const tunnel = Tunnel.withToken(this.cloudflareToken);
|
|
59
|
-
|
|
60
|
-
// Listen for URL event (from ConfigHandler for token-based tunnels)
|
|
61
|
-
tunnel.on("url", (url: string) => {
|
|
62
|
-
// Ensure URL has protocol for token-based tunnels
|
|
63
|
-
if (!url.startsWith("http")) {
|
|
64
|
-
url = `https://${url}`;
|
|
65
|
-
}
|
|
66
|
-
if (!this.tunnelUrl) {
|
|
67
|
-
this.tunnelUrl = url;
|
|
68
|
-
this.emit("ready", this.tunnelUrl);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Listen for connection event (Cloudflare establishes 4 connections per tunnel)
|
|
73
|
-
tunnel.on("connected", (connection: any) => {
|
|
74
|
-
this.connectionCount++;
|
|
75
|
-
console.log(
|
|
76
|
-
`Cloudflare tunnel connection ${this.connectionCount}/4 established:`,
|
|
77
|
-
connection,
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
// Emit 'connected' event for each connection (for external listeners)
|
|
81
|
-
this.emit("connected", connection);
|
|
82
|
-
|
|
83
|
-
// Mark as connected on first connection, but log all 4
|
|
84
|
-
if (!this.connected) {
|
|
85
|
-
this.connected = true;
|
|
86
|
-
this.emit("connect");
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
// Listen for error event
|
|
91
|
-
tunnel.on("error", (error: Error) => {
|
|
92
|
-
this.emit("error", error);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// Listen for exit event
|
|
96
|
-
tunnel.on("exit", (code: number | null) => {
|
|
97
|
-
this.connected = false;
|
|
98
|
-
this.emit("disconnect", `Tunnel process exited with code ${code}`);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// Wait for tunnel URL to be available (with timeout)
|
|
102
|
-
await this.waitForTunnelToConnect(30000); // 30 second timeout
|
|
103
|
-
} catch (error) {
|
|
104
|
-
this.emit("error", error as Error);
|
|
105
|
-
throw error;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Wait for tunnel URL to be available
|
|
111
|
-
*/
|
|
112
|
-
private async waitForTunnelToConnect(timeout: number): Promise<void> {
|
|
113
|
-
const startTime = Date.now();
|
|
114
|
-
|
|
115
|
-
while (!this.connected) {
|
|
116
|
-
if (Date.now() - startTime > timeout) {
|
|
117
|
-
throw new Error("Timeout waiting for tunnel URL");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Get the tunnel URL
|
|
126
|
-
*/
|
|
127
|
-
getTunnelUrl(): string | null {
|
|
128
|
-
return this.tunnelUrl;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Check if client is connected
|
|
133
|
-
*/
|
|
134
|
-
isConnected(): boolean {
|
|
135
|
-
return this.connected;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Disconnect and cleanup
|
|
140
|
-
*/
|
|
141
|
-
disconnect(): void {
|
|
142
|
-
if (this.tunnelProcess) {
|
|
143
|
-
this.tunnelProcess.kill();
|
|
144
|
-
this.tunnelProcess = null;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
this.connected = false;
|
|
148
|
-
this.emit("disconnect", "Client disconnected");
|
|
149
|
-
}
|
|
150
|
-
}
|
package/src/ConfigApiClient.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Config API response from cyrus-hosted
|
|
3
|
-
*/
|
|
4
|
-
export interface ConfigApiResponse {
|
|
5
|
-
success: boolean;
|
|
6
|
-
config?: {
|
|
7
|
-
cloudflareToken: string;
|
|
8
|
-
apiKey: string;
|
|
9
|
-
};
|
|
10
|
-
error?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Client for retrieving configuration from cyrus-hosted
|
|
15
|
-
* Authenticates using auth keys provided during onboarding
|
|
16
|
-
*/
|
|
17
|
-
export class ConfigApiClient {
|
|
18
|
-
private static readonly CONFIG_API_URL = "https://app.atcyrus.com/api/config";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Retrieve configuration using an auth key
|
|
22
|
-
* @param authKey - The auth key provided during onboarding
|
|
23
|
-
* @returns Configuration containing Cloudflare tunnel token and API key
|
|
24
|
-
*/
|
|
25
|
-
static async getConfig(authKey: string): Promise<ConfigApiResponse> {
|
|
26
|
-
try {
|
|
27
|
-
// Validate auth key
|
|
28
|
-
if (
|
|
29
|
-
!authKey ||
|
|
30
|
-
typeof authKey !== "string" ||
|
|
31
|
-
authKey.trim().length === 0
|
|
32
|
-
) {
|
|
33
|
-
return {
|
|
34
|
-
success: false,
|
|
35
|
-
error: "Auth key is required",
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Call config API with auth key
|
|
40
|
-
const url = `${ConfigApiClient.CONFIG_API_URL}?auth_key=${encodeURIComponent(authKey)}`;
|
|
41
|
-
const response = await fetch(url);
|
|
42
|
-
|
|
43
|
-
if (!response.ok) {
|
|
44
|
-
const errorText = await response.text();
|
|
45
|
-
return {
|
|
46
|
-
success: false,
|
|
47
|
-
error: `Config API request failed: ${response.status} ${response.statusText} - ${errorText}`,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const data = (await response.json()) as ConfigApiResponse;
|
|
52
|
-
|
|
53
|
-
// Validate response structure
|
|
54
|
-
if (!data.success || !data.config) {
|
|
55
|
-
return {
|
|
56
|
-
success: false,
|
|
57
|
-
error: data.error || "Invalid response format from config API",
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Validate required fields
|
|
62
|
-
if (!data.config.cloudflareToken || !data.config.apiKey) {
|
|
63
|
-
return {
|
|
64
|
-
success: false,
|
|
65
|
-
error: "Config API response missing required fields",
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return data;
|
|
70
|
-
} catch (error) {
|
|
71
|
-
if (error instanceof Error) {
|
|
72
|
-
return {
|
|
73
|
-
success: false,
|
|
74
|
-
error: `Failed to retrieve config: ${error.message}`,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
return {
|
|
78
|
-
success: false,
|
|
79
|
-
error: "Failed to retrieve config: Unknown error",
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Check if a config response is valid and usable
|
|
86
|
-
*/
|
|
87
|
-
static isValid(response: ConfigApiResponse): boolean {
|
|
88
|
-
return (
|
|
89
|
-
response.success &&
|
|
90
|
-
!!response.config?.cloudflareToken &&
|
|
91
|
-
!!response.config?.apiKey
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
}
|