@kendoo.agentdesk/agentdesk 0.1.1 → 0.2.1
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/README.md +9 -10
- package/bin/agentdesk.mjs +17 -5
- package/cli/init.mjs +3 -2
- package/cli/login.mjs +129 -0
- package/cli/team.mjs +14 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,20 +24,17 @@ npm i -g @kendoo.agentdesk/agentdesk
|
|
|
24
24
|
|
|
25
25
|
Requires [Node.js](https://nodejs.org/) 18+ and [Claude Code](https://claude.ai/claude-code) installed.
|
|
26
26
|
|
|
27
|
-
### 2.
|
|
28
|
-
|
|
29
|
-
Sign up at [agentdesk.live](https://agentdesk.live) and copy your API key from **Settings**.
|
|
30
|
-
|
|
31
|
-
### 3. Configure your project
|
|
32
|
-
|
|
33
|
-
Add your API key to your project's `.env` file:
|
|
27
|
+
### 2. Log in
|
|
34
28
|
|
|
35
29
|
```bash
|
|
36
|
-
|
|
37
|
-
echo "AGENTDESK_API_KEY=your-key-here" >> .env
|
|
30
|
+
agentdesk login
|
|
38
31
|
```
|
|
39
32
|
|
|
40
|
-
|
|
33
|
+
This opens your browser to [agentdesk.live](https://agentdesk.live) where you sign up or log in. Your API key is saved automatically to `~/.agentdesk/credentials.json`.
|
|
34
|
+
|
|
35
|
+
### 3. Set up your project
|
|
36
|
+
|
|
37
|
+
Navigate to your project and run init:
|
|
41
38
|
|
|
42
39
|
```bash
|
|
43
40
|
agentdesk init
|
|
@@ -93,6 +90,8 @@ AgentDesk also auto-discovers agents from `.claude/agents/`, `.claude/commands/`
|
|
|
93
90
|
## Usage
|
|
94
91
|
|
|
95
92
|
```
|
|
93
|
+
agentdesk login Log in to AgentDesk
|
|
94
|
+
agentdesk logout Log out and remove credentials
|
|
96
95
|
agentdesk init Detect project and show setup info
|
|
97
96
|
agentdesk team <TASK-ID> Run a team session on a task
|
|
98
97
|
agentdesk team <TASK-ID> -d "..." Run with a task description
|
package/bin/agentdesk.mjs
CHANGED
|
@@ -10,12 +10,13 @@ if (!command || command === "help" || command === "--help") {
|
|
|
10
10
|
AgentDesk — AI team orchestrator for Claude Code
|
|
11
11
|
|
|
12
12
|
Getting started:
|
|
13
|
-
1.
|
|
14
|
-
2.
|
|
15
|
-
3. Run: agentdesk
|
|
16
|
-
4. Run: agentdesk team <TASK-ID>
|
|
13
|
+
1. Run: agentdesk login
|
|
14
|
+
2. Run: agentdesk init
|
|
15
|
+
3. Run: agentdesk team <TASK-ID>
|
|
17
16
|
|
|
18
17
|
Usage:
|
|
18
|
+
agentdesk login Log in to AgentDesk
|
|
19
|
+
agentdesk logout Log out and remove credentials
|
|
19
20
|
agentdesk init Detect project and show setup info
|
|
20
21
|
agentdesk team <TASK-ID> Run a team session on a task
|
|
21
22
|
agentdesk team <TASK-ID> -d "..." Run with a task description
|
|
@@ -32,6 +33,7 @@ if (!command || command === "help" || command === "--help") {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
Examples:
|
|
36
|
+
agentdesk login
|
|
35
37
|
agentdesk init
|
|
36
38
|
agentdesk team KEN-517
|
|
37
39
|
agentdesk team BUG-42 -d "Fix the checkout total calculation"
|
|
@@ -41,7 +43,17 @@ if (!command || command === "help" || command === "--help") {
|
|
|
41
43
|
process.exit(0);
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
if (command === "
|
|
46
|
+
if (command === "login") {
|
|
47
|
+
const { runLogin } = await import("../cli/login.mjs");
|
|
48
|
+
await runLogin();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
else if (command === "logout") {
|
|
52
|
+
const { runLogout } = await import("../cli/login.mjs");
|
|
53
|
+
await runLogout();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
else if (command === "init") {
|
|
45
57
|
const { runInit } = await import("../cli/init.mjs");
|
|
46
58
|
await runInit(process.cwd());
|
|
47
59
|
}
|
package/cli/init.mjs
CHANGED
|
@@ -4,14 +4,15 @@ import { existsSync, readFileSync } from "fs";
|
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
import { detectProject } from "./detect.mjs";
|
|
6
6
|
import { loadConfig } from "./config.mjs";
|
|
7
|
+
import { getStoredApiKey } from "./login.mjs";
|
|
7
8
|
|
|
8
9
|
const SERVER = process.env.AGENTDESK_SERVER || "https://agentdesk.live";
|
|
9
10
|
|
|
10
11
|
function loadApiKey(dir) {
|
|
11
12
|
const envPath = join(dir, ".env");
|
|
12
|
-
if (!existsSync(envPath)) return
|
|
13
|
+
if (!existsSync(envPath)) return getStoredApiKey();
|
|
13
14
|
const match = readFileSync(envPath, "utf-8").match(/AGENTDESK_API_KEY=(.+)/);
|
|
14
|
-
return match?.[1]?.trim() ||
|
|
15
|
+
return match?.[1]?.trim() || getStoredApiKey();
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export async function runInit(cwd) {
|
package/cli/login.mjs
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// `agentdesk login` — authenticate with AgentDesk and save API key
|
|
2
|
+
|
|
3
|
+
import { createServer } from "http";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { randomUUID } from "crypto";
|
|
7
|
+
|
|
8
|
+
const AGENTDESK_SERVER = process.env.AGENTDESK_SERVER || "https://agentdesk.live";
|
|
9
|
+
const CONFIG_DIR = join(process.env.HOME || process.env.USERPROFILE, ".agentdesk");
|
|
10
|
+
const CREDENTIALS_PATH = join(CONFIG_DIR, "credentials.json");
|
|
11
|
+
|
|
12
|
+
export function getStoredApiKey() {
|
|
13
|
+
if (!existsSync(CREDENTIALS_PATH)) return null;
|
|
14
|
+
try {
|
|
15
|
+
const creds = JSON.parse(readFileSync(CREDENTIALS_PATH, "utf-8"));
|
|
16
|
+
return creds.apiKey || null;
|
|
17
|
+
} catch { return null; }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function runLogin() {
|
|
21
|
+
console.log("");
|
|
22
|
+
console.log(" AgentDesk — Login");
|
|
23
|
+
console.log(" ━━━━━━━━━━━━━━━━");
|
|
24
|
+
console.log("");
|
|
25
|
+
|
|
26
|
+
// Check if already logged in
|
|
27
|
+
const existing = getStoredApiKey();
|
|
28
|
+
if (existing) {
|
|
29
|
+
console.log(` Already logged in (key: ${existing.slice(0, 8)}...)`);
|
|
30
|
+
console.log("");
|
|
31
|
+
console.log(" To re-authenticate, run: agentdesk logout");
|
|
32
|
+
console.log("");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const state = randomUUID();
|
|
37
|
+
|
|
38
|
+
// Start a local server to receive the API key callback
|
|
39
|
+
const server = createServer((req, res) => {
|
|
40
|
+
// Allow CORS from agentdesk.live
|
|
41
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
42
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
43
|
+
if (req.method === "OPTIONS") { res.writeHead(200); res.end(); return; }
|
|
44
|
+
|
|
45
|
+
const url = new URL(req.url, `http://localhost`);
|
|
46
|
+
|
|
47
|
+
if (url.pathname === "/callback") {
|
|
48
|
+
const apiKey = url.searchParams.get("api_key");
|
|
49
|
+
const name = url.searchParams.get("name");
|
|
50
|
+
const returnedState = url.searchParams.get("state");
|
|
51
|
+
|
|
52
|
+
// CSRF check
|
|
53
|
+
if (returnedState !== state) {
|
|
54
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
55
|
+
res.end("<html><body><h2>Invalid state. Please try again.</h2></body></html>");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (apiKey) {
|
|
60
|
+
// Save credentials
|
|
61
|
+
if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true });
|
|
62
|
+
writeFileSync(CREDENTIALS_PATH, JSON.stringify({ apiKey, name, savedAt: Date.now() }, null, 2));
|
|
63
|
+
|
|
64
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
65
|
+
res.end(`<html><body style="background:#0f172a;color:#e2e8f0;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0"><div style="text-align:center"><h2 style="color:#2dd4bf">Logged in to AgentDesk</h2><p>You can close this tab and return to your terminal.</p></div></body></html>`);
|
|
66
|
+
|
|
67
|
+
console.log(` Logged in as ${name || "user"}`);
|
|
68
|
+
console.log(` API key saved to ~/.agentdesk/credentials.json`);
|
|
69
|
+
console.log("");
|
|
70
|
+
console.log(" Next steps:");
|
|
71
|
+
console.log(" agentdesk init");
|
|
72
|
+
console.log(" agentdesk team TASK-123");
|
|
73
|
+
console.log("");
|
|
74
|
+
|
|
75
|
+
setTimeout(() => { server.close(); process.exit(0); }, 500);
|
|
76
|
+
} else {
|
|
77
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
78
|
+
res.end("<html><body><h2>Login failed. No API key received.</h2></body></html>");
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
res.writeHead(404);
|
|
84
|
+
res.end();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Find an available port
|
|
88
|
+
await new Promise((resolve) => {
|
|
89
|
+
server.listen(0, "127.0.0.1", resolve);
|
|
90
|
+
});
|
|
91
|
+
const port = server.address().port;
|
|
92
|
+
|
|
93
|
+
const loginUrl = `${AGENTDESK_SERVER}/cli-auth?port=${port}&state=${state}`;
|
|
94
|
+
|
|
95
|
+
console.log(` Opening browser to log in...`);
|
|
96
|
+
console.log("");
|
|
97
|
+
console.log(` If the browser doesn't open, visit:`);
|
|
98
|
+
console.log(` ${loginUrl}`);
|
|
99
|
+
console.log("");
|
|
100
|
+
console.log(" Waiting for authentication...");
|
|
101
|
+
|
|
102
|
+
// Open browser
|
|
103
|
+
const { exec } = await import("child_process");
|
|
104
|
+
const platform = process.platform;
|
|
105
|
+
const cmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
|
|
106
|
+
exec(`${cmd} "${loginUrl}"`);
|
|
107
|
+
|
|
108
|
+
// Timeout after 5 minutes
|
|
109
|
+
setTimeout(() => {
|
|
110
|
+
console.log("");
|
|
111
|
+
console.log(" Login timed out. Please try again.");
|
|
112
|
+
server.close();
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}, 5 * 60 * 1000);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function runLogout() {
|
|
118
|
+
if (existsSync(CREDENTIALS_PATH)) {
|
|
119
|
+
const { unlinkSync } = await import("fs");
|
|
120
|
+
unlinkSync(CREDENTIALS_PATH);
|
|
121
|
+
console.log("");
|
|
122
|
+
console.log(" Logged out. Credentials removed.");
|
|
123
|
+
console.log("");
|
|
124
|
+
} else {
|
|
125
|
+
console.log("");
|
|
126
|
+
console.log(" Not logged in.");
|
|
127
|
+
console.log("");
|
|
128
|
+
}
|
|
129
|
+
}
|
package/cli/team.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { randomUUID } from "crypto";
|
|
|
9
9
|
import WebSocket from "ws";
|
|
10
10
|
import { detectProject, generateContext } from "./detect.mjs";
|
|
11
11
|
import { loadConfig } from "./config.mjs";
|
|
12
|
+
import { getStoredApiKey } from "./login.mjs";
|
|
12
13
|
|
|
13
14
|
function loadDotEnv(dir) {
|
|
14
15
|
const envPath = join(dir, ".env");
|
|
@@ -96,7 +97,7 @@ export async function runTeam(taskId, opts = {}) {
|
|
|
96
97
|
|
|
97
98
|
// --- AgentDesk config ---
|
|
98
99
|
const projectEnv = loadDotEnv(cwd);
|
|
99
|
-
const apiKey = projectEnv.AGENTDESK_API_KEY || process.env.AGENTDESK_API_KEY || null;
|
|
100
|
+
const apiKey = projectEnv.AGENTDESK_API_KEY || process.env.AGENTDESK_API_KEY || getStoredApiKey() || null;
|
|
100
101
|
const agentdeskServer = process.env.AGENTDESK_SERVER || "https://agentdesk.live";
|
|
101
102
|
const baseUrl = process.env.AGENTDESK_URL || "wss://agentdesk.live/ws/agent";
|
|
102
103
|
const AGENTDESK_URL = apiKey ? `${baseUrl}?api_key=${apiKey}` : baseUrl;
|
|
@@ -152,7 +153,18 @@ export async function runTeam(taskId, opts = {}) {
|
|
|
152
153
|
}
|
|
153
154
|
});
|
|
154
155
|
vizWs.on("error", () => { vizConnected = false; });
|
|
155
|
-
vizWs.on("close", () => {
|
|
156
|
+
vizWs.on("close", (code) => {
|
|
157
|
+
vizConnected = false;
|
|
158
|
+
if (code === 1006 && !vizConnected) {
|
|
159
|
+
console.log("AgentDesk: not connected — run 'agentdesk login' to authenticate");
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
vizWs.on("unexpected-response", (_req, res) => {
|
|
163
|
+
if (res.statusCode === 401) {
|
|
164
|
+
console.log("AgentDesk: authentication required — run 'agentdesk login'");
|
|
165
|
+
}
|
|
166
|
+
vizConnected = false;
|
|
167
|
+
});
|
|
156
168
|
} catch {
|
|
157
169
|
// AgentDesk not running
|
|
158
170
|
}
|