@cmmd-center/forge 0.13.73 → 0.13.74
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/bin.cjs +290 -27
- package/dist/bin.mjs +290 -27
- package/dist/client/assets/{DiffPanel-BEf3EXPK.js → DiffPanel-Bn53zJRq.js} +2 -2
- package/dist/client/assets/{DiffPanel.logic-Bw1O_6UU.js → DiffPanel.logic-DF8NdwcA.js} +2 -2
- package/dist/client/assets/{DiffPanelShell-BoAQcVta.js → DiffPanelShell-CTLS82pj.js} +3 -3
- package/dist/client/assets/{DiffWorkerPoolProvider-C5Ln-c7Y.js → DiffWorkerPoolProvider-kxyfllf9.js} +2 -2
- package/dist/client/assets/{PullRequestCodePanel-Bx3me2lj.js → PullRequestCodePanel-D0imQaMo.js} +2 -2
- package/dist/client/assets/{Virtualizer-B4jYnrUE.js → Virtualizer-DsHt9iaj.js} +2 -2
- package/dist/client/assets/{index-C2Y_gZoG.js → index-Bf50oKP5.js} +6 -6
- package/dist/client/index.html +4 -4
- package/dist/forge-build.json +1 -1
- package/package.json +1 -1
package/dist/bin.cjs
CHANGED
|
@@ -66275,7 +66275,7 @@ function normalizeNumberish(value) {
|
|
|
66275
66275
|
}
|
|
66276
66276
|
//#endregion
|
|
66277
66277
|
//#region package.json
|
|
66278
|
-
var version$1 = "0.13.
|
|
66278
|
+
var version$1 = "0.13.74";
|
|
66279
66279
|
//#endregion
|
|
66280
66280
|
//#region src/sentry.ts
|
|
66281
66281
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -241692,17 +241692,30 @@ function isPromiseLike(value) {
|
|
|
241692
241692
|
* outlives the delete re-acquires the hold on its next tick; that was
|
|
241693
241693
|
* previously measured leaking 37 renewals over 18 minutes, pinning a sprite
|
|
241694
241694
|
* running — and billing — 33 minutes after its work had ended.
|
|
241695
|
-
* 2.
|
|
241696
|
-
*
|
|
241697
|
-
* hold for another full expiry window
|
|
241698
|
-
* awaits its heartbeat for the same reason.
|
|
241695
|
+
* 2. Cancellation must be CONFIRMED before the final DELETE is trusted. A PUT
|
|
241696
|
+
* already on the wire has to settle first, or the DELETE can land mid-PUT
|
|
241697
|
+
* and the PUT recreate the hold for another full expiry window; fly.io's
|
|
241698
|
+
* reference worker cancels and awaits its heartbeat for the same reason.
|
|
241699
|
+
* The join is bounded on the shutdown path
|
|
241700
|
+
* (SPRITE_TASK_HOLD_RELEASE_JOIN_TIMEOUT_MS) and the bound decides the
|
|
241701
|
+
* shape: every PUT settled within it, with no earlier PUT rejection left
|
|
241702
|
+
* unanswered by a clean refresh, confirms cancellation and one DELETE
|
|
241703
|
+
* finishes the hold; the bound firing first means cancellation cannot be
|
|
241704
|
+
* confirmed, so the release sweeps, DELETE, verify with a bounded GET, and
|
|
241705
|
+
* re-DELETE while the server still shows the hold, capped at
|
|
241706
|
+
* SPRITE_TASK_HOLD_SWEEP_ROUNDS rounds. Every hold gets at least one
|
|
241707
|
+
* DELETE, a resurrected hold is cleaned and swept again, and only a hold
|
|
241708
|
+
* positively present after the cap is reported unreleased at warn instead
|
|
241709
|
+
* of hanging SIGTERM open.
|
|
241699
241710
|
* 3. `release` can arrive while `acquire` is still awaiting its own PUT —
|
|
241700
241711
|
* `releaseAll` runs from runtime drain and the shutdown finalizer,
|
|
241701
241712
|
* concurrently with the provider event stream. The hold is registered before
|
|
241702
|
-
* that await (so a duplicate turn.started cannot double-acquire),
|
|
241703
|
-
*
|
|
241704
|
-
*
|
|
241705
|
-
*
|
|
241713
|
+
* that await (so a duplicate turn.started cannot double-acquire), and while
|
|
241714
|
+
* a release runs the hold stays mapped (joining, then deleting), so the
|
|
241715
|
+
* shutdown finalizer's bounded `releaseAll` can take a parked hold over;
|
|
241716
|
+
* `acquire` must then NOT install a refresher on the old hold, and a fresh
|
|
241717
|
+
* acquire replaces a released hold in the map. One refresher attached to a
|
|
241718
|
+
* hold no release path can reach would re-PUT every 60 s for the life of
|
|
241706
241719
|
* the process — an unbounded pin.
|
|
241707
241720
|
*
|
|
241708
241721
|
* All three are pinned by tests.
|
|
@@ -241726,6 +241739,30 @@ const SPRITE_TASK_HOLD_REFRESH_MS = 6e4;
|
|
|
241726
241739
|
*/
|
|
241727
241740
|
const SPRITE_TASK_HOLD_REQUEST_TIMEOUT_MS = 1e4;
|
|
241728
241741
|
/**
|
|
241742
|
+
* Shutdown bounds for the release the server finalizer issues. Both are one
|
|
241743
|
+
* request deadline. With a transport that honors its abort, every PUT still on
|
|
241744
|
+
* the wire settles within its own 10 s deadline, so a join sized at one
|
|
241745
|
+
* deadline always completes, cancellation is confirmed, and a single DELETE is
|
|
241746
|
+
* issued; the DELETE answers within its own deadline too.
|
|
241747
|
+
*
|
|
241748
|
+
* When the join bound fires, cancellation is unconfirmed and the release
|
|
241749
|
+
* sweeps: DELETE, verify with a bounded GET, and re-DELETE if the hold
|
|
241750
|
+
* survived, capped at SPRITE_TASK_HOLD_SWEEP_ROUNDS rounds. Worst case a
|
|
241751
|
+
* shutdown release costs join + 2 x (delete + verify), about 50 s, after
|
|
241752
|
+
* which the process exits and any hold left behind expires on its own within
|
|
241753
|
+
* 5 minutes. Drain passes no bounds. It keeps the full join, and its tick is
|
|
241754
|
+
* already capped by the heartbeat timeout.
|
|
241755
|
+
*/
|
|
241756
|
+
const SPRITE_TASK_HOLD_RELEASE_JOIN_TIMEOUT_MS = SPRITE_TASK_HOLD_REQUEST_TIMEOUT_MS;
|
|
241757
|
+
const SPRITE_TASK_HOLD_RELEASE_DELETE_TIMEOUT_MS = SPRITE_TASK_HOLD_REQUEST_TIMEOUT_MS;
|
|
241758
|
+
/**
|
|
241759
|
+
* How many delete-plus-verify rounds an unconfirmed release sweeps before it
|
|
241760
|
+
* gives up and reports the hold unreleased. Two is one retry past the first
|
|
241761
|
+
* delete: enough to clean a timed-out PUT that commits late, cheap enough
|
|
241762
|
+
* that a truly wedged socket cannot hold SIGTERM open much past a minute.
|
|
241763
|
+
*/
|
|
241764
|
+
const SPRITE_TASK_HOLD_SWEEP_ROUNDS = 2;
|
|
241765
|
+
/**
|
|
241729
241766
|
* Resolve the hold configuration from the process environment.
|
|
241730
241767
|
*
|
|
241731
241768
|
* Returns null on every runtime that is not a project sprite — desktop-local and
|
|
@@ -241747,6 +241784,20 @@ function resolveSpriteTaskHoldConfig(env = process.env) {
|
|
|
241747
241784
|
function spriteTaskHoldName(namePrefix, turnId) {
|
|
241748
241785
|
return `${namePrefix}${turnId}`;
|
|
241749
241786
|
}
|
|
241787
|
+
const noTimer = () => {};
|
|
241788
|
+
/**
|
|
241789
|
+
* Resolve with `promise`, but stop waiting after `timeoutMs`. Rejections pass
|
|
241790
|
+
* through untouched, and the caller decides what a timeout means. The
|
|
241791
|
+
* underlying operation keeps running either way.
|
|
241792
|
+
*/
|
|
241793
|
+
function boundedAwait(promise, timeoutMs) {
|
|
241794
|
+
if (timeoutMs === void 0) return promise;
|
|
241795
|
+
let clearTimer = noTimer;
|
|
241796
|
+
return Promise.race([promise, new Promise((resolve) => {
|
|
241797
|
+
const timer = setTimeout(resolve, timeoutMs);
|
|
241798
|
+
clearTimer = () => clearTimeout(timer);
|
|
241799
|
+
})]).finally(() => clearTimer());
|
|
241800
|
+
}
|
|
241750
241801
|
const DEFAULT_TIMER = {
|
|
241751
241802
|
setInterval: (handler, ms) => setInterval(handler, ms),
|
|
241752
241803
|
clearInterval: (handle) => {
|
|
@@ -241782,7 +241833,8 @@ var SpriteTaskHoldManager = class {
|
|
|
241782
241833
|
* whose turn may never start. When the budget is spent the hold releases itself.
|
|
241783
241834
|
*/
|
|
241784
241835
|
async acquire(turnId, threadId = null, options = {}) {
|
|
241785
|
-
|
|
241836
|
+
const existing = this.#holds.get(turnId);
|
|
241837
|
+
if (existing !== void 0 && !existing.released) return;
|
|
241786
241838
|
let refreshesLeft = options.maxRefreshes ?? Number.POSITIVE_INFINITY;
|
|
241787
241839
|
const name = spriteTaskHoldName(this.#config.namePrefix, turnId);
|
|
241788
241840
|
const hold = {
|
|
@@ -241790,15 +241842,21 @@ var SpriteTaskHoldManager = class {
|
|
|
241790
241842
|
threadId,
|
|
241791
241843
|
refresher: null,
|
|
241792
241844
|
inFlight: /* @__PURE__ */ new Set(),
|
|
241793
|
-
released: false
|
|
241845
|
+
released: false,
|
|
241846
|
+
phase: "idle",
|
|
241847
|
+
releaseAttempt: null,
|
|
241848
|
+
deletion: null,
|
|
241849
|
+
uncertainPut: false
|
|
241794
241850
|
};
|
|
241795
241851
|
this.#holds.set(turnId, hold);
|
|
241796
241852
|
const put = this.#transport.put(name, 300);
|
|
241797
|
-
const tracked = put.then(() =>
|
|
241853
|
+
const tracked = put.then(() => "succeeded", () => "failed");
|
|
241798
241854
|
hold.inFlight.add(tracked);
|
|
241799
241855
|
try {
|
|
241800
241856
|
await put;
|
|
241857
|
+
hold.uncertainPut = false;
|
|
241801
241858
|
} catch (cause) {
|
|
241859
|
+
hold.uncertainPut = true;
|
|
241802
241860
|
hold.inFlight.delete(tracked);
|
|
241803
241861
|
this.#onError(`failed to acquire sprite task hold for turn ${turnId}`, cause);
|
|
241804
241862
|
if (hold.released || this.#holds.get(turnId) !== hold) return;
|
|
@@ -241812,28 +241870,189 @@ var SpriteTaskHoldManager = class {
|
|
|
241812
241870
|
return;
|
|
241813
241871
|
}
|
|
241814
241872
|
refreshesLeft -= 1;
|
|
241815
|
-
const refresh = this.#transport.put(name, 300).
|
|
241873
|
+
const refresh = this.#transport.put(name, 300).then(() => {
|
|
241874
|
+
hold.uncertainPut = false;
|
|
241875
|
+
return "succeeded";
|
|
241876
|
+
}, (cause) => {
|
|
241877
|
+
hold.uncertainPut = true;
|
|
241878
|
+
this.#onError(`failed to refresh sprite task hold for turn ${turnId}`, cause);
|
|
241879
|
+
return "failed";
|
|
241880
|
+
});
|
|
241816
241881
|
hold.inFlight.add(refresh);
|
|
241817
241882
|
refresh.finally(() => hold.inFlight.delete(refresh));
|
|
241818
241883
|
}, SPRITE_TASK_HOLD_REFRESH_MS);
|
|
241819
241884
|
}
|
|
241820
|
-
|
|
241885
|
+
/**
|
|
241886
|
+
* Release one hold.
|
|
241887
|
+
*
|
|
241888
|
+
* Cancellation must be confirmed before a single DELETE is trusted: every
|
|
241889
|
+
* PUT on the wire has to settle successfully, or the DELETE can land mid-PUT
|
|
241890
|
+
* and the PUT re-create the hold behind it. With a join bound (shutdown),
|
|
241891
|
+
* PUTs that do not settle within the bound, or settle as rejections, make
|
|
241892
|
+
* the attempt sweep instead: DELETE, verify, re-DELETE while the server
|
|
241893
|
+
* still shows the hold, capped at two rounds. The fate outlives the PUT's
|
|
241894
|
+
* settlement: a PUT that rejected before the release began, with no
|
|
241895
|
+
* successful refresh after it, sweeps too. Without a bound (drain,
|
|
241896
|
+
* per-turn release) the join is patient and never gives up, though a PUT
|
|
241897
|
+
* that rejects still routes the release through the sweep.
|
|
241898
|
+
*
|
|
241899
|
+
* While an attempt runs, the hold stays in the map in its joining or
|
|
241900
|
+
* deleting phase, so shutdown can see it. A bounded (shutdown) caller takes
|
|
241901
|
+
* a parked hold over with its own bounded pass; a hold already in its
|
|
241902
|
+
* deleting phase is skipped, because its single deletion is already in
|
|
241903
|
+
* flight. Unbounded duplicate callers join the running attempt instead.
|
|
241904
|
+
*/
|
|
241905
|
+
async release(turnId, options = {}) {
|
|
241821
241906
|
const hold = this.#holds.get(turnId);
|
|
241822
|
-
if (!hold) return;
|
|
241907
|
+
if (!hold) return "released";
|
|
241908
|
+
if (hold.phase === "deleting") return "released";
|
|
241909
|
+
const bounded = options.joinTimeoutMs !== void 0;
|
|
241910
|
+
if (hold.releaseAttempt !== null) {
|
|
241911
|
+
if (!bounded) return hold.releaseAttempt;
|
|
241912
|
+
return this.#concludeRelease(hold, turnId, options);
|
|
241913
|
+
}
|
|
241823
241914
|
hold.released = true;
|
|
241824
241915
|
if (hold.refresher !== null) {
|
|
241825
241916
|
this.#timer.clearInterval(hold.refresher);
|
|
241826
241917
|
hold.refresher = null;
|
|
241827
241918
|
}
|
|
241828
|
-
|
|
241829
|
-
|
|
241919
|
+
hold.phase = "joining";
|
|
241920
|
+
const attempt = (async () => {
|
|
241921
|
+
const outcome = await this.#concludeRelease(hold, turnId, options);
|
|
241922
|
+
if (outcome === "unreleased") {
|
|
241923
|
+
hold.phase = "idle";
|
|
241924
|
+
this.#removeIfCurrent(turnId, hold);
|
|
241925
|
+
}
|
|
241926
|
+
return outcome;
|
|
241927
|
+
})();
|
|
241928
|
+
hold.releaseAttempt = attempt;
|
|
241830
241929
|
try {
|
|
241831
|
-
await
|
|
241930
|
+
return await attempt;
|
|
241931
|
+
} finally {
|
|
241932
|
+
if (hold.releaseAttempt === attempt) hold.releaseAttempt = null;
|
|
241933
|
+
}
|
|
241934
|
+
}
|
|
241935
|
+
/**
|
|
241936
|
+
* Drive one release attempt to its outcome. On the confirmed path (every
|
|
241937
|
+
* PUT on the wire settled within the join bound) a single DELETE finishes
|
|
241938
|
+
* the hold. On the unconfirmed path cancellation is unknown, so the hold
|
|
241939
|
+
* is swept: DELETE, verify, and re-DELETE while the server still shows it,
|
|
241940
|
+
* capped. Shared by the first attempt and a shutdown takeover: whichever
|
|
241941
|
+
* caller first reaches its verdict installs the hold's single deletion
|
|
241942
|
+
* promise and the rest join it, so the DELETE or sweep is issued once and
|
|
241943
|
+
* concurrent callers report the same outcome.
|
|
241944
|
+
*/
|
|
241945
|
+
async #concludeRelease(hold, turnId, options) {
|
|
241946
|
+
const verdict = await this.#confirmCancellation(hold, options.joinTimeoutMs);
|
|
241947
|
+
if (hold.deletion !== null) return hold.deletion;
|
|
241948
|
+
hold.phase = "deleting";
|
|
241949
|
+
const deletion = verdict === "confirmed" ? this.#deleteConfirmed(hold, turnId, options) : this.#sweepUnconfirmed(hold, turnId, options);
|
|
241950
|
+
hold.deletion = deletion;
|
|
241951
|
+
return deletion;
|
|
241952
|
+
}
|
|
241953
|
+
/**
|
|
241954
|
+
* The confirmed-path deletion: every PUT on the wire settled cleanly, so
|
|
241955
|
+
* one DELETE finishes the hold. A rejected DELETE is logged, never thrown;
|
|
241956
|
+
* the hold still leaves the map and expires on its own server-side.
|
|
241957
|
+
*/
|
|
241958
|
+
async #deleteConfirmed(hold, turnId, options) {
|
|
241959
|
+
await this.#issueDelete(hold, options.deleteTimeoutMs, turnId);
|
|
241960
|
+
this.#removeIfCurrent(turnId, hold);
|
|
241961
|
+
return "released";
|
|
241962
|
+
}
|
|
241963
|
+
/**
|
|
241964
|
+
* Best-effort cleanup for a hold whose cancellation is uncertain: the join
|
|
241965
|
+
* bound fired with a PUT unsettled, or a PUT rejected after possibly being
|
|
241966
|
+
* processed server-side. The hold is owed its delete either way. Each round
|
|
241967
|
+
* is a bounded DELETE followed by a bounded GET; a verify that still shows
|
|
241968
|
+
* the hold (the timed-out PUT committed after our delete, or the delete
|
|
241969
|
+
* never landed) triggers the next round. Verification is best-effort too:
|
|
241970
|
+
* "absent" proves cleanup, "unknown" proves nothing and cannot by itself
|
|
241971
|
+
* report the hold unreleased, so after the cap an unverifiable hold is
|
|
241972
|
+
* reported released-with-its-delete while a positively present one is
|
|
241973
|
+
* reported unreleased at warn. Either way the hold expires on its own
|
|
241974
|
+
* within 300 s. Only `#concludeRelease` starts this, after it has already
|
|
241975
|
+
* moved the hold into its deleting phase.
|
|
241976
|
+
*/
|
|
241977
|
+
async #sweepUnconfirmed(hold, turnId, options) {
|
|
241978
|
+
for (let round = 1; round <= SPRITE_TASK_HOLD_SWEEP_ROUNDS; round += 1) {
|
|
241979
|
+
await this.#issueDelete(hold, options.deleteTimeoutMs, turnId);
|
|
241980
|
+
const state = await this.#verifyHold(hold, options.deleteTimeoutMs, turnId);
|
|
241981
|
+
const last = round === SPRITE_TASK_HOLD_SWEEP_ROUNDS;
|
|
241982
|
+
if (state === "absent") {
|
|
241983
|
+
this.#removeIfCurrent(turnId, hold);
|
|
241984
|
+
return "released";
|
|
241985
|
+
}
|
|
241986
|
+
if (state === "present") {
|
|
241987
|
+
this.#onError(`sprite task hold for turn ${turnId} was still present after delete (sweep ${round}/${SPRITE_TASK_HOLD_SWEEP_ROUNDS})`, "a refresh PUT that ignored its abort appears to have committed after the delete");
|
|
241988
|
+
if (last) {
|
|
241989
|
+
this.#onError(`sprite task hold for turn ${turnId} was left unreleased: still present after ${SPRITE_TASK_HOLD_SWEEP_ROUNDS} delete+verify sweeps, expires on its own`, "hold survived the full shutdown sweep");
|
|
241990
|
+
this.#removeIfCurrent(turnId, hold);
|
|
241991
|
+
return "unreleased";
|
|
241992
|
+
}
|
|
241993
|
+
continue;
|
|
241994
|
+
}
|
|
241995
|
+
this.#onError(`sprite task hold for turn ${turnId} could not be verified after delete (sweep ${round}/${SPRITE_TASK_HOLD_SWEEP_ROUNDS})`, "hold state unverifiable");
|
|
241996
|
+
if (last) {
|
|
241997
|
+
this.#removeIfCurrent(turnId, hold);
|
|
241998
|
+
return "released";
|
|
241999
|
+
}
|
|
242000
|
+
}
|
|
242001
|
+
return "released";
|
|
242002
|
+
}
|
|
242003
|
+
/**
|
|
242004
|
+
* Ask the transport whether the hold still exists. No transport `get`, a
|
|
242005
|
+
* rejection, or a bound that fires all come back "unknown": the sweep has
|
|
242006
|
+
* no evidence, and never invents it.
|
|
242007
|
+
*/
|
|
242008
|
+
async #verifyHold(hold, timeoutMs, turnId) {
|
|
242009
|
+
const get = this.#transport.get;
|
|
242010
|
+
if (!get) return "unknown";
|
|
242011
|
+
try {
|
|
242012
|
+
return await boundedAwait(get(hold.name), timeoutMs);
|
|
242013
|
+
} catch (cause) {
|
|
242014
|
+
this.#onError(`failed to verify sprite task hold for turn ${turnId}`, cause);
|
|
242015
|
+
return "unknown";
|
|
242016
|
+
}
|
|
242017
|
+
}
|
|
242018
|
+
/**
|
|
242019
|
+
* Wait for every PUT on the wire to settle and report whether cancellation
|
|
242020
|
+
* is confirmed. Only a clean success confirms: a PUT that REJECTED is
|
|
242021
|
+
* uncertain, because the server may have processed the request before the
|
|
242022
|
+
* failure surfaced, and a DELETE trusted after it could be raced by the
|
|
242023
|
+
* hold it re-created. `inFlight` forgets each PUT once it settles, so a
|
|
242024
|
+
* rejection that settled before the release even started is read from
|
|
242025
|
+
* `uncertainPut` instead, and stays uncertain until some later PUT
|
|
242026
|
+
* succeeds. With no bound the wait is patient; unbounded callers
|
|
242027
|
+
* (drain, per-turn release) trade shutdown speed for the certainty.
|
|
242028
|
+
*/
|
|
242029
|
+
async #confirmCancellation(hold, timeoutMs) {
|
|
242030
|
+
if (hold.inFlight.size === 0) return hold.uncertainPut ? "uncertain" : "confirmed";
|
|
242031
|
+
const settled = Promise.all(hold.inFlight).then((fates) => fates.every((fate) => fate === "succeeded") && !hold.uncertainPut ? "confirmed" : "uncertain");
|
|
242032
|
+
if (timeoutMs === void 0) return settled;
|
|
242033
|
+
let clearTimer = noTimer;
|
|
242034
|
+
return Promise.race([settled, new Promise((resolve) => {
|
|
242035
|
+
const timer = setTimeout(() => resolve("uncertain"), timeoutMs);
|
|
242036
|
+
clearTimer = () => clearTimeout(timer);
|
|
242037
|
+
})]).finally(() => clearTimer());
|
|
242038
|
+
}
|
|
242039
|
+
/** Issue the DELETE, reporting (never throwing) a failure. */
|
|
242040
|
+
async #issueDelete(hold, deleteTimeoutMs, turnId) {
|
|
242041
|
+
try {
|
|
242042
|
+
await boundedAwait(this.#transport.delete(hold.name), deleteTimeoutMs);
|
|
241832
242043
|
} catch (cause) {
|
|
241833
242044
|
this.#onError(`failed to release sprite task hold for turn ${turnId}`, cause);
|
|
241834
242045
|
}
|
|
241835
242046
|
}
|
|
241836
242047
|
/**
|
|
242048
|
+
* Drop the map entry only if this attempt's hold still owns the id: a newer
|
|
242049
|
+
* acquire may have replaced it while the release was awaiting, and deleting
|
|
242050
|
+
* by key alone would evict the replacement.
|
|
242051
|
+
*/
|
|
242052
|
+
#removeIfCurrent(turnId, hold) {
|
|
242053
|
+
if (this.#holds.get(turnId) === hold) this.#holds.delete(turnId);
|
|
242054
|
+
}
|
|
242055
|
+
/**
|
|
241837
242056
|
* Release every hold belonging to one thread.
|
|
241838
242057
|
*
|
|
241839
242058
|
* `session.exited` names only the thread whose provider process died, and a
|
|
@@ -241848,9 +242067,22 @@ var SpriteTaskHoldManager = class {
|
|
|
241848
242067
|
/**
|
|
241849
242068
|
* Release everything. Runtime drain and server shutdown must call this, or a
|
|
241850
242069
|
* drained sprite keeps billing until each hold expires.
|
|
242070
|
+
*
|
|
242071
|
+
* Shutdown passes `SPRITE_TASK_HOLD_RELEASE_JOIN_TIMEOUT_MS` /
|
|
242072
|
+
* `SPRITE_TASK_HOLD_RELEASE_DELETE_TIMEOUT_MS` so a socket that never
|
|
242073
|
+
* settles cannot hang the process open past its own termination, and gets a
|
|
242074
|
+
* per-hold report back: every hold gets its DELETE, and only holds the
|
|
242075
|
+
* sweep still saw present after the full delete+verify cap come back
|
|
242076
|
+
* unreleased, left to their own 300 s expiry. Drain passes no bounds and
|
|
242077
|
+
* keeps the patient join.
|
|
241851
242078
|
*/
|
|
241852
|
-
async releaseAll() {
|
|
241853
|
-
|
|
242079
|
+
async releaseAll(options = {}) {
|
|
242080
|
+
const turnIds = [...this.#holds.keys()];
|
|
242081
|
+
const outcomes = await Promise.all(turnIds.map(async (turnId) => [turnId, await this.release(turnId, options)]));
|
|
242082
|
+
return {
|
|
242083
|
+
released: outcomes.filter(([, outcome]) => outcome === "released").map(([turnId]) => turnId),
|
|
242084
|
+
unreleased: outcomes.filter(([, outcome]) => outcome === "unreleased").map(([turnId]) => turnId)
|
|
242085
|
+
};
|
|
241854
242086
|
}
|
|
241855
242087
|
};
|
|
241856
242088
|
/**
|
|
@@ -241881,7 +242113,25 @@ function makeSpriteTaskHoldTransport(config, fetchImpl = fetch) {
|
|
|
241881
242113
|
headers: { "content-type": "application/json" },
|
|
241882
242114
|
body: JSON.stringify({ expire: `${expireSeconds}s` })
|
|
241883
242115
|
}),
|
|
241884
|
-
delete: (name) => call(`/${encodeURIComponent(name)}`, { method: "DELETE" })
|
|
242116
|
+
delete: (name) => call(`/${encodeURIComponent(name)}`, { method: "DELETE" }),
|
|
242117
|
+
get: async (name) => {
|
|
242118
|
+
const controller = new AbortController();
|
|
242119
|
+
const deadline = setTimeout(() => controller.abort(), SPRITE_TASK_HOLD_REQUEST_TIMEOUT_MS);
|
|
242120
|
+
try {
|
|
242121
|
+
const response = await fetchImpl(`http://sprite/v1/tasks/${encodeURIComponent(name)}`, {
|
|
242122
|
+
method: "GET",
|
|
242123
|
+
signal: controller.signal,
|
|
242124
|
+
unix: config.socketPath
|
|
242125
|
+
});
|
|
242126
|
+
if (response.ok) return "present";
|
|
242127
|
+
if (response.status === 404) return "absent";
|
|
242128
|
+
return "unknown";
|
|
242129
|
+
} catch {
|
|
242130
|
+
return "unknown";
|
|
242131
|
+
} finally {
|
|
242132
|
+
clearTimeout(deadline);
|
|
242133
|
+
}
|
|
242134
|
+
}
|
|
241885
242135
|
};
|
|
241886
242136
|
}
|
|
241887
242137
|
/**
|
|
@@ -286476,10 +286726,10 @@ const AgentBrowserManagerLive = require_Schema$1.effect(BrowserManager, require_
|
|
|
286476
286726
|
mediaSourceFactory: displayCaptureRuntime.mediaSourceFactory,
|
|
286477
286727
|
taskHold: displayTaskHoldManager ? {
|
|
286478
286728
|
acquire: (holdId) => displayTaskHoldManager.acquire(holdId),
|
|
286479
|
-
release: (holdId) => displayTaskHoldManager.release(holdId)
|
|
286729
|
+
release: (holdId) => displayTaskHoldManager.release(holdId).then(() => {})
|
|
286480
286730
|
} : void 0
|
|
286481
286731
|
});
|
|
286482
|
-
yield* require_Schema$1.addFinalizer(() => require_Schema$1.promise(() => displayTaskHoldManager?.releaseAll() ?? Promise.resolve()));
|
|
286732
|
+
yield* require_Schema$1.addFinalizer(() => require_Schema$1.promise(() => displayTaskHoldManager?.releaseAll().then(() => {}) ?? Promise.resolve()));
|
|
286483
286733
|
const baseBackendOptions = displayCaptureRuntime.backendOptions;
|
|
286484
286734
|
const sessionInitialUrls = /* @__PURE__ */ new Map();
|
|
286485
286735
|
let runtimeBuild = null;
|
|
@@ -294456,6 +294706,19 @@ const make$6 = require_Schema$1.gen(function* () {
|
|
|
294456
294706
|
const manager = makeSpriteTaskHoldManager(process.env, (message, cause) => {
|
|
294457
294707
|
require_Schema$1.runFork(require_Schema$1.logWarning(message, { cause: cause instanceof Error ? cause.message : cause }));
|
|
294458
294708
|
});
|
|
294709
|
+
/**
|
|
294710
|
+
* Run a manager call, or nothing on runtimes that are not sprites. One
|
|
294711
|
+
* null-guard for every caller: the releaseAll service and the shutdown
|
|
294712
|
+
* finalizer alike.
|
|
294713
|
+
*/
|
|
294714
|
+
const onManager = (run) => require_Schema$1.promise(async () => {
|
|
294715
|
+
if (manager === null) return;
|
|
294716
|
+
await run(manager);
|
|
294717
|
+
});
|
|
294718
|
+
yield* require_Schema$1.addFinalizer(() => onManager((taskHolds) => taskHolds.releaseAll({
|
|
294719
|
+
joinTimeoutMs: SPRITE_TASK_HOLD_RELEASE_JOIN_TIMEOUT_MS,
|
|
294720
|
+
deleteTimeoutMs: SPRITE_TASK_HOLD_RELEASE_DELETE_TIMEOUT_MS
|
|
294721
|
+
})));
|
|
294459
294722
|
const launchCallers = /* @__PURE__ */ new Map();
|
|
294460
294723
|
const start = require_Schema$1.fn("start")(function* () {
|
|
294461
294724
|
if (manager === null) return;
|
|
@@ -294472,12 +294735,12 @@ const make$6 = require_Schema$1.gen(function* () {
|
|
|
294472
294735
|
if (event.type === "turn.completed") {
|
|
294473
294736
|
const turnId = event.turnId;
|
|
294474
294737
|
if (turnId === void 0) return require_Schema$1.void_;
|
|
294475
|
-
return require_Schema$1.promise(() => manager.release(String(turnId)));
|
|
294738
|
+
return require_Schema$1.promise(() => manager.release(String(turnId)).then(() => {}));
|
|
294476
294739
|
}
|
|
294477
294740
|
if (event.type === "turn.aborted") {
|
|
294478
294741
|
const turnId = event.turnId;
|
|
294479
294742
|
if (turnId === void 0) return require_Schema$1.void_;
|
|
294480
|
-
return require_Schema$1.promise(() => manager.release(String(turnId)));
|
|
294743
|
+
return require_Schema$1.promise(() => manager.release(String(turnId)).then(() => {}));
|
|
294481
294744
|
}
|
|
294482
294745
|
if (event.type === "session.exited") {
|
|
294483
294746
|
launchCallers.delete(String(event.threadId));
|
|
@@ -294504,7 +294767,7 @@ const make$6 = require_Schema$1.gen(function* () {
|
|
|
294504
294767
|
start,
|
|
294505
294768
|
holdLaunch,
|
|
294506
294769
|
releaseLaunch,
|
|
294507
|
-
releaseAll:
|
|
294770
|
+
releaseAll: onManager((taskHolds) => taskHolds.releaseAll())
|
|
294508
294771
|
};
|
|
294509
294772
|
});
|
|
294510
294773
|
const SpriteTaskHoldReactorLive = require_Schema$1.effect(SpriteTaskHoldReactor, make$6);
|
|
@@ -298649,7 +298912,7 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
298649
298912
|
* environment descriptor down instead of reporting an honest "unknown".
|
|
298650
298913
|
*/
|
|
298651
298914
|
function readBakedBuildCommit() {
|
|
298652
|
-
return "
|
|
298915
|
+
return "d1c5c1c87c650d1d49b596cbbe4bcc3ff14c7bfe";
|
|
298653
298916
|
}
|
|
298654
298917
|
async function resolveServerBuildCommit(input) {
|
|
298655
298918
|
if (isFullCommitSha(input.baked)) return input.baked;
|