kantban-cli 0.1.40 → 0.1.42
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/{chunk-FOVFBT7C.js → chunk-SGDJZXT6.js} +4 -3
- package/dist/{chunk-FOVFBT7C.js.map → chunk-SGDJZXT6.js.map} +1 -1
- package/dist/{cron-CVAFGFXN.js → cron-2YJPCZ5U.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/{pipeline-ILE7LMGI.js → pipeline-3V6PWWAW.js} +41 -5
- package/dist/pipeline-3V6PWWAW.js.map +1 -0
- package/package.json +1 -1
- package/dist/pipeline-ILE7LMGI.js.map +0 -1
- /package/dist/{cron-CVAFGFXN.js.map → cron-2YJPCZ5U.js.map} +0 -0
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
generateMcpConfig,
|
|
13
13
|
parseJsonFromLlmOutput,
|
|
14
14
|
parseStuckDetectionResponse
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-SGDJZXT6.js";
|
|
16
16
|
import {
|
|
17
17
|
LoopCheckpointSchema,
|
|
18
18
|
VerdictSchema,
|
|
@@ -469,6 +469,7 @@ function parseReplannerResponse(raw) {
|
|
|
469
469
|
}
|
|
470
470
|
|
|
471
471
|
// src/lib/orchestrator.ts
|
|
472
|
+
var ACTIVE_LOOP_ZOMBIE_TTL_MS = 90 * 60 * 1e3;
|
|
472
473
|
function classifyTier(input) {
|
|
473
474
|
if (input.invocationTier === "light") return "light";
|
|
474
475
|
if (input.invocationTier === "heavy") return "heavy";
|
|
@@ -885,6 +886,23 @@ var PipelineOrchestrator = class {
|
|
|
885
886
|
}
|
|
886
887
|
return;
|
|
887
888
|
}
|
|
889
|
+
const nowForSweep = Date.now();
|
|
890
|
+
for (const [ticketId, loop] of Array.from(this.activeLoops)) {
|
|
891
|
+
const age = nowForSweep - loop.startedAt;
|
|
892
|
+
if (age > ACTIVE_LOOP_ZOMBIE_TTL_MS) {
|
|
893
|
+
console.error(
|
|
894
|
+
` [watchdog] Evicting zombie active loop for ticket ${ticketId} in column ${loop.columnId} (age ${String(Math.round(age / 6e4))}min)`
|
|
895
|
+
);
|
|
896
|
+
try {
|
|
897
|
+
loop.abort.abort();
|
|
898
|
+
} catch {
|
|
899
|
+
}
|
|
900
|
+
this.activeLoops.delete(ticketId);
|
|
901
|
+
this.spawning.delete(ticketId);
|
|
902
|
+
this.completing.delete(ticketId);
|
|
903
|
+
this.abortedForMove.delete(ticketId);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
888
906
|
this.knownTickets.clear();
|
|
889
907
|
for (const ticketId of this.activeLoops.keys()) {
|
|
890
908
|
this.knownTickets.add(ticketId);
|
|
@@ -1147,7 +1165,7 @@ var PipelineOrchestrator = class {
|
|
|
1147
1165
|
})
|
|
1148
1166
|
);
|
|
1149
1167
|
const abort = new AbortController();
|
|
1150
|
-
this.activeLoops.set(ticketId, { columnId, promise, abort });
|
|
1168
|
+
this.activeLoops.set(ticketId, { columnId, promise, abort, startedAt: Date.now() });
|
|
1151
1169
|
this.lastFiredAt.set(columnId, /* @__PURE__ */ new Date());
|
|
1152
1170
|
void promise.then(
|
|
1153
1171
|
(result) => this.onLoopComplete(ticketId, columnId, result).catch((err) => {
|
|
@@ -1173,7 +1191,7 @@ var PipelineOrchestrator = class {
|
|
|
1173
1191
|
placeholder.catch(() => {
|
|
1174
1192
|
});
|
|
1175
1193
|
const abort = new AbortController();
|
|
1176
|
-
this.activeLoops.set(ticketId, { columnId, promise: placeholder, abort });
|
|
1194
|
+
this.activeLoops.set(ticketId, { columnId, promise: placeholder, abort, startedAt: Date.now() });
|
|
1177
1195
|
this.lastFiredAt.set(columnId, /* @__PURE__ */ new Date());
|
|
1178
1196
|
void readCheckpoint(
|
|
1179
1197
|
{
|
|
@@ -1244,7 +1262,7 @@ var PipelineOrchestrator = class {
|
|
|
1244
1262
|
const abort = new AbortController();
|
|
1245
1263
|
loopConfig.abortSignal = abort.signal;
|
|
1246
1264
|
const promise = this.deps.startLoop(ticketId, columnId, loopConfig);
|
|
1247
|
-
this.activeLoops.set(ticketId, { columnId, promise, abort });
|
|
1265
|
+
this.activeLoops.set(ticketId, { columnId, promise, abort, startedAt: Date.now() });
|
|
1248
1266
|
this.lastFiredAt.set(columnId, /* @__PURE__ */ new Date());
|
|
1249
1267
|
void promise.then(
|
|
1250
1268
|
(result) => this.onLoopComplete(ticketId, columnId, result).catch((err) => {
|
|
@@ -2334,6 +2352,14 @@ var PipelineWsClient = class _PipelineWsClient {
|
|
|
2334
2352
|
reconnectAttempt = 0;
|
|
2335
2353
|
lastPong = 0;
|
|
2336
2354
|
stopped = false;
|
|
2355
|
+
/**
|
|
2356
|
+
* In-flight `connect()` promise. Prevents concurrent callers (the internal
|
|
2357
|
+
* reconnect timer + the orchestrator's rescan-cycle `tryReconnect()`) from
|
|
2358
|
+
* racing to open two WebSockets at once, which leaks the first one's
|
|
2359
|
+
* listeners and ping timer and causes duplicate board events to be
|
|
2360
|
+
* delivered to `onEvent`.
|
|
2361
|
+
*/
|
|
2362
|
+
connectInFlight = null;
|
|
2337
2363
|
sendBuffer = [];
|
|
2338
2364
|
static CRITICAL_TYPES = /* @__PURE__ */ new Set([
|
|
2339
2365
|
"pipeline:session-start",
|
|
@@ -2357,6 +2383,15 @@ var PipelineWsClient = class _PipelineWsClient {
|
|
|
2357
2383
|
return this.ws?.readyState === WebSocket.OPEN;
|
|
2358
2384
|
}
|
|
2359
2385
|
async connect() {
|
|
2386
|
+
if (this.connectInFlight) return this.connectInFlight;
|
|
2387
|
+
this.connectInFlight = this.doConnect();
|
|
2388
|
+
this.connectInFlight.finally(() => {
|
|
2389
|
+
this.connectInFlight = null;
|
|
2390
|
+
}).catch(() => {
|
|
2391
|
+
});
|
|
2392
|
+
return this.connectInFlight;
|
|
2393
|
+
}
|
|
2394
|
+
async doConnect() {
|
|
2360
2395
|
if (this.ws) {
|
|
2361
2396
|
try {
|
|
2362
2397
|
this.ws.removeAllListeners();
|
|
@@ -2440,6 +2475,7 @@ var PipelineWsClient = class _PipelineWsClient {
|
|
|
2440
2475
|
this.sendBuffer = [];
|
|
2441
2476
|
this.stopped = true;
|
|
2442
2477
|
this.cleanupTimers();
|
|
2478
|
+
this.connectInFlight = null;
|
|
2443
2479
|
if (this.ws) {
|
|
2444
2480
|
try {
|
|
2445
2481
|
this.ws.removeAllListeners();
|
|
@@ -4948,4 +4984,4 @@ export {
|
|
|
4948
4984
|
runPipeline,
|
|
4949
4985
|
stopPipeline
|
|
4950
4986
|
};
|
|
4951
|
-
//# sourceMappingURL=pipeline-
|
|
4987
|
+
//# sourceMappingURL=pipeline-3V6PWWAW.js.map
|