@code-yeongyu/senpi-pty 2026.9.13-2 → 2026.9.15-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/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/registry-detached.d.ts +8 -1
- package/dist/registry-detached.d.ts.map +1 -1
- package/dist/registry-detached.js +45 -15
- package/dist/registry-detached.js.map +1 -1
- package/dist/registry-session.d.ts +12 -0
- package/dist/registry-session.d.ts.map +1 -1
- package/dist/registry-session.js +23 -0
- package/dist/registry-session.js.map +1 -1
- package/dist/registry-types.d.ts +16 -3
- package/dist/registry-types.d.ts.map +1 -1
- package/dist/registry-types.js.map +1 -1
- package/dist/registry.d.ts +8 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +43 -9
- package/dist/registry.js.map +1 -1
- package/dist/session-types.d.ts +8 -0
- package/dist/session-types.d.ts.map +1 -1
- package/dist/session-types.js.map +1 -1
- package/dist/session.d.ts +12 -3
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +67 -5
- package/dist/session.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,10 @@ import { type NativePtyLoadResult } from "./native-loader.ts";
|
|
|
2
2
|
import { TerminalSession as BaseTerminalSession, type TerminalSessionOptions } from "./session.ts";
|
|
3
3
|
export type { NativePtyBinding, NativePtyLoadResult, NativePtyUnavailableDiagnostic, } from "./native-loader.ts";
|
|
4
4
|
export { detectNativePtyRuntime, getNativePtyCandidatePaths, getNativePtyHost, type NativePtyRuntime, NativePtySentinelMismatchError, } from "./native-loader.ts";
|
|
5
|
+
export { isQuarantinedNativeFile, QUARANTINE_ATTRIBUTE } from "./quarantine.ts";
|
|
5
6
|
export { type InitialSessionRegistryEntry, isTerminalSessionExited, SessionRegistry, SessionRegistryCapacityError, type SessionRegistryCreateContext, type SessionRegistryCreateOptions, type SessionRegistryEntry, type SessionRegistryOptions, type SessionRegistrySession, type SessionRegistrySweepOptions, sessionIdPrefix, type TerminalSessionSignal, type TerminalSessionState, type TrackedDetachedChild, } from "./registry.ts";
|
|
6
7
|
export { TerminalScreen, type TerminalScreenOptions, type TerminalScreenSnapshot } from "./screen.ts";
|
|
7
|
-
export { type CreateNativeTerminalSession, createTerminalSession, TerminalSession, type TerminalSessionBackend, type TerminalSessionDataHandler, type TerminalSessionDependencies, type TerminalSessionExit, type TerminalSessionExitError, type TerminalSessionExitState, type TerminalSessionHandle, type TerminalSessionNativeOptions, type TerminalSessionOperationResult, type TerminalSessionOptions, } from "./session.ts";
|
|
8
|
+
export { type CreateNativeTerminalSession, createTerminalSession, TerminalSession, type TerminalSessionBackend, type TerminalSessionDataHandler, type TerminalSessionDependencies, type TerminalSessionExit, type TerminalSessionExitError, type TerminalSessionExitState, type TerminalSessionHandle, type TerminalSessionNativeOptions, type TerminalSessionOperationResult, type TerminalSessionOptions, type TerminalSessionTerminateOptions, } from "./session.ts";
|
|
8
9
|
export { type BunRuntime, type BunRuntimeVersions, type BunTerminal, createBunTerminalSession, ENV_BUN_TERMINAL, isBunTerminalEnabled, } from "./session-bun.ts";
|
|
9
10
|
export type PtySessionOptions = TerminalSessionOptions;
|
|
10
11
|
export declare class PtySession extends BaseTerminalSession {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,eAAe,IAAI,mBAAmB,EAAE,KAAK,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEnG,YAAY,EACX,gBAAgB,EAChB,mBAAmB,EACnB,8BAA8B,GAC9B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,sBAAsB,EACtB,0BAA0B,EAC1B,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,8BAA8B,GAC9B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,KAAK,2BAA2B,EAChC,uBAAuB,EACvB,eAAe,EACf,4BAA4B,EAC5B,KAAK,4BAA4B,EACjC,KAAK,4BAA4B,EACjC,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EAChC,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,GACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,KAAK,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACtG,OAAO,EACN,KAAK,2BAA2B,EAChC,qBAAqB,EACrB,eAAe,EACf,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EAChC,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,4BAA4B,EACjC,KAAK,8BAA8B,EACnC,KAAK,sBAAsB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,eAAe,IAAI,mBAAmB,EAAE,KAAK,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEnG,YAAY,EACX,gBAAgB,EAChB,mBAAmB,EACnB,8BAA8B,GAC9B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,sBAAsB,EACtB,0BAA0B,EAC1B,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,8BAA8B,GAC9B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAChF,OAAO,EACN,KAAK,2BAA2B,EAChC,uBAAuB,EACvB,eAAe,EACf,4BAA4B,EAC5B,KAAK,4BAA4B,EACjC,KAAK,4BAA4B,EACjC,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EAChC,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,GACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,KAAK,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACtG,OAAO,EACN,KAAK,2BAA2B,EAChC,qBAAqB,EACrB,eAAe,EACf,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,EAChC,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,4BAA4B,EACjC,KAAK,8BAA8B,EACnC,KAAK,sBAAsB,EAC3B,KAAK,+BAA+B,GACpC,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,wBAAwB,EACxB,gBAAgB,EAChB,oBAAoB,GACpB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,iBAAiB,GAAG,sBAAsB,CAAC;AAEvD,qBAAa,UAAW,SAAQ,mBAAmB;CAAG;AAEtD,wBAAgB,aAAa,IAAI,mBAAmB,CAEnD"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { loadNativePty } from "./native-loader.js";
|
|
2
2
|
import { TerminalSession as BaseTerminalSession } from "./session.js";
|
|
3
3
|
export { detectNativePtyRuntime, getNativePtyCandidatePaths, getNativePtyHost, NativePtySentinelMismatchError, } from "./native-loader.js";
|
|
4
|
+
export { isQuarantinedNativeFile, QUARANTINE_ATTRIBUTE } from "./quarantine.js";
|
|
4
5
|
export { isTerminalSessionExited, SessionRegistry, SessionRegistryCapacityError, sessionIdPrefix, } from "./registry.js";
|
|
5
6
|
export { TerminalScreen } from "./screen.js";
|
|
6
7
|
export { createTerminalSession, TerminalSession, } from "./session.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA4B,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,eAAe,IAAI,mBAAmB,EAA+B,MAAM,cAAc,CAAC;AAOnG,OAAO,EACN,sBAAsB,EACtB,0BAA0B,EAC1B,gBAAgB,EAEhB,8BAA8B,GAC9B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEN,uBAAuB,EACvB,eAAe,EACf,4BAA4B,EAO5B,eAAe,GAIf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAA2D,MAAM,aAAa,CAAC;AACtG,OAAO,EAEN,qBAAqB,EACrB,eAAe,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA4B,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,eAAe,IAAI,mBAAmB,EAA+B,MAAM,cAAc,CAAC;AAOnG,OAAO,EACN,sBAAsB,EACtB,0BAA0B,EAC1B,gBAAgB,EAEhB,8BAA8B,GAC9B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAEN,uBAAuB,EACvB,eAAe,EACf,4BAA4B,EAO5B,eAAe,GAIf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAA2D,MAAM,aAAa,CAAC;AACtG,OAAO,EAEN,qBAAqB,EACrB,eAAe,GAYf,MAAM,cAAc,CAAC;AACtB,OAAO,EAIN,wBAAwB,EACxB,gBAAgB,EAChB,oBAAoB,GACpB,MAAM,kBAAkB,CAAC;AAI1B,MAAM,OAAO,UAAW,SAAQ,mBAAmB;CAAG;AAEtD,MAAM,UAAU,aAAa;IAC5B,OAAO,aAAa,EAAE,CAAC;AACxB,CAAC","sourcesContent":["import { loadNativePty, type NativePtyLoadResult } from \"./native-loader.ts\";\nimport { TerminalSession as BaseTerminalSession, type TerminalSessionOptions } from \"./session.ts\";\n\nexport type {\n\tNativePtyBinding,\n\tNativePtyLoadResult,\n\tNativePtyUnavailableDiagnostic,\n} from \"./native-loader.ts\";\nexport {\n\tdetectNativePtyRuntime,\n\tgetNativePtyCandidatePaths,\n\tgetNativePtyHost,\n\ttype NativePtyRuntime,\n\tNativePtySentinelMismatchError,\n} from \"./native-loader.ts\";\nexport { isQuarantinedNativeFile, QUARANTINE_ATTRIBUTE } from \"./quarantine.ts\";\nexport {\n\ttype InitialSessionRegistryEntry,\n\tisTerminalSessionExited,\n\tSessionRegistry,\n\tSessionRegistryCapacityError,\n\ttype SessionRegistryCreateContext,\n\ttype SessionRegistryCreateOptions,\n\ttype SessionRegistryEntry,\n\ttype SessionRegistryOptions,\n\ttype SessionRegistrySession,\n\ttype SessionRegistrySweepOptions,\n\tsessionIdPrefix,\n\ttype TerminalSessionSignal,\n\ttype TerminalSessionState,\n\ttype TrackedDetachedChild,\n} from \"./registry.ts\";\nexport { TerminalScreen, type TerminalScreenOptions, type TerminalScreenSnapshot } from \"./screen.ts\";\nexport {\n\ttype CreateNativeTerminalSession,\n\tcreateTerminalSession,\n\tTerminalSession,\n\ttype TerminalSessionBackend,\n\ttype TerminalSessionDataHandler,\n\ttype TerminalSessionDependencies,\n\ttype TerminalSessionExit,\n\ttype TerminalSessionExitError,\n\ttype TerminalSessionExitState,\n\ttype TerminalSessionHandle,\n\ttype TerminalSessionNativeOptions,\n\ttype TerminalSessionOperationResult,\n\ttype TerminalSessionOptions,\n\ttype TerminalSessionTerminateOptions,\n} from \"./session.ts\";\nexport {\n\ttype BunRuntime,\n\ttype BunRuntimeVersions,\n\ttype BunTerminal,\n\tcreateBunTerminalSession,\n\tENV_BUN_TERMINAL,\n\tisBunTerminalEnabled,\n} from \"./session-bun.ts\";\n\nexport type PtySessionOptions = TerminalSessionOptions;\n\nexport class PtySession extends BaseTerminalSession {}\n\nexport function loadPtyNative(): NativePtyLoadResult {\n\treturn loadNativePty();\n}\n"]}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { SessionRegistrySession, TerminalSessionSignal } from "./registry-types.ts";
|
|
2
|
+
/** How long a detached child may take to exit after SIGTERM before it is SIGKILLed. */
|
|
3
|
+
export declare const DEFAULT_DETACHED_EXIT_GRACE_MS = 1000;
|
|
2
4
|
export declare function getRuntimePlatform(): string;
|
|
3
5
|
export declare function defaultKillProcess(target: number, signal: TerminalSessionSignal): void;
|
|
4
|
-
|
|
6
|
+
/**
|
|
7
|
+
* SIGTERM every tracked detached child, then — after a bounded grace — SIGKILL
|
|
8
|
+
* the ones still alive. Without the escalation a child that ignores SIGTERM is
|
|
9
|
+
* abandoned the moment the registry forgets its session.
|
|
10
|
+
*/
|
|
11
|
+
export declare function cleanupDetachedChildren(session: SessionRegistrySession, platform: string, killProcess: (target: number, signal: TerminalSessionSignal) => void, exitGraceMs?: number): Promise<void>;
|
|
5
12
|
//# sourceMappingURL=registry-detached.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-detached.d.ts","sourceRoot":"","sources":["../src/registry-detached.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAwB,MAAM,qBAAqB,CAAC;AAE/G,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAEtF;AAED,wBAAsB,uBAAuB,CAC5C,OAAO,EAAE,sBAAsB,EAC/B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,KAAK,IAAI,
|
|
1
|
+
{"version":3,"file":"registry-detached.d.ts","sourceRoot":"","sources":["../src/registry-detached.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAwB,MAAM,qBAAqB,CAAC;AAE/G,uFAAuF;AACvF,eAAO,MAAM,8BAA8B,OAAO,CAAC;AAEnD,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAEtF;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC5C,OAAO,EAAE,sBAAsB,EAC/B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,KAAK,IAAI,EACpE,WAAW,GAAE,MAAuC,GAClD,OAAO,CAAC,IAAI,CAAC,CAaf"}
|
|
@@ -1,30 +1,60 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
|
-
import { DEFAULT_SIGNAL } from "./registry-session.js";
|
|
2
|
+
import { DEFAULT_SIGNAL, FORCE_SIGNAL } from "./registry-session.js";
|
|
3
|
+
/** How long a detached child may take to exit after SIGTERM before it is SIGKILLed. */
|
|
4
|
+
export const DEFAULT_DETACHED_EXIT_GRACE_MS = 1000;
|
|
3
5
|
export function getRuntimePlatform() {
|
|
4
6
|
return process.platform;
|
|
5
7
|
}
|
|
6
8
|
export function defaultKillProcess(target, signal) {
|
|
7
9
|
process.kill(target, signal);
|
|
8
10
|
}
|
|
9
|
-
|
|
11
|
+
/**
|
|
12
|
+
* SIGTERM every tracked detached child, then — after a bounded grace — SIGKILL
|
|
13
|
+
* the ones still alive. Without the escalation a child that ignores SIGTERM is
|
|
14
|
+
* abandoned the moment the registry forgets its session.
|
|
15
|
+
*/
|
|
16
|
+
export async function cleanupDetachedChildren(session, platform, killProcess, exitGraceMs = DEFAULT_DETACHED_EXIT_GRACE_MS) {
|
|
17
|
+
const signalled = [];
|
|
10
18
|
for (const child of getTrackedDetachedChildren(session)) {
|
|
11
19
|
if (isTrackedDetachedChildExited(child))
|
|
12
20
|
continue;
|
|
13
|
-
if (child
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
if (await signalDetachedChild(child, platform, killProcess, DEFAULT_SIGNAL))
|
|
22
|
+
signalled.push(child);
|
|
23
|
+
}
|
|
24
|
+
if (signalled.length === 0)
|
|
25
|
+
return;
|
|
26
|
+
if (exitGraceMs > 0)
|
|
27
|
+
await delay(exitGraceMs);
|
|
28
|
+
for (const child of signalled) {
|
|
29
|
+
if (isTrackedDetachedChildExited(child))
|
|
19
30
|
continue;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
await signalDetachedChild(child, platform, killProcess, FORCE_SIGNAL);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Returns whether the signal was actually delivered to a live child. */
|
|
35
|
+
async function signalDetachedChild(child, platform, killProcess, signal) {
|
|
36
|
+
if (child.kill) {
|
|
37
|
+
await child.kill(signal);
|
|
38
|
+
return true;
|
|
27
39
|
}
|
|
40
|
+
const target = getDetachedChildKillTarget(child, platform);
|
|
41
|
+
if (target === null)
|
|
42
|
+
return false;
|
|
43
|
+
try {
|
|
44
|
+
killProcess(target, signal);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (!isMissingProcessError(error))
|
|
48
|
+
throw error;
|
|
49
|
+
// ESRCH: the process (or group) is already gone — nothing left to escalate.
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
async function delay(ms) {
|
|
55
|
+
await new Promise((resolve) => {
|
|
56
|
+
setTimeout(resolve, ms);
|
|
57
|
+
});
|
|
28
58
|
}
|
|
29
59
|
function getTrackedDetachedChildren(session) {
|
|
30
60
|
return session.getTrackedDetachedChildren?.() ?? session.trackedDetachedChildren ?? [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-detached.js","sourceRoot":"","sources":["../src/registry-detached.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"registry-detached.js","sourceRoot":"","sources":["../src/registry-detached.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGrE,uFAAuF;AACvF,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;AAEnD,MAAM,UAAU,kBAAkB;IACjC,OAAO,OAAO,CAAC,QAAQ,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,MAA6B;IAC/E,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAC9B,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC5C,OAA+B,EAC/B,QAAgB,EAChB,WAAoE,EACpE,WAAW,GAAW,8BAA8B;IAEpD,MAAM,SAAS,GAA2B,EAAE,CAAC;IAC7C,KAAK,MAAM,KAAK,IAAI,0BAA0B,CAAC,OAAO,CAAC,EAAE,CAAC;QACzD,IAAI,4BAA4B,CAAC,KAAK,CAAC;YAAE,SAAS;QAClD,IAAI,MAAM,mBAAmB,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,CAAC;YAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpG,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAEnC,IAAI,WAAW,GAAG,CAAC;QAAE,MAAM,KAAK,CAAC,WAAW,CAAC,CAAC;IAC9C,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC/B,IAAI,4BAA4B,CAAC,KAAK,CAAC;YAAE,SAAS;QAClD,MAAM,mBAAmB,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;IACvE,CAAC;AACF,CAAC;AAED,yEAAyE;AACzE,KAAK,UAAU,mBAAmB,CACjC,KAA2B,EAC3B,QAAgB,EAChB,WAAoE,EACpE,MAA6B;IAE7B,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAChB,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,OAAO,IAAI,CAAC;IACb,CAAC;IACD,MAAM,MAAM,GAAG,0BAA0B,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC3D,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAClC,IAAI,CAAC;QACJ,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC;QAC/C,4EAA4E;QAC5E,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,KAAK,UAAU,KAAK,CAAC,EAAU;IAC9B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,0BAA0B,CAAC,OAA+B;IAClE,OAAO,OAAO,CAAC,0BAA0B,EAAE,EAAE,IAAI,OAAO,CAAC,uBAAuB,IAAI,EAAE,CAAC;AACxF,CAAC;AAED,SAAS,4BAA4B,CAAC,KAA2B;IAChE,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAC3D,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC;IAC9D,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,0BAA0B,CAAC,KAA2B,EAAE,QAAgB;IAChF,IAAI,QAAQ,KAAK,OAAO,IAAI,iBAAiB,CAAC,KAAK,CAAC,cAAc,CAAC;QAAE,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC;IAClG,IAAI,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC;IACnD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAyB;IACnD,OAAO,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;AACpE,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAc;IAC5C,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACnC,OAAO,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC;AAC/B,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC/B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACpD,CAAC","sourcesContent":["import process from \"node:process\";\nimport { DEFAULT_SIGNAL, FORCE_SIGNAL } from \"./registry-session.ts\";\nimport type { SessionRegistrySession, TerminalSessionSignal, TrackedDetachedChild } from \"./registry-types.ts\";\n\n/** How long a detached child may take to exit after SIGTERM before it is SIGKILLed. */\nexport const DEFAULT_DETACHED_EXIT_GRACE_MS = 1000;\n\nexport function getRuntimePlatform(): string {\n\treturn process.platform;\n}\n\nexport function defaultKillProcess(target: number, signal: TerminalSessionSignal): void {\n\tprocess.kill(target, signal);\n}\n\n/**\n * SIGTERM every tracked detached child, then — after a bounded grace — SIGKILL\n * the ones still alive. Without the escalation a child that ignores SIGTERM is\n * abandoned the moment the registry forgets its session.\n */\nexport async function cleanupDetachedChildren(\n\tsession: SessionRegistrySession,\n\tplatform: string,\n\tkillProcess: (target: number, signal: TerminalSessionSignal) => void,\n\texitGraceMs: number = DEFAULT_DETACHED_EXIT_GRACE_MS,\n): Promise<void> {\n\tconst signalled: TrackedDetachedChild[] = [];\n\tfor (const child of getTrackedDetachedChildren(session)) {\n\t\tif (isTrackedDetachedChildExited(child)) continue;\n\t\tif (await signalDetachedChild(child, platform, killProcess, DEFAULT_SIGNAL)) signalled.push(child);\n\t}\n\tif (signalled.length === 0) return;\n\n\tif (exitGraceMs > 0) await delay(exitGraceMs);\n\tfor (const child of signalled) {\n\t\tif (isTrackedDetachedChildExited(child)) continue;\n\t\tawait signalDetachedChild(child, platform, killProcess, FORCE_SIGNAL);\n\t}\n}\n\n/** Returns whether the signal was actually delivered to a live child. */\nasync function signalDetachedChild(\n\tchild: TrackedDetachedChild,\n\tplatform: string,\n\tkillProcess: (target: number, signal: TerminalSessionSignal) => void,\n\tsignal: TerminalSessionSignal,\n): Promise<boolean> {\n\tif (child.kill) {\n\t\tawait child.kill(signal);\n\t\treturn true;\n\t}\n\tconst target = getDetachedChildKillTarget(child, platform);\n\tif (target === null) return false;\n\ttry {\n\t\tkillProcess(target, signal);\n\t} catch (error) {\n\t\tif (!isMissingProcessError(error)) throw error;\n\t\t// ESRCH: the process (or group) is already gone — nothing left to escalate.\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nasync function delay(ms: number): Promise<void> {\n\tawait new Promise<void>((resolve) => {\n\t\tsetTimeout(resolve, ms);\n\t});\n}\n\nfunction getTrackedDetachedChildren(session: SessionRegistrySession): readonly TrackedDetachedChild[] {\n\treturn session.getTrackedDetachedChildren?.() ?? session.trackedDetachedChildren ?? [];\n}\n\nfunction isTrackedDetachedChildExited(child: TrackedDetachedChild): boolean {\n\tif (typeof child.exited === \"boolean\") return child.exited;\n\tif (typeof child.exited === \"function\") return child.exited();\n\treturn false;\n}\n\nfunction getDetachedChildKillTarget(child: TrackedDetachedChild, platform: string): number | null {\n\tif (platform !== \"win32\" && isPositiveInteger(child.processGroupId)) return -child.processGroupId;\n\tif (isPositiveInteger(child.pid)) return child.pid;\n\treturn null;\n}\n\nfunction isPositiveInteger(value: number | undefined): value is number {\n\treturn value !== undefined && Number.isInteger(value) && value > 0;\n}\n\nfunction isMissingProcessError(error: unknown): boolean {\n\tif (!isRecord(error)) return false;\n\treturn error.code === \"ESRCH\";\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n\treturn typeof value === \"object\" && value !== null;\n}\n"]}
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import type { SessionRegistrySession, TerminalSessionSignal } from "./registry-types.ts";
|
|
2
2
|
export declare const DEFAULT_SIGNAL: TerminalSessionSignal;
|
|
3
|
+
/**
|
|
4
|
+
* Escalation signal. On Windows there is no SIGKILL: Node maps any signal but 0
|
|
5
|
+
* to TerminateProcess, and the pty session's own kill path routes it through
|
|
6
|
+
* `taskkill /T /F`, so the forced semantics hold on both platforms.
|
|
7
|
+
*/
|
|
8
|
+
export declare const FORCE_SIGNAL: TerminalSessionSignal;
|
|
3
9
|
export declare function sessionIdPrefix(command: string): string;
|
|
4
10
|
export declare function isTerminalSessionExited(session: SessionRegistrySession): boolean;
|
|
5
11
|
export declare function stopTerminalSession(session: SessionRegistrySession): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Deliver the forced signal to a session that outlived its stop grace. Returns
|
|
14
|
+
* `false` when the session exposes no way to signal it, so callers can skip a
|
|
15
|
+
* pointless forced-grace wait instead of hanging on an unkillable stub.
|
|
16
|
+
*/
|
|
17
|
+
export declare function forceKillTerminalSession(session: SessionRegistrySession): Promise<boolean>;
|
|
6
18
|
export declare function waitForTerminalSessionExit(session: SessionRegistrySession, graceMs?: number): Promise<boolean>;
|
|
7
19
|
//# sourceMappingURL=registry-session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-session.d.ts","sourceRoot":"","sources":["../src/registry-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAGzF,eAAO,MAAM,cAAc,EAAE,qBAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"registry-session.d.ts","sourceRoot":"","sources":["../src/registry-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAGzF,eAAO,MAAM,cAAc,EAAE,qBAAiC,CAAC;AAC/D;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,qBAAiC,CAAC;AAE7D,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAYvD;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAOhF;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAWxF;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CAWhG;AAED,wBAAsB,0BAA0B,CAAC,OAAO,EAAE,sBAAsB,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA0BpH"}
|
package/dist/registry-session.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
const DEFAULT_COMMAND = "bash";
|
|
2
2
|
export const DEFAULT_SIGNAL = "SIGTERM";
|
|
3
|
+
/**
|
|
4
|
+
* Escalation signal. On Windows there is no SIGKILL: Node maps any signal but 0
|
|
5
|
+
* to TerminateProcess, and the pty session's own kill path routes it through
|
|
6
|
+
* `taskkill /T /F`, so the forced semantics hold on both platforms.
|
|
7
|
+
*/
|
|
8
|
+
export const FORCE_SIGNAL = "SIGKILL";
|
|
3
9
|
export function sessionIdPrefix(command) {
|
|
4
10
|
const parts = command.split(/[\\/]/).filter(Boolean);
|
|
5
11
|
const baseName = parts[parts.length - 1] ?? DEFAULT_COMMAND;
|
|
@@ -39,6 +45,23 @@ export async function stopTerminalSession(session) {
|
|
|
39
45
|
if (session.signal)
|
|
40
46
|
await session.signal(DEFAULT_SIGNAL);
|
|
41
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Deliver the forced signal to a session that outlived its stop grace. Returns
|
|
50
|
+
* `false` when the session exposes no way to signal it, so callers can skip a
|
|
51
|
+
* pointless forced-grace wait instead of hanging on an unkillable stub.
|
|
52
|
+
*/
|
|
53
|
+
export async function forceKillTerminalSession(session) {
|
|
54
|
+
// Call through `session` so class methods keep their `this` binding.
|
|
55
|
+
if (session.kill) {
|
|
56
|
+
await session.kill(FORCE_SIGNAL);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
if (session.signal) {
|
|
60
|
+
await session.signal(FORCE_SIGNAL);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
42
65
|
export async function waitForTerminalSessionExit(session, graceMs) {
|
|
43
66
|
if (isTerminalSessionExited(session))
|
|
44
67
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-session.js","sourceRoot":"","sources":["../src/registry-session.ts"],"names":[],"mappings":"AAEA,MAAM,eAAe,GAAG,MAAM,CAAC;AAC/B,MAAM,CAAC,MAAM,cAAc,GAA0B,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"registry-session.js","sourceRoot":"","sources":["../src/registry-session.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SessionRegistrySession, TerminalSessionSignal } from \"./registry-types.ts\";\n\nconst DEFAULT_COMMAND = \"bash\";\nexport const DEFAULT_SIGNAL: TerminalSessionSignal = \"SIGTERM\";\n/**\n * Escalation signal. On Windows there is no SIGKILL: Node maps any signal but 0\n * to TerminateProcess, and the pty session's own kill path routes it through\n * `taskkill /T /F`, so the forced semantics hold on both platforms.\n */\nexport const FORCE_SIGNAL: TerminalSessionSignal = \"SIGKILL\";\n\nexport function sessionIdPrefix(command: string): string {\n\tconst parts = command.split(/[\\\\/]/).filter(Boolean);\n\tconst baseName = parts[parts.length - 1] ?? DEFAULT_COMMAND;\n\t// Strip a Windows executable extension first so `bash.exe` collapses to the same\n\t// `bash` prefix as POSIX `/bin/bash`; otherwise the `.exe` sanitizes to `_exe` and\n\t// session ids become `bash_exe_1`, breaking the cross-platform `bash_N` scheme.\n\tconst withoutExt = baseName.replace(/\\.(?:exe|com|bat|cmd|ps1)$/i, \"\");\n\tconst prefix = withoutExt\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9]+/g, \"_\")\n\t\t.replace(/^_+|_+$/g, \"\");\n\treturn prefix || \"session\";\n}\n\nexport function isTerminalSessionExited(session: SessionRegistrySession): boolean {\n\tif (session.exited === true) return true;\n\tif (typeof session.isExited === \"boolean\") return session.isExited;\n\tif (typeof session.isExited === \"function\" && session.isExited()) return true;\n\tif (session.exitState?.status === \"exited\") return true;\n\tif (session.status === \"exited\" || session.status === \"closed\" || session.status === \"stopped\") return true;\n\treturn session.exitResult !== undefined && session.exitResult !== null;\n}\n\nexport async function stopTerminalSession(session: SessionRegistrySession): Promise<void> {\n\t// Call through `session` so class methods keep their `this` binding.\n\tif (session.stop) {\n\t\tawait session.stop();\n\t\treturn;\n\t}\n\tif (session.kill) {\n\t\tawait session.kill(DEFAULT_SIGNAL);\n\t\treturn;\n\t}\n\tif (session.signal) await session.signal(DEFAULT_SIGNAL);\n}\n\n/**\n * Deliver the forced signal to a session that outlived its stop grace. Returns\n * `false` when the session exposes no way to signal it, so callers can skip a\n * pointless forced-grace wait instead of hanging on an unkillable stub.\n */\nexport async function forceKillTerminalSession(session: SessionRegistrySession): Promise<boolean> {\n\t// Call through `session` so class methods keep their `this` binding.\n\tif (session.kill) {\n\t\tawait session.kill(FORCE_SIGNAL);\n\t\treturn true;\n\t}\n\tif (session.signal) {\n\t\tawait session.signal(FORCE_SIGNAL);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nexport async function waitForTerminalSessionExit(session: SessionRegistrySession, graceMs?: number): Promise<boolean> {\n\tif (isTerminalSessionExited(session)) return true;\n\t// Invoke via the session object (not a detached reference) so `this` is preserved.\n\tconst wait = session.waitExit ? session.waitExit() : session.wait ? session.wait() : null;\n\tif (wait === null) return false;\n\tif (graceMs === undefined) {\n\t\tawait wait;\n\t\treturn isTerminalSessionExited(session);\n\t}\n\tlet graceTimer: ReturnType<typeof setTimeout> | undefined;\n\ttry {\n\t\tconst exitSettled = await Promise.race([\n\t\t\tPromise.resolve(wait).then(() => true),\n\t\t\tnew Promise<false>((resolve) => {\n\t\t\t\tgraceTimer = setTimeout(() => resolve(false), graceMs);\n\t\t\t}),\n\t\t]);\n\t\tif (!exitSettled) {\n\t\t\t// The wait may still settle (or reject) long after we stopped caring.\n\t\t\tvoid Promise.resolve(wait).catch(() => {});\n\t\t\treturn false;\n\t\t}\n\t} finally {\n\t\tif (graceTimer) clearTimeout(graceTimer);\n\t}\n\treturn isTerminalSessionExited(session);\n}\n"]}
|
package/dist/registry-types.d.ts
CHANGED
|
@@ -68,11 +68,22 @@ export interface SessionRegistryOptions<TSession extends SessionRegistrySession
|
|
|
68
68
|
readonly now?: () => number;
|
|
69
69
|
readonly platform?: string;
|
|
70
70
|
/**
|
|
71
|
-
* How long `stop()` waits for a killed session to report exit before
|
|
72
|
-
*
|
|
73
|
-
* settles (e.g. a surviving
|
|
71
|
+
* How long `stop()` waits for a killed session to report exit before it
|
|
72
|
+
* escalates to SIGKILL (and, if that also fails, marks it `stopping`).
|
|
73
|
+
* Guards against sessions whose exit never settles (e.g. a surviving
|
|
74
|
+
* descendant holding the PTY open). Default 5s.
|
|
74
75
|
*/
|
|
75
76
|
readonly stopExitGraceMs?: number;
|
|
77
|
+
/**
|
|
78
|
+
* How long `stop()` waits for the exit after escalating to SIGKILL before it
|
|
79
|
+
* gives up and marks the entry `stopping`. Default 1s.
|
|
80
|
+
*/
|
|
81
|
+
readonly forcedExitGraceMs?: number;
|
|
82
|
+
/**
|
|
83
|
+
* How long tracked detached children may take to exit after SIGTERM before
|
|
84
|
+
* they are SIGKILLed. `0` escalates immediately. Default 1s.
|
|
85
|
+
*/
|
|
86
|
+
readonly detachedExitGraceMs?: number;
|
|
76
87
|
}
|
|
77
88
|
export type StoredSessionRegistryEntry<TSession extends SessionRegistrySession> = {
|
|
78
89
|
id: string;
|
|
@@ -85,6 +96,8 @@ export type StoredSessionRegistryEntry<TSession extends SessionRegistrySession>
|
|
|
85
96
|
unsubscribeExit: (() => void) | null;
|
|
86
97
|
stopPromise: Promise<boolean> | null;
|
|
87
98
|
detachedCleanupPromise: Promise<void> | null;
|
|
99
|
+
/** Set once the entry was SIGKILLed; a second forced kill would add nothing. */
|
|
100
|
+
forceKilled: boolean;
|
|
88
101
|
};
|
|
89
102
|
export declare class SessionRegistryCapacityError extends Error {
|
|
90
103
|
readonly code = "session_registry_capacity";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-types.d.ts","sourceRoot":"","sources":["../src/registry-types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"registry-types.d.ts","sourceRoot":"","sources":["../src/registry-types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAC;AAClE,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,OAAO,CAAC;AACnD,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAE7C,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,qBAAqB,KAAK,YAAY,CAAC,OAAO,CAAC,CAAC;CAC1E;AAED,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,uBAAuB,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACnE,QAAQ,CAAC,0BAA0B,CAAC,EAAE,MAAM,SAAS,oBAAoB,EAAE,CAAC;IAC5E,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IACpE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,qBAAqB,KAAK,YAAY,CAAC,OAAO,CAAC,CAAC;IAC1E,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,YAAY,CAAC,OAAO,CAAC,CAAC;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,4BAA4B,CAAC,QAAQ,SAAS,sBAAsB,GAAG,sBAAsB;IAC7G,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC;CAC5B;AAED,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,2BAA2B,CAAC,QAAQ,SAAS,sBAAsB,GAAG,sBAAsB;IAC5G,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB,CAAC,QAAQ,SAAS,sBAAsB,GAAG,sBAAsB;IACrG,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,2BAA2B;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB,CAAC,QAAQ,SAAS,sBAAsB,GAAG,sBAAsB;IACvG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,2BAA2B,CAAC,QAAQ,CAAC,EAAE,CAAC;IAC5E,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,4BAA4B,KAAK,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC3F,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC/E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,MAAM,MAAM,0BAA0B,CAAC,QAAQ,SAAS,sBAAsB,IAAI;IACjF,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,QAAQ,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,oBAAoB,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,eAAe,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;IACrC,WAAW,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IACrC,sBAAsB,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC7C,gFAAgF;IAChF,WAAW,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,qBAAa,4BAA6B,SAAQ,KAAK;IACtD,QAAQ,CAAC,IAAI,+BAA+B;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,YAAY,WAAW,EAAE,MAAM,EAI9B;CACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-types.js","sourceRoot":"","sources":["../src/registry-types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"registry-types.js","sourceRoot":"","sources":["../src/registry-types.ts"],"names":[],"mappings":"AA6GA,MAAM,OAAO,4BAA6B,SAAQ,KAAK;IAItD,YAAY,WAAmB;QAC9B,KAAK,CAAC,uCAAuC,WAAW,2BAA2B,CAAC,CAAC;QAJ7E,SAAI,GAAG,2BAA2B,CAAC;QAK3C,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;QAC3C,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IAChC,CAAC;CACD","sourcesContent":["export type TerminalSessionState = \"live\" | \"stopping\" | \"exited\";\nexport type TerminalSessionSignal = NodeJS.Signals;\nexport type MaybePromise<T> = T | Promise<T>;\n\nexport interface TrackedDetachedChild {\n\treadonly pid?: number;\n\treadonly processGroupId?: number;\n\treadonly exited?: boolean | (() => boolean);\n\treadonly kill?: (signal?: TerminalSessionSignal) => MaybePromise<unknown>;\n}\n\nexport interface SessionRegistrySession {\n\treadonly command?: string;\n\treadonly exited?: boolean;\n\treadonly isExited?: boolean | (() => boolean);\n\treadonly exitResult?: unknown;\n\treadonly exitState?: { readonly status?: string };\n\treadonly status?: string;\n\treadonly trackedDetachedChildren?: readonly TrackedDetachedChild[];\n\treadonly getTrackedDetachedChildren?: () => readonly TrackedDetachedChild[];\n\treadonly onExit?: (handler: () => void) => (() => void) | undefined;\n\treadonly stop?: () => MaybePromise<unknown>;\n\treadonly kill?: (signal?: TerminalSessionSignal) => MaybePromise<unknown>;\n\treadonly signal?: (signal: TerminalSessionSignal) => MaybePromise<unknown>;\n\treadonly waitExit?: () => Promise<unknown>;\n\treadonly wait?: () => Promise<unknown>;\n}\n\nexport interface SessionRegistryCreateOptions<TSession extends SessionRegistrySession = SessionRegistrySession> {\n\treadonly command?: string;\n\treadonly args?: readonly string[];\n\treadonly cwd?: string;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n\treadonly cols?: number;\n\treadonly rows?: number;\n\treadonly session?: TSession;\n}\n\nexport interface SessionRegistryCreateContext {\n\treadonly id: string;\n\treadonly command: string;\n\treadonly args?: readonly string[];\n\treadonly cwd?: string;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n\treadonly cols?: number;\n\treadonly rows?: number;\n}\n\nexport interface InitialSessionRegistryEntry<TSession extends SessionRegistrySession = SessionRegistrySession> {\n\treadonly id: string;\n\treadonly session: TSession;\n\treadonly command?: string;\n}\n\nexport interface SessionRegistryEntry<TSession extends SessionRegistrySession = SessionRegistrySession> {\n\treadonly id: string;\n\treadonly command: string;\n\treadonly session: TSession;\n\treadonly createdAt: number;\n\treadonly lastUsedAt: number;\n\treadonly state: TerminalSessionState;\n\treadonly exitedAt: number | null;\n}\n\nexport interface SessionRegistrySweepOptions {\n\treadonly remove?: boolean;\n}\n\nexport interface SessionRegistryOptions<TSession extends SessionRegistrySession = SessionRegistrySession> {\n\treadonly maxSessions?: number;\n\treadonly initialSessions?: readonly InitialSessionRegistryEntry<TSession>[];\n\treadonly createSession?: (options: SessionRegistryCreateContext) => MaybePromise<TSession>;\n\treadonly killProcess?: (target: number, signal: TerminalSessionSignal) => void;\n\treadonly now?: () => number;\n\treadonly platform?: string;\n\t/**\n\t * How long `stop()` waits for a killed session to report exit before it\n\t * escalates to SIGKILL (and, if that also fails, marks it `stopping`).\n\t * Guards against sessions whose exit never settles (e.g. a surviving\n\t * descendant holding the PTY open). Default 5s.\n\t */\n\treadonly stopExitGraceMs?: number;\n\t/**\n\t * How long `stop()` waits for the exit after escalating to SIGKILL before it\n\t * gives up and marks the entry `stopping`. Default 1s.\n\t */\n\treadonly forcedExitGraceMs?: number;\n\t/**\n\t * How long tracked detached children may take to exit after SIGTERM before\n\t * they are SIGKILLed. `0` escalates immediately. Default 1s.\n\t */\n\treadonly detachedExitGraceMs?: number;\n}\n\nexport type StoredSessionRegistryEntry<TSession extends SessionRegistrySession> = {\n\tid: string;\n\tcommand: string;\n\tsession: TSession;\n\tcreatedAt: number;\n\tlastUsedAt: number;\n\tstate: TerminalSessionState;\n\texitedAt: number | null;\n\tunsubscribeExit: (() => void) | null;\n\tstopPromise: Promise<boolean> | null;\n\tdetachedCleanupPromise: Promise<void> | null;\n\t/** Set once the entry was SIGKILLed; a second forced kill would add nothing. */\n\tforceKilled: boolean;\n};\n\nexport class SessionRegistryCapacityError extends Error {\n\treadonly code = \"session_registry_capacity\";\n\treadonly maxSessions: number;\n\n\tconstructor(maxSessions: number) {\n\t\tsuper(`Cannot create terminal session: all ${maxSessions} registry slots are live.`);\n\t\tthis.name = \"SessionRegistryCapacityError\";\n\t\tthis.maxSessions = maxSessions;\n\t}\n}\n"]}
|
package/dist/registry.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export declare class SessionRegistry<TSession extends SessionRegistrySession = S
|
|
|
12
12
|
private sequence;
|
|
13
13
|
readonly maxSessions: number;
|
|
14
14
|
private readonly stopExitGraceMs;
|
|
15
|
+
private readonly forcedExitGraceMs;
|
|
16
|
+
private readonly detachedExitGraceMs;
|
|
15
17
|
constructor(options?: SessionRegistryOptions<TSession>);
|
|
16
18
|
get size(): number;
|
|
17
19
|
list(): readonly SessionRegistryEntry<TSession>[];
|
|
@@ -28,6 +30,12 @@ export declare class SessionRegistry<TSession extends SessionRegistrySession = S
|
|
|
28
30
|
private pruneExitedUntilAtMost;
|
|
29
31
|
private findLeastRecentlyUsedExited;
|
|
30
32
|
private stopEntry;
|
|
33
|
+
/**
|
|
34
|
+
* SIGKILL a session that outlived the graceful stop. Only a session that even
|
|
35
|
+
* survives the forced signal (or exposes no way to be signalled) stays
|
|
36
|
+
* `stopping`; everything else settles as `exited`.
|
|
37
|
+
*/
|
|
38
|
+
private forceKillEntry;
|
|
31
39
|
private cleanupDetachedChildren;
|
|
32
40
|
private cleanupDetachedChildrenOnce;
|
|
33
41
|
private refreshExitedStates;
|
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAGX,4BAA4B,EAC5B,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,2BAA2B,EAG3B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACjF,YAAY,EACX,2BAA2B,EAC3B,YAAY,EACZ,4BAA4B,EAC5B,4BAA4B,EAC5B,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAUnE,qBAAa,eAAe,CAAC,QAAQ,SAAS,sBAAsB,GAAG,sBAAsB;IAC5F,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2D;IACnF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IACxD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6E;IAC3G,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0D;IACtF,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAK;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAE7C,YAAY,OAAO,GAAE,sBAAsB,CAAC,QAAQ,CAAM,EAmBzD;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,IAAI,SAAS,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAGhD;IAED,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,oBAAoB,CAAC,QAAQ,CAAC,GAAG,IAAI,CAMrD;IAEK,MAAM,CAAC,OAAO,GAAE,4BAA4B,CAAC,QAAQ,CAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAgB1G;IAEK,WAAW,CAAC,OAAO,GAAE,2BAAgC,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAUvF;IAEK,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMvC;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAE7B;IAEK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAS9B;YAEa,wBAAwB;IAOtC,OAAO,CAAC,QAAQ;YAyBF,iBAAiB;YAOjB,wBAAwB;YAMxB,sBAAsB;IASpC,OAAO,CAAC,2BAA2B;YAUrB,SAAS;IAWvB;;;;OAIG;YACW,cAAc;YAiBd,uBAAuB;YAMvB,2BAA2B;IAIzC,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,SAAS;CAIjB"}
|
package/dist/registry.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { cleanupDetachedChildren, defaultKillProcess, getRuntimePlatform } from "./registry-detached.js";
|
|
2
|
-
import { isTerminalSessionExited, sessionIdPrefix, stopTerminalSession, waitForTerminalSessionExit, } from "./registry-session.js";
|
|
1
|
+
import { cleanupDetachedChildren, DEFAULT_DETACHED_EXIT_GRACE_MS, defaultKillProcess, getRuntimePlatform, } from "./registry-detached.js";
|
|
2
|
+
import { forceKillTerminalSession, isTerminalSessionExited, sessionIdPrefix, stopTerminalSession, waitForTerminalSessionExit, } from "./registry-session.js";
|
|
3
3
|
export { isTerminalSessionExited, sessionIdPrefix } from "./registry-session.js";
|
|
4
4
|
export { SessionRegistryCapacityError } from "./registry-types.js";
|
|
5
5
|
import { SessionRegistryCapacityError } from "./registry-types.js";
|
|
6
6
|
const DEFAULT_MAX_SESSIONS = 32;
|
|
7
7
|
const DEFAULT_COMMAND = "bash";
|
|
8
8
|
const DEFAULT_STOP_EXIT_GRACE_MS = 5000;
|
|
9
|
+
const DEFAULT_FORCED_EXIT_GRACE_MS = 1000;
|
|
9
10
|
const MAX_STOP_EXIT_GRACE_MS = 2_147_483_647;
|
|
10
11
|
export class SessionRegistry {
|
|
11
12
|
constructor(options = {}) {
|
|
@@ -19,6 +20,8 @@ export class SessionRegistry {
|
|
|
19
20
|
options.stopExitGraceMs > 0
|
|
20
21
|
? Math.min(options.stopExitGraceMs, MAX_STOP_EXIT_GRACE_MS)
|
|
21
22
|
: DEFAULT_STOP_EXIT_GRACE_MS;
|
|
23
|
+
this.forcedExitGraceMs = normalizeGraceMs(options.forcedExitGraceMs, DEFAULT_FORCED_EXIT_GRACE_MS);
|
|
24
|
+
this.detachedExitGraceMs = normalizeGraceMs(options.detachedExitGraceMs, DEFAULT_DETACHED_EXIT_GRACE_MS);
|
|
22
25
|
this.createSession = options.createSession ?? null;
|
|
23
26
|
this.killProcess = options.killProcess ?? defaultKillProcess;
|
|
24
27
|
this.now = options.now ?? Date.now;
|
|
@@ -86,6 +89,13 @@ export class SessionRegistry {
|
|
|
86
89
|
}
|
|
87
90
|
async teardown() {
|
|
88
91
|
await this.stopAll();
|
|
92
|
+
// Dropping an entry is the registry forgetting the process exists, so no entry
|
|
93
|
+
// leaves here without having been escalated to SIGKILL first.
|
|
94
|
+
for (const entry of [...this.entries.values()]) {
|
|
95
|
+
this.refreshEntryState(entry);
|
|
96
|
+
if (entry.state !== "exited")
|
|
97
|
+
await this.forceKillEntry(entry);
|
|
98
|
+
}
|
|
89
99
|
for (const id of [...this.entries.keys()])
|
|
90
100
|
this.removeEntry(id);
|
|
91
101
|
}
|
|
@@ -109,6 +119,7 @@ export class SessionRegistry {
|
|
|
109
119
|
unsubscribeExit: null,
|
|
110
120
|
stopPromise: null,
|
|
111
121
|
detachedCleanupPromise: null,
|
|
122
|
+
forceKilled: false,
|
|
112
123
|
};
|
|
113
124
|
entry.unsubscribeExit =
|
|
114
125
|
session.onExit?.(() => {
|
|
@@ -157,16 +168,34 @@ export class SessionRegistry {
|
|
|
157
168
|
await this.cleanupDetachedChildren(entry);
|
|
158
169
|
if (entry.state !== "exited") {
|
|
159
170
|
await stopTerminalSession(entry.session);
|
|
160
|
-
if (await waitForTerminalSessionExit(entry.session, this.stopExitGraceMs))
|
|
171
|
+
if (await waitForTerminalSessionExit(entry.session, this.stopExitGraceMs))
|
|
161
172
|
this.markExited(entry);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
entry.state = "stopping";
|
|
165
|
-
entry.exitedAt = null;
|
|
166
|
-
}
|
|
173
|
+
else
|
|
174
|
+
await this.forceKillEntry(entry);
|
|
167
175
|
}
|
|
168
176
|
return true;
|
|
169
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* SIGKILL a session that outlived the graceful stop. Only a session that even
|
|
180
|
+
* survives the forced signal (or exposes no way to be signalled) stays
|
|
181
|
+
* `stopping`; everything else settles as `exited`.
|
|
182
|
+
*/
|
|
183
|
+
async forceKillEntry(entry) {
|
|
184
|
+
if (!entry.forceKilled) {
|
|
185
|
+
entry.forceKilled = true;
|
|
186
|
+
const delivered = await forceKillTerminalSession(entry.session);
|
|
187
|
+
if (delivered && (await waitForTerminalSessionExit(entry.session, this.forcedExitGraceMs))) {
|
|
188
|
+
this.markExited(entry);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (isTerminalSessionExited(entry.session)) {
|
|
193
|
+
this.markExited(entry);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
entry.state = "stopping";
|
|
197
|
+
entry.exitedAt = null;
|
|
198
|
+
}
|
|
170
199
|
async cleanupDetachedChildren(entry) {
|
|
171
200
|
if (entry.detachedCleanupPromise)
|
|
172
201
|
return entry.detachedCleanupPromise;
|
|
@@ -174,7 +203,7 @@ export class SessionRegistry {
|
|
|
174
203
|
return entry.detachedCleanupPromise;
|
|
175
204
|
}
|
|
176
205
|
async cleanupDetachedChildrenOnce(session) {
|
|
177
|
-
await cleanupDetachedChildren(session, this.platform, this.killProcess);
|
|
206
|
+
await cleanupDetachedChildren(session, this.platform, this.killProcess, this.detachedExitGraceMs);
|
|
178
207
|
}
|
|
179
208
|
refreshExitedStates() {
|
|
180
209
|
for (const entry of this.entries.values())
|
|
@@ -222,6 +251,11 @@ export class SessionRegistry {
|
|
|
222
251
|
return this.now() * 1000 + this.sequence;
|
|
223
252
|
}
|
|
224
253
|
}
|
|
254
|
+
function normalizeGraceMs(value, fallback) {
|
|
255
|
+
if (value === undefined || !Number.isFinite(value) || value < 0)
|
|
256
|
+
return fallback;
|
|
257
|
+
return Math.min(value, MAX_STOP_EXIT_GRACE_MS);
|
|
258
|
+
}
|
|
225
259
|
function normalizeMaxSessions(value) {
|
|
226
260
|
if (value === undefined)
|
|
227
261
|
return DEFAULT_MAX_SESSIONS;
|
package/dist/registry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { cleanupDetachedChildren, defaultKillProcess, getRuntimePlatform } from \"./registry-detached.ts\";\nimport {\n\tisTerminalSessionExited,\n\tsessionIdPrefix,\n\tstopTerminalSession,\n\twaitForTerminalSessionExit,\n} from \"./registry-session.ts\";\nimport type {\n\tMaybePromise,\n\tSessionRegistryCreateContext,\n\tSessionRegistryCreateOptions,\n\tSessionRegistryEntry,\n\tSessionRegistryOptions,\n\tSessionRegistrySession,\n\tSessionRegistrySweepOptions,\n\tStoredSessionRegistryEntry,\n\tTerminalSessionSignal,\n} from \"./registry-types.ts\";\n\nexport { isTerminalSessionExited, sessionIdPrefix } from \"./registry-session.ts\";\nexport type {\n\tInitialSessionRegistryEntry,\n\tMaybePromise,\n\tSessionRegistryCreateContext,\n\tSessionRegistryCreateOptions,\n\tSessionRegistryEntry,\n\tSessionRegistryOptions,\n\tSessionRegistrySession,\n\tSessionRegistrySweepOptions,\n\tTerminalSessionSignal,\n\tTerminalSessionState,\n\tTrackedDetachedChild,\n} from \"./registry-types.ts\";\nexport { SessionRegistryCapacityError } from \"./registry-types.ts\";\n\nimport { SessionRegistryCapacityError } from \"./registry-types.ts\";\n\nconst DEFAULT_MAX_SESSIONS = 32;\nconst DEFAULT_COMMAND = \"bash\";\nconst DEFAULT_STOP_EXIT_GRACE_MS = 5000;\nconst MAX_STOP_EXIT_GRACE_MS = 2_147_483_647;\n\nexport class SessionRegistry<TSession extends SessionRegistrySession = SessionRegistrySession> {\n\tprivate readonly entries = new Map<string, StoredSessionRegistryEntry<TSession>>();\n\tprivate readonly idCounters = new Map<string, number>();\n\tprivate readonly createSession: ((options: SessionRegistryCreateContext) => MaybePromise<TSession>) | null;\n\tprivate readonly killProcess: (target: number, signal: TerminalSessionSignal) => void;\n\tprivate readonly now: () => number;\n\tprivate readonly platform: string;\n\tprivate sequence = 0;\n\treadonly maxSessions: number;\n\tprivate readonly stopExitGraceMs: number;\n\n\tconstructor(options: SessionRegistryOptions<TSession> = {}) {\n\t\tthis.maxSessions = normalizeMaxSessions(options.maxSessions);\n\t\tthis.stopExitGraceMs =\n\t\t\toptions.stopExitGraceMs !== undefined &&\n\t\t\tNumber.isFinite(options.stopExitGraceMs) &&\n\t\t\toptions.stopExitGraceMs > 0\n\t\t\t\t? Math.min(options.stopExitGraceMs, MAX_STOP_EXIT_GRACE_MS)\n\t\t\t\t: DEFAULT_STOP_EXIT_GRACE_MS;\n\t\tthis.createSession = options.createSession ?? null;\n\t\tthis.killProcess = options.killProcess ?? defaultKillProcess;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.platform = options.platform ?? getRuntimePlatform();\n\n\t\tfor (const initial of options.initialSessions ?? []) {\n\t\t\tthis.addEntry(initial.id, initial.session, initial.command ?? initial.session.command ?? DEFAULT_COMMAND);\n\t\t}\n\t\tvoid this.sweepExited();\n\t}\n\n\tget size(): number {\n\t\treturn this.entries.size;\n\t}\n\n\tlist(): readonly SessionRegistryEntry<TSession>[] {\n\t\tthis.refreshExitedStates();\n\t\treturn [...this.entries.values()].sort((left, right) => left.createdAt - right.createdAt);\n\t}\n\n\tget(id: string): SessionRegistryEntry<TSession> | null {\n\t\tconst entry = this.entries.get(id);\n\t\tif (!entry) return null;\n\t\tentry.lastUsedAt = this.timestamp();\n\t\tthis.refreshEntryState(entry);\n\t\treturn entry;\n\t}\n\n\tasync create(options: SessionRegistryCreateOptions<TSession> = {}): Promise<SessionRegistryEntry<TSession>> {\n\t\tawait this.enforceCapacityForCreate();\n\t\tconst command = options.command ?? options.session?.command ?? DEFAULT_COMMAND;\n\t\tconst id = this.allocateId(command);\n\t\tconst session =\n\t\t\toptions.session ??\n\t\t\t(await this.createSessionWithFactory({\n\t\t\t\tid,\n\t\t\t\tcommand,\n\t\t\t\targs: options.args,\n\t\t\t\tcwd: options.cwd,\n\t\t\t\tenv: options.env,\n\t\t\t\tcols: options.cols,\n\t\t\t\trows: options.rows,\n\t\t\t}));\n\t\treturn this.addEntry(id, session, command);\n\t}\n\n\tasync sweepExited(options: SessionRegistrySweepOptions = {}): Promise<readonly string[]> {\n\t\tthis.refreshExitedStates();\n\t\tconst sweptIds: string[] = [];\n\t\tfor (const entry of this.entries.values()) {\n\t\t\tif (entry.state !== \"exited\") continue;\n\t\t\tawait this.cleanupDetachedChildren(entry);\n\t\t\tsweptIds.push(entry.id);\n\t\t\tif (options.remove) this.removeEntry(entry.id);\n\t\t}\n\t\treturn sweptIds;\n\t}\n\n\tasync stop(id: string): Promise<boolean> {\n\t\tconst entry = this.entries.get(id);\n\t\tif (!entry) return false;\n\t\tif (entry.stopPromise) return entry.stopPromise;\n\t\tentry.stopPromise = this.stopEntry(entry);\n\t\treturn entry.stopPromise;\n\t}\n\n\tasync stopAll(): Promise<void> {\n\t\tawait Promise.all([...this.entries.keys()].map((id) => this.stop(id)));\n\t}\n\n\tasync teardown(): Promise<void> {\n\t\tawait this.stopAll();\n\t\tfor (const id of [...this.entries.keys()]) this.removeEntry(id);\n\t}\n\n\tprivate async createSessionWithFactory(options: SessionRegistryCreateContext): Promise<TSession> {\n\t\tif (!this.createSession) {\n\t\t\tthrow new Error(\"Cannot create terminal session: SessionRegistry requires a createSession factory.\");\n\t\t}\n\t\treturn this.createSession(options);\n\t}\n\n\tprivate addEntry(id: string, session: TSession, command: string): SessionRegistryEntry<TSession> {\n\t\tconst timestamp = this.timestamp();\n\t\tconst exited = isTerminalSessionExited(session);\n\t\tconst entry: StoredSessionRegistryEntry<TSession> = {\n\t\t\tid,\n\t\t\tcommand,\n\t\t\tsession,\n\t\t\tcreatedAt: timestamp,\n\t\t\tlastUsedAt: timestamp,\n\t\t\tstate: exited ? \"exited\" : \"live\",\n\t\t\texitedAt: exited ? timestamp : null,\n\t\t\tunsubscribeExit: null,\n\t\t\tstopPromise: null,\n\t\t\tdetachedCleanupPromise: null,\n\t\t};\n\t\tentry.unsubscribeExit =\n\t\t\tsession.onExit?.(() => {\n\t\t\t\tvoid this.handleSessionExit(id);\n\t\t\t}) ?? null;\n\t\tthis.entries.set(id, entry);\n\t\tthis.observeId(id);\n\t\treturn entry;\n\t}\n\n\tprivate async handleSessionExit(id: string): Promise<void> {\n\t\tconst entry = this.entries.get(id);\n\t\tif (!entry) return;\n\t\tthis.markExited(entry);\n\t\tawait this.cleanupDetachedChildren(entry);\n\t}\n\n\tprivate async enforceCapacityForCreate(): Promise<void> {\n\t\tthis.refreshExitedStates();\n\t\tawait this.pruneExitedUntilAtMost(this.maxSessions - 1);\n\t\tif (this.entries.size >= this.maxSessions) throw new SessionRegistryCapacityError(this.maxSessions);\n\t}\n\n\tprivate async pruneExitedUntilAtMost(maxSize: number): Promise<void> {\n\t\twhile (this.entries.size > maxSize) {\n\t\t\tconst entry = this.findLeastRecentlyUsedExited();\n\t\t\tif (!entry) return;\n\t\t\tawait this.cleanupDetachedChildren(entry);\n\t\t\tthis.removeEntry(entry.id);\n\t\t}\n\t}\n\n\tprivate findLeastRecentlyUsedExited(): StoredSessionRegistryEntry<TSession> | null {\n\t\tlet selected: StoredSessionRegistryEntry<TSession> | null = null;\n\t\tfor (const entry of this.entries.values()) {\n\t\t\tif (entry.state !== \"exited\") continue;\n\t\t\tif (!selected || entry.lastUsedAt < selected.lastUsedAt) selected = entry;\n\t\t\telse if (selected.lastUsedAt === entry.lastUsedAt && entry.createdAt < selected.createdAt) selected = entry;\n\t\t}\n\t\treturn selected;\n\t}\n\n\tprivate async stopEntry(entry: StoredSessionRegistryEntry<TSession>): Promise<boolean> {\n\t\tthis.refreshEntryState(entry);\n\t\tawait this.cleanupDetachedChildren(entry);\n\t\tif (entry.state !== \"exited\") {\n\t\t\tawait stopTerminalSession(entry.session);\n\t\t\tif (await waitForTerminalSessionExit(entry.session, this.stopExitGraceMs)) {\n\t\t\t\tthis.markExited(entry);\n\t\t\t} else {\n\t\t\t\tentry.state = \"stopping\";\n\t\t\t\tentry.exitedAt = null;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate async cleanupDetachedChildren(entry: StoredSessionRegistryEntry<TSession>): Promise<void> {\n\t\tif (entry.detachedCleanupPromise) return entry.detachedCleanupPromise;\n\t\tentry.detachedCleanupPromise = this.cleanupDetachedChildrenOnce(entry.session);\n\t\treturn entry.detachedCleanupPromise;\n\t}\n\n\tprivate async cleanupDetachedChildrenOnce(session: TSession): Promise<void> {\n\t\tawait cleanupDetachedChildren(session, this.platform, this.killProcess);\n\t}\n\n\tprivate refreshExitedStates(): void {\n\t\tfor (const entry of this.entries.values()) this.refreshEntryState(entry);\n\t}\n\n\tprivate refreshEntryState(entry: StoredSessionRegistryEntry<TSession>): void {\n\t\tif (entry.state === \"exited\") return;\n\t\tif (isTerminalSessionExited(entry.session)) this.markExited(entry);\n\t}\n\n\tprivate markExited(entry: StoredSessionRegistryEntry<TSession>): void {\n\t\tif (entry.state === \"exited\") return;\n\t\tentry.state = \"exited\";\n\t\tentry.exitedAt = this.timestamp();\n\t}\n\n\tprivate removeEntry(id: string): void {\n\t\tconst entry = this.entries.get(id);\n\t\tif (!entry) return;\n\t\tentry.unsubscribeExit?.();\n\t\tthis.entries.delete(id);\n\t}\n\n\tprivate allocateId(command: string): string {\n\t\tconst prefix = sessionIdPrefix(command);\n\t\tlet next = (this.idCounters.get(prefix) ?? 0) + 1;\n\t\twhile (this.entries.has(`${prefix}_${next}`)) next += 1;\n\t\tthis.idCounters.set(prefix, next);\n\t\treturn `${prefix}_${next}`;\n\t}\n\n\tprivate observeId(id: string): void {\n\t\tconst match = /^(.*)_(\\d+)$/.exec(id);\n\t\tif (!match) return;\n\t\tconst prefix = match[1];\n\t\tconst value = Number.parseInt(match[2] ?? \"\", 10);\n\t\tif (!prefix || !Number.isFinite(value)) return;\n\t\tthis.idCounters.set(prefix, Math.max(this.idCounters.get(prefix) ?? 0, value));\n\t}\n\n\tprivate timestamp(): number {\n\t\tthis.sequence += 1;\n\t\treturn this.now() * 1000 + this.sequence;\n\t}\n}\n\nfunction normalizeMaxSessions(value: number | undefined): number {\n\tif (value === undefined) return DEFAULT_MAX_SESSIONS;\n\tif (!Number.isFinite(value) || value < 1) throw new Error(\"maxSessions must be a positive finite number.\");\n\treturn Math.trunc(value);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tcleanupDetachedChildren,\n\tDEFAULT_DETACHED_EXIT_GRACE_MS,\n\tdefaultKillProcess,\n\tgetRuntimePlatform,\n} from \"./registry-detached.ts\";\nimport {\n\tforceKillTerminalSession,\n\tisTerminalSessionExited,\n\tsessionIdPrefix,\n\tstopTerminalSession,\n\twaitForTerminalSessionExit,\n} from \"./registry-session.ts\";\nimport type {\n\tMaybePromise,\n\tSessionRegistryCreateContext,\n\tSessionRegistryCreateOptions,\n\tSessionRegistryEntry,\n\tSessionRegistryOptions,\n\tSessionRegistrySession,\n\tSessionRegistrySweepOptions,\n\tStoredSessionRegistryEntry,\n\tTerminalSessionSignal,\n} from \"./registry-types.ts\";\n\nexport { isTerminalSessionExited, sessionIdPrefix } from \"./registry-session.ts\";\nexport type {\n\tInitialSessionRegistryEntry,\n\tMaybePromise,\n\tSessionRegistryCreateContext,\n\tSessionRegistryCreateOptions,\n\tSessionRegistryEntry,\n\tSessionRegistryOptions,\n\tSessionRegistrySession,\n\tSessionRegistrySweepOptions,\n\tTerminalSessionSignal,\n\tTerminalSessionState,\n\tTrackedDetachedChild,\n} from \"./registry-types.ts\";\nexport { SessionRegistryCapacityError } from \"./registry-types.ts\";\n\nimport { SessionRegistryCapacityError } from \"./registry-types.ts\";\n\nconst DEFAULT_MAX_SESSIONS = 32;\nconst DEFAULT_COMMAND = \"bash\";\nconst DEFAULT_STOP_EXIT_GRACE_MS = 5000;\nconst DEFAULT_FORCED_EXIT_GRACE_MS = 1000;\nconst MAX_STOP_EXIT_GRACE_MS = 2_147_483_647;\n\nexport class SessionRegistry<TSession extends SessionRegistrySession = SessionRegistrySession> {\n\tprivate readonly entries = new Map<string, StoredSessionRegistryEntry<TSession>>();\n\tprivate readonly idCounters = new Map<string, number>();\n\tprivate readonly createSession: ((options: SessionRegistryCreateContext) => MaybePromise<TSession>) | null;\n\tprivate readonly killProcess: (target: number, signal: TerminalSessionSignal) => void;\n\tprivate readonly now: () => number;\n\tprivate readonly platform: string;\n\tprivate sequence = 0;\n\treadonly maxSessions: number;\n\tprivate readonly stopExitGraceMs: number;\n\tprivate readonly forcedExitGraceMs: number;\n\tprivate readonly detachedExitGraceMs: number;\n\n\tconstructor(options: SessionRegistryOptions<TSession> = {}) {\n\t\tthis.maxSessions = normalizeMaxSessions(options.maxSessions);\n\t\tthis.stopExitGraceMs =\n\t\t\toptions.stopExitGraceMs !== undefined &&\n\t\t\tNumber.isFinite(options.stopExitGraceMs) &&\n\t\t\toptions.stopExitGraceMs > 0\n\t\t\t\t? Math.min(options.stopExitGraceMs, MAX_STOP_EXIT_GRACE_MS)\n\t\t\t\t: DEFAULT_STOP_EXIT_GRACE_MS;\n\t\tthis.forcedExitGraceMs = normalizeGraceMs(options.forcedExitGraceMs, DEFAULT_FORCED_EXIT_GRACE_MS);\n\t\tthis.detachedExitGraceMs = normalizeGraceMs(options.detachedExitGraceMs, DEFAULT_DETACHED_EXIT_GRACE_MS);\n\t\tthis.createSession = options.createSession ?? null;\n\t\tthis.killProcess = options.killProcess ?? defaultKillProcess;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.platform = options.platform ?? getRuntimePlatform();\n\n\t\tfor (const initial of options.initialSessions ?? []) {\n\t\t\tthis.addEntry(initial.id, initial.session, initial.command ?? initial.session.command ?? DEFAULT_COMMAND);\n\t\t}\n\t\tvoid this.sweepExited();\n\t}\n\n\tget size(): number {\n\t\treturn this.entries.size;\n\t}\n\n\tlist(): readonly SessionRegistryEntry<TSession>[] {\n\t\tthis.refreshExitedStates();\n\t\treturn [...this.entries.values()].sort((left, right) => left.createdAt - right.createdAt);\n\t}\n\n\tget(id: string): SessionRegistryEntry<TSession> | null {\n\t\tconst entry = this.entries.get(id);\n\t\tif (!entry) return null;\n\t\tentry.lastUsedAt = this.timestamp();\n\t\tthis.refreshEntryState(entry);\n\t\treturn entry;\n\t}\n\n\tasync create(options: SessionRegistryCreateOptions<TSession> = {}): Promise<SessionRegistryEntry<TSession>> {\n\t\tawait this.enforceCapacityForCreate();\n\t\tconst command = options.command ?? options.session?.command ?? DEFAULT_COMMAND;\n\t\tconst id = this.allocateId(command);\n\t\tconst session =\n\t\t\toptions.session ??\n\t\t\t(await this.createSessionWithFactory({\n\t\t\t\tid,\n\t\t\t\tcommand,\n\t\t\t\targs: options.args,\n\t\t\t\tcwd: options.cwd,\n\t\t\t\tenv: options.env,\n\t\t\t\tcols: options.cols,\n\t\t\t\trows: options.rows,\n\t\t\t}));\n\t\treturn this.addEntry(id, session, command);\n\t}\n\n\tasync sweepExited(options: SessionRegistrySweepOptions = {}): Promise<readonly string[]> {\n\t\tthis.refreshExitedStates();\n\t\tconst sweptIds: string[] = [];\n\t\tfor (const entry of this.entries.values()) {\n\t\t\tif (entry.state !== \"exited\") continue;\n\t\t\tawait this.cleanupDetachedChildren(entry);\n\t\t\tsweptIds.push(entry.id);\n\t\t\tif (options.remove) this.removeEntry(entry.id);\n\t\t}\n\t\treturn sweptIds;\n\t}\n\n\tasync stop(id: string): Promise<boolean> {\n\t\tconst entry = this.entries.get(id);\n\t\tif (!entry) return false;\n\t\tif (entry.stopPromise) return entry.stopPromise;\n\t\tentry.stopPromise = this.stopEntry(entry);\n\t\treturn entry.stopPromise;\n\t}\n\n\tasync stopAll(): Promise<void> {\n\t\tawait Promise.all([...this.entries.keys()].map((id) => this.stop(id)));\n\t}\n\n\tasync teardown(): Promise<void> {\n\t\tawait this.stopAll();\n\t\t// Dropping an entry is the registry forgetting the process exists, so no entry\n\t\t// leaves here without having been escalated to SIGKILL first.\n\t\tfor (const entry of [...this.entries.values()]) {\n\t\t\tthis.refreshEntryState(entry);\n\t\t\tif (entry.state !== \"exited\") await this.forceKillEntry(entry);\n\t\t}\n\t\tfor (const id of [...this.entries.keys()]) this.removeEntry(id);\n\t}\n\n\tprivate async createSessionWithFactory(options: SessionRegistryCreateContext): Promise<TSession> {\n\t\tif (!this.createSession) {\n\t\t\tthrow new Error(\"Cannot create terminal session: SessionRegistry requires a createSession factory.\");\n\t\t}\n\t\treturn this.createSession(options);\n\t}\n\n\tprivate addEntry(id: string, session: TSession, command: string): SessionRegistryEntry<TSession> {\n\t\tconst timestamp = this.timestamp();\n\t\tconst exited = isTerminalSessionExited(session);\n\t\tconst entry: StoredSessionRegistryEntry<TSession> = {\n\t\t\tid,\n\t\t\tcommand,\n\t\t\tsession,\n\t\t\tcreatedAt: timestamp,\n\t\t\tlastUsedAt: timestamp,\n\t\t\tstate: exited ? \"exited\" : \"live\",\n\t\t\texitedAt: exited ? timestamp : null,\n\t\t\tunsubscribeExit: null,\n\t\t\tstopPromise: null,\n\t\t\tdetachedCleanupPromise: null,\n\t\t\tforceKilled: false,\n\t\t};\n\t\tentry.unsubscribeExit =\n\t\t\tsession.onExit?.(() => {\n\t\t\t\tvoid this.handleSessionExit(id);\n\t\t\t}) ?? null;\n\t\tthis.entries.set(id, entry);\n\t\tthis.observeId(id);\n\t\treturn entry;\n\t}\n\n\tprivate async handleSessionExit(id: string): Promise<void> {\n\t\tconst entry = this.entries.get(id);\n\t\tif (!entry) return;\n\t\tthis.markExited(entry);\n\t\tawait this.cleanupDetachedChildren(entry);\n\t}\n\n\tprivate async enforceCapacityForCreate(): Promise<void> {\n\t\tthis.refreshExitedStates();\n\t\tawait this.pruneExitedUntilAtMost(this.maxSessions - 1);\n\t\tif (this.entries.size >= this.maxSessions) throw new SessionRegistryCapacityError(this.maxSessions);\n\t}\n\n\tprivate async pruneExitedUntilAtMost(maxSize: number): Promise<void> {\n\t\twhile (this.entries.size > maxSize) {\n\t\t\tconst entry = this.findLeastRecentlyUsedExited();\n\t\t\tif (!entry) return;\n\t\t\tawait this.cleanupDetachedChildren(entry);\n\t\t\tthis.removeEntry(entry.id);\n\t\t}\n\t}\n\n\tprivate findLeastRecentlyUsedExited(): StoredSessionRegistryEntry<TSession> | null {\n\t\tlet selected: StoredSessionRegistryEntry<TSession> | null = null;\n\t\tfor (const entry of this.entries.values()) {\n\t\t\tif (entry.state !== \"exited\") continue;\n\t\t\tif (!selected || entry.lastUsedAt < selected.lastUsedAt) selected = entry;\n\t\t\telse if (selected.lastUsedAt === entry.lastUsedAt && entry.createdAt < selected.createdAt) selected = entry;\n\t\t}\n\t\treturn selected;\n\t}\n\n\tprivate async stopEntry(entry: StoredSessionRegistryEntry<TSession>): Promise<boolean> {\n\t\tthis.refreshEntryState(entry);\n\t\tawait this.cleanupDetachedChildren(entry);\n\t\tif (entry.state !== \"exited\") {\n\t\t\tawait stopTerminalSession(entry.session);\n\t\t\tif (await waitForTerminalSessionExit(entry.session, this.stopExitGraceMs)) this.markExited(entry);\n\t\t\telse await this.forceKillEntry(entry);\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * SIGKILL a session that outlived the graceful stop. Only a session that even\n\t * survives the forced signal (or exposes no way to be signalled) stays\n\t * `stopping`; everything else settles as `exited`.\n\t */\n\tprivate async forceKillEntry(entry: StoredSessionRegistryEntry<TSession>): Promise<void> {\n\t\tif (!entry.forceKilled) {\n\t\t\tentry.forceKilled = true;\n\t\t\tconst delivered = await forceKillTerminalSession(entry.session);\n\t\t\tif (delivered && (await waitForTerminalSessionExit(entry.session, this.forcedExitGraceMs))) {\n\t\t\t\tthis.markExited(entry);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (isTerminalSessionExited(entry.session)) {\n\t\t\tthis.markExited(entry);\n\t\t\treturn;\n\t\t}\n\t\tentry.state = \"stopping\";\n\t\tentry.exitedAt = null;\n\t}\n\n\tprivate async cleanupDetachedChildren(entry: StoredSessionRegistryEntry<TSession>): Promise<void> {\n\t\tif (entry.detachedCleanupPromise) return entry.detachedCleanupPromise;\n\t\tentry.detachedCleanupPromise = this.cleanupDetachedChildrenOnce(entry.session);\n\t\treturn entry.detachedCleanupPromise;\n\t}\n\n\tprivate async cleanupDetachedChildrenOnce(session: TSession): Promise<void> {\n\t\tawait cleanupDetachedChildren(session, this.platform, this.killProcess, this.detachedExitGraceMs);\n\t}\n\n\tprivate refreshExitedStates(): void {\n\t\tfor (const entry of this.entries.values()) this.refreshEntryState(entry);\n\t}\n\n\tprivate refreshEntryState(entry: StoredSessionRegistryEntry<TSession>): void {\n\t\tif (entry.state === \"exited\") return;\n\t\tif (isTerminalSessionExited(entry.session)) this.markExited(entry);\n\t}\n\n\tprivate markExited(entry: StoredSessionRegistryEntry<TSession>): void {\n\t\tif (entry.state === \"exited\") return;\n\t\tentry.state = \"exited\";\n\t\tentry.exitedAt = this.timestamp();\n\t}\n\n\tprivate removeEntry(id: string): void {\n\t\tconst entry = this.entries.get(id);\n\t\tif (!entry) return;\n\t\tentry.unsubscribeExit?.();\n\t\tthis.entries.delete(id);\n\t}\n\n\tprivate allocateId(command: string): string {\n\t\tconst prefix = sessionIdPrefix(command);\n\t\tlet next = (this.idCounters.get(prefix) ?? 0) + 1;\n\t\twhile (this.entries.has(`${prefix}_${next}`)) next += 1;\n\t\tthis.idCounters.set(prefix, next);\n\t\treturn `${prefix}_${next}`;\n\t}\n\n\tprivate observeId(id: string): void {\n\t\tconst match = /^(.*)_(\\d+)$/.exec(id);\n\t\tif (!match) return;\n\t\tconst prefix = match[1];\n\t\tconst value = Number.parseInt(match[2] ?? \"\", 10);\n\t\tif (!prefix || !Number.isFinite(value)) return;\n\t\tthis.idCounters.set(prefix, Math.max(this.idCounters.get(prefix) ?? 0, value));\n\t}\n\n\tprivate timestamp(): number {\n\t\tthis.sequence += 1;\n\t\treturn this.now() * 1000 + this.sequence;\n\t}\n}\n\nfunction normalizeGraceMs(value: number | undefined, fallback: number): number {\n\tif (value === undefined || !Number.isFinite(value) || value < 0) return fallback;\n\treturn Math.min(value, MAX_STOP_EXIT_GRACE_MS);\n}\n\nfunction normalizeMaxSessions(value: number | undefined): number {\n\tif (value === undefined) return DEFAULT_MAX_SESSIONS;\n\tif (!Number.isFinite(value) || value < 1) throw new Error(\"maxSessions must be a positive finite number.\");\n\treturn Math.trunc(value);\n}\n"]}
|
package/dist/session-types.d.ts
CHANGED
|
@@ -22,6 +22,14 @@ export interface TerminalSessionNativeOptions {
|
|
|
22
22
|
readonly rows: number;
|
|
23
23
|
readonly timeoutMs?: number;
|
|
24
24
|
}
|
|
25
|
+
export interface TerminalSessionTerminateOptions {
|
|
26
|
+
/** Signal delivered first; defaults to SIGTERM. */
|
|
27
|
+
readonly signal?: TerminalSessionSignal;
|
|
28
|
+
/** How long to wait for a graceful exit before escalating to SIGKILL. Default 5000ms. */
|
|
29
|
+
readonly graceMs?: number;
|
|
30
|
+
/** How long to wait for the exit after SIGKILL before giving up. Default 1000ms. */
|
|
31
|
+
readonly forcedGraceMs?: number;
|
|
32
|
+
}
|
|
25
33
|
export interface TerminalSessionOperationResult {
|
|
26
34
|
readonly ok: boolean;
|
|
27
35
|
readonly note: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-types.d.ts","sourceRoot":"","sources":["../src/session-types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session-types.d.ts","sourceRoot":"","sources":["../src/session-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,MAAM,MAAM,sBAAsB,GAAG,QAAQ,GAAG,eAAe,GAAG,KAAK,CAAC;AACxE,MAAM,MAAM,0BAA0B,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;AACjE,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,OAAO,CAAC;AAEnD,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,+BAA+B;IAC/C,mDAAmD;IACnD,QAAQ,CAAC,MAAM,CAAC,EAAE,qBAAqB,CAAC;IACxC,yFAAyF;IACzF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,oFAAoF;IACpF,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,8BAA8B;IAC9C,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,OAAO,EAAE,sBAAsB,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,wBAAwB,CAAC;CAC1C;AAED,MAAM,MAAM,wBAAwB,GACjC;IACA,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;CACnB,GACD;IACA,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;CAClC,CAAC;AAEL,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,0BAA0B,KAAK,MAAM,IAAI,CAAC;IACtE,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,KAAK,8BAA8B,GAAG,SAAS,CAAC;IAC1F,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,8BAA8B,GAAG,SAAS,CAAC;IAC5F,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,qBAAqB,KAAK,8BAA8B,GAAG,SAAS,CAAC;IAC9F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,2BAA2B,GAAG,CACzC,OAAO,EAAE,4BAA4B,EACrC,MAAM,EAAE,0BAA0B,KAC9B,qBAAqB,CAAC;AAE3B,MAAM,WAAW,2BAA2B;IAC3C,QAAQ,CAAC,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IAChD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,2BAA2B,CAAC;IAC3D,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC5D,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,kBAAkB,EAAE,kBAAkB,CAAC;IACzE,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,kBAAkB,EAAE,UAAU,CAAC;CAC5D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-types.js","sourceRoot":"","sources":["../src/session-types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Buffer } from \"node:buffer\";\nimport type { NativePtyLoadResult } from \"./native-loader.ts\";\n\nexport type TerminalSessionBackend = \"native\" | \"pipe-fallback\" | \"bun\";\nexport type TerminalSessionDataHandler = (chunk: Buffer) => void;\nexport type TerminalSessionSignal = NodeJS.Signals;\n\nexport interface TerminalSessionOptions {\n\treadonly command?: string;\n\treadonly args?: readonly string[];\n\treadonly cwd?: string;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n\treadonly cols?: number;\n\treadonly rows?: number;\n\treadonly timeoutMs?: number;\n\treadonly rawTailBytes?: number;\n}\n\nexport interface TerminalSessionNativeOptions {\n\treadonly command: string;\n\treadonly args: readonly string[];\n\treadonly cwd?: string;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n\treadonly cols: number;\n\treadonly rows: number;\n\treadonly timeoutMs?: number;\n}\n\nexport interface TerminalSessionOperationResult {\n\treadonly ok: boolean;\n\treadonly note: string;\n\treadonly code?: string;\n\treadonly idempotent?: boolean;\n}\n\nexport interface TerminalSessionExitError {\n\treadonly code: string;\n\treadonly message: string;\n}\n\nexport interface TerminalSessionExit {\n\treadonly backend: TerminalSessionBackend;\n\treadonly exitCode: number | null;\n\treadonly signal: string | null;\n\treadonly cancelled: boolean;\n\treadonly timedOut: boolean;\n\treadonly error?: TerminalSessionExitError;\n}\n\nexport type TerminalSessionExitState =\n\t| {\n\t\t\treadonly status: \"not_started\" | \"running\";\n\t\t\treadonly exit: null;\n\t }\n\t| {\n\t\t\treadonly status: \"exited\";\n\t\t\treadonly exit: TerminalSessionExit;\n\t };\n\nexport interface TerminalSessionHandle {\n\treadonly onData?: (handler: TerminalSessionDataHandler) => () => void;\n\treadonly write: (data: string | Uint8Array) => TerminalSessionOperationResult | undefined;\n\treadonly resize: (cols: number, rows: number) => TerminalSessionOperationResult | undefined;\n\treadonly kill: (signal?: TerminalSessionSignal) => TerminalSessionOperationResult | undefined;\n\treadonly waitExit?: () => Promise<unknown>;\n\treadonly wait?: () => Promise<unknown>;\n}\n\nexport type CreateNativeTerminalSession = (\n\toptions: TerminalSessionNativeOptions,\n\tonData: TerminalSessionDataHandler,\n) => TerminalSessionHandle;\n\nexport interface TerminalSessionDependencies {\n\treadonly nativeLoadResult?: NativePtyLoadResult;\n\treadonly createNativeSession?: CreateNativeTerminalSession;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n\treadonly runtimeVersions?: import(\"./session-bun.ts\").BunRuntimeVersions;\n\treadonly bunRuntime?: import(\"./session-bun.ts\").BunRuntime;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-types.js","sourceRoot":"","sources":["../src/session-types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Buffer } from \"node:buffer\";\nimport type { NativePtyLoadResult } from \"./native-loader.ts\";\n\nexport type TerminalSessionBackend = \"native\" | \"pipe-fallback\" | \"bun\";\nexport type TerminalSessionDataHandler = (chunk: Buffer) => void;\nexport type TerminalSessionSignal = NodeJS.Signals;\n\nexport interface TerminalSessionOptions {\n\treadonly command?: string;\n\treadonly args?: readonly string[];\n\treadonly cwd?: string;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n\treadonly cols?: number;\n\treadonly rows?: number;\n\treadonly timeoutMs?: number;\n\treadonly rawTailBytes?: number;\n}\n\nexport interface TerminalSessionNativeOptions {\n\treadonly command: string;\n\treadonly args: readonly string[];\n\treadonly cwd?: string;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n\treadonly cols: number;\n\treadonly rows: number;\n\treadonly timeoutMs?: number;\n}\n\nexport interface TerminalSessionTerminateOptions {\n\t/** Signal delivered first; defaults to SIGTERM. */\n\treadonly signal?: TerminalSessionSignal;\n\t/** How long to wait for a graceful exit before escalating to SIGKILL. Default 5000ms. */\n\treadonly graceMs?: number;\n\t/** How long to wait for the exit after SIGKILL before giving up. Default 1000ms. */\n\treadonly forcedGraceMs?: number;\n}\n\nexport interface TerminalSessionOperationResult {\n\treadonly ok: boolean;\n\treadonly note: string;\n\treadonly code?: string;\n\treadonly idempotent?: boolean;\n}\n\nexport interface TerminalSessionExitError {\n\treadonly code: string;\n\treadonly message: string;\n}\n\nexport interface TerminalSessionExit {\n\treadonly backend: TerminalSessionBackend;\n\treadonly exitCode: number | null;\n\treadonly signal: string | null;\n\treadonly cancelled: boolean;\n\treadonly timedOut: boolean;\n\treadonly error?: TerminalSessionExitError;\n}\n\nexport type TerminalSessionExitState =\n\t| {\n\t\t\treadonly status: \"not_started\" | \"running\";\n\t\t\treadonly exit: null;\n\t }\n\t| {\n\t\t\treadonly status: \"exited\";\n\t\t\treadonly exit: TerminalSessionExit;\n\t };\n\nexport interface TerminalSessionHandle {\n\treadonly onData?: (handler: TerminalSessionDataHandler) => () => void;\n\treadonly write: (data: string | Uint8Array) => TerminalSessionOperationResult | undefined;\n\treadonly resize: (cols: number, rows: number) => TerminalSessionOperationResult | undefined;\n\treadonly kill: (signal?: TerminalSessionSignal) => TerminalSessionOperationResult | undefined;\n\treadonly waitExit?: () => Promise<unknown>;\n\treadonly wait?: () => Promise<unknown>;\n}\n\nexport type CreateNativeTerminalSession = (\n\toptions: TerminalSessionNativeOptions,\n\tonData: TerminalSessionDataHandler,\n) => TerminalSessionHandle;\n\nexport interface TerminalSessionDependencies {\n\treadonly nativeLoadResult?: NativePtyLoadResult;\n\treadonly createNativeSession?: CreateNativeTerminalSession;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n\treadonly runtimeVersions?: import(\"./session-bun.ts\").BunRuntimeVersions;\n\treadonly bunRuntime?: import(\"./session-bun.ts\").BunRuntime;\n}\n"]}
|
package/dist/session.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
2
|
import { type NativePtyLoadResult, type NativePtyUnavailableDiagnostic } from "./native-loader.ts";
|
|
3
|
-
import type { TerminalSessionBackend, TerminalSessionDataHandler, TerminalSessionDependencies, TerminalSessionExit, TerminalSessionExitState, TerminalSessionOperationResult, TerminalSessionOptions, TerminalSessionSignal } from "./session-types.ts";
|
|
4
|
-
export type { CreateNativeTerminalSession, TerminalSessionBackend, TerminalSessionDataHandler, TerminalSessionDependencies, TerminalSessionExit, TerminalSessionExitError, TerminalSessionExitState, TerminalSessionHandle, TerminalSessionNativeOptions, TerminalSessionOperationResult, TerminalSessionOptions, TerminalSessionSignal, } from "./session-types.ts";
|
|
3
|
+
import type { TerminalSessionBackend, TerminalSessionDataHandler, TerminalSessionDependencies, TerminalSessionExit, TerminalSessionExitState, TerminalSessionOperationResult, TerminalSessionOptions, TerminalSessionSignal, TerminalSessionTerminateOptions } from "./session-types.ts";
|
|
4
|
+
export type { CreateNativeTerminalSession, TerminalSessionBackend, TerminalSessionDataHandler, TerminalSessionDependencies, TerminalSessionExit, TerminalSessionExitError, TerminalSessionExitState, TerminalSessionHandle, TerminalSessionNativeOptions, TerminalSessionOperationResult, TerminalSessionOptions, TerminalSessionSignal, TerminalSessionTerminateOptions, } from "./session-types.ts";
|
|
5
5
|
export declare class TerminalSession {
|
|
6
6
|
readonly options: TerminalSessionOptions;
|
|
7
7
|
private readonly nativeLoadResult;
|
|
@@ -18,7 +18,7 @@ export declare class TerminalSession {
|
|
|
18
18
|
private settledExit;
|
|
19
19
|
private rawTailBuffer;
|
|
20
20
|
private rawByteCount;
|
|
21
|
-
private
|
|
21
|
+
private lastSignal;
|
|
22
22
|
private unsubscribeBackendData;
|
|
23
23
|
constructor(options?: TerminalSessionOptions, dependencies?: TerminalSessionDependencies);
|
|
24
24
|
get native(): NativePtyLoadResult;
|
|
@@ -39,8 +39,17 @@ export declare class TerminalSession {
|
|
|
39
39
|
resize(cols: number, rows: number): TerminalSessionOperationResult;
|
|
40
40
|
kill(signal?: TerminalSessionSignal): TerminalSessionOperationResult;
|
|
41
41
|
stop(): TerminalSessionOperationResult;
|
|
42
|
+
/**
|
|
43
|
+
* Stop the session for real: signal (SIGTERM by default), wait `graceMs` for
|
|
44
|
+
* the exit, then escalate to SIGKILL and wait `forcedGraceMs`. Resolves with
|
|
45
|
+
* the settled exit, or `null` when the process outlived both waits (or was
|
|
46
|
+
* never started).
|
|
47
|
+
*/
|
|
48
|
+
terminate(options?: TerminalSessionTerminateOptions): Promise<TerminalSessionExit | null>;
|
|
42
49
|
waitExit(): Promise<TerminalSessionExit>;
|
|
43
50
|
private waitBackendExit;
|
|
51
|
+
/** Await the settled exit for at most `graceMs`; `null` means it did not settle in time. */
|
|
52
|
+
private waitExitWithin;
|
|
44
53
|
private settleExit;
|
|
45
54
|
private emitData;
|
|
46
55
|
private appendRawTail;
|
package/dist/session.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAiB,KAAK,mBAAmB,EAAE,KAAK,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAWlH,OAAO,KAAK,EAEX,sBAAsB,EACtB,0BAA0B,EAC1B,2BAA2B,EAC3B,mBAAmB,EACnB,wBAAwB,EAExB,8BAA8B,EAC9B,sBAAsB,EACtB,qBAAqB,EACrB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EACX,2BAA2B,EAC3B,sBAAsB,EACtB,0BAA0B,EAC1B,2BAA2B,EAC3B,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,qBAAqB,EACrB,4BAA4B,EAC5B,8BAA8B,EAC9B,sBAAsB,EACtB,qBAAqB,
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAiB,KAAK,mBAAmB,EAAE,KAAK,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAWlH,OAAO,KAAK,EAEX,sBAAsB,EACtB,0BAA0B,EAC1B,2BAA2B,EAC3B,mBAAmB,EACnB,wBAAwB,EAExB,8BAA8B,EAC9B,sBAAsB,EACtB,qBAAqB,EACrB,+BAA+B,EAC/B,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EACX,2BAA2B,EAC3B,sBAAsB,EACtB,0BAA0B,EAC1B,2BAA2B,EAC3B,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,qBAAqB,EACrB,4BAA4B,EAC5B,8BAA8B,EAC9B,sBAAsB,EACtB,qBAAqB,EACrB,+BAA+B,GAC/B,MAAM,oBAAoB,CAAC;AAQ5B,qBAAa,eAAe;IAC3B,QAAQ,CAAC,OAAO,EAAE,sBAAsB,CAAC;IACzC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAsB;IACvD,OAAO,CAAC,QAAQ,CAAC,6BAA6B,CAAC,CAA8B;IAC7E,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA+C;IACnE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgD;IAChF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoD;IAC/E,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyC;IACtE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyB;IACtD,OAAO,CAAC,aAAa,CAAsC;IAC3D,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,WAAW,CAA6C;IAChE,OAAO,CAAC,WAAW,CAAoC;IACvD,OAAO,CAAC,aAAa,CAAmB;IACxC,OAAO,CAAC,YAAY,CAAK;IAIzB,OAAO,CAAC,UAAU,CAAsC;IACxD,OAAO,CAAC,sBAAsB,CAA6B;IAE3D,YAAY,OAAO,GAAE,sBAA2B,EAAE,YAAY,GAAE,2BAAgC,EAa/F;IAED,IAAI,MAAM,IAAI,mBAAmB,CAEhC;IAED,IAAI,qBAAqB,IAAI,8BAA8B,GAAG,IAAI,CAGjE;IAED,IAAI,OAAO,IAAI,sBAAsB,GAAG,IAAI,CAE3C;IAED,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,MAAM,IAAI,aAAa,GAAG,SAAS,GAAG,QAAQ,CAEjD;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,UAAU,IAAI,mBAAmB,GAAG,IAAI,CAE3C;IAED,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,IAAI,SAAS,IAAI,wBAAwB,CAGxC;IAED,KAAK,IAAI,IAAI,CAoCZ;IAED,MAAM,CAAC,OAAO,EAAE,0BAA0B,GAAG,MAAM,IAAI,CAGtD;IAED,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAItC;IAED,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,8BAA8B,CAK/D;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,8BAA8B,CAKjE;IAED,IAAI,CAAC,MAAM,GAAE,qBAAiC,GAAG,8BAA8B,CAuB9E;IAED,IAAI,IAAI,8BAA8B,CAErC;IAED;;;;;OAKG;IACG,SAAS,CAAC,OAAO,GAAE,+BAAoC,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAUlG;IAEK,QAAQ,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAI7C;YAEa,eAAe;IAU7B,4FAA4F;YAC9E,cAAc;IAoB5B,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,aAAa;CAUrB;AAOD,wBAAgB,qBAAqB,CACpC,OAAO,GAAE,sBAA2B,EACpC,YAAY,GAAE,2BAAgC,GAC5C,eAAe,CAEjB"}
|
package/dist/session.js
CHANGED
|
@@ -6,6 +6,11 @@ import { createBunTerminalSession, isBunTerminalEnabled } from "./session-bun.js
|
|
|
6
6
|
import { exitedOperation, normalizeOperationResult, normalizeTerminalExit, notStartedOperation, } from "./session-exit.js";
|
|
7
7
|
import { getNativeSessionFactory } from "./session-native.js";
|
|
8
8
|
import { defaultCommand, normalizeRawTailBytes, toNativeOptions, toPipeFallbackOptions } from "./session-options.js";
|
|
9
|
+
/** Default wait for a graceful exit before `terminate()` escalates to SIGKILL. */
|
|
10
|
+
const DEFAULT_TERMINATE_GRACE_MS = 5000;
|
|
11
|
+
/** Default wait for the exit after SIGKILL before `terminate()` reports failure. */
|
|
12
|
+
const DEFAULT_FORCED_GRACE_MS = 1000;
|
|
13
|
+
const FORCE_SIGNAL = "SIGKILL";
|
|
9
14
|
export class TerminalSession {
|
|
10
15
|
constructor(options = {}, dependencies = {}) {
|
|
11
16
|
this.dataHandlers = new Set();
|
|
@@ -16,7 +21,10 @@ export class TerminalSession {
|
|
|
16
21
|
this.settledExit = null;
|
|
17
22
|
this.rawTailBuffer = Buffer.alloc(0);
|
|
18
23
|
this.rawByteCount = 0;
|
|
19
|
-
|
|
24
|
+
// Last signal actually handed to the backend. Tracking the signal (instead of a
|
|
25
|
+
// boolean) keeps repeated kills idempotent while still letting an escalation
|
|
26
|
+
// (e.g. SIGKILL after an ignored SIGTERM) reach the process.
|
|
27
|
+
this.lastSignal = null;
|
|
20
28
|
this.unsubscribeBackendData = null;
|
|
21
29
|
this.options = {
|
|
22
30
|
...options,
|
|
@@ -131,7 +139,14 @@ export class TerminalSession {
|
|
|
131
139
|
}
|
|
132
140
|
kill(signal = "SIGTERM") {
|
|
133
141
|
const handle = this.backendHandle;
|
|
134
|
-
if (this.
|
|
142
|
+
if (this.settledExit !== null) {
|
|
143
|
+
return {
|
|
144
|
+
ok: true,
|
|
145
|
+
idempotent: true,
|
|
146
|
+
note: "Terminal session has already exited.",
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (this.lastSignal === signal) {
|
|
135
150
|
return {
|
|
136
151
|
ok: true,
|
|
137
152
|
idempotent: true,
|
|
@@ -140,15 +155,34 @@ export class TerminalSession {
|
|
|
140
155
|
}
|
|
141
156
|
if (handle === null)
|
|
142
157
|
return notStartedOperation("kill");
|
|
143
|
-
|
|
158
|
+
const previousSignal = this.lastSignal;
|
|
159
|
+
this.lastSignal = signal;
|
|
144
160
|
const result = normalizeOperationResult(handle.kill(signal), `Sent ${signal} to terminal session.`);
|
|
145
161
|
if (!result.ok)
|
|
146
|
-
this.
|
|
162
|
+
this.lastSignal = previousSignal;
|
|
147
163
|
return result;
|
|
148
164
|
}
|
|
149
165
|
stop() {
|
|
150
166
|
return this.kill();
|
|
151
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Stop the session for real: signal (SIGTERM by default), wait `graceMs` for
|
|
170
|
+
* the exit, then escalate to SIGKILL and wait `forcedGraceMs`. Resolves with
|
|
171
|
+
* the settled exit, or `null` when the process outlived both waits (or was
|
|
172
|
+
* never started).
|
|
173
|
+
*/
|
|
174
|
+
async terminate(options = {}) {
|
|
175
|
+
if (this.settledExit !== null)
|
|
176
|
+
return this.settledExit;
|
|
177
|
+
if (this.backendHandle === null)
|
|
178
|
+
return null;
|
|
179
|
+
this.kill(options.signal ?? "SIGTERM");
|
|
180
|
+
const graceful = await this.waitExitWithin(normalizeGraceMs(options.graceMs, DEFAULT_TERMINATE_GRACE_MS));
|
|
181
|
+
if (graceful !== null)
|
|
182
|
+
return graceful;
|
|
183
|
+
this.kill(FORCE_SIGNAL);
|
|
184
|
+
return await this.waitExitWithin(normalizeGraceMs(options.forcedGraceMs, DEFAULT_FORCED_GRACE_MS));
|
|
185
|
+
}
|
|
152
186
|
async waitExit() {
|
|
153
187
|
if (this.settledExit !== null)
|
|
154
188
|
return this.settledExit;
|
|
@@ -161,7 +195,30 @@ export class TerminalSession {
|
|
|
161
195
|
if (!wait)
|
|
162
196
|
throw new Error("Terminal session backend does not expose waitExit or wait");
|
|
163
197
|
const exit = await wait.call(handle);
|
|
164
|
-
return normalizeTerminalExit(exit, backend, this.
|
|
198
|
+
return normalizeTerminalExit(exit, backend, this.lastSignal !== null);
|
|
199
|
+
}
|
|
200
|
+
/** Await the settled exit for at most `graceMs`; `null` means it did not settle in time. */
|
|
201
|
+
async waitExitWithin(graceMs) {
|
|
202
|
+
if (this.settledExit !== null)
|
|
203
|
+
return this.settledExit;
|
|
204
|
+
const exitPromise = this.exitPromise;
|
|
205
|
+
if (exitPromise === null || graceMs <= 0)
|
|
206
|
+
return this.settledExit;
|
|
207
|
+
let graceTimer;
|
|
208
|
+
try {
|
|
209
|
+
return await Promise.race([
|
|
210
|
+
// Attaching the catch here also keeps a late backend rejection from
|
|
211
|
+
// surfacing as an unhandled rejection once the grace timer wins.
|
|
212
|
+
exitPromise.catch(() => null),
|
|
213
|
+
new Promise((resolve) => {
|
|
214
|
+
graceTimer = setTimeout(() => resolve(null), graceMs);
|
|
215
|
+
}),
|
|
216
|
+
]);
|
|
217
|
+
}
|
|
218
|
+
finally {
|
|
219
|
+
if (graceTimer !== undefined)
|
|
220
|
+
clearTimeout(graceTimer);
|
|
221
|
+
}
|
|
165
222
|
}
|
|
166
223
|
settleExit(exit) {
|
|
167
224
|
if (this.settledExit !== null)
|
|
@@ -191,6 +248,11 @@ export class TerminalSession {
|
|
|
191
248
|
next.byteLength <= this.rawTailLimit ? next : next.subarray(next.byteLength - this.rawTailLimit);
|
|
192
249
|
}
|
|
193
250
|
}
|
|
251
|
+
function normalizeGraceMs(value, fallback) {
|
|
252
|
+
if (value === undefined || !Number.isFinite(value) || value < 0)
|
|
253
|
+
return fallback;
|
|
254
|
+
return value;
|
|
255
|
+
}
|
|
194
256
|
export function createTerminalSession(options = {}, dependencies = {}) {
|
|
195
257
|
return new TerminalSession(options, dependencies).start();
|
|
196
258
|
}
|
package/dist/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport process from \"node:process\";\nimport { loadNativePty, type NativePtyLoadResult, type NativePtyUnavailableDiagnostic } from \"./native-loader.ts\";\nimport { PipeFallbackSession, shouldUsePipeFallback } from \"./pipe-fallback.ts\";\nimport { createBunTerminalSession, isBunTerminalEnabled } from \"./session-bun.ts\";\nimport {\n\texitedOperation,\n\tnormalizeOperationResult,\n\tnormalizeTerminalExit,\n\tnotStartedOperation,\n} from \"./session-exit.ts\";\nimport { getNativeSessionFactory } from \"./session-native.ts\";\nimport { defaultCommand, normalizeRawTailBytes, toNativeOptions, toPipeFallbackOptions } from \"./session-options.ts\";\nimport type {\n\tCreateNativeTerminalSession,\n\tTerminalSessionBackend,\n\tTerminalSessionDataHandler,\n\tTerminalSessionDependencies,\n\tTerminalSessionExit,\n\tTerminalSessionExitState,\n\tTerminalSessionHandle,\n\tTerminalSessionOperationResult,\n\tTerminalSessionOptions,\n\tTerminalSessionSignal,\n} from \"./session-types.ts\";\n\nexport type {\n\tCreateNativeTerminalSession,\n\tTerminalSessionBackend,\n\tTerminalSessionDataHandler,\n\tTerminalSessionDependencies,\n\tTerminalSessionExit,\n\tTerminalSessionExitError,\n\tTerminalSessionExitState,\n\tTerminalSessionHandle,\n\tTerminalSessionNativeOptions,\n\tTerminalSessionOperationResult,\n\tTerminalSessionOptions,\n\tTerminalSessionSignal,\n} from \"./session-types.ts\";\n\nexport class TerminalSession {\n\treadonly options: TerminalSessionOptions;\n\tprivate readonly nativeLoadResult: NativePtyLoadResult;\n\tprivate readonly createNativeSessionDependency?: CreateNativeTerminalSession;\n\tprivate readonly env: Readonly<Record<string, string | undefined>>;\n\tprivate readonly runtimeVersions: import(\"./session-bun.ts\").BunRuntimeVersions;\n\tprivate readonly bunRuntime: import(\"./session-bun.ts\").BunRuntime | undefined;\n\tprivate readonly rawTailLimit: number;\n\tprivate readonly dataHandlers = new Set<TerminalSessionDataHandler>();\n\tprivate readonly exitHandlers = new Set<() => void>();\n\tprivate backendHandle: TerminalSessionHandle | null = null;\n\tprivate backendValue: TerminalSessionBackend | null = null;\n\tprivate exitPromise: Promise<TerminalSessionExit> | null = null;\n\tprivate settledExit: TerminalSessionExit | null = null;\n\tprivate rawTailBuffer = Buffer.alloc(0);\n\tprivate rawByteCount = 0;\n\tprivate killRequested = false;\n\tprivate unsubscribeBackendData: (() => void) | null = null;\n\n\tconstructor(options: TerminalSessionOptions = {}, dependencies: TerminalSessionDependencies = {}) {\n\t\tthis.options = {\n\t\t\t...options,\n\t\t\targs: options.args ? [...options.args] : undefined,\n\t\t\tenv: options.env ? { ...options.env } : undefined,\n\t\t};\n\t\tthis.nativeLoadResult = dependencies.nativeLoadResult ?? loadNativePty();\n\t\tthis.createNativeSessionDependency = dependencies.createNativeSession;\n\t\tthis.env = dependencies.env ?? process.env;\n\t\tthis.runtimeVersions =\n\t\t\tdependencies.runtimeVersions ?? (process.versions as import(\"./session-bun.ts\").BunRuntimeVersions);\n\t\tthis.bunRuntime = dependencies.bunRuntime;\n\t\tthis.rawTailLimit = normalizeRawTailBytes(options.rawTailBytes);\n\t}\n\n\tget native(): NativePtyLoadResult {\n\t\treturn this.nativeLoadResult;\n\t}\n\n\tget unavailableDiagnostic(): NativePtyUnavailableDiagnostic | null {\n\t\tif (this.nativeLoadResult.native !== null) return null;\n\t\treturn this.nativeLoadResult.diagnostic;\n\t}\n\n\tget backend(): TerminalSessionBackend | null {\n\t\treturn this.backendValue;\n\t}\n\n\tget command(): string {\n\t\treturn this.options.command ?? defaultCommand();\n\t}\n\n\tget status(): \"not_started\" | \"running\" | \"exited\" {\n\t\treturn this.exitState.status;\n\t}\n\n\tget exited(): boolean {\n\t\treturn this.settledExit !== null;\n\t}\n\n\tget isExited(): boolean {\n\t\treturn this.exited;\n\t}\n\n\tget exitResult(): TerminalSessionExit | null {\n\t\treturn this.settledExit;\n\t}\n\n\tget rawTail(): Buffer {\n\t\treturn Buffer.from(this.rawTailBuffer);\n\t}\n\n\tget rawOutputBytes(): number {\n\t\treturn this.rawByteCount;\n\t}\n\n\tget exitState(): TerminalSessionExitState {\n\t\tif (this.settledExit !== null) return { status: \"exited\", exit: this.settledExit };\n\t\treturn { status: this.backendHandle === null ? \"not_started\" : \"running\", exit: null };\n\t}\n\n\tstart(): this {\n\t\tif (this.settledExit !== null) throw new Error(\"Cannot restart exited terminal session\");\n\t\tif (this.backendHandle !== null) throw new Error(\"Terminal session has already been started\");\n\n\t\tif (isBunTerminalEnabled(this.env, this.runtimeVersions)) {\n\t\t\tthis.backendValue = \"bun\";\n\t\t\tthis.backendHandle = createBunTerminalSession(\n\t\t\t\ttoNativeOptions(this.options),\n\t\t\t\t(chunk) => this.emitData(chunk),\n\t\t\t\tthis.bunRuntime,\n\t\t\t);\n\t\t} else {\n\t\t\tconst nativeFactory = this.createNativeSessionDependency ?? getNativeSessionFactory(this.nativeLoadResult);\n\t\t\tif (nativeFactory && !shouldUsePipeFallback(this.nativeLoadResult, this.env)) {\n\t\t\t\tthis.backendValue = \"native\";\n\t\t\t\tthis.backendHandle = nativeFactory(toNativeOptions(this.options), (chunk) => this.emitData(chunk));\n\t\t\t} else {\n\t\t\t\tthis.backendValue = \"pipe-fallback\";\n\t\t\t\tconst fallback = new PipeFallbackSession(toPipeFallbackOptions(this.options));\n\t\t\t\tthis.backendHandle = fallback;\n\t\t\t\tthis.unsubscribeBackendData = fallback.onData((chunk) => this.emitData(chunk));\n\t\t\t\tfallback.start();\n\t\t\t}\n\t\t}\n\n\t\tconst backendHandle = this.backendHandle;\n\t\tconst backendValue = this.backendValue;\n\t\tif (backendHandle === null || backendValue === null) {\n\t\t\tthrow new Error(\"Terminal session backend did not initialize\");\n\t\t}\n\n\t\tif (backendValue === \"native\" && backendHandle.onData) {\n\t\t\tthis.unsubscribeBackendData = backendHandle.onData((chunk) => this.emitData(chunk));\n\t\t}\n\t\tthis.exitPromise = this.waitBackendExit(backendHandle, backendValue).then((exit) => this.settleExit(exit));\n\t\treturn this;\n\t}\n\n\tonData(handler: TerminalSessionDataHandler): () => void {\n\t\tthis.dataHandlers.add(handler);\n\t\treturn () => this.dataHandlers.delete(handler);\n\t}\n\n\tonExit(handler: () => void): () => void {\n\t\tif (this.settledExit !== null) queueMicrotask(handler);\n\t\telse this.exitHandlers.add(handler);\n\t\treturn () => this.exitHandlers.delete(handler);\n\t}\n\n\twrite(data: string | Uint8Array): TerminalSessionOperationResult {\n\t\tconst handle = this.backendHandle;\n\t\tif (this.settledExit !== null) return exitedOperation(\"write\");\n\t\tif (handle === null) return notStartedOperation(\"write\");\n\t\treturn normalizeOperationResult(handle.write(data), \"Wrote data to terminal session.\");\n\t}\n\n\tresize(cols: number, rows: number): TerminalSessionOperationResult {\n\t\tconst handle = this.backendHandle;\n\t\tif (this.settledExit !== null) return exitedOperation(\"resize\");\n\t\tif (handle === null) return notStartedOperation(\"resize\");\n\t\treturn normalizeOperationResult(handle.resize(cols, rows), `Resized terminal session to ${cols}x${rows}.`);\n\t}\n\n\tkill(signal: TerminalSessionSignal = \"SIGTERM\"): TerminalSessionOperationResult {\n\t\tconst handle = this.backendHandle;\n\t\tif (this.killRequested || this.settledExit !== null) {\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tidempotent: true,\n\t\t\t\tnote: \"Terminal session kill was already requested.\",\n\t\t\t};\n\t\t}\n\t\tif (handle === null) return notStartedOperation(\"kill\");\n\n\t\tthis.killRequested = true;\n\t\tconst result = normalizeOperationResult(handle.kill(signal), `Sent ${signal} to terminal session.`);\n\t\tif (!result.ok) this.killRequested = false;\n\t\treturn result;\n\t}\n\n\tstop(): TerminalSessionOperationResult {\n\t\treturn this.kill();\n\t}\n\n\tasync waitExit(): Promise<TerminalSessionExit> {\n\t\tif (this.settledExit !== null) return this.settledExit;\n\t\tif (this.exitPromise === null) throw new Error(\"Cannot wait for terminal session exit before start\");\n\t\treturn await this.exitPromise;\n\t}\n\n\tprivate async waitBackendExit(\n\t\thandle: TerminalSessionHandle,\n\t\tbackend: TerminalSessionBackend,\n\t): Promise<TerminalSessionExit> {\n\t\tconst wait = handle.waitExit ?? handle.wait;\n\t\tif (!wait) throw new Error(\"Terminal session backend does not expose waitExit or wait\");\n\t\tconst exit = await wait.call(handle);\n\t\treturn normalizeTerminalExit(exit, backend, this.killRequested);\n\t}\n\n\tprivate settleExit(exit: TerminalSessionExit): TerminalSessionExit {\n\t\tif (this.settledExit !== null) return this.settledExit;\n\t\tthis.settledExit = exit;\n\t\tthis.unsubscribeBackendData?.();\n\t\tthis.unsubscribeBackendData = null;\n\t\tfor (const handler of this.exitHandlers) handler();\n\t\tthis.exitHandlers.clear();\n\t\treturn exit;\n\t}\n\n\tprivate emitData(chunk: Buffer | Uint8Array | string): void {\n\t\tconst buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n\t\tthis.appendRawTail(buffer);\n\t\tfor (const handler of this.dataHandlers) handler(buffer);\n\t}\n\n\tprivate appendRawTail(chunk: Buffer): void {\n\t\tthis.rawByteCount += chunk.byteLength;\n\t\tif (this.rawTailLimit === 0) {\n\t\t\tthis.rawTailBuffer = Buffer.alloc(0);\n\t\t\treturn;\n\t\t}\n\t\tconst next = Buffer.concat([this.rawTailBuffer, chunk]);\n\t\tthis.rawTailBuffer =\n\t\t\tnext.byteLength <= this.rawTailLimit ? next : next.subarray(next.byteLength - this.rawTailLimit);\n\t}\n}\n\nexport function createTerminalSession(\n\toptions: TerminalSessionOptions = {},\n\tdependencies: TerminalSessionDependencies = {},\n): TerminalSession {\n\treturn new TerminalSession(options, dependencies).start();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport process from \"node:process\";\nimport { loadNativePty, type NativePtyLoadResult, type NativePtyUnavailableDiagnostic } from \"./native-loader.ts\";\nimport { PipeFallbackSession, shouldUsePipeFallback } from \"./pipe-fallback.ts\";\nimport { createBunTerminalSession, isBunTerminalEnabled } from \"./session-bun.ts\";\nimport {\n\texitedOperation,\n\tnormalizeOperationResult,\n\tnormalizeTerminalExit,\n\tnotStartedOperation,\n} from \"./session-exit.ts\";\nimport { getNativeSessionFactory } from \"./session-native.ts\";\nimport { defaultCommand, normalizeRawTailBytes, toNativeOptions, toPipeFallbackOptions } from \"./session-options.ts\";\nimport type {\n\tCreateNativeTerminalSession,\n\tTerminalSessionBackend,\n\tTerminalSessionDataHandler,\n\tTerminalSessionDependencies,\n\tTerminalSessionExit,\n\tTerminalSessionExitState,\n\tTerminalSessionHandle,\n\tTerminalSessionOperationResult,\n\tTerminalSessionOptions,\n\tTerminalSessionSignal,\n\tTerminalSessionTerminateOptions,\n} from \"./session-types.ts\";\n\nexport type {\n\tCreateNativeTerminalSession,\n\tTerminalSessionBackend,\n\tTerminalSessionDataHandler,\n\tTerminalSessionDependencies,\n\tTerminalSessionExit,\n\tTerminalSessionExitError,\n\tTerminalSessionExitState,\n\tTerminalSessionHandle,\n\tTerminalSessionNativeOptions,\n\tTerminalSessionOperationResult,\n\tTerminalSessionOptions,\n\tTerminalSessionSignal,\n\tTerminalSessionTerminateOptions,\n} from \"./session-types.ts\";\n\n/** Default wait for a graceful exit before `terminate()` escalates to SIGKILL. */\nconst DEFAULT_TERMINATE_GRACE_MS = 5000;\n/** Default wait for the exit after SIGKILL before `terminate()` reports failure. */\nconst DEFAULT_FORCED_GRACE_MS = 1000;\nconst FORCE_SIGNAL: TerminalSessionSignal = \"SIGKILL\";\n\nexport class TerminalSession {\n\treadonly options: TerminalSessionOptions;\n\tprivate readonly nativeLoadResult: NativePtyLoadResult;\n\tprivate readonly createNativeSessionDependency?: CreateNativeTerminalSession;\n\tprivate readonly env: Readonly<Record<string, string | undefined>>;\n\tprivate readonly runtimeVersions: import(\"./session-bun.ts\").BunRuntimeVersions;\n\tprivate readonly bunRuntime: import(\"./session-bun.ts\").BunRuntime | undefined;\n\tprivate readonly rawTailLimit: number;\n\tprivate readonly dataHandlers = new Set<TerminalSessionDataHandler>();\n\tprivate readonly exitHandlers = new Set<() => void>();\n\tprivate backendHandle: TerminalSessionHandle | null = null;\n\tprivate backendValue: TerminalSessionBackend | null = null;\n\tprivate exitPromise: Promise<TerminalSessionExit> | null = null;\n\tprivate settledExit: TerminalSessionExit | null = null;\n\tprivate rawTailBuffer = Buffer.alloc(0);\n\tprivate rawByteCount = 0;\n\t// Last signal actually handed to the backend. Tracking the signal (instead of a\n\t// boolean) keeps repeated kills idempotent while still letting an escalation\n\t// (e.g. SIGKILL after an ignored SIGTERM) reach the process.\n\tprivate lastSignal: TerminalSessionSignal | null = null;\n\tprivate unsubscribeBackendData: (() => void) | null = null;\n\n\tconstructor(options: TerminalSessionOptions = {}, dependencies: TerminalSessionDependencies = {}) {\n\t\tthis.options = {\n\t\t\t...options,\n\t\t\targs: options.args ? [...options.args] : undefined,\n\t\t\tenv: options.env ? { ...options.env } : undefined,\n\t\t};\n\t\tthis.nativeLoadResult = dependencies.nativeLoadResult ?? loadNativePty();\n\t\tthis.createNativeSessionDependency = dependencies.createNativeSession;\n\t\tthis.env = dependencies.env ?? process.env;\n\t\tthis.runtimeVersions =\n\t\t\tdependencies.runtimeVersions ?? (process.versions as import(\"./session-bun.ts\").BunRuntimeVersions);\n\t\tthis.bunRuntime = dependencies.bunRuntime;\n\t\tthis.rawTailLimit = normalizeRawTailBytes(options.rawTailBytes);\n\t}\n\n\tget native(): NativePtyLoadResult {\n\t\treturn this.nativeLoadResult;\n\t}\n\n\tget unavailableDiagnostic(): NativePtyUnavailableDiagnostic | null {\n\t\tif (this.nativeLoadResult.native !== null) return null;\n\t\treturn this.nativeLoadResult.diagnostic;\n\t}\n\n\tget backend(): TerminalSessionBackend | null {\n\t\treturn this.backendValue;\n\t}\n\n\tget command(): string {\n\t\treturn this.options.command ?? defaultCommand();\n\t}\n\n\tget status(): \"not_started\" | \"running\" | \"exited\" {\n\t\treturn this.exitState.status;\n\t}\n\n\tget exited(): boolean {\n\t\treturn this.settledExit !== null;\n\t}\n\n\tget isExited(): boolean {\n\t\treturn this.exited;\n\t}\n\n\tget exitResult(): TerminalSessionExit | null {\n\t\treturn this.settledExit;\n\t}\n\n\tget rawTail(): Buffer {\n\t\treturn Buffer.from(this.rawTailBuffer);\n\t}\n\n\tget rawOutputBytes(): number {\n\t\treturn this.rawByteCount;\n\t}\n\n\tget exitState(): TerminalSessionExitState {\n\t\tif (this.settledExit !== null) return { status: \"exited\", exit: this.settledExit };\n\t\treturn { status: this.backendHandle === null ? \"not_started\" : \"running\", exit: null };\n\t}\n\n\tstart(): this {\n\t\tif (this.settledExit !== null) throw new Error(\"Cannot restart exited terminal session\");\n\t\tif (this.backendHandle !== null) throw new Error(\"Terminal session has already been started\");\n\n\t\tif (isBunTerminalEnabled(this.env, this.runtimeVersions)) {\n\t\t\tthis.backendValue = \"bun\";\n\t\t\tthis.backendHandle = createBunTerminalSession(\n\t\t\t\ttoNativeOptions(this.options),\n\t\t\t\t(chunk) => this.emitData(chunk),\n\t\t\t\tthis.bunRuntime,\n\t\t\t);\n\t\t} else {\n\t\t\tconst nativeFactory = this.createNativeSessionDependency ?? getNativeSessionFactory(this.nativeLoadResult);\n\t\t\tif (nativeFactory && !shouldUsePipeFallback(this.nativeLoadResult, this.env)) {\n\t\t\t\tthis.backendValue = \"native\";\n\t\t\t\tthis.backendHandle = nativeFactory(toNativeOptions(this.options), (chunk) => this.emitData(chunk));\n\t\t\t} else {\n\t\t\t\tthis.backendValue = \"pipe-fallback\";\n\t\t\t\tconst fallback = new PipeFallbackSession(toPipeFallbackOptions(this.options));\n\t\t\t\tthis.backendHandle = fallback;\n\t\t\t\tthis.unsubscribeBackendData = fallback.onData((chunk) => this.emitData(chunk));\n\t\t\t\tfallback.start();\n\t\t\t}\n\t\t}\n\n\t\tconst backendHandle = this.backendHandle;\n\t\tconst backendValue = this.backendValue;\n\t\tif (backendHandle === null || backendValue === null) {\n\t\t\tthrow new Error(\"Terminal session backend did not initialize\");\n\t\t}\n\n\t\tif (backendValue === \"native\" && backendHandle.onData) {\n\t\t\tthis.unsubscribeBackendData = backendHandle.onData((chunk) => this.emitData(chunk));\n\t\t}\n\t\tthis.exitPromise = this.waitBackendExit(backendHandle, backendValue).then((exit) => this.settleExit(exit));\n\t\treturn this;\n\t}\n\n\tonData(handler: TerminalSessionDataHandler): () => void {\n\t\tthis.dataHandlers.add(handler);\n\t\treturn () => this.dataHandlers.delete(handler);\n\t}\n\n\tonExit(handler: () => void): () => void {\n\t\tif (this.settledExit !== null) queueMicrotask(handler);\n\t\telse this.exitHandlers.add(handler);\n\t\treturn () => this.exitHandlers.delete(handler);\n\t}\n\n\twrite(data: string | Uint8Array): TerminalSessionOperationResult {\n\t\tconst handle = this.backendHandle;\n\t\tif (this.settledExit !== null) return exitedOperation(\"write\");\n\t\tif (handle === null) return notStartedOperation(\"write\");\n\t\treturn normalizeOperationResult(handle.write(data), \"Wrote data to terminal session.\");\n\t}\n\n\tresize(cols: number, rows: number): TerminalSessionOperationResult {\n\t\tconst handle = this.backendHandle;\n\t\tif (this.settledExit !== null) return exitedOperation(\"resize\");\n\t\tif (handle === null) return notStartedOperation(\"resize\");\n\t\treturn normalizeOperationResult(handle.resize(cols, rows), `Resized terminal session to ${cols}x${rows}.`);\n\t}\n\n\tkill(signal: TerminalSessionSignal = \"SIGTERM\"): TerminalSessionOperationResult {\n\t\tconst handle = this.backendHandle;\n\t\tif (this.settledExit !== null) {\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tidempotent: true,\n\t\t\t\tnote: \"Terminal session has already exited.\",\n\t\t\t};\n\t\t}\n\t\tif (this.lastSignal === signal) {\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tidempotent: true,\n\t\t\t\tnote: \"Terminal session kill was already requested.\",\n\t\t\t};\n\t\t}\n\t\tif (handle === null) return notStartedOperation(\"kill\");\n\n\t\tconst previousSignal = this.lastSignal;\n\t\tthis.lastSignal = signal;\n\t\tconst result = normalizeOperationResult(handle.kill(signal), `Sent ${signal} to terminal session.`);\n\t\tif (!result.ok) this.lastSignal = previousSignal;\n\t\treturn result;\n\t}\n\n\tstop(): TerminalSessionOperationResult {\n\t\treturn this.kill();\n\t}\n\n\t/**\n\t * Stop the session for real: signal (SIGTERM by default), wait `graceMs` for\n\t * the exit, then escalate to SIGKILL and wait `forcedGraceMs`. Resolves with\n\t * the settled exit, or `null` when the process outlived both waits (or was\n\t * never started).\n\t */\n\tasync terminate(options: TerminalSessionTerminateOptions = {}): Promise<TerminalSessionExit | null> {\n\t\tif (this.settledExit !== null) return this.settledExit;\n\t\tif (this.backendHandle === null) return null;\n\n\t\tthis.kill(options.signal ?? \"SIGTERM\");\n\t\tconst graceful = await this.waitExitWithin(normalizeGraceMs(options.graceMs, DEFAULT_TERMINATE_GRACE_MS));\n\t\tif (graceful !== null) return graceful;\n\n\t\tthis.kill(FORCE_SIGNAL);\n\t\treturn await this.waitExitWithin(normalizeGraceMs(options.forcedGraceMs, DEFAULT_FORCED_GRACE_MS));\n\t}\n\n\tasync waitExit(): Promise<TerminalSessionExit> {\n\t\tif (this.settledExit !== null) return this.settledExit;\n\t\tif (this.exitPromise === null) throw new Error(\"Cannot wait for terminal session exit before start\");\n\t\treturn await this.exitPromise;\n\t}\n\n\tprivate async waitBackendExit(\n\t\thandle: TerminalSessionHandle,\n\t\tbackend: TerminalSessionBackend,\n\t): Promise<TerminalSessionExit> {\n\t\tconst wait = handle.waitExit ?? handle.wait;\n\t\tif (!wait) throw new Error(\"Terminal session backend does not expose waitExit or wait\");\n\t\tconst exit = await wait.call(handle);\n\t\treturn normalizeTerminalExit(exit, backend, this.lastSignal !== null);\n\t}\n\n\t/** Await the settled exit for at most `graceMs`; `null` means it did not settle in time. */\n\tprivate async waitExitWithin(graceMs: number): Promise<TerminalSessionExit | null> {\n\t\tif (this.settledExit !== null) return this.settledExit;\n\t\tconst exitPromise = this.exitPromise;\n\t\tif (exitPromise === null || graceMs <= 0) return this.settledExit;\n\n\t\tlet graceTimer: ReturnType<typeof setTimeout> | undefined;\n\t\ttry {\n\t\t\treturn await Promise.race([\n\t\t\t\t// Attaching the catch here also keeps a late backend rejection from\n\t\t\t\t// surfacing as an unhandled rejection once the grace timer wins.\n\t\t\t\texitPromise.catch(() => null),\n\t\t\t\tnew Promise<null>((resolve) => {\n\t\t\t\t\tgraceTimer = setTimeout(() => resolve(null), graceMs);\n\t\t\t\t}),\n\t\t\t]);\n\t\t} finally {\n\t\t\tif (graceTimer !== undefined) clearTimeout(graceTimer);\n\t\t}\n\t}\n\n\tprivate settleExit(exit: TerminalSessionExit): TerminalSessionExit {\n\t\tif (this.settledExit !== null) return this.settledExit;\n\t\tthis.settledExit = exit;\n\t\tthis.unsubscribeBackendData?.();\n\t\tthis.unsubscribeBackendData = null;\n\t\tfor (const handler of this.exitHandlers) handler();\n\t\tthis.exitHandlers.clear();\n\t\treturn exit;\n\t}\n\n\tprivate emitData(chunk: Buffer | Uint8Array | string): void {\n\t\tconst buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n\t\tthis.appendRawTail(buffer);\n\t\tfor (const handler of this.dataHandlers) handler(buffer);\n\t}\n\n\tprivate appendRawTail(chunk: Buffer): void {\n\t\tthis.rawByteCount += chunk.byteLength;\n\t\tif (this.rawTailLimit === 0) {\n\t\t\tthis.rawTailBuffer = Buffer.alloc(0);\n\t\t\treturn;\n\t\t}\n\t\tconst next = Buffer.concat([this.rawTailBuffer, chunk]);\n\t\tthis.rawTailBuffer =\n\t\t\tnext.byteLength <= this.rawTailLimit ? next : next.subarray(next.byteLength - this.rawTailLimit);\n\t}\n}\n\nfunction normalizeGraceMs(value: number | undefined, fallback: number): number {\n\tif (value === undefined || !Number.isFinite(value) || value < 0) return fallback;\n\treturn value;\n}\n\nexport function createTerminalSession(\n\toptions: TerminalSessionOptions = {},\n\tdependencies: TerminalSessionDependencies = {},\n): TerminalSession {\n\treturn new TerminalSession(options, dependencies).start();\n}\n"]}
|