@tapflowio/relay 0.17.0 → 0.19.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/README.md +1 -1
- package/dist/RelayServer.d.ts +313 -2
- package/dist/RelayServer.d.ts.map +1 -1
- package/dist/RelayServer.js +1245 -211
- package/dist/RelayServer.js.map +1 -1
- package/dist/SessionManager.d.ts +124 -21
- package/dist/SessionManager.d.ts.map +1 -1
- package/dist/SessionManager.js +201 -22
- package/dist/SessionManager.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +3 -44
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -1
- package/package.json +10 -8
- package/public/assets/{AppCenter-BRmG2FEm.js → AppCenter-DyP0aqfF.js} +2 -2
- package/public/assets/AppCenter-DyP0aqfF.js.br +0 -0
- package/public/assets/Default-D7ScPE30.js +1 -0
- package/public/assets/Default-D7ScPE30.js.br +0 -0
- package/public/assets/Invite-UmhV5rAf.js +1 -0
- package/public/assets/Invite-UmhV5rAf.js.br +0 -0
- package/public/assets/MacResources-MS4E00w3.js +1 -0
- package/public/assets/MacResources-MS4E00w3.js.br +0 -0
- package/public/assets/NotFound-BoDDivK5.js +1 -0
- package/public/assets/NotFound-BoDDivK5.js.br +0 -0
- package/public/assets/QASession-CjgJHDZ3.js +125 -0
- package/public/assets/QASession-CjgJHDZ3.js.br +0 -0
- package/public/assets/ResetPassword-CAlB6CfP.js +1 -0
- package/public/assets/ResetPassword-CAlB6CfP.js.br +0 -0
- package/public/assets/Setup-CPm2-hPJ.js +1 -0
- package/public/assets/Setup-CPm2-hPJ.js.br +0 -0
- package/public/assets/{Team-D1iwg2vL.js → Team-GEIzrM1D.js} +2 -2
- package/public/assets/Team-GEIzrM1D.js.br +0 -0
- package/public/assets/Tokens-DSeBz_CJ.js +1 -0
- package/public/assets/Tokens-DSeBz_CJ.js.br +0 -0
- package/public/assets/alert-dialog-BsXI9SVV.js +1 -0
- package/public/assets/alert-dialog-BsXI9SVV.js.br +0 -0
- package/public/assets/badge-BkdYQFsh.js +1 -0
- package/public/assets/badge-BkdYQFsh.js.br +0 -0
- package/public/assets/{build-format-BZV6gt41.js → build-format-CXuQt1X2.js} +3 -3
- package/public/assets/build-format-CXuQt1X2.js.br +0 -0
- package/public/assets/dialog-DuXZpbiF.js +11 -0
- package/public/assets/dialog-DuXZpbiF.js.br +0 -0
- package/public/assets/index-Bk2hqaHZ.css +1 -0
- package/public/assets/index-Bk2hqaHZ.css.br +0 -0
- package/public/assets/index-bitr6DIC.js +215 -0
- package/public/assets/index-bitr6DIC.js.br +0 -0
- package/public/assets/{pencil-BGrOEXS6.js → pencil-iRR275s8.js} +1 -1
- package/public/assets/pencil-iRR275s8.js.br +0 -0
- package/public/assets/table-CHmhGpHG.js +1 -0
- package/public/assets/table-CHmhGpHG.js.br +0 -0
- package/public/assets/tabs-BnbeuAOe.js +1 -0
- package/public/assets/tabs-BnbeuAOe.js.br +0 -0
- package/public/assets/{trash-2-CwlFrbEF.js → trash-2-QI162w2K.js} +1 -1
- package/public/assets/trash-2-QI162w2K.js.br +0 -0
- package/public/assets/vendor-charts-DyX5CBcf.js +5 -0
- package/public/assets/vendor-charts-DyX5CBcf.js.br +0 -0
- package/public/assets/vendor-react-AJpf4YZ0.js +49 -0
- package/public/assets/vendor-react-AJpf4YZ0.js.br +0 -0
- package/public/assets/vendor-router-ChFy8CZ1.js +12 -0
- package/public/assets/vendor-router-ChFy8CZ1.js.br +0 -0
- package/public/index.html +4 -2
- package/public/index.html.br +0 -0
- package/public/assets/AppCenter-BRmG2FEm.js.br +0 -0
- package/public/assets/Default-DTr0wqds.js +0 -1
- package/public/assets/Default-DTr0wqds.js.br +0 -0
- package/public/assets/Invite-DKsNh2GB.js +0 -1
- package/public/assets/Invite-DKsNh2GB.js.br +0 -0
- package/public/assets/MacResources-CLDdcibL.js +0 -5
- package/public/assets/MacResources-CLDdcibL.js.br +0 -0
- package/public/assets/NotFound-BtKq2uv6.js +0 -1
- package/public/assets/NotFound-BtKq2uv6.js.br +0 -0
- package/public/assets/QASession-B_5aJ67l.js +0 -130
- package/public/assets/QASession-B_5aJ67l.js.br +0 -0
- package/public/assets/ResetPassword-gp6hVQrf.js +0 -1
- package/public/assets/ResetPassword-gp6hVQrf.js.br +0 -0
- package/public/assets/Setup-DiG4Nbs7.js +0 -1
- package/public/assets/Setup-DiG4Nbs7.js.br +0 -0
- package/public/assets/Team-D1iwg2vL.js.br +0 -0
- package/public/assets/Tokens-DGtE4Xcy.js +0 -1
- package/public/assets/Tokens-DGtE4Xcy.js.br +0 -0
- package/public/assets/alert-dialog-CXwkFlF3.js +0 -1
- package/public/assets/alert-dialog-CXwkFlF3.js.br +0 -0
- package/public/assets/badge-CavXTq2x.js +0 -1
- package/public/assets/badge-CavXTq2x.js.br +0 -0
- package/public/assets/build-format-BZV6gt41.js.br +0 -0
- package/public/assets/dialog-JbKwqqS0.js +0 -11
- package/public/assets/dialog-JbKwqqS0.js.br +0 -0
- package/public/assets/index-CBnxiD7-.js +0 -274
- package/public/assets/index-CBnxiD7-.js.br +0 -0
- package/public/assets/index-Ctx3_cYz.css +0 -1
- package/public/assets/index-Ctx3_cYz.css.br +0 -0
- package/public/assets/pencil-BGrOEXS6.js.br +0 -0
- package/public/assets/table-CqOA0Bb1.js +0 -1
- package/public/assets/table-CqOA0Bb1.js.br +0 -0
- package/public/assets/tabs-BVBbhCFG.js +0 -1
- package/public/assets/tabs-BVBbhCFG.js.br +0 -0
- package/public/assets/trash-2-CwlFrbEF.js.br +0 -0
package/dist/SessionManager.js
CHANGED
|
@@ -1,35 +1,66 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
2
|
import { WebSocket } from 'ws';
|
|
3
|
-
import { ValidationError } from '@tapflowio/agent-core';
|
|
4
3
|
const DEFAULT_IDLE_TIMEOUT_MS = parseInt(process.env['IDLE_TIMEOUT_MS'] ?? String(5 * 60 * 1000));
|
|
5
4
|
export class SessionManager {
|
|
6
5
|
sessions = new Map();
|
|
7
6
|
agentResources = new Map();
|
|
8
7
|
agentSocketIndex = new Map();
|
|
9
8
|
streamSocketIndex = new Map();
|
|
9
|
+
/**
|
|
10
|
+
* Which sessions a browser socket holds. **A set, because the relation is one-to-many** — and it was a
|
|
11
|
+
* single `Session` until #507 was diagnosed, which is the whole of that defect.
|
|
12
|
+
*
|
|
13
|
+
* `mcp-server` opens exactly one socket (`client.ts`, one `new WebSocket`) and sends a `session:start`
|
|
14
|
+
* per device, because an LLM can hold several sessions at once and its waiters are keyed per session.
|
|
15
|
+
* With one slot per socket, the second join silently overwrote the first — `A.browserSocket` still
|
|
16
|
+
* pointed at the socket, so *commands* kept working, and only the reverse lookup lost A. The close
|
|
17
|
+
* handler resolves through that lookup, so **A was never released**: `busy: true` for the life of the
|
|
18
|
+
* relay, no idle timer, and a device left booted with nobody watching it.
|
|
19
|
+
*
|
|
20
|
+
* That is also why the obvious reading of #507 — "release the previous session when a socket joins
|
|
21
|
+
* another" — is a regression rather than a fix. Two independent design reviews reached it separately.
|
|
22
|
+
*/
|
|
10
23
|
browserSocketIndex = new Map();
|
|
11
24
|
idleTimeoutMs;
|
|
12
25
|
constructor(options = {}) {
|
|
13
26
|
this.idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
|
|
14
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* The single place a session's fields are computed from an `agent:register`. `create()` spreads
|
|
30
|
+
* it and `rebind()` assigns it, so neither one names these fields itself — a field added here
|
|
31
|
+
* reaches both paths, and `rebind` is the one that would otherwise be forgotten.
|
|
32
|
+
*
|
|
33
|
+
* What is left out is what a register cannot change: the session's own id and sockets, and
|
|
34
|
+
* `deviceId`, which is the key the rebind matched on in the first place.
|
|
35
|
+
*/
|
|
36
|
+
static agentFields(agent, device) {
|
|
37
|
+
return {
|
|
38
|
+
agentId: agent.agentId,
|
|
39
|
+
agentName: agent.agentName,
|
|
40
|
+
agentPlatform: agent.agentPlatform,
|
|
41
|
+
agentCapabilities: agent.agentCapabilities,
|
|
42
|
+
deviceName: device.name,
|
|
43
|
+
devicePlatform: device.platform,
|
|
44
|
+
deviceStatus: device.status,
|
|
45
|
+
deviceOsVersion: device.osVersion,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
15
48
|
create(agentSocket, devices = [], agentName, agentPlatform, agentId, agentCapabilities) {
|
|
16
49
|
const agentIds = this.agentSocketIndex.get(agentSocket) ?? new Set();
|
|
50
|
+
const agent = { agentId, agentName, agentPlatform, agentCapabilities };
|
|
17
51
|
return devices.map((d) => {
|
|
18
52
|
const id = randomUUID();
|
|
19
53
|
this.sessions.set(id, {
|
|
20
54
|
id,
|
|
21
|
-
|
|
22
|
-
agentName,
|
|
23
|
-
agentPlatform,
|
|
24
|
-
agentCapabilities,
|
|
55
|
+
...SessionManager.agentFields(agent, d),
|
|
25
56
|
agentSocket,
|
|
26
57
|
browserSocket: null,
|
|
58
|
+
owner: null,
|
|
59
|
+
ownerMinted: false,
|
|
60
|
+
ownerUser: null,
|
|
27
61
|
streamSocket: null,
|
|
28
62
|
deviceId: d.id,
|
|
29
|
-
|
|
30
|
-
devicePlatform: d.platform,
|
|
31
|
-
deviceStatus: d.status,
|
|
32
|
-
deviceOsVersion: d.osVersion,
|
|
63
|
+
readySent: false,
|
|
33
64
|
idleTimer: null,
|
|
34
65
|
});
|
|
35
66
|
agentIds.add(id);
|
|
@@ -63,27 +94,86 @@ export class SessionManager {
|
|
|
63
94
|
}
|
|
64
95
|
return Array.from(sockets);
|
|
65
96
|
}
|
|
97
|
+
/** Sessions currently claiming a device id. More than one only while a held session (#426) has
|
|
98
|
+
* not yet been reclaimed or given up on. */
|
|
99
|
+
getAllByDeviceId(deviceId) {
|
|
100
|
+
return Array.from(this.sessions.values()).filter((s) => s.deviceId === deviceId);
|
|
101
|
+
}
|
|
66
102
|
getByStreamSocket(ws) {
|
|
67
103
|
return this.streamSocketIndex.get(ws);
|
|
68
104
|
}
|
|
105
|
+
/** Every session this socket holds. **Plural**, and the caller must treat it as such — the close
|
|
106
|
+
* handler releasing only the first is the defect the index comment above describes. */
|
|
69
107
|
getByBrowserSocket(ws) {
|
|
70
|
-
return this.browserSocketIndex.get(ws);
|
|
108
|
+
return [...(this.browserSocketIndex.get(ws) ?? [])];
|
|
71
109
|
}
|
|
72
|
-
|
|
110
|
+
/** Drop `session` from its browser socket's set, and the key with it when the set empties. Leaving an
|
|
111
|
+
* empty `Set` behind would keep a closed socket referenced for the life of the relay. */
|
|
112
|
+
unindexBrowser(session) {
|
|
113
|
+
const ws = session.browserSocket;
|
|
114
|
+
if (!ws)
|
|
115
|
+
return;
|
|
116
|
+
const held = this.browserSocketIndex.get(ws);
|
|
117
|
+
if (!held)
|
|
118
|
+
return;
|
|
119
|
+
held.delete(session);
|
|
120
|
+
if (held.size === 0)
|
|
121
|
+
this.browserSocketIndex.delete(ws);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* @returns `not-found` and `held-by-another` rather than throwing them, **so an unexpected failure and
|
|
125
|
+
* an expected one stop sharing a channel.** `session:start` answered both from one `catch`, and
|
|
126
|
+
* the comment there said reaching it meant something the handler did not anticipate — while the
|
|
127
|
+
* most common way to reach it was a socket re-joining the session it already holds (#515). An
|
|
128
|
+
* expected failure that travels as an exception is indistinguishable from a bug by the time it
|
|
129
|
+
* is caught, and the handler then has to guess a `reason`.
|
|
130
|
+
*/
|
|
131
|
+
join(sessionId, browserSocket, owner, isAlive) {
|
|
73
132
|
const session = this.sessions.get(sessionId);
|
|
74
133
|
if (!session)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
134
|
+
return { ok: false, failure: 'not-found' };
|
|
135
|
+
// **Three conditions, and each one is an issue.** A session is occupied only when someone *else* holds
|
|
136
|
+
// it, on a socket that is open, and that socket is answering.
|
|
137
|
+
//
|
|
138
|
+
// - `owner !== owner` is #527 and #515: a re-join by the same client is idempotent whether or not the
|
|
139
|
+
// socket is the same one, which is what makes a tab's four sockets one holder and an automatic
|
|
140
|
+
// reconnect a takeover rather than a refusal.
|
|
141
|
+
// - `readyState === OPEN` was the whole test, and it is not a liveness signal. A laptop that went to
|
|
142
|
+
// sleep leaves it OPEN until TCP or the heartbeat notices.
|
|
143
|
+
// - `isAlive` is that signal, passed in because liveness lives on the server rather than here — as a
|
|
144
|
+
// predicate over the socket rather than a thunk, so it is applied to *this* session's holder and
|
|
145
|
+
// cannot be closed over a stale one. `list()` below takes the same shape for the same reason.
|
|
146
|
+
// #579 is the gap between `readyState` and this.
|
|
147
|
+
if (session.browserSocket &&
|
|
148
|
+
session.owner !== null &&
|
|
149
|
+
session.owner !== owner.key &&
|
|
150
|
+
session.browserSocket.readyState === WebSocket.OPEN &&
|
|
151
|
+
isAlive(session.browserSocket)) {
|
|
152
|
+
return { ok: false, failure: 'held-by-another' };
|
|
78
153
|
}
|
|
79
154
|
if (session.idleTimer) {
|
|
80
155
|
clearTimeout(session.idleTimer);
|
|
81
156
|
session.idleTimer = null;
|
|
82
157
|
}
|
|
83
|
-
|
|
84
|
-
|
|
158
|
+
// Unconditional, and **the order is what makes it safe**: this releases the session from whichever
|
|
159
|
+
// socket held it, which on a re-join is the socket joining. Removing and re-adding the same entry is a
|
|
160
|
+
// no-op; doing it *after* the add below would delete the entry this join just made, leaving the
|
|
161
|
+
// session bound for commands and invisible to the close handler — #507 rebuilt one method over.
|
|
162
|
+
//
|
|
163
|
+
// A guard reading `!== browserSocket` stood here with that reasoning written as its justification. It
|
|
164
|
+
// was dead: mutation testing removed the guard and every test still passed, because the add follows.
|
|
165
|
+
this.unindexBrowser(session);
|
|
85
166
|
session.browserSocket = browserSocket;
|
|
86
|
-
|
|
167
|
+
session.owner = owner.key;
|
|
168
|
+
session.ownerMinted = owner.minted;
|
|
169
|
+
session.ownerUser = owner.user;
|
|
170
|
+
let held = this.browserSocketIndex.get(browserSocket);
|
|
171
|
+
if (!held) {
|
|
172
|
+
held = new Set();
|
|
173
|
+
this.browserSocketIndex.set(browserSocket, held);
|
|
174
|
+
}
|
|
175
|
+
held.add(session);
|
|
176
|
+
return { ok: true };
|
|
87
177
|
}
|
|
88
178
|
remove(sessionId) {
|
|
89
179
|
const session = this.sessions.get(sessionId);
|
|
@@ -95,8 +185,7 @@ export class SessionManager {
|
|
|
95
185
|
}
|
|
96
186
|
if (session.streamSocket)
|
|
97
187
|
this.streamSocketIndex.delete(session.streamSocket);
|
|
98
|
-
|
|
99
|
-
this.browserSocketIndex.delete(session.browserSocket);
|
|
188
|
+
this.unindexBrowser(session);
|
|
100
189
|
const agentIds = this.agentSocketIndex.get(session.agentSocket);
|
|
101
190
|
agentIds?.delete(sessionId);
|
|
102
191
|
if (agentIds?.size === 0)
|
|
@@ -108,9 +197,12 @@ export class SessionManager {
|
|
|
108
197
|
if (!session)
|
|
109
198
|
return;
|
|
110
199
|
if (session.browserSocket) {
|
|
111
|
-
this.
|
|
200
|
+
this.unindexBrowser(session);
|
|
112
201
|
session.browserSocket = null;
|
|
113
202
|
}
|
|
203
|
+
session.owner = null;
|
|
204
|
+
session.ownerMinted = false;
|
|
205
|
+
session.ownerUser = null;
|
|
114
206
|
if (session.idleTimer) {
|
|
115
207
|
clearTimeout(session.idleTimer);
|
|
116
208
|
session.idleTimer = null;
|
|
@@ -122,6 +214,51 @@ export class SessionManager {
|
|
|
122
214
|
}, this.idleTimeoutMs);
|
|
123
215
|
}
|
|
124
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Re-point an existing session at the socket of an agent that just restarted, keeping its id.
|
|
219
|
+
*
|
|
220
|
+
* Everything a rebind touches lives here rather than at the call site, and that is deliberate:
|
|
221
|
+
* the index move below has an order requirement that is invisible where it is used, and the
|
|
222
|
+
* field refresh has to stay in step with `create()`. Written inline in `RelayServer`, the next
|
|
223
|
+
* field added to `create()` would be missed on this path alone, and silently.
|
|
224
|
+
*/
|
|
225
|
+
rebind(sessionId, agentSocket, device, agent) {
|
|
226
|
+
const session = this.sessions.get(sessionId);
|
|
227
|
+
if (!session)
|
|
228
|
+
return;
|
|
229
|
+
const old = session.agentSocket;
|
|
230
|
+
// Drop from the old socket's set BEFORE reassigning. `remove()` dereferences the index through
|
|
231
|
+
// `session.agentSocket`, and following that idiom here would delete the id from the *new* set
|
|
232
|
+
// and leave the old one holding it — so the old socket's close, which fires late after an
|
|
233
|
+
// unclean drop, would evict the session that was just re-pointed.
|
|
234
|
+
const oldIds = this.agentSocketIndex.get(old);
|
|
235
|
+
oldIds?.delete(sessionId);
|
|
236
|
+
if (oldIds?.size === 0)
|
|
237
|
+
this.agentSocketIndex.delete(old);
|
|
238
|
+
session.agentSocket = agentSocket;
|
|
239
|
+
const ids = this.agentSocketIndex.get(agentSocket) ?? new Set();
|
|
240
|
+
ids.add(sessionId);
|
|
241
|
+
this.agentSocketIndex.set(agentSocket, ids);
|
|
242
|
+
// The stream died with the old process. `old.terminate()` only closes the control socket, so
|
|
243
|
+
// nothing else would clear this.
|
|
244
|
+
this.clearStreamSocket(sessionId);
|
|
245
|
+
// `clearStreamSocket` returns early when there is no stream socket, and a session that was
|
|
246
|
+
// never streamed has none — so this cannot be left to it.
|
|
247
|
+
session.readySent = false;
|
|
248
|
+
// Same argument as `readySent`, two fields over: `handleSessionStart` replays both to a browser
|
|
249
|
+
// that joins now, and both were measured by the process that just died. A viewer's own
|
|
250
|
+
// `device:boot` would clear them a moment later via `device:booting`, but an MCP-attached
|
|
251
|
+
// session never boots on its own and would keep them for as long as it lives.
|
|
252
|
+
session.chromeData = undefined;
|
|
253
|
+
session.deviceInfo = undefined;
|
|
254
|
+
Object.assign(session, SessionManager.agentFields(agent, device));
|
|
255
|
+
// Not a carry-over guard — resources are keyed by socket, so re-pointing the session at a new
|
|
256
|
+
// one already leaves the old reading behind, and every reader goes through
|
|
257
|
+
// `session.agentSocket`. This is the leak: `evictAgentSocket` drops the entry, but it returns
|
|
258
|
+
// early when the socket has no sessions left, which is precisely the case where every one of
|
|
259
|
+
// them was rebound. Without this line the map keeps a dead socket per restart, forever.
|
|
260
|
+
this.agentResources.delete(old);
|
|
261
|
+
}
|
|
125
262
|
setResources(agentSocket, resources) {
|
|
126
263
|
this.agentResources.set(agentSocket, resources);
|
|
127
264
|
}
|
|
@@ -138,6 +275,9 @@ export class SessionManager {
|
|
|
138
275
|
session.chromeData = undefined;
|
|
139
276
|
session.deviceInfo = undefined;
|
|
140
277
|
session.deviceStatus = 'shutdown';
|
|
278
|
+
// Called on `device:booting`: whatever we announced before is no longer true, and replaying it
|
|
279
|
+
// to a browser that joins mid-boot would promise a stream that is being torn down.
|
|
280
|
+
session.readySent = false;
|
|
141
281
|
}
|
|
142
282
|
setStreamSocket(sessionId, ws) {
|
|
143
283
|
const session = this.sessions.get(sessionId);
|
|
@@ -148,12 +288,16 @@ export class SessionManager {
|
|
|
148
288
|
session.streamSocket = ws;
|
|
149
289
|
this.streamSocketIndex.set(ws, session);
|
|
150
290
|
}
|
|
291
|
+
/** The stream socket is the stream. Losing it means whatever we announced is no longer true —
|
|
292
|
+
* and this is the only signal for it on the paths where the agent never reports back, such as a
|
|
293
|
+
* `simctl shutdown` that throws after the streamer has already been torn down. */
|
|
151
294
|
clearStreamSocket(sessionId) {
|
|
152
295
|
const session = this.sessions.get(sessionId);
|
|
153
296
|
if (!session?.streamSocket)
|
|
154
297
|
return;
|
|
155
298
|
this.streamSocketIndex.delete(session.streamSocket);
|
|
156
299
|
session.streamSocket = null;
|
|
300
|
+
session.readySent = false;
|
|
157
301
|
}
|
|
158
302
|
setChromeData(sessionId, data) {
|
|
159
303
|
const session = this.sessions.get(sessionId);
|
|
@@ -165,15 +309,42 @@ export class SessionManager {
|
|
|
165
309
|
if (session)
|
|
166
310
|
session.deviceInfo = info;
|
|
167
311
|
}
|
|
312
|
+
setReadySent(sessionId, value) {
|
|
313
|
+
const session = this.sessions.get(sessionId);
|
|
314
|
+
if (session)
|
|
315
|
+
session.readySent = value;
|
|
316
|
+
}
|
|
168
317
|
updateDeviceStatus(sessionId, status) {
|
|
169
318
|
const session = this.sessions.get(sessionId);
|
|
170
319
|
if (session)
|
|
171
320
|
session.deviceStatus = status;
|
|
172
321
|
}
|
|
173
|
-
|
|
322
|
+
/**
|
|
323
|
+
* @param asker the owner key of the socket that asked, so `busy` can mean *busy for you*.
|
|
324
|
+
* @param isAlive whether a holder's socket is answering — the same predicate occupancy uses.
|
|
325
|
+
*
|
|
326
|
+
* **`busy` has to agree with the occupancy test or the UI locks a tester out of their own session.**
|
|
327
|
+
* `browserSocket !== null` said "someone holds it", which is a different question from "you cannot have
|
|
328
|
+
* it": `QASession` renders `disabled={isBusy}`, so a tester whose socket reconnected automatically found
|
|
329
|
+
* their own device greyed out with no way to reach the takeover that would have worked. And a holder
|
|
330
|
+
* that has stopped answering keeps the card locked for everyone until the sweep notices.
|
|
331
|
+
*
|
|
332
|
+
* Passing the same `isAlive` rather than a second liveness rule is the point — two predicates that
|
|
333
|
+
* disagree would flash a device as free while a join for it is still refused.
|
|
334
|
+
*/
|
|
335
|
+
list(asker, isAlive) {
|
|
174
336
|
// Group sessions by agentSocket
|
|
175
337
|
const agentMap = new Map();
|
|
176
338
|
for (const session of this.sessions.values()) {
|
|
339
|
+
// A session whose agent socket is closed is being held for a returning agent (#426). It is
|
|
340
|
+
// not something anyone can pick right now, and listing it is worse than leaving it out: the
|
|
341
|
+
// card would render with the dead agent's last CPU/RAM reading and no warning — the `Stale`
|
|
342
|
+
// badge keys off a 30s-old resource sample, far longer than the window. Worse, if the agent
|
|
343
|
+
// comes back under a different identity (adding an agentId is a common reason to upgrade,
|
|
344
|
+
// and upgrading is a common reason to restart) nothing rebinds, and the dashboard groups by
|
|
345
|
+
// `agentName` — two groups, a duplicate React key, and the lookup picking the dead one.
|
|
346
|
+
if (session.agentSocket.readyState !== WebSocket.OPEN)
|
|
347
|
+
continue;
|
|
177
348
|
const group = agentMap.get(session.agentSocket) ?? [];
|
|
178
349
|
group.push(session);
|
|
179
350
|
agentMap.set(session.agentSocket, group);
|
|
@@ -189,7 +360,15 @@ export class SessionManager {
|
|
|
189
360
|
status: s.deviceStatus,
|
|
190
361
|
osVersion: s.deviceOsVersion,
|
|
191
362
|
sessionId: s.id,
|
|
192
|
-
busy: s.
|
|
363
|
+
busy: s.owner !== null &&
|
|
364
|
+
s.owner !== asker &&
|
|
365
|
+
s.browserSocket !== null &&
|
|
366
|
+
// The clause that was missing, and its absence was the mirror of the failure the docstring warns
|
|
367
|
+
// about: a socket in CLOSING has not fired `close` yet, so `isAlive` is still true — the card
|
|
368
|
+
// read `busy` while a join for it would have succeeded, locking every other tester out of a
|
|
369
|
+
// device the relay was ready to hand them.
|
|
370
|
+
s.browserSocket.readyState === WebSocket.OPEN &&
|
|
371
|
+
isAlive(s.browserSocket),
|
|
193
372
|
})),
|
|
194
373
|
}));
|
|
195
374
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionManager.js","sourceRoot":"","sources":["../src/SessionManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAA;AAE9B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAwBvD,MAAM,uBAAuB,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAA;AAEjG,MAAM,OAAO,cAAc;IACjB,QAAQ,GAAG,IAAI,GAAG,EAAmB,CAAA;IACrC,cAAc,GAAG,IAAI,GAAG,EAA6B,CAAA;IACrD,gBAAgB,GAAG,IAAI,GAAG,EAA0B,CAAA;IACpD,iBAAiB,GAAG,IAAI,GAAG,EAAsB,CAAA;IACjD,kBAAkB,GAAG,IAAI,GAAG,EAAsB,CAAA;IACzC,aAAa,CAAQ;IAEtC,YAAY,UAAsC,EAAE;QAClD,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,uBAAuB,CAAA;IACvE,CAAC;IAED,MAAM,CAAC,WAAsB,EAAE,UAAuB,EAAE,EAAE,SAAkB,EAAE,aAAsB,EAAE,OAAgB,EAAE,iBAA4B;QAClJ,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,IAAI,GAAG,EAAU,CAAA;QAC5E,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACvB,MAAM,EAAE,GAAG,UAAU,EAAE,CAAA;YACvB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE;gBACpB,EAAE;gBACF,OAAO;gBACP,SAAS;gBACT,aAAa;gBACb,iBAAiB;gBACjB,WAAW;gBACX,aAAa,EAAE,IAAI;gBACnB,YAAY,EAAE,IAAI;gBAClB,QAAQ,EAAE,CAAC,CAAC,EAAE;gBACd,UAAU,EAAE,CAAC,CAAC,IAAI;gBAClB,cAAc,EAAE,CAAC,CAAC,QAAQ;gBAC1B,YAAY,EAAE,CAAC,CAAC,MAAsB;gBACtC,eAAe,EAAE,CAAC,CAAC,SAAS;gBAC5B,SAAS,EAAE,IAAI;aAChB,CAAC,CAAA;YACF,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YAChB,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;YAChD,OAAO,EAAE,CAAA;QACX,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,GAAG,CAAC,SAAiB;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IACrC,CAAC;IAED,mBAAmB,CAAC,EAAa;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACzC,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,CAAA;QACnB,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAgB,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAA;IACxG,CAAC;IAED;;;;;;;;OAQG;IACH,yBAAyB,CAAC,QAAgB,EAAE,QAA4B;QACtE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAa,CAAA;QACpC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YACvC,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,SAAS,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,aAAa,KAAK,QAAQ;gBAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAA;QACzG,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC5B,CAAC;IAED,iBAAiB,CAAC,EAAa;QAC7B,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACvC,CAAC;IAED,kBAAkB,CAAC,EAAa;QAC9B,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACxC,CAAC;IAED,IAAI,CAAC,SAAiB,EAAE,aAAwB;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC5C,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,eAAe,CAAC,sBAAsB,SAAS,EAAE,CAAC,CAAA;QAC1E,IAAI,OAAO,CAAC,aAAa,EAAE,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;YACzD,MAAM,IAAI,eAAe,CAAC,iBAAiB,SAAS,EAAE,CAAC,CAAA;QACzD,CAAC;QACD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YAAC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAAC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAA;QAAC,CAAC;QACpF,IAAI,OAAO,CAAC,aAAa;YAAE,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA;QAChF,OAAO,CAAC,aAAa,GAAG,aAAa,CAAA;QACrC,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,CAAC,CAAA;IACrD,CAAC;IAED,MAAM,CAAC,SAAiB;QACtB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC5C,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YAAC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAAC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAA;QAAC,CAAC;QACpF,IAAI,OAAO,CAAC,YAAY;YAAE,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;QAC7E,IAAI,OAAO,CAAC,aAAa;YAAE,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA;QAChF,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QAC/D,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;QAC3B,IAAI,QAAQ,EAAE,IAAI,KAAK,CAAC;YAAE,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QAC3E,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;IACjC,CAAC;IAED,YAAY,CAAC,SAAiB,EAAE,SAAsB;QACpD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC5C,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;YAC1B,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA;YACrD,OAAO,CAAC,aAAa,GAAG,IAAI,CAAA;QAC9B,CAAC;QACD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YAAC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAAC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAA;QAAC,CAAC;QACpF,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;gBAClC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAA;gBACxB,SAAS,EAAE,CAAA;YACb,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC,CAAA;QACxB,CAAC;IACH,CAAC;IAED,YAAY,CAAC,WAAsB,EAAE,SAAyB;QAC5D,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAA;IACjD,CAAC;IAED,YAAY,CAAC,WAAsB;QACjC,OAAO,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IAC7C,CAAC;IAED,eAAe,CAAC,WAAsB;QACpC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;IACzC,CAAC;IAED,gBAAgB,CAAC,SAAiB;QAChC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC5C,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,OAAO,CAAC,UAAU,GAAG,SAAS,CAAA;QAC9B,OAAO,CAAC,UAAU,GAAG,SAAS,CAAA;QAC9B,OAAO,CAAC,YAAY,GAAG,UAAU,CAAA;IACnC,CAAC;IAED,eAAe,CAAC,SAAiB,EAAE,EAAa;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC5C,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,IAAI,OAAO,CAAC,YAAY;YAAE,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;QAC7E,OAAO,CAAC,YAAY,GAAG,EAAE,CAAA;QACzB,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IACzC,CAAC;IAED,iBAAiB,CAAC,SAAiB;QACjC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC5C,IAAI,CAAC,OAAO,EAAE,YAAY;YAAE,OAAM;QAClC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;QACnD,OAAO,CAAC,YAAY,GAAG,IAAI,CAAA;IAC7B,CAAC;IAED,aAAa,CAAC,SAAiB,EAAE,IAAa;QAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC5C,IAAI,OAAO;YAAE,OAAO,CAAC,UAAU,GAAG,IAAI,CAAA;IACxC,CAAC;IAED,aAAa,CAAC,SAAiB,EAAE,IAA+C;QAC9E,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC5C,IAAI,OAAO;YAAE,OAAO,CAAC,UAAU,GAAG,IAAI,CAAA;IACxC,CAAC;IAED,kBAAkB,CAAC,SAAiB,EAAE,MAAoB;QACxD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC5C,IAAI,OAAO;YAAE,OAAO,CAAC,YAAY,GAAG,MAAM,CAAA;IAC5C,CAAC;IAED,IAAI;QACF,gCAAgC;QAChC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAwB,CAAA;QAChD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC7C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,CAAA;YACrD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YACnB,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,CAAA;QAC1C,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACnD,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;YAC7B,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa;YAChC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;YACxD,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACzB,EAAE,EAAE,CAAC,CAAC,QAAQ;gBACd,IAAI,EAAE,CAAC,CAAC,UAAU;gBAClB,QAAQ,EAAE,CAAC,CAAC,cAAc;gBAC1B,MAAM,EAAE,CAAC,CAAC,YAAY;gBACtB,SAAS,EAAE,CAAC,CAAC,eAAe;gBAC5B,SAAS,EAAE,CAAC,CAAC,EAAE;gBACf,IAAI,EAAE,CAAC,CAAC,aAAa,KAAK,IAAI;aAC/B,CAAC,CAAC;SACJ,CAAC,CAAC,CAAA;IACL,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"SessionManager.js","sourceRoot":"","sources":["../src/SessionManager.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,5 +11,4 @@ export { startTlsBackgroundTasks } from './lib/tlsTasks.js';
|
|
|
11
11
|
export type { DnsProviderEntry } from './lib/cert/index.js';
|
|
12
12
|
export type { TlsConfig } from './lib/cert/index.js';
|
|
13
13
|
export type { Session } from './SessionManager.js';
|
|
14
|
-
export type { RelayMessage, MessageType } from './types.js';
|
|
15
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACvD,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAA;AACrF,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACnF,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAC/G,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AACpD,YAAY,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACvD,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAA;AACrF,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACnF,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAC/G,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AACpD,YAAY,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,47 +1,6 @@
|
|
|
1
|
-
export type MessageType = 'agent:register' | 'agent:registered' | 'agent:resources' | 'agents:list' | 'agents:listed' | 'session:start' | 'session:joined' | 'session:chrome' | 'session:deviceInfo' | 'session:end' | 'session:leave' | 'stream:register' | 'stream:registered' | 'device:boot' | 'device:booting' | 'device:ready' | 'device:boot-error' | 'device:shutdown' | 'device:shutdown-done' | 'app:install' | 'app:install-done' | 'app:install-error' | 'app:launch' | 'app:launch-done' | 'app:launch-error' | 'open-url' | 'open-url:done' | 'open-url:error' | 'app:clear-state' | 'app:clear-state-done' | 'app:clear-state-error' | 'input:touch:start' | 'input:touch:move' | 'input:touch:end' | 'input:pinch:start' | 'input:pinch:move' | 'input:pinch:end' | 'input:key' | 'input:type' | 'input:type-done' | 'input:type-error' | 'input:done' | 'input:error' | 'input:button' | 'input:rotate' | 'input:keyboard:toggle' | 'keyboard:toggled' | 'screenshot:request' | 'screenshot:done' | 'screenshot:error' | 'ui:tree:request' | 'ui:tree:response' | 'ui:tree:error' | 'clipboard:read' | 'clipboard:write' | 'clipboard:data' | 'clipboard:write-done' | 'clipboard:error' | 'error';
|
|
2
1
|
import type { AgentResources, UIElement } from '@tapflowio/agent-core';
|
|
3
2
|
export type { AgentResources, UIElement };
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
platform: string;
|
|
8
|
-
status: string;
|
|
9
|
-
osVersion?: string;
|
|
10
|
-
sessionId: string;
|
|
11
|
-
busy: boolean;
|
|
12
|
-
}
|
|
13
|
-
export interface SessionInfo {
|
|
14
|
-
agentName?: string;
|
|
15
|
-
platform?: string;
|
|
16
|
-
resources?: AgentResources;
|
|
17
|
-
devices: DeviceInfo[];
|
|
18
|
-
}
|
|
19
|
-
export interface RelayMessage {
|
|
20
|
-
type: MessageType;
|
|
21
|
-
sessionId?: string;
|
|
22
|
-
payload?: unknown;
|
|
23
|
-
message?: string;
|
|
24
|
-
agentName?: string;
|
|
25
|
-
agentId?: string;
|
|
26
|
-
devices?: Array<{
|
|
27
|
-
id: string;
|
|
28
|
-
name: string;
|
|
29
|
-
platform: string;
|
|
30
|
-
status: string;
|
|
31
|
-
osVersion?: string;
|
|
32
|
-
}>;
|
|
33
|
-
platform?: string;
|
|
34
|
-
capabilities?: string[];
|
|
35
|
-
sessions?: SessionInfo[];
|
|
36
|
-
registeredSessions?: Array<{
|
|
37
|
-
deviceId: string;
|
|
38
|
-
sessionId: string;
|
|
39
|
-
}>;
|
|
40
|
-
buildId?: number;
|
|
41
|
-
resources?: AgentResources;
|
|
42
|
-
requestId?: string;
|
|
43
|
-
data?: string;
|
|
44
|
-
format?: 'png' | 'jpeg';
|
|
45
|
-
elements?: UIElement[];
|
|
46
|
-
}
|
|
3
|
+
import type { DeviceSummary } from '@tapflowio/protocol';
|
|
4
|
+
export type { DeviceDetails, DeviceReport, DeviceSummary, SessionInfo, SessionTerminatedReason } from '@tapflowio/protocol';
|
|
5
|
+
export type DeviceInfo = DeviceSummary;
|
|
47
6
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACtE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,CAAA;AAIzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAE3H,MAAM,MAAM,UAAU,GAAG,aAAa,CAAA"}
|
package/dist/types.js
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
|
+
// The wire contract lives in @tapflowio/protocol. This file holds what the relay adds on top of it —
|
|
2
|
+
// today only the re-exports and aliases below.
|
|
3
|
+
//
|
|
4
|
+
// **`RelayMessage` and `MessageType` were removed here (#550).** They were a flat interface where
|
|
5
|
+
// `type` was the only required member and a hand-copied union of 63 literals beside it, and they were
|
|
6
|
+
// the relay's *inbound* type: `route` took a `RelayMessage`, so every field it read was optional by
|
|
7
|
+
// construction and every one it needed came with a `!`. That is why the two type systems could
|
|
8
|
+
// disagree about the same wire field — `format?` here against a required `format` in the protocol —
|
|
9
|
+
// with nothing to report it.
|
|
10
|
+
//
|
|
11
|
+
// What replaced them is not another declaration but a parse: `@tapflowio/protocol/validate` turns an
|
|
12
|
+
// inbound frame into a discriminated union at the door, and the relay's own membership assertions
|
|
13
|
+
// went with the literal list they were holding, because there is no longer a second copy to hold.
|
|
1
14
|
export {};
|
|
2
15
|
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,+CAA+C;AAC/C,EAAE;AACF,kGAAkG;AAClG,sGAAsG;AACtG,oGAAoG;AACpG,+FAA+F;AAC/F,oGAAoG;AACpG,6BAA6B;AAC7B,EAAE;AACF,qGAAqG;AACrG,kGAAkG;AAClG,kGAAkG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tapflowio/relay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "WebSocket relay server for tapflow — NAT traversal, session routing, JWT auth, bundled dashboard",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tapflow",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"LICENSE"
|
|
40
40
|
],
|
|
41
41
|
"engines": {
|
|
42
|
-
"node": ">=
|
|
42
|
+
"node": ">=22.0.0"
|
|
43
43
|
},
|
|
44
44
|
"publishConfig": {
|
|
45
45
|
"access": "public"
|
|
@@ -49,24 +49,26 @@
|
|
|
49
49
|
"better-sqlite3": "^12.11.1",
|
|
50
50
|
"busboy": "^1.6.0",
|
|
51
51
|
"jsonwebtoken": "^9.0.3",
|
|
52
|
-
"nodemailer": "^9.0.
|
|
53
|
-
"ws": "^8.21.
|
|
52
|
+
"nodemailer": "^9.0.5",
|
|
53
|
+
"ws": "^8.21.3",
|
|
54
54
|
"zod": "^4.4.3",
|
|
55
|
-
"@tapflowio/agent-core": "0.
|
|
55
|
+
"@tapflowio/agent-core": "0.19.0",
|
|
56
|
+
"@tapflowio/protocol": "0.19.0"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
58
59
|
"@types/better-sqlite3": "^7.6.13",
|
|
59
60
|
"@types/busboy": "^1.5.4",
|
|
60
61
|
"@types/jsonwebtoken": "^9.0.10",
|
|
61
|
-
"@types/node": "^20.
|
|
62
|
+
"@types/node": "^22.20.1",
|
|
62
63
|
"@types/nodemailer": "^8.0.1",
|
|
63
64
|
"@types/ws": "^8.0.0",
|
|
64
65
|
"typescript": "^5.0.0",
|
|
65
|
-
"vitest": "^4.1.10"
|
|
66
|
+
"vitest": "^4.1.10",
|
|
67
|
+
"@tapflowio/test-utils": "0.0.0"
|
|
66
68
|
},
|
|
67
69
|
"scripts": {
|
|
68
70
|
"build": "tsc -b && rm -rf dist/migrations && cp -r src/migrations dist/migrations",
|
|
69
|
-
"typecheck": "tsc -b",
|
|
71
|
+
"typecheck": "tsc -b && tsc -p src/__tests__/tsconfig.json",
|
|
70
72
|
"start": "node dist/server.js",
|
|
71
73
|
"test": "vitest run",
|
|
72
74
|
"lint": "eslint src"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{i as r,j as e}from"./vendor-react-AJpf4YZ0.js";import{W as C,B as y,L as b,a4 as k,J,I as M,S as Q,f as W}from"./index-bitr6DIC.js";import{e as Y,S as K,P as X,g as ee,h as se,i as ae,b as te,d as le,s as ne,u as re}from"./build-format-CXuQt1X2.js";import{D as O,f as L,a as U,d as E,e as V,S as A,i as _,j as B,g as z,h as i,C as ce}from"./dialog-DuXZpbiF.js";import{P as ie,T as oe}from"./trash-2-QI162w2K.js";import{B as T}from"./badge-BkdYQFsh.js";import{A as de,c as ue,f as he,g as xe,d as pe,e as me,b as fe,a as ge}from"./alert-dialog-BsXI9SVV.js";import{b as je,c as ve}from"./vendor-router-ChFy8CZ1.js";/**
|
|
2
2
|
* @license lucide-react v0.577.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -18,4 +18,4 @@ import{W as C,a6 as r,a3 as e,B as y,L as b,a8 as k,J,I as M,S as Q,f as W,ak as
|
|
|
18
18
|
*
|
|
19
19
|
* This source code is licensed under the ISC license.
|
|
20
20
|
* See the LICENSE file in the root directory of this source tree.
|
|
21
|
-
*/const De=[["path",{d:"M12 3v12",key:"1x0j5s"}],["path",{d:"m17 8-5-5-5 5",key:"7q97r8"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}]],Ce=C("upload",De);function Ae({onSuccess:s,appId:l}){const[h,c]=r.useState(!1),[t,g]=r.useState(null),[o,x]=r.useState("none"),j=r.useRef(null);function p(n){if(n.preventDefault(),!t)return;const d=new FormData;d.append("file",t),o!=="none"&&d.append("status",o),l&&d.append("app_id",String(l));const w=fetch("/api/v1/builds",{method:"POST",credentials:"include",body:d}).then(async u=>{if(!u.ok){const m=await u.json().catch(()=>({}));throw new Error(m.error??"Upload failed. Check the file format.")}s()});k.promise(w,{loading:"Uploading build…",success:"Build uploaded",error:u=>u instanceof Error?u.message:"Failed to upload build"}),c(!1),g(null),x("none")}function N(n){c(n),n||(g(null),x("none"))}return e.jsxs(O,{open:h,onOpenChange:N,children:[e.jsx(L,{asChild:!0,children:e.jsxs(y,{size:"sm",children:[e.jsx(Ce,{className:"mr-2 h-4 w-4"}),"Upload build"]})}),e.jsxs(U,{className:"sm:max-w-md",children:[e.jsx(E,{children:e.jsx(V,{children:"Upload build"})}),e.jsxs("form",{onSubmit:p,className:"flex flex-col gap-4 pt-2",children:[e.jsxs("div",{className:"grid gap-2",children:[e.jsx(b,{children:"File"}),e.jsx("div",{className:"flex h-24 cursor-pointer items-center justify-center rounded-md border-2 border-dashed text-sm text-muted-foreground hover:border-primary",onClick:()=>{var n;return(n=j.current)==null?void 0:n.click()},onDragOver:n=>n.preventDefault(),onDrop:n=>{n.preventDefault();const d=n.dataTransfer.files[0];d&&g(d)},children:t?t.name:"Click or drag to upload"}),e.jsx("input",{ref:j,type:"file",accept:".zip,.apk,.tgz,.tar.gz,application/gzip,application/x-gzip",className:"hidden",onChange:n=>{var d;return g(((d=n.target.files)==null?void 0:d[0])??null)}}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:["iOS: ",e.jsx("code",{children:".app.zip"})," or ",e.jsx("code",{children:".tar.gz"})," / ",e.jsx("code",{children:".tgz"})," (e.g. an EAS simulator build) · Android: ",e.jsx("code",{children:".apk"})]})]}),e.jsxs("div",{className:"grid gap-2",children:[e.jsxs(b,{children:["Status ",e.jsx("span",{className:"text-muted-foreground",children:"(optional)"})]}),e.jsxs(A,{value:o,onValueChange:x,children:[e.jsx(_,{children:e.jsx(B,{})}),e.jsxs(z,{children:[e.jsx(i,{value:"none",children:"None"}),e.jsx(i,{value:"Backlog",children:"Backlog"}),e.jsx(i,{value:"In Progress",children:"In Progress"}),e.jsx(i,{value:"Done",children:"Done"}),e.jsx(i,{value:"Rejected",children:"Rejected"})]})]})]}),e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsx(y,{type:"button",variant:"outline",className:"w-24",onClick:()=>c(!1),children:"Cancel"}),e.jsx(y,{type:"submit",className:"w-24",disabled:!t,children:"Upload"})]})]})]})]})}function D({className:s,...l}){return e.jsx("span",{className:J("font-mono text-xs tabular-nums tracking-tight",s),...l})}function _e({onSuccess:s}){const[l,h]=r.useState(!1),[c,t]=r.useState(""),[g,o]=r.useState(""),[x,j]=r.useState("ios"),[p,N]=r.useState(""),[n,d]=r.useState(!1);async function w(u){u.preventDefault(),N(""),d(!0);try{const m=await X({name:c.trim(),bundle_id_key:g.trim(),platform:x});if(!m){N("Failed to create app");return}if("error"in m){N(m.error);return}h(!1),t(""),o(""),j("ios"),k.success("App created"),s()}catch{k.error("Failed to create app — check your network")}finally{d(!1)}}return e.jsxs(O,{open:l,onOpenChange:h,children:[e.jsx(L,{asChild:!0,children:e.jsxs("button",{className:"flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-left text-sm text-muted-foreground hover:bg-accent hover:text-foreground transition-colors",children:[e.jsx(de,{className:"size-3.5 shrink-0"}),e.jsx("span",{children:"Add App"})]})}),e.jsxs(U,{className:"sm:max-w-sm",children:[e.jsx(E,{children:e.jsx(V,{children:"Add App"})}),e.jsxs("form",{onSubmit:w,className:"flex flex-col gap-4 pt-2",children:[e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(b,{htmlFor:"app-name",children:"Name"}),e.jsx(M,{id:"app-name",value:c,onChange:u=>t(u.target.value),placeholder:"My App",required:!0})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(b,{htmlFor:"bundle-id",children:"Bundle ID"}),e.jsx(M,{id:"bundle-id",value:g,onChange:u=>o(u.target.value),placeholder:"com.example.myapp",required:!0})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(b,{htmlFor:"platform",children:"Platform"}),e.jsxs(A,{value:x,onValueChange:u=>j(u),children:[e.jsx(_,{id:"platform",children:e.jsx(B,{})}),e.jsxs(z,{children:[e.jsx(i,{value:"ios",children:"iOS"}),e.jsx(i,{value:"android",children:"Android"}),e.jsx(i,{value:"both",children:"Both"})]})]})]}),p&&e.jsx("p",{className:"text-sm text-destructive",children:p}),e.jsx(y,{type:"submit",disabled:n,className:"w-full",children:n?"Creating…":"Create App"})]})]})]})}function Be({apps:s,selectedAppId:l,onSelect:h,onAdd:c}){return e.jsxs("aside",{className:"w-64 shrink-0 border-r flex flex-col gap-1 p-3 overflow-y-auto",children:[e.jsx("span",{className:"px-2 pb-1 font-mono text-xs font-medium text-muted-foreground uppercase tracking-wider",children:"Apps"}),s.length===0&&e.jsx("span",{className:"px-2 text-sm text-muted-foreground",children:"No apps yet"}),s.map(t=>e.jsxs("button",{onClick:()=>h(t.id),className:["flex flex-col gap-0.5 rounded-md px-2 py-2 text-left text-sm hover:bg-accent overflow-hidden",t.id===l?"bg-accent font-medium":""].join(" "),children:[e.jsx("span",{className:"truncate",children:t.name}),e.jsx(D,{className:"text-muted-foreground truncate",children:t.bundle_id_key})]},t.id)),e.jsx("div",{className:"mt-1 border-t pt-1",children:e.jsx(_e,{onSuccess:c})})]})}function ze(s){const l=/[zZ]|[+-]\d{2}:?\d{2}$/.test(s),c=new Date(l?s:s.replace(" ","T")+"Z").getTime()-Date.now();if(c<=0)return{label:"Deleting…",urgent:!0};const t=Math.floor(c/36e5);return t<1?{label:"Deletes in < 1h",urgent:!0}:t<24?{label:`Deletes in ${t}h`,urgent:t<6}:{label:`Deletes in ${Math.floor(t/24)}d`,urgent:!1}}function Ie({build:s,isLast:l,onNavigate:h,onStatusChange:c,onScheduleDeletion:t,onCancelDeletion:g}){const[o,x]=r.useState(!1),j=s.status_label==="Done",p=s.delete_after?ze(s.delete_after):null;function N(n){c(s.id,n==="none"?null:n)}return e.jsxs(e.Fragment,{children:[e.jsx(he,{open:o,onOpenChange:x,children:e.jsxs(xe,{children:[e.jsxs(pe,{children:[e.jsx(me,{children:"Schedule deletion?"}),e.jsx(fe,{children:"Build files will be deleted after 7 days. You can cancel the scheduled deletion anytime before then."})]}),e.jsxs(ge,{children:[e.jsx(je,{children:"Cancel"}),e.jsx(ve,{onClick:()=>{t(s.id),x(!1)},children:"Schedule deletion"})]})]})}),e.jsxs("div",{className:["flex items-center gap-3 px-4 py-3",l?"":"border-b"].join(" "),children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsxs("span",{className:"text-sm font-medium",children:["build ",e.jsx(D,{children:s.build_number??"—"})]}),e.jsx(T,{tone:s.platform==="ios"?"ios":"android",className:"text-xs capitalize",children:s.platform}),s.status_label&&e.jsx(T,{tone:ee[s.status_label],className:"text-xs",children:s.status_label}),p&&e.jsx(T,{tone:p.urgent?"rejected":"backlog",className:"text-xs",children:p.label})]}),e.jsxs("div",{style:{display:"flex",flexDirection:"row",gap:"14px",marginTop:"0.7rem",alignItems:"center"},className:"text-xs text-muted-foreground",children:[s.uploader&&e.jsxs(e.Fragment,{children:[e.jsx("span",{children:s.uploader}),e.jsx(Q,{orientation:"vertical",className:"h-3"})]}),e.jsx(D,{children:new Date(s.uploaded_at).toLocaleDateString()})]})]}),e.jsxs(A,{value:s.status_label??"none",onValueChange:N,children:[e.jsx(_,{className:"h-9 w-32 text-xs",children:e.jsx(B,{})}),e.jsxs(z,{children:[e.jsx(i,{value:"none",children:e.jsx("span",{className:"text-muted-foreground",children:"—"})}),e.jsx(i,{value:"Backlog",children:"Backlog"}),e.jsx(i,{value:"In Progress",children:"In Progress"}),e.jsx(i,{value:"Done",children:"Done"}),e.jsx(i,{value:"Rejected",children:"Rejected"})]})]}),p?e.jsx(y,{size:"icon-sm",variant:"outline",onClick:()=>g(s.id),title:"Cancel scheduled deletion",children:e.jsx(ke,{className:"h-3.5 w-3.5"})}):e.jsx(y,{size:"icon-sm",variant:"destructive",onClick:()=>x(!0),title:"Schedule deletion",children:e.jsx(ue,{className:"h-3.5 w-3.5"})}),e.jsxs(y,{size:"sm",onClick:()=>h(s.id),disabled:j,children:[e.jsx(se,{className:"mr-1.5 h-3.5 w-3.5"}),"Start QA"]})]})]})}function Pe({versionName:s,builds:l,isOpen:h,onToggle:c,onNavigate:t,onStatusChange:g,onScheduleDeletion:o,onCancelDeletion:x}){return e.jsxs("div",{className:"rounded-lg shadow-card-2 overflow-hidden",children:[e.jsxs("button",{className:"flex w-full items-center gap-2 px-4 py-3 text-left hover:bg-accent/50 bg-card",onClick:c,children:[h?e.jsx(oe,{className:"h-4 w-4 shrink-0 text-muted-foreground"}):e.jsx(W,{className:"h-4 w-4 shrink-0 text-muted-foreground"}),e.jsx(D,{className:"font-medium text-sm text-foreground",children:s}),e.jsxs("span",{className:"text-xs text-muted-foreground",children:["(",l.length," build",l.length>1?"s":"",")"]})]}),h&&e.jsx("div",{className:"border-t bg-card",children:l.map((j,p)=>e.jsx(Ie,{build:j,isLast:p===l.length-1,onNavigate:t,onStatusChange:g,onScheduleDeletion:o,onCancelDeletion:x},j.id))})]})}function Ue(){const s=Y(),[l,h]=K(),[c,t]=r.useState([]),[g,o]=r.useState([]),[x,j]=r.useState(""),[p,N]=r.useState("all"),[n,d]=r.useState(!0),[w,u]=r.useState(new Set),m=l.get("appId")?Number(l.get("appId")):null,F=c.find(a=>a.id===m)??null,I=r.useCallback(async()=>{const a=await ae();t(a),!l.get("appId")&&a.length>0&&h({appId:String(a[0].id)},{replace:!0})},[l,h]),P=r.useCallback(async()=>{if(!m){o([]),d(!1);return}d(!0);try{const a=await te({appId:m,search:x,statusFilter:p});o(a),a.length>0&&w.size===0&&u(new Set([a[0].version_name??"Unversioned"]))}finally{d(!1)}},[m,x,p]);r.useEffect(()=>{I()},[]),r.useEffect(()=>{P()},[P]);function $(a){a!==m&&(o([]),u(new Set)),h({appId:String(a)})}function q(a){u(v=>{const f=new Set(v);return f.has(a)?f.delete(a):f.add(a),f})}async function H(a,v){await ie(a,v),o(f=>f.map(S=>S.id===a?{...S,status_label:v}:S))}async function Z(a){try{const v=await ce(a);o(f=>f.map(S=>S.id===a?{...S,delete_after:v}:S))}catch{k.error("Failed to schedule deletion")}}async function G(a){try{await re(a),o(v=>v.map(f=>f.id===a?{...f,delete_after:null}:f))}catch{k.error("Failed to cancel scheduled deletion")}}const R=le(g);return e.jsxs("div",{className:"flex h-full gap-0",children:[e.jsx(Be,{apps:c,selectedAppId:m,onSelect:$,onAdd:I}),e.jsxs("div",{className:"flex-1 flex flex-col gap-4 p-4 overflow-y-auto min-w-0",children:[e.jsxs("div",{className:"flex items-center justify-between flex-wrap gap-2",children:[e.jsx("h1",{className:"text-xl font-semibold tracking-display-sm",children:F?F.name:"App Center"}),e.jsx(Ae,{onSuccess:()=>{I(),P()},appId:m})]}),m?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx(ne,{placeholder:"Search version…",value:x,onChange:j}),e.jsxs(A,{value:p,onValueChange:N,children:[e.jsx(_,{className:"h-8 w-36",children:e.jsx(B,{placeholder:"Status"})}),e.jsxs(z,{children:[e.jsx(i,{value:"all",children:"All statuses"}),e.jsx(i,{value:"Backlog",children:"Backlog"}),e.jsx(i,{value:"In Progress",children:"In Progress"}),e.jsx(i,{value:"Done",children:"Done"}),e.jsx(i,{value:"Rejected",children:"Rejected"})]})]})]}),n?e.jsx("p",{className:"text-sm text-muted-foreground",children:"Loading…"}):R.length===0?e.jsxs("div",{className:"flex-1 flex flex-col items-center justify-center gap-2 text-center",children:[e.jsx(we,{className:"w-8 h-8 text-muted-foreground/40"}),e.jsx("p",{className:"text-sm font-medium",children:"No builds yet"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Upload the first build to get started."})]}):e.jsx("div",{className:"flex flex-col gap-2",children:R.map(({versionName:a,builds:v})=>e.jsx(Pe,{versionName:a,builds:v,isOpen:w.has(a),onToggle:()=>q(a),onNavigate:f=>s(`/app-center/build?id=${f}`),onStatusChange:H,onScheduleDeletion:Z,onCancelDeletion:G},a))})]}):e.jsxs("div",{className:"flex-1 flex flex-col items-center justify-center gap-2 text-center",children:[e.jsx(Se,{className:"w-8 h-8 text-muted-foreground/40"}),e.jsx("p",{className:"text-sm font-medium",children:"No app selected"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Choose an app from the sidebar to view its builds."})]})]})]})}export{Ue as AppCenter};
|
|
21
|
+
*/const De=[["path",{d:"M12 3v12",key:"1x0j5s"}],["path",{d:"m17 8-5-5-5 5",key:"7q97r8"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}]],Ce=C("upload",De);function Ae({onSuccess:s,appId:l}){const[h,c]=r.useState(!1),[t,g]=r.useState(null),[o,x]=r.useState("none"),j=r.useRef(null);function p(n){if(n.preventDefault(),!t)return;const d=new FormData;d.append("file",t),o!=="none"&&d.append("status",o),l&&d.append("app_id",String(l));const w=fetch("/api/v1/builds",{method:"POST",credentials:"include",body:d}).then(async u=>{if(!u.ok){const m=await u.json().catch(()=>({}));throw new Error(m.error??"Upload failed. Check the file format.")}s()});k.promise(w,{loading:"Uploading build…",success:"Build uploaded",error:u=>u instanceof Error?u.message:"Failed to upload build"}),c(!1),g(null),x("none")}function N(n){c(n),n||(g(null),x("none"))}return e.jsxs(O,{open:h,onOpenChange:N,children:[e.jsx(L,{asChild:!0,children:e.jsxs(y,{size:"sm",children:[e.jsx(Ce,{className:"mr-2 h-4 w-4"}),"Upload build"]})}),e.jsxs(U,{className:"sm:max-w-md",children:[e.jsx(E,{children:e.jsx(V,{children:"Upload build"})}),e.jsxs("form",{onSubmit:p,className:"flex flex-col gap-4 pt-2",children:[e.jsxs("div",{className:"grid gap-2",children:[e.jsx(b,{children:"File"}),e.jsx("div",{className:"flex h-24 cursor-pointer items-center justify-center rounded-md border-2 border-dashed text-sm text-muted-foreground hover:border-primary",onClick:()=>{var n;return(n=j.current)==null?void 0:n.click()},onDragOver:n=>n.preventDefault(),onDrop:n=>{n.preventDefault();const d=n.dataTransfer.files[0];d&&g(d)},children:t?t.name:"Click or drag to upload"}),e.jsx("input",{ref:j,type:"file",accept:".zip,.apk,.tgz,.tar.gz,application/gzip,application/x-gzip",className:"hidden",onChange:n=>{var d;return g(((d=n.target.files)==null?void 0:d[0])??null)}}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:["iOS: ",e.jsx("code",{children:".app.zip"})," or ",e.jsx("code",{children:".tar.gz"})," / ",e.jsx("code",{children:".tgz"})," (e.g. an EAS simulator build) · Android: ",e.jsx("code",{children:".apk"})]})]}),e.jsxs("div",{className:"grid gap-2",children:[e.jsxs(b,{children:["Status ",e.jsx("span",{className:"text-muted-foreground",children:"(optional)"})]}),e.jsxs(A,{value:o,onValueChange:x,children:[e.jsx(_,{children:e.jsx(B,{})}),e.jsxs(z,{children:[e.jsx(i,{value:"none",children:"None"}),e.jsx(i,{value:"Backlog",children:"Backlog"}),e.jsx(i,{value:"In Progress",children:"In Progress"}),e.jsx(i,{value:"Done",children:"Done"}),e.jsx(i,{value:"Rejected",children:"Rejected"})]})]})]}),e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsx(y,{type:"button",variant:"outline",className:"w-24",onClick:()=>c(!1),children:"Cancel"}),e.jsx(y,{type:"submit",className:"w-24",disabled:!t,children:"Upload"})]})]})]})]})}function D({className:s,...l}){return e.jsx("span",{className:J("font-mono text-xs tabular-nums tracking-tight",s),...l})}function _e({onSuccess:s}){const[l,h]=r.useState(!1),[c,t]=r.useState(""),[g,o]=r.useState(""),[x,j]=r.useState("ios"),[p,N]=r.useState(""),[n,d]=r.useState(!1);async function w(u){u.preventDefault(),N(""),d(!0);try{const m=await Y({name:c.trim(),bundle_id_key:g.trim(),platform:x});if(!m){N("Failed to create app");return}if("error"in m){N(m.error);return}h(!1),t(""),o(""),j("ios"),k.success("App created"),s()}catch{k.error("Failed to create app — check your network")}finally{d(!1)}}return e.jsxs(O,{open:l,onOpenChange:h,children:[e.jsx(L,{asChild:!0,children:e.jsxs("button",{className:"flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-left text-sm text-muted-foreground hover:bg-accent hover:text-foreground transition-colors",children:[e.jsx(ie,{className:"size-3.5 shrink-0"}),e.jsx("span",{children:"Add App"})]})}),e.jsxs(U,{className:"sm:max-w-sm",children:[e.jsx(E,{children:e.jsx(V,{children:"Add App"})}),e.jsxs("form",{onSubmit:w,className:"flex flex-col gap-4 pt-2",children:[e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(b,{htmlFor:"app-name",children:"Name"}),e.jsx(M,{id:"app-name",value:c,onChange:u=>t(u.target.value),placeholder:"My App",required:!0})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(b,{htmlFor:"bundle-id",children:"Bundle ID"}),e.jsx(M,{id:"bundle-id",value:g,onChange:u=>o(u.target.value),placeholder:"com.example.myapp",required:!0})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(b,{htmlFor:"platform",children:"Platform"}),e.jsxs(A,{value:x,onValueChange:u=>j(u),children:[e.jsx(_,{id:"platform",children:e.jsx(B,{})}),e.jsxs(z,{children:[e.jsx(i,{value:"ios",children:"iOS"}),e.jsx(i,{value:"android",children:"Android"}),e.jsx(i,{value:"both",children:"Both"})]})]})]}),p&&e.jsx("p",{className:"text-sm text-destructive",children:p}),e.jsx(y,{type:"submit",disabled:n,className:"w-full",children:n?"Creating…":"Create App"})]})]})]})}function Be({apps:s,selectedAppId:l,onSelect:h,onAdd:c}){return e.jsxs("aside",{className:"w-64 shrink-0 border-r flex flex-col gap-1 p-3 overflow-y-auto",children:[e.jsx("span",{className:"px-2 pb-1 font-mono text-xs font-medium text-muted-foreground uppercase tracking-wider",children:"Apps"}),s.length===0&&e.jsx("span",{className:"px-2 text-sm text-muted-foreground",children:"No apps yet"}),s.map(t=>e.jsxs("button",{onClick:()=>h(t.id),className:["flex flex-col gap-0.5 rounded-md px-2 py-2 text-left text-sm hover:bg-accent overflow-hidden",t.id===l?"bg-accent font-medium":""].join(" "),children:[e.jsx("span",{className:"truncate",children:t.name}),e.jsx(D,{className:"text-muted-foreground truncate",children:t.bundle_id_key})]},t.id)),e.jsx("div",{className:"mt-1 border-t pt-1",children:e.jsx(_e,{onSuccess:c})})]})}function ze(s){const l=/[zZ]|[+-]\d{2}:?\d{2}$/.test(s),c=new Date(l?s:s.replace(" ","T")+"Z").getTime()-Date.now();if(c<=0)return{label:"Deleting…",urgent:!0};const t=Math.floor(c/36e5);return t<1?{label:"Deletes in < 1h",urgent:!0}:t<24?{label:`Deletes in ${t}h`,urgent:t<6}:{label:`Deletes in ${Math.floor(t/24)}d`,urgent:!1}}function Ie({build:s,isLast:l,onNavigate:h,onStatusChange:c,onScheduleDeletion:t,onCancelDeletion:g}){const[o,x]=r.useState(!1),j=s.status_label==="Done",p=s.delete_after?ze(s.delete_after):null;function N(n){c(s.id,n==="none"?null:n)}return e.jsxs(e.Fragment,{children:[e.jsx(de,{open:o,onOpenChange:x,children:e.jsxs(ue,{children:[e.jsxs(he,{children:[e.jsx(xe,{children:"Schedule deletion?"}),e.jsx(pe,{children:"Build files will be deleted after 7 days. You can cancel the scheduled deletion anytime before then."})]}),e.jsxs(me,{children:[e.jsx(fe,{children:"Cancel"}),e.jsx(ge,{onClick:()=>{t(s.id),x(!1)},children:"Schedule deletion"})]})]})}),e.jsxs("div",{className:["flex items-center gap-3 px-4 py-3",l?"":"border-b"].join(" "),children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsxs("span",{className:"text-sm font-medium",children:["build ",e.jsx(D,{children:s.build_number??"—"})]}),e.jsx(T,{tone:s.platform==="ios"?"ios":"android",className:"text-xs capitalize",children:s.platform}),s.status_label&&e.jsx(T,{tone:K[s.status_label],className:"text-xs",children:s.status_label}),p&&e.jsx(T,{tone:p.urgent?"rejected":"backlog",className:"text-xs",children:p.label})]}),e.jsxs("div",{style:{display:"flex",flexDirection:"row",gap:"14px",marginTop:"0.7rem",alignItems:"center"},className:"text-xs text-muted-foreground",children:[s.uploader&&e.jsxs(e.Fragment,{children:[e.jsx("span",{children:s.uploader}),e.jsx(Q,{orientation:"vertical",className:"h-3"})]}),e.jsx(D,{children:new Date(s.uploaded_at).toLocaleDateString()})]})]}),e.jsxs(A,{value:s.status_label??"none",onValueChange:N,children:[e.jsx(_,{className:"h-9 w-32 text-xs",children:e.jsx(B,{})}),e.jsxs(z,{children:[e.jsx(i,{value:"none",children:e.jsx("span",{className:"text-muted-foreground",children:"—"})}),e.jsx(i,{value:"Backlog",children:"Backlog"}),e.jsx(i,{value:"In Progress",children:"In Progress"}),e.jsx(i,{value:"Done",children:"Done"}),e.jsx(i,{value:"Rejected",children:"Rejected"})]})]}),p?e.jsx(y,{size:"icon-sm",variant:"outline",onClick:()=>g(s.id),title:"Cancel scheduled deletion",children:e.jsx(ke,{className:"h-3.5 w-3.5"})}):e.jsx(y,{size:"icon-sm",variant:"destructive",onClick:()=>x(!0),title:"Schedule deletion",children:e.jsx(oe,{className:"h-3.5 w-3.5"})}),e.jsxs(y,{size:"sm",onClick:()=>h(s.id),disabled:j,children:[e.jsx(X,{className:"mr-1.5 h-3.5 w-3.5"}),"Start QA"]})]})]})}function Pe({versionName:s,builds:l,isOpen:h,onToggle:c,onNavigate:t,onStatusChange:g,onScheduleDeletion:o,onCancelDeletion:x}){return e.jsxs("div",{className:"rounded-lg shadow-card-2 overflow-hidden",children:[e.jsxs("button",{className:"flex w-full items-center gap-2 px-4 py-3 text-left hover:bg-accent/50 bg-card",onClick:c,children:[h?e.jsx(ce,{className:"h-4 w-4 shrink-0 text-muted-foreground"}):e.jsx(W,{className:"h-4 w-4 shrink-0 text-muted-foreground"}),e.jsx(D,{className:"font-medium text-sm text-foreground",children:s}),e.jsxs("span",{className:"text-xs text-muted-foreground",children:["(",l.length," build",l.length>1?"s":"",")"]})]}),h&&e.jsx("div",{className:"border-t bg-card",children:l.map((j,p)=>e.jsx(Ie,{build:j,isLast:p===l.length-1,onNavigate:t,onStatusChange:g,onScheduleDeletion:o,onCancelDeletion:x},j.id))})]})}function Ve(){const s=je(),[l,h]=ve(),[c,t]=r.useState([]),[g,o]=r.useState([]),[x,j]=r.useState(""),[p,N]=r.useState("all"),[n,d]=r.useState(!0),[w,u]=r.useState(new Set),m=l.get("appId")?Number(l.get("appId")):null,F=c.find(a=>a.id===m)??null,I=r.useCallback(async()=>{const a=await ee();t(a),!l.get("appId")&&a.length>0&&h({appId:String(a[0].id)},{replace:!0})},[l,h]),P=r.useCallback(async()=>{if(!m){o([]),d(!1);return}d(!0);try{const a=await se({appId:m,search:x,statusFilter:p});o(a),a.length>0&&w.size===0&&u(new Set([a[0].version_name??"Unversioned"]))}finally{d(!1)}},[m,x,p]);r.useEffect(()=>{I()},[]),r.useEffect(()=>{P()},[P]);function $(a){a!==m&&(o([]),u(new Set)),h({appId:String(a)})}function q(a){u(v=>{const f=new Set(v);return f.has(a)?f.delete(a):f.add(a),f})}async function H(a,v){await re(a,v),o(f=>f.map(S=>S.id===a?{...S,status_label:v}:S))}async function Z(a){try{const v=await ne(a);o(f=>f.map(S=>S.id===a?{...S,delete_after:v}:S))}catch{k.error("Failed to schedule deletion")}}async function G(a){try{await le(a),o(v=>v.map(f=>f.id===a?{...f,delete_after:null}:f))}catch{k.error("Failed to cancel scheduled deletion")}}const R=ae(g);return e.jsxs("div",{className:"flex h-full gap-0",children:[e.jsx(Be,{apps:c,selectedAppId:m,onSelect:$,onAdd:I}),e.jsxs("div",{className:"flex-1 flex flex-col gap-4 p-4 overflow-y-auto min-w-0",children:[e.jsxs("div",{className:"flex items-center justify-between flex-wrap gap-2",children:[e.jsx("h1",{className:"text-xl font-semibold tracking-display-sm",children:F?F.name:"App Center"}),e.jsx(Ae,{onSuccess:()=>{I(),P()},appId:m})]}),m?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx(te,{placeholder:"Search version…",value:x,onChange:j}),e.jsxs(A,{value:p,onValueChange:N,children:[e.jsx(_,{className:"h-8 w-36",children:e.jsx(B,{placeholder:"Status"})}),e.jsxs(z,{children:[e.jsx(i,{value:"all",children:"All statuses"}),e.jsx(i,{value:"Backlog",children:"Backlog"}),e.jsx(i,{value:"In Progress",children:"In Progress"}),e.jsx(i,{value:"Done",children:"Done"}),e.jsx(i,{value:"Rejected",children:"Rejected"})]})]})]}),n?e.jsx("p",{className:"text-sm text-muted-foreground",children:"Loading…"}):R.length===0?e.jsxs("div",{className:"flex-1 flex flex-col items-center justify-center gap-2 text-center",children:[e.jsx(we,{className:"w-8 h-8 text-muted-foreground/40"}),e.jsx("p",{className:"text-sm font-medium",children:"No builds yet"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Upload the first build to get started."})]}):e.jsx("div",{className:"flex flex-col gap-2",children:R.map(({versionName:a,builds:v})=>e.jsx(Pe,{versionName:a,builds:v,isOpen:w.has(a),onToggle:()=>q(a),onNavigate:f=>s(`/app-center/build?id=${f}`),onStatusChange:H,onScheduleDeletion:Z,onCancelDeletion:G},a))})]}):e.jsxs("div",{className:"flex-1 flex flex-col items-center justify-center gap-2 text-center",children:[e.jsx(Se,{className:"w-8 h-8 text-muted-foreground/40"}),e.jsx("p",{className:"text-sm font-medium",children:"No app selected"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Choose an app from the sidebar to view its builds."})]})]})]})}export{Ve as AppCenter};
|
|
Binary file
|