@linkshell/gateway 0.5.3 → 0.6.1

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.
Files changed (109) hide show
  1. package/Dockerfile +8 -22
  2. package/README.md +102 -44
  3. package/dist/gateway/src/accounts.js.map +1 -0
  4. package/dist/gateway/src/index.d.ts +4 -1
  5. package/dist/gateway/src/index.js +4 -863
  6. package/dist/gateway/src/index.js.map +1 -1
  7. package/dist/gateway/src/main.js +57 -0
  8. package/dist/gateway/src/main.js.map +1 -0
  9. package/dist/gateway/src/rate-limit.d.ts +32 -0
  10. package/dist/gateway/src/rate-limit.js +106 -0
  11. package/dist/gateway/src/rate-limit.js.map +1 -0
  12. package/dist/gateway/src/relay.d.ts +84 -5
  13. package/dist/gateway/src/relay.js +402 -529
  14. package/dist/gateway/src/relay.js.map +1 -1
  15. package/dist/gateway/src/serve.d.ts +34 -0
  16. package/dist/gateway/src/serve.js +115 -0
  17. package/dist/gateway/src/serve.js.map +1 -0
  18. package/dist/{gateway-v2 → gateway}/src/store.d.ts +5 -0
  19. package/dist/{gateway-v2 → gateway}/src/store.js +7 -0
  20. package/dist/gateway/src/store.js.map +1 -0
  21. package/dist/gateway/src/subscription.d.ts +18 -0
  22. package/dist/gateway/src/subscription.js +46 -0
  23. package/dist/gateway/src/subscription.js.map +1 -0
  24. package/dist/gateway/tsconfig.tsbuildinfo +1 -1
  25. package/dist/wire/src/direct.d.ts +22 -0
  26. package/dist/wire/src/direct.js +37 -0
  27. package/dist/wire/src/direct.js.map +1 -0
  28. package/dist/wire/src/index.d.ts +1 -0
  29. package/dist/wire/src/index.js +1 -0
  30. package/dist/wire/src/index.js.map +1 -1
  31. package/dist/wire/src/model.d.ts +287 -6
  32. package/dist/wire/src/model.js +33 -0
  33. package/dist/wire/src/model.js.map +1 -1
  34. package/dist/wire/src/relay.d.ts +8 -8
  35. package/dist/wire/src/rpc.d.ts +1780 -44
  36. package/dist/wire/src/rpc.js +48 -3
  37. package/dist/wire/src/rpc.js.map +1 -1
  38. package/dist/wire/src/updates.d.ts +356 -0
  39. package/dist/wire/src/updates.js +11 -1
  40. package/dist/wire/src/updates.js.map +1 -1
  41. package/package.json +13 -13
  42. package/src/accounts.ts +32 -0
  43. package/src/index.ts +4 -997
  44. package/src/main.ts +65 -0
  45. package/src/rate-limit.ts +114 -0
  46. package/src/relay.ts +435 -620
  47. package/src/serve.ts +142 -0
  48. package/src/store.ts +135 -0
  49. package/src/subscription.ts +51 -0
  50. package/dist/gateway/src/auth-middleware.d.ts +0 -44
  51. package/dist/gateway/src/auth-middleware.js +0 -204
  52. package/dist/gateway/src/auth-middleware.js.map +0 -1
  53. package/dist/gateway/src/cors.d.ts +0 -2
  54. package/dist/gateway/src/cors.js +0 -7
  55. package/dist/gateway/src/cors.js.map +0 -1
  56. package/dist/gateway/src/embedded.d.ts +0 -21
  57. package/dist/gateway/src/embedded.js +0 -438
  58. package/dist/gateway/src/embedded.js.map +0 -1
  59. package/dist/gateway/src/host-auth.d.ts +0 -32
  60. package/dist/gateway/src/host-auth.js +0 -78
  61. package/dist/gateway/src/host-auth.js.map +0 -1
  62. package/dist/gateway/src/pairings.d.ts +0 -38
  63. package/dist/gateway/src/pairings.js +0 -119
  64. package/dist/gateway/src/pairings.js.map +0 -1
  65. package/dist/gateway/src/presence.d.ts +0 -28
  66. package/dist/gateway/src/presence.js +0 -92
  67. package/dist/gateway/src/presence.js.map +0 -1
  68. package/dist/gateway/src/sessions.d.ts +0 -113
  69. package/dist/gateway/src/sessions.js +0 -352
  70. package/dist/gateway/src/sessions.js.map +0 -1
  71. package/dist/gateway/src/state-store.d.ts +0 -21
  72. package/dist/gateway/src/state-store.js +0 -103
  73. package/dist/gateway/src/state-store.js.map +0 -1
  74. package/dist/gateway/src/static-web.d.ts +0 -15
  75. package/dist/gateway/src/static-web.js +0 -129
  76. package/dist/gateway/src/static-web.js.map +0 -1
  77. package/dist/gateway/src/tokens.d.ts +0 -19
  78. package/dist/gateway/src/tokens.js +0 -151
  79. package/dist/gateway/src/tokens.js.map +0 -1
  80. package/dist/gateway/src/tunnel.d.ts +0 -61
  81. package/dist/gateway/src/tunnel.js +0 -576
  82. package/dist/gateway/src/tunnel.js.map +0 -1
  83. package/dist/gateway-v2/src/accounts.js.map +0 -1
  84. package/dist/gateway-v2/src/index.d.ts +0 -3
  85. package/dist/gateway-v2/src/index.js +0 -4
  86. package/dist/gateway-v2/src/index.js.map +0 -1
  87. package/dist/gateway-v2/src/main.js +0 -17
  88. package/dist/gateway-v2/src/main.js.map +0 -1
  89. package/dist/gateway-v2/src/server.d.ts +0 -84
  90. package/dist/gateway-v2/src/server.js +0 -426
  91. package/dist/gateway-v2/src/server.js.map +0 -1
  92. package/dist/gateway-v2/src/store.js.map +0 -1
  93. package/dist/shared-protocol/src/index.d.ts +0 -17029
  94. package/dist/shared-protocol/src/index.js +0 -1064
  95. package/dist/shared-protocol/src/index.js.map +0 -1
  96. package/src/auth-middleware.ts +0 -266
  97. package/src/cors.ts +0 -8
  98. package/src/embedded.ts +0 -526
  99. package/src/host-auth.ts +0 -88
  100. package/src/pairings.ts +0 -133
  101. package/src/presence.ts +0 -118
  102. package/src/sessions.ts +0 -432
  103. package/src/state-store.ts +0 -140
  104. package/src/static-web.ts +0 -133
  105. package/src/tokens.ts +0 -159
  106. package/src/tunnel.ts +0 -644
  107. /package/dist/{gateway-v2 → gateway}/src/accounts.d.ts +0 -0
  108. /package/dist/{gateway-v2 → gateway}/src/accounts.js +0 -0
  109. /package/dist/{gateway-v2 → gateway}/src/main.d.ts +0 -0
