dsh-cliworker-now 0.0.0-stage → 0.7.7

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 (344) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +364 -0
  3. package/README.md +368 -2
  4. package/cordis.patch.yml +3 -0
  5. package/lib/account-models.mjs +262 -0
  6. package/lib/client.js +22139 -0
  7. package/lib/client.js.map +1 -0
  8. package/lib/grok-catalog.mjs +145 -0
  9. package/lib/hermes-native-entry.py +96 -0
  10. package/lib/hermes-nous-refresh.py +331 -0
  11. package/lib/index.js +14544 -0
  12. package/lib/index.js.map +1 -0
  13. package/lib/native-launch.mjs +234 -0
  14. package/lib/native-spawn.mjs +32 -0
  15. package/lib/own-account-lease.mjs +105 -0
  16. package/lib/pi-installation.mjs +87 -0
  17. package/lib/pi-login.mjs +80 -0
  18. package/lib/pi-native-catalog.mjs +417 -0
  19. package/lib/pi-omp-bridge.mjs +602 -0
  20. package/lib/pi-omp-environment.mjs +15 -0
  21. package/lib/pi-omp-refresh.mjs +324 -0
  22. package/lib/private-launch.mjs +23 -0
  23. package/lib/terminal-bridge.mjs +18 -0
  24. package/lib/typert.host.d.ts +3 -0
  25. package/lib/typert.host.js +1430 -0
  26. package/lib/typert.remote-client.d.ts +72 -0
  27. package/lib/typert.remote-client.d.ts.map +1 -0
  28. package/lib/typert.remote-client.js +1256 -0
  29. package/lib/types/client/account-query-queue.d.ts +16 -0
  30. package/lib/types/client/account-query-queue.js +61 -0
  31. package/lib/types/client/account-query-queue.js.map +1 -0
  32. package/lib/types/client/account-terminal-css.d.ts +1 -0
  33. package/lib/types/client/account-terminal-css.js +3 -0
  34. package/lib/types/client/account-terminal-css.js.map +1 -0
  35. package/lib/types/client/account-terminal.d.ts +17 -0
  36. package/lib/types/client/account-terminal.js +525 -0
  37. package/lib/types/client/account-terminal.js.map +1 -0
  38. package/lib/types/client/assets/antigravity.png +0 -0
  39. package/lib/types/client/assets/claude-code.png +0 -0
  40. package/lib/types/client/assets/cliworker-now-logo.png +0 -0
  41. package/lib/types/client/assets/codex.png +0 -0
  42. package/lib/types/client/assets/grok.png +0 -0
  43. package/lib/types/client/assets/hermes.png +0 -0
  44. package/lib/types/client/assets/kimi-dark.png +0 -0
  45. package/lib/types/client/assets/kimi-light.png +0 -0
  46. package/lib/types/client/assets/mimo-code.png +0 -0
  47. package/lib/types/client/assets/omp.png +0 -0
  48. package/lib/types/client/assets/opencode.png +0 -0
  49. package/lib/types/client/assets/pi.png +0 -0
  50. package/lib/types/client/assets/zcode.png +0 -0
  51. package/lib/types/client/composer-keymap.d.ts +18 -0
  52. package/lib/types/client/composer-keymap.js +49 -0
  53. package/lib/types/client/composer-keymap.js.map +1 -0
  54. package/lib/types/client/conversation-timeline.d.ts +6 -0
  55. package/lib/types/client/conversation-timeline.js +65 -0
  56. package/lib/types/client/conversation-timeline.js.map +1 -0
  57. package/lib/types/client/copy-text.d.ts +5 -0
  58. package/lib/types/client/copy-text.js +45 -0
  59. package/lib/types/client/copy-text.js.map +1 -0
  60. package/lib/types/client/icons.d.ts +21 -0
  61. package/lib/types/client/icons.js +68 -0
  62. package/lib/types/client/icons.js.map +1 -0
  63. package/lib/types/client/index.d.ts +3 -0
  64. package/lib/types/client/index.js +124 -0
  65. package/lib/types/client/index.js.map +1 -0
  66. package/lib/types/client/management-styles.d.ts +1 -0
  67. package/lib/types/client/management-styles.js +17 -0
  68. package/lib/types/client/management-styles.js.map +1 -0
  69. package/lib/types/client/name-copy.d.ts +5 -0
  70. package/lib/types/client/name-copy.js +107 -0
  71. package/lib/types/client/name-copy.js.map +1 -0
  72. package/lib/types/client/native-job-overlay.d.ts +11 -0
  73. package/lib/types/client/native-job-overlay.js +129 -0
  74. package/lib/types/client/native-job-overlay.js.map +1 -0
  75. package/lib/types/client/operation-error.d.ts +2 -0
  76. package/lib/types/client/operation-error.js +12 -0
  77. package/lib/types/client/operation-error.js.map +1 -0
  78. package/lib/types/client/panel.d.ts +8 -0
  79. package/lib/types/client/panel.js +310 -0
  80. package/lib/types/client/panel.js.map +1 -0
  81. package/lib/types/client/rename-copy-styles.d.ts +1 -0
  82. package/lib/types/client/rename-copy-styles.js +21 -0
  83. package/lib/types/client/rename-copy-styles.js.map +1 -0
  84. package/lib/types/client/rename-worker.d.ts +9 -0
  85. package/lib/types/client/rename-worker.js +53 -0
  86. package/lib/types/client/rename-worker.js.map +1 -0
  87. package/lib/types/client/role-presets.d.ts +6 -0
  88. package/lib/types/client/role-presets.js +111 -0
  89. package/lib/types/client/role-presets.js.map +1 -0
  90. package/lib/types/client/settings-dialog.d.ts +13 -0
  91. package/lib/types/client/settings-dialog.js +442 -0
  92. package/lib/types/client/settings-dialog.js.map +1 -0
  93. package/lib/types/client/stat-popover.d.ts +10 -0
  94. package/lib/types/client/stat-popover.js +32 -0
  95. package/lib/types/client/stat-popover.js.map +1 -0
  96. package/lib/types/client/styles.d.ts +9 -0
  97. package/lib/types/client/styles.js +221 -0
  98. package/lib/types/client/styles.js.map +1 -0
  99. package/lib/types/client/telemetry.d.ts +11 -0
  100. package/lib/types/client/telemetry.js +24 -0
  101. package/lib/types/client/telemetry.js.map +1 -0
  102. package/lib/types/client/worker-card.d.ts +10 -0
  103. package/lib/types/client/worker-card.js +75 -0
  104. package/lib/types/client/worker-card.js.map +1 -0
  105. package/lib/types/client/worker-model-menu.d.ts +9 -0
  106. package/lib/types/client/worker-model-menu.js +136 -0
  107. package/lib/types/client/worker-model-menu.js.map +1 -0
  108. package/lib/types/client/workers.d.ts +21 -0
  109. package/lib/types/client/workers.js +59 -0
  110. package/lib/types/client/workers.js.map +1 -0
  111. package/lib/types/client/zcode-login-link-provider.d.ts +9 -0
  112. package/lib/types/client/zcode-login-link-provider.js +105 -0
  113. package/lib/types/client/zcode-login-link-provider.js.map +1 -0
  114. package/lib/types/client/zcode-login-link.d.ts +16 -0
  115. package/lib/types/client/zcode-login-link.js +194 -0
  116. package/lib/types/client/zcode-login-link.js.map +1 -0
  117. package/lib/types/host/account-identity.d.ts +16 -0
  118. package/lib/types/host/account-identity.js +126 -0
  119. package/lib/types/host/account-identity.js.map +1 -0
  120. package/lib/types/host/account-login.d.ts +10 -0
  121. package/lib/types/host/account-login.js +126 -0
  122. package/lib/types/host/account-login.js.map +1 -0
  123. package/lib/types/host/account-models.d.mts +21 -0
  124. package/lib/types/host/account-models.mjs +262 -0
  125. package/lib/types/host/accounts.d.ts +42 -0
  126. package/lib/types/host/accounts.js +945 -0
  127. package/lib/types/host/accounts.js.map +1 -0
  128. package/lib/types/host/adapters.d.ts +24 -0
  129. package/lib/types/host/adapters.js +380 -0
  130. package/lib/types/host/adapters.js.map +1 -0
  131. package/lib/types/host/agy-context.d.ts +8 -0
  132. package/lib/types/host/agy-context.js +122 -0
  133. package/lib/types/host/agy-context.js.map +1 -0
  134. package/lib/types/host/agy-models.d.ts +5 -0
  135. package/lib/types/host/agy-models.js +37 -0
  136. package/lib/types/host/agy-models.js.map +1 -0
  137. package/lib/types/host/agy-windows-account.d.ts +14 -0
  138. package/lib/types/host/agy-windows-account.js +104 -0
  139. package/lib/types/host/agy-windows-account.js.map +1 -0
  140. package/lib/types/host/authorized-catalog.d.ts +14 -0
  141. package/lib/types/host/authorized-catalog.js +35 -0
  142. package/lib/types/host/authorized-catalog.js.map +1 -0
  143. package/lib/types/host/bridge-protocol.d.ts +15 -0
  144. package/lib/types/host/bridge-protocol.js +80 -0
  145. package/lib/types/host/bridge-protocol.js.map +1 -0
  146. package/lib/types/host/claude-native-oauth.d.ts +17 -0
  147. package/lib/types/host/claude-native-oauth.js +272 -0
  148. package/lib/types/host/claude-native-oauth.js.map +1 -0
  149. package/lib/types/host/claude-own-route.d.ts +33 -0
  150. package/lib/types/host/claude-own-route.js +250 -0
  151. package/lib/types/host/claude-own-route.js.map +1 -0
  152. package/lib/types/host/cli-account-binding.d.ts +33 -0
  153. package/lib/types/host/cli-account-binding.js +540 -0
  154. package/lib/types/host/cli-account-binding.js.map +1 -0
  155. package/lib/types/host/cli-protocol.d.ts +27 -0
  156. package/lib/types/host/cli-protocol.js +212 -0
  157. package/lib/types/host/cli-protocol.js.map +1 -0
  158. package/lib/types/host/codex-account.d.ts +8 -0
  159. package/lib/types/host/codex-account.js +143 -0
  160. package/lib/types/host/codex-account.js.map +1 -0
  161. package/lib/types/host/codex-models.d.ts +6 -0
  162. package/lib/types/host/codex-models.js +169 -0
  163. package/lib/types/host/codex-models.js.map +1 -0
  164. package/lib/types/host/extended-adapters.d.ts +44 -0
  165. package/lib/types/host/extended-adapters.js +207 -0
  166. package/lib/types/host/extended-adapters.js.map +1 -0
  167. package/lib/types/host/first-party-account-context.d.ts +26 -0
  168. package/lib/types/host/first-party-account-context.js +55 -0
  169. package/lib/types/host/first-party-account-context.js.map +1 -0
  170. package/lib/types/host/first-party-models.d.ts +29 -0
  171. package/lib/types/host/first-party-models.js +459 -0
  172. package/lib/types/host/first-party-models.js.map +1 -0
  173. package/lib/types/host/grok-adapter.d.ts +44 -0
  174. package/lib/types/host/grok-adapter.js +293 -0
  175. package/lib/types/host/grok-adapter.js.map +1 -0
  176. package/lib/types/host/grok-models.d.ts +47 -0
  177. package/lib/types/host/grok-models.js +320 -0
  178. package/lib/types/host/grok-models.js.map +1 -0
  179. package/lib/types/host/harness-opencode-accounts.d.ts +17 -0
  180. package/lib/types/host/harness-opencode-accounts.js +80 -0
  181. package/lib/types/host/harness-opencode-accounts.js.map +1 -0
  182. package/lib/types/host/hermes-account-context.d.ts +8 -0
  183. package/lib/types/host/hermes-account-context.js +99 -0
  184. package/lib/types/host/hermes-account-context.js.map +1 -0
  185. package/lib/types/host/hermes-accounts.d.ts +19 -0
  186. package/lib/types/host/hermes-accounts.js +356 -0
  187. package/lib/types/host/hermes-accounts.js.map +1 -0
  188. package/lib/types/host/hermes-adapter.d.ts +93 -0
  189. package/lib/types/host/hermes-adapter.js +455 -0
  190. package/lib/types/host/hermes-adapter.js.map +1 -0
  191. package/lib/types/host/hermes-env.d.ts +4 -0
  192. package/lib/types/host/hermes-env.js +22 -0
  193. package/lib/types/host/hermes-env.js.map +1 -0
  194. package/lib/types/host/hermes-installation.d.ts +10 -0
  195. package/lib/types/host/hermes-installation.js +181 -0
  196. package/lib/types/host/hermes-installation.js.map +1 -0
  197. package/lib/types/host/hermes-models.d.ts +26 -0
  198. package/lib/types/host/hermes-models.js +328 -0
  199. package/lib/types/host/hermes-models.js.map +1 -0
  200. package/lib/types/host/hermes-native-entry.py +96 -0
  201. package/lib/types/host/hermes-nous-refresh.py +331 -0
  202. package/lib/types/host/hermes-nous.d.ts +53 -0
  203. package/lib/types/host/hermes-nous.js +343 -0
  204. package/lib/types/host/hermes-nous.js.map +1 -0
  205. package/lib/types/host/hermes-renewal.d.ts +27 -0
  206. package/lib/types/host/hermes-renewal.js +292 -0
  207. package/lib/types/host/hermes-renewal.js.map +1 -0
  208. package/lib/types/host/hermes-suppression.d.ts +7 -0
  209. package/lib/types/host/hermes-suppression.js +69 -0
  210. package/lib/types/host/hermes-suppression.js.map +1 -0
  211. package/lib/types/host/hermes-windows-installation.d.ts +5 -0
  212. package/lib/types/host/hermes-windows-installation.js +91 -0
  213. package/lib/types/host/hermes-windows-installation.js.map +1 -0
  214. package/lib/types/host/index.d.ts +140 -0
  215. package/lib/types/host/index.js +947 -0
  216. package/lib/types/host/index.js.map +1 -0
  217. package/lib/types/host/kimi-accounts.d.ts +17 -0
  218. package/lib/types/host/kimi-accounts.js +220 -0
  219. package/lib/types/host/kimi-accounts.js.map +1 -0
  220. package/lib/types/host/kimi-oauth-models.d.ts +14 -0
  221. package/lib/types/host/kimi-oauth-models.js +246 -0
  222. package/lib/types/host/kimi-oauth-models.js.map +1 -0
  223. package/lib/types/host/managed-agent.d.ts +4 -0
  224. package/lib/types/host/managed-agent.js +127 -0
  225. package/lib/types/host/managed-agent.js.map +1 -0
  226. package/lib/types/host/mimo-configuration.d.ts +22 -0
  227. package/lib/types/host/mimo-configuration.js +258 -0
  228. package/lib/types/host/mimo-configuration.js.map +1 -0
  229. package/lib/types/host/mimo-metadata-cache.d.ts +22 -0
  230. package/lib/types/host/mimo-metadata-cache.js +121 -0
  231. package/lib/types/host/mimo-metadata-cache.js.map +1 -0
  232. package/lib/types/host/native-capability-cache.d.ts +11 -0
  233. package/lib/types/host/native-capability-cache.js +94 -0
  234. package/lib/types/host/native-capability-cache.js.map +1 -0
  235. package/lib/types/host/native-launch.d.mts +2 -0
  236. package/lib/types/host/native-launch.mjs +234 -0
  237. package/lib/types/host/native-process.d.ts +5 -0
  238. package/lib/types/host/native-process.js +41 -0
  239. package/lib/types/host/native-process.js.map +1 -0
  240. package/lib/types/host/native-spawn.mjs +32 -0
  241. package/lib/types/host/opencode-adapter.d.ts +94 -0
  242. package/lib/types/host/opencode-adapter.js +507 -0
  243. package/lib/types/host/opencode-adapter.js.map +1 -0
  244. package/lib/types/host/opencode-native.d.ts +27 -0
  245. package/lib/types/host/opencode-native.js +403 -0
  246. package/lib/types/host/opencode-native.js.map +1 -0
  247. package/lib/types/host/own-account-lease.d.mts +5 -0
  248. package/lib/types/host/own-account-lease.mjs +105 -0
  249. package/lib/types/host/pi-installation.d.mts +7 -0
  250. package/lib/types/host/pi-installation.mjs +87 -0
  251. package/lib/types/host/pi-omp-accounts.d.ts +28 -0
  252. package/lib/types/host/pi-omp-accounts.js +175 -0
  253. package/lib/types/host/pi-omp-accounts.js.map +1 -0
  254. package/lib/types/host/pi-omp-adapter.d.ts +32 -0
  255. package/lib/types/host/pi-omp-adapter.js +190 -0
  256. package/lib/types/host/pi-omp-adapter.js.map +1 -0
  257. package/lib/types/host/pi-omp-environment.d.mts +1 -0
  258. package/lib/types/host/pi-omp-environment.mjs +15 -0
  259. package/lib/types/host/pi-omp-identity.d.ts +2 -0
  260. package/lib/types/host/pi-omp-identity.js +142 -0
  261. package/lib/types/host/pi-omp-identity.js.map +1 -0
  262. package/lib/types/host/pi-omp-logout.d.ts +3 -0
  263. package/lib/types/host/pi-omp-logout.js +152 -0
  264. package/lib/types/host/pi-omp-logout.js.map +1 -0
  265. package/lib/types/host/pi-omp-native.d.ts +71 -0
  266. package/lib/types/host/pi-omp-native.js +753 -0
  267. package/lib/types/host/pi-omp-native.js.map +1 -0
  268. package/lib/types/host/pi-omp-refresh.d.mts +7 -0
  269. package/lib/types/host/pi-omp-refresh.mjs +324 -0
  270. package/lib/types/host/private-launch.mjs +23 -0
  271. package/lib/types/host/process.d.ts +43 -0
  272. package/lib/types/host/process.js +134 -0
  273. package/lib/types/host/process.js.map +1 -0
  274. package/lib/types/host/protocol.d.ts +23 -0
  275. package/lib/types/host/protocol.js +112 -0
  276. package/lib/types/host/protocol.js.map +1 -0
  277. package/lib/types/host/roles.d.ts +34 -0
  278. package/lib/types/host/roles.js +80 -0
  279. package/lib/types/host/roles.js.map +1 -0
  280. package/lib/types/host/routing.d.ts +31 -0
  281. package/lib/types/host/routing.js +248 -0
  282. package/lib/types/host/routing.js.map +1 -0
  283. package/lib/types/host/runtime.d.ts +60 -0
  284. package/lib/types/host/runtime.js +598 -0
  285. package/lib/types/host/runtime.js.map +1 -0
  286. package/lib/types/host/storage.d.ts +48 -0
  287. package/lib/types/host/storage.js +449 -0
  288. package/lib/types/host/storage.js.map +1 -0
  289. package/lib/types/host/telemetry.d.ts +17 -0
  290. package/lib/types/host/telemetry.js +158 -0
  291. package/lib/types/host/telemetry.js.map +1 -0
  292. package/lib/types/host/windows-account-bootstrap.ps1 +44 -0
  293. package/lib/types/host/windows-account-launch.mjs +47 -0
  294. package/lib/types/host/windows-account-owner.ps1 +137 -0
  295. package/lib/types/host/windows-account-stdio.mjs +19 -0
  296. package/lib/types/host/windows-account-terminal.d.ts +17 -0
  297. package/lib/types/host/windows-account-terminal.js +208 -0
  298. package/lib/types/host/windows-account-terminal.js.map +1 -0
  299. package/lib/types/host/windows-agy-account.ps1 +81 -0
  300. package/lib/types/host/windows-cli-installation.d.ts +31 -0
  301. package/lib/types/host/windows-cli-installation.js +157 -0
  302. package/lib/types/host/windows-cli-installation.js.map +1 -0
  303. package/lib/types/host/windows-hidden-launch.mjs +49 -0
  304. package/lib/types/host/windows-hidden-process.d.ts +12 -0
  305. package/lib/types/host/windows-hidden-process.js +116 -0
  306. package/lib/types/host/windows-hidden-process.js.map +1 -0
  307. package/lib/types/host/zcode-account-models.d.ts +16 -0
  308. package/lib/types/host/zcode-account-models.js +114 -0
  309. package/lib/types/host/zcode-account-models.js.map +1 -0
  310. package/lib/types/host/zcode-adapter.d.ts +93 -0
  311. package/lib/types/host/zcode-adapter.js +808 -0
  312. package/lib/types/host/zcode-adapter.js.map +1 -0
  313. package/lib/types/host/zcode-grok-accounts.d.ts +30 -0
  314. package/lib/types/host/zcode-grok-accounts.js +408 -0
  315. package/lib/types/host/zcode-grok-accounts.js.map +1 -0
  316. package/lib/types/shared/accounts.d.ts +47 -0
  317. package/lib/types/shared/accounts.js +2 -0
  318. package/lib/types/shared/accounts.js.map +1 -0
  319. package/lib/types/shared/builtin-presets.d.ts +2 -0
  320. package/lib/types/shared/builtin-presets.js +231 -0
  321. package/lib/types/shared/builtin-presets.js.map +1 -0
  322. package/lib/types/shared/cli-settings.d.ts +5 -0
  323. package/lib/types/shared/cli-settings.js +2 -0
  324. package/lib/types/shared/cli-settings.js.map +1 -0
  325. package/lib/types/shared/model-presentation.d.ts +10 -0
  326. package/lib/types/shared/model-presentation.js +95 -0
  327. package/lib/types/shared/model-presentation.js.map +1 -0
  328. package/lib/types/shared/models.d.ts +7 -0
  329. package/lib/types/shared/models.js +35 -0
  330. package/lib/types/shared/models.js.map +1 -0
  331. package/lib/types/shared/telemetry.d.ts +18 -0
  332. package/lib/types/shared/telemetry.js +16 -0
  333. package/lib/types/shared/telemetry.js.map +1 -0
  334. package/lib/types/shared/types.d.ts +127 -0
  335. package/lib/types/shared/types.js +147 -0
  336. package/lib/types/shared/types.js.map +1 -0
  337. package/lib/windows-account-bootstrap.ps1 +44 -0
  338. package/lib/windows-account-launch.mjs +47 -0
  339. package/lib/windows-account-owner.ps1 +137 -0
  340. package/lib/windows-account-stdio.mjs +19 -0
  341. package/lib/windows-agy-account.ps1 +81 -0
  342. package/lib/windows-hidden-launch.mjs +49 -0
  343. package/lib/zcode-resume.mjs +423 -0
  344. package/package.json +109 -4
