@factiii/runner 0.13.0 → 0.13.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.
Files changed (2) hide show
  1. package/dist/cli.js +44 -6
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -17685,6 +17685,8 @@ var NOISE_GLOBS = ["**/.DS_Store", "**/4913", "**/*~", "**/.#*", "**/#*#"];
17685
17685
  var MAX_BATCH = 200;
17686
17686
  var FLUSH_MS = 120;
17687
17687
  var SELF_WRITE_MS = 2e3;
17688
+ var RETRY_MIN_MS = 500;
17689
+ var RETRY_MAX_MS = 15e3;
17688
17690
  var dynamicImport = new Function("s", "return import(s)");
17689
17691
  var importFs = new Function("s", "return import(s)");
17690
17692
  async function resolveReal(path23) {
@@ -17715,13 +17717,16 @@ var WorkspaceWatcher = class {
17715
17717
  * A no-git card's root is the space dir itself, which also
17716
17718
  * holds the runner's own bookkeeping.
17717
17719
  */
17718
- constructor(root, emit, exclude = []) {
17720
+ constructor(root, emit, exclude = [], subscribeImpl) {
17719
17721
  this.root = root;
17720
17722
  this.emit = emit;
17721
17723
  this.exclude = exclude;
17724
+ this.subscribeImpl = subscribeImpl;
17722
17725
  this.sub = null;
17723
17726
  this.pending = emptyPending();
17724
17727
  this.flushTimer = null;
17728
+ this.retryTimer = null;
17729
+ this.retryDelay = RETRY_MIN_MS;
17725
17730
  /** path -> expiry. Events for these are ours (the editor just saved) and must
17726
17731
  * not come back as "changed on disk". */
17727
17732
  this.selfWrites = /* @__PURE__ */ new Map();
@@ -17731,11 +17736,11 @@ var WorkspaceWatcher = class {
17731
17736
  /** Fire-and-forget: callers treat starting a watch as synchronous, and
17732
17737
  * nothing can be emitted before `subscribe` resolves anyway. */
17733
17738
  start() {
17734
- if (this.sub || this.closed) return;
17739
+ if (this.sub || this.closed || this.retryTimer) return;
17735
17740
  void this.open();
17736
17741
  }
17737
17742
  async open() {
17738
- const subscribe = await getSubscribe();
17743
+ const subscribe = this.subscribeImpl ?? await getSubscribe();
17739
17744
  if (!subscribe) return this.stop();
17740
17745
  this.watchRoot = await resolveReal(this.root);
17741
17746
  const exclude = await Promise.all(this.exclude.map(resolveReal));
@@ -17743,7 +17748,7 @@ var WorkspaceWatcher = class {
17743
17748
  const sub = await subscribe(
17744
17749
  this.watchRoot,
17745
17750
  (err, events) => {
17746
- if (err) return this.stop();
17751
+ if (err) return this.retry();
17747
17752
  for (const event of events) this.onEvent(event);
17748
17753
  },
17749
17754
  // Prunes the walk itself, so a pruned tree is never enumerated.
@@ -17760,10 +17765,39 @@ var WorkspaceWatcher = class {
17760
17765
  return;
17761
17766
  }
17762
17767
  this.sub = sub;
17768
+ this.retryDelay = RETRY_MIN_MS;
17763
17769
  } catch {
17764
- this.stop();
17770
+ this.retry();
17765
17771
  }
17766
17772
  }
17773
+ /**
17774
+ * Re-subscribe after the OS dropped the watch — a remount, a wake from
17775
+ * sleep, an inotify limit easing. Treating those as terminal left a runner
17776
+ * watching nothing for the rest of its life. `resync` on the way back up,
17777
+ * since whatever changed while it was down produced no events.
17778
+ */
17779
+ retry() {
17780
+ if (this.closed || this.retryTimer) return;
17781
+ const sub = this.sub;
17782
+ this.sub = null;
17783
+ if (sub) void sub.unsubscribe().catch(() => void 0);
17784
+ const delay = this.retryDelay;
17785
+ this.retryDelay = Math.min(this.retryDelay * 2, RETRY_MAX_MS);
17786
+ this.retryTimer = setTimeout(() => {
17787
+ this.retryTimer = null;
17788
+ if (this.closed) return;
17789
+ void this.open().then(() => {
17790
+ if (this.sub) {
17791
+ this.emit({
17792
+ added: [],
17793
+ changed: [],
17794
+ removed: [],
17795
+ resync: true
17796
+ });
17797
+ }
17798
+ });
17799
+ }, delay);
17800
+ }
17767
17801
  /** Give up and say so once, so the UI falls back to manual refresh rather
17768
17802
  * than showing a silently frozen tree. */
17769
17803
  stop() {
@@ -17799,6 +17833,10 @@ var WorkspaceWatcher = class {
17799
17833
  this.flushTimer = null;
17800
17834
  }
17801
17835
  this.pending = emptyPending();
17836
+ if (this.retryTimer) {
17837
+ clearTimeout(this.retryTimer);
17838
+ this.retryTimer = null;
17839
+ }
17802
17840
  this.selfWrites.clear();
17803
17841
  const sub = this.sub;
17804
17842
  this.sub = null;
@@ -19936,7 +19974,6 @@ var ScratchpadStore = class {
19936
19974
  */
19937
19975
  async list() {
19938
19976
  const dir = this.dir();
19939
- this.startWatching();
19940
19977
  const out = await this.target().run([
19941
19978
  "sh",
19942
19979
  "-c",
@@ -19946,6 +19983,7 @@ var ScratchpadStore = class {
19946
19983
  echo "$meta $f"
19947
19984
  done | LC_ALL=C sort -rn | head -n ${SCRATCHPAD_MAX_ITEMS}`
19948
19985
  ]);
19986
+ this.startWatching();
19949
19987
  const items = [];
19950
19988
  for (const line of out.split("\n")) {
19951
19989
  const match = /^(\d+) (\d+) (.+)$/.exec(line);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@factiii/runner",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "description": "Factiii Runner, run Board AI agents on a machine you control. Pairs with the Factiii web/mobile clients over WebRTC.",
5
5
  "license": "ISC",
6
6
  "keywords": [