harmony-mcp 1.6.2 → 1.6.3

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.
Files changed (3) hide show
  1. package/dist/cli.js +86 -13
  2. package/dist/index.js +86 -13
  3. package/package.json +4 -1
package/dist/cli.js CHANGED
@@ -29506,6 +29506,50 @@ function hasProjectContext(cwd) {
29506
29506
  }
29507
29507
 
29508
29508
  // src/api-client.ts
29509
+ var RETRY_CONFIG = {
29510
+ maxRetries: 3,
29511
+ baseDelayMs: 1000,
29512
+ maxDelayMs: 1e4,
29513
+ retryableStatusCodes: [408, 429, 500, 502, 503, 504]
29514
+ };
29515
+ function isRetryableError(error49, status) {
29516
+ if (status && RETRY_CONFIG.retryableStatusCodes.includes(status)) {
29517
+ return true;
29518
+ }
29519
+ if (error49 instanceof TypeError)
29520
+ return true;
29521
+ const msg = error49 instanceof Error ? error49.message : String(error49);
29522
+ return msg.includes("ECONNRESET") || msg.includes("ETIMEDOUT") || msg.includes("fetch failed");
29523
+ }
29524
+ function getRetryDelay(attempt) {
29525
+ const delay = Math.min(RETRY_CONFIG.baseDelayMs * Math.pow(2, attempt), RETRY_CONFIG.maxDelayMs);
29526
+ return Math.round(delay + delay * 0.25 * (Math.random() * 2 - 1));
29527
+ }
29528
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
29529
+
29530
+ class Semaphore {
29531
+ permits;
29532
+ queue = [];
29533
+ constructor(permits) {
29534
+ this.permits = permits;
29535
+ }
29536
+ async acquire() {
29537
+ if (this.permits > 0) {
29538
+ this.permits--;
29539
+ return;
29540
+ }
29541
+ return new Promise((resolve) => this.queue.push(resolve));
29542
+ }
29543
+ release() {
29544
+ const next = this.queue.shift();
29545
+ if (next)
29546
+ next();
29547
+ else
29548
+ this.permits++;
29549
+ }
29550
+ }
29551
+ var requestSemaphore = new Semaphore(3);
29552
+
29509
29553
  class HarmonyApiClient {
29510
29554
  apiKey;
29511
29555
  apiUrl;
@@ -29514,20 +29558,49 @@ class HarmonyApiClient {
29514
29558
  this.apiUrl = getApiUrl();
29515
29559
  }
29516
29560
  async request(method, path, body) {
29561
+ await requestSemaphore.acquire();
29562
+ try {
29563
+ return await this.requestWithRetry(method, path, body);
29564
+ } finally {
29565
+ requestSemaphore.release();
29566
+ }
29567
+ }
29568
+ async requestWithRetry(method, path, body) {
29517
29569
  const url2 = `${this.apiUrl}/v1${path}`;
29518
- const response = await fetch(url2, {
29519
- method,
29520
- headers: {
29521
- "Content-Type": "application/json",
29522
- "X-API-Key": this.apiKey
29523
- },
29524
- body: body ? JSON.stringify(body) : undefined
29525
- });
29526
- const data = await response.json();
29527
- if (!response.ok) {
29528
- throw new Error(data.error || `API error: ${response.status}`);
29570
+ let lastError = null;
29571
+ for (let attempt = 0;attempt <= RETRY_CONFIG.maxRetries; attempt++) {
29572
+ try {
29573
+ const response = await fetch(url2, {
29574
+ method,
29575
+ headers: {
29576
+ "Content-Type": "application/json",
29577
+ "X-API-Key": this.apiKey
29578
+ },
29579
+ body: body ? JSON.stringify(body) : undefined
29580
+ });
29581
+ const data = await response.json();
29582
+ if (!response.ok) {
29583
+ if (!isRetryableError(null, response.status)) {
29584
+ throw new Error(data.error || `API error: ${response.status}`);
29585
+ }
29586
+ lastError = new Error(data.error || `API error: ${response.status}`);
29587
+ if (attempt < RETRY_CONFIG.maxRetries) {
29588
+ await sleep(getRetryDelay(attempt));
29589
+ continue;
29590
+ }
29591
+ }
29592
+ return data;
29593
+ } catch (error49) {
29594
+ lastError = error49 instanceof Error ? error49 : new Error(String(error49));
29595
+ if (!isRetryableError(error49))
29596
+ throw lastError;
29597
+ if (attempt < RETRY_CONFIG.maxRetries) {
29598
+ await sleep(getRetryDelay(attempt));
29599
+ continue;
29600
+ }
29601
+ }
29529
29602
  }
29530
- return data;
29603
+ throw lastError || new Error("Request failed after retries");
29531
29604
  }
29532
29605
  async listWorkspaces() {
29533
29606
  return this.request("GET", "/workspaces");
@@ -30277,7 +30350,7 @@ var TOOLS = {
30277
30350
  cardId: { type: "string", description: "Card ID with active session" },
30278
30351
  agentIdentifier: { type: "string", description: "Agent identifier" },
30279
30352
  agentName: { type: "string", description: "Agent name" },
30280
- status: { type: "string", enum: ["working", "blocked", "paused"], description: "Current status" },
30353
+ status: { type: "string", enum: ["working", "blocked", "waiting", "paused"], description: "Current status" },
30281
30354
  progressPercent: { type: "number", description: "Progress percentage (0-100)" },
30282
30355
  currentTask: { type: "string", description: "What the agent is currently doing" },
30283
30356
  blockers: { type: "array", items: { type: "string" }, description: "List of blocking issues" },
package/dist/index.js CHANGED
@@ -27269,6 +27269,50 @@ function hasProjectContext(cwd) {
27269
27269
  }
27270
27270
 
27271
27271
  // src/api-client.ts
27272
+ var RETRY_CONFIG = {
27273
+ maxRetries: 3,
27274
+ baseDelayMs: 1000,
27275
+ maxDelayMs: 1e4,
27276
+ retryableStatusCodes: [408, 429, 500, 502, 503, 504]
27277
+ };
27278
+ function isRetryableError(error49, status) {
27279
+ if (status && RETRY_CONFIG.retryableStatusCodes.includes(status)) {
27280
+ return true;
27281
+ }
27282
+ if (error49 instanceof TypeError)
27283
+ return true;
27284
+ const msg = error49 instanceof Error ? error49.message : String(error49);
27285
+ return msg.includes("ECONNRESET") || msg.includes("ETIMEDOUT") || msg.includes("fetch failed");
27286
+ }
27287
+ function getRetryDelay(attempt) {
27288
+ const delay = Math.min(RETRY_CONFIG.baseDelayMs * Math.pow(2, attempt), RETRY_CONFIG.maxDelayMs);
27289
+ return Math.round(delay + delay * 0.25 * (Math.random() * 2 - 1));
27290
+ }
27291
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
27292
+
27293
+ class Semaphore {
27294
+ permits;
27295
+ queue = [];
27296
+ constructor(permits) {
27297
+ this.permits = permits;
27298
+ }
27299
+ async acquire() {
27300
+ if (this.permits > 0) {
27301
+ this.permits--;
27302
+ return;
27303
+ }
27304
+ return new Promise((resolve) => this.queue.push(resolve));
27305
+ }
27306
+ release() {
27307
+ const next = this.queue.shift();
27308
+ if (next)
27309
+ next();
27310
+ else
27311
+ this.permits++;
27312
+ }
27313
+ }
27314
+ var requestSemaphore = new Semaphore(3);
27315
+
27272
27316
  class HarmonyApiClient {
27273
27317
  apiKey;
27274
27318
  apiUrl;
@@ -27277,20 +27321,49 @@ class HarmonyApiClient {
27277
27321
  this.apiUrl = getApiUrl();
27278
27322
  }
27279
27323
  async request(method, path, body) {
27324
+ await requestSemaphore.acquire();
27325
+ try {
27326
+ return await this.requestWithRetry(method, path, body);
27327
+ } finally {
27328
+ requestSemaphore.release();
27329
+ }
27330
+ }
27331
+ async requestWithRetry(method, path, body) {
27280
27332
  const url2 = `${this.apiUrl}/v1${path}`;
27281
- const response = await fetch(url2, {
27282
- method,
27283
- headers: {
27284
- "Content-Type": "application/json",
27285
- "X-API-Key": this.apiKey
27286
- },
27287
- body: body ? JSON.stringify(body) : undefined
27288
- });
27289
- const data = await response.json();
27290
- if (!response.ok) {
27291
- throw new Error(data.error || `API error: ${response.status}`);
27333
+ let lastError = null;
27334
+ for (let attempt = 0;attempt <= RETRY_CONFIG.maxRetries; attempt++) {
27335
+ try {
27336
+ const response = await fetch(url2, {
27337
+ method,
27338
+ headers: {
27339
+ "Content-Type": "application/json",
27340
+ "X-API-Key": this.apiKey
27341
+ },
27342
+ body: body ? JSON.stringify(body) : undefined
27343
+ });
27344
+ const data = await response.json();
27345
+ if (!response.ok) {
27346
+ if (!isRetryableError(null, response.status)) {
27347
+ throw new Error(data.error || `API error: ${response.status}`);
27348
+ }
27349
+ lastError = new Error(data.error || `API error: ${response.status}`);
27350
+ if (attempt < RETRY_CONFIG.maxRetries) {
27351
+ await sleep(getRetryDelay(attempt));
27352
+ continue;
27353
+ }
27354
+ }
27355
+ return data;
27356
+ } catch (error49) {
27357
+ lastError = error49 instanceof Error ? error49 : new Error(String(error49));
27358
+ if (!isRetryableError(error49))
27359
+ throw lastError;
27360
+ if (attempt < RETRY_CONFIG.maxRetries) {
27361
+ await sleep(getRetryDelay(attempt));
27362
+ continue;
27363
+ }
27364
+ }
27292
27365
  }
27293
- return data;
27366
+ throw lastError || new Error("Request failed after retries");
27294
27367
  }
27295
27368
  async listWorkspaces() {
27296
27369
  return this.request("GET", "/workspaces");
@@ -28040,7 +28113,7 @@ var TOOLS = {
28040
28113
  cardId: { type: "string", description: "Card ID with active session" },
28041
28114
  agentIdentifier: { type: "string", description: "Agent identifier" },
28042
28115
  agentName: { type: "string", description: "Agent name" },
28043
- status: { type: "string", enum: ["working", "blocked", "paused"], description: "Current status" },
28116
+ status: { type: "string", enum: ["working", "blocked", "waiting", "paused"], description: "Current status" },
28044
28117
  progressPercent: { type: "number", description: "Progress percentage (0-100)" },
28045
28118
  currentTask: { type: "string", description: "What the agent is currently doing" },
28046
28119
  blockers: { type: "array", items: { type: "string" }, description: "List of blocking issues" },
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "harmony-mcp",
3
- "version": "1.6.2",
3
+ "version": "1.6.3",
4
4
  "description": "MCP server for Harmony Kanban board - enables AI coding agents to manage your boards",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
5
8
  "type": "module",
6
9
  "main": "dist/index.js",
7
10
  "bin": {