@tapflowio/relay 0.28.0 → 0.29.0
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/RelayServer.d.ts +75 -0
- package/dist/RelayServer.d.ts.map +1 -1
- package/dist/RelayServer.js +496 -49
- package/dist/RelayServer.js.map +1 -1
- package/dist/SessionManager.d.ts +41 -2
- package/dist/SessionManager.d.ts.map +1 -1
- package/dist/SessionManager.js +31 -1
- package/dist/SessionManager.js.map +1 -1
- package/dist/admission/HostBootGate.d.ts +181 -0
- package/dist/admission/HostBootGate.d.ts.map +1 -0
- package/dist/admission/HostBootGate.js +388 -0
- package/dist/admission/HostBootGate.js.map +1 -0
- package/dist/admission/capacity.d.ts +51 -0
- package/dist/admission/capacity.d.ts.map +1 -0
- package/dist/admission/capacity.js +49 -0
- package/dist/admission/capacity.js.map +1 -0
- package/dist/admission/classifyBoot.d.ts +17 -0
- package/dist/admission/classifyBoot.d.ts.map +1 -0
- package/dist/admission/classifyBoot.js +23 -0
- package/dist/admission/classifyBoot.js.map +1 -0
- package/dist/admission/hostKey.d.ts +10 -0
- package/dist/admission/hostKey.d.ts.map +1 -0
- package/dist/admission/hostKey.js +13 -0
- package/dist/admission/hostKey.js.map +1 -0
- package/dist/admission/leaseChoice.d.ts +38 -0
- package/dist/admission/leaseChoice.d.ts.map +1 -0
- package/dist/admission/leaseChoice.js +40 -0
- package/dist/admission/leaseChoice.js.map +1 -0
- package/dist/admission/settle.d.ts +35 -0
- package/dist/admission/settle.d.ts.map +1 -0
- package/dist/admission/settle.js +41 -0
- package/dist/admission/settle.js.map +1 -0
- package/dist/api/builds.d.ts.map +1 -1
- package/dist/api/builds.js +3 -2
- package/dist/api/builds.js.map +1 -1
- package/dist/api/profile.d.ts.map +1 -1
- package/dist/api/profile.js +3 -1
- package/dist/api/profile.js.map +1 -1
- package/dist/api/runs.d.ts +21 -2
- package/dist/api/runs.d.ts.map +1 -1
- package/dist/api/runs.js +181 -26
- package/dist/api/runs.js.map +1 -1
- package/dist/api/settings.d.ts.map +1 -1
- package/dist/api/settings.js +4 -2
- package/dist/api/settings.js.map +1 -1
- package/dist/migrations/017_flow_run_skipped.sql +34 -0
- package/dist/migrations/018_flow_run_service_tier.sql +6 -0
- package/dist/migrations/019_flow_run_device_reset.sql +7 -0
- package/package.json +4 -4
- package/public/assets/{AISessionWatch-Bxew762e.js → AISessionWatch-BSBkpTom.js} +13 -18
- package/public/assets/AISessionWatch-BSBkpTom.js.br +0 -0
- package/public/assets/AISessionWatch-BSBkpTom.js.gz +0 -0
- package/public/assets/{AppCenter-93hnrrdy.js → AppCenter-DD_6LQx-.js} +6 -6
- package/public/assets/AppCenter-DD_6LQx-.js.br +0 -0
- package/public/assets/AppCenter-DD_6LQx-.js.gz +0 -0
- package/public/assets/Default-CQ3Ae2mK.js +6 -0
- package/public/assets/Default-CQ3Ae2mK.js.br +0 -0
- package/public/assets/Default-CQ3Ae2mK.js.gz +0 -0
- package/public/assets/{Invite-DQBwLaOl.js → Invite-D1uNMm0-.js} +1 -1
- package/public/assets/Invite-D1uNMm0-.js.br +0 -0
- package/public/assets/Invite-D1uNMm0-.js.gz +0 -0
- package/public/assets/{MacResources-D4LMGxgV.js → MacResources-D2UeRbKL.js} +1 -1
- package/public/assets/MacResources-D2UeRbKL.js.br +0 -0
- package/public/assets/MacResources-D2UeRbKL.js.gz +0 -0
- package/public/assets/QASession-d0eOjXXo.js +81 -0
- package/public/assets/QASession-d0eOjXXo.js.br +0 -0
- package/public/assets/QASession-d0eOjXXo.js.gz +0 -0
- package/public/assets/{ResetPassword-inTbbral.js → ResetPassword-B-4Uyfwr.js} +1 -1
- package/public/assets/ResetPassword-B-4Uyfwr.js.br +0 -0
- package/public/assets/ResetPassword-B-4Uyfwr.js.gz +0 -0
- package/public/assets/RunDetail-C50RjsuM.js +16 -0
- package/public/assets/RunDetail-C50RjsuM.js.br +0 -0
- package/public/assets/RunDetail-C50RjsuM.js.gz +0 -0
- package/public/assets/Runs-A7RcDLgO.js +6 -0
- package/public/assets/Runs-A7RcDLgO.js.br +0 -0
- package/public/assets/Runs-A7RcDLgO.js.gz +0 -0
- package/public/assets/{Setup-CPa_UWpa.js → Setup-DK6TZbwh.js} +1 -1
- package/public/assets/Setup-DK6TZbwh.js.br +0 -0
- package/public/assets/Setup-DK6TZbwh.js.gz +0 -0
- package/public/assets/{Team-DZie8W7R.js → Team-VoGput2p.js} +3 -3
- package/public/assets/Team-VoGput2p.js.br +0 -0
- package/public/assets/Team-VoGput2p.js.gz +0 -0
- package/public/assets/{Tokens-u3-uRAwd.js → Tokens-qkcJgunR.js} +1 -1
- package/public/assets/Tokens-qkcJgunR.js.br +0 -0
- package/public/assets/Tokens-qkcJgunR.js.gz +0 -0
- package/public/assets/{aiSession-BsmDnyV1.js → aiSession-U7UqgdSb.js} +11 -16
- package/public/assets/aiSession-U7UqgdSb.js.br +0 -0
- package/public/assets/aiSession-U7UqgdSb.js.gz +0 -0
- package/public/assets/{alert-dialog-Bq_Ln5bp.js → alert-dialog-C0mRms8g.js} +1 -1
- package/public/assets/alert-dialog-C0mRms8g.js.br +0 -0
- package/public/assets/alert-dialog-C0mRms8g.js.gz +0 -0
- package/public/assets/{badge-D-bt97WR.js → badge-BjEnOmb0.js} +1 -1
- package/public/assets/badge-BjEnOmb0.js.br +0 -0
- package/public/assets/badge-BjEnOmb0.js.gz +0 -0
- package/public/assets/{dialog-CtzPkg0O.js → dialog-Cugbp1C4.js} +1 -1
- package/public/assets/dialog-Cugbp1C4.js.br +0 -0
- package/public/assets/dialog-Cugbp1C4.js.gz +0 -0
- package/public/assets/{field-hint-B774BIWK.js → field-hint-qmBa0qqN.js} +1 -1
- package/public/assets/field-hint-qmBa0qqN.js.br +0 -0
- package/public/assets/field-hint-qmBa0qqN.js.gz +0 -0
- package/public/assets/index-C_3Q51YJ.js +248 -0
- package/public/assets/index-C_3Q51YJ.js.br +0 -0
- package/public/assets/index-C_3Q51YJ.js.gz +0 -0
- package/public/assets/{index-EPyP3g9A.css → index-h2hiZ2OM.css} +1 -1
- package/public/assets/index-h2hiZ2OM.css.br +0 -0
- package/public/assets/index-h2hiZ2OM.css.gz +0 -0
- package/public/assets/list-view-CyM0QUaH.js +1 -0
- package/public/assets/list-view-CyM0QUaH.js.br +0 -0
- package/public/assets/list-view-CyM0QUaH.js.gz +0 -0
- package/public/assets/{pencil-1XSs32nb.js → pencil-DUZRZB3S.js} +2 -2
- package/public/assets/pencil-DUZRZB3S.js.br +0 -0
- package/public/assets/pencil-DUZRZB3S.js.gz +0 -0
- package/public/assets/{play-DCvrorIC.js → play-Z1L2Eb91.js} +2 -2
- package/public/assets/play-Z1L2Eb91.js.br +0 -0
- package/public/assets/play-Z1L2Eb91.js.gz +0 -0
- package/public/assets/{runs-CInQb6v1.js → runs-CFQ6M4rx.js} +1 -1
- package/public/assets/runs-CFQ6M4rx.js.br +0 -0
- package/public/assets/runs-CFQ6M4rx.js.gz +0 -0
- package/public/assets/{search-input-CFWgLcKA.js → search-input-B0s9mzRt.js} +2 -2
- package/public/assets/search-input-B0s9mzRt.js.br +0 -0
- package/public/assets/search-input-B0s9mzRt.js.gz +0 -0
- package/public/assets/{select-5ZtzWRYG.js → select-CHbE6M4S.js} +3 -3
- package/public/assets/select-CHbE6M4S.js.br +0 -0
- package/public/assets/select-CHbE6M4S.js.gz +0 -0
- package/public/assets/{slider-DHuMDXcP.js → slider-tyb3JvL2.js} +1 -1
- package/public/assets/slider-tyb3JvL2.js.br +0 -0
- package/public/assets/slider-tyb3JvL2.js.gz +0 -0
- package/public/assets/{tabs-CmgV_sSI.js → tabs-Bf7ou-eS.js} +1 -1
- package/public/assets/tabs-Bf7ou-eS.js.br +0 -0
- package/public/assets/tabs-Bf7ou-eS.js.gz +0 -0
- package/public/assets/{tech-label-qLodPeUI.js → tech-label-KAwM5qZ_.js} +1 -1
- package/public/assets/tech-label-KAwM5qZ_.js.br +0 -0
- package/public/assets/tech-label-KAwM5qZ_.js.gz +0 -0
- package/public/assets/{trash-CXjB9oU1.js → trash-HEJmY7DQ.js} +3 -3
- package/public/assets/trash-HEJmY7DQ.js.br +0 -0
- package/public/assets/trash-HEJmY7DQ.js.gz +0 -0
- package/public/assets/{video-CK2q3-0B.js → video-DdHH0Q0l.js} +2 -2
- package/public/assets/video-DdHH0Q0l.js.br +0 -0
- package/public/assets/video-DdHH0Q0l.js.gz +0 -0
- package/public/index.html +14 -2
- package/public/index.html.br +0 -0
- package/public/index.html.gz +0 -0
- package/public/og-image.jpg +0 -0
- package/public/assets/AISessionWatch-Bxew762e.js.br +0 -0
- package/public/assets/AISessionWatch-Bxew762e.js.gz +0 -0
- package/public/assets/AppCenter-93hnrrdy.js.br +0 -0
- package/public/assets/AppCenter-93hnrrdy.js.gz +0 -0
- package/public/assets/Default-BhgcUKBm.js +0 -6
- package/public/assets/Default-BhgcUKBm.js.br +0 -0
- package/public/assets/Default-BhgcUKBm.js.gz +0 -0
- package/public/assets/Invite-DQBwLaOl.js.br +0 -0
- package/public/assets/Invite-DQBwLaOl.js.gz +0 -0
- package/public/assets/MacResources-D4LMGxgV.js.br +0 -0
- package/public/assets/MacResources-D4LMGxgV.js.gz +0 -0
- package/public/assets/QASession-Dnuq0yrN.js +0 -81
- package/public/assets/QASession-Dnuq0yrN.js.br +0 -0
- package/public/assets/QASession-Dnuq0yrN.js.gz +0 -0
- package/public/assets/ResetPassword-inTbbral.js.br +0 -0
- package/public/assets/ResetPassword-inTbbral.js.gz +0 -0
- package/public/assets/RunDetail-B3dw8_U0.js +0 -16
- package/public/assets/RunDetail-B3dw8_U0.js.br +0 -0
- package/public/assets/RunDetail-B3dw8_U0.js.gz +0 -0
- package/public/assets/Runs-DqZl9Y6M.js +0 -6
- package/public/assets/Runs-DqZl9Y6M.js.br +0 -0
- package/public/assets/Runs-DqZl9Y6M.js.gz +0 -0
- package/public/assets/Setup-CPa_UWpa.js.br +0 -0
- package/public/assets/Setup-CPa_UWpa.js.gz +0 -0
- package/public/assets/Team-DZie8W7R.js.br +0 -0
- package/public/assets/Team-DZie8W7R.js.gz +0 -0
- package/public/assets/Tokens-u3-uRAwd.js.br +0 -0
- package/public/assets/Tokens-u3-uRAwd.js.gz +0 -0
- package/public/assets/aiSession-BsmDnyV1.js.br +0 -0
- package/public/assets/aiSession-BsmDnyV1.js.gz +0 -0
- package/public/assets/alert-dialog-Bq_Ln5bp.js.br +0 -0
- package/public/assets/alert-dialog-Bq_Ln5bp.js.gz +0 -0
- package/public/assets/badge-D-bt97WR.js.br +0 -0
- package/public/assets/badge-D-bt97WR.js.gz +0 -0
- package/public/assets/dialog-CtzPkg0O.js.br +0 -0
- package/public/assets/dialog-CtzPkg0O.js.gz +0 -0
- package/public/assets/field-hint-B774BIWK.js.br +0 -1
- package/public/assets/field-hint-B774BIWK.js.gz +0 -0
- package/public/assets/index-EPyP3g9A.css.br +0 -0
- package/public/assets/index-EPyP3g9A.css.gz +0 -0
- package/public/assets/index-cgl1yEKu.js +0 -223
- package/public/assets/index-cgl1yEKu.js.br +0 -0
- package/public/assets/index-cgl1yEKu.js.gz +0 -0
- package/public/assets/list-view-BcjDCJy5.js +0 -1
- package/public/assets/list-view-BcjDCJy5.js.br +0 -0
- package/public/assets/list-view-BcjDCJy5.js.gz +0 -0
- package/public/assets/pencil-1XSs32nb.js.br +0 -0
- package/public/assets/pencil-1XSs32nb.js.gz +0 -0
- package/public/assets/play-DCvrorIC.js.br +0 -0
- package/public/assets/play-DCvrorIC.js.gz +0 -0
- package/public/assets/runs-CInQb6v1.js.br +0 -0
- package/public/assets/runs-CInQb6v1.js.gz +0 -0
- package/public/assets/search-input-CFWgLcKA.js.br +0 -0
- package/public/assets/search-input-CFWgLcKA.js.gz +0 -0
- package/public/assets/select-5ZtzWRYG.js.br +0 -0
- package/public/assets/select-5ZtzWRYG.js.gz +0 -0
- package/public/assets/slider-DHuMDXcP.js.br +0 -0
- package/public/assets/slider-DHuMDXcP.js.gz +0 -0
- package/public/assets/tabs-CmgV_sSI.js.br +0 -0
- package/public/assets/tabs-CmgV_sSI.js.gz +0 -0
- package/public/assets/tech-label-qLodPeUI.js.br +0 -0
- package/public/assets/tech-label-qLodPeUI.js.gz +0 -0
- package/public/assets/trash-CXjB9oU1.js.br +0 -0
- package/public/assets/trash-CXjB9oU1.js.gz +0 -0
- package/public/assets/video-CK2q3-0B.js.br +0 -0
- package/public/assets/video-CK2q3-0B.js.gz +0 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { isSettled, SETTLE } from './settle.js';
|
|
2
|
+
/** Numbers and their sources: L2 admission plan, appendix 숫자 결정. */
|
|
3
|
+
export const GATE_DEFAULTS = {
|
|
4
|
+
bootMs: 180_000,
|
|
5
|
+
settleCapMs: 120_000,
|
|
6
|
+
noSampleMs: 90_000,
|
|
7
|
+
humanWaitMs: 60_000,
|
|
8
|
+
reboundMs: 30_000,
|
|
9
|
+
agingCount: 3,
|
|
10
|
+
agingWindowMs: 45_000,
|
|
11
|
+
recentRefusalMs: 60_000,
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Two agents on one Mac each report every 5 s, and each report is the host's CPU averaged since that agent's
|
|
15
|
+
* previous one. Taking both would make `SETTLE.samples` mean half the time it was measured at, so a report
|
|
16
|
+
* closer than this to the last one taken is skipped.
|
|
17
|
+
*/
|
|
18
|
+
const MIN_SAMPLE_GAP_MS = 4_000;
|
|
19
|
+
export class HostBootGate {
|
|
20
|
+
now;
|
|
21
|
+
timing;
|
|
22
|
+
hosts = new Map();
|
|
23
|
+
constructor(now, timing = GATE_DEFAULTS) {
|
|
24
|
+
this.now = now;
|
|
25
|
+
this.timing = timing;
|
|
26
|
+
}
|
|
27
|
+
admit(req) {
|
|
28
|
+
const fx = [];
|
|
29
|
+
const host = this.host(req.hostKey);
|
|
30
|
+
const now = this.now();
|
|
31
|
+
if (host.reservation?.sessionId === req.sessionId) {
|
|
32
|
+
// The same person asked again (a reconnect re-sends its boot). Keep their place and their clock.
|
|
33
|
+
fx.push({ kind: 'withdraw', sessionId: req.sessionId, requestId: host.reservation.requestId, why: 'replaced', message: host.reservation.message });
|
|
34
|
+
if (req.cost === 'warm') {
|
|
35
|
+
host.reservation = null;
|
|
36
|
+
fx.push(this.deliverOf(req));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
host.reservation = { ...host.reservation, requestId: req.requestId, message: req.message, reason: this.waitReason(host) };
|
|
40
|
+
fx.push(this.waitingOf(host.reservation));
|
|
41
|
+
}
|
|
42
|
+
return fx;
|
|
43
|
+
}
|
|
44
|
+
if (host.slot?.sessionId === req.sessionId) {
|
|
45
|
+
// A re-boot of the device already holding the slot: the agent supersedes its own boot, and the Mac is
|
|
46
|
+
// paying for one boot, not two. The slot follows the newest correlator so only its failure releases it.
|
|
47
|
+
Object.assign(host.slot, { requestId: req.requestId, phase: 'booting', since: now, readyAt: null, samples: [], droppedFirst: false, reboundAt: null });
|
|
48
|
+
fx.push(this.deliverOf(req));
|
|
49
|
+
return fx;
|
|
50
|
+
}
|
|
51
|
+
if (req.cost === 'warm') {
|
|
52
|
+
fx.push(this.deliverOf(req));
|
|
53
|
+
return fx;
|
|
54
|
+
}
|
|
55
|
+
// Below the same-session and warm branches on purpose: a re-boot of the slot's own device and a device already up
|
|
56
|
+
// add no boot to the Mac, and refusing the first would orphan the slot until its boot timeout.
|
|
57
|
+
const critical = this.isCritical(host);
|
|
58
|
+
const windowOpen = host.agingWindowUntil !== null;
|
|
59
|
+
if (req.booterClass === 'automated') {
|
|
60
|
+
if (critical) {
|
|
61
|
+
host.lastAutomatedRefusalAt = now;
|
|
62
|
+
fx.push({ kind: 'refuse', sessionId: req.sessionId, requestId: req.requestId, reason: 'host-memory-critical', message: req.message });
|
|
63
|
+
}
|
|
64
|
+
else if (host.slot === null && (windowOpen || host.reservation === null)) {
|
|
65
|
+
this.takeSlot(host, req);
|
|
66
|
+
host.humanStreak = 0;
|
|
67
|
+
host.agingWindowUntil = null;
|
|
68
|
+
fx.push(this.deliverOf(req));
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
host.lastAutomatedRefusalAt = now;
|
|
72
|
+
fx.push({ kind: 'refuse', sessionId: req.sessionId, requestId: req.requestId, reason: 'host-booting', message: req.message });
|
|
73
|
+
}
|
|
74
|
+
return fx;
|
|
75
|
+
}
|
|
76
|
+
if (host.slot === null && !windowOpen && !critical) {
|
|
77
|
+
this.takeSlot(host, req);
|
|
78
|
+
this.noteHumanGrant(host);
|
|
79
|
+
fx.push(this.deliverOf(req));
|
|
80
|
+
}
|
|
81
|
+
else if (host.reservation === null) {
|
|
82
|
+
host.reservation = { sessionId: req.sessionId, requestId: req.requestId, message: req.message, since: now, reason: this.waitReason(host) };
|
|
83
|
+
fx.push(this.waitingOf(host.reservation));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// The one place is taken. A second person at once is rare enough to let through rather than queue.
|
|
87
|
+
fx.push({ kind: 'log', event: 'overlap', hostKey: req.hostKey, sessionId: req.sessionId });
|
|
88
|
+
const held = host.reservation;
|
|
89
|
+
if (host.slot === null && critical) {
|
|
90
|
+
// The free slot goes to whoever asked first: the held person goes now, and this one takes their place.
|
|
91
|
+
host.reservation = { sessionId: req.sessionId, requestId: req.requestId, message: req.message, since: now, reason: this.waitReason(host) };
|
|
92
|
+
this.deliverAnyway(host, held, fx);
|
|
93
|
+
fx.push(this.waitingOf(host.reservation));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.deliverAnyway(host, req, fx);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return fx;
|
|
100
|
+
}
|
|
101
|
+
onEvent(ev) {
|
|
102
|
+
const fx = [];
|
|
103
|
+
switch (ev.kind) {
|
|
104
|
+
case 'ready': {
|
|
105
|
+
const found = this.slotOf(ev.sessionId);
|
|
106
|
+
if (found) {
|
|
107
|
+
if (ev.cold === false) {
|
|
108
|
+
// Charged as cold, and the agent found the device running: nothing to wait for.
|
|
109
|
+
this.release(found.key, found.host, fx);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
Object.assign(found.host.slot, { phase: 'settling', readyAt: this.now(), samples: [], droppedFirst: false, lastSampleAt: null });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else if (ev.cold === true) {
|
|
116
|
+
// Let through as warm, and the agent had to bring it up after all (stopped between our view and its).
|
|
117
|
+
const host = this.host(ev.hostKey);
|
|
118
|
+
if (host.slot === null) {
|
|
119
|
+
const now = this.now();
|
|
120
|
+
host.slot = { sessionId: ev.sessionId, requestId: '', phase: 'settling', since: now, readyAt: now, samples: [], droppedFirst: false, lastSampleAt: null, reboundAt: null };
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
fx.push({ kind: 'log', event: 'overlap', hostKey: ev.hostKey, sessionId: ev.sessionId });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
case 'sample': {
|
|
129
|
+
const slot = this.hosts.get(ev.hostKey)?.slot;
|
|
130
|
+
if (!slot || slot.phase !== 'settling')
|
|
131
|
+
break;
|
|
132
|
+
const now = this.now();
|
|
133
|
+
if (slot.lastSampleAt !== null && now - slot.lastSampleAt < MIN_SAMPLE_GAP_MS)
|
|
134
|
+
break;
|
|
135
|
+
if (slot.lastSampleAt !== null && now - slot.lastSampleAt > SETTLE.freshMs)
|
|
136
|
+
slot.samples = [];
|
|
137
|
+
slot.lastSampleAt = now;
|
|
138
|
+
if (!slot.droppedFirst) {
|
|
139
|
+
slot.droppedFirst = true;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
slot.samples.push(ev.cpuPercent);
|
|
143
|
+
if (isSettled(slot.samples))
|
|
144
|
+
this.release(ev.hostKey, this.host(ev.hostKey), fx);
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case 'pressure': {
|
|
148
|
+
const host = this.host(ev.hostKey);
|
|
149
|
+
const now = this.now();
|
|
150
|
+
if (ev.critical) {
|
|
151
|
+
const was = this.isCritical(host);
|
|
152
|
+
host.criticalUntil = now + SETTLE.freshMs;
|
|
153
|
+
if (!was) {
|
|
154
|
+
fx.push({ kind: 'log', event: 'memory-critical', hostKey: ev.hostKey });
|
|
155
|
+
if (host.agingWindowUntil !== null)
|
|
156
|
+
host.windowLeftMs = Math.max(0, host.agingWindowUntil - now);
|
|
157
|
+
this.refreshWaiting(host, fx);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else if (host.criticalUntil !== null) {
|
|
161
|
+
host.criticalUntil = null;
|
|
162
|
+
this.criticalEnded(host, fx);
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
case 'boot-error': {
|
|
167
|
+
const found = this.slotOf(ev.sessionId);
|
|
168
|
+
if (found && (ev.requestId === undefined || ev.requestId === found.host.slot.requestId)) {
|
|
169
|
+
this.release(found.key, found.host, fx);
|
|
170
|
+
}
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
case 'shutdown-done':
|
|
174
|
+
case 'session-removed': {
|
|
175
|
+
this.withdrawHeld(ev.sessionId, fx);
|
|
176
|
+
const found = this.slotOf(ev.sessionId);
|
|
177
|
+
if (found)
|
|
178
|
+
this.release(found.key, found.host, fx);
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
case 'browser-closed':
|
|
182
|
+
this.withdrawHeld(ev.sessionId, fx);
|
|
183
|
+
break;
|
|
184
|
+
case 'rebound': {
|
|
185
|
+
const found = this.slotOf(ev.sessionId);
|
|
186
|
+
if (found)
|
|
187
|
+
found.host.slot.reboundAt = this.now();
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
default: {
|
|
191
|
+
const never = ev;
|
|
192
|
+
throw new Error(`unhandled gate event ${JSON.stringify(never)}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return fx;
|
|
196
|
+
}
|
|
197
|
+
/** Act on every deadline that has passed. */
|
|
198
|
+
tick() {
|
|
199
|
+
const fx = [];
|
|
200
|
+
const now = this.now();
|
|
201
|
+
const t = this.timing;
|
|
202
|
+
for (const [key, host] of this.hosts) {
|
|
203
|
+
// First, so a reservation the expiry frees goes out in this same tick.
|
|
204
|
+
if (host.criticalUntil !== null && now >= host.criticalUntil) {
|
|
205
|
+
host.criticalUntil = null;
|
|
206
|
+
fx.push({ kind: 'log', event: 'memory-critical-stale', hostKey: key });
|
|
207
|
+
this.criticalEnded(host, fx);
|
|
208
|
+
}
|
|
209
|
+
const slot = host.slot;
|
|
210
|
+
if (slot) {
|
|
211
|
+
let expired = null;
|
|
212
|
+
// Only while booting: once the device is up its slot runs on the settle clocks, and the automated clients
|
|
213
|
+
// re-boot after a rebind only when their next step needs it, so a settling device would be released early.
|
|
214
|
+
if (slot.phase === 'booting' && slot.reboundAt !== null && now >= slot.reboundAt + t.reboundMs)
|
|
215
|
+
expired = 'rebound-abandoned';
|
|
216
|
+
else if (slot.phase === 'booting' && now >= slot.since + t.bootMs)
|
|
217
|
+
expired = 'boot-timeout';
|
|
218
|
+
else if (slot.phase === 'settling' && slot.readyAt !== null) {
|
|
219
|
+
if (now >= slot.readyAt + t.settleCapMs)
|
|
220
|
+
expired = 'settle-timeout';
|
|
221
|
+
else if (slot.lastSampleAt === null && now >= slot.readyAt + t.noSampleMs)
|
|
222
|
+
expired = 'no-samples';
|
|
223
|
+
}
|
|
224
|
+
if (expired) {
|
|
225
|
+
fx.push({ kind: 'log', event: expired, hostKey: key, sessionId: slot.sessionId });
|
|
226
|
+
this.release(key, host, fx);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// Not while critical: automation is refused then, so the window would run out with nobody able to use it.
|
|
230
|
+
// `criticalEnded` starts it again.
|
|
231
|
+
if (host.agingWindowUntil !== null && now >= host.agingWindowUntil && !this.isCritical(host)) {
|
|
232
|
+
host.agingWindowUntil = null;
|
|
233
|
+
host.humanStreak = 0;
|
|
234
|
+
this.dispatchReservation(host, fx);
|
|
235
|
+
}
|
|
236
|
+
const held = host.reservation;
|
|
237
|
+
if (held && now >= held.since + t.humanWaitMs) {
|
|
238
|
+
host.reservation = null;
|
|
239
|
+
fx.push({ kind: 'log', event: 'human-wait-expired', hostKey: key, sessionId: held.sessionId });
|
|
240
|
+
this.deliverAnyway(host, held, fx);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return fx;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* What `admit` would answer an automated cold boot on this Mac now — `null` for go — without recording anything.
|
|
247
|
+
* A lease tries Macs in turn, and an `admit` per candidate would stamp a refusal on each Mac it passed over: that
|
|
248
|
+
* stamp is what opens an aging window for automation, so people on those Macs would be made to wait for a boot
|
|
249
|
+
* that went elsewhere. Mirrors the automated branch of `admit` and must move with it.
|
|
250
|
+
*/
|
|
251
|
+
wouldRefuseAutomated(hostKey) {
|
|
252
|
+
const host = this.hosts.get(hostKey);
|
|
253
|
+
if (!host)
|
|
254
|
+
return null;
|
|
255
|
+
if (this.isCritical(host))
|
|
256
|
+
return 'host-memory-critical';
|
|
257
|
+
return host.slot === null && (host.agingWindowUntil !== null || host.reservation === null) ? null : 'host-booting';
|
|
258
|
+
}
|
|
259
|
+
/** Whether a cold boot is being paid for on this Mac now. The relay pauses its CPU join gate while it is. */
|
|
260
|
+
isBooting(hostKey) {
|
|
261
|
+
return this.hosts.get(hostKey)?.slot != null;
|
|
262
|
+
}
|
|
263
|
+
release(key, host, fx) {
|
|
264
|
+
host.slot = null;
|
|
265
|
+
const now = this.now();
|
|
266
|
+
const automationWaiting = host.lastAutomatedRefusalAt !== null && now - host.lastAutomatedRefusalAt <= this.timing.recentRefusalMs;
|
|
267
|
+
if (automationWaiting && host.humanStreak >= this.timing.agingCount) {
|
|
268
|
+
host.agingWindowUntil = now + this.timing.agingWindowMs;
|
|
269
|
+
fx.push({ kind: 'log', event: 'aging-window', hostKey: key });
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
this.dispatchReservation(host, fx);
|
|
273
|
+
}
|
|
274
|
+
criticalEnded(host, fx) {
|
|
275
|
+
// A window that opened during critical gets its whole length; one that was running gets what it had left.
|
|
276
|
+
if (host.agingWindowUntil !== null)
|
|
277
|
+
host.agingWindowUntil = this.now() + (host.windowLeftMs ?? this.timing.agingWindowMs);
|
|
278
|
+
host.windowLeftMs = null;
|
|
279
|
+
this.dispatchReservation(host, fx);
|
|
280
|
+
this.refreshWaiting(host, fx);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* A boot let through regardless — a second person while the reservation is taken, or a person whose wait ran
|
|
284
|
+
* out. With the slot free it takes the slot, so the boot is tracked to its settle like any other: free here means
|
|
285
|
+
* the Mac was critical, and letting it through slotless would let the next automated boot start beside it the
|
|
286
|
+
* moment pressure cleared. An open aging window keeps it slotless only while automation can use that slot; under
|
|
287
|
+
* critical pressure it cannot, and a window frozen by critical can outlast a person's wait.
|
|
288
|
+
*/
|
|
289
|
+
deliverAnyway(host, req, fx) {
|
|
290
|
+
if (host.slot === null && (host.agingWindowUntil === null || this.isCritical(host))) {
|
|
291
|
+
this.takeSlot(host, req);
|
|
292
|
+
this.noteHumanGrant(host);
|
|
293
|
+
}
|
|
294
|
+
fx.push({ kind: 'deliver', sessionId: req.sessionId, requestId: req.requestId, message: req.message });
|
|
295
|
+
}
|
|
296
|
+
isCritical(host) {
|
|
297
|
+
return host.criticalUntil !== null && this.now() < host.criticalUntil;
|
|
298
|
+
}
|
|
299
|
+
waitReason(host) {
|
|
300
|
+
return this.isCritical(host) ? 'host-memory-critical' : 'host-booting';
|
|
301
|
+
}
|
|
302
|
+
/** Tell a held person when what they wait for has changed, keeping their place and clock. Called on each change of
|
|
303
|
+
* pressure, since that is the only thing `waitReason` reads. */
|
|
304
|
+
refreshWaiting(host, fx) {
|
|
305
|
+
const held = host.reservation;
|
|
306
|
+
if (!held)
|
|
307
|
+
return;
|
|
308
|
+
const reason = this.waitReason(host);
|
|
309
|
+
if (reason === held.reason)
|
|
310
|
+
return;
|
|
311
|
+
held.reason = reason;
|
|
312
|
+
fx.push(this.waitingOf(held));
|
|
313
|
+
}
|
|
314
|
+
waitingOf(held) {
|
|
315
|
+
return { kind: 'waiting', sessionId: held.sessionId, requestId: held.requestId, until: held.since + this.timing.humanWaitMs, reason: held.reason, message: held.message };
|
|
316
|
+
}
|
|
317
|
+
dispatchReservation(host, fx) {
|
|
318
|
+
const held = host.reservation;
|
|
319
|
+
if (!held || host.slot !== null || host.agingWindowUntil !== null || this.isCritical(host))
|
|
320
|
+
return;
|
|
321
|
+
host.reservation = null;
|
|
322
|
+
this.takeSlot(host, held);
|
|
323
|
+
this.noteHumanGrant(host);
|
|
324
|
+
fx.push({ kind: 'deliver', sessionId: held.sessionId, requestId: held.requestId, message: held.message });
|
|
325
|
+
}
|
|
326
|
+
noteHumanGrant(host) {
|
|
327
|
+
const automationWaiting = host.lastAutomatedRefusalAt !== null && this.now() - host.lastAutomatedRefusalAt <= this.timing.recentRefusalMs;
|
|
328
|
+
host.humanStreak = automationWaiting ? host.humanStreak + 1 : 0;
|
|
329
|
+
}
|
|
330
|
+
withdrawHeld(sessionId, fx) {
|
|
331
|
+
for (const host of this.hosts.values()) {
|
|
332
|
+
if (host.reservation?.sessionId !== sessionId)
|
|
333
|
+
continue;
|
|
334
|
+
fx.push({ kind: 'withdraw', sessionId, requestId: host.reservation.requestId, why: 'cancelled', message: host.reservation.message });
|
|
335
|
+
host.reservation = null;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
takeSlot(host, req) {
|
|
339
|
+
host.slot = { sessionId: req.sessionId, requestId: req.requestId, phase: 'booting', since: this.now(), readyAt: null, samples: [], droppedFirst: false, lastSampleAt: null, reboundAt: null };
|
|
340
|
+
}
|
|
341
|
+
deliverOf(req) {
|
|
342
|
+
return { kind: 'deliver', sessionId: req.sessionId, requestId: req.requestId, message: req.message };
|
|
343
|
+
}
|
|
344
|
+
slotOf(sessionId) {
|
|
345
|
+
for (const [key, host] of this.hosts)
|
|
346
|
+
if (host.slot?.sessionId === sessionId)
|
|
347
|
+
return { key, host };
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
host(key) {
|
|
351
|
+
let host = this.hosts.get(key);
|
|
352
|
+
if (!host) {
|
|
353
|
+
host = { slot: null, reservation: null, humanStreak: 0, lastAutomatedRefusalAt: null, agingWindowUntil: null, criticalUntil: null, windowLeftMs: null };
|
|
354
|
+
this.hosts.set(key, host);
|
|
355
|
+
}
|
|
356
|
+
return host;
|
|
357
|
+
}
|
|
358
|
+
/** When `tick()` must next run, or null when nothing is pending. */
|
|
359
|
+
nextDeadline() {
|
|
360
|
+
const t = this.timing;
|
|
361
|
+
let next = null;
|
|
362
|
+
const consider = (at) => { if (next === null || at < next)
|
|
363
|
+
next = at; };
|
|
364
|
+
for (const host of this.hosts.values()) {
|
|
365
|
+
const slot = host.slot;
|
|
366
|
+
if (slot) {
|
|
367
|
+
if (slot.phase === 'booting' && slot.reboundAt !== null)
|
|
368
|
+
consider(slot.reboundAt + t.reboundMs);
|
|
369
|
+
if (slot.phase === 'booting')
|
|
370
|
+
consider(slot.since + t.bootMs);
|
|
371
|
+
else if (slot.readyAt !== null) {
|
|
372
|
+
consider(slot.readyAt + t.settleCapMs);
|
|
373
|
+
if (slot.lastSampleAt === null)
|
|
374
|
+
consider(slot.readyAt + t.noSampleMs);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (host.criticalUntil !== null)
|
|
378
|
+
consider(host.criticalUntil);
|
|
379
|
+
// A window does not run out while critical, and a past deadline here would spin the relay's timer.
|
|
380
|
+
if (host.agingWindowUntil !== null && !this.isCritical(host))
|
|
381
|
+
consider(host.agingWindowUntil);
|
|
382
|
+
if (host.reservation)
|
|
383
|
+
consider(host.reservation.since + t.humanWaitMs);
|
|
384
|
+
}
|
|
385
|
+
return next;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
//# sourceMappingURL=HostBootGate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HostBootGate.js","sourceRoot":"","sources":["../../src/admission/HostBootGate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How many devices automation may keep up on one Mac, and which idle one goes to make room.
|
|
3
|
+
*
|
|
4
|
+
* A boot is what hurts the other sessions on a Mac; a device that has settled does not (`settle.ts`). But devices
|
|
5
|
+
* add up: 4 iOS simulators on an 8 GB Mac and 9 devices on a 32 GB one kept a person's session at baseline, and
|
|
6
|
+
* nothing past that was measured. Automation is what piles them up — a flow run or an MCP client ends with
|
|
7
|
+
* `session:leave`, which leaves the device running — so it is automation that is capped. A person is never counted,
|
|
8
|
+
* never refused and never has a device shut down for this.
|
|
9
|
+
*
|
|
10
|
+
* Pure: the relay hands in the Mac's devices and carries out the answer.
|
|
11
|
+
*/
|
|
12
|
+
/** One device on the Mac, as the cap sees it. */
|
|
13
|
+
export interface CapacityDevice {
|
|
14
|
+
sessionId: string;
|
|
15
|
+
/** Android emulators weigh 2: one fills memory like two simulators (M2). */
|
|
16
|
+
platform: string;
|
|
17
|
+
/** Started by automation and still up, or being started by it now. */
|
|
18
|
+
automated: boolean;
|
|
19
|
+
/** Up and settled enough to be shut down, rather than booting. */
|
|
20
|
+
booted: boolean;
|
|
21
|
+
/** Someone holds the session, or watches it. Never shut down. */
|
|
22
|
+
inUse: boolean;
|
|
23
|
+
/** The relay can reach its agent, so a shutdown would arrive. */
|
|
24
|
+
reachable: boolean;
|
|
25
|
+
/** When automation started it; the oldest goes first. */
|
|
26
|
+
bootedAt: number;
|
|
27
|
+
}
|
|
28
|
+
export type CapacityPlan = {
|
|
29
|
+
ok: true;
|
|
30
|
+
evict: string[];
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
};
|
|
34
|
+
export declare function deviceWeight(platform: string): number;
|
|
35
|
+
/** The default cap: half the Mac's memory in GB, between 1 and 8 (8 GB → 4, the measured point). */
|
|
36
|
+
export declare function defaultCap(memTotalMB: number): number;
|
|
37
|
+
/**
|
|
38
|
+
* Whether `requester` may cold-boot under `cap`, and which idle automation devices must go first. The requester's
|
|
39
|
+
* own weight is left out of what is already up: a re-boot of a device automation already runs adds nothing.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* The one device to shut down when an automated boot is refused because the Mac's memory is critical: the oldest idle
|
|
43
|
+
* automation device, or none. One per refusal, so memory is freed a device at a time while the client retries, and
|
|
44
|
+
* stops as soon as the pressure lifts.
|
|
45
|
+
*/
|
|
46
|
+
export declare function idleToFree(devices: readonly CapacityDevice[], requesterId: string): string | null;
|
|
47
|
+
export declare function planCapacity(devices: readonly CapacityDevice[], requester: {
|
|
48
|
+
sessionId: string;
|
|
49
|
+
platform: string;
|
|
50
|
+
}, cap: number): CapacityPlan;
|
|
51
|
+
//# sourceMappingURL=capacity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capacity.d.ts","sourceRoot":"","sources":["../../src/admission/capacity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,iDAAiD;AACjD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAA;IAChB,sEAAsE;IACtE,SAAS,EAAE,OAAO,CAAA;IAClB,kEAAkE;IAClE,MAAM,EAAE,OAAO,CAAA;IACf,iEAAiE;IACjE,KAAK,EAAE,OAAO,CAAA;IACd,iEAAiE;IACjE,SAAS,EAAE,OAAO,CAAA;IAClB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,MAAM,YAAY,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAA;AAExE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,oGAAoG;AACpG,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;GAGG;AACH;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIjG;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,EAAE,SAAS,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAY9I"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How many devices automation may keep up on one Mac, and which idle one goes to make room.
|
|
3
|
+
*
|
|
4
|
+
* A boot is what hurts the other sessions on a Mac; a device that has settled does not (`settle.ts`). But devices
|
|
5
|
+
* add up: 4 iOS simulators on an 8 GB Mac and 9 devices on a 32 GB one kept a person's session at baseline, and
|
|
6
|
+
* nothing past that was measured. Automation is what piles them up — a flow run or an MCP client ends with
|
|
7
|
+
* `session:leave`, which leaves the device running — so it is automation that is capped. A person is never counted,
|
|
8
|
+
* never refused and never has a device shut down for this.
|
|
9
|
+
*
|
|
10
|
+
* Pure: the relay hands in the Mac's devices and carries out the answer.
|
|
11
|
+
*/
|
|
12
|
+
export function deviceWeight(platform) {
|
|
13
|
+
return platform === 'android' ? 2 : 1;
|
|
14
|
+
}
|
|
15
|
+
/** The default cap: half the Mac's memory in GB, between 1 and 8 (8 GB → 4, the measured point). */
|
|
16
|
+
export function defaultCap(memTotalMB) {
|
|
17
|
+
return Math.min(8, Math.max(1, Math.floor(memTotalMB / 1024 / 2)));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Whether `requester` may cold-boot under `cap`, and which idle automation devices must go first. The requester's
|
|
21
|
+
* own weight is left out of what is already up: a re-boot of a device automation already runs adds nothing.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* The one device to shut down when an automated boot is refused because the Mac's memory is critical: the oldest idle
|
|
25
|
+
* automation device, or none. One per refusal, so memory is freed a device at a time while the client retries, and
|
|
26
|
+
* stops as soon as the pressure lifts.
|
|
27
|
+
*/
|
|
28
|
+
export function idleToFree(devices, requesterId) {
|
|
29
|
+
const idle = devices.filter((d) => d.automated && d.booted && !d.inUse && d.reachable && d.sessionId !== requesterId);
|
|
30
|
+
if (idle.length === 0)
|
|
31
|
+
return null;
|
|
32
|
+
return idle.reduce((a, b) => (b.bootedAt < a.bootedAt ? b : a)).sessionId;
|
|
33
|
+
}
|
|
34
|
+
export function planCapacity(devices, requester, cap) {
|
|
35
|
+
const counted = devices.filter((d) => d.automated && d.sessionId !== requester.sessionId);
|
|
36
|
+
let over = counted.reduce((sum, d) => sum + deviceWeight(d.platform), 0) + deviceWeight(requester.platform) - cap;
|
|
37
|
+
if (over <= 0)
|
|
38
|
+
return { ok: true, evict: [] };
|
|
39
|
+
const evict = [];
|
|
40
|
+
const idle = counted.filter((d) => d.booted && !d.inUse && d.reachable).sort((a, b) => a.bootedAt - b.bootedAt);
|
|
41
|
+
for (const d of idle) {
|
|
42
|
+
if (over <= 0)
|
|
43
|
+
break;
|
|
44
|
+
evict.push(d.sessionId);
|
|
45
|
+
over -= deviceWeight(d.platform);
|
|
46
|
+
}
|
|
47
|
+
return over <= 0 ? { ok: true, evict } : { ok: false };
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=capacity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capacity.js","sourceRoot":"","sources":["../../src/admission/capacity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAqBH,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACvC,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,UAAU,CAAC,UAAkB;IAC3C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;AACpE,CAAC;AAED;;;GAGG;AACH;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,OAAkC,EAAE,WAAmB;IAChF,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,KAAK,WAAW,CAAC,CAAA;IACrH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAClC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AAC3E,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAkC,EAAE,SAAkD,EAAE,GAAW;IAC9H,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,SAAS,CAAC,CAAA;IACzF,IAAI,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,GAAG,YAAY,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAA;IACjH,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;IAC7C,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAA;IAC/G,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,IAAI,IAAI,CAAC;YAAE,MAAK;QACpB,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;QACvB,IAAI,IAAI,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;IAClC,CAAC;IACD,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAA;AACxD,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DeviceStatus } from '@tapflowio/agent-core';
|
|
2
|
+
import type { DeviceBoot, ServiceTier } from '@tapflowio/protocol';
|
|
3
|
+
/**
|
|
4
|
+
* Whether a boot will cost the Mac a boot: `cold` brings a device up, `warm` finds it running and only opens a
|
|
5
|
+
* stream. Decided before the boot is forwarded, because that is when the gate has to charge it — the agent's
|
|
6
|
+
* own answer (`device:ready.payload.cold`) comes after, and corrects this one in the gate when they differ.
|
|
7
|
+
*
|
|
8
|
+
* `deviceStatus` is what the relay last heard: the agent's list at registration, then `booted` on every ready and
|
|
9
|
+
* `shutdown` on every shutdown. Only `'full-erase'` restarts a running device; `'app-only'`, which the dashboard
|
|
10
|
+
* sends on every boot, does not.
|
|
11
|
+
*
|
|
12
|
+
* **A running device on another service tier than the boot asks for is cold too**: the agent restarts it onto the
|
|
13
|
+
* tier (`IOSAgent.handleDeviceBoot`, `retier`). Compared only when both sides are known — a boot that asks for
|
|
14
|
+
* none takes whatever the device has, and a device whose tier is `unsupported` or unreported cannot change.
|
|
15
|
+
*/
|
|
16
|
+
export declare function classifyBoot(deviceStatus: DeviceStatus, payload: Pick<DeviceBoot['payload'], 'resetMode' | 'serviceTier'>, currentTier: ServiceTier | 'unsupported' | undefined): 'cold' | 'warm';
|
|
17
|
+
//# sourceMappingURL=classifyBoot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifyBoot.d.ts","sourceRoot":"","sources":["../../src/admission/classifyBoot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEzD,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAElE;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC1B,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC,EACjE,WAAW,EAAE,WAAW,GAAG,aAAa,GAAG,SAAS,GACnD,MAAM,GAAG,MAAM,CAKjB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { sameServiceTier } from '@tapflowio/agent-core/utils';
|
|
2
|
+
/**
|
|
3
|
+
* Whether a boot will cost the Mac a boot: `cold` brings a device up, `warm` finds it running and only opens a
|
|
4
|
+
* stream. Decided before the boot is forwarded, because that is when the gate has to charge it — the agent's
|
|
5
|
+
* own answer (`device:ready.payload.cold`) comes after, and corrects this one in the gate when they differ.
|
|
6
|
+
*
|
|
7
|
+
* `deviceStatus` is what the relay last heard: the agent's list at registration, then `booted` on every ready and
|
|
8
|
+
* `shutdown` on every shutdown. Only `'full-erase'` restarts a running device; `'app-only'`, which the dashboard
|
|
9
|
+
* sends on every boot, does not.
|
|
10
|
+
*
|
|
11
|
+
* **A running device on another service tier than the boot asks for is cold too**: the agent restarts it onto the
|
|
12
|
+
* tier (`IOSAgent.handleDeviceBoot`, `retier`). Compared only when both sides are known — a boot that asks for
|
|
13
|
+
* none takes whatever the device has, and a device whose tier is `unsupported` or unreported cannot change.
|
|
14
|
+
*/
|
|
15
|
+
export function classifyBoot(deviceStatus, payload, currentTier) {
|
|
16
|
+
if (deviceStatus !== 'booted' || payload.resetMode === 'full-erase')
|
|
17
|
+
return 'cold';
|
|
18
|
+
const asked = payload.serviceTier;
|
|
19
|
+
if (asked && currentTier && currentTier !== 'unsupported' && !sameServiceTier(currentTier, asked))
|
|
20
|
+
return 'cold';
|
|
21
|
+
return 'warm';
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=classifyBoot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifyBoot.js","sourceRoot":"","sources":["../../src/admission/classifyBoot.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAG7D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,YAAY,CAC1B,YAA0B,EAC1B,OAAiE,EACjE,WAAoD;IAEpD,IAAI,YAAY,KAAK,QAAQ,IAAI,OAAO,CAAC,SAAS,KAAK,YAAY;QAAE,OAAO,MAAM,CAAA;IAClF,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,CAAA;IACjC,IAAI,KAAK,IAAI,WAAW,IAAI,WAAW,KAAK,aAAa,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,KAAK,CAAC;QAAE,OAAO,MAAM,CAAA;IAChH,OAAO,MAAM,CAAA;AACf,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Session } from '../SessionManager.js';
|
|
2
|
+
/**
|
|
3
|
+
* Which Mac a session's device runs on. The boot gate counts per Mac, not per agent socket: an iOS and an Android
|
|
4
|
+
* agent on one Mac page against the same memory. Both agents send the machine's IOPlatformUUID as `agentId`; an
|
|
5
|
+
* agent that predates it is keyed by its name, so two old agents on one Mac are counted apart — a residual gap,
|
|
6
|
+
* accepted because it fails open (two boots at once, as before) rather than shut. The session id is the last
|
|
7
|
+
* resort for an agent with neither, which gates that one session against nothing.
|
|
8
|
+
*/
|
|
9
|
+
export declare function hostKeyOf(session: Pick<Session, 'id' | 'agentId' | 'agentName'>): string;
|
|
10
|
+
//# sourceMappingURL=hostKey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hostKey.d.ts","sourceRoot":"","sources":["../../src/admission/hostKey.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAEnD;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,SAAS,GAAG,WAAW,CAAC,GAAG,MAAM,CAIxF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which Mac a session's device runs on. The boot gate counts per Mac, not per agent socket: an iOS and an Android
|
|
3
|
+
* agent on one Mac page against the same memory. Both agents send the machine's IOPlatformUUID as `agentId`; an
|
|
4
|
+
* agent that predates it is keyed by its name, so two old agents on one Mac are counted apart — a residual gap,
|
|
5
|
+
* accepted because it fails open (two boots at once, as before) rather than shut. The session id is the last
|
|
6
|
+
* resort for an agent with neither, which gates that one session against nothing.
|
|
7
|
+
*/
|
|
8
|
+
export function hostKeyOf(session) {
|
|
9
|
+
// `||`, not `??`: the register schema defaults a missing `agentName` to `''`, and an empty key would put every
|
|
10
|
+
// such agent, on whatever Mac, behind one slot.
|
|
11
|
+
return session.agentId || session.agentName || `session:${session.id}`;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=hostKey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hostKey.js","sourceRoot":"","sources":["../../src/admission/hostKey.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,OAAsD;IAC9E,+GAA+G;IAC/G,gDAAgD;IAChD,OAAO,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,SAAS,IAAI,WAAW,OAAO,CAAC,EAAE,EAAE,CAAA;AACxE,CAAC"}
|