@staff0rd/assist 0.326.1 → 0.327.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.
- package/README.md +5 -4
- package/dist/index.js +151 -77
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -277,13 +277,14 @@ From WSL, the selector can also surface and drive Windows-host repos (requires `
|
|
|
277
277
|
|
|
278
278
|
- `sessions.windowsProjectsRoot` — the Windows `.claude/projects` directory as seen from WSL (e.g. `/mnt/c/Users/<user>/.claude/projects`); enables discovery of Windows-host repos, tagged with a `Windows` badge. Selecting one launches a native assist daemon on Windows and runs an interactive session there.
|
|
279
279
|
- `sessions.windowsDaemonHost` / `sessions.windowsDaemonPort` — where the WSL daemon reaches the native Windows daemon (defaults `127.0.0.1` / `51764`; set the host to the Windows IP on WSL2 NAT-mode networking).
|
|
280
|
+
- `sessions.windowsVersionCheck` — how the WSL↔Windows daemon handshake reacts to a protocol-version mismatch: `block` (default) refuses creates and auto-heals the host, `warn` logs and proceeds anyway, `off` skips the check. Use `warn`/`off` to keep working across an unfixable version gap.
|
|
280
281
|
|
|
281
|
-
When iterating on assist itself: web server changes only need the `assist sessions` process restarted — sessions survive. Daemon/session-core changes need `assist daemon restart` to load the new code; this kills the PTYs, then claude sessions — including assist sessions that wrap claude, like `assist draft` — are auto-respawned via `claude --resume` with scrollback starting fresh, while run sessions (and assist sessions whose claude sessionId was never discovered) reappear as not-restored tiles that can be retried.
|
|
282
|
+
When iterating on assist itself: web server changes only need the `assist sessions` process restarted — sessions survive. Daemon/session-core changes need `assist daemon restart` to load the new code; this kills the PTYs, then claude sessions — including assist sessions that wrap claude, like `assist draft` — are auto-respawned via `claude --resume` with scrollback starting fresh, while run sessions (and assist sessions whose claude sessionId was never discovered) reappear as not-restored tiles that can be retried. A `--once` draft/bug/refine session is respawned through its assist wrapper instead of bare `claude --resume`, so its `assist signal done` watcher is re-established and the card still auto-closes.
|
|
282
283
|
|
|
283
284
|
- `assist next [id] [--once]` - Alias for `backlog next [id]`; `--once` exits after the first completed item run instead of prompting for another
|
|
284
|
-
- `assist draft [description] [--once]` (alias: `feat`) - Launch Claude in `/draft` mode, chain into next on `/next` signal; an optional `description` is forwarded as `/draft <description>`; `--once` exits when the done signal arrives after the initial draft completes
|
|
285
|
-
- `assist bug [description] [--once]` - Launch Claude in `/bug` mode, chain into next on `/next` signal; an optional `description` is forwarded as `/bug <description>`; `--once` exits when the done signal arrives after the initial bug report completes
|
|
286
|
-
- `assist refine [id] [--once]` - Launch Claude in `/refine` mode to refine a backlog item; prompts for selection when no id given; `--once` exits when the done signal arrives after refinement completes
|
|
285
|
+
- `assist draft [description] [--once]` (alias: `feat`) - Launch Claude in `/draft` mode, chain into next on `/next` signal; an optional `description` is forwarded as `/draft <description>`; `--once` exits when the done signal arrives after the initial draft completes; `--resume-session <id>` resumes an interrupted Claude session (used by the sessions daemon when it restarts a running item)
|
|
286
|
+
- `assist bug [description] [--once]` - Launch Claude in `/bug` mode, chain into next on `/next` signal; an optional `description` is forwarded as `/bug <description>`; `--once` exits when the done signal arrives after the initial bug report completes; `--resume-session <id>` resumes an interrupted Claude session (used by the sessions daemon when it restarts a running item)
|
|
287
|
+
- `assist refine [id] [--once]` - Launch Claude in `/refine` mode to refine a backlog item; prompts for selection when no id given; `--once` exits when the done signal arrives after refinement completes; `--resume-session <id>` resumes an interrupted Claude session (used by the sessions daemon when it restarts a running item)
|
|
287
288
|
- `assist review-comments [number]` - Launch Claude in `/review-comments` mode to process PR review comments (single session, no chaining); when a PR number is supplied, checks out that PR via `gh pr checkout` first
|
|
288
289
|
- `assist signal next [id]` - Write a next signal to chain into `assist next`; when `id` is supplied, the parent launcher runs that backlog item directly
|
|
289
290
|
- `assist signal done [id]` - Write a done signal marking the session's initial task complete; an optional `id` surfaces the backlog item the session created onto its session card; `--once` launch sessions exit when it arrives, plain sessions ignore it
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.327.1",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -285,7 +285,8 @@ var assistConfigSchema = z2.strictObject({
|
|
|
285
285
|
// why: host the WSL daemon dials to reach the native Windows daemon over TCP (WSL can't use the Windows named pipe); defaults to 127.0.0.1 (WSL2 mirrored networking). NAT-mode users set the Windows host IP.
|
|
286
286
|
windowsDaemonHost: z2.string().optional(),
|
|
287
287
|
// why: TCP port the native Windows daemon listens on for the WSL bridge; defaults to 51764
|
|
288
|
-
windowsDaemonPort: z2.number().optional()
|
|
288
|
+
windowsDaemonPort: z2.number().optional(),
|
|
289
|
+
windowsVersionCheck: z2.enum(["block", "warn", "off"]).default("block")
|
|
289
290
|
}).optional(),
|
|
290
291
|
database: z2.strictObject({
|
|
291
292
|
url: z2.string().optional()
|
|
@@ -7571,9 +7572,10 @@ function buildSlashCommand(slashCommand, description) {
|
|
|
7571
7572
|
return trimmed ? `/${slashCommand} ${trimmed}` : `/${slashCommand}`;
|
|
7572
7573
|
}
|
|
7573
7574
|
async function launchMode(slashCommand, options2) {
|
|
7574
|
-
|
|
7575
|
+
const resumeSessionId = options2?.resumeSessionId;
|
|
7576
|
+
if (!resumeSessionId) pullIfConfigured();
|
|
7575
7577
|
process.env.ASSIST_SESSION_ID ??= String(process.pid);
|
|
7576
|
-
const claudeSessionId = randomUUID2();
|
|
7578
|
+
const claudeSessionId = resumeSessionId ?? randomUUID2();
|
|
7577
7579
|
emitActivity({
|
|
7578
7580
|
kind: "command",
|
|
7579
7581
|
name: slashCommand,
|
|
@@ -7582,8 +7584,8 @@ async function launchMode(slashCommand, options2) {
|
|
|
7582
7584
|
claudeSessionId
|
|
7583
7585
|
});
|
|
7584
7586
|
const { child, done: done2 } = spawnClaude(
|
|
7585
|
-
buildSlashCommand(slashCommand, options2?.description),
|
|
7586
|
-
{ allowEdits: true, sessionId: claudeSessionId }
|
|
7587
|
+
resumeSessionId ? buildResumePrompt() : buildSlashCommand(slashCommand, options2?.description),
|
|
7588
|
+
{ allowEdits: true, sessionId: claudeSessionId, resumeSessionId }
|
|
7587
7589
|
);
|
|
7588
7590
|
watchForMarker(child, { actOnDone: options2?.once });
|
|
7589
7591
|
const launched = await awaitClaude(done2, `/${slashCommand}`);
|
|
@@ -12242,23 +12244,51 @@ async function reviewComments(number) {
|
|
|
12242
12244
|
}
|
|
12243
12245
|
|
|
12244
12246
|
// src/commands/registerLaunch.ts
|
|
12245
|
-
|
|
12247
|
+
var RESUME_SESSION_FLAG = "Resume an interrupted Claude session (used by the sessions daemon on restart)";
|
|
12248
|
+
function registerNext(program2) {
|
|
12246
12249
|
program2.command("next").argument("[id]", "Backlog item ID to run first").description("Alias for backlog next").option("--once", "Exit after the first completed item run").action(
|
|
12247
12250
|
(id2, opts) => next({ allowEdits: true, once: opts.once }, id2)
|
|
12248
12251
|
);
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
|
|
12254
|
-
|
|
12255
|
-
(
|
|
12252
|
+
}
|
|
12253
|
+
function registerDescriptionLaunch(program2, name, slashCommand, description, alias) {
|
|
12254
|
+
const command = program2.command(name).argument(
|
|
12255
|
+
"[description]",
|
|
12256
|
+
`Text to forward to the /${slashCommand} slash command`
|
|
12257
|
+
).description(description).option("--once", "Exit when the initial task completes").option("--resume-session <id>", RESUME_SESSION_FLAG).action(
|
|
12258
|
+
(text6, opts) => launchMode(slashCommand, {
|
|
12259
|
+
once: opts.once,
|
|
12260
|
+
description: text6,
|
|
12261
|
+
resumeSessionId: opts.resumeSession
|
|
12262
|
+
})
|
|
12256
12263
|
);
|
|
12264
|
+
if (alias) command.alias(alias);
|
|
12265
|
+
}
|
|
12266
|
+
function registerReviewComments(program2) {
|
|
12257
12267
|
program2.command("review-comments").argument("[number]", "PR number to check out first").description("Launch Claude in /review-comments mode (single session)").action((number) => reviewComments(number));
|
|
12258
|
-
|
|
12259
|
-
|
|
12268
|
+
}
|
|
12269
|
+
function registerRefine(program2) {
|
|
12270
|
+
program2.command("refine").argument("[id]", "Backlog item ID").description("Launch Claude in /refine mode to refine a backlog item").option("--once", "Exit when the initial task completes").option("--resume-session <id>", RESUME_SESSION_FLAG).action(
|
|
12271
|
+
(id2, opts) => refine(id2, { once: opts.once, resumeSessionId: opts.resumeSession })
|
|
12260
12272
|
);
|
|
12261
12273
|
}
|
|
12274
|
+
function registerLaunch(program2) {
|
|
12275
|
+
registerNext(program2);
|
|
12276
|
+
registerDescriptionLaunch(
|
|
12277
|
+
program2,
|
|
12278
|
+
"draft",
|
|
12279
|
+
"draft",
|
|
12280
|
+
"Launch Claude in /draft mode, chain into next on /next signal",
|
|
12281
|
+
"feat"
|
|
12282
|
+
);
|
|
12283
|
+
registerDescriptionLaunch(
|
|
12284
|
+
program2,
|
|
12285
|
+
"bug",
|
|
12286
|
+
"bug",
|
|
12287
|
+
"Launch Claude in /bug mode, chain into next on /next signal"
|
|
12288
|
+
);
|
|
12289
|
+
registerReviewComments(program2);
|
|
12290
|
+
registerRefine(program2);
|
|
12291
|
+
}
|
|
12262
12292
|
|
|
12263
12293
|
// src/commands/registerList.ts
|
|
12264
12294
|
function registerList(program2) {
|
|
@@ -20882,8 +20912,8 @@ function errorSession(id2, persisted, error) {
|
|
|
20882
20912
|
};
|
|
20883
20913
|
}
|
|
20884
20914
|
|
|
20885
|
-
// src/commands/sessions/daemon/
|
|
20886
|
-
function
|
|
20915
|
+
// src/commands/sessions/daemon/assistResumeArgs.ts
|
|
20916
|
+
function assistResumeArgs(persisted) {
|
|
20887
20917
|
const args = ["assist", ...persisted.assistArgs];
|
|
20888
20918
|
return persisted.claudeSessionId ? [...args, "--resume-session", persisted.claudeSessionId] : args;
|
|
20889
20919
|
}
|
|
@@ -20904,6 +20934,46 @@ function runningSession(base, persisted, pty2) {
|
|
|
20904
20934
|
};
|
|
20905
20935
|
}
|
|
20906
20936
|
|
|
20937
|
+
// src/commands/sessions/daemon/restoreInteractiveSession.ts
|
|
20938
|
+
function restoreInteractiveSession(id2, persisted, base) {
|
|
20939
|
+
if (persisted.commandType !== "run" && persisted.claudeSessionId) {
|
|
20940
|
+
return resumeViaClaude(id2, persisted, base);
|
|
20941
|
+
}
|
|
20942
|
+
if (persisted.commandType === "claude") {
|
|
20943
|
+
return unrecoverableClaude(id2, persisted);
|
|
20944
|
+
}
|
|
20945
|
+
return notRestoredStub(base, persisted);
|
|
20946
|
+
}
|
|
20947
|
+
function resumeViaClaude(id2, persisted, base) {
|
|
20948
|
+
const pty2 = spawnClaude2({
|
|
20949
|
+
resumeSessionId: persisted.claudeSessionId,
|
|
20950
|
+
prompt: buildResumePrompt(),
|
|
20951
|
+
cwd: persisted.cwd,
|
|
20952
|
+
sessionId: id2
|
|
20953
|
+
});
|
|
20954
|
+
return runningSession(base, persisted, pty2);
|
|
20955
|
+
}
|
|
20956
|
+
function unrecoverableClaude(id2, persisted) {
|
|
20957
|
+
return errorSession(
|
|
20958
|
+
id2,
|
|
20959
|
+
persisted,
|
|
20960
|
+
"no claude session id was recorded before the daemon stopped, so the conversation cannot be resumed"
|
|
20961
|
+
);
|
|
20962
|
+
}
|
|
20963
|
+
function notRestoredStub(base, persisted) {
|
|
20964
|
+
return {
|
|
20965
|
+
...base,
|
|
20966
|
+
status: "done",
|
|
20967
|
+
startedAt: persisted.startedAt,
|
|
20968
|
+
runningMs: persisted.runningMs ?? 0,
|
|
20969
|
+
runningSince: null,
|
|
20970
|
+
pty: null,
|
|
20971
|
+
runName: persisted.runName,
|
|
20972
|
+
runArgs: persisted.runArgs,
|
|
20973
|
+
restored: false
|
|
20974
|
+
};
|
|
20975
|
+
}
|
|
20976
|
+
|
|
20907
20977
|
// src/commands/sessions/daemon/updatedSession.ts
|
|
20908
20978
|
function updatedSession(id2, persisted) {
|
|
20909
20979
|
return {
|
|
@@ -20923,41 +20993,22 @@ function isUpdate(persisted) {
|
|
|
20923
20993
|
function restoreSession(id2, persisted) {
|
|
20924
20994
|
const base = restoreBase(id2, persisted);
|
|
20925
20995
|
if (isUpdate(persisted)) return updatedSession(id2, persisted);
|
|
20926
|
-
if (
|
|
20927
|
-
const pty2 = spawnPty(
|
|
20996
|
+
if (needsWrapperRelaunch(persisted)) {
|
|
20997
|
+
const pty2 = spawnPty(assistResumeArgs(persisted), persisted.cwd, id2);
|
|
20928
20998
|
return runningSession(base, persisted, pty2);
|
|
20929
20999
|
}
|
|
20930
|
-
|
|
20931
|
-
|
|
20932
|
-
|
|
20933
|
-
|
|
20934
|
-
cwd: persisted.cwd,
|
|
20935
|
-
sessionId: id2
|
|
20936
|
-
});
|
|
20937
|
-
return runningSession(base, persisted, pty2);
|
|
20938
|
-
}
|
|
20939
|
-
if (persisted.commandType === "claude") {
|
|
20940
|
-
return errorSession(
|
|
20941
|
-
id2,
|
|
20942
|
-
persisted,
|
|
20943
|
-
"no claude session id was recorded before the daemon stopped, so the conversation cannot be resumed"
|
|
20944
|
-
);
|
|
20945
|
-
}
|
|
20946
|
-
return {
|
|
20947
|
-
...base,
|
|
20948
|
-
status: "done",
|
|
20949
|
-
startedAt: persisted.startedAt,
|
|
20950
|
-
runningMs: persisted.runningMs ?? 0,
|
|
20951
|
-
runningSince: null,
|
|
20952
|
-
pty: null,
|
|
20953
|
-
runName: persisted.runName,
|
|
20954
|
-
runArgs: persisted.runArgs,
|
|
20955
|
-
restored: false
|
|
20956
|
-
};
|
|
21000
|
+
return restoreInteractiveSession(id2, persisted, base);
|
|
21001
|
+
}
|
|
21002
|
+
function needsWrapperRelaunch(persisted) {
|
|
21003
|
+
return isBacklogRun(persisted) || isOnceLaunch(persisted) && !!persisted.claudeSessionId;
|
|
20957
21004
|
}
|
|
20958
21005
|
function isBacklogRun(persisted) {
|
|
20959
21006
|
return persisted.commandType === "assist" && persisted.assistArgs?.[0] === "backlog" && persisted.assistArgs?.[1] === "run";
|
|
20960
21007
|
}
|
|
21008
|
+
var LAUNCH_COMMANDS = ["draft", "feat", "bug", "refine"];
|
|
21009
|
+
function isOnceLaunch(persisted) {
|
|
21010
|
+
return persisted.commandType === "assist" && !!persisted.assistArgs && LAUNCH_COMMANDS.includes(persisted.assistArgs[0]) && persisted.assistArgs.includes("--once");
|
|
21011
|
+
}
|
|
20961
21012
|
|
|
20962
21013
|
// src/commands/sessions/daemon/restoreOne.ts
|
|
20963
21014
|
function restoreOne(persisted, spawn12, sessions) {
|
|
@@ -21342,18 +21393,6 @@ function stripOutboundSessionId(data) {
|
|
|
21342
21393
|
return { ...data, sessionId: stripWindowsSessionId(data.sessionId) };
|
|
21343
21394
|
}
|
|
21344
21395
|
|
|
21345
|
-
// src/commands/sessions/daemon/buildHello.ts
|
|
21346
|
-
var ASSIST_VERSION = package_default.version;
|
|
21347
|
-
function buildHello() {
|
|
21348
|
-
return { type: "hello", version: ASSIST_VERSION };
|
|
21349
|
-
}
|
|
21350
|
-
function isHello(msg) {
|
|
21351
|
-
return msg.type === "hello" && typeof msg.version === "string";
|
|
21352
|
-
}
|
|
21353
|
-
function versionsMatch(a, b) {
|
|
21354
|
-
return a === b;
|
|
21355
|
-
}
|
|
21356
|
-
|
|
21357
21396
|
// src/commands/sessions/daemon/WindowsProxyState.ts
|
|
21358
21397
|
var MAX_SCROLLBACK2 = 256 * 1024;
|
|
21359
21398
|
var DEFAULT_CREATE_TIMEOUT_MS = 5e3;
|
|
@@ -21399,6 +21438,59 @@ function appendScrollback2(state, sessionId, data) {
|
|
|
21399
21438
|
);
|
|
21400
21439
|
}
|
|
21401
21440
|
|
|
21441
|
+
// src/commands/sessions/daemon/handleSessions.ts
|
|
21442
|
+
function handleSessions(state, msg) {
|
|
21443
|
+
state.windowsSessions = msg.sessions.map((s) => ({
|
|
21444
|
+
...s,
|
|
21445
|
+
id: toWindowsSessionId(s.id)
|
|
21446
|
+
}));
|
|
21447
|
+
const live = new Set(state.windowsSessions.map((s) => s.id));
|
|
21448
|
+
for (const id2 of state.scrollback.keys())
|
|
21449
|
+
if (!live.has(id2)) state.scrollback.delete(id2);
|
|
21450
|
+
state.onSessionsChanged();
|
|
21451
|
+
}
|
|
21452
|
+
|
|
21453
|
+
// src/commands/sessions/daemon/buildHello.ts
|
|
21454
|
+
var ASSIST_VERSION = package_default.version;
|
|
21455
|
+
var PROTOCOL_VERSION = 1;
|
|
21456
|
+
function buildHello() {
|
|
21457
|
+
return { type: "hello", version: ASSIST_VERSION, protocol: PROTOCOL_VERSION };
|
|
21458
|
+
}
|
|
21459
|
+
function isHello(msg) {
|
|
21460
|
+
return msg.type === "hello" && typeof msg.version === "string" && (msg.protocol === void 0 || typeof msg.protocol === "number");
|
|
21461
|
+
}
|
|
21462
|
+
function helloCompatible(msg) {
|
|
21463
|
+
if (typeof msg.protocol === "number")
|
|
21464
|
+
return msg.protocol === PROTOCOL_VERSION;
|
|
21465
|
+
return msg.version === ASSIST_VERSION;
|
|
21466
|
+
}
|
|
21467
|
+
|
|
21468
|
+
// src/commands/sessions/daemon/windowsVersionCheck.ts
|
|
21469
|
+
function windowsVersionCheck() {
|
|
21470
|
+
return loadConfig().sessions?.windowsVersionCheck ?? "block";
|
|
21471
|
+
}
|
|
21472
|
+
|
|
21473
|
+
// src/commands/sessions/daemon/handleHello.ts
|
|
21474
|
+
function handleHello(state, msg) {
|
|
21475
|
+
if (!isHello(msg) || helloCompatible(msg)) return;
|
|
21476
|
+
const mode = windowsVersionCheck();
|
|
21477
|
+
const detail = `protocol ${msg.protocol ?? "legacy"} version ${msg.version} (wsl protocol ${PROTOCOL_VERSION} version ${ASSIST_VERSION})`;
|
|
21478
|
+
if (mode === "off") {
|
|
21479
|
+
daemonLog(
|
|
21480
|
+
`windows daemon protocol mismatch: ${detail}; check disabled (sessions.windowsVersionCheck=off), proceeding`
|
|
21481
|
+
);
|
|
21482
|
+
return;
|
|
21483
|
+
}
|
|
21484
|
+
if (mode === "warn") {
|
|
21485
|
+
daemonLog(
|
|
21486
|
+
`windows daemon protocol mismatch: ${detail}; proceeding with warning (sessions.windowsVersionCheck=warn)`
|
|
21487
|
+
);
|
|
21488
|
+
return;
|
|
21489
|
+
}
|
|
21490
|
+
daemonLog(`windows daemon protocol mismatch: ${detail}`);
|
|
21491
|
+
state.onVersionMismatch(msg.version);
|
|
21492
|
+
}
|
|
21493
|
+
|
|
21402
21494
|
// src/commands/sessions/daemon/handleInbound.ts
|
|
21403
21495
|
function handleInbound(state, line) {
|
|
21404
21496
|
let msg;
|
|
@@ -21424,14 +21516,6 @@ var inbound = {
|
|
|
21424
21516
|
state.broadcast(msg);
|
|
21425
21517
|
}
|
|
21426
21518
|
};
|
|
21427
|
-
function handleHello(state, msg) {
|
|
21428
|
-
if (isHello(msg) && !versionsMatch(msg.version, ASSIST_VERSION)) {
|
|
21429
|
-
daemonLog(
|
|
21430
|
-
`windows daemon version mismatch: ${msg.version} (wsl ${ASSIST_VERSION})`
|
|
21431
|
-
);
|
|
21432
|
-
state.onVersionMismatch(msg.version);
|
|
21433
|
-
}
|
|
21434
|
-
}
|
|
21435
21519
|
function handleCreated(state, msg) {
|
|
21436
21520
|
daemonLog(`windows daemon: created session ${nsId(msg)}`);
|
|
21437
21521
|
const client = takePendingCreator(state);
|
|
@@ -21443,16 +21527,6 @@ function handleCreated(state, msg) {
|
|
|
21443
21527
|
});
|
|
21444
21528
|
else daemonLog("windows daemon: created with no pending creator (dropped)");
|
|
21445
21529
|
}
|
|
21446
|
-
function handleSessions(state, msg) {
|
|
21447
|
-
state.windowsSessions = msg.sessions.map((s) => ({
|
|
21448
|
-
...s,
|
|
21449
|
-
id: toWindowsSessionId(s.id)
|
|
21450
|
-
}));
|
|
21451
|
-
const live = new Set(state.windowsSessions.map((s) => s.id));
|
|
21452
|
-
for (const id2 of state.scrollback.keys())
|
|
21453
|
-
if (!live.has(id2)) state.scrollback.delete(id2);
|
|
21454
|
-
state.onSessionsChanged();
|
|
21455
|
-
}
|
|
21456
21530
|
function handleOutput(state, msg) {
|
|
21457
21531
|
const sessionId = nsId(msg);
|
|
21458
21532
|
const data = msg.data;
|