@code-yeongyu/senpi-pty 2026.7.25-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.
Files changed (62) hide show
  1. package/README.md +5 -0
  2. package/dist/index.d.ts +12 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +12 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/loader.d.ts +56 -0
  7. package/dist/loader.d.ts.map +1 -0
  8. package/dist/loader.js +130 -0
  9. package/dist/loader.js.map +1 -0
  10. package/dist/native-loader.d.ts +2 -0
  11. package/dist/native-loader.d.ts.map +1 -0
  12. package/dist/native-loader.js +2 -0
  13. package/dist/native-loader.js.map +1 -0
  14. package/dist/pipe-fallback.d.ts +58 -0
  15. package/dist/pipe-fallback.d.ts.map +1 -0
  16. package/dist/pipe-fallback.js +192 -0
  17. package/dist/pipe-fallback.js.map +1 -0
  18. package/dist/registry-detached.d.ts +5 -0
  19. package/dist/registry-detached.d.ts.map +1 -0
  20. package/dist/registry-detached.js +57 -0
  21. package/dist/registry-detached.js.map +1 -0
  22. package/dist/registry-session.d.ts +7 -0
  23. package/dist/registry-session.d.ts.map +1 -0
  24. package/dist/registry-session.js +73 -0
  25. package/dist/registry-session.js.map +1 -0
  26. package/dist/registry-types.d.ts +94 -0
  27. package/dist/registry-types.d.ts.map +1 -0
  28. package/dist/registry-types.js +9 -0
  29. package/dist/registry-types.js.map +1 -0
  30. package/dist/registry.d.ts +41 -0
  31. package/dist/registry.d.ts.map +1 -0
  32. package/dist/registry.js +232 -0
  33. package/dist/registry.js.map +1 -0
  34. package/dist/screen.d.ts +33 -0
  35. package/dist/screen.d.ts.map +1 -0
  36. package/dist/screen.js +155 -0
  37. package/dist/screen.js.map +1 -0
  38. package/dist/session-exit.d.ts +8 -0
  39. package/dist/session-exit.d.ts.map +1 -0
  40. package/dist/session-exit.js +103 -0
  41. package/dist/session-exit.js.map +1 -0
  42. package/dist/session-native.d.ts +4 -0
  43. package/dist/session-native.d.ts.map +1 -0
  44. package/dist/session-native.js +94 -0
  45. package/dist/session-native.js.map +1 -0
  46. package/dist/session-options.d.ts +10 -0
  47. package/dist/session-options.d.ts.map +1 -0
  48. package/dist/session-options.js +38 -0
  49. package/dist/session-options.js.map +1 -0
  50. package/dist/session-types.d.ts +64 -0
  51. package/dist/session-types.d.ts.map +1 -0
  52. package/dist/session-types.js +2 -0
  53. package/dist/session-types.js.map +1 -0
  54. package/dist/session.d.ts +47 -0
  55. package/dist/session.d.ts.map +1 -0
  56. package/dist/session.js +187 -0
  57. package/dist/session.js.map +1 -0
  58. package/native/check-prebuild-fresh.mjs +171 -0
  59. package/native/index.d.ts +25 -0
  60. package/native/index.js +30 -0
  61. package/native/prebuilds/darwin-arm64/senpi_pty.darwin-arm64.node +0 -0
  62. package/package.json +61 -0
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # @earendil-works/pi-pty
2
+
3
+ Typed loader package for Senpi PTY sessions.
4
+
5
+ The package exposes `PtySession` and a native loader result. `loadPtyNative()` searches the shipped `native/prebuilds/<platform>-<arch>/` package layout and returns the loaded binding when a matching prebuild is present. Unsupported hosts return `native: null` with a `native-unavailable` diagnostic for the attempted host paths.
@@ -0,0 +1,12 @@
1
+ import { type NativePtyLoadResult } from "./native-loader.ts";
2
+ import { TerminalSession as BaseTerminalSession, type TerminalSessionOptions } from "./session.ts";
3
+ export type { NativePtyBinding, NativePtyLoadResult, NativePtyUnavailableDiagnostic, } from "./native-loader.ts";
4
+ export { detectNativePtyRuntime, getNativePtyCandidatePaths, getNativePtyHost, type NativePtyRuntime, NativePtySentinelMismatchError, } from "./native-loader.ts";
5
+ 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
+ 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 PtySessionOptions = TerminalSessionOptions;
9
+ export declare class PtySession extends BaseTerminalSession {
10
+ }
11
+ export declare function loadPtyNative(): NativePtyLoadResult;
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,GAC3B,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,iBAAiB,GAAG,sBAAsB,CAAC;AAEvD,qBAAa,UAAW,SAAQ,mBAAmB;CAAG;AAEtD,wBAAgB,aAAa,IAAI,mBAAmB,CAEnD"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ import { loadNativePty } from "./native-loader.js";
2
+ import { TerminalSession as BaseTerminalSession } from "./session.js";
3
+ export { detectNativePtyRuntime, getNativePtyCandidatePaths, getNativePtyHost, NativePtySentinelMismatchError, } from "./native-loader.js";
4
+ export { isTerminalSessionExited, SessionRegistry, SessionRegistryCapacityError, sessionIdPrefix, } from "./registry.js";
5
+ export { TerminalScreen } from "./screen.js";
6
+ export { createTerminalSession, TerminalSession, } from "./session.js";
7
+ export class PtySession extends BaseTerminalSession {
8
+ }
9
+ export function loadPtyNative() {
10
+ return loadNativePty();
11
+ }
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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,GAWf,MAAM,cAAc,CAAC;AAItB,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 {\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} from \"./session.ts\";\n\nexport type PtySessionOptions = TerminalSessionOptions;\n\nexport class PtySession extends BaseTerminalSession {}\n\nexport function loadPtyNative(): NativePtyLoadResult {\n\treturn loadNativePty();\n}\n"]}
@@ -0,0 +1,56 @@
1
+ export type NativePtyRuntime = "node" | "bun";
2
+ export interface NativePtyBinding {
3
+ readonly PtySession: unknown;
4
+ readonly [exportName: string]: unknown;
5
+ }
6
+ export interface NativePtyUnavailableDiagnostic {
7
+ readonly code: "native-unavailable";
8
+ readonly runtime: NativePtyRuntime;
9
+ readonly host: string;
10
+ readonly attemptedPath: string;
11
+ readonly attemptedPaths: readonly string[];
12
+ readonly message: string;
13
+ readonly cause?: string;
14
+ }
15
+ export type NativePtyLoadResult = {
16
+ readonly native: NativePtyBinding;
17
+ readonly diagnostic: null;
18
+ } | {
19
+ readonly native: null;
20
+ readonly diagnostic: NativePtyUnavailableDiagnostic;
21
+ };
22
+ export declare class NativePtySentinelMismatchError extends Error {
23
+ readonly code = "native-sentinel-mismatch";
24
+ readonly modulePath: string;
25
+ readonly expectedExport: string;
26
+ readonly actualExports: readonly string[];
27
+ constructor(modulePath: string, expectedExport: string, actualExports: readonly string[]);
28
+ }
29
+ export interface NativePtyCandidatePathOptions {
30
+ readonly runtime?: NativePtyRuntime;
31
+ readonly platform?: string;
32
+ readonly arch?: string;
33
+ readonly moduleDir?: string;
34
+ readonly execDir?: string;
35
+ }
36
+ export type NativePtyRequireBinding = (modulePath: string) => unknown;
37
+ export interface NativePtyLoaderOptions extends NativePtyCandidatePathOptions {
38
+ readonly requireBinding?: NativePtyRequireBinding;
39
+ }
40
+ export declare const NATIVE_PTY_PACKAGE_VERSION: string;
41
+ /**
42
+ * Native ABI version. INTENTIONALLY decoupled from the package/CalVer version: it
43
+ * identifies the native surface (exports + signatures) this loader requires. Must
44
+ * match `NATIVE_PTY_ABI_VERSION` and the `__senpiPtyAbi<N>` export in
45
+ * `crates/senpi-pty/src/lib.rs`. Bump ONLY on a backward-incompatible native change
46
+ * (and rebuild the vendored prebuilds); a CalVer release must NOT change it.
47
+ */
48
+ export declare const NATIVE_PTY_ABI_VERSION = "1";
49
+ export declare function getNativePtySentinelExport(abiVersion?: string): string;
50
+ export declare function detectNativePtyRuntime(versions?: NodeJS.ProcessVersions & {
51
+ readonly bun?: unknown;
52
+ }): NativePtyRuntime;
53
+ export declare function getNativePtyHost(platform?: string, arch?: string): string;
54
+ export declare function getNativePtyCandidatePaths(options?: NativePtyCandidatePathOptions): readonly string[];
55
+ export declare function loadNativePty(options?: NativePtyLoaderOptions): NativePtyLoadResult;
56
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,KAAK,CAAC;AAE9C,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;CACvC;AAED,MAAM,WAAW,8BAA8B;IAC9C,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,mBAAmB,GAC5B;IACA,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC;CACzB,GACD;IACA,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,8BAA8B,CAAC;CACnD,CAAC;AAEL,qBAAa,8BAA+B,SAAQ,KAAK;IACxD,QAAQ,CAAC,IAAI,8BAA8B;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAE1C,YAAY,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,MAAM,EAAE,EAQvF;CACD;AAED,MAAM,WAAW,6BAA6B;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,uBAAuB,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;AAEtE,MAAM,WAAW,sBAAuB,SAAQ,6BAA6B;IAC5E,QAAQ,CAAC,cAAc,CAAC,EAAE,uBAAuB,CAAC;CAClD;AAGD,eAAO,MAAM,0BAA0B,QAAuB,CAAC;AAC/D;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAyB1C,wBAAgB,0BAA0B,CAAC,UAAU,GAAE,MAA+B,GAAG,MAAM,CAE9F;AAED,wBAAgB,sBAAsB,CACrC,QAAQ,GAAE,MAAM,CAAC,eAAe,GAAG;IAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAqB,GAC9E,gBAAgB,CAElB;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,GAAE,MAAyB,EAAE,IAAI,GAAE,MAAqB,GAAG,MAAM,CAEzG;AAED,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,6BAAkC,GAAG,SAAS,MAAM,EAAE,CAOzG;AAED,wBAAgB,aAAa,CAAC,OAAO,GAAE,sBAA2B,GAAG,mBAAmB,CAgCvF"}
package/dist/loader.js ADDED
@@ -0,0 +1,130 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ export class NativePtySentinelMismatchError extends Error {
6
+ constructor(modulePath, expectedExport, actualExports) {
7
+ super(`@earendil-works/pi-pty native sentinel mismatch: expected ${expectedExport}() in ${modulePath}, exports=${actualExports.join(",") || "<none>"}`);
8
+ this.code = "native-sentinel-mismatch";
9
+ this.name = "NativePtySentinelMismatchError";
10
+ this.modulePath = modulePath;
11
+ this.expectedExport = expectedExport;
12
+ this.actualExports = actualExports;
13
+ }
14
+ }
15
+ const cjsRequire = createRequire(import.meta.url);
16
+ export const NATIVE_PTY_PACKAGE_VERSION = readPackageVersion();
17
+ /**
18
+ * Native ABI version. INTENTIONALLY decoupled from the package/CalVer version: it
19
+ * identifies the native surface (exports + signatures) this loader requires. Must
20
+ * match `NATIVE_PTY_ABI_VERSION` and the `__senpiPtyAbi<N>` export in
21
+ * `crates/senpi-pty/src/lib.rs`. Bump ONLY on a backward-incompatible native change
22
+ * (and rebuild the vendored prebuilds); a CalVer release must NOT change it.
23
+ */
24
+ export const NATIVE_PTY_ABI_VERSION = "1";
25
+ function readPackageVersion() {
26
+ // node/tsx/tarball layouts: the sibling `../package.json` resolves normally.
27
+ try {
28
+ const packageMetadata = cjsRequire("../package.json");
29
+ if (isRecord(packageMetadata) && typeof packageMetadata.version === "string") {
30
+ return packageMetadata.version;
31
+ }
32
+ }
33
+ catch {
34
+ // Compiled Bun binary: `../package.json` is not in the embedded FS. Fall through.
35
+ }
36
+ // Compiled Bun binary: the release ships package.json beside the executable. All
37
+ // workspace packages are lockstep-versioned, so the sibling version matches pty's.
38
+ try {
39
+ const parsed = JSON.parse(readFileSync(join(dirname(process.execPath), "package.json"), "utf-8"));
40
+ if (isRecord(parsed) && typeof parsed.version === "string") {
41
+ return parsed.version;
42
+ }
43
+ }
44
+ catch {
45
+ // No readable sibling package.json either.
46
+ }
47
+ throw new Error("@earendil-works/pi-pty package.json is missing a string version");
48
+ }
49
+ export function getNativePtySentinelExport(abiVersion = NATIVE_PTY_ABI_VERSION) {
50
+ return `__senpiPtyAbi${abiVersion}`;
51
+ }
52
+ export function detectNativePtyRuntime(versions = process.versions) {
53
+ return typeof versions.bun === "string" ? "bun" : "node";
54
+ }
55
+ export function getNativePtyHost(platform = process.platform, arch = process.arch) {
56
+ return `${platform}-${arch}`;
57
+ }
58
+ export function getNativePtyCandidatePaths(options = {}) {
59
+ const host = getNativePtyHost(options.platform, options.arch);
60
+ const moduleDir = options.moduleDir ?? dirname(fileURLToPath(import.meta.url));
61
+ const execDir = options.execDir ?? dirname(process.execPath);
62
+ const nativePath = join("native", "prebuilds", host, `senpi_pty.${host}.node`);
63
+ return [join(moduleDir, "..", nativePath), join(moduleDir, nativePath), join(execDir, nativePath)];
64
+ }
65
+ export function loadNativePty(options = {}) {
66
+ const runtime = options.runtime ?? detectNativePtyRuntime();
67
+ const host = getNativePtyHost(options.platform, options.arch);
68
+ const attemptedPaths = getNativePtyCandidatePaths({ ...options, runtime });
69
+ const requireBinding = options.requireBinding ?? cjsRequire;
70
+ const causes = [];
71
+ for (const modulePath of attemptedPaths) {
72
+ try {
73
+ const native = requireBinding(modulePath);
74
+ return {
75
+ native: assertNativePtyBinding(native, modulePath),
76
+ diagnostic: null,
77
+ };
78
+ }
79
+ catch (error) {
80
+ if (error instanceof NativePtySentinelMismatchError)
81
+ throw error;
82
+ causes.push(formatErrorCause(modulePath, error));
83
+ }
84
+ }
85
+ return {
86
+ native: null,
87
+ diagnostic: {
88
+ code: "native-unavailable",
89
+ runtime,
90
+ host,
91
+ attemptedPath: attemptedPaths[0] ?? "",
92
+ attemptedPaths,
93
+ message: `No @earendil-works/pi-pty ${runtime} native prebuild is available for ${host}.`,
94
+ cause: causes.join("; "),
95
+ },
96
+ };
97
+ }
98
+ function assertNativePtyBinding(value, modulePath) {
99
+ if (!isNativePtyBinding(value)) {
100
+ throw new NativePtySentinelMismatchError(modulePath, getNativePtySentinelExport(), []);
101
+ }
102
+ const actualExports = Object.keys(value).sort();
103
+ if (typeof value.PtySession !== "function") {
104
+ throw new NativePtySentinelMismatchError(modulePath, "PtySession", actualExports);
105
+ }
106
+ const sentinelExport = getNativePtySentinelExport();
107
+ const sentinel = value[sentinelExport];
108
+ if (!isUnknownFunction(sentinel)) {
109
+ throw new NativePtySentinelMismatchError(modulePath, sentinelExport, actualExports);
110
+ }
111
+ const sentinelValue = sentinel();
112
+ if (sentinelValue !== NATIVE_PTY_ABI_VERSION) {
113
+ throw new NativePtySentinelMismatchError(modulePath, sentinelExport, actualExports);
114
+ }
115
+ return value;
116
+ }
117
+ function isRecord(value) {
118
+ return typeof value === "object" && value !== null;
119
+ }
120
+ function isNativePtyBinding(value) {
121
+ return isRecord(value) && typeof value.PtySession === "function";
122
+ }
123
+ function isUnknownFunction(value) {
124
+ return typeof value === "function";
125
+ }
126
+ function formatErrorCause(modulePath, error) {
127
+ const message = error instanceof Error ? error.message : String(error);
128
+ return `${modulePath}: ${message}`;
129
+ }
130
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport type NativePtyRuntime = \"node\" | \"bun\";\n\nexport interface NativePtyBinding {\n\treadonly PtySession: unknown;\n\treadonly [exportName: string]: unknown;\n}\n\nexport interface NativePtyUnavailableDiagnostic {\n\treadonly code: \"native-unavailable\";\n\treadonly runtime: NativePtyRuntime;\n\treadonly host: string;\n\treadonly attemptedPath: string;\n\treadonly attemptedPaths: readonly string[];\n\treadonly message: string;\n\treadonly cause?: string;\n}\n\nexport type NativePtyLoadResult =\n\t| {\n\t\t\treadonly native: NativePtyBinding;\n\t\t\treadonly diagnostic: null;\n\t }\n\t| {\n\t\t\treadonly native: null;\n\t\t\treadonly diagnostic: NativePtyUnavailableDiagnostic;\n\t };\n\nexport class NativePtySentinelMismatchError extends Error {\n\treadonly code = \"native-sentinel-mismatch\";\n\treadonly modulePath: string;\n\treadonly expectedExport: string;\n\treadonly actualExports: readonly string[];\n\n\tconstructor(modulePath: string, expectedExport: string, actualExports: readonly string[]) {\n\t\tsuper(\n\t\t\t`@earendil-works/pi-pty native sentinel mismatch: expected ${expectedExport}() in ${modulePath}, exports=${actualExports.join(\",\") || \"<none>\"}`,\n\t\t);\n\t\tthis.name = \"NativePtySentinelMismatchError\";\n\t\tthis.modulePath = modulePath;\n\t\tthis.expectedExport = expectedExport;\n\t\tthis.actualExports = actualExports;\n\t}\n}\n\nexport interface NativePtyCandidatePathOptions {\n\treadonly runtime?: NativePtyRuntime;\n\treadonly platform?: string;\n\treadonly arch?: string;\n\treadonly moduleDir?: string;\n\treadonly execDir?: string;\n}\n\nexport type NativePtyRequireBinding = (modulePath: string) => unknown;\n\nexport interface NativePtyLoaderOptions extends NativePtyCandidatePathOptions {\n\treadonly requireBinding?: NativePtyRequireBinding;\n}\n\nconst cjsRequire = createRequire(import.meta.url);\nexport const NATIVE_PTY_PACKAGE_VERSION = readPackageVersion();\n/**\n * Native ABI version. INTENTIONALLY decoupled from the package/CalVer version: it\n * identifies the native surface (exports + signatures) this loader requires. Must\n * match `NATIVE_PTY_ABI_VERSION` and the `__senpiPtyAbi<N>` export in\n * `crates/senpi-pty/src/lib.rs`. Bump ONLY on a backward-incompatible native change\n * (and rebuild the vendored prebuilds); a CalVer release must NOT change it.\n */\nexport const NATIVE_PTY_ABI_VERSION = \"1\";\n\nfunction readPackageVersion(): string {\n\t// node/tsx/tarball layouts: the sibling `../package.json` resolves normally.\n\ttry {\n\t\tconst packageMetadata = cjsRequire(\"../package.json\");\n\t\tif (isRecord(packageMetadata) && typeof packageMetadata.version === \"string\") {\n\t\t\treturn packageMetadata.version;\n\t\t}\n\t} catch {\n\t\t// Compiled Bun binary: `../package.json` is not in the embedded FS. Fall through.\n\t}\n\t// Compiled Bun binary: the release ships package.json beside the executable. All\n\t// workspace packages are lockstep-versioned, so the sibling version matches pty's.\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(readFileSync(join(dirname(process.execPath), \"package.json\"), \"utf-8\"));\n\t\tif (isRecord(parsed) && typeof parsed.version === \"string\") {\n\t\t\treturn parsed.version;\n\t\t}\n\t} catch {\n\t\t// No readable sibling package.json either.\n\t}\n\tthrow new Error(\"@earendil-works/pi-pty package.json is missing a string version\");\n}\n\nexport function getNativePtySentinelExport(abiVersion: string = NATIVE_PTY_ABI_VERSION): string {\n\treturn `__senpiPtyAbi${abiVersion}`;\n}\n\nexport function detectNativePtyRuntime(\n\tversions: NodeJS.ProcessVersions & { readonly bun?: unknown } = process.versions,\n): NativePtyRuntime {\n\treturn typeof versions.bun === \"string\" ? \"bun\" : \"node\";\n}\n\nexport function getNativePtyHost(platform: string = process.platform, arch: string = process.arch): string {\n\treturn `${platform}-${arch}`;\n}\n\nexport function getNativePtyCandidatePaths(options: NativePtyCandidatePathOptions = {}): readonly string[] {\n\tconst host = getNativePtyHost(options.platform, options.arch);\n\tconst moduleDir = options.moduleDir ?? dirname(fileURLToPath(import.meta.url));\n\tconst execDir = options.execDir ?? dirname(process.execPath);\n\tconst nativePath = join(\"native\", \"prebuilds\", host, `senpi_pty.${host}.node`);\n\n\treturn [join(moduleDir, \"..\", nativePath), join(moduleDir, nativePath), join(execDir, nativePath)];\n}\n\nexport function loadNativePty(options: NativePtyLoaderOptions = {}): NativePtyLoadResult {\n\tconst runtime = options.runtime ?? detectNativePtyRuntime();\n\tconst host = getNativePtyHost(options.platform, options.arch);\n\tconst attemptedPaths = getNativePtyCandidatePaths({ ...options, runtime });\n\tconst requireBinding = options.requireBinding ?? cjsRequire;\n\tconst causes: string[] = [];\n\n\tfor (const modulePath of attemptedPaths) {\n\t\ttry {\n\t\t\tconst native = requireBinding(modulePath);\n\t\t\treturn {\n\t\t\t\tnative: assertNativePtyBinding(native, modulePath),\n\t\t\t\tdiagnostic: null,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (error instanceof NativePtySentinelMismatchError) throw error;\n\t\t\tcauses.push(formatErrorCause(modulePath, error));\n\t\t}\n\t}\n\n\treturn {\n\t\tnative: null,\n\t\tdiagnostic: {\n\t\t\tcode: \"native-unavailable\",\n\t\t\truntime,\n\t\t\thost,\n\t\t\tattemptedPath: attemptedPaths[0] ?? \"\",\n\t\t\tattemptedPaths,\n\t\t\tmessage: `No @earendil-works/pi-pty ${runtime} native prebuild is available for ${host}.`,\n\t\t\tcause: causes.join(\"; \"),\n\t\t},\n\t};\n}\n\nfunction assertNativePtyBinding(value: unknown, modulePath: string): NativePtyBinding {\n\tif (!isNativePtyBinding(value)) {\n\t\tthrow new NativePtySentinelMismatchError(modulePath, getNativePtySentinelExport(), []);\n\t}\n\n\tconst actualExports = Object.keys(value).sort();\n\tif (typeof value.PtySession !== \"function\") {\n\t\tthrow new NativePtySentinelMismatchError(modulePath, \"PtySession\", actualExports);\n\t}\n\n\tconst sentinelExport = getNativePtySentinelExport();\n\tconst sentinel = value[sentinelExport];\n\tif (!isUnknownFunction(sentinel)) {\n\t\tthrow new NativePtySentinelMismatchError(modulePath, sentinelExport, actualExports);\n\t}\n\n\tconst sentinelValue = sentinel();\n\tif (sentinelValue !== NATIVE_PTY_ABI_VERSION) {\n\t\tthrow new NativePtySentinelMismatchError(modulePath, sentinelExport, actualExports);\n\t}\n\n\treturn value;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction isNativePtyBinding(value: unknown): value is NativePtyBinding {\n\treturn isRecord(value) && typeof value.PtySession === \"function\";\n}\n\nfunction isUnknownFunction(value: unknown): value is () => unknown {\n\treturn typeof value === \"function\";\n}\n\nfunction formatErrorCause(modulePath: string, error: unknown): string {\n\tconst message = error instanceof Error ? error.message : String(error);\n\treturn `${modulePath}: ${message}`;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export { detectNativePtyRuntime, getNativePtyCandidatePaths, getNativePtyHost, loadNativePty, type NativePtyBinding, type NativePtyCandidatePathOptions, type NativePtyLoaderOptions, type NativePtyLoadResult, type NativePtyRequireBinding, type NativePtyRuntime, NativePtySentinelMismatchError, type NativePtyUnavailableDiagnostic, } from "./loader.ts";
2
+ //# sourceMappingURL=native-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-loader.d.ts","sourceRoot":"","sources":["../src/native-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,sBAAsB,EACtB,0BAA0B,EAC1B,gBAAgB,EAChB,aAAa,EACb,KAAK,gBAAgB,EACrB,KAAK,6BAA6B,EAClC,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACrB,8BAA8B,EAC9B,KAAK,8BAA8B,GACnC,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { detectNativePtyRuntime, getNativePtyCandidatePaths, getNativePtyHost, loadNativePty, NativePtySentinelMismatchError, } from "./loader.js";
2
+ //# sourceMappingURL=native-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-loader.js","sourceRoot":"","sources":["../src/native-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,sBAAsB,EACtB,0BAA0B,EAC1B,gBAAgB,EAChB,aAAa,EAOb,8BAA8B,GAE9B,MAAM,aAAa,CAAC","sourcesContent":["export {\n\tdetectNativePtyRuntime,\n\tgetNativePtyCandidatePaths,\n\tgetNativePtyHost,\n\tloadNativePty,\n\ttype NativePtyBinding,\n\ttype NativePtyCandidatePathOptions,\n\ttype NativePtyLoaderOptions,\n\ttype NativePtyLoadResult,\n\ttype NativePtyRequireBinding,\n\ttype NativePtyRuntime,\n\tNativePtySentinelMismatchError,\n\ttype NativePtyUnavailableDiagnostic,\n} from \"./loader.ts\";\n"]}
@@ -0,0 +1,58 @@
1
+ import type { NativePtyLoadResult } from "./native-loader.ts";
2
+ export interface PipeFallbackSessionOptions {
3
+ readonly command: string;
4
+ readonly args?: readonly string[];
5
+ readonly cwd?: string;
6
+ readonly env?: Readonly<Record<string, string | undefined>>;
7
+ readonly timeoutMs?: number;
8
+ }
9
+ export interface PipeFallbackSessionError {
10
+ readonly code: "spawn_error";
11
+ readonly message: string;
12
+ }
13
+ export interface PipeFallbackSessionExit {
14
+ readonly exitCode: number | null;
15
+ readonly signal: NodeJS.Signals | null;
16
+ readonly timedOut: boolean;
17
+ readonly error?: PipeFallbackSessionError;
18
+ }
19
+ export type PipeFallbackOperationResult = {
20
+ readonly ok: true;
21
+ readonly note: string;
22
+ } | {
23
+ readonly ok: false;
24
+ readonly code: "exited" | "not_pty" | "not_started" | "stdin_unavailable";
25
+ readonly note: string;
26
+ };
27
+ type DataHandler = (chunk: Buffer) => void;
28
+ type ExitHandler = (exit: PipeFallbackSessionExit) => void;
29
+ export declare function isPipeFallbackForced(env?: Readonly<Record<string, string | undefined>>): boolean;
30
+ export declare function shouldUsePipeFallback(nativeLoadResult: NativePtyLoadResult, env?: Readonly<Record<string, string | undefined>>): boolean;
31
+ export declare class PipeFallbackSession {
32
+ readonly backend = "pipe-fallback";
33
+ readonly note = "Running with child_process pipe fallback because no PTY backend is active; terminal screen state and resize are unavailable.";
34
+ readonly options: PipeFallbackSessionOptions;
35
+ private child;
36
+ private exitResult;
37
+ private exitPromise;
38
+ private resolveExit;
39
+ private spawnError;
40
+ private timedOut;
41
+ private timeoutHandle;
42
+ private readonly dataHandlers;
43
+ private readonly exitHandlers;
44
+ constructor(options: PipeFallbackSessionOptions);
45
+ start(): this;
46
+ onData(handler: DataHandler): () => void;
47
+ onExit(handler: ExitHandler): () => void;
48
+ write(data: string | Uint8Array): PipeFallbackOperationResult;
49
+ closeInput(): PipeFallbackOperationResult;
50
+ resize(cols: number, rows: number): PipeFallbackOperationResult;
51
+ kill(signal?: NodeJS.Signals): PipeFallbackOperationResult;
52
+ waitExit(): Promise<PipeFallbackSessionExit>;
53
+ private emitData;
54
+ private settleExit;
55
+ }
56
+ export declare function createPipeFallbackSession(options: PipeFallbackSessionOptions): PipeFallbackSession;
57
+ export {};
58
+ //# sourceMappingURL=pipe-fallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipe-fallback.d.ts","sourceRoot":"","sources":["../src/pipe-fallback.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,192 @@
1
+ import { spawn } from "node:child_process";
2
+ const PIPE_FALLBACK_NOTE = "Running with child_process pipe fallback because no PTY backend is active; terminal screen state and resize are unavailable.";
3
+ export function isPipeFallbackForced(env = process.env) {
4
+ const value = env.SENPI_PTY_FORCE_PIPE;
5
+ return value === "1" || value === "true";
6
+ }
7
+ export function shouldUsePipeFallback(nativeLoadResult, env = process.env) {
8
+ return isPipeFallbackForced(env) || nativeLoadResult.native === null;
9
+ }
10
+ function normalizeSpawnError(command, error) {
11
+ return {
12
+ code: "spawn_error",
13
+ message: `Failed to spawn pipe fallback command "${command}": ${error.message}`,
14
+ };
15
+ }
16
+ function toBuffer(chunk) {
17
+ return Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
18
+ }
19
+ function validateTimeout(timeoutMs) {
20
+ if (timeoutMs === undefined)
21
+ return;
22
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
23
+ throw new Error("Invalid timeoutMs: must be a finite positive number");
24
+ }
25
+ }
26
+ export class PipeFallbackSession {
27
+ constructor(options) {
28
+ this.backend = "pipe-fallback";
29
+ this.note = PIPE_FALLBACK_NOTE;
30
+ this.child = null;
31
+ this.exitResult = null;
32
+ this.resolveExit = null;
33
+ this.timedOut = false;
34
+ this.dataHandlers = new Set();
35
+ this.exitHandlers = new Set();
36
+ this.options = {
37
+ ...options,
38
+ args: options.args ? [...options.args] : undefined,
39
+ env: options.env ? { ...options.env } : undefined,
40
+ };
41
+ this.exitPromise = new Promise((resolve) => {
42
+ this.resolveExit = resolve;
43
+ });
44
+ }
45
+ start() {
46
+ if (this.exitResult !== null)
47
+ throw new Error("Cannot restart exited pipe fallback session");
48
+ if (this.child !== null)
49
+ throw new Error("Pipe fallback session has already been started");
50
+ validateTimeout(this.options.timeoutMs);
51
+ try {
52
+ const child = spawn(this.options.command, [...(this.options.args ?? [])], {
53
+ cwd: this.options.cwd,
54
+ env: this.options.env ? { ...process.env, ...this.options.env } : process.env,
55
+ stdio: ["pipe", "pipe", "pipe"],
56
+ windowsHide: true,
57
+ });
58
+ this.child = child;
59
+ child.stdout?.on("data", (chunk) => this.emitData(toBuffer(chunk)));
60
+ child.stderr?.on("data", (chunk) => this.emitData(toBuffer(chunk)));
61
+ child.stdin?.on("error", () => { });
62
+ child.on("error", (error) => {
63
+ this.spawnError = normalizeSpawnError(this.options.command, error);
64
+ });
65
+ child.on("close", (exitCode, signal) => {
66
+ this.settleExit({
67
+ exitCode: this.spawnError ? null : exitCode,
68
+ signal,
69
+ timedOut: this.timedOut,
70
+ error: this.spawnError,
71
+ });
72
+ });
73
+ if (this.options.timeoutMs !== undefined) {
74
+ this.timeoutHandle = setTimeout(() => {
75
+ this.timedOut = true;
76
+ child.kill("SIGTERM");
77
+ }, this.options.timeoutMs);
78
+ }
79
+ }
80
+ catch (error) {
81
+ const cause = error instanceof Error ? error : new Error(String(error));
82
+ this.settleExit({
83
+ exitCode: null,
84
+ signal: null,
85
+ timedOut: false,
86
+ error: normalizeSpawnError(this.options.command, cause),
87
+ });
88
+ }
89
+ return this;
90
+ }
91
+ onData(handler) {
92
+ this.dataHandlers.add(handler);
93
+ return () => this.dataHandlers.delete(handler);
94
+ }
95
+ onExit(handler) {
96
+ const exitResult = this.exitResult;
97
+ if (exitResult !== null)
98
+ queueMicrotask(() => handler(exitResult));
99
+ else
100
+ this.exitHandlers.add(handler);
101
+ return () => this.exitHandlers.delete(handler);
102
+ }
103
+ write(data) {
104
+ if (this.exitResult !== null) {
105
+ return { ok: false, code: "exited", note: "Cannot write to pipe fallback stdin: session has exited." };
106
+ }
107
+ if (this.child === null) {
108
+ return {
109
+ ok: false,
110
+ code: "not_started",
111
+ note: "Cannot write to pipe fallback stdin: session has not started.",
112
+ };
113
+ }
114
+ if (!this.child.stdin || this.child.stdin.destroyed || this.child.stdin.writableEnded) {
115
+ return {
116
+ ok: false,
117
+ code: "stdin_unavailable",
118
+ note: "Cannot write to pipe fallback stdin: child stdin is unavailable.",
119
+ };
120
+ }
121
+ const accepted = this.child.stdin.write(data);
122
+ return {
123
+ ok: true,
124
+ note: accepted
125
+ ? "Wrote stdin through child_process pipe fallback; this is not a PTY."
126
+ : "Queued stdin through child_process pipe fallback with stream backpressure; this is not a PTY.",
127
+ };
128
+ }
129
+ closeInput() {
130
+ if (this.exitResult !== null) {
131
+ return { ok: false, code: "exited", note: "Cannot close pipe fallback stdin: session has exited." };
132
+ }
133
+ if (this.child === null) {
134
+ return { ok: false, code: "not_started", note: "Cannot close pipe fallback stdin: session has not started." };
135
+ }
136
+ this.child.stdin?.end();
137
+ return { ok: true, note: "Closed child_process pipe fallback stdin." };
138
+ }
139
+ resize(cols, rows) {
140
+ return {
141
+ ok: false,
142
+ code: "not_pty",
143
+ note: `Cannot resize pipe fallback session to ${cols}x${rows}: child_process pipes are not a PTY; session is still valid.`,
144
+ };
145
+ }
146
+ kill(signal = "SIGTERM") {
147
+ if (this.exitResult !== null) {
148
+ return { ok: false, code: "exited", note: "Cannot kill pipe fallback session: session has exited." };
149
+ }
150
+ if (this.child === null) {
151
+ return { ok: false, code: "not_started", note: "Cannot kill pipe fallback session: session has not started." };
152
+ }
153
+ const pid = this.child.pid;
154
+ if (process.platform === "win32" && pid !== undefined) {
155
+ // On Windows child.kill() TerminateProcess-es only the direct child, leaving
156
+ // grandchildren (e.g. `sleep` under `bash.exe -c`) alive and holding the stdout
157
+ // pipe open — so 'close' never fires and waitExit() hangs teardown. taskkill /T /F
158
+ // terminates the whole tree so the pipe EOFs and the session settles.
159
+ try {
160
+ spawn("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
161
+ return { ok: true, note: `Terminated child_process pipe fallback process tree (pid ${pid}).` };
162
+ }
163
+ catch {
164
+ // Fall through to the direct kill if taskkill is unavailable.
165
+ }
166
+ }
167
+ this.child.kill(signal);
168
+ return { ok: true, note: `Sent ${signal} to child_process pipe fallback session.` };
169
+ }
170
+ waitExit() {
171
+ return this.exitPromise;
172
+ }
173
+ emitData(chunk) {
174
+ for (const handler of this.dataHandlers)
175
+ handler(chunk);
176
+ }
177
+ settleExit(exit) {
178
+ if (this.exitResult !== null)
179
+ return;
180
+ if (this.timeoutHandle)
181
+ clearTimeout(this.timeoutHandle);
182
+ this.exitResult = exit;
183
+ this.resolveExit?.(exit);
184
+ for (const handler of this.exitHandlers)
185
+ handler(exit);
186
+ this.exitHandlers.clear();
187
+ }
188
+ }
189
+ export function createPipeFallbackSession(options) {
190
+ return new PipeFallbackSession(options).start();
191
+ }
192
+ //# sourceMappingURL=pipe-fallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipe-fallback.js","sourceRoot":"","sources":["../src/pipe-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAsC3C,MAAM,kBAAkB,GACvB,8HAA8H,CAAC;AAEhI,MAAM,UAAU,oBAAoB,CAAC,GAAG,GAAiD,OAAO,CAAC,GAAG;IACnG,MAAM,KAAK,GAAG,GAAG,CAAC,oBAAoB,CAAC;IACvC,OAAO,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,MAAM,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,qBAAqB,CACpC,gBAAqC,EACrC,GAAG,GAAiD,OAAO,CAAC,GAAG;IAE/D,OAAO,oBAAoB,CAAC,GAAG,CAAC,IAAI,gBAAgB,CAAC,MAAM,KAAK,IAAI,CAAC;AACtE,CAAC;AAED,SAAS,mBAAmB,CAAC,OAAe,EAAE,KAAY;IACzD,OAAO;QACN,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,0CAA0C,OAAO,MAAM,KAAK,CAAC,OAAO,EAAE;KAC/E,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAsB;IACvC,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,eAAe,CAAC,SAA6B;IACrD,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO;IACpC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACxE,CAAC;AACF,CAAC;AAED,MAAM,OAAO,mBAAmB;IAc/B,YAAY,OAAmC;QAbtC,YAAO,GAAG,eAAe,CAAC;QAC1B,SAAI,GAAG,kBAAkB,CAAC;QAE3B,UAAK,GAA8B,IAAI,CAAC;QACxC,eAAU,GAAmC,IAAI,CAAC;QAElD,gBAAW,GAAqD,IAAI,CAAC;QAErE,aAAQ,GAAG,KAAK,CAAC;QAER,iBAAY,GAAG,IAAI,GAAG,EAAe,CAAC;QACtC,iBAAY,GAAG,IAAI,GAAG,EAAe,CAAC;QAGtD,IAAI,CAAC,OAAO,GAAG;YACd,GAAG,OAAO;YACV,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;YAClD,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS;SACjD,CAAC;QACF,IAAI,CAAC,WAAW,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC1C,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;QAC5B,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK;QACJ,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QAC7F,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QAC3F,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAExC,IAAI,CAAC;YACJ,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,EAAE;gBACzE,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG;gBACrB,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG;gBAC7E,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;gBAC/B,WAAW,EAAE,IAAI;aACjB,CAAC,CAAC;YACH,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;YACnB,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAsB,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACrF,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAsB,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACrF,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACnC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE;gBAClC,IAAI,CAAC,UAAU,GAAG,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACpE,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;gBACtC,IAAI,CAAC,UAAU,CAAC;oBACf,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ;oBAC3C,MAAM;oBACN,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,KAAK,EAAE,IAAI,CAAC,UAAU;iBACtB,CAAC,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC1C,IAAI,CAAC,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;oBACpC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;oBACrB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACvB,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAC5B,CAAC;QACF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,KAAK,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACxE,IAAI,CAAC,UAAU,CAAC;gBACf,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,IAAI;gBACZ,QAAQ,EAAE,KAAK;gBACf,KAAK,EAAE,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC;aACvD,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;IAED,MAAM,CAAC,OAAoB;QAC1B,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,CAAC,OAAoB;QAC1B,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QACnC,IAAI,UAAU,KAAK,IAAI;YAAE,cAAc,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;;YAC9D,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACpC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,IAAyB;QAC9B,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAC9B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,0DAA0D,EAAE,CAAC;QACxG,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACzB,OAAO;gBACN,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,+DAA+D;aACrE,CAAC;QACH,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;YACvF,OAAO;gBACN,EAAE,EAAE,KAAK;gBACT,IAAI,EAAE,mBAAmB;gBACzB,IAAI,EAAE,kEAAkE;aACxE,CAAC;QACH,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9C,OAAO;YACN,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,QAAQ;gBACb,CAAC,CAAC,qEAAqE;gBACvE,CAAC,CAAC,+FAA+F;SAClG,CAAC;IACH,CAAC;IAED,UAAU;QACT,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAC9B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,uDAAuD,EAAE,CAAC;QACrG,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACzB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,4DAA4D,EAAE,CAAC;QAC/G,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;QACxB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,2CAA2C,EAAE,CAAC;IACxE,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,IAAY;QAChC,OAAO;YACN,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,0CAA0C,IAAI,IAAI,IAAI,8DAA8D;SAC1H,CAAC;IACH,CAAC;IAED,IAAI,CAAC,MAAM,GAAmB,SAAS;QACtC,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAC9B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,wDAAwD,EAAE,CAAC;QACtG,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACzB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,6DAA6D,EAAE,CAAC;QAChH,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;QAC3B,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACvD,6EAA6E;YAC7E,gFAAgF;YAChF,mFAAmF;YACnF,sEAAsE;YACtE,IAAI,CAAC;gBACJ,KAAK,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC7F,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,4DAA4D,GAAG,IAAI,EAAE,CAAC;YAChG,CAAC;YAAC,MAAM,CAAC;gBACR,8DAA8D;YAC/D,CAAC;QACF,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,MAAM,0CAA0C,EAAE,CAAC;IACrF,CAAC;IAED,QAAQ;QACP,OAAO,IAAI,CAAC,WAAW,CAAC;IACzB,CAAC;IAEO,QAAQ,CAAC,KAAa;QAC7B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IACzD,CAAC;IAEO,UAAU,CAAC,IAA6B;QAC/C,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI;YAAE,OAAO;QACrC,IAAI,IAAI,CAAC,aAAa;YAAE,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACzD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC;QACzB,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;CACD;AAED,MAAM,UAAU,yBAAyB,CAAC,OAAmC;IAC5E,OAAO,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC;AACjD,CAAC","sourcesContent":["import { spawn } from \"node:child_process\";\nimport type { NativePtyLoadResult } from \"./native-loader.ts\";\n\nexport interface PipeFallbackSessionOptions {\n\treadonly command: string;\n\treadonly args?: readonly string[];\n\treadonly cwd?: string;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n\treadonly timeoutMs?: number;\n}\n\nexport interface PipeFallbackSessionError {\n\treadonly code: \"spawn_error\";\n\treadonly message: string;\n}\n\nexport interface PipeFallbackSessionExit {\n\treadonly exitCode: number | null;\n\treadonly signal: NodeJS.Signals | null;\n\treadonly timedOut: boolean;\n\treadonly error?: PipeFallbackSessionError;\n}\n\nexport type PipeFallbackOperationResult =\n\t| {\n\t\t\treadonly ok: true;\n\t\t\treadonly note: string;\n\t }\n\t| {\n\t\t\treadonly ok: false;\n\t\t\treadonly code: \"exited\" | \"not_pty\" | \"not_started\" | \"stdin_unavailable\";\n\t\t\treadonly note: string;\n\t };\n\ntype ChildProcessHandle = ReturnType<typeof spawn>;\ntype DataHandler = (chunk: Buffer) => void;\ntype ExitHandler = (exit: PipeFallbackSessionExit) => void;\n\nconst PIPE_FALLBACK_NOTE =\n\t\"Running with child_process pipe fallback because no PTY backend is active; terminal screen state and resize are unavailable.\";\n\nexport function isPipeFallbackForced(env: Readonly<Record<string, string | undefined>> = process.env): boolean {\n\tconst value = env.SENPI_PTY_FORCE_PIPE;\n\treturn value === \"1\" || value === \"true\";\n}\n\nexport function shouldUsePipeFallback(\n\tnativeLoadResult: NativePtyLoadResult,\n\tenv: Readonly<Record<string, string | undefined>> = process.env,\n): boolean {\n\treturn isPipeFallbackForced(env) || nativeLoadResult.native === null;\n}\n\nfunction normalizeSpawnError(command: string, error: Error): PipeFallbackSessionError {\n\treturn {\n\t\tcode: \"spawn_error\",\n\t\tmessage: `Failed to spawn pipe fallback command \"${command}\": ${error.message}`,\n\t};\n}\n\nfunction toBuffer(chunk: string | Buffer): Buffer {\n\treturn Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n}\n\nfunction validateTimeout(timeoutMs: number | undefined): void {\n\tif (timeoutMs === undefined) return;\n\tif (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n\t\tthrow new Error(\"Invalid timeoutMs: must be a finite positive number\");\n\t}\n}\n\nexport class PipeFallbackSession {\n\treadonly backend = \"pipe-fallback\";\n\treadonly note = PIPE_FALLBACK_NOTE;\n\treadonly options: PipeFallbackSessionOptions;\n\tprivate child: ChildProcessHandle | null = null;\n\tprivate exitResult: PipeFallbackSessionExit | null = null;\n\tprivate exitPromise: Promise<PipeFallbackSessionExit>;\n\tprivate resolveExit: ((exit: PipeFallbackSessionExit) => void) | null = null;\n\tprivate spawnError: PipeFallbackSessionError | undefined;\n\tprivate timedOut = false;\n\tprivate timeoutHandle: NodeJS.Timeout | undefined;\n\tprivate readonly dataHandlers = new Set<DataHandler>();\n\tprivate readonly exitHandlers = new Set<ExitHandler>();\n\n\tconstructor(options: PipeFallbackSessionOptions) {\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.exitPromise = new Promise((resolve) => {\n\t\t\tthis.resolveExit = resolve;\n\t\t});\n\t}\n\n\tstart(): this {\n\t\tif (this.exitResult !== null) throw new Error(\"Cannot restart exited pipe fallback session\");\n\t\tif (this.child !== null) throw new Error(\"Pipe fallback session has already been started\");\n\t\tvalidateTimeout(this.options.timeoutMs);\n\n\t\ttry {\n\t\t\tconst child = spawn(this.options.command, [...(this.options.args ?? [])], {\n\t\t\t\tcwd: this.options.cwd,\n\t\t\t\tenv: this.options.env ? { ...process.env, ...this.options.env } : process.env,\n\t\t\t\tstdio: [\"pipe\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tthis.child = child;\n\t\t\tchild.stdout?.on(\"data\", (chunk: string | Buffer) => this.emitData(toBuffer(chunk)));\n\t\t\tchild.stderr?.on(\"data\", (chunk: string | Buffer) => this.emitData(toBuffer(chunk)));\n\t\t\tchild.stdin?.on(\"error\", () => {});\n\t\t\tchild.on(\"error\", (error: Error) => {\n\t\t\t\tthis.spawnError = normalizeSpawnError(this.options.command, error);\n\t\t\t});\n\t\t\tchild.on(\"close\", (exitCode, signal) => {\n\t\t\t\tthis.settleExit({\n\t\t\t\t\texitCode: this.spawnError ? null : exitCode,\n\t\t\t\t\tsignal,\n\t\t\t\t\ttimedOut: this.timedOut,\n\t\t\t\t\terror: this.spawnError,\n\t\t\t\t});\n\t\t\t});\n\t\t\tif (this.options.timeoutMs !== undefined) {\n\t\t\t\tthis.timeoutHandle = setTimeout(() => {\n\t\t\t\t\tthis.timedOut = true;\n\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t}, this.options.timeoutMs);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : new Error(String(error));\n\t\t\tthis.settleExit({\n\t\t\t\texitCode: null,\n\t\t\t\tsignal: null,\n\t\t\t\ttimedOut: false,\n\t\t\t\terror: normalizeSpawnError(this.options.command, cause),\n\t\t\t});\n\t\t}\n\t\treturn this;\n\t}\n\n\tonData(handler: DataHandler): () => void {\n\t\tthis.dataHandlers.add(handler);\n\t\treturn () => this.dataHandlers.delete(handler);\n\t}\n\n\tonExit(handler: ExitHandler): () => void {\n\t\tconst exitResult = this.exitResult;\n\t\tif (exitResult !== null) queueMicrotask(() => handler(exitResult));\n\t\telse this.exitHandlers.add(handler);\n\t\treturn () => this.exitHandlers.delete(handler);\n\t}\n\n\twrite(data: string | Uint8Array): PipeFallbackOperationResult {\n\t\tif (this.exitResult !== null) {\n\t\t\treturn { ok: false, code: \"exited\", note: \"Cannot write to pipe fallback stdin: session has exited.\" };\n\t\t}\n\t\tif (this.child === null) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tcode: \"not_started\",\n\t\t\t\tnote: \"Cannot write to pipe fallback stdin: session has not started.\",\n\t\t\t};\n\t\t}\n\t\tif (!this.child.stdin || this.child.stdin.destroyed || this.child.stdin.writableEnded) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tcode: \"stdin_unavailable\",\n\t\t\t\tnote: \"Cannot write to pipe fallback stdin: child stdin is unavailable.\",\n\t\t\t};\n\t\t}\n\t\tconst accepted = this.child.stdin.write(data);\n\t\treturn {\n\t\t\tok: true,\n\t\t\tnote: accepted\n\t\t\t\t? \"Wrote stdin through child_process pipe fallback; this is not a PTY.\"\n\t\t\t\t: \"Queued stdin through child_process pipe fallback with stream backpressure; this is not a PTY.\",\n\t\t};\n\t}\n\n\tcloseInput(): PipeFallbackOperationResult {\n\t\tif (this.exitResult !== null) {\n\t\t\treturn { ok: false, code: \"exited\", note: \"Cannot close pipe fallback stdin: session has exited.\" };\n\t\t}\n\t\tif (this.child === null) {\n\t\t\treturn { ok: false, code: \"not_started\", note: \"Cannot close pipe fallback stdin: session has not started.\" };\n\t\t}\n\t\tthis.child.stdin?.end();\n\t\treturn { ok: true, note: \"Closed child_process pipe fallback stdin.\" };\n\t}\n\n\tresize(cols: number, rows: number): PipeFallbackOperationResult {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tcode: \"not_pty\",\n\t\t\tnote: `Cannot resize pipe fallback session to ${cols}x${rows}: child_process pipes are not a PTY; session is still valid.`,\n\t\t};\n\t}\n\n\tkill(signal: NodeJS.Signals = \"SIGTERM\"): PipeFallbackOperationResult {\n\t\tif (this.exitResult !== null) {\n\t\t\treturn { ok: false, code: \"exited\", note: \"Cannot kill pipe fallback session: session has exited.\" };\n\t\t}\n\t\tif (this.child === null) {\n\t\t\treturn { ok: false, code: \"not_started\", note: \"Cannot kill pipe fallback session: session has not started.\" };\n\t\t}\n\t\tconst pid = this.child.pid;\n\t\tif (process.platform === \"win32\" && pid !== undefined) {\n\t\t\t// On Windows child.kill() TerminateProcess-es only the direct child, leaving\n\t\t\t// grandchildren (e.g. `sleep` under `bash.exe -c`) alive and holding the stdout\n\t\t\t// pipe open — so 'close' never fires and waitExit() hangs teardown. taskkill /T /F\n\t\t\t// terminates the whole tree so the pipe EOFs and the session settles.\n\t\t\ttry {\n\t\t\t\tspawn(\"taskkill\", [\"/pid\", String(pid), \"/T\", \"/F\"], { stdio: \"ignore\", windowsHide: true });\n\t\t\t\treturn { ok: true, note: `Terminated child_process pipe fallback process tree (pid ${pid}).` };\n\t\t\t} catch {\n\t\t\t\t// Fall through to the direct kill if taskkill is unavailable.\n\t\t\t}\n\t\t}\n\t\tthis.child.kill(signal);\n\t\treturn { ok: true, note: `Sent ${signal} to child_process pipe fallback session.` };\n\t}\n\n\twaitExit(): Promise<PipeFallbackSessionExit> {\n\t\treturn this.exitPromise;\n\t}\n\n\tprivate emitData(chunk: Buffer): void {\n\t\tfor (const handler of this.dataHandlers) handler(chunk);\n\t}\n\n\tprivate settleExit(exit: PipeFallbackSessionExit): void {\n\t\tif (this.exitResult !== null) return;\n\t\tif (this.timeoutHandle) clearTimeout(this.timeoutHandle);\n\t\tthis.exitResult = exit;\n\t\tthis.resolveExit?.(exit);\n\t\tfor (const handler of this.exitHandlers) handler(exit);\n\t\tthis.exitHandlers.clear();\n\t}\n}\n\nexport function createPipeFallbackSession(options: PipeFallbackSessionOptions): PipeFallbackSession {\n\treturn new PipeFallbackSession(options).start();\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import type { SessionRegistrySession, TerminalSessionSignal } from "./registry-types.ts";
2
+ export declare function getRuntimePlatform(): string;
3
+ export declare function defaultKillProcess(target: number, signal: TerminalSessionSignal): void;
4
+ export declare function cleanupDetachedChildren(session: SessionRegistrySession, platform: string, killProcess: (target: number, signal: TerminalSessionSignal) => void): Promise<void>;
5
+ //# sourceMappingURL=registry-detached.d.ts.map
@@ -0,0 +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,GAClE,OAAO,CAAC,IAAI,CAAC,CAef"}