@@ -0,0 +1,602 @@
1
+ // Subprocess protocol bridge. The Host owns the external sandbox, credentials,
2
+ // deadlines, directory/session locks, and descendant cleanup. No shell is used.
3
+ import { spawnNativeProcess } from './native-spawn.mjs'
4
+ import { createHash, randomBytes } from 'node:crypto'
5
+ import { mkdir, writeFile, rename, realpath, unlink, rmdir, lstat, open, rm } from 'node:fs/promises'
6
+ import { constants } from 'node:fs'
7
+ import { dirname, isAbsolute, join, resolve, sep } from 'node:path'
8
+ import { ompConfigurationEnvironment } from './pi-omp-environment.mjs'
9
+ import { readFile } from 'node:fs/promises'
10
+ import { nativeLaunchArgv } from './native-launch.mjs'
11
+ import { ownsOwnAccountLease, OWN_ACCOUNT_BUSY } from './own-account-lease.mjs'
12
+ import { assertPiOmpRefreshSources, assertPiOmpRefreshCache } from './pi-omp-refresh.mjs'
13
+ import {
14
+ queryNativeCandidates,
15
+ snapshotNativeCandidates,
16
+ accountSupportedChoices,
17
+ } from './pi-native-catalog.mjs'
18
+
19
+ process.umask(0o077)
20
+ const output = (value) => process.stdout.write(JSON.stringify(value) + '\n')
21
+ const event = (value) => output({ type: 'event', event: value })
22
+ const safeErrors = new Set([
23
+ '任务已停止',
24
+ '所选模型或强度不在原生可用目录中',
25
+ '无法读取 Pi/OMP 原生模型目录,请检查本机配置',
26
+ OWN_ACCOUNT_BUSY,
27
+ '账号配置已变化,请重新刷新模型',
28
+ ])
29
+ const errorMessage = (error) =>
30
+ error instanceof Error && safeErrors.has(error.message)
31
+ ? error.message
32
+ : 'Pi/OMP 原生配置、模型或协议校验失败'
33
+ const inside = (root, path) => path.startsWith(root + sep)
34
+ let selectedProvider, selectedModel
35
+ async function privateDirectory(path) {
36
+ await mkdir(path, { recursive: true, mode: 0o700 })
37
+ const info = await lstat(path)
38
+ if (info.isSymbolicLink() || !info.isDirectory()) throw new Error('Unsafe Pi/OMP state directory symlink')
39
+ if (process.platform === 'win32') {
40
+ // Windows cannot open directory descriptors with Node's POSIX O_DIRECTORY.
41
+ const canonical = await realpath(path),
42
+ current = await lstat(path)
43
+ if (current.isSymbolicLink() || current.dev !== info.dev || current.ino !== info.ino)
44
+ throw new Error('Pi/OMP state directory changed while opening')
45
+ return canonical
46
+ }
47
+ const handle = await open(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW)
48
+ try {
49
+ const opened = await handle.stat()
50
+ if (info.dev !== opened.dev || info.ino !== opened.ino)
51
+ throw new Error('Pi/OMP state directory changed while opening')
52
+ await handle.chmod(0o700)
53
+ } finally {
54
+ await handle.close()
55
+ }
56
+ return realpath(path)
57
+ }
58
+ async function privateFile(path, read = false, limit = 1024 * 1024) {
59
+ const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK)
60
+ try {
61
+ const info = await handle.stat()
62
+ if (!info.isFile() || info.nlink !== 1) throw new Error('Unsafe Pi/OMP state file or hard link')
63
+ if (read) {
64
+ if (info.size > limit) throw new Error('Pi/OMP state JSON exceeds limit')
65
+ const contents = await handle.readFile('utf8')
66
+ const after = await handle.stat()
67
+ if (Buffer.byteLength(contents) > limit || info.size !== after.size || info.mtimeMs !== after.mtimeMs)
68
+ throw new Error('Pi/OMP state JSON changed while reading')
69
+ return contents
70
+ }
71
+ } finally {
72
+ await handle.close()
73
+ }
74
+ }
75
+ async function writePrivateFile(path, contents) {
76
+ await privateDirectory(dirname(path))
77
+ const temporary = join(dirname(path), `.cliworker-${randomBytes(16).toString('hex')}.tmp`)
78
+ try {
79
+ await writeFile(temporary, contents, { flag: 'wx', mode: 0o600 })
80
+ // Replaces a planted file link itself; never truncates its external inode.
81
+ await rename(temporary, path)
82
+ } finally {
83
+ await rm(temporary, { force: true })
84
+ }
85
+ }
86
+ let child,
87
+ stopped = false,
88
+ failure,
89
+ closeTimer,
90
+ forceTimer,
91
+ startupTimer,
92
+ config,
93
+ session,
94
+ finalized = false
95
+ const metadataController = new AbortController()
96
+ const stop = (reason) => {
97
+ failure ??= reason
98
+ child?.stdin?.end()
99
+ child?.kill('SIGTERM')
100
+ forceTimer ??= setTimeout(() => child?.kill('SIGKILL'), 5000)
101
+ }
102
+ process.on('SIGTERM', () => {
103
+ stopped = true
104
+ metadataController.abort()
105
+ stop('任务已停止')
106
+ })
107
+ process.on('SIGINT', () => {
108
+ stopped = true
109
+ metadataController.abort()
110
+ stop('任务已停止')
111
+ })
112
+ const persist = async () => {
113
+ if (!session || config.discover) return
114
+ const path = await realpath(session.path)
115
+ const nativeRoot = await privateDirectory(config.nativeRoot ?? join(config.stateDirectory, 'agent'))
116
+ if (!inside(nativeRoot, path) || path !== resolve(session.path))
117
+ throw new Error('CLI session path escaped its private directory or used a symlink')
118
+ await privateFile(path)
119
+ const mapDirectory = join(config.stateDirectory, 'sessions')
120
+ await privateDirectory(mapDirectory)
121
+ const filename = join(mapDirectory, createHash('sha256').update(session.id).digest('hex') + '.json')
122
+ await writePrivateFile(
123
+ filename,
124
+ JSON.stringify({
125
+ cli: config.cli,
126
+ project: config.project,
127
+ id: session.id,
128
+ path,
129
+ model: config.preference.model,
130
+ }),
131
+ )
132
+ }
133
+ try {
134
+ const requestPath = process.argv[2]
135
+ config = JSON.parse(await privateFile(requestPath, true))
136
+ await unlink(requestPath)
137
+ await rmdir(dirname(requestPath))
138
+ if (
139
+ !['pi', 'omp'].includes(config.cli) ||
140
+ typeof config.executable !== 'string' ||
141
+ !isAbsolute(config.stateDirectory)
142
+ )
143
+ throw new Error('Invalid bridge request')
144
+ config.stateDirectory = await privateDirectory(config.stateDirectory)
145
+ if (config.leaseDirectory) {
146
+ if (typeof config.nativeRoot !== 'string' || dirname(config.nativeRoot) !== config.leaseDirectory)
147
+ throw new Error('Invalid shared account directory')
148
+ if (!(await ownsOwnAccountLease(config.leaseDirectory, config.leaseNonce)))
149
+ throw new Error(OWN_ACCOUNT_BUSY)
150
+ await assertPiOmpRefreshSources(config.leaseDirectory, config.refreshReceipt)
151
+ await assertPiOmpRefreshCache(config.leaseDirectory, config.refreshReceipt)
152
+ }
153
+ const nativeRoot = await privateDirectory(config.nativeRoot ?? join(config.stateDirectory, 'agent'))
154
+ const temporaryDirectory = await privateDirectory(join(config.stateDirectory, 't'))
155
+ await privateDirectory(join(nativeRoot, 'sessions'))
156
+ if (!config.discover) await privateDirectory(join(config.stateDirectory, 'sessions'))
157
+ const env = Object.fromEntries(
158
+ [
159
+ 'PATH',
160
+ 'BUN_INSTALL',
161
+ 'HOME',
162
+ 'USERPROFILE',
163
+ 'SystemRoot',
164
+ 'SYSTEMROOT',
165
+ 'WINDIR',
166
+ 'COMSPEC',
167
+ 'ComSpec',
168
+ 'PATHEXT',
169
+ 'APPDATA',
170
+ 'LOCALAPPDATA',
171
+ 'TEMP',
172
+ 'TMP',
173
+ 'LANG',
174
+ 'LC_ALL',
175
+ 'USER',
176
+ 'LOGNAME',
177
+ 'SHELL',
178
+ 'TMPDIR',
179
+ 'NODE_EXTRA_CA_CERTS',
180
+ 'SSL_CERT_FILE',
181
+ 'HTTPS_PROXY',
182
+ 'HTTP_PROXY',
183
+ 'ALL_PROXY',
184
+ 'NO_PROXY',
185
+ ].flatMap((key) => (process.env[key] ? [[key, process.env[key]]] : [])),
186
+ )
187
+ Object.assign(env, {
188
+ PI_CODING_AGENT_DIR: nativeRoot,
189
+ TMPDIR: temporaryDirectory,
190
+ ...(process.platform === 'win32' ? { TMP: temporaryDirectory, TEMP: temporaryDirectory } : {}),
191
+ PI_OFFLINE: '1',
192
+ PI_TELEMETRY: '0',
193
+ ELECTRON_RUN_AS_NODE: '1',
194
+ NO_COLOR: '1',
195
+ })
196
+ const nativeEnvironment = JSON.parse(await privateFile(join(nativeRoot, 'native-env.json'), true))
197
+ if (
198
+ !nativeEnvironment ||
199
+ typeof nativeEnvironment !== 'object' ||
200
+ Array.isArray(nativeEnvironment) ||
201
+ Object.entries(nativeEnvironment).some(
202
+ ([key, value]) =>
203
+ !/^[A-Z][A-Z0-9_]*$/.test(key) ||
204
+ /^(?:PATH|HOME|USERPROFILE|SYSTEMROOT|WINDIR|COMSPEC|PATHEXT|APPDATA|LOCALAPPDATA|SHELL|TMPDIR|TMP|TEMP|NODE_OPTIONS|NODE_PATH|ELECTRON_RUN_AS_NODE|OMP_AUTH_BROKER_.*|OMP_PROFILE|PI_PROFILE|PI_CONFIG_DIR|PI_CODING_AGENT_DIR|LD_.*|DYLD_.*)$/i.test(
205
+ key,
206
+ ) ||
207
+ typeof value !== 'string',
208
+ )
209
+ )
210
+ throw new Error('Invalid native environment snapshot')
211
+ Object.assign(env, nativeEnvironment)
212
+ // Pi's SDK is loaded in this process and consults process.env itself. Apply
213
+ // the same own-source environment before importing it, not only at spawn.
214
+ process.env = env
215
+ const isolated =
216
+ config.cli === 'pi'
217
+ ? [
218
+ '--no-extensions',
219
+ '--no-skills',
220
+ '--no-prompt-templates',
221
+ '--no-themes',
222
+ '--no-context-files',
223
+ '--no-approve',
224
+ ]
225
+ : [
226
+ '--no-extensions',
227
+ '--no-skills',
228
+ '--no-rules',
229
+ '--no-title',
230
+ '--no-lsp',
231
+ '--no-pty',
232
+ '--approval-mode',
233
+ 'write',
234
+ '--config',
235
+ join(nativeRoot, 'config.yml'),
236
+ ]
237
+ if (config.cli === 'omp') {
238
+ Object.assign(env, ompConfigurationEnvironment(dirname(nativeRoot)))
239
+ Object.assign(process.env, ompConfigurationEnvironment(dirname(nativeRoot)))
240
+ const nativeAuth = JSON.parse(await readFile(join(nativeRoot, 'native-auth.json'), 'utf8'))
241
+ await writePrivateFile(
242
+ join(nativeRoot, 'config.yml'),
243
+ JSON.stringify({
244
+ ...nativeAuth,
245
+ startup: { setupWizard: false, checkUpdate: false, showSplash: false },
246
+ disabledProviders: [
247
+ 'native',
248
+ 'claude',
249
+ 'claude-plugins',
250
+ 'codex',
251
+ 'cursor',
252
+ 'gemini',
253
+ 'opencode',
254
+ 'mcp-json',
255
+ 'vscode',
256
+ 'windsurf',
257
+ 'omp-plugins',
258
+ ],
259
+ mcp: { enableProjectConfig: false },
260
+ retry: { modelFallback: false },
261
+ memory: { backend: 'off' },
262
+ tools: { approvalMode: 'write' },
263
+ }),
264
+ )
265
+ }
266
+ let resume
267
+ let catalog
268
+ if (config.metadataPhase === 'native-candidates') {
269
+ const candidates = await queryNativeCandidates(
270
+ config.cli,
271
+ config.executable,
272
+ nativeRoot,
273
+ env,
274
+ (process) => {
275
+ child = process
276
+ if (stopped) stop('任务已停止')
277
+ },
278
+ { signal: metadataController.signal },
279
+ )
280
+ if (stopped || failure) throw new Error('任务已停止')
281
+ const snapshot = snapshotNativeCandidates(candidates, env)
282
+ await writePrivateFile(join(nativeRoot, 'catalog-candidates.json'), JSON.stringify(snapshot))
283
+ output({ phase: 'native-candidates', count: snapshot.length })
284
+ finalized = true
285
+ } else if (config.metadataPhase === 'account-scope') {
286
+ const candidates = JSON.parse(
287
+ await privateFile(join(nativeRoot, 'catalog-candidates.json'), true, 16 * 1024 * 1024),
288
+ )
289
+ if (!Array.isArray(candidates)) throw new Error('Invalid native candidate snapshot')
290
+ catalog = await accountSupportedChoices(config.cli, nativeRoot, candidates, env, {
291
+ signal: metadataController.signal,
292
+ })
293
+ } else {
294
+ catalog = config.confirmedCatalog
295
+ if (!Array.isArray(catalog)) throw new Error('Missing confirmed account model catalog')
296
+ }
297
+ if (stopped || failure) throw new Error('任务已停止')
298
+ if (finalized) {
299
+ // Candidate phase has no account networking or model execution.
300
+ } else if (config.discover || config.metadataPhase === 'account-scope') {
301
+ output(catalog)
302
+ finalized = true
303
+ } else {
304
+ if (
305
+ typeof config.preference?.model !== 'string' ||
306
+ !/^[A-Za-z0-9_.:-]+\/[^\s\x00-\x1f]+$/.test(config.preference.model)
307
+ )
308
+ throw new Error('Invalid selected native provider/model')
309
+ const slash = config.preference.model.indexOf('/')
310
+ selectedProvider = config.preference.model.slice(0, slash)
311
+ selectedModel = config.preference.model.slice(slash + 1)
312
+ const selected = catalog.find((model) => model.id === config.preference.model)
313
+ if (
314
+ !selected ||
315
+ (config.preference.effort !== 'default' && !selected.efforts.includes(config.preference.effort))
316
+ )
317
+ throw new Error('所选模型或强度不在原生可用目录中')
318
+ if (
319
+ !['plan', 'accept-edits'].includes(config.mode) ||
320
+ typeof config.prompt !== 'string' ||
321
+ !config.prompt.trim()
322
+ )
323
+ throw new Error('Invalid task mode or prompt')
324
+ config.project = await realpath(config.project)
325
+ if (config.conversationId) {
326
+ await privateDirectory(join(config.stateDirectory, 'sessions'))
327
+ const mapping = join(
328
+ config.stateDirectory,
329
+ 'sessions',
330
+ createHash('sha256').update(config.conversationId).digest('hex') + '.json',
331
+ )
332
+ const mapped = JSON.parse(await privateFile(mapping, true))
333
+ if (
334
+ mapped.cli !== config.cli ||
335
+ mapped.project !== config.project ||
336
+ mapped.id !== config.conversationId
337
+ )
338
+ throw new Error('Saved session identity does not match this request')
339
+ resume = await realpath(mapped.path)
340
+ if (!inside(nativeRoot, resume) || resume !== resolve(mapped.path))
341
+ throw new Error('Saved session path escaped its private directory or used a symlink')
342
+ await privateFile(resume)
343
+ }
344
+ const tools = config.cli === 'pi' ? ['read', 'grep', 'find', 'ls'] : ['read', 'grep', 'glob']
345
+ if (config.mode === 'accept-edits') tools.push('write', 'edit')
346
+ const nativeArgs = [
347
+ '--mode',
348
+ 'rpc',
349
+ ...isolated,
350
+ '--provider',
351
+ selectedProvider,
352
+ '--model',
353
+ selectedModel,
354
+ ...(config.preference.effort === 'default'
355
+ ? []
356
+ : ['--thinking', config.preference.effort === 'none' ? 'off' : config.preference.effort]),
357
+ '--tools',
358
+ tools.join(','),
359
+ ...(resume ? [config.cli === 'pi' ? '--session' : '--resume', resume] : []),
360
+ ]
361
+ const launch = nativeLaunchArgv([config.executable, ...nativeArgs])
362
+ if (stopped) throw new Error('任务已停止')
363
+ child = spawnNativeProcess(launch[0], launch.slice(1), {
364
+ cwd: config.project,
365
+ env,
366
+ detached: false,
367
+ stdio: ['pipe', 'pipe', 'pipe'],
368
+ })
369
+ let pending = '',
370
+ bytes = 0,
371
+ stderrBytes = 0,
372
+ settled = false,
373
+ finalState = false,
374
+ sent = false,
375
+ response = '',
376
+ currentText = '',
377
+ assistantStep = 0
378
+ const toolSteps = new Map()
379
+ const send = (value) => {
380
+ if (!child.stdin.destroyed) child.stdin.write(JSON.stringify(value) + '\n')
381
+ }
382
+ const validateState = (state, validateModel = true) => {
383
+ if (
384
+ !state ||
385
+ typeof state.sessionId !== 'string' ||
386
+ !state.sessionId ||
387
+ typeof state.sessionFile !== 'string' ||
388
+ !inside(nativeRoot, resolve(state.sessionFile))
389
+ )
390
+ throw new Error('CLI did not return a private session identity')
391
+ if (
392
+ validateModel &&
393
+ (state.model?.provider !== selectedProvider ||
394
+ state.model?.id !== selectedModel ||
395
+ (config.preference.effort !== 'default' &&
396
+ state.thinkingLevel !== (config.preference.effort === 'none' ? 'off' : config.preference.effort)))
397
+ )
398
+ throw new Error('CLI model or effort differs from the selected preference')
399
+ if (
400
+ (session && session.id !== state.sessionId) ||
401
+ (config.conversationId && config.conversationId !== state.sessionId)
402
+ )
403
+ throw new Error('CLI changed the conversation identity')
404
+ if (
405
+ (resume && resume !== resolve(state.sessionFile)) ||
406
+ (session && session.path !== resolve(state.sessionFile))
407
+ )
408
+ throw new Error('CLI changed the saved session file')
409
+ session = { id: state.sessionId, path: resolve(state.sessionFile) }
410
+ }
411
+ const consume = (value) => {
412
+ if (!value || typeof value !== 'object' || typeof value.type !== 'string')
413
+ throw new Error('Invalid native RPC record')
414
+ if (value.type === 'response') {
415
+ if (!value.success) throw new Error('Native CLI rejected an RPC request')
416
+ if (value.id === 'select-model') {
417
+ if (config.preference.effort !== 'default')
418
+ send({
419
+ id: 'select-effort',
420
+ type: 'set_thinking_level',
421
+ level: config.preference.effort === 'none' ? 'off' : config.preference.effort,
422
+ })
423
+ else send({ id: 'selected', type: 'get_state' })
424
+ } else if (value.id === 'select-effort') send({ id: 'selected', type: 'get_state' })
425
+ else if (value.id === 'initial' || value.id === 'selected') {
426
+ validateState(value.data, value.id === 'selected')
427
+ if (value.data.isStreaming) throw new Error('Native session is already streaming')
428
+ if (
429
+ value.id === 'initial' &&
430
+ (value.data.model?.provider !== selectedProvider || value.data.model?.id !== selectedModel)
431
+ ) {
432
+ send({
433
+ id: 'select-model',
434
+ type: 'set_model',
435
+ provider: selectedProvider,
436
+ modelId: selectedModel,
437
+ })
438
+ return
439
+ }
440
+ if (
441
+ value.id === 'initial' &&
442
+ config.preference.effort !== 'default' &&
443
+ value.data.thinkingLevel !==
444
+ (config.preference.effort === 'none' ? 'off' : config.preference.effort)
445
+ ) {
446
+ send({
447
+ id: 'select-effort',
448
+ type: 'set_thinking_level',
449
+ level: config.preference.effort === 'none' ? 'off' : config.preference.effort,
450
+ })
451
+ return
452
+ }
453
+ validateState(value.data)
454
+ clearTimeout(startupTimer)
455
+ output({
456
+ type: 'session',
457
+ id: session.id,
458
+ model: `${value.data.model.provider}/${value.data.model.id}`,
459
+ })
460
+ event({
461
+ kind: 'status',
462
+ text: `${config.cli === 'pi' ? 'Pi' : 'OMP'} 原生模型已确认`,
463
+ observedModel: `${value.data.model.provider}/${value.data.model.id}`,
464
+ })
465
+ if (!stopped && !failure) {
466
+ sent = true
467
+ send({ id: 'prompt', type: 'prompt', message: config.prompt })
468
+ }
469
+ } else if (value.id === 'final') {
470
+ validateState(value.data)
471
+ if (value.data.isStreaming || value.data.isCompacting || (value.data.pendingMessageCount ?? 0) > 0)
472
+ throw new Error('Native agent is still active after its terminal event')
473
+ finalState = true
474
+ child.stdin.end()
475
+ closeTimer ??= setTimeout(() => stop('CLI did not exit after completion'), 5000)
476
+ }
477
+ return
478
+ }
479
+ if (config.discover) return
480
+ if (value.type === 'extension_ui_request') {
481
+ // Notifications carry no obligation. Interactive requests are denied.
482
+ if (['confirm', 'select', 'input', 'editor'].includes(value.method)) {
483
+ failure ??= 'CLI 需要额外原生交互授权'
484
+ send({ type: 'extension_ui_response', id: value.id, cancelled: true, confirmed: false })
485
+ }
486
+ return
487
+ }
488
+ if (value.type === 'message_start' && value.message?.role === 'assistant') {
489
+ currentText = ''
490
+ assistantStep++
491
+ return
492
+ }
493
+ if (value.type === 'message_update' && value.assistantMessageEvent?.type === 'text_delta') {
494
+ const text = value.assistantMessageEvent.delta
495
+ if (typeof text !== 'string') throw new Error('Invalid text delta')
496
+ currentText += text
497
+ response += text
498
+ event({ kind: 'assistant', step: assistantStep, text })
499
+ return
500
+ }
501
+ if (value.type === 'message_end' && value.message?.role === 'assistant') {
502
+ if (['error', 'aborted'].includes(value.message.stopReason)) failure ??= '模型请求失败或被中止'
503
+ if (value.message.model && value.message.model !== selectedModel)
504
+ failure ??= '模型响应与所选型号不一致'
505
+ if (value.message.provider && value.message.provider !== selectedProvider)
506
+ failure ??= '模型响应与所选提供商不一致'
507
+ if (!currentText) {
508
+ const text = (value.message.content ?? [])
509
+ .filter((c) => c.type === 'text')
510
+ .map((c) => c.text ?? '')
511
+ .join('')
512
+ if (text) {
513
+ response += text
514
+ event({ kind: 'assistant', step: assistantStep, text })
515
+ }
516
+ }
517
+ return
518
+ }
519
+ if (value.type === 'tool_execution_start') {
520
+ const step = 100000 + toolSteps.size
521
+ toolSteps.set(value.toolCallId, step)
522
+ event({ kind: 'tool', step, text: String(value.toolName ?? '工具'), state: 'RUNNING' })
523
+ return
524
+ }
525
+ if (value.type === 'tool_execution_end') {
526
+ if (value.isError) failure ??= '至少一个 CLI 工具执行失败'
527
+ const detail = (value.result?.content ?? [])
528
+ .filter((c) => c.type === 'text')
529
+ .map((c) => c.text ?? '')
530
+ .join('\n')
531
+ .slice(0, 32768)
532
+ event({
533
+ kind: 'tool',
534
+ step: toolSteps.get(value.toolCallId),
535
+ text: String(value.toolName ?? '工具'),
536
+ state: value.isError ? 'FAILED' : 'COMPLETED',
537
+ detail,
538
+ })
539
+ return
540
+ }
541
+ if (
542
+ (config.cli === 'pi' && value.type === 'agent_settled') ||
543
+ (config.cli === 'omp' && value.type === 'agent_end' && value.isTerminal !== false)
544
+ ) {
545
+ if (!sent) throw new Error('Unexpected native terminal event')
546
+ settled = true
547
+ send({ id: 'final', type: 'get_state' })
548
+ }
549
+ }
550
+ child.stdin.on('error', () => {})
551
+ child.stdout.setEncoding('utf8')
552
+ child.stdout.on('data', (chunk) => {
553
+ try {
554
+ bytes += Buffer.byteLength(chunk)
555
+ if (bytes > 16 * 1024 * 1024) throw new Error('Native RPC output exceeds run limit')
556
+ pending += chunk
557
+ let end
558
+ while ((end = pending.indexOf('\n')) >= 0) {
559
+ const line = pending.slice(0, end)
560
+ pending = pending.slice(end + 1)
561
+ if (Buffer.byteLength(line) > 1024 * 1024) throw new Error('Native RPC line exceeds limit')
562
+ if (line.trim()) consume(JSON.parse(line))
563
+ }
564
+ if (Buffer.byteLength(pending) > 1024 * 1024) throw new Error('Native RPC line exceeds limit')
565
+ } catch (error) {
566
+ stop(errorMessage(error))
567
+ }
568
+ })
569
+ child.stderr.on('data', (chunk) => {
570
+ stderrBytes += chunk.length
571
+ if (stderrBytes > 2 * 1024 * 1024) stop('Native stderr exceeds limit')
572
+ })
573
+ startupTimer = setTimeout(() => stop('Native RPC startup timed out'), 20000)
574
+ send({ id: 'initial', type: 'get_state' })
575
+ const outcome = await new Promise((resolve) => {
576
+ child.once('error', () => {
577
+ failure ??= '无法启动原生 CLI'
578
+ })
579
+ child.once('close', (code, signal) => resolve({ code, signal }))
580
+ })
581
+ clearTimeout(startupTimer)
582
+ clearTimeout(closeTimer)
583
+ clearTimeout(forceTimer)
584
+ if (pending.trim()) failure ??= 'Truncated native RPC frame'
585
+ if (outcome.code !== 0) failure ??= '原生 CLI 异常退出'
586
+ if (!settled || !finalState || !sent) failure ??= '原生 CLI 未确认任务完成'
587
+ try {
588
+ await persist()
589
+ } catch (error) {
590
+ failure ??= errorMessage(error)
591
+ }
592
+ output({ type: 'result', status: failure || stopped ? 'ERROR' : 'SUCCESS', response, error: failure })
593
+ finalized = true
594
+ if (failure || stopped) process.exitCode = 1
595
+ }
596
+ } catch (error) {
597
+ clearTimeout(startupTimer)
598
+ clearTimeout(closeTimer)
599
+ clearTimeout(forceTimer)
600
+ if (!finalized) output({ type: 'result', status: 'ERROR', response: '', error: errorMessage(error) })
601
+ process.exitCode = 1
602
+ }
@@ -0,0 +1,15 @@
1
+ import { homedir } from 'node:os'
2
+ import { basename, dirname, relative, resolve } from 'node:path'
3
+
4
+ /** OMP 16 resolves its config root with join(os.homedir(), PI_CONFIG_DIR).
5
+ * Windows roots on another drive cannot be expressed relative to the real home.
6
+ * Rebase only this child environment; Host account discovery retains the real home.
7
+ */
8
+ export function ompConfigurationEnvironment(configRoot) {
9
+ if (typeof configRoot !== 'string' || !configRoot.trim() || configRoot.includes('\0'))
10
+ throw new Error('Invalid OMP configuration root')
11
+ const root = resolve(configRoot)
12
+ if (process.platform === 'win32')
13
+ return { HOME: dirname(root), USERPROFILE: dirname(root), PI_CONFIG_DIR: basename(root) }
14
+ return { PI_CONFIG_DIR: relative(homedir(), root) }
15
+ }