@@ -1,119 +0,0 @@
1
- import { randomInt, randomUUID } from "node:crypto";
2
- const PAIRING_TTL = Number(process.env.PAIRING_TTL_MS ?? 10 * 60_000); // 10 minutes
3
- const CLEANUP_INTERVAL = 60_000;
4
- // Invalidate a code after this many failed claim attempts to cap brute-forcing.
5
- const MAX_FAILED_CLAIM_ATTEMPTS = Number(process.env.PAIRING_MAX_FAILED_ATTEMPTS ?? 5);
6
- export class PairingManager {
7
- store;
8
- pairings = new Map();
9
- cleanupTimer;
10
- constructor(store) {
11
- this.store = store;
12
- this.cleanupTimer = setInterval(() => this.cleanup(), CLEANUP_INTERVAL);
13
- }
14
- async hydrate() {
15
- if (!this.store)
16
- return;
17
- try {
18
- const records = await this.store.loadPairings();
19
- const now = Date.now();
20
- for (const record of records) {
21
- if (record.expiresAt < now) {
22
- void this.store.deletePairing(record.pairingCode).catch(() => { });
23
- continue;
24
- }
25
- // Stored records predate the failedAttempts field; default it.
26
- this.pairings.set(record.pairingCode, { ...record, failedAttempts: 0 });
27
- }
28
- }
29
- catch (err) {
30
- process.stderr.write(`[gateway] pairing store hydrate failed, using memory only: ${err}\n`);
31
- }
32
- }
33
- create(sessionId) {
34
- const id = sessionId ?? randomUUID();
35
- const code = String(randomInt(100000, 999999));
36
- const record = {
37
- sessionId: id,
38
- pairingCode: code,
39
- expiresAt: Date.now() + PAIRING_TTL,
40
- claimed: false,
41
- failedAttempts: 0,
42
- };
43
- this.pairings.set(code, record);
44
- this.persist(record);
45
- return record;
46
- }
47
- claim(pairingCode) {
48
- const record = this.pairings.get(pairingCode);
49
- if (!record) {
50
- return { error: "pairing_not_found", status: 404 };
51
- }
52
- if (record.expiresAt < Date.now()) {
53
- this.pairings.delete(pairingCode);
54
- void this.store?.deletePairing(pairingCode).catch(() => { });
55
- return { error: "pairing_expired", status: 410 };
56
- }
57
- if (record.claimed) {
58
- // Count repeated claim attempts on an existing code; once the cap is
59
- // exceeded, invalidate it so it can no longer be targeted.
60
- record.failedAttempts += 1;
61
- if (record.failedAttempts >= MAX_FAILED_CLAIM_ATTEMPTS) {
62
- this.pairings.delete(pairingCode);
63
- void this.store?.deletePairing(pairingCode).catch(() => { });
64
- }
65
- else {
66
- this.persist(record);
67
- }
68
- return { error: "pairing_already_claimed", status: 409 };
69
- }
70
- record.claimed = true;
71
- this.persist(record);
72
- return record;
73
- }
74
- /** Look up a code's session without consuming an attempt or mutating state.
75
- * Lets the claim endpoint stay idempotent for a device that already owns the
76
- * mapped session (it re-issues instead of erroring with already_claimed). */
77
- peek(pairingCode) {
78
- const record = this.pairings.get(pairingCode);
79
- if (!record)
80
- return null;
81
- if (record.expiresAt < Date.now())
82
- return null;
83
- return { sessionId: record.sessionId, claimed: record.claimed };
84
- }
85
- getStatus(pairingCode) {
86
- const record = this.pairings.get(pairingCode);
87
- if (!record) {
88
- return { error: "pairing_not_found", httpStatus: 404 };
89
- }
90
- if (record.expiresAt < Date.now()) {
91
- this.pairings.delete(pairingCode);
92
- void this.store?.deletePairing(pairingCode).catch(() => { });
93
- return { error: "pairing_expired", httpStatus: 410 };
94
- }
95
- return {
96
- status: record.claimed ? "claimed" : "waiting",
97
- expiresAt: record.expiresAt,
98
- sessionId: record.sessionId,
99
- };
100
- }
101
- cleanup() {
102
- const now = Date.now();
103
- for (const [code, record] of this.pairings) {
104
- if (record.expiresAt < now) {
105
- this.pairings.delete(code);
106
- void this.store?.deletePairing(code).catch(() => { });
107
- }
108
- }
109
- }
110
- persist(record) {
111
- void this.store?.savePairing(record).catch((err) => {
112
- process.stderr.write(`[gateway] pairing store save failed: ${err}\n`);
113
- });
114
- }
115
- destroy() {
116
- clearInterval(this.cleanupTimer);
117
- }
118
- }
119
- //# sourceMappingURL=pairings.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pairings.js","sourceRoot":"","sources":["../../../src/pairings.ts"],"names":[],"mappings":"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"}
@@ -1,28 +0,0 @@
1
- import type WebSocket from "ws";
2
- import type { Session, SessionManager } from "./sessions.js";
3
- interface Watcher {
4
- socket: WebSocket;
5
- /** Session ids this watcher is allowed to see. Re-evaluated per notify
6
- * so newly-bound tokens pick up sessions without reconnecting. */
7
- sessionIds: () => Set<string>;
8
- userId?: string;
9
- }
10
- /** Cross-session presence fan-out for the web session list. */
11
- export declare class PresenceHub {
12
- private watchers;
13
- add(watcher: Watcher): void;
14
- /** Send the current summary for every visible session (connect snapshot). */
15
- snapshot(watcher: Watcher, sessions: SessionManager): void;
16
- notify(session: Session): void;
17
- private visible;
18
- private send;
19
- }
20
- export declare function startPresenceWatcher(input: {
21
- socket: WebSocket;
22
- sessions: SessionManager;
23
- hub: PresenceHub;
24
- sessionIds: () => Set<string>;
25
- userId?: string;
26
- pingIntervalMs?: number;
27
- }): void;
28
- export {};
@@ -1,92 +0,0 @@
1
- import { createEnvelope, serializeEnvelope, PROTOCOL_VERSION } from "@linkshell/protocol";
2
- /** Cross-session presence fan-out for the web session list. */
3
- export class PresenceHub {
4
- watchers = new Set();
5
- add(watcher) {
6
- this.watchers.add(watcher);
7
- watcher.socket.on("close", () => this.watchers.delete(watcher));
8
- watcher.socket.on("error", () => this.watchers.delete(watcher));
9
- }
10
- /** Send the current summary for every visible session (connect snapshot). */
11
- snapshot(watcher, sessions) {
12
- const allowed = watcher.sessionIds();
13
- for (const session of sessions.listActive()) {
14
- if (!this.visible(watcher, session, allowed))
15
- continue;
16
- this.send(watcher.socket, session);
17
- }
18
- }
19
- notify(session) {
20
- if (this.watchers.size === 0)
21
- return;
22
- for (const watcher of this.watchers) {
23
- if (watcher.socket.readyState !== watcher.socket.OPEN)
24
- continue;
25
- if (!this.visible(watcher, session, watcher.sessionIds()))
26
- continue;
27
- this.send(watcher.socket, session);
28
- }
29
- }
30
- visible(watcher, session, allowed) {
31
- if (allowed.has(session.id))
32
- return true;
33
- if (watcher.userId && session.userId && watcher.userId === session.userId)
34
- return true;
35
- return false;
36
- }
37
- send(socket, session) {
38
- if (socket.readyState !== socket.OPEN)
39
- return;
40
- socket.send(serializeEnvelope(createEnvelope({
41
- type: "session.presence",
42
- sessionId: session.id,
43
- payload: {
44
- hasHost: !!session.host &&
45
- session.host.socket.readyState === session.host.socket.OPEN,
46
- agentStatus: session.agentStatus ?? null,
47
- agentProvider: session.agentProvider ?? null,
48
- agentConversationId: session.agentConversationId ?? null,
49
- agentTitle: session.agentTitle ?? null,
50
- agentDetail: session.agentDetail ?? null,
51
- agentLastActivity: session.agentLastActivity ?? null,
52
- lastActivity: session.lastActivity,
53
- agentUsage: session.agentUsage ?? null,
54
- },
55
- })));
56
- }
57
- }
58
- export function startPresenceWatcher(input) {
59
- const { socket, sessions, hub, sessionIds, userId, pingIntervalMs = 20_000 } = input;
60
- const watcher = { socket, sessionIds, userId };
61
- hub.add(watcher);
62
- socket.send(serializeEnvelope(createEnvelope({
63
- type: "session.connect",
64
- sessionId: "_presence",
65
- payload: {
66
- role: "client",
67
- clientName: "watcher",
68
- protocolVersion: PROTOCOL_VERSION,
69
- },
70
- })));
71
- hub.snapshot(watcher, sessions);
72
- const liveSocket = socket;
73
- liveSocket.isAlive = true;
74
- socket.on("pong", () => {
75
- liveSocket.isAlive = true;
76
- });
77
- socket.on("message", () => {
78
- liveSocket.isAlive = true;
79
- });
80
- const pingTimer = setInterval(() => {
81
- if (socket.readyState !== socket.OPEN)
82
- return;
83
- if (liveSocket.isAlive === false) {
84
- socket.terminate();
85
- return;
86
- }
87
- liveSocket.isAlive = false;
88
- socket.ping();
89
- }, pingIntervalMs);
90
- socket.on("close", () => clearInterval(pingTimer));
91
- }
92
- //# sourceMappingURL=presence.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"presence.js","sourceRoot":"","sources":["../../../src/presence.ts"],"names":[],"mappings":"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"}
@@ -1,113 +0,0 @@
1
- import type WebSocket from "ws";
2
- import type { Envelope } from "@linkshell/protocol";
3
- export type SessionState = "active" | "host_disconnected" | "terminated";
4
- export interface ConnectedDevice {
5
- socket: WebSocket;
6
- role: "host" | "client";
7
- deviceId: string;
8
- connectedAt: number;
9
- }
10
- export interface Session {
11
- id: string;
12
- state: SessionState;
13
- host: ConnectedDevice | undefined;
14
- clients: Map<string, ConnectedDevice>;
15
- controllerId: string | undefined;
16
- lastActivity: number;
17
- createdAt: number;
18
- outputBuffers: Map<string, Envelope[]>;
19
- outputBufferBytes: Map<string, number>;
20
- lastStatusByTerminal: Map<string, Envelope>;
21
- hostDisconnectedAt: number | undefined;
22
- provider: string | undefined;
23
- machineId: string | undefined;
24
- hostname: string | undefined;
25
- platform: string | undefined;
26
- cwd: string | undefined;
27
- projectName: string | undefined;
28
- userId: string | undefined;
29
- agentStatus: string | undefined;
30
- agentProvider: string | undefined;
31
- agentConversationId: string | undefined;
32
- agentTitle: string | undefined;
33
- agentDetail: string | undefined;
34
- agentLastActivity: number | undefined;
35
- agentUsage: AgentUsageSummary | undefined;
36
- agentUsageReport: unknown | undefined;
37
- lastAgentSnapshot: Envelope | undefined;
38
- agentReplay: Envelope[];
39
- }
40
- export interface AgentUsageSummary {
41
- inputTokens?: number;
42
- outputTokens?: number;
43
- cacheReadTokens?: number;
44
- totalTokens?: number;
45
- contextWindow?: number;
46
- totalCostUsd?: number;
47
- }
48
- export declare class SessionManager {
49
- private sessions;
50
- private cleanupTimer;
51
- /** Fired when host liveness or visible agent summary fields change. */
52
- onPresenceChange?: (session: Session) => void;
53
- constructor();
54
- private emitPresence;
55
- getOrCreate(sessionId: string): Session;
56
- get(sessionId: string): Session | undefined;
57
- setHost(sessionId: string, device: ConnectedDevice): void;
58
- addClient(sessionId: string, device: ConnectedDevice): void;
59
- removeHost(sessionId: string): {
60
- clients: Map<string, ConnectedDevice>;
61
- } | undefined;
62
- removeClient(sessionId: string, deviceId: string): void;
63
- /** Force-delete a session: disconnect host + all clients, remove from map. */
64
- forceDelete(sessionId: string): boolean;
65
- bufferOutput(sessionId: string, envelope: Envelope): void;
66
- cacheStatus(sessionId: string, envelope: Envelope): void;
67
- getStatusReplay(sessionId: string): Envelope[];
68
- getReplayFrom(sessionId: string, afterSeqByTerminal: Record<string, number>, fallbackAfterSeq?: number): Envelope[];
69
- claimControl(sessionId: string, deviceId: string): boolean;
70
- releaseControl(sessionId: string, deviceId: string): boolean;
71
- terminate(sessionId: string): void;
72
- listActive(): Session[];
73
- getSummary(sessionId: string): {
74
- id: string;
75
- state: SessionState;
76
- hasHost: boolean;
77
- clientCount: number;
78
- controllerId: string | null;
79
- lastActivity: number;
80
- createdAt: number;
81
- bufferSize: number;
82
- provider: string | null;
83
- machineId: string | null;
84
- hostname: string | null;
85
- platform: string | null;
86
- cwd: string | null;
87
- projectName: string | null;
88
- userId: string | null;
89
- agentStatus: string | null;
90
- agentProvider: string | null;
91
- agentConversationId: string | null;
92
- agentTitle: string | null;
93
- agentDetail: string | null;
94
- agentLastActivity: number | null;
95
- agentUsage: AgentUsageSummary | null;
96
- agentUsageReport: {} | null;
97
- } | undefined;
98
- cacheAgentSummary(sessionId: string, summary: {
99
- status?: string;
100
- provider?: string | null;
101
- conversationId?: string | null;
102
- title?: string | null;
103
- detail?: string | null;
104
- lastActivity?: number | null;
105
- usage?: AgentUsageSummary | null;
106
- }): boolean;
107
- bufferAgentEnvelope(sessionId: string, envelope: Envelope): void;
108
- getAgentReplay(sessionId: string): Envelope[];
109
- setMetadata(sessionId: string, provider?: string, machineId?: string, hostname?: string, platform?: string, cwd?: string, projectName?: string): void;
110
- private maybeDelete;
111
- private cleanup;
112
- destroy(): void;
113
- }