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,40 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import type { CloudflareTunnelClientEvents } from "./types.js";
|
|
3
|
+
export declare interface CloudflareTunnelClient {
|
|
4
|
+
on<K extends keyof CloudflareTunnelClientEvents>(event: K, listener: CloudflareTunnelClientEvents[K]): this;
|
|
5
|
+
emit<K extends keyof CloudflareTunnelClientEvents>(event: K, ...args: Parameters<CloudflareTunnelClientEvents[K]>): boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Cloudflare tunnel client for establishing tunnels to local services
|
|
9
|
+
* Handles ONLY tunnel establishment - HTTP handling is done by SharedApplicationServer
|
|
10
|
+
*/
|
|
11
|
+
export declare class CloudflareTunnelClient extends EventEmitter {
|
|
12
|
+
private tunnelProcess;
|
|
13
|
+
private tunnelUrl;
|
|
14
|
+
private connected;
|
|
15
|
+
private connectionCount;
|
|
16
|
+
private cloudflareToken;
|
|
17
|
+
private localPort;
|
|
18
|
+
constructor(cloudflareToken: string, localPort: number, onReady?: (tunnelUrl: string) => void);
|
|
19
|
+
/**
|
|
20
|
+
* Start the Cloudflare tunnel
|
|
21
|
+
*/
|
|
22
|
+
startTunnel(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Wait for tunnel URL to be available
|
|
25
|
+
*/
|
|
26
|
+
private waitForTunnelToConnect;
|
|
27
|
+
/**
|
|
28
|
+
* Get the tunnel URL
|
|
29
|
+
*/
|
|
30
|
+
getTunnelUrl(): string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Check if client is connected
|
|
33
|
+
*/
|
|
34
|
+
isConnected(): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Disconnect and cleanup
|
|
37
|
+
*/
|
|
38
|
+
disconnect(): void;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=CloudflareTunnelClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CloudflareTunnelClient.d.ts","sourceRoot":"","sources":["../src/CloudflareTunnelClient.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAE/D,MAAM,CAAC,OAAO,WAAW,sBAAsB;IAC9C,EAAE,CAAC,CAAC,SAAS,MAAM,4BAA4B,EAC9C,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,4BAA4B,CAAC,CAAC,CAAC,GACvC,IAAI,CAAC;IACR,IAAI,CAAC,CAAC,SAAS,MAAM,4BAA4B,EAChD,KAAK,EAAE,CAAC,EACR,GAAG,IAAI,EAAE,UAAU,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC,GAClD,OAAO,CAAC;CACX;AAED;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,YAAY;IACvD,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,SAAS,CAAS;gBAGzB,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI;IAYtC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA6DlC;;OAEG;YACW,sBAAsB;IAYpC;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,UAAU,IAAI,IAAI;CASlB"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { bin, install, Tunnel } from "cloudflared";
|
|
4
|
+
/**
|
|
5
|
+
* Cloudflare tunnel client for establishing tunnels to local services
|
|
6
|
+
* Handles ONLY tunnel establishment - HTTP handling is done by SharedApplicationServer
|
|
7
|
+
*/
|
|
8
|
+
export class CloudflareTunnelClient extends EventEmitter {
|
|
9
|
+
tunnelProcess = null;
|
|
10
|
+
tunnelUrl = null;
|
|
11
|
+
connected = false;
|
|
12
|
+
connectionCount = 0;
|
|
13
|
+
cloudflareToken;
|
|
14
|
+
localPort;
|
|
15
|
+
constructor(cloudflareToken, localPort, onReady) {
|
|
16
|
+
super();
|
|
17
|
+
this.cloudflareToken = cloudflareToken;
|
|
18
|
+
this.localPort = localPort;
|
|
19
|
+
// Set up onReady callback if provided
|
|
20
|
+
if (onReady) {
|
|
21
|
+
this.on("ready", onReady);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Start the Cloudflare tunnel
|
|
26
|
+
*/
|
|
27
|
+
async startTunnel() {
|
|
28
|
+
try {
|
|
29
|
+
// Ensure cloudflared binary is installed
|
|
30
|
+
if (!existsSync(bin)) {
|
|
31
|
+
await install(bin);
|
|
32
|
+
}
|
|
33
|
+
console.log(`Starting tunnel to localhost:${this.localPort}`);
|
|
34
|
+
// Create tunnel with token-based authentication (no URL needed for remotely-managed tunnels)
|
|
35
|
+
const tunnel = Tunnel.withToken(this.cloudflareToken);
|
|
36
|
+
// Listen for URL event (from ConfigHandler for token-based tunnels)
|
|
37
|
+
tunnel.on("url", (url) => {
|
|
38
|
+
// Ensure URL has protocol for token-based tunnels
|
|
39
|
+
if (!url.startsWith("http")) {
|
|
40
|
+
url = `https://${url}`;
|
|
41
|
+
}
|
|
42
|
+
if (!this.tunnelUrl) {
|
|
43
|
+
this.tunnelUrl = url;
|
|
44
|
+
this.emit("ready", this.tunnelUrl);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
// Listen for connection event (Cloudflare establishes 4 connections per tunnel)
|
|
48
|
+
tunnel.on("connected", (connection) => {
|
|
49
|
+
this.connectionCount++;
|
|
50
|
+
console.log(`Cloudflare tunnel connection ${this.connectionCount}/4 established:`, connection);
|
|
51
|
+
// Emit 'connected' event for each connection (for external listeners)
|
|
52
|
+
this.emit("connected", connection);
|
|
53
|
+
// Mark as connected on first connection, but log all 4
|
|
54
|
+
if (!this.connected) {
|
|
55
|
+
this.connected = true;
|
|
56
|
+
this.emit("connect");
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
// Listen for error event
|
|
60
|
+
tunnel.on("error", (error) => {
|
|
61
|
+
this.emit("error", error);
|
|
62
|
+
});
|
|
63
|
+
// Listen for exit event
|
|
64
|
+
tunnel.on("exit", (code) => {
|
|
65
|
+
this.connected = false;
|
|
66
|
+
this.emit("disconnect", `Tunnel process exited with code ${code}`);
|
|
67
|
+
});
|
|
68
|
+
// Wait for tunnel URL to be available (with timeout)
|
|
69
|
+
await this.waitForTunnelToConnect(30000); // 30 second timeout
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
this.emit("error", error);
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Wait for tunnel URL to be available
|
|
78
|
+
*/
|
|
79
|
+
async waitForTunnelToConnect(timeout) {
|
|
80
|
+
const startTime = Date.now();
|
|
81
|
+
while (!this.connected) {
|
|
82
|
+
if (Date.now() - startTime > timeout) {
|
|
83
|
+
throw new Error("Timeout waiting for tunnel URL");
|
|
84
|
+
}
|
|
85
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the tunnel URL
|
|
90
|
+
*/
|
|
91
|
+
getTunnelUrl() {
|
|
92
|
+
return this.tunnelUrl;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Check if client is connected
|
|
96
|
+
*/
|
|
97
|
+
isConnected() {
|
|
98
|
+
return this.connected;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Disconnect and cleanup
|
|
102
|
+
*/
|
|
103
|
+
disconnect() {
|
|
104
|
+
if (this.tunnelProcess) {
|
|
105
|
+
this.tunnelProcess.kill();
|
|
106
|
+
this.tunnelProcess = null;
|
|
107
|
+
}
|
|
108
|
+
this.connected = false;
|
|
109
|
+
this.emit("disconnect", "Client disconnected");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=CloudflareTunnelClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CloudflareTunnelClient.js","sourceRoot":"","sources":["../src/CloudflareTunnelClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
* Client for retrieving configuration from cyrus-hosted
|
|
14
|
+
* Authenticates using auth keys provided during onboarding
|
|
15
|
+
*/
|
|
16
|
+
export declare class ConfigApiClient {
|
|
17
|
+
private static readonly CONFIG_API_URL;
|
|
18
|
+
/**
|
|
19
|
+
* Retrieve configuration using an auth key
|
|
20
|
+
* @param authKey - The auth key provided during onboarding
|
|
21
|
+
* @returns Configuration containing Cloudflare tunnel token and API key
|
|
22
|
+
*/
|
|
23
|
+
static getConfig(authKey: string): Promise<ConfigApiResponse>;
|
|
24
|
+
/**
|
|
25
|
+
* Check if a config response is valid and usable
|
|
26
|
+
*/
|
|
27
|
+
static isValid(response: ConfigApiResponse): boolean;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=ConfigApiClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigApiClient.d.ts","sourceRoot":"","sources":["../src/ConfigApiClient.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE;QACR,eAAe,EAAE,MAAM,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC;KACf,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,qBAAa,eAAe;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAwC;IAE9E;;;;OAIG;WACU,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA2DnE;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO;CAOpD"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client for retrieving configuration from cyrus-hosted
|
|
3
|
+
* Authenticates using auth keys provided during onboarding
|
|
4
|
+
*/
|
|
5
|
+
export class ConfigApiClient {
|
|
6
|
+
static CONFIG_API_URL = "https://app.atcyrus.com/api/config";
|
|
7
|
+
/**
|
|
8
|
+
* Retrieve configuration using an auth key
|
|
9
|
+
* @param authKey - The auth key provided during onboarding
|
|
10
|
+
* @returns Configuration containing Cloudflare tunnel token and API key
|
|
11
|
+
*/
|
|
12
|
+
static async getConfig(authKey) {
|
|
13
|
+
try {
|
|
14
|
+
// Validate auth key
|
|
15
|
+
if (!authKey ||
|
|
16
|
+
typeof authKey !== "string" ||
|
|
17
|
+
authKey.trim().length === 0) {
|
|
18
|
+
return {
|
|
19
|
+
success: false,
|
|
20
|
+
error: "Auth key is required",
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// Call config API with auth key
|
|
24
|
+
const url = `${ConfigApiClient.CONFIG_API_URL}?auth_key=${encodeURIComponent(authKey)}`;
|
|
25
|
+
const response = await fetch(url);
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
const errorText = await response.text();
|
|
28
|
+
return {
|
|
29
|
+
success: false,
|
|
30
|
+
error: `Config API request failed: ${response.status} ${response.statusText} - ${errorText}`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const data = (await response.json());
|
|
34
|
+
// Validate response structure
|
|
35
|
+
if (!data.success || !data.config) {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
error: data.error || "Invalid response format from config API",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// Validate required fields
|
|
42
|
+
if (!data.config.cloudflareToken || !data.config.apiKey) {
|
|
43
|
+
return {
|
|
44
|
+
success: false,
|
|
45
|
+
error: "Config API response missing required fields",
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return data;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error instanceof Error) {
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
error: `Failed to retrieve config: ${error.message}`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
success: false,
|
|
59
|
+
error: "Failed to retrieve config: Unknown error",
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if a config response is valid and usable
|
|
65
|
+
*/
|
|
66
|
+
static isValid(response) {
|
|
67
|
+
return (response.success &&
|
|
68
|
+
!!response.config?.cloudflareToken &&
|
|
69
|
+
!!response.config?.apiKey);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=ConfigApiClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigApiClient.js","sourceRoot":"","sources":["../src/ConfigApiClient.ts"],"names":[],"mappings":"AAYA;;;GAGG;AACH,MAAM,OAAO,eAAe;IACnB,MAAM,CAAU,cAAc,GAAG,oCAAoC,CAAC;IAE9E;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,OAAe;QACrC,IAAI,CAAC;YACJ,oBAAoB;YACpB,IACC,CAAC,OAAO;gBACR,OAAO,OAAO,KAAK,QAAQ;gBAC3B,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAC1B,CAAC;gBACF,OAAO;oBACN,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,sBAAsB;iBAC7B,CAAC;YACH,CAAC;YAED,gCAAgC;YAChC,MAAM,GAAG,GAAG,GAAG,eAAe,CAAC,cAAc,aAAa,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;YACxF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;YAElC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAClB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACxC,OAAO;oBACN,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,8BAA8B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,MAAM,SAAS,EAAE;iBAC5F,CAAC;YACH,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAsB,CAAC;YAE1D,8BAA8B;YAC9B,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACnC,OAAO;oBACN,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,yCAAyC;iBAC9D,CAAC;YACH,CAAC;YAED,2BAA2B;YAC3B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACzD,OAAO;oBACN,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,6CAA6C;iBACpD,CAAC;YACH,CAAC;YAED,OAAO,IAAI,CAAC;QACb,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;gBAC5B,OAAO;oBACN,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,8BAA8B,KAAK,CAAC,OAAO,EAAE;iBACpD,CAAC;YACH,CAAC;YACD,OAAO;gBACN,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,0CAA0C;aACjD,CAAC;QACH,CAAC;IACF,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,QAA2B;QACzC,OAAO,CACN,QAAQ,CAAC,OAAO;YAChB,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe;YAClC,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CACzB,CAAC;IACH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ApiResponse, ConfigureMcpPayload } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Handle MCP server configuration
|
|
4
|
+
* Writes individual MCP config files to ~/.cyrus/mcp-{slug}.json
|
|
5
|
+
*/
|
|
6
|
+
export declare function handleConfigureMcp(payload: ConfigureMcpPayload, cyrusHome: string): Promise<ApiResponse>;
|
|
7
|
+
//# sourceMappingURL=configureMcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configureMcp.d.ts","sourceRoot":"","sources":["../../src/handlers/configureMcp.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEpE;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,mBAAmB,EAC5B,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,CA2EtB"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Handle MCP server configuration
|
|
5
|
+
* Writes individual MCP config files to ~/.cyrus/mcp-{slug}.json
|
|
6
|
+
*/
|
|
7
|
+
export async function handleConfigureMcp(payload, cyrusHome) {
|
|
8
|
+
try {
|
|
9
|
+
// Validate payload
|
|
10
|
+
if (!payload.mcpServers || typeof payload.mcpServers !== "object") {
|
|
11
|
+
return {
|
|
12
|
+
success: false,
|
|
13
|
+
error: "MCP configuration requires server definitions",
|
|
14
|
+
details: "The mcpServers field must be an object containing server configurations.",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const serverSlugs = Object.keys(payload.mcpServers);
|
|
18
|
+
if (serverSlugs.length === 0) {
|
|
19
|
+
return {
|
|
20
|
+
success: false,
|
|
21
|
+
error: "No MCP servers to configure",
|
|
22
|
+
details: "At least one MCP server configuration must be provided.",
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Ensure the .cyrus directory exists
|
|
26
|
+
if (!existsSync(cyrusHome)) {
|
|
27
|
+
mkdirSync(cyrusHome, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
const mcpFilesWritten = [];
|
|
30
|
+
// Write each MCP server configuration to its own file
|
|
31
|
+
for (const slug of serverSlugs) {
|
|
32
|
+
const serverConfig = payload.mcpServers[slug];
|
|
33
|
+
const mcpFilePath = join(cyrusHome, `mcp-${slug}.json`);
|
|
34
|
+
// Perform environment variable substitution
|
|
35
|
+
const processedConfig = performEnvSubstitution(serverConfig);
|
|
36
|
+
// Write the config file
|
|
37
|
+
try {
|
|
38
|
+
const configData = {
|
|
39
|
+
mcpServers: {
|
|
40
|
+
[slug]: processedConfig,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
writeFileSync(mcpFilePath, JSON.stringify(configData, null, 2), "utf-8");
|
|
44
|
+
mcpFilesWritten.push(mcpFilePath);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
error: `Failed to save MCP server configuration for "${slug}"`,
|
|
50
|
+
details: `Could not write to ${mcpFilePath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
success: true,
|
|
56
|
+
message: "MCP configuration files written successfully",
|
|
57
|
+
data: {
|
|
58
|
+
mcpFilesWritten,
|
|
59
|
+
serversConfigured: serverSlugs,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
error: "MCP server configuration failed",
|
|
67
|
+
details: error instanceof Error ? error.message : String(error),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Perform environment variable substitution on a config object
|
|
73
|
+
* Replaces ${VAR_NAME} placeholders with values from the env map
|
|
74
|
+
*/
|
|
75
|
+
function performEnvSubstitution(config) {
|
|
76
|
+
if (!config)
|
|
77
|
+
return config;
|
|
78
|
+
// Get environment variables from the config
|
|
79
|
+
const env = config.env || {};
|
|
80
|
+
// Deep clone the config to avoid mutations
|
|
81
|
+
const processed = JSON.parse(JSON.stringify(config));
|
|
82
|
+
// Recursively process all string values
|
|
83
|
+
function processValue(value) {
|
|
84
|
+
if (typeof value === "string") {
|
|
85
|
+
// Replace ${VAR_NAME} with the actual value from env
|
|
86
|
+
return value.replace(/\$\{([^}]+)\}/g, (match, varName) => {
|
|
87
|
+
return env[varName] || match;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (Array.isArray(value)) {
|
|
91
|
+
return value.map(processValue);
|
|
92
|
+
}
|
|
93
|
+
if (typeof value === "object" && value !== null) {
|
|
94
|
+
const result = {};
|
|
95
|
+
for (const key of Object.keys(value)) {
|
|
96
|
+
result[key] = processValue(value[key]);
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
return processValue(processed);
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=configureMcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configureMcp.js","sourceRoot":"","sources":["../../src/handlers/configureMcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ApiResponse, CyrusConfigPayload } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Handle Cyrus configuration update
|
|
4
|
+
* Updates the ~/.cyrus/config.json file with the provided configuration
|
|
5
|
+
*/
|
|
6
|
+
export declare function handleCyrusConfig(payload: CyrusConfigPayload, cyrusHome: string): Promise<ApiResponse>;
|
|
7
|
+
/**
|
|
8
|
+
* Read current Cyrus configuration
|
|
9
|
+
*/
|
|
10
|
+
export declare function readCyrusConfig(cyrusHome: string): any;
|
|
11
|
+
//# sourceMappingURL=cyrusConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cyrusConfig.d.ts","sourceRoot":"","sources":["../../src/handlers/cyrusConfig.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEnE;;;GAGG;AACH,wBAAsB,iBAAiB,CACtC,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,CA2JtB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,CAatD"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Handle Cyrus configuration update
|
|
5
|
+
* Updates the ~/.cyrus/config.json file with the provided configuration
|
|
6
|
+
*/
|
|
7
|
+
export async function handleCyrusConfig(payload, cyrusHome) {
|
|
8
|
+
try {
|
|
9
|
+
// Validate payload
|
|
10
|
+
if (!payload.repositories || !Array.isArray(payload.repositories)) {
|
|
11
|
+
return {
|
|
12
|
+
success: false,
|
|
13
|
+
error: "Configuration update requires repositories array",
|
|
14
|
+
details: "The repositories field must be provided as an array, even if empty.",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
// Validate each repository has required fields
|
|
18
|
+
for (const repo of payload.repositories) {
|
|
19
|
+
if (!repo.id || !repo.name || !repo.repositoryPath || !repo.baseBranch) {
|
|
20
|
+
const missingFields = [];
|
|
21
|
+
if (!repo.id)
|
|
22
|
+
missingFields.push("id");
|
|
23
|
+
if (!repo.name)
|
|
24
|
+
missingFields.push("name");
|
|
25
|
+
if (!repo.repositoryPath)
|
|
26
|
+
missingFields.push("repositoryPath");
|
|
27
|
+
if (!repo.baseBranch)
|
|
28
|
+
missingFields.push("baseBranch");
|
|
29
|
+
return {
|
|
30
|
+
success: false,
|
|
31
|
+
error: "Repository configuration is incomplete",
|
|
32
|
+
details: `Repository "${repo.name || "unknown"}" is missing required fields: ${missingFields.join(", ")}`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const configPath = join(cyrusHome, "config.json");
|
|
37
|
+
// Ensure the .cyrus directory exists
|
|
38
|
+
const configDir = dirname(configPath);
|
|
39
|
+
if (!existsSync(configDir)) {
|
|
40
|
+
mkdirSync(configDir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
// Build the config object with repositories and optional settings
|
|
43
|
+
const repositories = payload.repositories.map((repo) => {
|
|
44
|
+
const repoConfig = {
|
|
45
|
+
id: repo.id,
|
|
46
|
+
name: repo.name,
|
|
47
|
+
repositoryPath: repo.repositoryPath,
|
|
48
|
+
baseBranch: repo.baseBranch,
|
|
49
|
+
};
|
|
50
|
+
// Add optional Linear fields
|
|
51
|
+
if (repo.linearWorkspaceId) {
|
|
52
|
+
repoConfig.linearWorkspaceId = repo.linearWorkspaceId;
|
|
53
|
+
}
|
|
54
|
+
if (repo.linearToken) {
|
|
55
|
+
repoConfig.linearToken = repo.linearToken;
|
|
56
|
+
}
|
|
57
|
+
// Set workspaceBaseDir (use provided or default to ~/.cyrus/workspaces)
|
|
58
|
+
repoConfig.workspaceBaseDir =
|
|
59
|
+
repo.workspaceBaseDir || join(cyrusHome, "workspaces");
|
|
60
|
+
// Set isActive (defaults to true)
|
|
61
|
+
repoConfig.isActive = repo.isActive !== false;
|
|
62
|
+
// Optional arrays and objects
|
|
63
|
+
if (repo.allowedTools && repo.allowedTools.length > 0) {
|
|
64
|
+
repoConfig.allowedTools = repo.allowedTools;
|
|
65
|
+
}
|
|
66
|
+
if (repo.mcpConfigPath && repo.mcpConfigPath.length > 0) {
|
|
67
|
+
repoConfig.mcpConfigPath = repo.mcpConfigPath;
|
|
68
|
+
}
|
|
69
|
+
if (repo.teamKeys) {
|
|
70
|
+
repoConfig.teamKeys = repo.teamKeys;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
repoConfig.teamKeys = [];
|
|
74
|
+
}
|
|
75
|
+
if (repo.labelPrompts && Object.keys(repo.labelPrompts).length > 0) {
|
|
76
|
+
repoConfig.labelPrompts = repo.labelPrompts;
|
|
77
|
+
}
|
|
78
|
+
return repoConfig;
|
|
79
|
+
});
|
|
80
|
+
// Build complete config
|
|
81
|
+
const config = {
|
|
82
|
+
repositories,
|
|
83
|
+
};
|
|
84
|
+
// Add optional global settings
|
|
85
|
+
if (payload.disallowedTools && payload.disallowedTools.length > 0) {
|
|
86
|
+
config.disallowedTools = payload.disallowedTools;
|
|
87
|
+
}
|
|
88
|
+
if (payload.ngrokAuthToken) {
|
|
89
|
+
config.ngrokAuthToken = payload.ngrokAuthToken;
|
|
90
|
+
}
|
|
91
|
+
if (payload.stripeCustomerId) {
|
|
92
|
+
config.stripeCustomerId = payload.stripeCustomerId;
|
|
93
|
+
}
|
|
94
|
+
if (payload.defaultModel) {
|
|
95
|
+
config.defaultModel = payload.defaultModel;
|
|
96
|
+
}
|
|
97
|
+
if (payload.defaultFallbackModel) {
|
|
98
|
+
config.defaultFallbackModel = payload.defaultFallbackModel;
|
|
99
|
+
}
|
|
100
|
+
if (payload.global_setup_script) {
|
|
101
|
+
config.global_setup_script = payload.global_setup_script;
|
|
102
|
+
}
|
|
103
|
+
// Backup existing config if requested
|
|
104
|
+
if (payload.backupConfig && existsSync(configPath)) {
|
|
105
|
+
try {
|
|
106
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
107
|
+
const backupPath = join(cyrusHome, `config.backup-${timestamp}.json`);
|
|
108
|
+
const existingConfig = readFileSync(configPath, "utf-8");
|
|
109
|
+
writeFileSync(backupPath, existingConfig, "utf-8");
|
|
110
|
+
}
|
|
111
|
+
catch (backupError) {
|
|
112
|
+
// Log but don't fail - backup is not critical
|
|
113
|
+
console.warn(`Failed to backup config: ${backupError instanceof Error ? backupError.message : String(backupError)}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Write config file
|
|
117
|
+
try {
|
|
118
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
119
|
+
return {
|
|
120
|
+
success: true,
|
|
121
|
+
message: "Cyrus configuration updated successfully",
|
|
122
|
+
data: {
|
|
123
|
+
configPath,
|
|
124
|
+
repositoriesCount: repositories.length,
|
|
125
|
+
restartCyrus: payload.restartCyrus || false,
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
return {
|
|
131
|
+
success: false,
|
|
132
|
+
error: "Failed to save configuration file",
|
|
133
|
+
details: `Could not write configuration to ${configPath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
return {
|
|
139
|
+
success: false,
|
|
140
|
+
error: "Configuration update failed",
|
|
141
|
+
details: error instanceof Error ? error.message : String(error),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Read current Cyrus configuration
|
|
147
|
+
*/
|
|
148
|
+
export function readCyrusConfig(cyrusHome) {
|
|
149
|
+
const configPath = join(cyrusHome, "config.json");
|
|
150
|
+
if (!existsSync(configPath)) {
|
|
151
|
+
return { repositories: [] };
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const data = readFileSync(configPath, "utf-8");
|
|
155
|
+
return JSON.parse(data);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return { repositories: [] };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=cyrusConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cyrusConfig.js","sourceRoot":"","sources":["../../src/handlers/cyrusConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ApiResponse, CyrusEnvPayload } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Handle Cyrus environment variables update
|
|
4
|
+
* Primarily used to update/provide the Claude API token
|
|
5
|
+
*/
|
|
6
|
+
export declare function handleCyrusEnv(payload: CyrusEnvPayload, cyrusHome: string): Promise<ApiResponse>;
|
|
7
|
+
//# sourceMappingURL=cyrusEnv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cyrusEnv.d.ts","sourceRoot":"","sources":["../../src/handlers/cyrusEnv.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEhE;;;GAGG;AACH,wBAAsB,cAAc,CACnC,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,CAwHtB"}
|