@drisp/cli 0.6.2 → 0.6.4

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.
@@ -15084,6 +15084,14 @@ function createDashboardFeedOutbox(options = {}) {
15084
15084
  SET acked_at = ?
15085
15085
  WHERE event_id = ?
15086
15086
  `);
15087
+ const deleteOtherInstances = db.prepare(`
15088
+ DELETE FROM dashboard_feed_outbox
15089
+ WHERE delivery_seq IN (
15090
+ SELECT delivery_seq FROM dashboard_feed_outbox
15091
+ WHERE instance_id < ? OR instance_id > ?
15092
+ LIMIT ?
15093
+ )
15094
+ `);
15087
15095
  const enqueueTx = db.transaction(
15088
15096
  (input) => {
15089
15097
  for (const feedEvent of input.feedEvents) {
@@ -15148,6 +15156,13 @@ function createDashboardFeedOutbox(options = {}) {
15148
15156
  ackByEventId.run(now, input.eventId);
15149
15157
  }
15150
15158
  },
15159
+ pruneOtherInstances(input) {
15160
+ return deleteOtherInstances.run(
15161
+ input.instanceId,
15162
+ input.instanceId,
15163
+ input.limit
15164
+ ).changes;
15165
+ },
15151
15166
  close() {
15152
15167
  owned2?.close();
15153
15168
  }
@@ -15156,15 +15171,20 @@ function createDashboardFeedOutbox(options = {}) {
15156
15171
 
15157
15172
  // src/app/dashboard/pairedFeedPublisher.ts
15158
15173
  var DEFAULT_DRAIN_INTERVAL_MS = 1e3;
15174
+ var DEFAULT_PRUNE_BATCH_SIZE = 500;
15159
15175
  function createPairedFeedPublisher(options = {}) {
15160
15176
  const readConfig2 = options.readConfig ?? (() => readDashboardClientConfig());
15161
15177
  const now = options.now ?? (() => Date.now());
15162
15178
  const onError = options.onError ?? (() => {
15163
15179
  });
15180
+ const onInfo = options.onInfo ?? (() => {
15181
+ });
15164
15182
  const drainIntervalMs = options.drainIntervalMs ?? DEFAULT_DRAIN_INTERVAL_MS;
15183
+ const pruneBatchSize = options.pruneBatchSize ?? DEFAULT_PRUNE_BATCH_SIZE;
15165
15184
  let ownedOutbox = null;
15166
15185
  let transport = null;
15167
15186
  let drainTimer = null;
15187
+ let prunedSoFar = null;
15168
15188
  function getOutbox() {
15169
15189
  if (options.outbox) return options.outbox;
15170
15190
  ownedOutbox ??= createDashboardFeedOutbox();
@@ -15176,7 +15196,7 @@ function createPairedFeedPublisher(options = {}) {
15176
15196
  drainTimer = null;
15177
15197
  }
15178
15198
  function drain(force = false) {
15179
- if (!transport) return;
15199
+ if (!transport) return null;
15180
15200
  let config2;
15181
15201
  try {
15182
15202
  config2 = readConfig2();
@@ -15184,9 +15204,9 @@ function createPairedFeedPublisher(options = {}) {
15184
15204
  onError(
15185
15205
  `paired feed drain failed: ${err instanceof Error ? err.message : String(err)}`
15186
15206
  );
15187
- return;
15207
+ return null;
15188
15208
  }
15189
- if (!config2) return;
15209
+ if (!config2) return null;
15190
15210
  const rows = getOutbox().pendingBatch({
15191
15211
  instanceId: config2.instanceId,
15192
15212
  limit: 100,
@@ -15202,13 +15222,41 @@ function createPairedFeedPublisher(options = {}) {
15202
15222
  nextAttemptAt: now() + Math.min(3e4, (row.attempt + 1) * 1e3)
15203
15223
  });
15204
15224
  }
15225
+ return config2;
15226
+ }
15227
+ function tick(force = false) {
15228
+ const config2 = drain(force);
15229
+ if (config2) pruneStep(config2.instanceId);
15230
+ }
15231
+ function pruneStep(instanceId) {
15232
+ if (prunedSoFar === null) return;
15233
+ try {
15234
+ const deleted = getOutbox().pruneOtherInstances({
15235
+ instanceId,
15236
+ limit: pruneBatchSize
15237
+ });
15238
+ prunedSoFar += deleted;
15239
+ if (deleted < pruneBatchSize) {
15240
+ if (prunedSoFar > 0) {
15241
+ onInfo(
15242
+ `paired feed: pruned ${prunedSoFar} outbox row(s) left by previous pairings`
15243
+ );
15244
+ }
15245
+ prunedSoFar = null;
15246
+ }
15247
+ } catch (err) {
15248
+ prunedSoFar = null;
15249
+ onError(
15250
+ `paired feed prune failed: ${err instanceof Error ? err.message : String(err)}`
15251
+ );
15252
+ }
15205
15253
  }
15206
15254
  function startDrainTimer() {
15207
15255
  clearDrainTimer();
15208
- const timer = setInterval(drain, drainIntervalMs);
15256
+ const timer = setInterval(() => tick(), drainIntervalMs);
15209
15257
  timer.unref();
15210
15258
  drainTimer = timer;
15211
- drain(true);
15259
+ tick(true);
15212
15260
  }
15213
15261
  return {
15214
15262
  publish(input) {
@@ -15232,6 +15280,7 @@ function createPairedFeedPublisher(options = {}) {
15232
15280
  },
15233
15281
  attachTransport(nextTransport) {
15234
15282
  transport = nextTransport;
15283
+ prunedSoFar ??= 0;
15235
15284
  startDrainTimer();
15236
15285
  },
15237
15286
  detachTransport() {
@@ -31944,6 +31993,11 @@ function ensureRemoteWorkflowInstalled(input) {
31944
31993
  );
31945
31994
  return input.installWorkflowFromSourceFn(resolved);
31946
31995
  }
31996
+ function isParkedCompletion(event) {
31997
+ if (event.type !== "exec.completed") return false;
31998
+ const data = event.data;
31999
+ return data?.workflowOutcome?.status === "awaiting_attention";
32000
+ }
31947
32001
  function eventKind(event) {
31948
32002
  if (event.type === "exec.completed") {
31949
32003
  const data = event.data;
@@ -32089,6 +32143,7 @@ async function executeRemoteAssignment({
32089
32143
  deferredFailedCompletion.current = event;
32090
32144
  continue;
32091
32145
  }
32146
+ if (isParkedCompletion(event)) continue;
32092
32147
  send(eventKind(event), eventPayload(event), now());
32093
32148
  if (event.type === "run.suspended") reportNeedsHuman(event);
32094
32149
  } catch (err) {
@@ -33210,6 +33265,8 @@ var DEFAULT_REFRESH_LEAD_SEC = 60;
33210
33265
  var DEFAULT_REFRESH_FAILURE_LIMIT = 5;
33211
33266
  var DEFAULT_REFRESH_FAILURE_WINDOW_MS = 5 * 6e4;
33212
33267
  var DEFAULT_REFRESH_COOLDOWN_MS = 5 * 6e4;
33268
+ var DEFAULT_COOLDOWN_PROBE_DELAYS_MS = [2e3, 5e3, 1e4, 15e3];
33269
+ var HUB_PROBE_TIMEOUT_MS = 5e3;
33213
33270
  var DEFAULT_RUN_HISTORY_LIMIT2 = 100;
33214
33271
  function delay(ms) {
33215
33272
  return new Promise((resolve) => {
@@ -33217,6 +33274,28 @@ function delay(ms) {
33217
33274
  timer.unref();
33218
33275
  });
33219
33276
  }
33277
+ async function probeHubReachable(dashboardUrl, signal) {
33278
+ const controller = new AbortController();
33279
+ const onAbort = () => controller.abort();
33280
+ signal.addEventListener("abort", onAbort, { once: true });
33281
+ const timer = setTimeout(onAbort, HUB_PROBE_TIMEOUT_MS);
33282
+ timer.unref();
33283
+ try {
33284
+ const response = await fetch(`${dashboardUrl}/api/instances/refresh`, {
33285
+ method: "POST",
33286
+ headers: { "content-type": "application/json" },
33287
+ body: "{}",
33288
+ redirect: "manual",
33289
+ signal: controller.signal
33290
+ });
33291
+ return response.status < 500;
33292
+ } catch {
33293
+ return false;
33294
+ } finally {
33295
+ clearTimeout(timer);
33296
+ signal.removeEventListener("abort", onAbort);
33297
+ }
33298
+ }
33220
33299
  async function runDashboardRuntimeDaemon(options = {}) {
33221
33300
  const readConfig2 = options.readConfig ?? (() => readDashboardClientConfig());
33222
33301
  const refreshAccessTokenFn = options.refreshAccessToken ?? (async () => refreshDashboardAccessToken({}));
@@ -33237,6 +33316,8 @@ async function runDashboardRuntimeDaemon(options = {}) {
33237
33316
  const refreshFailureLimit = options.refreshFailureLimit ?? DEFAULT_REFRESH_FAILURE_LIMIT;
33238
33317
  const refreshFailureWindowMs = options.refreshFailureWindowMs ?? DEFAULT_REFRESH_FAILURE_WINDOW_MS;
33239
33318
  const refreshCooldownMs = options.refreshCooldownMs ?? DEFAULT_REFRESH_COOLDOWN_MS;
33319
+ const cooldownProbeDelays = options.cooldownProbeDelaysMs ?? DEFAULT_COOLDOWN_PROBE_DELAYS_MS;
33320
+ const probeHub = options.probeHub ?? probeHubReachable;
33240
33321
  const runHistoryLimit = options.runHistoryLimit ?? DEFAULT_RUN_HISTORY_LIMIT2;
33241
33322
  const now = options.now ?? (() => Date.now());
33242
33323
  const writeMirror = options.writeMirror ?? writeAttachmentMirror;
@@ -33262,6 +33343,9 @@ async function runDashboardRuntimeDaemon(options = {}) {
33262
33343
  let refreshTimer = null;
33263
33344
  const refreshFailures = [];
33264
33345
  let cooldownUntil = 0;
33346
+ let cooldownEndedEarly = false;
33347
+ let hubUnreachable = false;
33348
+ const probeAbort = new AbortController();
33265
33349
  const executionClient = {
33266
33350
  sendRunEvent(event) {
33267
33351
  const current = client ?? lastSocketClient;
@@ -33378,8 +33462,9 @@ async function runDashboardRuntimeDaemon(options = {}) {
33378
33462
  while (refreshFailures.length > 0 && ts - (refreshFailures[0] ?? 0) > refreshFailureWindowMs) {
33379
33463
  refreshFailures.shift();
33380
33464
  }
33381
- if (refreshFailures.length >= refreshFailureLimit) {
33465
+ if (refreshFailures.length >= refreshFailureLimit || cooldownEndedEarly) {
33382
33466
  cooldownUntil = ts + refreshCooldownMs;
33467
+ cooldownEndedEarly = false;
33383
33468
  refreshFailures.length = 0;
33384
33469
  log(
33385
33470
  "warn",
@@ -33389,6 +33474,38 @@ async function runDashboardRuntimeDaemon(options = {}) {
33389
33474
  );
33390
33475
  }
33391
33476
  }
33477
+ async function waitOutCooldown(dashboardUrl) {
33478
+ let attempt = 0;
33479
+ let sawHubDown = false;
33480
+ try {
33481
+ while (!stopped && cooldownUntil > now()) {
33482
+ const remainingMs = cooldownUntil - now();
33483
+ if (cooldownProbeDelays.length === 0) {
33484
+ await delay(remainingMs);
33485
+ return;
33486
+ }
33487
+ const probeDelayMs = cooldownProbeDelays[Math.min(attempt, cooldownProbeDelays.length - 1)] ?? remainingMs;
33488
+ attempt += 1;
33489
+ await delay(Math.min(remainingMs, probeDelayMs));
33490
+ if (stopped || cooldownUntil <= now()) return;
33491
+ const reachable = await probeHub(dashboardUrl, probeAbort.signal);
33492
+ hubUnreachable = !reachable;
33493
+ if (!reachable) {
33494
+ sawHubDown = true;
33495
+ } else if (sawHubDown) {
33496
+ cooldownUntil = 0;
33497
+ cooldownEndedEarly = true;
33498
+ log(
33499
+ "info",
33500
+ "runtime daemon: hub is reachable again; ending refresh cooldown early"
33501
+ );
33502
+ return;
33503
+ }
33504
+ }
33505
+ } finally {
33506
+ hubUnreachable = false;
33507
+ }
33508
+ }
33392
33509
  async function connectOnce() {
33393
33510
  const config2 = readConfig2();
33394
33511
  if (!config2) {
@@ -33398,17 +33515,18 @@ async function runDashboardRuntimeDaemon(options = {}) {
33398
33515
  const remainingMs = Math.max(0, cooldownUntil - now());
33399
33516
  log(
33400
33517
  "warn",
33401
- `runtime daemon: refresh cooldown active, sleeping ${Math.ceil(
33518
+ `runtime daemon: refresh cooldown active for ${Math.ceil(
33402
33519
  remainingMs / 1e3
33403
- )}s`
33520
+ )}s; probing the hub meanwhile`
33404
33521
  );
33405
- await delay(remainingMs);
33522
+ await waitOutCooldown(config2.dashboardUrl);
33406
33523
  if (stopped) return;
33407
33524
  }
33408
33525
  let token;
33409
33526
  try {
33410
33527
  token = await refreshAccessTokenFn();
33411
33528
  refreshFailures.length = 0;
33529
+ cooldownEndedEarly = false;
33412
33530
  } catch (err) {
33413
33531
  noteRefreshFailure();
33414
33532
  throw err;
@@ -33540,7 +33658,8 @@ async function runDashboardRuntimeDaemon(options = {}) {
33540
33658
  const executionSnapshot = pairedExecution.snapshot();
33541
33659
  const refreshState = refreshFailures.length > 0 || cooldownUntil > now() ? {
33542
33660
  recentFailures: refreshFailures.length,
33543
- ...cooldownUntil > now() ? { cooldownUntilMs: cooldownUntil } : {}
33661
+ ...cooldownUntil > now() ? { cooldownUntilMs: cooldownUntil } : {},
33662
+ ...hubUnreachable ? { hubUnreachable: true } : {}
33544
33663
  } : void 0;
33545
33664
  return {
33546
33665
  startedAt,
@@ -33560,6 +33679,7 @@ async function runDashboardRuntimeDaemon(options = {}) {
33560
33679
  async stop(reason = "stopped") {
33561
33680
  stopped = true;
33562
33681
  clearRefreshTimer();
33682
+ probeAbort.abort();
33563
33683
  workflowWatcher.close();
33564
33684
  pairedFeedPublisher.close();
33565
33685
  const current = client;
@@ -33962,6 +34082,7 @@ async function startRunnerProcess(options = {}) {
33962
34082
  outbox,
33963
34083
  now,
33964
34084
  onError: (message) => log("warn", message),
34085
+ onInfo: (message) => log("info", message),
33965
34086
  ...options.feedDrainIntervalMs !== void 0 ? { drainIntervalMs: options.feedDrainIntervalMs } : {}
33966
34087
  });
33967
34088
  daemon = await runDashboardRuntimeDaemon({
@@ -34136,4 +34257,4 @@ export {
34136
34257
  startRunnerProcess,
34137
34258
  waitForShutdownSignal
34138
34259
  };
34139
- //# sourceMappingURL=chunk-ANYY7XRT.js.map
34260
+ //# sourceMappingURL=chunk-BLCX7JVV.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  startRunnerProcess,
3
3
  waitForShutdownSignal
4
- } from "./chunk-ANYY7XRT.js";
4
+ } from "./chunk-BLCX7JVV.js";
5
5
 
6
6
  // src/app/entry/runnerDaemon.ts
7
7
  import path from "path";
@@ -46,4 +46,4 @@ if (process.argv.includes("--help")) {
46
46
  export {
47
47
  runRunnerDaemonEntry
48
48
  };
49
- //# sourceMappingURL=chunk-VETXW62U.js.map
49
+ //# sourceMappingURL=chunk-FY3TIGHI.js.map
package/dist/cli.js CHANGED
@@ -102,7 +102,7 @@ import {
102
102
  wrapText,
103
103
  writeAttachmentMirror,
104
104
  writeDashboardClientConfig
105
- } from "./chunk-ANYY7XRT.js";
105
+ } from "./chunk-BLCX7JVV.js";
106
106
  import {
107
107
  generateId as generateId2
108
108
  } from "./chunk-USK5VSQ7.js";
@@ -13148,7 +13148,7 @@ var cachedVersion = null;
13148
13148
  function readPackageVersion() {
13149
13149
  if (cachedVersion !== null) return cachedVersion;
13150
13150
  try {
13151
- const injected = "0.6.2";
13151
+ const injected = "0.6.4";
13152
13152
  if (typeof injected === "string" && injected.length > 0) {
13153
13153
  cachedVersion = injected;
13154
13154
  return cachedVersion;
@@ -13467,11 +13467,19 @@ async function runRunnerCommand(input, deps = {}) {
13467
13467
  const remainingSec = Math.ceil(
13468
13468
  (r.refreshState.cooldownUntilMs - now()) / 1e3
13469
13469
  );
13470
- logError(
13471
- `refresh: circuit-broken \u2014 sleeping for ${formatDuration2(
13472
- remainingSec
13473
- )} before retry. Re-pair if this persists.`
13474
- );
13470
+ if (r.refreshState.hubUnreachable) {
13471
+ logError(
13472
+ `refresh: circuit-broken \u2014 hub unreachable; probing it and reconnecting as soon as it answers (retries anyway in ${formatDuration2(
13473
+ remainingSec
13474
+ )})`
13475
+ );
13476
+ } else {
13477
+ logError(
13478
+ `refresh: circuit-broken \u2014 sleeping for ${formatDuration2(
13479
+ remainingSec
13480
+ )} before retry. Re-pair if this persists.`
13481
+ );
13482
+ }
13475
13483
  } else {
13476
13484
  logOut(
13477
13485
  `refresh: ${r.refreshState.recentFailures} recent failure(s); next reconnect will retry`
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  runRunnerDaemonEntry
3
- } from "./chunk-VETXW62U.js";
4
- import "./chunk-ANYY7XRT.js";
3
+ } from "./chunk-FY3TIGHI.js";
4
+ import "./chunk-BLCX7JVV.js";
5
5
  import "./chunk-USK5VSQ7.js";
6
6
  import "./chunk-QVRTU2MM.js";
7
7
  import "./chunk-PZ5AY32C.js";
package/dist/runner.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  runRunnerDaemonEntry
3
- } from "./chunk-VETXW62U.js";
4
- import "./chunk-ANYY7XRT.js";
3
+ } from "./chunk-FY3TIGHI.js";
4
+ import "./chunk-BLCX7JVV.js";
5
5
  import "./chunk-USK5VSQ7.js";
6
6
  import "./chunk-QVRTU2MM.js";
7
7
  import "./chunk-PZ5AY32C.js";
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "hooks",
18
18
  "dashboard"
19
19
  ],
20
- "version": "0.6.2",
20
+ "version": "0.6.4",
21
21
  "license": "MIT",
22
22
  "bin": {
23
23
  "athena": "dist/cli.js",