@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 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. Create an account
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
- cd your-project
37
- echo "AGENTDESK_API_KEY=your-key-here" >> .env
30
+ agentdesk login
38
31
  ```
39
32
 
40
- Run init to verify the setup:
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. Sign up at https://agentdesk.live and copy your API key from Settings
14
- 2. Add to your project's .env: AGENTDESK_API_KEY=<your-key>
15
- 3. Run: agentdesk init
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 === "init") {
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 null;
13
+ if (!existsSync(envPath)) return getStoredApiKey();
13
14
  const match = readFileSync(envPath, "utf-8").match(/AGENTDESK_API_KEY=(.+)/);
14
- return match?.[1]?.trim() || null;
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", () => { vizConnected = false; });
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kendoo.agentdesk/agentdesk",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "AI team orchestrator for Claude Code — run collaborative agent sessions from your terminal",
5
5
  "type": "module",
6
6
  "bin": {