claude-remote-cli 2.15.16 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/claude-remote-cli.js +3 -0
- package/dist/frontend/assets/index-Bz_R9N9S.css +32 -0
- package/dist/frontend/assets/index-Yv6LVq28.js +47 -0
- package/dist/frontend/index.html +2 -2
- package/dist/server/index.js +66 -2
- package/dist/server/pty-handler.js +214 -0
- package/dist/server/push.js +54 -3
- package/dist/server/sdk-handler.js +536 -0
- package/dist/server/sessions.js +183 -230
- package/dist/server/types.js +13 -1
- package/dist/server/ws.js +92 -9
- package/dist/test/sessions.test.js +175 -6
- package/package.json +3 -2
- package/dist/frontend/assets/index-DQ-fMetm.js +0 -47
- package/dist/frontend/assets/index-XlU0yxtO.css +0 -32
package/dist/frontend/index.html
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
12
12
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
13
13
|
<meta name="theme-color" content="#1a1a1a" />
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-Yv6LVq28.js"></script>
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Bz_R9N9S.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
18
18
|
<div id="app"></div>
|
package/dist/server/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import cookieParser from 'cookie-parser';
|
|
|
11
11
|
import { loadConfig, saveConfig, DEFAULTS, readMeta, writeMeta, deleteMeta, ensureMetaDir } from './config.js';
|
|
12
12
|
import * as auth from './auth.js';
|
|
13
13
|
import * as sessions from './sessions.js';
|
|
14
|
-
import { AGENT_CONTINUE_ARGS, AGENT_YOLO_ARGS, serializeAll, restoreFromDisk, activeTmuxSessionNames } from './sessions.js';
|
|
14
|
+
import { AGENT_CONTINUE_ARGS, AGENT_YOLO_ARGS, serializeAll, restoreFromDisk, activeTmuxSessionNames, startSdkIdleSweep, stopSdkIdleSweep } from './sessions.js';
|
|
15
15
|
import { setupWebSocket } from './ws.js';
|
|
16
16
|
import { WorktreeWatcher, WORKTREE_DIRS, isValidWorktreePath, parseWorktreeListPorcelain, parseAllWorktrees } from './watcher.js';
|
|
17
17
|
import { isInstalled as serviceIsInstalled } from './service.js';
|
|
@@ -156,6 +156,12 @@ async function main() {
|
|
|
156
156
|
config.port = parseInt(process.env.CLAUDE_REMOTE_PORT, 10);
|
|
157
157
|
if (process.env.CLAUDE_REMOTE_HOST)
|
|
158
158
|
config.host = process.env.CLAUDE_REMOTE_HOST;
|
|
159
|
+
// Enable SDK debug logging if requested
|
|
160
|
+
if (process.env.CLAUDE_REMOTE_DEBUG_LOG === '1' || config.debugLog) {
|
|
161
|
+
const { enableDebugLog } = await import('./sdk-handler.js');
|
|
162
|
+
enableDebugLog(true);
|
|
163
|
+
console.log('SDK debug logging enabled → ~/.config/claude-remote-cli/debug/');
|
|
164
|
+
}
|
|
159
165
|
push.ensureVapidKeys(config, CONFIG_PATH, saveConfig);
|
|
160
166
|
if (!config.pinHash) {
|
|
161
167
|
const pin = await promptPin('Set up a PIN for claude-remote-cli:');
|
|
@@ -884,6 +890,58 @@ async function main() {
|
|
|
884
890
|
res.status(404).json({ error: 'Session not found' });
|
|
885
891
|
}
|
|
886
892
|
});
|
|
893
|
+
// POST /sessions/:id/message — send message to SDK session
|
|
894
|
+
app.post('/sessions/:id/message', requireAuth, (req, res) => {
|
|
895
|
+
const id = req.params['id'];
|
|
896
|
+
const { text } = req.body;
|
|
897
|
+
if (!text) {
|
|
898
|
+
res.status(400).json({ error: 'text is required' });
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
const session = sessions.get(id);
|
|
902
|
+
if (!session) {
|
|
903
|
+
res.status(404).json({ error: 'Session not found' });
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
if (session.mode !== 'sdk') {
|
|
907
|
+
res.status(400).json({ error: 'Session is not an SDK session — use WebSocket for PTY sessions' });
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
try {
|
|
911
|
+
sessions.write(id, text);
|
|
912
|
+
res.json({ ok: true });
|
|
913
|
+
}
|
|
914
|
+
catch (err) {
|
|
915
|
+
const message = err instanceof Error ? err.message : 'Failed to send message';
|
|
916
|
+
res.status(500).json({ error: message });
|
|
917
|
+
}
|
|
918
|
+
});
|
|
919
|
+
// POST /sessions/:id/permission — handle permission approval for SDK session
|
|
920
|
+
app.post('/sessions/:id/permission', requireAuth, (req, res) => {
|
|
921
|
+
const id = req.params['id'];
|
|
922
|
+
const { requestId, approved } = req.body;
|
|
923
|
+
if (!requestId || typeof approved !== 'boolean') {
|
|
924
|
+
res.status(400).json({ error: 'requestId and approved are required' });
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
const session = sessions.get(id);
|
|
928
|
+
if (!session) {
|
|
929
|
+
res.status(404).json({ error: 'Session not found' });
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
if (session.mode !== 'sdk') {
|
|
933
|
+
res.status(400).json({ error: 'Session is not an SDK session' });
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
try {
|
|
937
|
+
sessions.handlePermission(id, requestId, approved);
|
|
938
|
+
res.json({ ok: true });
|
|
939
|
+
}
|
|
940
|
+
catch (err) {
|
|
941
|
+
const message = err instanceof Error ? err.message : 'Failed to handle permission';
|
|
942
|
+
res.status(500).json({ error: message });
|
|
943
|
+
}
|
|
944
|
+
});
|
|
887
945
|
// PATCH /sessions/:id — update displayName and persist to metadata
|
|
888
946
|
app.patch('/sessions/:id', requireAuth, (req, res) => {
|
|
889
947
|
const { displayName } = req.body;
|
|
@@ -991,9 +1049,15 @@ async function main() {
|
|
|
991
1049
|
catch {
|
|
992
1050
|
// tmux not installed or no sessions — ignore
|
|
993
1051
|
}
|
|
1052
|
+
// Start SDK idle sweep
|
|
1053
|
+
startSdkIdleSweep();
|
|
994
1054
|
function gracefulShutdown() {
|
|
995
1055
|
server.close();
|
|
996
|
-
|
|
1056
|
+
stopSdkIdleSweep();
|
|
1057
|
+
// Serialize sessions to disk BEFORE killing them
|
|
1058
|
+
const configDir = path.dirname(CONFIG_PATH);
|
|
1059
|
+
serializeAll(configDir);
|
|
1060
|
+
// Kill all active sessions (PTY + tmux + SDK)
|
|
997
1061
|
for (const s of sessions.list()) {
|
|
998
1062
|
try {
|
|
999
1063
|
sessions.kill(s.id);
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import pty from 'node-pty';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { AGENT_COMMANDS, AGENT_CONTINUE_ARGS } from './types.js';
|
|
6
|
+
import { readMeta, writeMeta } from './config.js';
|
|
7
|
+
const IDLE_TIMEOUT_MS = 5000;
|
|
8
|
+
const MAX_SCROLLBACK = 256 * 1024; // 256KB max
|
|
9
|
+
export function generateTmuxSessionName(displayName, id) {
|
|
10
|
+
const sanitized = displayName.replace(/[^a-zA-Z0-9-]/g, '-').replace(/-+/g, '-').slice(0, 30);
|
|
11
|
+
return `crc-${sanitized}-${id.slice(0, 8)}`;
|
|
12
|
+
}
|
|
13
|
+
export function resolveTmuxSpawn(command, args, tmuxSessionName) {
|
|
14
|
+
return {
|
|
15
|
+
command: 'tmux',
|
|
16
|
+
args: [
|
|
17
|
+
'-u', 'new-session', '-s', tmuxSessionName, '--', command, ...args,
|
|
18
|
+
';', 'set', 'set-clipboard', 'on',
|
|
19
|
+
';', 'set', 'allow-passthrough', 'on',
|
|
20
|
+
';', 'set', 'mode-keys', 'vi',
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function createPtySession(params, sessionsMap, idleChangeCallbacks) {
|
|
25
|
+
const { id, type, agent = 'claude', repoName, repoPath, cwd, root, worktreeName, branchName, displayName, command, args = [], cols = 80, rows = 24, configPath, useTmux: paramUseTmux, tmuxSessionName: paramTmuxSessionName, initialScrollback, restored: paramRestored, } = params;
|
|
26
|
+
const createdAt = new Date().toISOString();
|
|
27
|
+
const resolvedCommand = command || AGENT_COMMANDS[agent];
|
|
28
|
+
// Strip CLAUDECODE env var to allow spawning claude inside a claude-managed server
|
|
29
|
+
const env = Object.assign({}, process.env);
|
|
30
|
+
delete env.CLAUDECODE;
|
|
31
|
+
const useTmux = !command && !!paramUseTmux;
|
|
32
|
+
let spawnCommand = resolvedCommand;
|
|
33
|
+
let spawnArgs = args;
|
|
34
|
+
const tmuxSessionName = paramTmuxSessionName || (useTmux ? generateTmuxSessionName(displayName || repoName || 'session', id) : '');
|
|
35
|
+
if (useTmux) {
|
|
36
|
+
const tmux = resolveTmuxSpawn(resolvedCommand, args, tmuxSessionName);
|
|
37
|
+
spawnCommand = tmux.command;
|
|
38
|
+
spawnArgs = tmux.args;
|
|
39
|
+
}
|
|
40
|
+
const ptyProcess = pty.spawn(spawnCommand, spawnArgs, {
|
|
41
|
+
name: 'xterm-256color',
|
|
42
|
+
cols,
|
|
43
|
+
rows,
|
|
44
|
+
cwd: cwd || repoPath,
|
|
45
|
+
env,
|
|
46
|
+
});
|
|
47
|
+
// Scrollback buffer: stores all PTY output so we can replay on WebSocket (re)connect
|
|
48
|
+
const scrollback = initialScrollback ? [...initialScrollback] : [];
|
|
49
|
+
let scrollbackBytes = initialScrollback ? initialScrollback.reduce((sum, s) => sum + s.length, 0) : 0;
|
|
50
|
+
const resolvedCwd = cwd || repoPath;
|
|
51
|
+
const session = {
|
|
52
|
+
id,
|
|
53
|
+
type: type || 'worktree',
|
|
54
|
+
agent,
|
|
55
|
+
mode: 'pty',
|
|
56
|
+
root: root || '',
|
|
57
|
+
repoName: repoName || '',
|
|
58
|
+
repoPath,
|
|
59
|
+
worktreeName: worktreeName || '',
|
|
60
|
+
branchName: branchName || worktreeName || '',
|
|
61
|
+
displayName: displayName || worktreeName || repoName || '',
|
|
62
|
+
pty: ptyProcess,
|
|
63
|
+
createdAt,
|
|
64
|
+
lastActivity: createdAt,
|
|
65
|
+
scrollback,
|
|
66
|
+
idle: false,
|
|
67
|
+
cwd: resolvedCwd,
|
|
68
|
+
customCommand: command || null,
|
|
69
|
+
useTmux,
|
|
70
|
+
tmuxSessionName,
|
|
71
|
+
onPtyReplacedCallbacks: [],
|
|
72
|
+
status: 'active',
|
|
73
|
+
restored: paramRestored || false,
|
|
74
|
+
};
|
|
75
|
+
sessionsMap.set(id, session);
|
|
76
|
+
// Load existing metadata to preserve a previously-set displayName
|
|
77
|
+
if (configPath && worktreeName) {
|
|
78
|
+
const existing = readMeta(configPath, repoPath);
|
|
79
|
+
if (existing && existing.displayName) {
|
|
80
|
+
session.displayName = existing.displayName;
|
|
81
|
+
}
|
|
82
|
+
writeMeta(configPath, { worktreePath: repoPath, displayName: session.displayName, lastActivity: createdAt });
|
|
83
|
+
}
|
|
84
|
+
let metaFlushTimer = null;
|
|
85
|
+
let idleTimer = null;
|
|
86
|
+
function resetIdleTimer() {
|
|
87
|
+
if (session.idle) {
|
|
88
|
+
session.idle = false;
|
|
89
|
+
for (const cb of idleChangeCallbacks)
|
|
90
|
+
cb(session.id, false);
|
|
91
|
+
}
|
|
92
|
+
if (idleTimer)
|
|
93
|
+
clearTimeout(idleTimer);
|
|
94
|
+
idleTimer = setTimeout(() => {
|
|
95
|
+
if (!session.idle) {
|
|
96
|
+
session.idle = true;
|
|
97
|
+
for (const cb of idleChangeCallbacks)
|
|
98
|
+
cb(session.id, true);
|
|
99
|
+
}
|
|
100
|
+
}, IDLE_TIMEOUT_MS);
|
|
101
|
+
}
|
|
102
|
+
const continueArgs = AGENT_CONTINUE_ARGS[agent];
|
|
103
|
+
function attachHandlers(proc, canRetry) {
|
|
104
|
+
const spawnTime = Date.now();
|
|
105
|
+
// Clear restored flag after 3s of running — means the PTY is healthy
|
|
106
|
+
const restoredClearTimer = session.restored ? setTimeout(() => { session.restored = false; }, 3000) : null;
|
|
107
|
+
proc.onData((data) => {
|
|
108
|
+
session.lastActivity = new Date().toISOString();
|
|
109
|
+
resetIdleTimer();
|
|
110
|
+
scrollback.push(data);
|
|
111
|
+
scrollbackBytes += data.length;
|
|
112
|
+
// Trim oldest entries if over limit
|
|
113
|
+
while (scrollbackBytes > MAX_SCROLLBACK && scrollback.length > 1) {
|
|
114
|
+
scrollbackBytes -= scrollback.shift().length;
|
|
115
|
+
}
|
|
116
|
+
if (configPath && worktreeName && !metaFlushTimer) {
|
|
117
|
+
metaFlushTimer = setTimeout(() => {
|
|
118
|
+
metaFlushTimer = null;
|
|
119
|
+
writeMeta(configPath, { worktreePath: repoPath, displayName: session.displayName, lastActivity: session.lastActivity });
|
|
120
|
+
}, 5000);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
proc.onExit(() => {
|
|
124
|
+
if (canRetry && (Date.now() - spawnTime) < 3000) {
|
|
125
|
+
const retryArgs = args.filter(a => !continueArgs.includes(a));
|
|
126
|
+
const retryNotice = '\r\n[claude-remote-cli] --continue not available; starting new session...\r\n';
|
|
127
|
+
scrollback.length = 0;
|
|
128
|
+
scrollbackBytes = 0;
|
|
129
|
+
scrollback.push(retryNotice);
|
|
130
|
+
scrollbackBytes = retryNotice.length;
|
|
131
|
+
let retryCommand = resolvedCommand;
|
|
132
|
+
let retrySpawnArgs = retryArgs;
|
|
133
|
+
if (useTmux && tmuxSessionName) {
|
|
134
|
+
const retryTmuxName = tmuxSessionName + '-retry';
|
|
135
|
+
session.tmuxSessionName = retryTmuxName;
|
|
136
|
+
const tmux = resolveTmuxSpawn(resolvedCommand, retryArgs, retryTmuxName);
|
|
137
|
+
retryCommand = tmux.command;
|
|
138
|
+
retrySpawnArgs = tmux.args;
|
|
139
|
+
}
|
|
140
|
+
let retryPty;
|
|
141
|
+
try {
|
|
142
|
+
retryPty = pty.spawn(retryCommand, retrySpawnArgs, {
|
|
143
|
+
name: 'xterm-256color',
|
|
144
|
+
cols,
|
|
145
|
+
rows,
|
|
146
|
+
cwd: cwd || repoPath,
|
|
147
|
+
env,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Retry spawn failed — fall through to normal exit cleanup
|
|
152
|
+
if (restoredClearTimer)
|
|
153
|
+
clearTimeout(restoredClearTimer);
|
|
154
|
+
if (idleTimer)
|
|
155
|
+
clearTimeout(idleTimer);
|
|
156
|
+
if (metaFlushTimer)
|
|
157
|
+
clearTimeout(metaFlushTimer);
|
|
158
|
+
sessionsMap.delete(id);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
session.pty = retryPty;
|
|
162
|
+
for (const cb of session.onPtyReplacedCallbacks)
|
|
163
|
+
cb(retryPty);
|
|
164
|
+
attachHandlers(retryPty, false);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (restoredClearTimer)
|
|
168
|
+
clearTimeout(restoredClearTimer);
|
|
169
|
+
// If PTY exited and this is a restored session, mark disconnected rather than delete
|
|
170
|
+
if (session.restored) {
|
|
171
|
+
session.status = 'disconnected';
|
|
172
|
+
session.restored = false; // clear so user-initiated kills can delete normally
|
|
173
|
+
if (idleTimer)
|
|
174
|
+
clearTimeout(idleTimer);
|
|
175
|
+
if (metaFlushTimer)
|
|
176
|
+
clearTimeout(metaFlushTimer);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (idleTimer)
|
|
180
|
+
clearTimeout(idleTimer);
|
|
181
|
+
if (metaFlushTimer)
|
|
182
|
+
clearTimeout(metaFlushTimer);
|
|
183
|
+
if (configPath && worktreeName) {
|
|
184
|
+
writeMeta(configPath, { worktreePath: repoPath, displayName: session.displayName, lastActivity: session.lastActivity });
|
|
185
|
+
}
|
|
186
|
+
sessionsMap.delete(id);
|
|
187
|
+
const tmpDir = path.join(os.tmpdir(), 'claude-remote-cli', id);
|
|
188
|
+
fs.rm(tmpDir, { recursive: true, force: true }, () => { });
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
attachHandlers(ptyProcess, continueArgs.some(a => args.includes(a)));
|
|
192
|
+
const result = {
|
|
193
|
+
id,
|
|
194
|
+
type: session.type,
|
|
195
|
+
agent: session.agent,
|
|
196
|
+
mode: 'pty',
|
|
197
|
+
root: session.root,
|
|
198
|
+
repoName: session.repoName,
|
|
199
|
+
repoPath,
|
|
200
|
+
worktreeName: session.worktreeName,
|
|
201
|
+
branchName: session.branchName,
|
|
202
|
+
displayName: session.displayName,
|
|
203
|
+
pid: ptyProcess.pid,
|
|
204
|
+
createdAt,
|
|
205
|
+
lastActivity: createdAt,
|
|
206
|
+
idle: false,
|
|
207
|
+
cwd: resolvedCwd,
|
|
208
|
+
customCommand: command || null,
|
|
209
|
+
useTmux,
|
|
210
|
+
tmuxSessionName,
|
|
211
|
+
status: 'active',
|
|
212
|
+
};
|
|
213
|
+
return { session, result };
|
|
214
|
+
}
|
package/dist/server/push.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import webpush from 'web-push';
|
|
2
2
|
let vapidPublicKey = null;
|
|
3
3
|
const subscriptions = new Map();
|
|
4
|
+
const MAX_PAYLOAD_SIZE = 4 * 1024; // 4KB
|
|
4
5
|
export function ensureVapidKeys(config, configPath, save) {
|
|
5
6
|
if (config.vapidPublicKey && config.vapidPrivateKey) {
|
|
6
7
|
vapidPublicKey = config.vapidPublicKey;
|
|
@@ -39,15 +40,65 @@ export function removeSession(sessionId) {
|
|
|
39
40
|
entry.sessionIds.delete(sessionId);
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
|
-
export function
|
|
43
|
+
export function enrichNotification(event) {
|
|
44
|
+
try {
|
|
45
|
+
switch (event.type) {
|
|
46
|
+
case 'tool_call': {
|
|
47
|
+
const action = event.toolName || 'use a tool';
|
|
48
|
+
const target = event.path || (event.toolInput && typeof event.toolInput === 'object'
|
|
49
|
+
? event.toolInput.file_path || event.toolInput.command || ''
|
|
50
|
+
: '');
|
|
51
|
+
const msg = target
|
|
52
|
+
? `Claude wants to ${action} ${target}`
|
|
53
|
+
: `Claude wants to ${action}`;
|
|
54
|
+
return msg.slice(0, 200);
|
|
55
|
+
}
|
|
56
|
+
case 'turn_completed':
|
|
57
|
+
return 'Claude finished';
|
|
58
|
+
case 'error': {
|
|
59
|
+
const brief = (event.text || 'unknown error').slice(0, 150);
|
|
60
|
+
return `Claude hit an error: ${brief}`;
|
|
61
|
+
}
|
|
62
|
+
default:
|
|
63
|
+
return 'Claude is waiting for your input';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return 'Claude is waiting for your input';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function truncatePayload(payload) {
|
|
71
|
+
if (payload.length <= MAX_PAYLOAD_SIZE)
|
|
72
|
+
return payload;
|
|
73
|
+
// Try to parse, truncate text fields, and re-serialize
|
|
74
|
+
try {
|
|
75
|
+
const obj = JSON.parse(payload);
|
|
76
|
+
if (typeof obj.enrichedMessage === 'string' && obj.enrichedMessage.length > 100) {
|
|
77
|
+
obj.enrichedMessage = obj.enrichedMessage.slice(0, 100) + '...';
|
|
78
|
+
}
|
|
79
|
+
const truncated = JSON.stringify(obj);
|
|
80
|
+
if (truncated.length <= MAX_PAYLOAD_SIZE)
|
|
81
|
+
return truncated;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// fall through
|
|
85
|
+
}
|
|
86
|
+
return payload.slice(0, MAX_PAYLOAD_SIZE);
|
|
87
|
+
}
|
|
88
|
+
export function notifySessionIdle(sessionId, session, sdkEvent) {
|
|
43
89
|
if (!vapidPublicKey)
|
|
44
90
|
return;
|
|
45
|
-
const
|
|
91
|
+
const enrichedMessage = sdkEvent ? enrichNotification(sdkEvent) : undefined;
|
|
92
|
+
const payloadObj = {
|
|
46
93
|
type: 'session-attention',
|
|
47
94
|
sessionId,
|
|
48
95
|
displayName: session.displayName,
|
|
49
96
|
sessionType: session.type,
|
|
50
|
-
}
|
|
97
|
+
};
|
|
98
|
+
if (enrichedMessage) {
|
|
99
|
+
payloadObj.enrichedMessage = enrichedMessage;
|
|
100
|
+
}
|
|
101
|
+
const payload = truncatePayload(JSON.stringify(payloadObj));
|
|
51
102
|
for (const [endpoint, entry] of subscriptions) {
|
|
52
103
|
if (!entry.sessionIds.has(sessionId))
|
|
53
104
|
continue;
|