crukx-mcp 0.1.0 → 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 CHANGED
@@ -1,60 +1,126 @@
1
1
  #!/usr/bin/env node
2
- #!/usr/bin/env node
3
- import {
4
- CONFIG_PATH,
5
- clearAuth,
6
- getConfig,
7
- loadAuth,
8
- saveAuth
9
- } from "../chunk-5FGEDZZW.js";
10
2
 
11
3
  // src/bin/cli.ts
12
4
  import { Command } from "commander";
13
- import { mkdirSync } from "fs";
5
+ import { mkdirSync as mkdirSync2 } from "fs";
6
+ import { dirname as dirname2 } from "path";
7
+
8
+ // src/auth/index.ts
9
+ import { mkdirSync, writeFileSync, readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
14
10
  import { dirname } from "path";
15
- import { createInterface } from "readline";
11
+
12
+ // src/config/index.ts
13
+ import "dotenv/config";
14
+ import { z } from "zod";
15
+ import { homedir } from "os";
16
+ import { join } from "path";
17
+ import { readFileSync, existsSync } from "fs";
18
+ var ConfigSchema = z.object({
19
+ CRUKX_API_URL: z.string().url().default("https://crukx-gateway.salmonisland-7ebc5692.centralindia.azurecontainerapps.io"),
20
+ CRUKX_ACCESS_TOKEN: z.string().optional(),
21
+ CRUKX_WORKSPACE_ID: z.string().optional()
22
+ });
23
+ function loadLocalConfig() {
24
+ const configPath = join(homedir(), ".crukx", "config.json");
25
+ if (!existsSync(configPath)) return {};
26
+ try {
27
+ return JSON.parse(readFileSync(configPath, "utf-8"));
28
+ } catch {
29
+ return {};
30
+ }
31
+ }
32
+ function getConfig() {
33
+ const local = loadLocalConfig();
34
+ return ConfigSchema.parse({ ...local, ...process.env });
35
+ }
36
+ var CONFIG_PATH = join(homedir(), ".crukx", "config.json");
37
+
38
+ // src/auth/index.ts
39
+ function saveAuth(auth) {
40
+ mkdirSync(dirname(CONFIG_PATH), { recursive: true });
41
+ const existing = loadAuth() ?? {};
42
+ writeFileSync(CONFIG_PATH, JSON.stringify({ ...existing, ...auth }, null, 2));
43
+ }
44
+ function loadAuth() {
45
+ if (!existsSync2(CONFIG_PATH)) return null;
46
+ try {
47
+ return JSON.parse(readFileSync2(CONFIG_PATH, "utf-8"));
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+ function clearAuth() {
53
+ if (existsSync2(CONFIG_PATH)) writeFileSync(CONFIG_PATH, "{}");
54
+ }
55
+
56
+ // src/bin/cli.ts
16
57
  import { spawn } from "child_process";
17
58
  import { fileURLToPath } from "url";
18
- import { join, dirname as pathDirname } from "path";
59
+ import { join as join2, dirname as pathDirname } from "path";
19
60
  var __dirname = pathDirname(fileURLToPath(import.meta.url));
20
61
  var program = new Command();
21
- program.name("crukx").description("Crukx CLI \u2014 reliability control plane for autonomous software engineering").version("0.1.0");
22
- program.command("login").description("Authenticate with Crukx").option("--api-url <url>", "Crukx gateway URL", getConfig().CRUKX_API_URL).option("--workspace-id <id>", "Workspace ID").action(async (opts) => {
23
- const rl = createInterface({ input: process.stdin, output: process.stderr });
24
- const ask = (q) => new Promise((r) => rl.question(q, r));
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;
25
66
  try {
26
- const email = await ask("Email: ");
27
- const password = await ask("Password: ");
28
- const workspaceId = opts.workspaceId ?? await ask("Workspace ID: ");
29
- rl.close();
30
- process.stderr.write("Authenticating...\n");
31
- const res = await fetch(`${opts.apiUrl}/v1/mcp/auth/login`, {
32
- method: "POST",
33
- headers: { "Content-Type": "application/json" },
34
- body: JSON.stringify({ email, password, workspace_id: workspaceId })
35
- });
36
- if (!res.ok) {
37
- const err = await res.json();
38
- process.stderr.write(`Login failed: ${err.error ?? res.status}
39
- `);
40
- process.exit(1);
41
- }
42
- const data = await res.json();
43
- mkdirSync(dirname(CONFIG_PATH), { recursive: true });
44
- saveAuth({
45
- access_token: data.access_token,
46
- workspace_id: data.workspace_id,
47
- expires_at: data.expires_at,
48
- api_url: opts.apiUrl
49
- });
50
- process.stderr.write(`\u2705 Logged in. Config saved to ${CONFIG_PATH}
51
- `);
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();
52
70
  } catch (err) {
53
- rl.close();
54
- 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)}
55
72
  `);
56
73
  process.exit(1);
57
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);
58
124
  });
59
125
  program.command("logout").description("Revoke current session").action(() => {
60
126
  clearAuth();
@@ -72,7 +138,7 @@ API: ${auth.api_url}
72
138
  `);
73
139
  });
74
140
  program.command("mcp").description("Start the Crukx MCP server (stdio transport)").action(() => {
75
- const serverPath = join(__dirname, "server.js");
141
+ const serverPath = join2(__dirname, "server.js");
76
142
  const child = spawn(process.execPath, [serverPath], {
77
143
  stdio: "inherit",
78
144
  env: process.env
package/dist/server.js CHANGED
@@ -1,47 +1,81 @@
1
- #!/usr/bin/env node
2
- import {
3
- getConfig,
4
- loadAuth
5
- } from "./chunk-5FGEDZZW.js";
6
-
7
1
  // src/server.ts
8
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10
4
 
11
5
  // src/tools/index.ts
12
- import { z as z2 } from "zod";
6
+ import { z as z3 } from "zod";
13
7
 
14
- // src/api/client.ts
8
+ // src/auth/index.ts
9
+ import { mkdirSync, writeFileSync, readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
10
+ import { dirname } from "path";
11
+
12
+ // src/config/index.ts
13
+ import "dotenv/config";
15
14
  import { z } from "zod";
15
+ import { homedir } from "os";
16
+ import { join } from "path";
17
+ import { readFileSync, existsSync } from "fs";
18
+ var ConfigSchema = z.object({
19
+ CRUKX_API_URL: z.string().url().default("https://crukx-gateway.salmonisland-7ebc5692.centralindia.azurecontainerapps.io"),
20
+ CRUKX_ACCESS_TOKEN: z.string().optional(),
21
+ CRUKX_WORKSPACE_ID: z.string().optional()
22
+ });
23
+ function loadLocalConfig() {
24
+ const configPath = join(homedir(), ".crukx", "config.json");
25
+ if (!existsSync(configPath)) return {};
26
+ try {
27
+ return JSON.parse(readFileSync(configPath, "utf-8"));
28
+ } catch {
29
+ return {};
30
+ }
31
+ }
32
+ function getConfig() {
33
+ const local = loadLocalConfig();
34
+ return ConfigSchema.parse({ ...local, ...process.env });
35
+ }
36
+ var CONFIG_PATH = join(homedir(), ".crukx", "config.json");
37
+
38
+ // src/auth/index.ts
39
+ function loadAuth() {
40
+ if (!existsSync2(CONFIG_PATH)) return null;
41
+ try {
42
+ return JSON.parse(readFileSync2(CONFIG_PATH, "utf-8"));
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+
48
+ // src/api/client.ts
49
+ import { z as z2 } from "zod";
16
50
  import { randomUUID } from "crypto";
17
51
  var TIMEOUT_MS = 6e4;
18
- var AuditResultSchema = z.object({
19
- auditId: z.string(),
20
- findings: z.array(z.object({
21
- id: z.string(),
22
- rule: z.string(),
23
- severity: z.enum(["critical", "high", "medium", "low", "info"]),
24
- message: z.string(),
25
- file: z.string(),
26
- line: z.number(),
27
- suggestion: z.string().optional(),
28
- confidence: z.number(),
29
- category: z.string()
52
+ var AuditResultSchema = z2.object({
53
+ auditId: z2.string(),
54
+ findings: z2.array(z2.object({
55
+ id: z2.string(),
56
+ rule: z2.string(),
57
+ severity: z2.enum(["critical", "high", "medium", "low", "info"]),
58
+ message: z2.string(),
59
+ file: z2.string(),
60
+ line: z2.number(),
61
+ suggestion: z2.string().optional(),
62
+ confidence: z2.number(),
63
+ category: z2.string()
30
64
  })),
31
- summary: z.object({
32
- totalFindings: z.number(),
33
- criticalCount: z.number(),
34
- highCount: z.number(),
35
- mediumCount: z.number(),
36
- lowCount: z.number(),
37
- infoCount: z.number(),
38
- reliabilityScore: z.number(),
39
- confidence: z.number()
65
+ summary: z2.object({
66
+ totalFindings: z2.number(),
67
+ criticalCount: z2.number(),
68
+ highCount: z2.number(),
69
+ mediumCount: z2.number(),
70
+ lowCount: z2.number(),
71
+ infoCount: z2.number(),
72
+ reliabilityScore: z2.number(),
73
+ confidence: z2.number()
40
74
  }),
41
- metadata: z.object({
42
- processingTimeMs: z.number(),
43
- agentsUsed: z.array(z.string()),
44
- swarmId: z.string()
75
+ metadata: z2.object({
76
+ processingTimeMs: z2.number(),
77
+ agentsUsed: z2.array(z2.string()),
78
+ swarmId: z2.string()
45
79
  })
46
80
  });
47
81
  async function request(path, init = {}) {
@@ -134,9 +168,9 @@ function registerTools(server2) {
134
168
  "swarm_audit",
135
169
  "Run a full Crukx 7-agent swarm audit on code. Returns reliability score, findings, and recommendations.",
136
170
  {
137
- diff: z2.string().optional().describe("Unified diff or code snippet to audit"),
138
- repoPath: z2.string().optional().describe("Repository path or name for context"),
139
- focus: z2.string().optional().describe('Specific area to focus on (e.g. "security", "concurrency")')
171
+ diff: z3.string().optional().describe("Unified diff or code snippet to audit"),
172
+ repoPath: z3.string().optional().describe("Repository path or name for context"),
173
+ focus: z3.string().optional().describe('Specific area to focus on (e.g. "security", "concurrency")')
140
174
  },
141
175
  async ({ diff, repoPath, focus }) => {
142
176
  const result = await swarmAudit({ diff, repoPath, focus });
@@ -147,7 +181,7 @@ function registerTools(server2) {
147
181
  "security_scan",
148
182
  "Run a targeted security scan using the Red Teamer and Hunter agents.",
149
183
  {
150
- diff: z2.string().describe("Code diff or snippet to scan for security issues")
184
+ diff: z3.string().describe("Code diff or snippet to scan for security issues")
151
185
  },
152
186
  async ({ diff }) => {
153
187
  const result = await swarmAudit({ diff, focus: "security vulnerabilities, injection attacks, secrets, authentication flaws" });
@@ -164,7 +198,7 @@ ${lines.join("\n")}` }] };
164
198
  "reliability_score",
165
199
  "Get a reliability score for a code change without full findings detail.",
166
200
  {
167
- diff: z2.string().describe("Code diff to score")
201
+ diff: z3.string().describe("Code diff to score")
168
202
  },
169
203
  async ({ diff }) => {
170
204
  const result = await swarmAudit({ diff });
@@ -183,7 +217,7 @@ ${lines.join("\n")}` }] };
183
217
  "report_fetch",
184
218
  "Fetch a previously generated Crukx audit report by ID.",
185
219
  {
186
- report_id: z2.string().uuid().describe("Audit run ID to fetch")
220
+ report_id: z3.string().uuid().describe("Audit run ID to fetch")
187
221
  },
188
222
  async ({ report_id }) => {
189
223
  const report = await fetchReport(report_id);
@@ -194,8 +228,8 @@ ${lines.join("\n")}` }] };
194
228
  "suggest_fix",
195
229
  "Get remediation suggestions for a specific finding or code issue.",
196
230
  {
197
- issue: z2.string().describe("Description of the issue or paste the problematic code"),
198
- severity: z2.enum(["critical", "high", "medium", "low"]).optional().describe("Severity level")
231
+ issue: z3.string().describe("Description of the issue or paste the problematic code"),
232
+ severity: z3.enum(["critical", "high", "medium", "low"]).optional().describe("Severity level")
199
233
  },
200
234
  async ({ issue, severity }) => {
201
235
  const focus = `remediation and fix suggestions for: ${issue}. Severity: ${severity ?? "unknown"}`;
package/package.json CHANGED
@@ -1,22 +1,30 @@
1
1
  {
2
2
  "name": "crukx-mcp",
3
- "version": "0.1.0",
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",
7
7
  "bin": {
8
- "crukx": "./dist/bin/cli.js"
8
+ "crukx": "dist/bin/cli.js"
9
9
  },
10
10
  "files": [
11
11
  "dist",
12
12
  "README.md"
13
13
  ],
14
- "keywords": ["mcp", "crukx", "reliability", "ai", "llm", "code-review", "swarm"],
14
+ "keywords": [
15
+ "mcp",
16
+ "crukx",
17
+ "reliability",
18
+ "ai",
19
+ "llm",
20
+ "code-review",
21
+ "swarm"
22
+ ],
15
23
  "author": "Crukx",
16
24
  "license": "MIT",
17
25
  "repository": {
18
26
  "type": "git",
19
- "url": "https://github.com/Shafwansafi06/observability-hub"
27
+ "url": "git+https://github.com/Shafwansafi06/observability-hub.git"
20
28
  },
21
29
  "scripts": {
22
30
  "build": "tsup",
@@ -28,6 +36,7 @@
28
36
  "@modelcontextprotocol/sdk": "^1.29.0",
29
37
  "commander": "^12.1.0",
30
38
  "dotenv": "^16.4.5",
39
+ "open": "^10.1.0",
31
40
  "zod": "^3.23.8"
32
41
  },
33
42
  "devDependencies": {
package/dist/bin/cli.d.ts DELETED
@@ -1 +0,0 @@
1
- #!/usr/bin/env node
@@ -1,55 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/config/index.ts
4
- import "dotenv/config";
5
- import { z } from "zod";
6
- import { homedir } from "os";
7
- import { join } from "path";
8
- import { readFileSync, existsSync } from "fs";
9
- var ConfigSchema = z.object({
10
- CRUKX_API_URL: z.string().url().default("https://crukx-gateway.salmonisland-7ebc5692.centralindia.azurecontainerapps.io"),
11
- CRUKX_ACCESS_TOKEN: z.string().optional(),
12
- CRUKX_WORKSPACE_ID: z.string().optional()
13
- });
14
- function loadLocalConfig() {
15
- const configPath = join(homedir(), ".crukx", "config.json");
16
- if (!existsSync(configPath)) return {};
17
- try {
18
- return JSON.parse(readFileSync(configPath, "utf-8"));
19
- } catch {
20
- return {};
21
- }
22
- }
23
- function getConfig() {
24
- const local = loadLocalConfig();
25
- return ConfigSchema.parse({ ...local, ...process.env });
26
- }
27
- var CONFIG_PATH = join(homedir(), ".crukx", "config.json");
28
-
29
- // src/auth/index.ts
30
- import { mkdirSync, writeFileSync, readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
31
- import { dirname } from "path";
32
- function saveAuth(auth) {
33
- mkdirSync(dirname(CONFIG_PATH), { recursive: true });
34
- const existing = loadAuth() ?? {};
35
- writeFileSync(CONFIG_PATH, JSON.stringify({ ...existing, ...auth }, null, 2));
36
- }
37
- function loadAuth() {
38
- if (!existsSync2(CONFIG_PATH)) return null;
39
- try {
40
- return JSON.parse(readFileSync2(CONFIG_PATH, "utf-8"));
41
- } catch {
42
- return null;
43
- }
44
- }
45
- function clearAuth() {
46
- if (existsSync2(CONFIG_PATH)) writeFileSync(CONFIG_PATH, "{}");
47
- }
48
-
49
- export {
50
- getConfig,
51
- CONFIG_PATH,
52
- saveAuth,
53
- loadAuth,
54
- clearAuth
55
- };