adhdev 0.8.25 → 0.8.28
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/cli/index.js +2794 -720
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +1240 -615
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/vendor/session-host-daemon/index.d.mts +16 -0
- package/vendor/session-host-daemon/index.d.ts +16 -0
- package/vendor/session-host-daemon/index.js +200 -4
- package/vendor/session-host-daemon/index.js.map +1 -1
- package/vendor/session-host-daemon/index.mjs +200 -4
- package/vendor/session-host-daemon/index.mjs.map +1 -1
- package/vendor/session-host-daemon/node_modules/@adhdev/session-host-core/index.d.mts +72 -1
- package/vendor/session-host-daemon/node_modules/@adhdev/session-host-core/index.d.ts +72 -1
- package/vendor/session-host-daemon/node_modules/@adhdev/session-host-core/index.js.map +1 -1
package/package.json
CHANGED
|
@@ -7,6 +7,7 @@ interface SessionHostServerOptions {
|
|
|
7
7
|
appName?: string;
|
|
8
8
|
}
|
|
9
9
|
declare class SessionHostServer extends EventEmitter {
|
|
10
|
+
private static readonly MAX_RECENT_DIAGNOSTICS;
|
|
10
11
|
readonly endpoint: SessionHostEndpoint;
|
|
11
12
|
readonly registry: SessionHostRegistry;
|
|
12
13
|
private runtimes;
|
|
@@ -14,6 +15,11 @@ declare class SessionHostServer extends EventEmitter {
|
|
|
14
15
|
private ipcServer;
|
|
15
16
|
private sockets;
|
|
16
17
|
private persistTimers;
|
|
18
|
+
private readonly startedAt;
|
|
19
|
+
private recentLogs;
|
|
20
|
+
private recentRequests;
|
|
21
|
+
private recentTransitions;
|
|
22
|
+
private exitWaiters;
|
|
17
23
|
constructor(options?: SessionHostServerOptions);
|
|
18
24
|
start(): Promise<void>;
|
|
19
25
|
stop(): Promise<void>;
|
|
@@ -24,8 +30,18 @@ declare class SessionHostServer extends EventEmitter {
|
|
|
24
30
|
private handleIncomingRequest;
|
|
25
31
|
private schedulePersist;
|
|
26
32
|
private persistNow;
|
|
33
|
+
private getHostDiagnostics;
|
|
34
|
+
private getRequestSessionId;
|
|
35
|
+
private getRequestClientId;
|
|
36
|
+
private pushRecent;
|
|
37
|
+
private recordHostLog;
|
|
38
|
+
private recordRequestTrace;
|
|
39
|
+
private recordRuntimeTransition;
|
|
40
|
+
private waitForRuntimeExit;
|
|
41
|
+
private resolveExitWaiters;
|
|
27
42
|
private getSnapshot;
|
|
28
43
|
flushAllPersistence(): void;
|
|
44
|
+
private restartRuntime;
|
|
29
45
|
private restorePersistedRuntimes;
|
|
30
46
|
private buildPayloadFromRecord;
|
|
31
47
|
private startRuntime;
|
|
@@ -7,6 +7,7 @@ interface SessionHostServerOptions {
|
|
|
7
7
|
appName?: string;
|
|
8
8
|
}
|
|
9
9
|
declare class SessionHostServer extends EventEmitter {
|
|
10
|
+
private static readonly MAX_RECENT_DIAGNOSTICS;
|
|
10
11
|
readonly endpoint: SessionHostEndpoint;
|
|
11
12
|
readonly registry: SessionHostRegistry;
|
|
12
13
|
private runtimes;
|
|
@@ -14,6 +15,11 @@ declare class SessionHostServer extends EventEmitter {
|
|
|
14
15
|
private ipcServer;
|
|
15
16
|
private sockets;
|
|
16
17
|
private persistTimers;
|
|
18
|
+
private readonly startedAt;
|
|
19
|
+
private recentLogs;
|
|
20
|
+
private recentRequests;
|
|
21
|
+
private recentTransitions;
|
|
22
|
+
private exitWaiters;
|
|
17
23
|
constructor(options?: SessionHostServerOptions);
|
|
18
24
|
start(): Promise<void>;
|
|
19
25
|
stop(): Promise<void>;
|
|
@@ -24,8 +30,18 @@ declare class SessionHostServer extends EventEmitter {
|
|
|
24
30
|
private handleIncomingRequest;
|
|
25
31
|
private schedulePersist;
|
|
26
32
|
private persistNow;
|
|
33
|
+
private getHostDiagnostics;
|
|
34
|
+
private getRequestSessionId;
|
|
35
|
+
private getRequestClientId;
|
|
36
|
+
private pushRecent;
|
|
37
|
+
private recordHostLog;
|
|
38
|
+
private recordRequestTrace;
|
|
39
|
+
private recordRuntimeTransition;
|
|
40
|
+
private waitForRuntimeExit;
|
|
41
|
+
private resolveExitWaiters;
|
|
27
42
|
private getSnapshot;
|
|
28
43
|
flushAllPersistence(): void;
|
|
44
|
+
private restartRuntime;
|
|
29
45
|
private restorePersistedRuntimes;
|
|
30
46
|
private buildPayloadFromRecord;
|
|
31
47
|
private startRuntime;
|
|
@@ -254,6 +254,20 @@ var PtySessionRuntime = class {
|
|
|
254
254
|
if (!this.ptyProcess) return;
|
|
255
255
|
this.ptyProcess.kill();
|
|
256
256
|
}
|
|
257
|
+
sendSignal(signal) {
|
|
258
|
+
if (!this.ptyProcess) throw new Error(`Session not running: ${this.sessionId}`);
|
|
259
|
+
const normalized = String(signal || "").trim().toUpperCase();
|
|
260
|
+
if (!normalized) throw new Error("signal is required");
|
|
261
|
+
try {
|
|
262
|
+
process.kill(this.ptyProcess.pid, normalized);
|
|
263
|
+
} catch {
|
|
264
|
+
if (normalized === "SIGTERM" || normalized === "SIGKILL") {
|
|
265
|
+
this.ptyProcess.kill();
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
throw new Error(`Unsupported signal for runtime ${this.sessionId}: ${normalized}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
257
271
|
getSnapshotText() {
|
|
258
272
|
return this.screenMirror?.formatVT() || "";
|
|
259
273
|
}
|
|
@@ -322,7 +336,8 @@ var SessionHostStorage = class {
|
|
|
322
336
|
};
|
|
323
337
|
|
|
324
338
|
// src/server.ts
|
|
325
|
-
var SessionHostServer = class extends import_events.EventEmitter {
|
|
339
|
+
var SessionHostServer = class _SessionHostServer extends import_events.EventEmitter {
|
|
340
|
+
static MAX_RECENT_DIAGNOSTICS = 200;
|
|
326
341
|
endpoint;
|
|
327
342
|
registry = new import_session_host_core2.SessionHostRegistry();
|
|
328
343
|
runtimes = /* @__PURE__ */ new Map();
|
|
@@ -330,6 +345,11 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
330
345
|
ipcServer = null;
|
|
331
346
|
sockets = /* @__PURE__ */ new Set();
|
|
332
347
|
persistTimers = /* @__PURE__ */ new Map();
|
|
348
|
+
startedAt = Date.now();
|
|
349
|
+
recentLogs = [];
|
|
350
|
+
recentRequests = [];
|
|
351
|
+
recentTransitions = [];
|
|
352
|
+
exitWaiters = /* @__PURE__ */ new Map();
|
|
333
353
|
constructor(options = {}) {
|
|
334
354
|
super();
|
|
335
355
|
this.endpoint = options.endpoint || (0, import_session_host_core2.getDefaultSessionHostEndpoint)(options.appName || "adhdev");
|
|
@@ -357,12 +377,12 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
357
377
|
this.ipcServer?.once("error", reject);
|
|
358
378
|
this.ipcServer?.listen(this.endpoint.path);
|
|
359
379
|
});
|
|
360
|
-
this.
|
|
380
|
+
this.recordHostLog("info", `session host endpoint ready: ${this.endpoint.path}`);
|
|
361
381
|
setTimeout(() => {
|
|
362
382
|
try {
|
|
363
383
|
this.restorePersistedRuntimes();
|
|
364
384
|
} catch (error) {
|
|
365
|
-
this.
|
|
385
|
+
this.recordHostLog("error", `session host restore failed: ${error?.message || String(error)}`);
|
|
366
386
|
}
|
|
367
387
|
}, 0);
|
|
368
388
|
}
|
|
@@ -403,12 +423,14 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
403
423
|
const record = this.registry.createSession(request.payload);
|
|
404
424
|
this.schedulePersist(record.sessionId);
|
|
405
425
|
this.emitEvent({ type: "session_created", sessionId: record.sessionId, record });
|
|
426
|
+
this.recordRuntimeTransition(record.sessionId, "create_session", "starting", `provider=${record.providerType}`, true);
|
|
406
427
|
try {
|
|
407
428
|
const startedRecord = this.startRuntime(record, request.payload, "session_started");
|
|
408
429
|
return { success: true, result: startedRecord };
|
|
409
430
|
} catch (error) {
|
|
410
431
|
this.registry.markStopped(record.sessionId, "failed");
|
|
411
432
|
this.persistNow(record.sessionId);
|
|
433
|
+
this.recordRuntimeTransition(record.sessionId, "create_session_failed", "failed", void 0, false, error?.message || String(error));
|
|
412
434
|
return { success: false, error: error?.message || String(error) };
|
|
413
435
|
}
|
|
414
436
|
}
|
|
@@ -421,32 +443,39 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
421
443
|
if (client) {
|
|
422
444
|
this.emitEvent({ type: "client_attached", sessionId: record.sessionId, client });
|
|
423
445
|
}
|
|
446
|
+
this.recordRuntimeTransition(record.sessionId, "attach_client", record.lifecycle, request.payload.clientId, true);
|
|
424
447
|
return { success: true, result: record };
|
|
425
448
|
}
|
|
426
449
|
case "detach_session": {
|
|
427
450
|
const record = this.registry.detachClient(request.payload);
|
|
428
451
|
this.schedulePersist(record.sessionId);
|
|
429
452
|
this.emitEvent({ type: "client_detached", sessionId: record.sessionId, clientId: request.payload.clientId });
|
|
453
|
+
this.recordRuntimeTransition(record.sessionId, "detach_client", record.lifecycle, request.payload.clientId, true);
|
|
430
454
|
return { success: true, result: record };
|
|
431
455
|
}
|
|
432
456
|
case "acquire_write": {
|
|
433
457
|
const record = this.registry.acquireWrite(request.payload);
|
|
434
458
|
this.persistNow(record.sessionId);
|
|
435
459
|
this.emitEvent({ type: "write_owner_changed", sessionId: record.sessionId, owner: record.writeOwner });
|
|
460
|
+
this.recordRuntimeTransition(record.sessionId, "acquire_write", record.lifecycle, request.payload.clientId, true);
|
|
436
461
|
return { success: true, result: record };
|
|
437
462
|
}
|
|
438
463
|
case "release_write": {
|
|
439
464
|
const record = this.registry.releaseWrite(request.payload);
|
|
440
465
|
this.persistNow(record.sessionId);
|
|
441
466
|
this.emitEvent({ type: "write_owner_changed", sessionId: record.sessionId, owner: record.writeOwner });
|
|
467
|
+
this.recordRuntimeTransition(record.sessionId, "release_write", record.lifecycle, request.payload.clientId, true);
|
|
442
468
|
return { success: true, result: record };
|
|
443
469
|
}
|
|
444
470
|
case "get_snapshot":
|
|
445
471
|
return { success: true, result: this.getSnapshot(request.payload.sessionId, request.payload.sinceSeq) };
|
|
472
|
+
case "get_host_diagnostics":
|
|
473
|
+
return { success: true, result: this.getHostDiagnostics(request.payload) };
|
|
446
474
|
case "clear_session_buffer": {
|
|
447
475
|
const record = this.registry.clearBuffer(request.payload.sessionId);
|
|
448
476
|
this.persistNow(record.sessionId);
|
|
449
477
|
this.emitEvent({ type: "session_cleared", sessionId: record.sessionId });
|
|
478
|
+
this.recordRuntimeTransition(record.sessionId, "clear_buffer", record.lifecycle, void 0, true);
|
|
450
479
|
return { success: true, result: record };
|
|
451
480
|
}
|
|
452
481
|
case "update_session_meta": {
|
|
@@ -456,6 +485,7 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
456
485
|
request.payload.replace === true
|
|
457
486
|
);
|
|
458
487
|
this.persistNow(record.sessionId);
|
|
488
|
+
this.recordRuntimeTransition(record.sessionId, "update_meta", record.lifecycle, void 0, true);
|
|
459
489
|
return { success: true, result: record };
|
|
460
490
|
}
|
|
461
491
|
case "send_input": {
|
|
@@ -500,6 +530,7 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
500
530
|
this.persistNow(request.payload.sessionId);
|
|
501
531
|
this.requireRuntime(request.payload.sessionId).stop();
|
|
502
532
|
this.emitEvent({ type: "session_stopped", sessionId: request.payload.sessionId });
|
|
533
|
+
this.recordRuntimeTransition(request.payload.sessionId, "stop_session", "stopping", void 0, true);
|
|
503
534
|
return { success: true, result: this.registry.getSession(request.payload.sessionId) };
|
|
504
535
|
}
|
|
505
536
|
case "resume_session": {
|
|
@@ -511,8 +542,38 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
511
542
|
return { success: true, result: existing };
|
|
512
543
|
}
|
|
513
544
|
const resumed = this.startRuntime(existing, this.buildPayloadFromRecord(existing), "session_resumed");
|
|
545
|
+
this.recordRuntimeTransition(request.payload.sessionId, "resume_session", resumed.lifecycle, void 0, true);
|
|
514
546
|
return { success: true, result: resumed };
|
|
515
547
|
}
|
|
548
|
+
case "restart_session": {
|
|
549
|
+
const restarted = await this.restartRuntime(request.payload.sessionId);
|
|
550
|
+
return { success: true, result: restarted };
|
|
551
|
+
}
|
|
552
|
+
case "send_signal": {
|
|
553
|
+
const runtime = this.requireRuntime(request.payload.sessionId);
|
|
554
|
+
runtime.sendSignal(request.payload.signal);
|
|
555
|
+
const record = this.registry.getSession(request.payload.sessionId);
|
|
556
|
+
this.recordRuntimeTransition(request.payload.sessionId, "send_signal", record?.lifecycle, request.payload.signal, true);
|
|
557
|
+
return { success: true, result: record };
|
|
558
|
+
}
|
|
559
|
+
case "force_detach_client": {
|
|
560
|
+
const session = this.registry.getSession(request.payload.sessionId);
|
|
561
|
+
if (session?.writeOwner?.clientId === request.payload.clientId) {
|
|
562
|
+
const released = this.registry.releaseWrite({
|
|
563
|
+
sessionId: request.payload.sessionId,
|
|
564
|
+
clientId: request.payload.clientId
|
|
565
|
+
});
|
|
566
|
+
this.emitEvent({ type: "write_owner_changed", sessionId: released.sessionId, owner: released.writeOwner });
|
|
567
|
+
}
|
|
568
|
+
const record = this.registry.detachClient({
|
|
569
|
+
sessionId: request.payload.sessionId,
|
|
570
|
+
clientId: request.payload.clientId
|
|
571
|
+
});
|
|
572
|
+
this.schedulePersist(record.sessionId);
|
|
573
|
+
this.emitEvent({ type: "client_detached", sessionId: record.sessionId, clientId: request.payload.clientId });
|
|
574
|
+
this.recordRuntimeTransition(record.sessionId, "force_detach_client", record.lifecycle, request.payload.clientId, true);
|
|
575
|
+
return { success: true, result: record };
|
|
576
|
+
}
|
|
516
577
|
default:
|
|
517
578
|
return { success: false, error: `Unsupported session host request: ${request?.type || "unknown"}` };
|
|
518
579
|
}
|
|
@@ -539,7 +600,18 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
539
600
|
this.emit("event", event);
|
|
540
601
|
}
|
|
541
602
|
async handleIncomingRequest(socket, envelope) {
|
|
603
|
+
const startedAt = Date.now();
|
|
542
604
|
const response = await this.handleRequest(envelope.request);
|
|
605
|
+
this.recordRequestTrace({
|
|
606
|
+
timestamp: startedAt,
|
|
607
|
+
requestId: envelope.requestId,
|
|
608
|
+
type: envelope.request.type,
|
|
609
|
+
sessionId: this.getRequestSessionId(envelope.request),
|
|
610
|
+
clientId: this.getRequestClientId(envelope.request),
|
|
611
|
+
success: response.success,
|
|
612
|
+
durationMs: Math.max(0, Date.now() - startedAt),
|
|
613
|
+
error: response.success ? void 0 : response.error
|
|
614
|
+
});
|
|
543
615
|
(0, import_session_host_core2.writeEnvelope)(socket, (0, import_session_host_core2.createResponseEnvelope)(envelope.requestId, response));
|
|
544
616
|
}
|
|
545
617
|
schedulePersist(sessionId) {
|
|
@@ -556,6 +628,99 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
556
628
|
const snapshot = this.getSnapshot(sessionId);
|
|
557
629
|
this.storage.save(record, snapshot);
|
|
558
630
|
}
|
|
631
|
+
getHostDiagnostics(payload) {
|
|
632
|
+
const limit = Math.max(1, Math.min(200, Number(payload?.limit) || 50));
|
|
633
|
+
return {
|
|
634
|
+
hostStartedAt: this.startedAt,
|
|
635
|
+
endpoint: this.endpoint.path,
|
|
636
|
+
runtimeCount: this.runtimes.size,
|
|
637
|
+
sessions: payload?.includeSessions === false ? void 0 : this.registry.listSessions(),
|
|
638
|
+
recentLogs: this.recentLogs.slice(-limit),
|
|
639
|
+
recentRequests: this.recentRequests.slice(-limit),
|
|
640
|
+
recentTransitions: this.recentTransitions.slice(-limit)
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
getRequestSessionId(request) {
|
|
644
|
+
const payload = request.payload;
|
|
645
|
+
return typeof payload?.sessionId === "string" ? payload.sessionId : void 0;
|
|
646
|
+
}
|
|
647
|
+
getRequestClientId(request) {
|
|
648
|
+
const payload = request.payload;
|
|
649
|
+
return typeof payload?.clientId === "string" ? payload.clientId : void 0;
|
|
650
|
+
}
|
|
651
|
+
pushRecent(bucket, entry) {
|
|
652
|
+
bucket.push(entry);
|
|
653
|
+
if (bucket.length > _SessionHostServer.MAX_RECENT_DIAGNOSTICS) {
|
|
654
|
+
bucket.splice(0, bucket.length - _SessionHostServer.MAX_RECENT_DIAGNOSTICS);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
recordHostLog(level, message, sessionId, data) {
|
|
658
|
+
const entry = {
|
|
659
|
+
timestamp: Date.now(),
|
|
660
|
+
level,
|
|
661
|
+
message,
|
|
662
|
+
sessionId,
|
|
663
|
+
data
|
|
664
|
+
};
|
|
665
|
+
this.pushRecent(this.recentLogs, entry);
|
|
666
|
+
this.emitEvent({ type: "host_log", entry });
|
|
667
|
+
this.emit("log", `[${level}] ${message}`);
|
|
668
|
+
}
|
|
669
|
+
recordRequestTrace(trace) {
|
|
670
|
+
this.pushRecent(this.recentRequests, trace);
|
|
671
|
+
this.emitEvent({ type: "request_trace", trace });
|
|
672
|
+
if (!trace.success) {
|
|
673
|
+
this.recordHostLog(
|
|
674
|
+
"warn",
|
|
675
|
+
`request ${trace.type} failed after ${trace.durationMs}ms${trace.error ? `: ${trace.error}` : ""}`,
|
|
676
|
+
trace.sessionId,
|
|
677
|
+
{ requestId: trace.requestId, clientId: trace.clientId }
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
recordRuntimeTransition(sessionId, action, lifecycle, detail, success = true, error) {
|
|
682
|
+
const transition = {
|
|
683
|
+
timestamp: Date.now(),
|
|
684
|
+
sessionId,
|
|
685
|
+
action,
|
|
686
|
+
lifecycle,
|
|
687
|
+
detail,
|
|
688
|
+
success,
|
|
689
|
+
error
|
|
690
|
+
};
|
|
691
|
+
this.pushRecent(this.recentTransitions, transition);
|
|
692
|
+
this.emitEvent({ type: "runtime_transition", transition });
|
|
693
|
+
}
|
|
694
|
+
waitForRuntimeExit(sessionId, timeoutMs = 5e3) {
|
|
695
|
+
if (!this.runtimes.has(sessionId)) {
|
|
696
|
+
return Promise.resolve(this.registry.getSession(sessionId)?.lifecycle === "failed" ? 1 : 0);
|
|
697
|
+
}
|
|
698
|
+
return new Promise((resolve, reject) => {
|
|
699
|
+
const timeout = setTimeout(() => {
|
|
700
|
+
const waiters2 = this.exitWaiters.get(sessionId) || [];
|
|
701
|
+
this.exitWaiters.set(sessionId, waiters2.filter((waiter) => waiter !== onExit));
|
|
702
|
+
reject(new Error(`Timed out waiting for runtime ${sessionId} to exit`));
|
|
703
|
+
}, timeoutMs);
|
|
704
|
+
const onExit = (exitCode) => {
|
|
705
|
+
clearTimeout(timeout);
|
|
706
|
+
resolve(exitCode);
|
|
707
|
+
};
|
|
708
|
+
const waiters = this.exitWaiters.get(sessionId) || [];
|
|
709
|
+
waiters.push(onExit);
|
|
710
|
+
this.exitWaiters.set(sessionId, waiters);
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
resolveExitWaiters(sessionId, exitCode) {
|
|
714
|
+
const waiters = this.exitWaiters.get(sessionId);
|
|
715
|
+
if (!waiters?.length) return;
|
|
716
|
+
this.exitWaiters.delete(sessionId);
|
|
717
|
+
for (const waiter of waiters) {
|
|
718
|
+
try {
|
|
719
|
+
waiter(exitCode);
|
|
720
|
+
} catch {
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
559
724
|
getSnapshot(sessionId, sinceSeq) {
|
|
560
725
|
const snapshot = this.registry.getSnapshot(sessionId, sinceSeq);
|
|
561
726
|
const record = this.registry.getSession(sessionId);
|
|
@@ -591,6 +756,23 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
591
756
|
this.persistNow(record.sessionId);
|
|
592
757
|
}
|
|
593
758
|
}
|
|
759
|
+
async restartRuntime(sessionId) {
|
|
760
|
+
const existing = this.registry.getSession(sessionId);
|
|
761
|
+
if (!existing) {
|
|
762
|
+
throw new Error(`Unknown session: ${sessionId}`);
|
|
763
|
+
}
|
|
764
|
+
if (this.runtimes.has(sessionId)) {
|
|
765
|
+
this.registry.setLifecycle(sessionId, "stopping");
|
|
766
|
+
this.persistNow(sessionId);
|
|
767
|
+
this.recordRuntimeTransition(sessionId, "restart_requested", "stopping", void 0, true);
|
|
768
|
+
this.requireRuntime(sessionId).stop();
|
|
769
|
+
await this.waitForRuntimeExit(sessionId);
|
|
770
|
+
}
|
|
771
|
+
const latest = this.registry.getSession(sessionId) || existing;
|
|
772
|
+
const restarted = this.startRuntime(latest, this.buildPayloadFromRecord(latest), "session_resumed");
|
|
773
|
+
this.recordRuntimeTransition(sessionId, "restart_completed", restarted.lifecycle, void 0, true);
|
|
774
|
+
return restarted;
|
|
775
|
+
}
|
|
594
776
|
restorePersistedRuntimes() {
|
|
595
777
|
const states = this.storage.loadAll();
|
|
596
778
|
const runtimesToResume = [];
|
|
@@ -623,7 +805,7 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
623
805
|
}
|
|
624
806
|
}
|
|
625
807
|
if (skippedOrphanLiveSessions > 0) {
|
|
626
|
-
this.
|
|
808
|
+
this.recordHostLog("warn", `session host skipped ${skippedOrphanLiveSessions} orphan live runtime(s) during restore`);
|
|
627
809
|
}
|
|
628
810
|
for (const { persisted, recoveredRecord } of runtimesToResume) {
|
|
629
811
|
try {
|
|
@@ -642,6 +824,7 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
642
824
|
this.registry.getSnapshot(resumed.sessionId)
|
|
643
825
|
);
|
|
644
826
|
this.persistNow(resumed.sessionId);
|
|
827
|
+
this.recordRuntimeTransition(resumed.sessionId, "restore_auto_resumed", resumed.lifecycle, void 0, true);
|
|
645
828
|
} catch (error) {
|
|
646
829
|
const interrupted = this.registry.setLifecycle(recoveredRecord.sessionId, "interrupted");
|
|
647
830
|
this.registry.restoreSession({
|
|
@@ -654,6 +837,8 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
654
837
|
}
|
|
655
838
|
}, persisted.snapshot);
|
|
656
839
|
this.persistNow(recoveredRecord.sessionId);
|
|
840
|
+
this.recordRuntimeTransition(recoveredRecord.sessionId, "restore_resume_failed", "interrupted", void 0, false, error?.message || String(error));
|
|
841
|
+
this.recordHostLog("error", `restore resume failed for ${recoveredRecord.sessionId}: ${error?.message || String(error)}`, recoveredRecord.sessionId);
|
|
657
842
|
}
|
|
658
843
|
}
|
|
659
844
|
}
|
|
@@ -683,8 +868,17 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
683
868
|
onExit: (exitCode) => {
|
|
684
869
|
this.registry.markStopped(record.sessionId, exitCode === 0 ? "stopped" : "failed");
|
|
685
870
|
this.runtimes.delete(record.sessionId);
|
|
871
|
+
this.resolveExitWaiters(record.sessionId, exitCode);
|
|
686
872
|
this.persistNow(record.sessionId);
|
|
687
873
|
this.emitEvent({ type: "session_exit", sessionId: record.sessionId, exitCode });
|
|
874
|
+
this.recordRuntimeTransition(
|
|
875
|
+
record.sessionId,
|
|
876
|
+
"session_exit",
|
|
877
|
+
exitCode === 0 ? "stopped" : "failed",
|
|
878
|
+
void 0,
|
|
879
|
+
exitCode === 0,
|
|
880
|
+
exitCode === 0 ? void 0 : `exitCode=${exitCode}`
|
|
881
|
+
);
|
|
688
882
|
setTimeout(() => this.storage.remove(record.sessionId), 5e3);
|
|
689
883
|
}
|
|
690
884
|
});
|
|
@@ -694,6 +888,7 @@ var SessionHostServer = class extends import_events.EventEmitter {
|
|
|
694
888
|
const startedRecord = this.registry.markStarted(record.sessionId, pid);
|
|
695
889
|
this.persistNow(record.sessionId);
|
|
696
890
|
this.emitEvent({ type: startEventType, sessionId: record.sessionId, pid });
|
|
891
|
+
this.recordRuntimeTransition(record.sessionId, startEventType, startedRecord.lifecycle, `pid=${pid}`, true);
|
|
697
892
|
return startedRecord;
|
|
698
893
|
}
|
|
699
894
|
};
|
|
@@ -955,6 +1150,7 @@ async function attachRuntime(target, readOnly = false, takeover = false) {
|
|
|
955
1150
|
return { signal, handler };
|
|
956
1151
|
});
|
|
957
1152
|
const unsubscribe = client.onEvent((event) => {
|
|
1153
|
+
if (!("sessionId" in event)) return;
|
|
958
1154
|
if (event.sessionId !== runtimeId) return;
|
|
959
1155
|
if (event.type === "session_output") {
|
|
960
1156
|
if (event.seq <= lastSeq) return;
|