crukx-mcp 0.1.1 → 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.
- package/dist/bin/cli.js +57 -33
- package/package.json +2 -1
package/dist/bin/cli.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
6
6
|
import { dirname as dirname2 } from "path";
|
|
7
|
-
import { createInterface } from "readline";
|
|
8
7
|
|
|
9
8
|
// src/auth/index.ts
|
|
10
9
|
import { mkdirSync, writeFileSync, readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
@@ -60,43 +59,68 @@ import { fileURLToPath } from "url";
|
|
|
60
59
|
import { join as join2, dirname as pathDirname } from "path";
|
|
61
60
|
var __dirname = pathDirname(fileURLToPath(import.meta.url));
|
|
62
61
|
var program = new Command();
|
|
63
|
-
program.name("crukx").description("Crukx CLI \u2014 reliability control plane for autonomous software engineering").version("0.1.
|
|
64
|
-
program.command("login").description("Authenticate with Crukx").option("--api-url <url>", "Crukx gateway URL", getConfig().CRUKX_API_URL).
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
program.name("crukx").description("Crukx CLI \u2014 reliability control plane for autonomous software engineering").version("0.1.2");
|
|
63
|
+
program.command("login").description("Authenticate with Crukx via browser").option("--api-url <url>", "Crukx gateway URL", getConfig().CRUKX_API_URL).action(async (opts) => {
|
|
64
|
+
process.stderr.write("Opening browser to complete login\u2026\n");
|
|
65
|
+
let startData;
|
|
67
66
|
try {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
rl.close();
|
|
72
|
-
process.stderr.write("Authenticating...\n");
|
|
73
|
-
const res = await fetch(`${opts.apiUrl}/v1/mcp/auth/login`, {
|
|
74
|
-
method: "POST",
|
|
75
|
-
headers: { "Content-Type": "application/json" },
|
|
76
|
-
body: JSON.stringify({ email, password, workspace_id: workspaceId })
|
|
77
|
-
});
|
|
78
|
-
if (!res.ok) {
|
|
79
|
-
const err = await res.json();
|
|
80
|
-
process.stderr.write(`Login failed: ${err.error ?? res.status}
|
|
81
|
-
`);
|
|
82
|
-
process.exit(1);
|
|
83
|
-
}
|
|
84
|
-
const data = await res.json();
|
|
85
|
-
mkdirSync2(dirname2(CONFIG_PATH), { recursive: true });
|
|
86
|
-
saveAuth({
|
|
87
|
-
access_token: data.access_token,
|
|
88
|
-
workspace_id: data.workspace_id,
|
|
89
|
-
expires_at: data.expires_at,
|
|
90
|
-
api_url: opts.apiUrl
|
|
91
|
-
});
|
|
92
|
-
process.stderr.write(`\u2705 Logged in. Config saved to ${CONFIG_PATH}
|
|
93
|
-
`);
|
|
67
|
+
const res = await fetch(`${opts.apiUrl}/v1/mcp/auth/device/start`, { method: "POST" });
|
|
68
|
+
if (!res.ok) throw new Error(`Gateway error: ${res.status}`);
|
|
69
|
+
startData = await res.json();
|
|
94
70
|
} catch (err) {
|
|
95
|
-
|
|
96
|
-
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
71
|
+
process.stderr.write(`Failed to start login: ${err instanceof Error ? err.message : String(err)}
|
|
97
72
|
`);
|
|
98
73
|
process.exit(1);
|
|
99
74
|
}
|
|
75
|
+
const { default: open } = await import("open");
|
|
76
|
+
await open(startData.url);
|
|
77
|
+
process.stderr.write(`
|
|
78
|
+
If the browser didn't open, visit:
|
|
79
|
+
${startData.url}
|
|
80
|
+
|
|
81
|
+
Waiting for you to complete login in the browser\u2026
|
|
82
|
+
`);
|
|
83
|
+
const deadline = new Date(startData.expires_at).getTime();
|
|
84
|
+
const interval = startData.poll_interval_ms ?? 2e3;
|
|
85
|
+
while (Date.now() < deadline) {
|
|
86
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
87
|
+
try {
|
|
88
|
+
const pollRes = await fetch(`${opts.apiUrl}/v1/mcp/auth/device/poll/${startData.code}`);
|
|
89
|
+
if (pollRes.status === 410) {
|
|
90
|
+
process.stderr.write("Code expired. Run crukx login again.\n");
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
if (!pollRes.ok) continue;
|
|
94
|
+
const poll = await pollRes.json();
|
|
95
|
+
if (poll.status !== "complete") continue;
|
|
96
|
+
const claimRes = await fetch(`${opts.apiUrl}/v1/mcp/auth/device/claim`, {
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers: { "Content-Type": "application/json" },
|
|
99
|
+
body: JSON.stringify({ code: startData.code })
|
|
100
|
+
});
|
|
101
|
+
if (!claimRes.ok) {
|
|
102
|
+
const err = await claimRes.json();
|
|
103
|
+
process.stderr.write(`Claim failed: ${err.error ?? claimRes.status}
|
|
104
|
+
`);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
const claimed = await claimRes.json();
|
|
108
|
+
mkdirSync2(dirname2(CONFIG_PATH), { recursive: true });
|
|
109
|
+
saveAuth({
|
|
110
|
+
access_token: claimed.access_token,
|
|
111
|
+
workspace_id: claimed.workspace_id,
|
|
112
|
+
expires_at: claimed.expires_at,
|
|
113
|
+
api_url: opts.apiUrl
|
|
114
|
+
});
|
|
115
|
+
process.stderr.write(`
|
|
116
|
+
\u2705 Logged in! Config saved to ${CONFIG_PATH}
|
|
117
|
+
`);
|
|
118
|
+
process.exit(0);
|
|
119
|
+
} catch {
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
process.stderr.write("Login timed out. Run crukx login again.\n");
|
|
123
|
+
process.exit(1);
|
|
100
124
|
});
|
|
101
125
|
program.command("logout").description("Revoke current session").action(() => {
|
|
102
126
|
clearAuth();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crukx-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Crukx MCP server — reliability control plane for autonomous software engineering",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/server.js",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
37
37
|
"commander": "^12.1.0",
|
|
38
38
|
"dotenv": "^16.4.5",
|
|
39
|
+
"open": "^10.1.0",
|
|
39
40
|
"zod": "^3.23.8"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|