@kendoo.agentdesk/agentdesk 0.4.8 → 0.5.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/cli/config.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // .agentdesk.json config loader
2
- // Place this file in your project root to customize agent behavior
2
+ // Loads from: DEFAULTS server settings local .agentdesk.json (local overrides server)
3
3
 
4
4
  import { existsSync, readFileSync } from "fs";
5
5
  import { join } from "path";
@@ -37,28 +37,59 @@ const DEFAULTS = {
37
37
 
38
38
  // Existing agents/bots in the project — declare them so the team knows about them
39
39
  // Each entry: { name, role, when, how }
40
- // - name: Agent/bot name (e.g., "ReviewBot", "DataSync Agent")
41
- // - role: What it does (e.g., "Automated code review on PR creation")
42
- // - when: When to use or expect it (e.g., "Triggers on every PR", "Run manually via npm run agent:sync")
43
- // - how: How to interact with it (e.g., "Comment /review on a PR", "Set INPUT_MODE=dry-run for testing")
44
40
  projectAgents: [],
45
41
 
46
42
  // Extra prompt instructions appended to the team prompt
47
43
  instructions: null,
48
44
  };
49
45
 
50
- export function loadConfig(dir) {
46
+ async function fetchServerConfig(projectName, apiKey, serverUrl) {
47
+ if (!projectName || !apiKey || !serverUrl) return null;
48
+ try {
49
+ const res = await fetch(`${serverUrl}/api/projects/${projectName}/settings`, {
50
+ headers: { "x-api-key": apiKey },
51
+ signal: AbortSignal.timeout(5000),
52
+ });
53
+ if (!res.ok) return null;
54
+ const data = await res.json();
55
+ return Object.keys(data).length > 0 ? data : null;
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+
61
+ export async function loadConfig(dir, opts = {}) {
62
+ const { apiKey, serverUrl, projectName } = opts;
63
+
64
+ // Load local .agentdesk.json
51
65
  const configPath = join(dir, ".agentdesk.json");
66
+ let localConfig = null;
67
+ if (existsSync(configPath)) {
68
+ try {
69
+ localConfig = JSON.parse(readFileSync(configPath, "utf-8"));
70
+ } catch (err) {
71
+ console.error(`Warning: Failed to parse .agentdesk.json: ${err.message}`);
72
+ }
73
+ }
52
74
 
53
- if (!existsSync(configPath)) return { ...DEFAULTS };
75
+ // Fetch server settings
76
+ const serverConfig = await fetchServerConfig(projectName, apiKey, serverUrl);
54
77
 
55
- try {
56
- const raw = JSON.parse(readFileSync(configPath, "utf-8"));
57
- return deepMerge(DEFAULTS, raw);
58
- } catch (err) {
59
- console.error(`Warning: Failed to parse .agentdesk.json: ${err.message}`);
60
- return { ...DEFAULTS };
78
+ // Merge: DEFAULTS ← server ← local (local overrides server)
79
+ let config = { ...DEFAULTS };
80
+ if (serverConfig) config = deepMerge(config, serverConfig);
81
+ if (localConfig) config = deepMerge(config, localConfig);
82
+
83
+ // Auto-sync: if local config exists but server is empty, push local to server
84
+ if (localConfig && !serverConfig && apiKey && serverUrl && projectName) {
85
+ fetch(`${serverUrl}/api/projects/${projectName}/settings`, {
86
+ method: "PUT",
87
+ headers: { "Content-Type": "application/json", "x-api-key": apiKey },
88
+ body: JSON.stringify(localConfig),
89
+ }).catch(() => {});
61
90
  }
91
+
92
+ return config;
62
93
  }
63
94
 
64
95
  function deepMerge(defaults, overrides) {
package/cli/init.mjs CHANGED
@@ -141,6 +141,15 @@ export async function runInit(cwd) {
141
141
  if (res.ok) {
142
142
  console.log(" Registered with agentdesk.live");
143
143
  }
144
+ // Push settings to server
145
+ const key = loadApiKey(cwd);
146
+ if (key) {
147
+ await fetch(`${SERVER}/api/projects/${projectId}/settings`, {
148
+ method: "PUT",
149
+ headers: { "Content-Type": "application/json", "x-api-key": key },
150
+ body: JSON.stringify(merged),
151
+ }).catch(() => {});
152
+ }
144
153
  } catch {
145
154
  // Server not available
146
155
  }
package/cli/team.mjs CHANGED
@@ -33,9 +33,13 @@ export async function runTeam(taskId, opts = {}) {
33
33
  const cwd = opts.cwd || process.cwd();
34
34
  const description = opts.description || "";
35
35
 
36
- // Detect project and load config
36
+ // Detect project and resolve API key early (needed for server config fetch)
37
37
  const project = detectProject(cwd);
38
- const config = loadConfig(cwd);
38
+ const projectEnv = loadDotEnv(cwd);
39
+ const apiKey = projectEnv.AGENTDESK_API_KEY || process.env.AGENTDESK_API_KEY || getStoredApiKey() || null;
40
+ const agentdeskServer = process.env.AGENTDESK_SERVER || "https://agentdesk.live";
41
+
42
+ const config = await loadConfig(cwd, { apiKey, serverUrl: agentdeskServer, projectName: project.name });
39
43
  const tracker = config.tracker || (project.hasLinear ? "linear" : null);
40
44
 
41
45
  // If no task ID, handle based on tracker
@@ -135,10 +139,7 @@ export async function runTeam(taskId, opts = {}) {
135
139
  prompt += `\n\n## ADDITIONAL INSTRUCTIONS\n\n${config.instructions}\n`;
136
140
  }
137
141
 
138
- // --- AgentDesk config ---
139
- const projectEnv = loadDotEnv(cwd);
140
- const apiKey = projectEnv.AGENTDESK_API_KEY || process.env.AGENTDESK_API_KEY || getStoredApiKey() || null;
141
- const agentdeskServer = process.env.AGENTDESK_SERVER || "https://agentdesk.live";
142
+ // --- AgentDesk WebSocket config ---
142
143
  const AGENTDESK_URL = process.env.AGENTDESK_URL || "wss://agentdesk.live/ws/agent";
143
144
  const sessionId = `${taskId}-${randomUUID().slice(0, 8)}`;
144
145
  const inboxUrl = `${agentdeskServer}/api/sessions/${sessionId}/inbox`;
@@ -173,48 +174,66 @@ export async function runTeam(taskId, opts = {}) {
173
174
  }
174
175
  }
175
176
 
176
- try {
177
- vizWs = new WebSocket(AGENTDESK_URL);
178
- vizWs.on("open", () => {
179
- // Authenticate via first message (proxy-safe — query params can be stripped)
180
- if (apiKey) {
181
- vizWs.send(JSON.stringify({ type: "auth", apiKey }));
182
- }
183
- });
184
- vizWs.on("message", (raw) => {
185
- let msg;
186
- try { msg = JSON.parse(raw.toString()); } catch { return; }
187
- if (msg.type === "auth:ok") {
188
- vizConnected = true;
189
- console.log("AgentDesk: connected");
190
- vizSend({
191
- type: "session:start",
192
- taskId,
193
- taskLink,
194
- title: description || taskId,
195
- project: project.name || null,
196
- sessionNumber: 1,
197
- agents: ["Jane", "Dennis", "Bart", "Vera", "Sam", "Luna", "Mark"],
198
- });
199
- while (vizQueue.length > 0 && vizWs.readyState === WebSocket.OPEN) {
200
- vizWs.send(vizQueue.shift());
177
+ let sessionStartSent = false;
178
+ let reconnectTimer = null;
179
+
180
+ function connectWs() {
181
+ try {
182
+ vizWs = new WebSocket(AGENTDESK_URL);
183
+ vizWs.on("open", () => {
184
+ if (apiKey) {
185
+ vizWs.send(JSON.stringify({ type: "auth", apiKey }));
201
186
  }
202
- } else if (msg.type === "auth:error") {
203
- console.log("AgentDesk: authentication failed — run 'agentdesk login'");
187
+ });
188
+ vizWs.on("message", (raw) => {
189
+ let msg;
190
+ try { msg = JSON.parse(raw.toString()); } catch { return; }
191
+ if (msg.type === "auth:ok") {
192
+ vizConnected = true;
193
+ if (!sessionStartSent) {
194
+ console.log("AgentDesk: connected");
195
+ } else {
196
+ console.log("AgentDesk: reconnected");
197
+ }
198
+ // Always re-send session:start on (re)connect so server knows about the session
199
+ vizSend({
200
+ type: "session:start",
201
+ taskId,
202
+ taskLink,
203
+ title: description || taskId,
204
+ project: project.name || null,
205
+ sessionNumber: 1,
206
+ agents: ["Jane", "Dennis", "Bart", "Vera", "Sam", "Luna", "Mark"],
207
+ });
208
+ sessionStartSent = true;
209
+ while (vizQueue.length > 0 && vizWs.readyState === WebSocket.OPEN) {
210
+ vizWs.send(vizQueue.shift());
211
+ }
212
+ } else if (msg.type === "auth:error") {
213
+ console.log("AgentDesk: authentication failed — run 'agentdesk login'");
214
+ vizConnected = false;
215
+ }
216
+ });
217
+ vizWs.on("error", () => { vizConnected = false; });
218
+ vizWs.on("close", (code) => {
204
219
  vizConnected = false;
205
- }
206
- });
207
- vizWs.on("error", () => { vizConnected = false; });
208
- vizWs.on("close", (code) => {
209
- vizConnected = false;
210
- if (code === 4001) {
211
- console.log("AgentDesk: authentication required — run 'agentdesk login'");
212
- }
213
- });
214
- } catch {
215
- // AgentDesk not running
220
+ if (code === 4001) {
221
+ console.log("AgentDesk: authentication required — run 'agentdesk login'");
222
+ return; // Don't reconnect on auth failure
223
+ }
224
+ // Auto-reconnect after 5 seconds
225
+ clearTimeout(reconnectTimer);
226
+ reconnectTimer = setTimeout(connectWs, 5000);
227
+ });
228
+ } catch {
229
+ // AgentDesk not running — retry in 10s
230
+ clearTimeout(reconnectTimer);
231
+ reconnectTimer = setTimeout(connectWs, 10000);
232
+ }
216
233
  }
217
234
 
235
+ connectWs();
236
+
218
237
  // --- Spawn Claude ---
219
238
  const child = spawn(
220
239
  "claude",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kendoo.agentdesk/agentdesk",
3
- "version": "0.4.8",
3
+ "version": "0.5.1",
4
4
  "description": "AI team orchestrator for Claude Code — run collaborative agent sessions from your terminal",
5
5
  "type": "module",
6
6
  "bin": {