shraga 0.1.47 → 0.1.48

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shraga",
3
- "version": "0.1.47",
3
+ "version": "0.1.48",
4
4
  "description": "The teammate you delegate coding to — a self-hostable, multi-user AI coding agent web UI (Claude Code, with a pluggable engine seam).",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -25,6 +25,8 @@ export class SelfUpgrade {
25
25
 
26
26
  private get o(): SelfUpgradeOptions { return this.options as SelfUpgradeOptions; }
27
27
 
28
+ private watching = false;
29
+
28
30
  /** Version of `pkg` this deployment currently has pinned in its app-root package.json. */
29
31
  public currentVersion(): string | null {
30
32
  try {
@@ -170,7 +172,14 @@ export class SelfUpgrade {
170
172
  */
171
173
  public deliverPendingReport(): UpgradeReport | null {
172
174
  const report = this.lastReport();
173
- if (!report) return null;
175
+ if (!report) {
176
+ // The supervisor writes the report only AFTER this process has booted and soaked, so on a
177
+ // successful upgrade there is NOTHING here yet — boot always loses the race. Without this
178
+ // watch the outcome DM was never delivered, and the in-flight marker sat until it aged out
179
+ // (30m), blocking every retry in between. Keep watching while the marker is live.
180
+ if (this.inFlight()) this.watchForReport();
181
+ return null;
182
+ }
174
183
  try { unlinkSync(this.o.reportFile); } catch { /* report already gone; emit anyway */ }
175
184
  try { unlinkSync(this.o.lockFile); } catch { /* no lock to clear */ }
176
185
 
@@ -178,6 +187,21 @@ export class SelfUpgrade {
178
187
  emitEvent('self-upgrade.finished', report);
179
188
  return report;
180
189
  }
190
+
191
+ /** Poll until the supervisor's report lands (or its marker ages out), then deliver it exactly
192
+ * once. Unref'd: a pending upgrade watch must never hold the process open. */
193
+ private watchForReport(): void {
194
+ if (this.watching) return;
195
+ this.watching = true;
196
+ const timer = setInterval(() => {
197
+ if (!this.inFlight()) { clearInterval(timer); this.watching = false; return; } // aged out
198
+ if (!existsSync(this.o.reportFile)) return;
199
+ clearInterval(timer);
200
+ this.watching = false;
201
+ this.deliverPendingReport();
202
+ }, this.o.reportPollMs);
203
+ timer.unref?.();
204
+ }
181
205
  }
182
206
 
183
207
  export class SelfUpgradeOptions {
@@ -193,6 +217,8 @@ export class SelfUpgradeOptions {
193
217
  public supervisorScript: string = path.join(PACKAGE_ROOT, 'src', 'server', 'self-upgrade', 'supervisor.sh');
194
218
  public reportFile: string = dataPath('.self-upgrade', 'report.json');
195
219
  public lockFile: string = dataPath('.self-upgrade', 'in-flight.json');
220
+ /** How often the post-boot watch looks for the supervisor's report. */
221
+ public reportPollMs: number = 5_000;
196
222
  public bootTimeoutSec: number = 180;
197
223
  public soakSec: number = 60;
198
224
  /** After this, an in-flight marker is treated as abandoned (supervisor killed by a reboot). */