@linkshell/gateway 0.2.47 → 0.3.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 +14 -13
- package/dist/gateway/src/agent-permission-http.d.ts +10 -37
- package/dist/gateway/src/agent-permission-http.js +16 -56
- package/dist/gateway/src/agent-permission-http.js.map +1 -1
- package/dist/gateway/src/embedded.js +121 -57
- package/dist/gateway/src/embedded.js.map +1 -1
- package/dist/gateway/src/index.js +161 -94
- package/dist/gateway/src/index.js.map +1 -1
- package/dist/gateway/src/pairings.d.ts +3 -3
- package/dist/gateway/src/pairings.js +4 -5
- package/dist/gateway/src/pairings.js.map +1 -1
- package/dist/gateway/src/relay.d.ts +2 -2
- package/dist/gateway/src/relay.js +27 -38
- package/dist/gateway/src/relay.js.map +1 -1
- package/dist/gateway/src/sessions.d.ts +31 -28
- package/dist/gateway/src/sessions.js +163 -145
- package/dist/gateway/src/sessions.js.map +1 -1
- package/dist/gateway/src/state-store.d.ts +9 -6
- package/dist/gateway/src/state-store.js +26 -19
- package/dist/gateway/src/state-store.js.map +1 -1
- package/dist/gateway/src/tokens.d.ts +27 -7
- package/dist/gateway/src/tokens.js +86 -60
- package/dist/gateway/src/tokens.js.map +1 -1
- package/dist/gateway/src/tunnel.d.ts +11 -13
- package/dist/gateway/src/tunnel.js +36 -36
- package/dist/gateway/src/tunnel.js.map +1 -1
- package/dist/gateway/tsconfig.tsbuildinfo +1 -1
- package/dist/shared-protocol/src/index.d.ts +3961 -5788
- package/dist/shared-protocol/src/index.js +19 -84
- package/dist/shared-protocol/src/index.js.map +1 -1
- package/package.json +10 -10
- package/src/agent-permission-http.ts +20 -63
- package/src/embedded.ts +124 -56
- package/src/index.ts +165 -94
- package/src/pairings.ts +6 -7
- package/src/relay.ts +38 -48
- package/src/sessions.ts +174 -150
- package/src/state-store.ts +41 -25
- package/src/tokens.ts +109 -63
- package/src/tunnel.ts +49 -43
package/src/relay.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "@linkshell/protocol";
|
|
9
9
|
import type { Envelope, ProtocolMessageType } from "@linkshell/protocol";
|
|
10
10
|
import { ZodError } from "zod";
|
|
11
|
-
import type {
|
|
11
|
+
import type { DeviceManager, ConnectedDevice } from "./sessions.js";
|
|
12
12
|
import {
|
|
13
13
|
handleTunnelResponse,
|
|
14
14
|
handleTunnelWsData,
|
|
@@ -20,31 +20,31 @@ export function handleSocketMessage(
|
|
|
20
20
|
socket: WebSocket,
|
|
21
21
|
raw: string,
|
|
22
22
|
role: "host" | "client",
|
|
23
|
-
|
|
23
|
+
hostDeviceId: string,
|
|
24
24
|
deviceId: string,
|
|
25
|
-
sessions:
|
|
25
|
+
sessions: DeviceManager,
|
|
26
26
|
): void {
|
|
27
27
|
let envelope: Envelope;
|
|
28
28
|
try {
|
|
29
29
|
envelope = parseEnvelope(raw);
|
|
30
30
|
} catch {
|
|
31
|
-
sendSessionError(socket,
|
|
31
|
+
sendSessionError(socket, hostDeviceId, "invalid_message", "Failed to parse envelope");
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
if (envelope.
|
|
35
|
+
if (envelope.hostDeviceId !== hostDeviceId) {
|
|
36
36
|
sendSessionError(
|
|
37
37
|
socket,
|
|
38
|
-
|
|
38
|
+
hostDeviceId,
|
|
39
39
|
"invalid_message",
|
|
40
|
-
"Envelope
|
|
40
|
+
"Envelope hostDeviceId does not match connection hostDeviceId",
|
|
41
41
|
);
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
const session = sessions.get(
|
|
45
|
+
const session = sessions.get(hostDeviceId);
|
|
46
46
|
if (!session) {
|
|
47
|
-
sendSessionError(socket,
|
|
47
|
+
sendSessionError(socket, hostDeviceId, "device_not_found", "Device not found");
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -65,13 +65,13 @@ export function handleSocketMessage(
|
|
|
65
65
|
if (error instanceof ZodError) {
|
|
66
66
|
sendSessionError(
|
|
67
67
|
socket,
|
|
68
|
-
|
|
68
|
+
hostDeviceId,
|
|
69
69
|
"invalid_message",
|
|
70
70
|
error.errors[0]?.message ?? "Invalid message payload",
|
|
71
71
|
);
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
|
-
sendSessionError(socket,
|
|
74
|
+
sendSessionError(socket, hostDeviceId, "invalid_message", "Failed to handle message");
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -81,7 +81,7 @@ function isProtocolMessageType(type: string): type is ProtocolMessageType {
|
|
|
81
81
|
|
|
82
82
|
function sendSessionError(
|
|
83
83
|
socket: WebSocket,
|
|
84
|
-
|
|
84
|
+
hostDeviceId: string,
|
|
85
85
|
code: string,
|
|
86
86
|
message: string,
|
|
87
87
|
): void {
|
|
@@ -89,8 +89,8 @@ function sendSessionError(
|
|
|
89
89
|
socket.send(
|
|
90
90
|
serializeEnvelope(
|
|
91
91
|
createEnvelope({
|
|
92
|
-
type: "
|
|
93
|
-
|
|
92
|
+
type: "device.error",
|
|
93
|
+
hostDeviceId,
|
|
94
94
|
payload: { code, message },
|
|
95
95
|
}),
|
|
96
96
|
),
|
|
@@ -99,22 +99,23 @@ function sendSessionError(
|
|
|
99
99
|
|
|
100
100
|
function handleHostMessage(
|
|
101
101
|
envelope: Envelope,
|
|
102
|
-
session: ReturnType<
|
|
103
|
-
sessions:
|
|
102
|
+
session: ReturnType<DeviceManager["get"]> & {},
|
|
103
|
+
sessions: DeviceManager,
|
|
104
104
|
): void {
|
|
105
105
|
switch (envelope.type) {
|
|
106
|
-
case "
|
|
106
|
+
case "device.connect": {
|
|
107
107
|
// Extract metadata from host's connect message
|
|
108
|
-
const p = parseTypedPayload("
|
|
109
|
-
if (p.
|
|
108
|
+
const p = parseTypedPayload("device.connect", envelope.payload);
|
|
109
|
+
if (p.machineId || p.hostname || p.platform || p.cwd || p.capabilities) {
|
|
110
110
|
sessions.setMetadata(
|
|
111
111
|
session.id,
|
|
112
|
-
|
|
112
|
+
undefined,
|
|
113
113
|
p.machineId ?? undefined,
|
|
114
114
|
p.hostname ?? undefined,
|
|
115
115
|
p.platform ?? undefined,
|
|
116
116
|
p.cwd ?? undefined,
|
|
117
|
-
|
|
117
|
+
undefined,
|
|
118
|
+
p.capabilities ?? undefined,
|
|
118
119
|
);
|
|
119
120
|
}
|
|
120
121
|
break;
|
|
@@ -129,12 +130,12 @@ function handleHostMessage(
|
|
|
129
130
|
broadcastToClients(session, envelope);
|
|
130
131
|
break;
|
|
131
132
|
}
|
|
132
|
-
case "
|
|
133
|
+
case "device.heartbeat":
|
|
133
134
|
break;
|
|
134
135
|
case "permission.decision.result": {
|
|
135
136
|
const p = parseTypedPayload("permission.decision.result", envelope.payload);
|
|
136
137
|
resolveAgentPermissionHttpAck({
|
|
137
|
-
|
|
138
|
+
hostDeviceId: session.id,
|
|
138
139
|
ack: {
|
|
139
140
|
requestId: p.requestId,
|
|
140
141
|
decision: p.decision,
|
|
@@ -172,11 +173,7 @@ function handleHostMessage(
|
|
|
172
173
|
case "screen.status":
|
|
173
174
|
case "screen.offer":
|
|
174
175
|
case "screen.ice":
|
|
175
|
-
// Agent
|
|
176
|
-
case "agent.capabilities":
|
|
177
|
-
case "agent.update":
|
|
178
|
-
case "agent.permission.request":
|
|
179
|
-
case "agent.snapshot":
|
|
176
|
+
// Agent Workspace: host → clients
|
|
180
177
|
case "agent.v2.capabilities":
|
|
181
178
|
case "agent.v2.conversation.opened":
|
|
182
179
|
case "agent.v2.conversation.list.result":
|
|
@@ -204,17 +201,17 @@ function handleHostMessage(
|
|
|
204
201
|
function handleClientMessage(
|
|
205
202
|
envelope: Envelope,
|
|
206
203
|
socket: WebSocket,
|
|
207
|
-
session: ReturnType<
|
|
204
|
+
session: ReturnType<DeviceManager["get"]> & {},
|
|
208
205
|
deviceId: string,
|
|
209
|
-
sessions:
|
|
206
|
+
sessions: DeviceManager,
|
|
210
207
|
): void {
|
|
211
208
|
const requireController = (): boolean => {
|
|
212
209
|
if (session.controllerId === deviceId) return true;
|
|
213
210
|
socket.send(
|
|
214
211
|
serializeEnvelope(
|
|
215
212
|
createEnvelope({
|
|
216
|
-
type: "
|
|
217
|
-
|
|
213
|
+
type: "device.error",
|
|
214
|
+
hostDeviceId: session.id,
|
|
218
215
|
payload: {
|
|
219
216
|
code: "control_conflict",
|
|
220
217
|
message: "Not the controller",
|
|
@@ -236,13 +233,13 @@ function handleClientMessage(
|
|
|
236
233
|
sendToHost(session, envelope);
|
|
237
234
|
break;
|
|
238
235
|
}
|
|
239
|
-
case "
|
|
236
|
+
case "device.ack": {
|
|
240
237
|
// Forward ACK to host
|
|
241
238
|
sendToHost(session, envelope);
|
|
242
239
|
break;
|
|
243
240
|
}
|
|
244
|
-
case "
|
|
245
|
-
const p = parseTypedPayload("
|
|
241
|
+
case "device.resume": {
|
|
242
|
+
const p = parseTypedPayload("device.resume", envelope.payload);
|
|
246
243
|
// Replay from gateway buffer first
|
|
247
244
|
const replay = sessions.getReplayFrom(
|
|
248
245
|
session.id,
|
|
@@ -255,7 +252,7 @@ function handleClientMessage(
|
|
|
255
252
|
serializeEnvelope(
|
|
256
253
|
createEnvelope({
|
|
257
254
|
type: "terminal.output",
|
|
258
|
-
|
|
255
|
+
hostDeviceId: session.id,
|
|
259
256
|
terminalId: msg.terminalId,
|
|
260
257
|
seq: msg.seq,
|
|
261
258
|
payload: { ...payload, isReplay: true },
|
|
@@ -284,7 +281,7 @@ function handleClientMessage(
|
|
|
284
281
|
sessions.claimControl(session.id, deviceId);
|
|
285
282
|
const grantMsg = createEnvelope({
|
|
286
283
|
type: "control.grant",
|
|
287
|
-
|
|
284
|
+
hostDeviceId: session.id,
|
|
288
285
|
payload: { deviceId },
|
|
289
286
|
});
|
|
290
287
|
// Broadcast to ALL clients so previous controller updates its state
|
|
@@ -296,25 +293,20 @@ function handleClientMessage(
|
|
|
296
293
|
sessions.releaseControl(session.id, deviceId);
|
|
297
294
|
const releaseMsg = createEnvelope({
|
|
298
295
|
type: "control.release",
|
|
299
|
-
|
|
296
|
+
hostDeviceId: session.id,
|
|
300
297
|
payload: { deviceId },
|
|
301
298
|
});
|
|
302
299
|
broadcastToClients(session, releaseMsg);
|
|
303
300
|
sendToHost(session, releaseMsg);
|
|
304
301
|
break;
|
|
305
302
|
}
|
|
306
|
-
case "
|
|
303
|
+
case "device.heartbeat":
|
|
307
304
|
break;
|
|
308
305
|
// Screen sharing: client → host
|
|
309
306
|
case "screen.start":
|
|
310
307
|
case "screen.stop":
|
|
311
308
|
case "screen.answer":
|
|
312
309
|
case "screen.ice":
|
|
313
|
-
case "agent.session.new":
|
|
314
|
-
case "agent.session.load":
|
|
315
|
-
case "agent.prompt":
|
|
316
|
-
case "agent.cancel":
|
|
317
|
-
case "agent.permission.response":
|
|
318
310
|
case "agent.v2.conversation.open":
|
|
319
311
|
case "agent.v2.prompt":
|
|
320
312
|
case "agent.v2.command.execute":
|
|
@@ -334,8 +326,6 @@ function handleClientMessage(
|
|
|
334
326
|
if (!requireController()) return;
|
|
335
327
|
sendToHost(session, envelope);
|
|
336
328
|
break;
|
|
337
|
-
case "agent.initialize":
|
|
338
|
-
case "agent.session.list":
|
|
339
329
|
case "agent.v2.capabilities.request":
|
|
340
330
|
case "agent.v2.conversation.list":
|
|
341
331
|
case "agent.v2.snapshot.request":
|
|
@@ -348,7 +338,7 @@ function handleClientMessage(
|
|
|
348
338
|
}
|
|
349
339
|
|
|
350
340
|
function broadcastToClients(
|
|
351
|
-
session: ReturnType<
|
|
341
|
+
session: ReturnType<DeviceManager["get"]> & {},
|
|
352
342
|
envelope: Envelope,
|
|
353
343
|
): void {
|
|
354
344
|
const data = serializeEnvelope(envelope);
|
|
@@ -360,7 +350,7 @@ function broadcastToClients(
|
|
|
360
350
|
}
|
|
361
351
|
|
|
362
352
|
function sendToHost(
|
|
363
|
-
session: ReturnType<
|
|
353
|
+
session: ReturnType<DeviceManager["get"]> & {},
|
|
364
354
|
envelope: Envelope,
|
|
365
355
|
): void {
|
|
366
356
|
if (
|