@code-yeongyu/senpi 2026.8.10 → 2026.8.11-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 (203) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/brand-dir-migration.d.ts +28 -0
  3. package/dist/brand-dir-migration.d.ts.map +1 -0
  4. package/dist/brand-dir-migration.js +77 -0
  5. package/dist/brand-dir-migration.js.map +1 -0
  6. package/dist/cli/grok-neo-gate.d.ts +0 -8
  7. package/dist/cli/grok-neo-gate.d.ts.map +1 -1
  8. package/dist/cli/grok-neo-gate.js +4 -2
  9. package/dist/cli/grok-neo-gate.js.map +1 -1
  10. package/dist/cli-main.js +3 -0
  11. package/dist/cli-main.js.map +1 -1
  12. package/dist/cli.js +2 -2
  13. package/dist/cli.js.map +1 -1
  14. package/dist/config.d.ts +27 -1
  15. package/dist/config.d.ts.map +1 -1
  16. package/dist/config.js +41 -9
  17. package/dist/config.js.map +1 -1
  18. package/dist/core/agent-session.d.ts.map +1 -1
  19. package/dist/core/agent-session.js +2 -1
  20. package/dist/core/agent-session.js.map +1 -1
  21. package/dist/core/brand.d.ts +79 -0
  22. package/dist/core/brand.d.ts.map +1 -0
  23. package/dist/core/brand.js +141 -0
  24. package/dist/core/brand.js.map +1 -0
  25. package/dist/core/dynamic-prompt/identity.d.ts.map +1 -1
  26. package/dist/core/dynamic-prompt/identity.js +2 -1
  27. package/dist/core/dynamic-prompt/identity.js.map +1 -1
  28. package/dist/core/experimental.d.ts.map +1 -1
  29. package/dist/core/experimental.js +2 -1
  30. package/dist/core/experimental.js.map +1 -1
  31. package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.d.ts.map +1 -1
  32. package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.js +10 -1
  33. package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.js.map +1 -1
  34. package/dist/core/extensions/builtin/compaction/log.d.ts.map +1 -1
  35. package/dist/core/extensions/builtin/compaction/log.js +2 -1
  36. package/dist/core/extensions/builtin/compaction/log.js.map +1 -1
  37. package/dist/core/extensions/builtin/config-reload/generated-shim-filter.d.ts.map +1 -1
  38. package/dist/core/extensions/builtin/config-reload/generated-shim-filter.js +4 -4
  39. package/dist/core/extensions/builtin/config-reload/generated-shim-filter.js.map +1 -1
  40. package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
  41. package/dist/core/extensions/builtin/goal/index.js +5 -0
  42. package/dist/core/extensions/builtin/goal/index.js.map +1 -1
  43. package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts +2 -2
  44. package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts.map +1 -1
  45. package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +3 -3
  46. package/dist/core/extensions/builtin/goal/lifecycle-helpers.js.map +1 -1
  47. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
  48. package/dist/core/extensions/builtin/goal/monitor-continuation.js +6 -0
  49. package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
  50. package/dist/core/extensions/builtin/goal/store.d.ts +3 -2
  51. package/dist/core/extensions/builtin/goal/store.d.ts.map +1 -1
  52. package/dist/core/extensions/builtin/goal/store.js +128 -101
  53. package/dist/core/extensions/builtin/goal/store.js.map +1 -1
  54. package/dist/core/extensions/builtin/help/index.d.ts.map +1 -1
  55. package/dist/core/extensions/builtin/help/index.js +3 -2
  56. package/dist/core/extensions/builtin/help/index.js.map +1 -1
  57. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.d.ts.map +1 -1
  58. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js +2 -1
  59. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js.map +1 -1
  60. package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.d.ts.map +1 -1
  61. package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.js +2 -1
  62. package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.js.map +1 -1
  63. package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.d.ts.map +1 -1
  64. package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.js +2 -1
  65. package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.js.map +1 -1
  66. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts.map +1 -1
  67. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +2 -1
  68. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -1
  69. package/dist/core/extensions/builtin/prompt-preset/grok-4.5.d.ts.map +1 -1
  70. package/dist/core/extensions/builtin/prompt-preset/grok-4.5.js +6 -5
  71. package/dist/core/extensions/builtin/prompt-preset/grok-4.5.js.map +1 -1
  72. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.d.ts.map +1 -1
  73. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js +2 -1
  74. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js.map +1 -1
  75. package/dist/core/footer-data-provider.d.ts +1 -4
  76. package/dist/core/footer-data-provider.d.ts.map +1 -1
  77. package/dist/core/footer-data-provider.js +0 -7
  78. package/dist/core/footer-data-provider.js.map +1 -1
  79. package/dist/core/generated-shim-banner.d.ts +15 -0
  80. package/dist/core/generated-shim-banner.d.ts.map +1 -0
  81. package/dist/core/generated-shim-banner.js +26 -0
  82. package/dist/core/generated-shim-banner.js.map +1 -0
  83. package/dist/core/model-runtime.d.ts.map +1 -1
  84. package/dist/core/model-runtime.js +7 -3
  85. package/dist/core/model-runtime.js.map +1 -1
  86. package/dist/core/package-manager.d.ts.map +1 -1
  87. package/dist/core/package-manager.js +2 -1
  88. package/dist/core/package-manager.js.map +1 -1
  89. package/dist/core/resource-loader.d.ts.map +1 -1
  90. package/dist/core/resource-loader.js +3 -5
  91. package/dist/core/resource-loader.js.map +1 -1
  92. package/dist/core/session-log.d.ts.map +1 -1
  93. package/dist/core/session-log.js +4 -2
  94. package/dist/core/session-log.js.map +1 -1
  95. package/dist/core/settings-manager.d.ts.map +1 -1
  96. package/dist/core/settings-manager.js +3 -2
  97. package/dist/core/settings-manager.js.map +1 -1
  98. package/dist/core/telemetry.d.ts.map +1 -1
  99. package/dist/core/telemetry.js +2 -1
  100. package/dist/core/telemetry.js.map +1 -1
  101. package/dist/core/timings.d.ts +0 -4
  102. package/dist/core/timings.d.ts.map +1 -1
  103. package/dist/core/timings.js +2 -1
  104. package/dist/core/timings.js.map +1 -1
  105. package/dist/inspector-policy.d.ts.map +1 -1
  106. package/dist/inspector-policy.js +2 -1
  107. package/dist/inspector-policy.js.map +1 -1
  108. package/dist/main.d.ts.map +1 -1
  109. package/dist/main.js +5 -4
  110. package/dist/main.js.map +1 -1
  111. package/dist/migrations.d.ts.map +1 -1
  112. package/dist/migrations.js +4 -0
  113. package/dist/migrations.js.map +1 -1
  114. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  115. package/dist/modes/interactive/components/footer.js +1 -8
  116. package/dist/modes/interactive/components/footer.js.map +1 -1
  117. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  118. package/dist/modes/interactive/interactive-mode.js +6 -7
  119. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  120. package/dist/modes/interactive/tips/catalog/cli-tips.d.ts.map +1 -1
  121. package/dist/modes/interactive/tips/catalog/cli-tips.js +2 -1
  122. package/dist/modes/interactive/tips/catalog/cli-tips.js.map +1 -1
  123. package/dist/modes/interactive/tips/catalog/ethos-tips.d.ts.map +1 -1
  124. package/dist/modes/interactive/tips/catalog/ethos-tips.js +4 -3
  125. package/dist/modes/interactive/tips/catalog/ethos-tips.js.map +1 -1
  126. package/dist/modes/interactive/tips/catalog/model-tips.d.ts.map +1 -1
  127. package/dist/modes/interactive/tips/catalog/model-tips.js +2 -1
  128. package/dist/modes/interactive/tips/catalog/model-tips.js.map +1 -1
  129. package/dist/modes/interactive/tips/catalog/session-tips.d.ts.map +1 -1
  130. package/dist/modes/interactive/tips/catalog/session-tips.js +2 -1
  131. package/dist/modes/interactive/tips/catalog/session-tips.js.map +1 -1
  132. package/dist/modes/interactive/tips/catalog/settings-tips.d.ts.map +1 -1
  133. package/dist/modes/interactive/tips/catalog/settings-tips.js +3 -2
  134. package/dist/modes/interactive/tips/catalog/settings-tips.js.map +1 -1
  135. package/dist/modes/interactive/tips/catalog/workspace-tips.d.ts.map +1 -1
  136. package/dist/modes/interactive/tips/catalog/workspace-tips.js +4 -3
  137. package/dist/modes/interactive/tips/catalog/workspace-tips.js.map +1 -1
  138. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  139. package/dist/modes/rpc/rpc-mode.js +2 -1
  140. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  141. package/dist/nearest-parent-config.d.ts +6 -1
  142. package/dist/nearest-parent-config.d.ts.map +1 -1
  143. package/dist/nearest-parent-config.js +18 -2
  144. package/dist/nearest-parent-config.js.map +1 -1
  145. package/dist/package-manager-cli.d.ts.map +1 -1
  146. package/dist/package-manager-cli.js +13 -1
  147. package/dist/package-manager-cli.js.map +1 -1
  148. package/dist/rpc-entry.js +2 -0
  149. package/dist/rpc-entry.js.map +1 -1
  150. package/dist/self-update-bootstrap.d.ts.map +1 -1
  151. package/dist/self-update-bootstrap.js +6 -0
  152. package/dist/self-update-bootstrap.js.map +1 -1
  153. package/dist/senpi +2 -2
  154. package/dist/utils/pi-user-agent.d.ts +1 -1
  155. package/dist/utils/pi-user-agent.d.ts.map +1 -1
  156. package/dist/utils/pi-user-agent.js +4 -3
  157. package/dist/utils/pi-user-agent.js.map +1 -1
  158. package/dist/utils/tools-manager.d.ts.map +1 -1
  159. package/dist/utils/tools-manager.js +2 -1
  160. package/dist/utils/tools-manager.js.map +1 -1
  161. package/dist/utils/version-check.d.ts +3 -0
  162. package/dist/utils/version-check.d.ts.map +1 -1
  163. package/dist/utils/version-check.js +37 -6
  164. package/dist/utils/version-check.js.map +1 -1
  165. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +24 -0
  166. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  167. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  168. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts.map +1 -1
  169. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +4 -2
  170. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js.map +1 -1
  171. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/openai-codex.d.ts.map +1 -1
  172. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/openai-codex.js +2 -1
  173. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/openai-codex.js.map +1 -1
  174. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
  175. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  176. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
  177. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  178. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  179. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  180. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  181. package/node_modules/@earendil-works/pi-ai/dist/wire-identity.d.ts +14 -0
  182. package/node_modules/@earendil-works/pi-ai/dist/wire-identity.d.ts.map +1 -0
  183. package/node_modules/@earendil-works/pi-ai/dist/wire-identity.js +24 -0
  184. package/node_modules/@earendil-works/pi-ai/dist/wire-identity.js.map +1 -0
  185. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  186. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.d.ts.map +1 -1
  187. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.js +11 -3
  188. package/node_modules/@earendil-works/pi-pty/dist/pipe-fallback.js.map +1 -1
  189. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  190. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  191. package/package.json +6 -6
  192. package/dist/core/omo-native-detect.d.ts +0 -3
  193. package/dist/core/omo-native-detect.d.ts.map +0 -1
  194. package/dist/core/omo-native-detect.js +0 -64
  195. package/dist/core/omo-native-detect.js.map +0 -1
  196. package/dist/core/omo-native-detect.test.d.ts +0 -2
  197. package/dist/core/omo-native-detect.test.d.ts.map +0 -1
  198. package/dist/core/omo-native-detect.test.js +0 -47
  199. package/dist/core/omo-native-detect.test.js.map +0 -1
  200. package/dist/modes/interactive/components/footer-omo-native-badge.test.d.ts +0 -2
  201. package/dist/modes/interactive/components/footer-omo-native-badge.test.d.ts.map +0 -1
  202. package/dist/modes/interactive/components/footer-omo-native-badge.test.js +0 -52
  203. package/dist/modes/interactive/components/footer-omo-native-badge.test.js.map +0 -1
