@petukhovart/agent-view 0.13.0 → 0.13.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/.claude-plugin/plugin.json +21 -21
- package/LICENSE +21 -21
- package/README.md +1 -1
- package/dist/cdp/console-stream.d.ts +35 -0
- package/dist/cdp/console-stream.d.ts.map +1 -0
- package/dist/cdp/console-stream.js +103 -0
- package/dist/cdp/console-stream.js.map +1 -0
- package/dist/cdp/transport.js +11 -11
- package/dist/inspectors/dom.d.ts +17 -0
- package/dist/inspectors/dom.d.ts.map +1 -0
- package/dist/inspectors/dom.js +131 -0
- package/dist/inspectors/dom.js.map +1 -0
- package/dist/inspectors/scene/pixi.d.ts +3 -0
- package/dist/inspectors/scene/pixi.d.ts.map +1 -0
- package/dist/inspectors/scene/pixi.js +52 -0
- package/dist/inspectors/scene/pixi.js.map +1 -0
- package/dist/server/server.d.ts +4 -5
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +104 -62
- package/dist/server/server.js.map +1 -1
- package/package.json +1 -1
- package/skills/verify/SKILL.md +2 -0
package/dist/server/server.js
CHANGED
|
@@ -164,13 +164,9 @@ export class AgentViewServer {
|
|
|
164
164
|
sceneCache = new Map();
|
|
165
165
|
domTextCache = new Map();
|
|
166
166
|
axTreeCache = new AxTreeCache();
|
|
167
|
-
|
|
168
|
-
networkStream = new NetworkStream();
|
|
169
|
-
networkRefs = new Map();
|
|
170
|
-
networkNextRef = 1;
|
|
167
|
+
portStates = new Map();
|
|
171
168
|
token = '';
|
|
172
169
|
activeWatches = new Set();
|
|
173
|
-
logRecorder = null;
|
|
174
170
|
handlers = {
|
|
175
171
|
discover: (req) => this.handleDiscover(req),
|
|
176
172
|
launch: (req) => this.handleLaunch(req),
|
|
@@ -210,10 +206,31 @@ export class AgentViewServer {
|
|
|
210
206
|
});
|
|
211
207
|
await writeFile(TOKEN_PATH, this.token, { mode: 0o600 });
|
|
212
208
|
}
|
|
209
|
+
/** Per-port state bucket, created on first use. */
|
|
210
|
+
stateFor(port) {
|
|
211
|
+
let state = this.portStates.get(port);
|
|
212
|
+
if (!state) {
|
|
213
|
+
state = {
|
|
214
|
+
consoleStream: new ConsoleStream(),
|
|
215
|
+
networkStream: new NetworkStream(),
|
|
216
|
+
networkRefs: new Map(),
|
|
217
|
+
networkNextRef: 1,
|
|
218
|
+
logRecorder: null,
|
|
219
|
+
};
|
|
220
|
+
this.portStates.set(port, state);
|
|
221
|
+
}
|
|
222
|
+
return state;
|
|
223
|
+
}
|
|
224
|
+
isAnyRecording() {
|
|
225
|
+
for (const state of this.portStates.values())
|
|
226
|
+
if (state.logRecorder)
|
|
227
|
+
return true;
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
213
230
|
resetIdleTimer() {
|
|
214
231
|
if (this.idleTimer)
|
|
215
232
|
clearTimeout(this.idleTimer);
|
|
216
|
-
if (this.activeWatches.size > 0 || this.
|
|
233
|
+
if (this.activeWatches.size > 0 || this.isAnyRecording()) {
|
|
217
234
|
// Pause idle shutdown while streaming handlers or a log recording are alive —
|
|
218
235
|
// a recording that dies at the 5-min mark loses exactly the long scenario it was for.
|
|
219
236
|
this.idleTimer = null;
|
|
@@ -326,8 +343,9 @@ export class AgentViewServer {
|
|
|
326
343
|
return;
|
|
327
344
|
this.connections.delete(connKey);
|
|
328
345
|
const targetId = cached.session.target.id;
|
|
329
|
-
this.
|
|
330
|
-
|
|
346
|
+
const state = this.portStates.get(Number(connKey.slice(0, connKey.indexOf(':'))));
|
|
347
|
+
state?.consoleStream.detach(targetId);
|
|
348
|
+
state?.networkStream.detach(targetId);
|
|
331
349
|
this.axTreeCache.invalidate(connKey);
|
|
332
350
|
cached.session.close().catch(() => { });
|
|
333
351
|
}
|
|
@@ -441,8 +459,9 @@ export class AgentViewServer {
|
|
|
441
459
|
* mirroring the consoleStream pattern.
|
|
442
460
|
*/
|
|
443
461
|
async ensureNetworkAttached(req, config) {
|
|
444
|
-
|
|
445
|
-
|
|
462
|
+
const state = this.stateFor(req.port);
|
|
463
|
+
if (state.networkStream.attachedCount === 0) {
|
|
464
|
+
state.networkStream = new NetworkStream({
|
|
446
465
|
capacity: config?.networkBufferSize ?? DEFAULT_NETWORK_BUFFER,
|
|
447
466
|
captureBody: config?.captureBody ?? false,
|
|
448
467
|
});
|
|
@@ -453,7 +472,7 @@ export class AgentViewServer {
|
|
|
453
472
|
continue;
|
|
454
473
|
try {
|
|
455
474
|
const session = await this.getPageSession(req, t.id);
|
|
456
|
-
await
|
|
475
|
+
await state.networkStream.attach(session);
|
|
457
476
|
}
|
|
458
477
|
catch { /* a single unreachable target shouldn't abort capture */ }
|
|
459
478
|
}
|
|
@@ -1153,6 +1172,7 @@ export class AgentViewServer {
|
|
|
1153
1172
|
*/
|
|
1154
1173
|
async attachConsoleTargets(req, opts) {
|
|
1155
1174
|
const sessions = [];
|
|
1175
|
+
const state = this.stateFor(req.port);
|
|
1156
1176
|
if (process.env.AV_DEBUG_CONSOLE) {
|
|
1157
1177
|
// eslint-disable-next-line no-console
|
|
1158
1178
|
console.error(`[av-debug] attachConsoleTargets: targets=${opts.targets.length} explicit=${opts.targetId ?? 'none'} types=${[...opts.allowedTypes].join(',')}`);
|
|
@@ -1166,11 +1186,11 @@ export class AgentViewServer {
|
|
|
1166
1186
|
continue;
|
|
1167
1187
|
try {
|
|
1168
1188
|
const session = await this.getRuntimeSession(req, t);
|
|
1169
|
-
|
|
1189
|
+
state.consoleStream.attach(session);
|
|
1170
1190
|
sessions.push(session);
|
|
1171
1191
|
if (process.env.AV_DEBUG_CONSOLE) {
|
|
1172
1192
|
// eslint-disable-next-line no-console
|
|
1173
|
-
console.error(`[av-debug] attachConsoleTargets: attached ${t.type}:${t.id.slice(0, 8)} (stream now has ${
|
|
1193
|
+
console.error(`[av-debug] attachConsoleTargets: attached ${t.type}:${t.id.slice(0, 8)} (stream now has ${state.consoleStream.attachedCount})`);
|
|
1174
1194
|
}
|
|
1175
1195
|
}
|
|
1176
1196
|
catch (err) {
|
|
@@ -1186,10 +1206,11 @@ export class AgentViewServer {
|
|
|
1186
1206
|
const cwd = argStr(req.args, 'cwd');
|
|
1187
1207
|
const config = cwd ? readConfig(resolve(cwd)) : null;
|
|
1188
1208
|
const bufferSize = config?.consoleBufferSize ?? 500;
|
|
1189
|
-
|
|
1209
|
+
const state = this.stateFor(req.port);
|
|
1210
|
+
if (state.consoleStream.attachedCount === 0 && state.logRecorder === null) {
|
|
1190
1211
|
// Recreate with config-tuned capacity on first attach. Never while recording — the
|
|
1191
1212
|
// recorder's subscription lives on the stream instance and would be dropped silently.
|
|
1192
|
-
|
|
1213
|
+
state.consoleStream = new ConsoleStream({ capacity: bufferSize });
|
|
1193
1214
|
}
|
|
1194
1215
|
const targetQuery = argStr(req.args, 'target');
|
|
1195
1216
|
// Fuzzy-resolve the target query once (id exact → title substring → url substring)
|
|
@@ -1203,7 +1224,7 @@ export class AgentViewServer {
|
|
|
1203
1224
|
resolvedTargetId = match.target.id;
|
|
1204
1225
|
}
|
|
1205
1226
|
if (argBool(req.args, 'clear')) {
|
|
1206
|
-
|
|
1227
|
+
state.consoleStream.clear(resolvedTargetId);
|
|
1207
1228
|
return { ok: true, data: 'Console buffer cleared' };
|
|
1208
1229
|
}
|
|
1209
1230
|
await this.attachConsoleTargets(req, {
|
|
@@ -1220,7 +1241,7 @@ export class AgentViewServer {
|
|
|
1220
1241
|
}
|
|
1221
1242
|
if (follow) {
|
|
1222
1243
|
const timeoutSec = argNum(req.args, 'timeout') ?? 10;
|
|
1223
|
-
const collected =
|
|
1244
|
+
const collected = state.consoleStream.drain({
|
|
1224
1245
|
since,
|
|
1225
1246
|
level: levelFilter,
|
|
1226
1247
|
targetId: resolvedTargetId,
|
|
@@ -1233,7 +1254,7 @@ export class AgentViewServer {
|
|
|
1233
1254
|
return { ok: true, data: formatConsoleMessages(collected.slice(0, earlyMatch + 1)) };
|
|
1234
1255
|
}
|
|
1235
1256
|
const timedOut = await new Promise((resolveFollow) => {
|
|
1236
|
-
const dispose =
|
|
1257
|
+
const dispose = state.consoleStream.subscribe((msg) => {
|
|
1237
1258
|
if (resolvedTargetId && msg.targetId !== resolvedTargetId)
|
|
1238
1259
|
return;
|
|
1239
1260
|
if (levelFilter && !levelFilter.has(msg.level))
|
|
@@ -1258,7 +1279,7 @@ export class AgentViewServer {
|
|
|
1258
1279
|
}
|
|
1259
1280
|
return { ok: true, data: formatConsoleMessages(collected) };
|
|
1260
1281
|
}
|
|
1261
|
-
const messages =
|
|
1282
|
+
const messages = state.consoleStream.drain({
|
|
1262
1283
|
since,
|
|
1263
1284
|
level: levelFilter,
|
|
1264
1285
|
targetId: resolvedTargetId,
|
|
@@ -1269,6 +1290,7 @@ export class AgentViewServer {
|
|
|
1269
1290
|
const cwd = argStr(req.args, 'cwd');
|
|
1270
1291
|
const config = cwd ? readConfig(resolve(cwd)) : null;
|
|
1271
1292
|
await this.ensureNetworkAttached(req, config);
|
|
1293
|
+
const state = this.stateFor(req.port);
|
|
1272
1294
|
let resolvedTargetId;
|
|
1273
1295
|
const targetQuery = argStr(req.args, 'target') ?? argStr(req.args, 'window');
|
|
1274
1296
|
if (targetQuery) {
|
|
@@ -1281,18 +1303,18 @@ export class AgentViewServer {
|
|
|
1281
1303
|
}
|
|
1282
1304
|
const reqN = argNum(req.args, 'req');
|
|
1283
1305
|
if (reqN !== undefined) {
|
|
1284
|
-
const ref =
|
|
1306
|
+
const ref = state.networkRefs.get(reqN);
|
|
1285
1307
|
if (!ref) {
|
|
1286
1308
|
return { ok: false, error: `Invalid req: ${reqN}. Run \`agent-view network\` to get fresh handles.` };
|
|
1287
1309
|
}
|
|
1288
|
-
const entry =
|
|
1310
|
+
const entry = state.networkStream.getEntry(ref.targetId, ref.requestId);
|
|
1289
1311
|
if (!entry) {
|
|
1290
1312
|
return { ok: false, error: `Request ${reqN} is no longer buffered (evicted or app restarted).` };
|
|
1291
1313
|
}
|
|
1292
1314
|
return { ok: true, data: formatNetworkDetail(entry, { rawHeaders: argBool(req.args, 'rawHeaders') ?? false }) };
|
|
1293
1315
|
}
|
|
1294
1316
|
if (argBool(req.args, 'clear')) {
|
|
1295
|
-
|
|
1317
|
+
state.networkStream.clear(resolvedTargetId);
|
|
1296
1318
|
return { ok: true, data: 'Network buffer cleared' };
|
|
1297
1319
|
}
|
|
1298
1320
|
const filter = {
|
|
@@ -1312,31 +1334,33 @@ export class AgentViewServer {
|
|
|
1312
1334
|
if (follow) {
|
|
1313
1335
|
return this.followNetwork(req, filter, maxLines, untilPattern);
|
|
1314
1336
|
}
|
|
1315
|
-
const entries =
|
|
1316
|
-
return { ok: true, data: this.renderNetworkList(entries, maxLines) };
|
|
1337
|
+
const entries = state.networkStream.drain(filter);
|
|
1338
|
+
return { ok: true, data: this.renderNetworkList(req.port, entries, maxLines) };
|
|
1317
1339
|
}
|
|
1318
|
-
renderNetworkList(entries, maxLines) {
|
|
1319
|
-
const
|
|
1320
|
-
|
|
1340
|
+
renderNetworkList(port, entries, maxLines) {
|
|
1341
|
+
const state = this.stateFor(port);
|
|
1342
|
+
const { text, refs, nextRef } = formatNetworkList(entries, { startRef: state.networkNextRef, maxLines });
|
|
1343
|
+
state.networkRefs.clear();
|
|
1321
1344
|
for (const r of refs)
|
|
1322
|
-
|
|
1323
|
-
|
|
1345
|
+
state.networkRefs.set(r.ref, { targetId: r.targetId, requestId: r.requestId });
|
|
1346
|
+
state.networkNextRef = nextRef;
|
|
1324
1347
|
return text;
|
|
1325
1348
|
}
|
|
1326
1349
|
async followNetwork(req, filter, maxLines, untilPattern) {
|
|
1327
1350
|
const timeoutSec = argNum(req.args, 'timeout') ?? 10;
|
|
1328
1351
|
const matcher = untilPattern ? buildMatcher(untilPattern) : null;
|
|
1329
1352
|
const matchText = (e) => `${e.method ?? (e.isWebSocket ? 'WS' : e.isEventSource ? 'SSE' : '')} ${e.status ?? e.state} ${e.url}`;
|
|
1353
|
+
const state = this.stateFor(req.port);
|
|
1330
1354
|
if (matcher) {
|
|
1331
|
-
const pre =
|
|
1355
|
+
const pre = state.networkStream.drain(filter);
|
|
1332
1356
|
const hit = pre.findIndex(e => matcher(matchText(e)));
|
|
1333
1357
|
if (hit !== -1)
|
|
1334
|
-
return { ok: true, data: this.renderNetworkList(pre.slice(0, hit + 1), maxLines) };
|
|
1358
|
+
return { ok: true, data: this.renderNetworkList(req.port, pre.slice(0, hit + 1), maxLines) };
|
|
1335
1359
|
}
|
|
1336
1360
|
const matched = await new Promise((resolveFollow) => {
|
|
1337
1361
|
const dispose = matcher
|
|
1338
|
-
?
|
|
1339
|
-
const cur =
|
|
1362
|
+
? state.networkStream.subscribe(() => {
|
|
1363
|
+
const cur = state.networkStream.drain(filter);
|
|
1340
1364
|
if (cur.some(e => matcher(matchText(e)))) {
|
|
1341
1365
|
clearTimeout(timer);
|
|
1342
1366
|
dispose();
|
|
@@ -1353,8 +1377,8 @@ export class AgentViewServer {
|
|
|
1353
1377
|
if (matcher && !matched) {
|
|
1354
1378
|
return { ok: false, error: `Timeout: pattern not seen in ${timeoutSec}s` };
|
|
1355
1379
|
}
|
|
1356
|
-
const entries =
|
|
1357
|
-
return { ok: true, data: this.renderNetworkList(entries, maxLines) };
|
|
1380
|
+
const entries = state.networkStream.drain(filter);
|
|
1381
|
+
return { ok: true, data: this.renderNetworkList(req.port, entries, maxLines) };
|
|
1358
1382
|
}
|
|
1359
1383
|
/**
|
|
1360
1384
|
* Durable side of the console feed. `console` answers from a ring buffer that dies with the
|
|
@@ -1367,25 +1391,35 @@ export class AgentViewServer {
|
|
|
1367
1391
|
const projectDir = cwd ? resolve(cwd) : process.cwd();
|
|
1368
1392
|
const config = cwd ? readConfig(projectDir) : null;
|
|
1369
1393
|
const explicitFile = argStr(req.args, 'file');
|
|
1370
|
-
// An active recording owns the feed path — only an explicit --file overrides it.
|
|
1394
|
+
// An active recording owns the feed path — only an explicit --file overrides it. "Active"
|
|
1395
|
+
// means *this port's* recording: another slot's recorder must never redirect this feed.
|
|
1396
|
+
const state = this.stateFor(req.port);
|
|
1371
1397
|
const file = explicitFile
|
|
1372
1398
|
? resolveLogFile(projectDir, explicitFile)
|
|
1373
|
-
:
|
|
1399
|
+
: state.logRecorder?.file ?? resolveLogFile(projectDir, config?.logFile);
|
|
1374
1400
|
switch (action) {
|
|
1375
1401
|
case 'start': return this.startLogRecording(req, config, file);
|
|
1376
|
-
case 'stop': return this.stopLogRecording();
|
|
1377
|
-
case 'status': return { ok: true, data:
|
|
1378
|
-
case 'clear': return this.clearLogFeed(file);
|
|
1402
|
+
case 'stop': return this.stopLogRecording(req.port);
|
|
1403
|
+
case 'status': return { ok: true, data: state.logRecorder ? formatRecorderStatus(state.logRecorder.status()) : formatIdleFeed(file) };
|
|
1404
|
+
case 'clear': return this.clearLogFeed(req.port, file);
|
|
1379
1405
|
case 'tail': return this.tailLogFeed(req, file);
|
|
1380
1406
|
default: return { ok: false, error: `Unknown logs action: ${action}` };
|
|
1381
1407
|
}
|
|
1382
1408
|
}
|
|
1383
1409
|
async startLogRecording(req, config, file) {
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1410
|
+
const state = this.stateFor(req.port);
|
|
1411
|
+
if (state.logRecorder) {
|
|
1412
|
+
if (state.logRecorder.file === file) {
|
|
1413
|
+
return { ok: true, data: `Already recording\n${formatRecorderStatus(state.logRecorder.status())}` };
|
|
1414
|
+
}
|
|
1415
|
+
return { ok: false, error: `Already recording into ${state.logRecorder.file}. Run \`agent-view logs stop\` first.` };
|
|
1416
|
+
}
|
|
1417
|
+
// Two checkouts pointed at one feed file interleave records and truncate each other, which is
|
|
1418
|
+
// exactly the cross-slot corruption port-scoping removes elsewhere — refuse it instead.
|
|
1419
|
+
for (const [otherPort, other] of this.portStates) {
|
|
1420
|
+
if (otherPort !== req.port && other.logRecorder?.file === file) {
|
|
1421
|
+
return { ok: false, error: `Port ${otherPort} is already recording into ${file}. Use a per-slot feed path (--file) or stop that recording first.` };
|
|
1387
1422
|
}
|
|
1388
|
-
return { ok: false, error: `Already recording into ${this.logRecorder.file}. Run \`agent-view logs stop\` first.` };
|
|
1389
1423
|
}
|
|
1390
1424
|
const probes = parseProbes(req.args);
|
|
1391
1425
|
if (probes.length > 0 && !config?.allowEval) {
|
|
@@ -1401,8 +1435,8 @@ export class AgentViewServer {
|
|
|
1401
1435
|
}
|
|
1402
1436
|
resolvedTargetId = match.target.id;
|
|
1403
1437
|
}
|
|
1404
|
-
if (
|
|
1405
|
-
|
|
1438
|
+
if (state.consoleStream.attachedCount === 0) {
|
|
1439
|
+
state.consoleStream = new ConsoleStream({ capacity: config?.consoleBufferSize ?? 500 });
|
|
1406
1440
|
}
|
|
1407
1441
|
const allowedTypes = this.resolveConsoleTypes(req, config);
|
|
1408
1442
|
const recorder = new LogRecorder({
|
|
@@ -1418,7 +1452,7 @@ export class AgentViewServer {
|
|
|
1418
1452
|
allowedTypes,
|
|
1419
1453
|
targetId: resolvedTargetId,
|
|
1420
1454
|
}),
|
|
1421
|
-
subscribe: (handler) =>
|
|
1455
|
+
subscribe: (handler) => state.consoleStream.subscribe(handler),
|
|
1422
1456
|
});
|
|
1423
1457
|
try {
|
|
1424
1458
|
await recorder.start();
|
|
@@ -1427,23 +1461,25 @@ export class AgentViewServer {
|
|
|
1427
1461
|
recorder.stop('start failed');
|
|
1428
1462
|
return { ok: false, error: `Could not start recording into ${file}: ${err instanceof Error ? err.message : String(err)}` };
|
|
1429
1463
|
}
|
|
1430
|
-
|
|
1464
|
+
state.logRecorder = recorder;
|
|
1431
1465
|
this.resetIdleTimer();
|
|
1432
1466
|
return { ok: true, data: formatRecorderStatus(recorder.status()) };
|
|
1433
1467
|
}
|
|
1434
|
-
async stopLogRecording() {
|
|
1435
|
-
|
|
1468
|
+
async stopLogRecording(port) {
|
|
1469
|
+
const state = this.stateFor(port);
|
|
1470
|
+
if (!state.logRecorder)
|
|
1436
1471
|
return { ok: true, data: 'Not recording' };
|
|
1437
|
-
const { file, lines } =
|
|
1438
|
-
|
|
1439
|
-
|
|
1472
|
+
const { file, lines } = state.logRecorder.status();
|
|
1473
|
+
state.logRecorder.stop('stop requested');
|
|
1474
|
+
state.logRecorder = null;
|
|
1440
1475
|
this.resetIdleTimer();
|
|
1441
1476
|
return { ok: true, data: `Recording stopped — ${lines} lines in ${file}` };
|
|
1442
1477
|
}
|
|
1443
|
-
async clearLogFeed(file) {
|
|
1444
|
-
this.
|
|
1445
|
-
|
|
1446
|
-
|
|
1478
|
+
async clearLogFeed(port, file) {
|
|
1479
|
+
const state = this.stateFor(port);
|
|
1480
|
+
state.consoleStream.clear();
|
|
1481
|
+
if (state.logRecorder?.file === file) {
|
|
1482
|
+
state.logRecorder.clearFeed();
|
|
1447
1483
|
return { ok: true, data: `Feed cleared, recording continues — ${file}` };
|
|
1448
1484
|
}
|
|
1449
1485
|
if (!existsSync(file)) {
|
|
@@ -1465,6 +1501,7 @@ export class AgentViewServer {
|
|
|
1465
1501
|
}
|
|
1466
1502
|
since = parsed;
|
|
1467
1503
|
}
|
|
1504
|
+
const state = this.stateFor(req.port);
|
|
1468
1505
|
const { lines, scanTruncated } = readFeedLines(file);
|
|
1469
1506
|
const selected = filterLogLines(lines, {
|
|
1470
1507
|
grep: argStr(req.args, 'grep'),
|
|
@@ -1480,7 +1517,7 @@ export class AgentViewServer {
|
|
|
1480
1517
|
dropped > 0 ? `Output cap hit — ${dropped} older matching records omitted.` : null,
|
|
1481
1518
|
scanTruncated ? `Feed exceeds the scan window — older records are only in ${file}.` : null,
|
|
1482
1519
|
// Without this, a static feed reads as "the app went quiet" instead of "nobody is recording".
|
|
1483
|
-
|
|
1520
|
+
state.logRecorder?.file === file ? null : 'Not recording — this feed is static. Run `agent-view logs start`.',
|
|
1484
1521
|
].filter((w) => w !== null);
|
|
1485
1522
|
return { ok: true, data: text, warning: warnings.length > 0 ? warnings.join(' ') : undefined };
|
|
1486
1523
|
}
|
|
@@ -1494,11 +1531,16 @@ export class AgentViewServer {
|
|
|
1494
1531
|
for (const watch of [...this.activeWatches]) {
|
|
1495
1532
|
watch.stop(StopReason.ServerShutdown, false);
|
|
1496
1533
|
}
|
|
1497
|
-
this.
|
|
1498
|
-
|
|
1534
|
+
for (const state of this.portStates.values()) {
|
|
1535
|
+
state.logRecorder?.stop('server shutdown');
|
|
1536
|
+
state.logRecorder = null;
|
|
1537
|
+
}
|
|
1499
1538
|
await unlink(TOKEN_PATH).catch(() => { });
|
|
1500
|
-
this.
|
|
1501
|
-
|
|
1539
|
+
for (const state of this.portStates.values()) {
|
|
1540
|
+
state.consoleStream.detach();
|
|
1541
|
+
state.networkStream.detach();
|
|
1542
|
+
}
|
|
1543
|
+
this.portStates.clear();
|
|
1502
1544
|
for (const cached of this.connections.values()) {
|
|
1503
1545
|
try {
|
|
1504
1546
|
await cached.session.close();
|