@quantiya/codevibe-antigravity-plugin 2.0.50 → 2.0.51
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/server.js +221 -38
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -290,7 +290,64 @@ var fs2 = __toESM(require("fs"));
|
|
|
290
290
|
var os2 = __toESM(require("os"));
|
|
291
291
|
var path2 = __toESM(require("path"));
|
|
292
292
|
var import_chokidar = require("chokidar");
|
|
293
|
+
|
|
294
|
+
// src/shutdown-bounds.ts
|
|
295
|
+
var TEARDOWN_STEP_TIMEOUT_MS = 3e3;
|
|
296
|
+
var WATCHER_CLOSE_TIMEOUT_MS = 2e3;
|
|
297
|
+
var SHUTDOWN_WATCHDOG_MS = 15e3;
|
|
298
|
+
async function boundedStep(work, ms, onTimeout) {
|
|
299
|
+
let timer;
|
|
300
|
+
try {
|
|
301
|
+
const started = (() => {
|
|
302
|
+
try {
|
|
303
|
+
return Promise.resolve(work());
|
|
304
|
+
} catch (err) {
|
|
305
|
+
return Promise.reject(err);
|
|
306
|
+
}
|
|
307
|
+
})();
|
|
308
|
+
const timeout = new Promise((resolve3) => {
|
|
309
|
+
timer = setTimeout(() => resolve3("timeout"), ms);
|
|
310
|
+
if (timer.unref) timer.unref();
|
|
311
|
+
});
|
|
312
|
+
const outcome = await Promise.race([
|
|
313
|
+
started.then(
|
|
314
|
+
() => "settled",
|
|
315
|
+
() => "settled"
|
|
316
|
+
),
|
|
317
|
+
timeout
|
|
318
|
+
]);
|
|
319
|
+
if (outcome === "timeout") {
|
|
320
|
+
void started.catch(() => void 0);
|
|
321
|
+
try {
|
|
322
|
+
onTimeout?.();
|
|
323
|
+
} catch {
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return outcome;
|
|
327
|
+
} finally {
|
|
328
|
+
if (timer) clearTimeout(timer);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function armShutdownWatchdog(exit, onFire, ms = SHUTDOWN_WATCHDOG_MS, durableObligationSettled = () => true) {
|
|
332
|
+
const timer = setTimeout(() => {
|
|
333
|
+
try {
|
|
334
|
+
onFire();
|
|
335
|
+
} catch {
|
|
336
|
+
}
|
|
337
|
+
let settled = false;
|
|
338
|
+
try {
|
|
339
|
+
settled = durableObligationSettled();
|
|
340
|
+
} catch {
|
|
341
|
+
settled = false;
|
|
342
|
+
}
|
|
343
|
+
exit(settled ? 0 : 1);
|
|
344
|
+
}, ms);
|
|
345
|
+
return () => clearTimeout(timer);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// src/transcript-tailer.ts
|
|
293
349
|
var OFFSET_FLUSH_INTERVAL_MS = 3e4;
|
|
350
|
+
var ERROR_LOG_MIN_INTERVAL_MS = 1e4;
|
|
294
351
|
var LRU_CAP = 50;
|
|
295
352
|
var TRANSCRIPT_FILE_REGEX = /\/brain\/([0-9a-fA-F-]{36})\/\.system_generated\/logs\/transcript\.jsonl$/;
|
|
296
353
|
function defaultPaths() {
|
|
@@ -306,6 +363,11 @@ var TranscriptTailer = class extends import_events.EventEmitter {
|
|
|
306
363
|
this.watcher = null;
|
|
307
364
|
this.isWatching = false;
|
|
308
365
|
this.isShuttingDown = false;
|
|
366
|
+
/** Error-log rate limiting (incident 2026-09-22 — a wedged watcher wrote
|
|
367
|
+
* 356,298 identical EMFILE errors / 96 MB before the daemon was killed). */
|
|
368
|
+
this.lastErrorSignature = null;
|
|
369
|
+
this.lastErrorLoggedAtMs = 0;
|
|
370
|
+
this.suppressedErrorCount = 0;
|
|
309
371
|
/** Per-file offset state (in-memory mirror of the persisted store). */
|
|
310
372
|
this.offsets = /* @__PURE__ */ new Map();
|
|
311
373
|
/** Conversation UUIDs we're actively tailing. v1.0 pins to single-active
|
|
@@ -383,8 +445,7 @@ var TranscriptTailer = class extends import_events.EventEmitter {
|
|
|
383
445
|
this.watcher.on("change", (filePath) => this.onFileChanged(filePath));
|
|
384
446
|
this.watcher.on("unlink", (filePath) => this.onFileUnlinked(filePath));
|
|
385
447
|
this.watcher.on("error", (error) => {
|
|
386
|
-
|
|
387
|
-
this.emitError(error);
|
|
448
|
+
this.emitError(error, "Chokidar error");
|
|
388
449
|
});
|
|
389
450
|
this.watcher.on("ready", () => {
|
|
390
451
|
logger.info("Transcript tailer ready (chokidar initial scan complete)");
|
|
@@ -404,15 +465,25 @@ var TranscriptTailer = class extends import_events.EventEmitter {
|
|
|
404
465
|
* covers the residual windows — the doStop teardown detaches listeners
|
|
405
466
|
* BEFORE the tailer stops, and any embedder that never attached one.
|
|
406
467
|
*/
|
|
407
|
-
emitError(error) {
|
|
468
|
+
emitError(error, chokidarLabel) {
|
|
408
469
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
409
|
-
|
|
410
|
-
|
|
470
|
+
const signature = String(err);
|
|
471
|
+
const now = Date.now();
|
|
472
|
+
const suppressed = signature === this.lastErrorSignature && now - this.lastErrorLoggedAtMs < ERROR_LOG_MIN_INTERVAL_MS;
|
|
473
|
+
const hasListener = this.listenerCount("error") > 0;
|
|
474
|
+
if (suppressed) {
|
|
475
|
+
this.suppressedErrorCount++;
|
|
411
476
|
} else {
|
|
412
|
-
logger.error("Transcript tailer error (
|
|
413
|
-
error:
|
|
477
|
+
logger.error(chokidarLabel ?? "Transcript tailer error (non-fatal)", {
|
|
478
|
+
error: signature,
|
|
479
|
+
...hasListener ? {} : { noListenerAttached: true },
|
|
480
|
+
...this.suppressedErrorCount > 0 ? { suppressedRepeats: this.suppressedErrorCount } : {}
|
|
414
481
|
});
|
|
482
|
+
this.lastErrorSignature = signature;
|
|
483
|
+
this.lastErrorLoggedAtMs = now;
|
|
484
|
+
this.suppressedErrorCount = 0;
|
|
415
485
|
}
|
|
486
|
+
if (hasListener) this.emit("error", err);
|
|
416
487
|
}
|
|
417
488
|
/**
|
|
418
489
|
* Stop watching. Flushes offsets one last time before closing chokidar.
|
|
@@ -432,10 +503,16 @@ var TranscriptTailer = class extends import_events.EventEmitter {
|
|
|
432
503
|
logger.warn("Final offset flush failed", { error });
|
|
433
504
|
}
|
|
434
505
|
if (this.watcher) {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
506
|
+
const watcher = this.watcher;
|
|
507
|
+
const outcome = await boundedStep(
|
|
508
|
+
() => watcher.close(),
|
|
509
|
+
WATCHER_CLOSE_TIMEOUT_MS,
|
|
510
|
+
() => logger.warn("chokidar watcher.close() timed out \u2014 abandoning it", {
|
|
511
|
+
timeoutMs: WATCHER_CLOSE_TIMEOUT_MS
|
|
512
|
+
})
|
|
513
|
+
);
|
|
514
|
+
if (outcome === "timeout") {
|
|
515
|
+
logger.warn("Transcript tailer watcher abandoned; relying on shutdown watchdog");
|
|
439
516
|
}
|
|
440
517
|
this.watcher = null;
|
|
441
518
|
}
|
|
@@ -443,7 +520,12 @@ var TranscriptTailer = class extends import_events.EventEmitter {
|
|
|
443
520
|
this.pendingReads.clear();
|
|
444
521
|
this.isWatching = false;
|
|
445
522
|
this.isShuttingDown = false;
|
|
446
|
-
logger.info(
|
|
523
|
+
logger.info(
|
|
524
|
+
"Transcript tailer stopped",
|
|
525
|
+
this.suppressedErrorCount > 0 ? { suppressedErrorRepeats: this.suppressedErrorCount } : void 0
|
|
526
|
+
);
|
|
527
|
+
this.lastErrorSignature = null;
|
|
528
|
+
this.suppressedErrorCount = 0;
|
|
447
529
|
}
|
|
448
530
|
/**
|
|
449
531
|
* Best-effort sync flush. Safe to call from a process EXIT trap.
|
|
@@ -3703,6 +3785,17 @@ var McpServer = class _McpServer {
|
|
|
3703
3785
|
* stop() — preserving the monotonic invariant lets stale callbacks
|
|
3704
3786
|
* reliably detect they're stale. (Codex Stage 2 R1 v5 MED 2026-05-20.) */
|
|
3705
3787
|
this.lifecycleGen = 0;
|
|
3788
|
+
/** Tracks the durable settlement obligation for a launch row. Set when a
|
|
3789
|
+
* create is ATTEMPTED (before the await — the marker is pre-set), cleared on
|
|
3790
|
+
* a confirmed retirement AND when the create threw. Read by the shutdown
|
|
3791
|
+
* watchdog to choose its exit code. (R2-F1 / R3-F1 / R3-F2 — see
|
|
3792
|
+
* isDurableObligationSettled() for the full predicate and its accepted
|
|
3793
|
+
* residual.) */
|
|
3794
|
+
this.durableObligationOpen = false;
|
|
3795
|
+
/** Teardown steps that exceeded their bound during the current stop().
|
|
3796
|
+
* Surfaced in the shutdown summary so a wedge is visible in the log even
|
|
3797
|
+
* though it no longer blocks exit. (Incident 2026-09-22.) */
|
|
3798
|
+
this.teardownStepsTimedOut = [];
|
|
3706
3799
|
/** In-flight stop() promise so a re-entrant call (e.g. double SIGINT)
|
|
3707
3800
|
* awaits the same shutdown instead of bypassing it and calling
|
|
3708
3801
|
* process.exit before the original stop's cleanup completes.
|
|
@@ -3852,8 +3945,7 @@ var McpServer = class _McpServer {
|
|
|
3852
3945
|
if (!this.started) {
|
|
3853
3946
|
return { httpPort: 0 };
|
|
3854
3947
|
}
|
|
3855
|
-
this.addListener(this.transcriptTailer, "error", (
|
|
3856
|
-
logger.error("transcript tailer error (non-fatal)", { error: String(err) });
|
|
3948
|
+
this.addListener(this.transcriptTailer, "error", () => {
|
|
3857
3949
|
});
|
|
3858
3950
|
this.addListener(this.transcriptTailer, "event", (emit) => {
|
|
3859
3951
|
const task = this.handleTranscriptEmit(emit).catch((err) => {
|
|
@@ -3915,6 +4007,7 @@ var McpServer = class _McpServer {
|
|
|
3915
4007
|
}
|
|
3916
4008
|
async doStop() {
|
|
3917
4009
|
this.started = false;
|
|
4010
|
+
this.teardownStepsTimedOut = [];
|
|
3918
4011
|
this.stopTmuxLifecycleMonitor();
|
|
3919
4012
|
const launchSettlement = this.launchSessionPromise?.catch(() => void 0) ?? null;
|
|
3920
4013
|
let mustAwaitLaunchSettlement = false;
|
|
@@ -3935,6 +4028,7 @@ var McpServer = class _McpServer {
|
|
|
3935
4028
|
const pendingId = this.pendingLaunchSessionId;
|
|
3936
4029
|
this.stopRequestedDuringCreate = true;
|
|
3937
4030
|
const deactivated = await this.deactivateLaunchRow(pendingId);
|
|
4031
|
+
if (deactivated) this.durableObligationOpen = false;
|
|
3938
4032
|
if (deactivated) {
|
|
3939
4033
|
if (this.pendingLaunchSessionId === pendingId) this.pendingLaunchSessionId = null;
|
|
3940
4034
|
} else {
|
|
@@ -3971,6 +4065,7 @@ var McpServer = class _McpServer {
|
|
|
3971
4065
|
logger.warn("[tool-activity] Agy shutdown drain failed", { error: String(err) });
|
|
3972
4066
|
}
|
|
3973
4067
|
const retired = await this.deactivateLaunchRow(this.session.sessionId);
|
|
4068
|
+
if (retired) this.durableObligationOpen = false;
|
|
3974
4069
|
if (!retired) {
|
|
3975
4070
|
hostedRetirementError = new Error(
|
|
3976
4071
|
`Failed to retire hosted Antigravity session ${this.session.sessionId}`
|
|
@@ -3984,26 +4079,10 @@ var McpServer = class _McpServer {
|
|
|
3984
4079
|
}
|
|
3985
4080
|
}
|
|
3986
4081
|
this.unregisterSignalHandlers();
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
}
|
|
3992
|
-
try {
|
|
3993
|
-
await this.paneObserver.stop();
|
|
3994
|
-
} catch (err) {
|
|
3995
|
-
logger.warn("paneObserver.stop failed", { error: String(err) });
|
|
3996
|
-
}
|
|
3997
|
-
try {
|
|
3998
|
-
await this.approvalDetector.stop();
|
|
3999
|
-
} catch (err) {
|
|
4000
|
-
logger.warn("approvalDetector.stop failed", { error: String(err) });
|
|
4001
|
-
}
|
|
4002
|
-
try {
|
|
4003
|
-
await this.httpApi.stop();
|
|
4004
|
-
} catch (err) {
|
|
4005
|
-
logger.warn("httpApi.stop failed", { error: String(err) });
|
|
4006
|
-
}
|
|
4082
|
+
await this.boundedTeardownStep("transcriptTailer.stop", () => this.transcriptTailer.stop());
|
|
4083
|
+
await this.boundedTeardownStep("paneObserver.stop", () => this.paneObserver.stop());
|
|
4084
|
+
await this.boundedTeardownStep("approvalDetector.stop", () => this.approvalDetector.stop());
|
|
4085
|
+
await this.boundedTeardownStep("httpApi.stop", () => this.httpApi.stop());
|
|
4007
4086
|
try {
|
|
4008
4087
|
this.appSyncClient.cleanupSubscriptions();
|
|
4009
4088
|
} catch (err) {
|
|
@@ -4021,6 +4100,7 @@ var McpServer = class _McpServer {
|
|
|
4021
4100
|
if (this.pendingLaunchSessionId) {
|
|
4022
4101
|
const pendingId = this.pendingLaunchSessionId;
|
|
4023
4102
|
const retired = await this.deactivateLaunchRow(pendingId);
|
|
4103
|
+
if (retired) this.durableObligationOpen = false;
|
|
4024
4104
|
if (retired) {
|
|
4025
4105
|
if (this.pendingLaunchSessionId === pendingId) this.pendingLaunchSessionId = null;
|
|
4026
4106
|
} else {
|
|
@@ -4117,6 +4197,7 @@ var McpServer = class _McpServer {
|
|
|
4117
4197
|
const userId = this.appSyncClient.getCurrentUserId();
|
|
4118
4198
|
const projectPath = process.cwd();
|
|
4119
4199
|
this.pendingLaunchSessionId = sessionId;
|
|
4200
|
+
this.durableObligationOpen = true;
|
|
4120
4201
|
let sessionKey = null;
|
|
4121
4202
|
let sessionKeyGen = null;
|
|
4122
4203
|
try {
|
|
@@ -4138,7 +4219,10 @@ var McpServer = class _McpServer {
|
|
|
4138
4219
|
sessionKey = result.sessionKey ?? null;
|
|
4139
4220
|
sessionKeyGen = result.sessionKeyGen ?? null;
|
|
4140
4221
|
} catch (err) {
|
|
4141
|
-
if (this.pendingLaunchSessionId === sessionId)
|
|
4222
|
+
if (this.pendingLaunchSessionId === sessionId) {
|
|
4223
|
+
this.pendingLaunchSessionId = null;
|
|
4224
|
+
this.durableObligationOpen = false;
|
|
4225
|
+
}
|
|
4142
4226
|
const msg = String(err);
|
|
4143
4227
|
if (msg.includes("session-limit-exceeded")) {
|
|
4144
4228
|
logger.warn("Free-tier session limit reached \u2014 mobile sync disabled for this wrapper", { sessionId });
|
|
@@ -4158,7 +4242,10 @@ var McpServer = class _McpServer {
|
|
|
4158
4242
|
if (gen !== this.lifecycleGen || !this.started || this.stopRequestedDuringCreate) {
|
|
4159
4243
|
const deactivated = await this.deactivateLaunchRow(sessionId);
|
|
4160
4244
|
if (deactivated) {
|
|
4161
|
-
if (this.pendingLaunchSessionId === sessionId)
|
|
4245
|
+
if (this.pendingLaunchSessionId === sessionId) {
|
|
4246
|
+
this.pendingLaunchSessionId = null;
|
|
4247
|
+
this.durableObligationOpen = false;
|
|
4248
|
+
}
|
|
4162
4249
|
} else {
|
|
4163
4250
|
logger.error(
|
|
4164
4251
|
"createLaunchSession bail: INACTIVE write for the just-created row FAILED \u2014 settlement obligation remains open (marker kept); the row may leak ACTIVE if nothing retries before exit (#638 H4)",
|
|
@@ -5503,9 +5590,23 @@ var McpServer = class _McpServer {
|
|
|
5503
5590
|
logger.info("Native Antigravity tmux session ended; stopping companion daemon", {
|
|
5504
5591
|
tmuxTarget
|
|
5505
5592
|
});
|
|
5593
|
+
const disarmLifecycleWatchdog = armShutdownWatchdog(
|
|
5594
|
+
(code) => process.exit(code),
|
|
5595
|
+
() => logger.error("Shutdown watchdog fired after native session end; exiting anyway", {
|
|
5596
|
+
watchdogMs: SHUTDOWN_WATCHDOG_MS,
|
|
5597
|
+
timedOutSteps: this.teardownStepsTimedOut,
|
|
5598
|
+
durableObligationSettled: this.isDurableObligationSettled()
|
|
5599
|
+
}),
|
|
5600
|
+
SHUTDOWN_WATCHDOG_MS,
|
|
5601
|
+
() => this.isDurableObligationSettled()
|
|
5602
|
+
);
|
|
5506
5603
|
void this.stop().then(
|
|
5507
|
-
() =>
|
|
5604
|
+
() => {
|
|
5605
|
+
disarmLifecycleWatchdog();
|
|
5606
|
+
process.exit(0);
|
|
5607
|
+
},
|
|
5508
5608
|
(error) => {
|
|
5609
|
+
disarmLifecycleWatchdog();
|
|
5509
5610
|
logger.error("Failed to stop companion daemon after native session end", {
|
|
5510
5611
|
error: error instanceof Error ? error.message : String(error)
|
|
5511
5612
|
});
|
|
@@ -5527,9 +5628,26 @@ var McpServer = class _McpServer {
|
|
|
5527
5628
|
this.signalsRegistered = true;
|
|
5528
5629
|
const onSignal = (sig) => {
|
|
5529
5630
|
logger.info(`${sig} received \u2014 evaluating terminal shutdown`);
|
|
5631
|
+
const disarm = armShutdownWatchdog(
|
|
5632
|
+
(code) => process.exit(code),
|
|
5633
|
+
() => logger.error("Shutdown watchdog fired \u2014 teardown did not complete in time; exiting anyway", {
|
|
5634
|
+
signal: sig,
|
|
5635
|
+
watchdogMs: SHUTDOWN_WATCHDOG_MS,
|
|
5636
|
+
timedOutSteps: this.teardownStepsTimedOut,
|
|
5637
|
+
durableObligationSettled: this.isDurableObligationSettled()
|
|
5638
|
+
}),
|
|
5639
|
+
SHUTDOWN_WATCHDOG_MS,
|
|
5640
|
+
() => this.isDurableObligationSettled()
|
|
5641
|
+
);
|
|
5530
5642
|
void this.stopForTerminalSignal(sig).then((stopped) => {
|
|
5531
|
-
if (stopped)
|
|
5643
|
+
if (stopped) {
|
|
5644
|
+
disarm();
|
|
5645
|
+
process.exit(0);
|
|
5646
|
+
} else {
|
|
5647
|
+
disarm();
|
|
5648
|
+
}
|
|
5532
5649
|
}).catch((err) => {
|
|
5650
|
+
disarm();
|
|
5533
5651
|
logger.error("shutdown error", { error: String(err) });
|
|
5534
5652
|
process.exit(1);
|
|
5535
5653
|
});
|
|
@@ -5566,6 +5684,71 @@ var McpServer = class _McpServer {
|
|
|
5566
5684
|
await this.stop();
|
|
5567
5685
|
return true;
|
|
5568
5686
|
}
|
|
5687
|
+
/**
|
|
5688
|
+
* Has shutdown's DURABLE obligation landed? (Stage 1 r1 finding F3.)
|
|
5689
|
+
*
|
|
5690
|
+
* The only obligation that outliving this process can strand is the backend
|
|
5691
|
+
* session row being marked INACTIVE.
|
|
5692
|
+
*
|
|
5693
|
+
* R2-F1: an earlier version read `pendingLaunchSessionId === null &&
|
|
5694
|
+
* session === null`. That was lifecycle-WIRING state used as a proxy for the
|
|
5695
|
+
* durable OUTCOME, and it was wrong in BOTH directions:
|
|
5696
|
+
* (a) `this.session = null` is cleared UNCONDITIONALLY at the end of
|
|
5697
|
+
* Phase 3, including after a retirement that FAILED and set
|
|
5698
|
+
* `hostedRetirementError` — so it reported success on a row known to
|
|
5699
|
+
* be stranded ACTIVE, which is the exact laundering F3 asked to close;
|
|
5700
|
+
* (b) `session` is only cleared AFTER Phase 3, so while the watchdog was
|
|
5701
|
+
* outlasting a wedged Phase-3 step it still read non-null even though
|
|
5702
|
+
* the INACTIVE write had already succeeded — reporting failure for the
|
|
5703
|
+
* wedged-local-handle case that F3 explicitly wanted to keep at exit 0.
|
|
5704
|
+
*
|
|
5705
|
+
* `durableObligationOpen` tracks the outcome directly instead:
|
|
5706
|
+
* - SET when a create is ATTEMPTED (the marker is pre-set before the
|
|
5707
|
+
* await), so it means "a create was attempted", not "a row exists";
|
|
5708
|
+
* - CLEARED when `deactivateLaunchRow` returns true. The stale-lifecycle
|
|
5709
|
+
* clear is additionally identity-guarded because that branch's
|
|
5710
|
+
* precondition is that a NEWER lifecycle exists. The other confirmed
|
|
5711
|
+
* clears are NOT guarded, deliberately: none of them has "a newer
|
|
5712
|
+
* lifecycle exists" as a precondition, and reaching the hazard would
|
|
5713
|
+
* need start() to overlap an in-flight doStop() (doStop sets
|
|
5714
|
+
* started=false first). The Phase-2 clear additionally retires
|
|
5715
|
+
* this.session.sessionId and has no marker to compare against at all,
|
|
5716
|
+
* so "unguarded" there is a category difference, not an omission;
|
|
5717
|
+
* - CLEARED when the create THREW (R3-F2).
|
|
5718
|
+
*
|
|
5719
|
+
* ACCEPTED RESIDUAL: the last rule also clears for a lost-response create
|
|
5720
|
+
* (the server made the row, the response never arrived), so that case is
|
|
5721
|
+
* reported SETTLED even though a row may exist. This is the irreducible
|
|
5722
|
+
* SIGKILL-class residual the file already accepts for
|
|
5723
|
+
* `pendingLaunchSessionId`; the alternative left the flag permanently true
|
|
5724
|
+
* for every free-tier and unkeyed daemon. The row leaked before this change
|
|
5725
|
+
* too and a non-zero exit would not have retired it, so only a diagnostic
|
|
5726
|
+
* signal is traded away.
|
|
5727
|
+
*/
|
|
5728
|
+
isDurableObligationSettled() {
|
|
5729
|
+
return !this.durableObligationOpen;
|
|
5730
|
+
}
|
|
5731
|
+
/**
|
|
5732
|
+
* Run one teardown step with a hard bound. Never throws and never hangs, so
|
|
5733
|
+
* a wedged step cannot prevent the steps after it from running.
|
|
5734
|
+
* (Incident 2026-09-22 — see src/shutdown-bounds.ts.)
|
|
5735
|
+
*/
|
|
5736
|
+
async boundedTeardownStep(label, work) {
|
|
5737
|
+
const outcome = await boundedStep(
|
|
5738
|
+
async () => {
|
|
5739
|
+
try {
|
|
5740
|
+
await work();
|
|
5741
|
+
} catch (err) {
|
|
5742
|
+
logger.warn(`${label} failed`, { error: String(err) });
|
|
5743
|
+
}
|
|
5744
|
+
},
|
|
5745
|
+
TEARDOWN_STEP_TIMEOUT_MS,
|
|
5746
|
+
() => logger.warn(`${label} timed out \u2014 continuing teardown without it`, {
|
|
5747
|
+
timeoutMs: TEARDOWN_STEP_TIMEOUT_MS
|
|
5748
|
+
})
|
|
5749
|
+
);
|
|
5750
|
+
if (outcome === "timeout") this.teardownStepsTimedOut.push(label);
|
|
5751
|
+
}
|
|
5569
5752
|
unregisterSignalHandlers() {
|
|
5570
5753
|
if (!this.signalsRegistered) return;
|
|
5571
5754
|
if (this.boundSigintHandler) process.off("SIGINT", this.boundSigintHandler);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quantiya/codevibe-antigravity-plugin",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.51",
|
|
4
4
|
"description": "Control Antigravity CLI from your iPhone and Android — real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"codevibe": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"node": ">=22.0.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@quantiya/codevibe-core": "^2.0.
|
|
49
|
+
"@quantiya/codevibe-core": "^2.0.48",
|
|
50
50
|
"chokidar": "^5.0.0",
|
|
51
51
|
"dotenv": "^16.6.1",
|
|
52
52
|
"express": "^5.1.0",
|