@@ -180,18 +180,26 @@ export class PipeFallbackSession {
180
180
  if (this.child === null) {
181
181
  return { ok: false, code: "not_started", note: "Cannot kill pipe fallback session: session has not started." };
182
182
  }
183
- const pid = this.child.pid;
183
+ const child = this.child;
184
+ const pid = child.pid;
184
185
  if (process.platform === "win32" && pid !== undefined) {
185
186
  // On Windows child.kill() TerminateProcess-es only the direct child, leaving
186
187
  // grandchildren (e.g. `sleep` under `bash.exe -c`) alive and holding the stdout
187
188
  // pipe open — so 'close' never fires and waitExit() hangs teardown. taskkill /T /F
188
189
  // terminates the whole tree so the pipe EOFs and the session settles.
189
190
  try {
190
- spawn("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
191
+ const killer = spawn("taskkill.exe", ["/pid", String(pid), "/T", "/F"], {
192
+ stdio: "ignore",
193
+ windowsHide: true,
194
+ });
195
+ killer.once("error", () => {
196
+ terminateChildTree(child, signal);
197
+ });
191
198
  return { ok: true, note: `Terminated child_process pipe fallback process tree (pid ${pid}).` };
192
199
  }
193
200
  catch {
194
- // Fall through to the direct kill if taskkill is unavailable.
201
+ terminateChildTree(child, signal);
202
+ return { ok: true, note: `Sent ${signal} to child_process pipe fallback session.` };
195
203
  }
196
204
  }
197
205
  if (terminateChildTree(this.child, signal) === "group") {
@@ -1 +1 @@
1
- {"version":3,"file":"pipe-fallback.js","sourceRoot":"","sources":["../src/pipe-fallback.ts"],"names":[],"mappings":"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","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\n/**\n * Terminate the child, preferring its whole process group on POSIX. The\n * detached child is its own group leader and grandchildren inherit the stdio\n * pipes, so leaving them alive keeps 'close' from ever firing (the POSIX twin\n * of the Windows taskkill /T branch below). Every path that stops a child must\n * go through here: when the group signal is unavailable (Windows, no pid) or\n * the group is already gone, it still falls back to a direct kill, so the\n * child is never left running.\n */\nexport function terminateChildTree(\n\tchild: Pick<ChildProcessHandle, \"pid\" | \"kill\">,\n\tsignal: NodeJS.Signals,\n): \"group\" | \"direct\" {\n\tconst pid = child.pid;\n\tif (pid !== undefined && process.platform !== \"win32\") {\n\t\ttry {\n\t\t\tprocess.kill(-pid, signal);\n\t\t\treturn \"group\";\n\t\t} catch {\n\t\t\t// Group already gone — fall through to the direct kill.\n\t\t}\n\t}\n\tchild.kill(signal);\n\treturn \"direct\";\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\t// Detach on POSIX so the child leads its own session with no\n\t\t\t\t// controlling terminal. Without this, a tty-reading program (e.g. a\n\t\t\t\t// sudo password prompt) opens /dev/tty — the user's real terminal —\n\t\t\t\t// and races the TUI's raw-mode stdin reader byte-by-byte, splitting\n\t\t\t\t// keystrokes and Kitty escape sequences between the two readers.\n\t\t\t\t// Detached, the /dev/tty open fails with a clear error instead.\n\t\t\t\tdetached: process.platform !== \"win32\",\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\tterminateChildTree(child, \"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\tif (terminateChildTree(this.child, signal) === \"group\") {\n\t\t\treturn { ok: true, note: `Sent ${signal} to child_process pipe fallback process group (pgid ${pid}).` };\n\t\t}\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"]}
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;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CACjC,KAA+C,EAC/C,MAAsB;IAEtB,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;IACtB,IAAI,GAAG,KAAK,SAAS,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACvD,IAAI,CAAC;YACJ,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC3B,OAAO,OAAO,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACR,wDAAwD;QACzD,CAAC;IACF,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnB,OAAO,QAAQ,CAAC;AACjB,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,6DAA6D;gBAC7D,oEAAoE;gBACpE,oEAAoE;gBACpE,oEAAoE;gBACpE,iEAAiE;gBACjE,gEAAgE;gBAChE,QAAQ,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO;gBACtC,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,kBAAkB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;gBACtC,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,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;QACtB,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACvD,6EAA6E;YAC7E,gFAAgF;YAChF,mFAAmF;YACnF,sEAAsE;YACtE,IAAI,CAAC;gBACJ,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE;oBACvE,KAAK,EAAE,QAAQ;oBACf,WAAW,EAAE,IAAI;iBACjB,CAAC,CAAC;gBACH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;oBACzB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;gBACnC,CAAC,CAAC,CAAC;gBACH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,4DAA4D,GAAG,IAAI,EAAE,CAAC;YAChG,CAAC;YAAC,MAAM,CAAC;gBACR,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;gBAClC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,MAAM,0CAA0C,EAAE,CAAC;YACrF,CAAC;QACF,CAAC;QACD,IAAI,kBAAkB,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,OAAO,EAAE,CAAC;YACxD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,MAAM,uDAAuD,GAAG,IAAI,EAAE,CAAC;QACzG,CAAC;QACD,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\n/**\n * Terminate the child, preferring its whole process group on POSIX. The\n * detached child is its own group leader and grandchildren inherit the stdio\n * pipes, so leaving them alive keeps 'close' from ever firing (the POSIX twin\n * of the Windows taskkill /T branch below). Every path that stops a child must\n * go through here: when the group signal is unavailable (Windows, no pid) or\n * the group is already gone, it still falls back to a direct kill, so the\n * child is never left running.\n */\nexport function terminateChildTree(\n\tchild: Pick<ChildProcessHandle, \"pid\" | \"kill\">,\n\tsignal: NodeJS.Signals,\n): \"group\" | \"direct\" {\n\tconst pid = child.pid;\n\tif (pid !== undefined && process.platform !== \"win32\") {\n\t\ttry {\n\t\t\tprocess.kill(-pid, signal);\n\t\t\treturn \"group\";\n\t\t} catch {\n\t\t\t// Group already gone — fall through to the direct kill.\n\t\t}\n\t}\n\tchild.kill(signal);\n\treturn \"direct\";\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\t// Detach on POSIX so the child leads its own session with no\n\t\t\t\t// controlling terminal. Without this, a tty-reading program (e.g. a\n\t\t\t\t// sudo password prompt) opens /dev/tty — the user's real terminal —\n\t\t\t\t// and races the TUI's raw-mode stdin reader byte-by-byte, splitting\n\t\t\t\t// keystrokes and Kitty escape sequences between the two readers.\n\t\t\t\t// Detached, the /dev/tty open fails with a clear error instead.\n\t\t\t\tdetached: process.platform !== \"win32\",\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\tterminateChildTree(child, \"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 child = this.child;\n\t\tconst pid = 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\tconst killer = spawn(\"taskkill.exe\", [\"/pid\", String(pid), \"/T\", \"/F\"], {\n\t\t\t\t\tstdio: \"ignore\",\n\t\t\t\t\twindowsHide: true,\n\t\t\t\t});\n\t\t\t\tkiller.once(\"error\", () => {\n\t\t\t\t\tterminateChildTree(child, signal);\n\t\t\t\t});\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\tterminateChildTree(child, signal);\n\t\t\t\treturn { ok: true, note: `Sent ${signal} to child_process pipe fallback session.` };\n\t\t\t}\n\t\t}\n\t\tif (terminateChildTree(this.child, signal) === \"group\") {\n\t\t\treturn { ok: true, note: `Sent ${signal} to child_process pipe fallback process group (pgid ${pid}).` };\n\t\t}\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"]}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-pty",
3
3
  "private": true,
4
- "version": "2026.8.10",
4
+ "version": "2026.8.11-2",
5
5
  "description": "Typed PTY session loader for Senpi persistent terminals",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-tui",
3
3
  "private": true,
4
- "version": "2026.8.10",
4
+ "version": "2026.8.11-2",
5
5
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi",
3
- "version": "2026.8.10",
3
+ "version": "2026.8.11-2",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -48,11 +48,11 @@
48
48
  "dependencies": {
49
49
  "@anthropic-ai/sdk": "0.91.1",
50
50
  "@aws-sdk/client-bedrock-runtime": "3.1095.0",
51
- "@code-yeongyu/senpi-codemode": "2026.8.10",
52
- "@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.8.10",
53
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.8.10",
54
- "@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.8.10",
55
- "@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.8.10",
51
+ "@code-yeongyu/senpi-codemode": "2026.8.11-2",
52
+ "@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.8.11-2",
53
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.8.11-2",
54
+ "@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.8.11-2",
55
+ "@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.8.11-2",
56
56
  "@mistralai/mistralai": "2.5.0",
57
57
  "@modelcontextprotocol/sdk": "1.30.0",
58
58
  "@mozilla/readability": "0.6.0",
@@ -1,3 +0,0 @@
1
- import type { PackageSource } from "./settings-manager.ts";
2
- export declare function detectOmoNativeInstall(packages: PackageSource[] | undefined, agentDir: string, env?: Record<string, string | undefined>): boolean;
3
- //# sourceMappingURL=omo-native-detect.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"omo-native-detect.d.ts","sourceRoot":"","sources":["../../src/core/omo-native-detect.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AA+B3D,wBAAgB,sBAAsB,CACrC,QAAQ,EAAE,aAAa,EAAE,GAAG,SAAS,EACrC,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACnD,OAAO,CA+BT"}
@@ -1,64 +0,0 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { homedir } from "node:os";
3
- import { join, resolve } from "node:path";
4
- import { isLocalPath, resolvePath } from "../utils/paths.js";
5
- /**
6
- * Synchronous, dependency-free detection of the "OMO Native" local install: a settings
7
- * `packages` entry that is a local path resolving to a dir whose package.json name is
8
- * `@code-yeongyu/omo-senpi`, whose derived repo root (pluginPath/../../..) contains both
9
- * workspace packages `@oh-my-opencode/omo-senpi` and `@oh-my-opencode/senpi-task`.
10
- *
11
- * This mirrors gates 1 and 2 of the beta `detectOmoLocalInstall` (beta/omo-local-update.ts)
12
- * but drops gate 3 (the `git rev-parse --show-toplevel` integrity check) so it stays sync and
13
- * cheap enough for footer rendering. The beta module's export policy forbids importing its
14
- * helpers into production core, so this isolated module is the footer's own detection.
15
- */
16
- const OMO_SENPI_PLUGIN_PACKAGE = "@code-yeongyu/omo-senpi";
17
- const OMO_SENPI_WORKSPACE_PACKAGE = "@oh-my-opencode/omo-senpi";
18
- const SENPI_TASK_WORKSPACE_PACKAGE = "@oh-my-opencode/senpi-task";
19
- function homeDir() {
20
- return process.env.HOME || homedir();
21
- }
22
- function readPackageName(packageJsonPath) {
23
- try {
24
- const json = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
25
- const name = json.name;
26
- return typeof name === "string" ? name : undefined;
27
- }
28
- catch {
29
- return undefined;
30
- }
31
- }
32
- export function detectOmoNativeInstall(packages, agentDir, env = process.env) {
33
- // The omo launcher marks every senpi spawn it owns; omo-ai global installs load the plugin via
34
- // --extension, so no settings.packages entry ever exists for them and the env marker is the only
35
- // signal available at footer-render time.
36
- if (env.OMO_NATIVE === "1") {
37
- return true;
38
- }
39
- if (!packages) {
40
- return false;
41
- }
42
- for (const entry of packages) {
43
- const source = typeof entry === "string" ? entry : entry.source;
44
- if (!isLocalPath(source)) {
45
- continue;
46
- }
47
- const pluginPath = resolvePath(source, agentDir, { homeDir: homeDir(), trim: true });
48
- if (readPackageName(join(pluginPath, "package.json")) !== OMO_SENPI_PLUGIN_PACKAGE) {
49
- continue;
50
- }
51
- const repoRoot = resolve(pluginPath, "..", "..", "..");
52
- const omoSenpiPkg = join(repoRoot, "packages", "omo-senpi", "package.json");
53
- if (!existsSync(omoSenpiPkg) || readPackageName(omoSenpiPkg) !== OMO_SENPI_WORKSPACE_PACKAGE) {
54
- continue;
55
- }
56
- const senpiTaskPkg = join(repoRoot, "packages", "senpi-task", "package.json");
57
- if (!existsSync(senpiTaskPkg) || readPackageName(senpiTaskPkg) !== SENPI_TASK_WORKSPACE_PACKAGE) {
58
- continue;
59
- }
60
- return true;
61
- }
62
- return false;
63
- }
64
- //# sourceMappingURL=omo-native-detect.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"omo-native-detect.js","sourceRoot":"","sources":["../../src/core/omo-native-detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG7D;;;;;;;;;;GAUG;AACH,MAAM,wBAAwB,GAAG,yBAAyB,CAAC;AAC3D,MAAM,2BAA2B,GAAG,2BAA2B,CAAC;AAChE,MAAM,4BAA4B,GAAG,4BAA4B,CAAC;AAElE,SAAS,OAAO;IACf,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC;AACtC,CAAC;AAED,SAAS,eAAe,CAAC,eAAuB;IAC/C,IAAI,CAAC;QACJ,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAuB,CAAC;QACtF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AACF,CAAC;AAED,MAAM,UAAU,sBAAsB,CACrC,QAAqC,EACrC,QAAgB,EAChB,GAAG,GAAuC,OAAO,CAAC,GAAG;IAErD,+FAA+F;IAC/F,iGAAiG;IACjG,0CAA0C;IAC1C,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACb,CAAC;IACD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,OAAO,KAAK,CAAC;IACd,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;QAChE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,SAAS;QACV,CAAC;QACD,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACrF,IAAI,eAAe,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,KAAK,wBAAwB,EAAE,CAAC;YACpF,SAAS;QACV,CAAC;QACD,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACvD,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC;QAC5E,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,eAAe,CAAC,WAAW,CAAC,KAAK,2BAA2B,EAAE,CAAC;YAC9F,SAAS;QACV,CAAC;QACD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;QAC9E,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,eAAe,CAAC,YAAY,CAAC,KAAK,4BAA4B,EAAE,CAAC;YACjG,SAAS;QACV,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { isLocalPath, resolvePath } from \"../utils/paths.ts\";\nimport type { PackageSource } from \"./settings-manager.ts\";\n\n/**\n * Synchronous, dependency-free detection of the \"OMO Native\" local install: a settings\n * `packages` entry that is a local path resolving to a dir whose package.json name is\n * `@code-yeongyu/omo-senpi`, whose derived repo root (pluginPath/../../..) contains both\n * workspace packages `@oh-my-opencode/omo-senpi` and `@oh-my-opencode/senpi-task`.\n *\n * This mirrors gates 1 and 2 of the beta `detectOmoLocalInstall` (beta/omo-local-update.ts)\n * but drops gate 3 (the `git rev-parse --show-toplevel` integrity check) so it stays sync and\n * cheap enough for footer rendering. The beta module's export policy forbids importing its\n * helpers into production core, so this isolated module is the footer's own detection.\n */\nconst OMO_SENPI_PLUGIN_PACKAGE = \"@code-yeongyu/omo-senpi\";\nconst OMO_SENPI_WORKSPACE_PACKAGE = \"@oh-my-opencode/omo-senpi\";\nconst SENPI_TASK_WORKSPACE_PACKAGE = \"@oh-my-opencode/senpi-task\";\n\nfunction homeDir(): string {\n\treturn process.env.HOME || homedir();\n}\n\nfunction readPackageName(packageJsonPath: string): string | undefined {\n\ttry {\n\t\tconst json = JSON.parse(readFileSync(packageJsonPath, \"utf-8\")) as { name?: unknown };\n\t\tconst name = json.name;\n\t\treturn typeof name === \"string\" ? name : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function detectOmoNativeInstall(\n\tpackages: PackageSource[] | undefined,\n\tagentDir: string,\n\tenv: Record<string, string | undefined> = process.env,\n): boolean {\n\t// The omo launcher marks every senpi spawn it owns; omo-ai global installs load the plugin via\n\t// --extension, so no settings.packages entry ever exists for them and the env marker is the only\n\t// signal available at footer-render time.\n\tif (env.OMO_NATIVE === \"1\") {\n\t\treturn true;\n\t}\n\tif (!packages) {\n\t\treturn false;\n\t}\n\tfor (const entry of packages) {\n\t\tconst source = typeof entry === \"string\" ? entry : entry.source;\n\t\tif (!isLocalPath(source)) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst pluginPath = resolvePath(source, agentDir, { homeDir: homeDir(), trim: true });\n\t\tif (readPackageName(join(pluginPath, \"package.json\")) !== OMO_SENPI_PLUGIN_PACKAGE) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst repoRoot = resolve(pluginPath, \"..\", \"..\", \"..\");\n\t\tconst omoSenpiPkg = join(repoRoot, \"packages\", \"omo-senpi\", \"package.json\");\n\t\tif (!existsSync(omoSenpiPkg) || readPackageName(omoSenpiPkg) !== OMO_SENPI_WORKSPACE_PACKAGE) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst senpiTaskPkg = join(repoRoot, \"packages\", \"senpi-task\", \"package.json\");\n\t\tif (!existsSync(senpiTaskPkg) || readPackageName(senpiTaskPkg) !== SENPI_TASK_WORKSPACE_PACKAGE) {\n\t\t\tcontinue;\n\t\t}\n\t\treturn true;\n\t}\n\treturn false;\n}\n"]}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=omo-native-detect.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"omo-native-detect.test.d.ts","sourceRoot":"","sources":["../../src/core/omo-native-detect.test.ts"],"names":[],"mappings":""}
@@ -1,47 +0,0 @@
1
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
- import { afterEach, describe, expect, it } from "vitest";
5
- import { detectOmoNativeInstall } from "./omo-native-detect.js";
6
- // The omo-ai global install loads the plugin through the launcher's --extension flag, never through a
7
- // settings.packages entry, so detection must also honor the launcher-set OMO_NATIVE env marker or the
8
- // footer badge can never render for shipped installs.
9
- const roots = [];
10
- function omoRepoFixture() {
11
- const root = mkdtempSync(join(tmpdir(), "omo-detect-"));
12
- roots.push(root);
13
- const pluginPath = join(root, "packages", "omo-senpi", "plugin");
14
- mkdirSync(pluginPath, { recursive: true });
15
- writeFileSync(join(pluginPath, "package.json"), JSON.stringify({ name: "@code-yeongyu/omo-senpi" }));
16
- writeFileSync(join(root, "packages", "omo-senpi", "package.json"), JSON.stringify({ name: "@oh-my-opencode/omo-senpi" }));
17
- mkdirSync(join(root, "packages", "senpi-task"), { recursive: true });
18
- writeFileSync(join(root, "packages", "senpi-task", "package.json"), JSON.stringify({ name: "@oh-my-opencode/senpi-task" }));
19
- return { pluginPath };
20
- }
21
- afterEach(() => {
22
- for (const root of roots.splice(0))
23
- rmSync(root, { recursive: true, force: true });
24
- });
25
- describe("detectOmoNativeInstall", () => {
26
- describe("#given the launcher-set OMO_NATIVE env marker", () => {
27
- it("#when packages carry no omo entry #then detection still fires", () => {
28
- expect(detectOmoNativeInstall([], "/tmp/agent", { OMO_NATIVE: "1" })).toBe(true);
29
- });
30
- it("#when the marker holds any other value #then detection ignores it", () => {
31
- expect(detectOmoNativeInstall([], "/tmp/agent", { OMO_NATIVE: "0" })).toBe(false);
32
- expect(detectOmoNativeInstall([], "/tmp/agent", {})).toBe(false);
33
- });
34
- });
35
- describe("#given a settings.packages local-path omo plugin entry", () => {
36
- it("#when the repo gates all pass #then detection fires without the env marker", () => {
37
- const { pluginPath } = omoRepoFixture();
38
- expect(detectOmoNativeInstall([pluginPath], "/tmp/agent", {})).toBe(true);
39
- });
40
- });
41
- describe("#given neither the marker nor a matching package entry", () => {
42
- it("#when detection runs #then it stays false", () => {
43
- expect(detectOmoNativeInstall(undefined, "/tmp/agent", {})).toBe(false);
44
- });
45
- });
46
- });
47
- //# sourceMappingURL=omo-native-detect.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"omo-native-detect.test.js","sourceRoot":"","sources":["../../src/core/omo-native-detect.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { detectOmoNativeInstall } from \"./omo-native-detect.ts\";\n\n// The omo-ai global install loads the plugin through the launcher's --extension flag, never through a\n// settings.packages entry, so detection must also honor the launcher-set OMO_NATIVE env marker or the\n// footer badge can never render for shipped installs.\n\nconst roots: string[] = [];\n\nfunction omoRepoFixture(): { pluginPath: string } {\n\tconst root = mkdtempSync(join(tmpdir(), \"omo-detect-\"));\n\troots.push(root);\n\tconst pluginPath = join(root, \"packages\", \"omo-senpi\", \"plugin\");\n\tmkdirSync(pluginPath, { recursive: true });\n\twriteFileSync(join(pluginPath, \"package.json\"), JSON.stringify({ name: \"@code-yeongyu/omo-senpi\" }));\n\twriteFileSync(\n\t\tjoin(root, \"packages\", \"omo-senpi\", \"package.json\"),\n\t\tJSON.stringify({ name: \"@oh-my-opencode/omo-senpi\" }),\n\t);\n\tmkdirSync(join(root, \"packages\", \"senpi-task\"), { recursive: true });\n\twriteFileSync(\n\t\tjoin(root, \"packages\", \"senpi-task\", \"package.json\"),\n\t\tJSON.stringify({ name: \"@oh-my-opencode/senpi-task\" }),\n\t);\n\treturn { pluginPath };\n}\n\nafterEach(() => {\n\tfor (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });\n});\n\ndescribe(\"detectOmoNativeInstall\", () => {\n\tdescribe(\"#given the launcher-set OMO_NATIVE env marker\", () => {\n\t\tit(\"#when packages carry no omo entry #then detection still fires\", () => {\n\t\t\texpect(detectOmoNativeInstall([], \"/tmp/agent\", { OMO_NATIVE: \"1\" })).toBe(true);\n\t\t});\n\n\t\tit(\"#when the marker holds any other value #then detection ignores it\", () => {\n\t\t\texpect(detectOmoNativeInstall([], \"/tmp/agent\", { OMO_NATIVE: \"0\" })).toBe(false);\n\t\t\texpect(detectOmoNativeInstall([], \"/tmp/agent\", {})).toBe(false);\n\t\t});\n\t});\n\n\tdescribe(\"#given a settings.packages local-path omo plugin entry\", () => {\n\t\tit(\"#when the repo gates all pass #then detection fires without the env marker\", () => {\n\t\t\tconst { pluginPath } = omoRepoFixture();\n\t\t\texpect(detectOmoNativeInstall([pluginPath], \"/tmp/agent\", {})).toBe(true);\n\t\t});\n\t});\n\n\tdescribe(\"#given neither the marker nor a matching package entry\", () => {\n\t\tit(\"#when detection runs #then it stays false\", () => {\n\t\t\texpect(detectOmoNativeInstall(undefined, \"/tmp/agent\", {})).toBe(false);\n\t\t});\n\t});\n});\n"]}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=footer-omo-native-badge.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"footer-omo-native-badge.test.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer-omo-native-badge.test.ts"],"names":[],"mappings":""}
@@ -1,52 +0,0 @@
1
- import { beforeAll, describe, expect, it } from "vitest";
2
- import { initTheme } from "../theme/theme.js";
3
- import { FooterComponent } from "./footer.js";
4
- // The badge is the only visible marker that a session is running the omo native distribution, so it is
5
- // pinned here at the render() boundary: emoji, wording, and bottom-left anchoring all matter to users.
6
- const OMO_NATIVE_BADGE = "(😺 OmO Native)";
7
- function footerData(isOmoNative) {
8
- return {
9
- getGitBranch: () => undefined,
10
- getExtensionStatuses: () => [],
11
- getAvailableProviderCount: () => 1,
12
- onBranchChange: () => () => { },
13
- isOmoNative: () => isOmoNative,
14
- };
15
- }
16
- function session() {
17
- return {
18
- state: { model: undefined, thinkingLevel: "off" },
19
- sessionManager: {
20
- getCwd: () => "/tmp/omo-footer-fixture",
21
- getSessionName: () => undefined,
22
- getUsageTotals: () => ({ cacheRead: 0, cacheWrite: 0, cost: 0, latestCacheHitRate: undefined }),
23
- },
24
- getContextUsage: () => undefined,
25
- isFastModeActive: () => false,
26
- modelRuntime: { isUsingOAuth: () => false },
27
- };
28
- }
29
- function renderFooter(isOmoNative) {
30
- return new FooterComponent(session(), footerData(isOmoNative)).render(200).join("\n");
31
- }
32
- describe("footer omo native badge", () => {
33
- beforeAll(() => initTheme("dark"));
34
- describe("#given a session running the omo native distribution", () => {
35
- it("#when the footer renders #then the cat badge appears", () => {
36
- expect(renderFooter(true)).toContain(OMO_NATIVE_BADGE);
37
- });
38
- it("#when the footer renders #then the badge leads the left anchor row", () => {
39
- const line = renderFooter(true);
40
- const badgeAt = line.indexOf(OMO_NATIVE_BADGE);
41
- const cwdAt = line.indexOf("omo-footer-fixture");
42
- expect(badgeAt).toBeGreaterThanOrEqual(0);
43
- expect(badgeAt).toBeLessThan(cwdAt);
44
- });
45
- });
46
- describe("#given a session that is not omo native", () => {
47
- it("#when the footer renders #then no badge appears", () => {
48
- expect(renderFooter(false)).not.toContain("OmO Native");
49
- });
50
- });
51
- });
52
- //# sourceMappingURL=footer-omo-native-badge.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"footer-omo-native-badge.test.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer-omo-native-badge.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAGzD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,uGAAuG;AACvG,uGAAuG;AACvG,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AAE3C,SAAS,UAAU,CAAC,WAAoB;IACvC,OAAO;QACN,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;QAC7B,oBAAoB,EAAE,GAAG,EAAE,CAAC,EAAE;QAC9B,yBAAyB,EAAE,GAAG,EAAE,CAAC,CAAC;QAClC,cAAc,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,GAAE,CAAC;QAC9B,WAAW,EAAE,GAAG,EAAE,CAAC,WAAW;KACW,CAAC;AAC5C,CAAC;AAED,SAAS,OAAO;IACf,OAAO;QACN,KAAK,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,EAAE;QACjD,cAAc,EAAE;YACf,MAAM,EAAE,GAAG,EAAE,CAAC,yBAAyB;YACvC,cAAc,EAAE,GAAG,EAAE,CAAC,SAAS;YAC/B,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,kBAAkB,EAAE,SAAS,EAAE,CAAC;SAC/F;QACD,eAAe,EAAE,GAAG,EAAE,CAAC,SAAS;QAChC,gBAAgB,EAAE,GAAG,EAAE,CAAC,KAAK;QAC7B,YAAY,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE;KAChB,CAAC;AAC9B,CAAC;AAED,SAAS,YAAY,CAAC,WAAoB;IACzC,OAAO,IAAI,eAAe,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvF,CAAC;AAED,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACxC,SAAS,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAEnC,QAAQ,CAAC,sDAAsD,EAAE,GAAG,EAAE;QACrE,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;YAC/D,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;YAC7E,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YAChC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;YAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;YACjD,MAAM,CAAC,OAAO,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;YAC1C,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACxD,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;YAC1D,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import { beforeAll, describe, expect, it } from \"vitest\";\nimport type { AgentSession } from \"../../../core/agent-session.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport { initTheme } from \"../theme/theme.ts\";\nimport { FooterComponent } from \"./footer.ts\";\n\n// The badge is the only visible marker that a session is running the omo native distribution, so it is\n// pinned here at the render() boundary: emoji, wording, and bottom-left anchoring all matter to users.\nconst OMO_NATIVE_BADGE = \"(😺 OmO Native)\";\n\nfunction footerData(isOmoNative: boolean): ReadonlyFooterDataProvider {\n\treturn {\n\t\tgetGitBranch: () => undefined,\n\t\tgetExtensionStatuses: () => [],\n\t\tgetAvailableProviderCount: () => 1,\n\t\tonBranchChange: () => () => {},\n\t\tisOmoNative: () => isOmoNative,\n\t} as unknown as ReadonlyFooterDataProvider;\n}\n\nfunction session(): AgentSession {\n\treturn {\n\t\tstate: { model: undefined, thinkingLevel: \"off\" },\n\t\tsessionManager: {\n\t\t\tgetCwd: () => \"/tmp/omo-footer-fixture\",\n\t\t\tgetSessionName: () => undefined,\n\t\t\tgetUsageTotals: () => ({ cacheRead: 0, cacheWrite: 0, cost: 0, latestCacheHitRate: undefined }),\n\t\t},\n\t\tgetContextUsage: () => undefined,\n\t\tisFastModeActive: () => false,\n\t\tmodelRuntime: { isUsingOAuth: () => false },\n\t} as unknown as AgentSession;\n}\n\nfunction renderFooter(isOmoNative: boolean): string {\n\treturn new FooterComponent(session(), footerData(isOmoNative)).render(200).join(\"\\n\");\n}\n\ndescribe(\"footer omo native badge\", () => {\n\tbeforeAll(() => initTheme(\"dark\"));\n\n\tdescribe(\"#given a session running the omo native distribution\", () => {\n\t\tit(\"#when the footer renders #then the cat badge appears\", () => {\n\t\t\texpect(renderFooter(true)).toContain(OMO_NATIVE_BADGE);\n\t\t});\n\n\t\tit(\"#when the footer renders #then the badge leads the left anchor row\", () => {\n\t\t\tconst line = renderFooter(true);\n\t\t\tconst badgeAt = line.indexOf(OMO_NATIVE_BADGE);\n\t\t\tconst cwdAt = line.indexOf(\"omo-footer-fixture\");\n\t\t\texpect(badgeAt).toBeGreaterThanOrEqual(0);\n\t\t\texpect(badgeAt).toBeLessThan(cwdAt);\n\t\t});\n\t});\n\n\tdescribe(\"#given a session that is not omo native\", () => {\n\t\tit(\"#when the footer renders #then no badge appears\", () => {\n\t\t\texpect(renderFooter(false)).not.toContain(\"OmO Native\");\n\t\t});\n\t});\n});\n"]}