@linkshell/gateway 0.5.2 → 0.6.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/Dockerfile +8 -22
- package/README.md +99 -44
- package/dist/gateway/src/accounts.js.map +1 -0
- package/dist/gateway/src/index.d.ts +4 -1
- package/dist/gateway/src/index.js +4 -863
- package/dist/gateway/src/index.js.map +1 -1
- package/dist/gateway/src/main.js +57 -0
- package/dist/gateway/src/main.js.map +1 -0
- package/dist/gateway/src/rate-limit.d.ts +22 -0
- package/dist/gateway/src/rate-limit.js +66 -0
- package/dist/gateway/src/rate-limit.js.map +1 -0
- package/dist/gateway/src/relay.d.ts +84 -5
- package/dist/gateway/src/relay.js +402 -529
- package/dist/gateway/src/relay.js.map +1 -1
- package/dist/gateway/src/serve.d.ts +34 -0
- package/dist/gateway/src/serve.js +102 -0
- package/dist/gateway/src/serve.js.map +1 -0
- package/dist/{gateway-v2 → gateway}/src/store.d.ts +5 -0
- package/dist/{gateway-v2 → gateway}/src/store.js +7 -0
- package/dist/gateway/src/store.js.map +1 -0
- package/dist/gateway/src/subscription.d.ts +18 -0
- package/dist/gateway/src/subscription.js +46 -0
- package/dist/gateway/src/subscription.js.map +1 -0
- package/dist/gateway/tsconfig.tsbuildinfo +1 -1
- package/dist/wire/src/direct.d.ts +22 -0
- package/dist/wire/src/direct.js +37 -0
- package/dist/wire/src/direct.js.map +1 -0
- package/dist/wire/src/index.d.ts +1 -0
- package/dist/wire/src/index.js +1 -0
- package/dist/wire/src/index.js.map +1 -1
- package/dist/wire/src/model.d.ts +387 -6
- package/dist/wire/src/model.js +74 -0
- package/dist/wire/src/model.js.map +1 -1
- package/dist/wire/src/relay.d.ts +8 -8
- package/dist/wire/src/rpc.d.ts +2524 -200
- package/dist/wire/src/rpc.js +77 -4
- package/dist/wire/src/rpc.js.map +1 -1
- package/dist/wire/src/updates.d.ts +390 -34
- package/dist/wire/src/updates.js +11 -1
- package/dist/wire/src/updates.js.map +1 -1
- package/package.json +12 -12
- package/src/accounts.ts +32 -0
- package/src/index.ts +4 -997
- package/src/main.ts +65 -0
- package/src/rate-limit.ts +72 -0
- package/src/relay.ts +435 -620
- package/src/serve.ts +130 -0
- package/src/store.ts +135 -0
- package/src/subscription.ts +51 -0
- package/dist/gateway/src/auth-middleware.d.ts +0 -44
- package/dist/gateway/src/auth-middleware.js +0 -204
- package/dist/gateway/src/auth-middleware.js.map +0 -1
- package/dist/gateway/src/cors.d.ts +0 -2
- package/dist/gateway/src/cors.js +0 -7
- package/dist/gateway/src/cors.js.map +0 -1
- package/dist/gateway/src/embedded.d.ts +0 -21
- package/dist/gateway/src/embedded.js +0 -438
- package/dist/gateway/src/embedded.js.map +0 -1
- package/dist/gateway/src/host-auth.d.ts +0 -32
- package/dist/gateway/src/host-auth.js +0 -78
- package/dist/gateway/src/host-auth.js.map +0 -1
- package/dist/gateway/src/pairings.d.ts +0 -38
- package/dist/gateway/src/pairings.js +0 -119
- package/dist/gateway/src/pairings.js.map +0 -1
- package/dist/gateway/src/presence.d.ts +0 -28
- package/dist/gateway/src/presence.js +0 -92
- package/dist/gateway/src/presence.js.map +0 -1
- package/dist/gateway/src/sessions.d.ts +0 -113
- package/dist/gateway/src/sessions.js +0 -352
- package/dist/gateway/src/sessions.js.map +0 -1
- package/dist/gateway/src/state-store.d.ts +0 -21
- package/dist/gateway/src/state-store.js +0 -103
- package/dist/gateway/src/state-store.js.map +0 -1
- package/dist/gateway/src/static-web.d.ts +0 -15
- package/dist/gateway/src/static-web.js +0 -129
- package/dist/gateway/src/static-web.js.map +0 -1
- package/dist/gateway/src/tokens.d.ts +0 -19
- package/dist/gateway/src/tokens.js +0 -151
- package/dist/gateway/src/tokens.js.map +0 -1
- package/dist/gateway/src/tunnel.d.ts +0 -61
- package/dist/gateway/src/tunnel.js +0 -576
- package/dist/gateway/src/tunnel.js.map +0 -1
- package/dist/gateway-v2/src/accounts.js.map +0 -1
- package/dist/gateway-v2/src/index.d.ts +0 -3
- package/dist/gateway-v2/src/index.js +0 -4
- package/dist/gateway-v2/src/index.js.map +0 -1
- package/dist/gateway-v2/src/main.js +0 -17
- package/dist/gateway-v2/src/main.js.map +0 -1
- package/dist/gateway-v2/src/server.d.ts +0 -84
- package/dist/gateway-v2/src/server.js +0 -426
- package/dist/gateway-v2/src/server.js.map +0 -1
- package/dist/gateway-v2/src/store.js.map +0 -1
- package/dist/shared-protocol/src/index.d.ts +0 -17029
- package/dist/shared-protocol/src/index.js +0 -1064
- package/dist/shared-protocol/src/index.js.map +0 -1
- package/src/auth-middleware.ts +0 -266
- package/src/cors.ts +0 -8
- package/src/embedded.ts +0 -526
- package/src/host-auth.ts +0 -88
- package/src/pairings.ts +0 -133
- package/src/presence.ts +0 -118
- package/src/sessions.ts +0 -432
- package/src/state-store.ts +0 -140
- package/src/static-web.ts +0 -133
- package/src/tokens.ts +0 -159
- package/src/tunnel.ts +0 -644
- /package/dist/{gateway-v2 → gateway}/src/accounts.d.ts +0 -0
- /package/dist/{gateway-v2 → gateway}/src/accounts.js +0 -0
- /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
|
-
}
|