@drisp/cli 0.6.2 → 0.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.
@@ -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() {
@@ -33210,6 +33259,8 @@ var DEFAULT_REFRESH_LEAD_SEC = 60;
33210
33259
  var DEFAULT_REFRESH_FAILURE_LIMIT = 5;
33211
33260
  var DEFAULT_REFRESH_FAILURE_WINDOW_MS = 5 * 6e4;
33212
33261
  var DEFAULT_REFRESH_COOLDOWN_MS = 5 * 6e4;
33262
+ var DEFAULT_COOLDOWN_PROBE_DELAYS_MS = [2e3, 5e3, 1e4, 15e3];
33263
+ var HUB_PROBE_TIMEOUT_MS = 5e3;
33213
33264
  var DEFAULT_RUN_HISTORY_LIMIT2 = 100;
33214
33265
  function delay(ms) {
33215
33266
  return new Promise((resolve) => {
@@ -33217,6 +33268,28 @@ function delay(ms) {
33217
33268
  timer.unref();
33218
33269
  });
33219
33270
  }
33271
+ async function probeHubReachable(dashboardUrl, signal) {
33272
+ const controller = new AbortController();
33273
+ const onAbort = () => controller.abort();
33274
+ signal.addEventListener("abort", onAbort, { once: true });
33275
+ const timer = setTimeout(onAbort, HUB_PROBE_TIMEOUT_MS);
33276
+ timer.unref();
33277
+ try {
33278
+ const response = await fetch(`${dashboardUrl}/api/instances/refresh`, {
33279
+ method: "POST",
33280
+ headers: { "content-type": "application/json" },
33281
+ body: "{}",
33282
+ redirect: "manual",
33283
+ signal: controller.signal
33284
+ });
33285
+ return response.status < 500;
33286
+ } catch {
33287
+ return false;
33288
+ } finally {
33289
+ clearTimeout(timer);
33290
+ signal.removeEventListener("abort", onAbort);
33291
+ }
33292
+ }
33220
33293
  async function runDashboardRuntimeDaemon(options = {}) {
33221
33294
  const readConfig2 = options.readConfig ?? (() => readDashboardClientConfig());
33222
33295
  const refreshAccessTokenFn = options.refreshAccessToken ?? (async () => refreshDashboardAccessToken({}));
@@ -33237,6 +33310,8 @@ async function runDashboardRuntimeDaemon(options = {}) {
33237
33310
  const refreshFailureLimit = options.refreshFailureLimit ?? DEFAULT_REFRESH_FAILURE_LIMIT;
33238
33311
  const refreshFailureWindowMs = options.refreshFailureWindowMs ?? DEFAULT_REFRESH_FAILURE_WINDOW_MS;
33239
33312
  const refreshCooldownMs = options.refreshCooldownMs ?? DEFAULT_REFRESH_COOLDOWN_MS;
33313
+ const cooldownProbeDelays = options.cooldownProbeDelaysMs ?? DEFAULT_COOLDOWN_PROBE_DELAYS_MS;
33314
+ const probeHub = options.probeHub ?? probeHubReachable;
33240
33315
  const runHistoryLimit = options.runHistoryLimit ?? DEFAULT_RUN_HISTORY_LIMIT2;
33241
33316
  const now = options.now ?? (() => Date.now());
33242
33317
  const writeMirror = options.writeMirror ?? writeAttachmentMirror;
@@ -33262,6 +33337,9 @@ async function runDashboardRuntimeDaemon(options = {}) {
33262
33337
  let refreshTimer = null;
33263
33338
  const refreshFailures = [];
33264
33339
  let cooldownUntil = 0;
33340
+ let cooldownEndedEarly = false;
33341
+ let hubUnreachable = false;
33342
+ const probeAbort = new AbortController();
33265
33343
  const executionClient = {
33266
33344
  sendRunEvent(event) {
33267
33345
  const current = client ?? lastSocketClient;
@@ -33378,8 +33456,9 @@ async function runDashboardRuntimeDaemon(options = {}) {
33378
33456
  while (refreshFailures.length > 0 && ts - (refreshFailures[0] ?? 0) > refreshFailureWindowMs) {
33379
33457
  refreshFailures.shift();
33380
33458
  }
33381
- if (refreshFailures.length >= refreshFailureLimit) {
33459
+ if (refreshFailures.length >= refreshFailureLimit || cooldownEndedEarly) {
33382
33460
  cooldownUntil = ts + refreshCooldownMs;
33461
+ cooldownEndedEarly = false;
33383
33462
  refreshFailures.length = 0;
33384
33463
  log(
33385
33464
  "warn",
@@ -33389,6 +33468,38 @@ async function runDashboardRuntimeDaemon(options = {}) {
33389
33468
  );
33390
33469
  }
33391
33470
  }
33471
+ async function waitOutCooldown(dashboardUrl) {
33472
+ let attempt = 0;
33473
+ let sawHubDown = false;
33474
+ try {
33475
+ while (!stopped && cooldownUntil > now()) {
33476
+ const remainingMs = cooldownUntil - now();
33477
+ if (cooldownProbeDelays.length === 0) {
33478
+ await delay(remainingMs);
33479
+ return;
33480
+ }
33481
+ const probeDelayMs = cooldownProbeDelays[Math.min(attempt, cooldownProbeDelays.length - 1)] ?? remainingMs;
33482
+ attempt += 1;
33483
+ await delay(Math.min(remainingMs, probeDelayMs));
33484
+ if (stopped || cooldownUntil <= now()) return;
33485
+ const reachable = await probeHub(dashboardUrl, probeAbort.signal);
33486
+ hubUnreachable = !reachable;
33487
+ if (!reachable) {
33488
+ sawHubDown = true;
33489
+ } else if (sawHubDown) {
33490
+ cooldownUntil = 0;
33491
+ cooldownEndedEarly = true;
33492
+ log(
33493
+ "info",
33494
+ "runtime daemon: hub is reachable again; ending refresh cooldown early"
33495
+ );
33496
+ return;
33497
+ }
33498
+ }
33499
+ } finally {
33500
+ hubUnreachable = false;
33501
+ }
33502
+ }
33392
33503
  async function connectOnce() {
33393
33504
  const config2 = readConfig2();
33394
33505
  if (!config2) {
@@ -33398,17 +33509,18 @@ async function runDashboardRuntimeDaemon(options = {}) {
33398
33509
  const remainingMs = Math.max(0, cooldownUntil - now());
33399
33510
  log(
33400
33511
  "warn",
33401
- `runtime daemon: refresh cooldown active, sleeping ${Math.ceil(
33512
+ `runtime daemon: refresh cooldown active for ${Math.ceil(
33402
33513
  remainingMs / 1e3
33403
- )}s`
33514
+ )}s; probing the hub meanwhile`
33404
33515
  );
33405
- await delay(remainingMs);
33516
+ await waitOutCooldown(config2.dashboardUrl);
33406
33517
  if (stopped) return;
33407
33518
  }
33408
33519
  let token;
33409
33520
  try {
33410
33521
  token = await refreshAccessTokenFn();
33411
33522
  refreshFailures.length = 0;
33523
+ cooldownEndedEarly = false;
33412
33524
  } catch (err) {
33413
33525
  noteRefreshFailure();
33414
33526
  throw err;
@@ -33540,7 +33652,8 @@ async function runDashboardRuntimeDaemon(options = {}) {
33540
33652
  const executionSnapshot = pairedExecution.snapshot();
33541
33653
  const refreshState = refreshFailures.length > 0 || cooldownUntil > now() ? {
33542
33654
  recentFailures: refreshFailures.length,
33543
- ...cooldownUntil > now() ? { cooldownUntilMs: cooldownUntil } : {}
33655
+ ...cooldownUntil > now() ? { cooldownUntilMs: cooldownUntil } : {},
33656
+ ...hubUnreachable ? { hubUnreachable: true } : {}
33544
33657
  } : void 0;
33545
33658
  return {
33546
33659
  startedAt,
@@ -33560,6 +33673,7 @@ async function runDashboardRuntimeDaemon(options = {}) {
33560
33673
  async stop(reason = "stopped") {
33561
33674
  stopped = true;
33562
33675
  clearRefreshTimer();
33676
+ probeAbort.abort();
33563
33677
  workflowWatcher.close();
33564
33678
  pairedFeedPublisher.close();
33565
33679
  const current = client;
@@ -33962,6 +34076,7 @@ async function startRunnerProcess(options = {}) {
33962
34076
  outbox,
33963
34077
  now,
33964
34078
  onError: (message) => log("warn", message),
34079
+ onInfo: (message) => log("info", message),
33965
34080
  ...options.feedDrainIntervalMs !== void 0 ? { drainIntervalMs: options.feedDrainIntervalMs } : {}
33966
34081
  });
33967
34082
  daemon = await runDashboardRuntimeDaemon({
@@ -34136,4 +34251,4 @@ export {
34136
34251
  startRunnerProcess,
34137
34252
  waitForShutdownSignal
34138
34253
  };
34139
- //# sourceMappingURL=chunk-ANYY7XRT.js.map
34254
+ //# sourceMappingURL=chunk-7YXGHTA7.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  startRunnerProcess,
3
3
  waitForShutdownSignal
4
- } from "./chunk-ANYY7XRT.js";
4
+ } from "./chunk-7YXGHTA7.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-DQE7XYTJ.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-7YXGHTA7.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.3";
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-DQE7XYTJ.js";
4
+ import "./chunk-7YXGHTA7.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-DQE7XYTJ.js";
4
+ import "./chunk-7YXGHTA7.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.3",
21
21
  "license": "MIT",
22
22
  "bin": {
23
23
  "athena": "dist/cli.js",