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,945 @@
1
+ import { readClaudeOwnRoute, claudeNativeSettingsArguments, claudeRouteStatusArguments, claudeRouteIdentity, ClaudeOwnRouteError, } from "./claude-own-route.js";
2
+ import { isExtendedCli } from "./extended-adapters.js";
3
+ import { nativeAgyAccountIdentity } from "./agy-windows-account.js";
4
+ import { firstPartyEnvironment, MimoConfigurationError } from "./first-party-models.js";
5
+ import { randomUUID } from 'node:crypto';
6
+ import { StringDecoder } from 'node:string_decoder';
7
+ import { stripVTControlCharacters } from 'node:util';
8
+ import { resolveCliExecutable } from "./adapters.js";
9
+ import { projectDirectory, ProcessCleanupUnconfirmedError, } from "./process.js";
10
+ import { CLI_IDS } from "../shared/types.js";
11
+ import { accountEmail, } from "./account-identity.js";
12
+ import { readCodexAccount } from "./codex-account.js";
13
+ import { homedir } from 'node:os';
14
+ import { join } from 'node:path';
15
+ import { zcodeGrokAccountLaunch, zcodeGrokAccountStatus, zcodeGrokInstruction, ZCodeAccountCapabilityError, } from "./zcode-grok-accounts.js";
16
+ import { readPiOmpAccount, readOpenCodeAccount, prepareOpenCodeAccount } from "./harness-opencode-accounts.js";
17
+ import { prepareHermesAccount, readHermesAccount, listHermesAccountSources } from "./hermes-accounts.js";
18
+ import { listPiOmpAccountSources } from "./pi-omp-native.js";
19
+ import { verifyHermesExecutable, HermesExecutableError } from "./hermes-installation.js";
20
+ import { preparePiOmpAccountTerminal } from "./pi-omp-accounts.js";
21
+ import { readKimiAccount, KIMI_MANAGE_LOGIN, prepareKimiAccountDirectory } from "./kimi-accounts.js";
22
+ import { readMimoAccount } from "./mimo-configuration.js";
23
+ import { prepareMimoAccountTerminal } from "./first-party-account-context.js";
24
+ import { spawnAccountTerminal, WindowsAccountTerminalError } from "./windows-account-terminal.js";
25
+ import { WindowsCliInstallationError } from "./windows-cli-installation.js";
26
+ import { effectiveHermesHome } from "./hermes-account-context.js";
27
+ import { ensureHermesNousFresh, HermesRenewalError, reserveHermesAccount } from "./hermes-renewal.js";
28
+ const STATUS_TIMEOUT = 10_000;
29
+ const STARTUP_TIMEOUT = 30_000;
30
+ const TERMINAL_TIMEOUT = 30 * 60_000;
31
+ const MAX_OUTPUT_BYTES = 256 * 1024;
32
+ const MAX_FRAMES = 512;
33
+ const MAX_INPUT_BYTES = 16 * 1024;
34
+ const ACTIONS = ['login', 'logout', 'manage'];
35
+ const validateCli = (cli) => {
36
+ if (!CLI_IDS.includes(cli))
37
+ throw new Error('不支持的 CLI');
38
+ };
39
+ const instructionFor = (cli, action) => {
40
+ if (cli === 'zcode' || cli === 'grok')
41
+ return zcodeGrokInstruction(cli, action);
42
+ if (cli === 'antigravity')
43
+ return action === 'manage'
44
+ ? '在 Antigravity 原生终端中使用 /login 登录,或 /logout 退出。'
45
+ : `在 Antigravity 原生终端中输入 /${action} 并按 Enter。`;
46
+ if (cli === 'kimi' && action !== 'login')
47
+ return action === 'logout'
48
+ ? '在 Kimi 原生终端中输入 /logout 并按 Enter,再选择要退出的提供商。'
49
+ : '在 Kimi 原生终端中使用 /login 或 /logout 管理提供商与账号。';
50
+ if (action === 'manage')
51
+ return '在原生 CLI 中管理账号。';
52
+ return action === 'login'
53
+ ? '按 CLI 原生提示完成登录;需要浏览器授权时由 CLI 打开浏览器。'
54
+ : '按 CLI 原生流程退出登录。';
55
+ };
56
+ const actionsFor = (cli, config) => {
57
+ if (cli === 'claude')
58
+ return [
59
+ {
60
+ id: 'login',
61
+ label: '官方登录 / 切换账号',
62
+ description: '打开 Claude 原生官方登录;保留本地代理设置。',
63
+ },
64
+ { id: 'logout', label: '退出官方账号', description: '仅退出 Claude 官方账号;本地代理设置保留。' },
65
+ { id: 'manage', label: '官方账号终端', description: '打开 Claude 官方账号原生终端。' },
66
+ ];
67
+ if (cli === 'pi' || cli === 'omp' || cli === 'hermes')
68
+ return [
69
+ { id: 'login', label: '登录设置', description: '打开原生终端登录界面,选择提供商后由你完成授权。' },
70
+ { id: 'logout', label: '退出登录', description: '选择当前账号来源,在原生菜单中退出或移除凭据。' },
71
+ { id: 'manage', label: '账号终端', description: '打开原生终端管理账号。' },
72
+ ];
73
+ return ACTIONS.map((id) => ({
74
+ id,
75
+ label: id === 'login'
76
+ ? cli === 'opencode' || cli === 'kimi'
77
+ ? '登录设置'
78
+ : '登录 / 切换账号'
79
+ : id === 'logout'
80
+ ? '退出登录'
81
+ : '账号终端',
82
+ description: instructionFor(cli, id),
83
+ }));
84
+ };
85
+ /** Only commands verified against the installed CLIs. Never send a prompt/slash command. */
86
+ const argumentsFor = (cli, action) => {
87
+ if (cli === 'antigravity' || action === 'manage' || (cli === 'kimi' && action === 'logout'))
88
+ return [];
89
+ if (cli === 'codex' || cli === 'kimi')
90
+ return [action];
91
+ return ['auth', action];
92
+ };
93
+ /** No raw command output, exception text, account tokens, or key prefixes cross this boundary. */
94
+ function summarize(cli, raw, exitCode) {
95
+ const text = stripVTControlCharacters(raw);
96
+ if (exitCode !== 0 &&
97
+ /\b(?:authentication failed|invalid (?:api key|credentials|token)|(?:credentials|token) (?:have |has )?expired|401\s+Unauthorized)\b/i.test(text))
98
+ return { state: 'unauthenticated', summary: 'CLI 报告认证失效,请重新登录或检查凭据' };
99
+ if (cli === 'codex') {
100
+ if (exitCode === 0 && /\bLogged in using ChatGPT\b/.test(text))
101
+ return {
102
+ state: 'authenticated',
103
+ summary: '已通过 ChatGPT 登录',
104
+ authMethod: 'oauth',
105
+ verification: 'cli',
106
+ };
107
+ if (exitCode === 0 && /\bLogged in using (?:an )?API key\b/i.test(text))
108
+ return { state: 'authenticated', summary: 'API 登录', authMethod: 'api', verification: 'cli' };
109
+ if (/^Not logged in\s*$/m.test(text))
110
+ return { state: 'unconfigured', summary: '尚未登录' };
111
+ }
112
+ else if (cli === 'claude') {
113
+ try {
114
+ const value = JSON.parse(text);
115
+ if (value.loggedIn === false)
116
+ return { state: 'unconfigured', summary: '尚未登录' };
117
+ if (exitCode === 0 && value.loggedIn === true) {
118
+ // Claude's installed auth/status implementation only returns email for
119
+ // claude.ai sessions. Environment OAuth tokens do not identify an account.
120
+ const oauth = value.authMethod === 'claude.ai';
121
+ const api = value.authMethod === 'api_key' || value.authMethod === 'api_key_helper';
122
+ if (value.authMethod === 'oauth_token')
123
+ return { state: 'configured', summary: 'CLI 已配置令牌,账号类型待确认', verification: 'cli' };
124
+ return {
125
+ state: 'authenticated',
126
+ summary: api ? 'API 登录' : oauth ? '已通过 OAuth 登录' : 'CLI 报告已登录',
127
+ authMethod: api ? 'api' : oauth ? 'oauth' : undefined,
128
+ accountLabel: value.authMethod === 'claude.ai' ? accountEmail(value.email) : undefined,
129
+ verification: 'cli',
130
+ };
131
+ }
132
+ }
133
+ catch {
134
+ return { state: 'unavailable', summary: '账号状态响应无法解析,请在账号终端检查' };
135
+ }
136
+ }
137
+ else if (cli === 'mimo') {
138
+ if (exitCode === 0 && /Provider: MiMo\b/.test(text)) {
139
+ // Installed whoami source emits User ID only in the type === 'api'
140
+ // metadata branch. Never expose that metadata or a key prefix for API auth.
141
+ if (/\bType:\s*api\b|\bUser ID:/.test(text))
142
+ return {
143
+ state: 'authenticated',
144
+ summary: 'API 登录',
145
+ authMethod: 'api',
146
+ verification: 'local',
147
+ };
148
+ return { state: 'configured', summary: '已配置 MiMo 账号', verification: 'cli' };
149
+ }
150
+ if (/Not logged in\. Run `mimo auth login` to log in\./.test(text))
151
+ return { state: 'unconfigured', summary: '尚未登录 MiMo' };
152
+ }
153
+ if (exitCode !== 0)
154
+ return { state: 'unavailable', summary: 'CLI 账号状态查询失败,请在账号终端检查' };
155
+ return { state: 'unknown', summary: '登录状态未知,请打开账号终端' };
156
+ }
157
+ function abortable(promise, signal) {
158
+ signal.throwIfAborted();
159
+ return new Promise((resolve, reject) => {
160
+ const cancel = () => reject(signal.reason);
161
+ signal.addEventListener('abort', cancel, { once: true });
162
+ promise.then(resolve, reject).finally(() => signal.removeEventListener('abort', cancel));
163
+ });
164
+ }
165
+ /** User-only auth processes. Only allowlisted identity metadata leaves the Host. */
166
+ export class AccountManager {
167
+ backend;
168
+ config;
169
+ identity;
170
+ sessions = new Map();
171
+ reserved = new Set();
172
+ unsafeQueries = new Set();
173
+ unsafeTerminals = new Map();
174
+ statusQueries = new Map();
175
+ pending = new Set();
176
+ parents = new Map();
177
+ parentClosings = new Map();
178
+ controller = new AbortController();
179
+ closing;
180
+ constructor(backend, config, identity = nativeAgyAccountIdentity(backend, config)) {
181
+ this.backend = backend;
182
+ this.config = config;
183
+ this.identity = identity;
184
+ }
185
+ isBusy(cli) {
186
+ validateCli(cli);
187
+ return this.reserved.has(cli) || this.unsafeQueries.has(cli) || this.unsafeTerminals.has(cli);
188
+ }
189
+ async status(cli, cwd, signal) {
190
+ validateCli(cli);
191
+ if (signal.aborted || this.controller.signal.aborted)
192
+ throw new Error('账号状态查询已取消');
193
+ const directory = projectDirectory(cwd);
194
+ const key = JSON.stringify([cli, directory]);
195
+ let query = this.statusQueries.get(key);
196
+ if (query && [...query.subscribers].every((reader) => reader.signal.aborted))
197
+ query.controller.abort();
198
+ // The abandoned process still owns cleanup; a new reader must not overlap it.
199
+ if (query?.controller.signal.aborted) {
200
+ await abortable(query.operation.catch(() => undefined), signal);
201
+ return this.status(cli, directory, signal);
202
+ }
203
+ if (!query) {
204
+ const controller = new AbortController();
205
+ query = {
206
+ cli,
207
+ controller,
208
+ operation: this.track(this.readStatus(cli, directory, controller.signal)),
209
+ subscribers: new Set(),
210
+ settled: false,
211
+ };
212
+ this.statusQueries.set(key, query);
213
+ const current = query;
214
+ void current.operation
215
+ .finally(() => {
216
+ current.settled = true;
217
+ if (this.statusQueries.get(key) === current)
218
+ this.statusQueries.delete(key);
219
+ })
220
+ .catch(() => undefined);
221
+ }
222
+ const reader = { signal };
223
+ query.subscribers.add(reader);
224
+ try {
225
+ // Share only the running read, never a completed login/authorization result.
226
+ return await abortable(query.operation, signal);
227
+ }
228
+ catch (error) {
229
+ if (signal.aborted)
230
+ throw new Error('账号状态查询已取消');
231
+ throw error;
232
+ }
233
+ finally {
234
+ query.subscribers.delete(reader);
235
+ if (!query.subscribers.size && !query.settled) {
236
+ query.controller.abort();
237
+ await query.operation.catch(() => undefined);
238
+ }
239
+ }
240
+ }
241
+ async readStatus(cli, cwd, signal) {
242
+ if (this.unsafeTerminals.has(cli))
243
+ return {
244
+ cli,
245
+ installed: true,
246
+ state: 'unavailable',
247
+ summary: '账号终端尚未确认完全退出,请重启应用',
248
+ actions: [],
249
+ };
250
+ if (this.unsafeQueries.has(cli))
251
+ return {
252
+ cli,
253
+ installed: true,
254
+ state: 'unavailable',
255
+ summary: '账号查询未完成清理,请重启应用',
256
+ actions: [],
257
+ };
258
+ const timer = new AbortController();
259
+ const timeout = setTimeout(() => timer.abort(), STATUS_TIMEOUT);
260
+ timeout.unref?.();
261
+ const control = AbortSignal.any([signal, this.controller.signal, timer.signal]);
262
+ let installed = false;
263
+ try {
264
+ const executable = await resolveCliExecutable(cli, this.backend, this.config, control);
265
+ installed = true;
266
+ control.throwIfAborted();
267
+ if (cli === 'mimo')
268
+ return {
269
+ cli,
270
+ installed,
271
+ ...(await readMimoAccount(control)),
272
+ actions: this.backend.spawnTerminal ? actionsFor(cli, this.config) : [],
273
+ };
274
+ if (cli === 'hermes') {
275
+ await verifyHermesExecutable(executable, control);
276
+ const local = await readHermesAccount(this.config, control);
277
+ if (local.logins?.some((login) => login.providerLabel === 'Nous') ||
278
+ local.summary === 'Hermes 登录已保存,等待自动续期')
279
+ await ensureHermesNousFresh({
280
+ home: effectiveHermesHome(this.config), executable,
281
+ backend: this.backend, signal: control, graceMs: this.config.graceMs,
282
+ });
283
+ }
284
+ if (isExtendedCli(cli)) {
285
+ const identity = await abortable(cli === 'zcode' || cli === 'grok'
286
+ ? zcodeGrokAccountStatus(cli, this.config, control)
287
+ : cli === 'opencode'
288
+ ? readOpenCodeAccount(this.config, control)
289
+ : cli === 'hermes'
290
+ ? readHermesAccount(this.config, control)
291
+ : readPiOmpAccount(cli, this.config, control), control);
292
+ const sources = cli === 'pi' || cli === 'omp'
293
+ ? await listPiOmpAccountSources(cli, this.config.stateDirectory, control)
294
+ : cli === 'hermes'
295
+ ? await listHermesAccountSources(this.config, control)
296
+ : undefined;
297
+ return {
298
+ cli,
299
+ installed,
300
+ ...identity,
301
+ actions: actionsFor(cli, this.config)
302
+ .map((item) => (item.id === 'logout' && sources ? { ...item, sources } : item))
303
+ .filter((item) => item.target === 'models' || this.backend.spawnTerminal),
304
+ };
305
+ }
306
+ if (cli === 'antigravity') {
307
+ const identity = await abortable(this.identity(cli, control), control);
308
+ return {
309
+ cli,
310
+ installed,
311
+ ...(identity ?? {
312
+ state: 'unknown',
313
+ summary: '已安装,登录状态未知',
314
+ }),
315
+ actions: this.backend.spawnTerminal ? actionsFor(cli, this.config) : [],
316
+ };
317
+ }
318
+ if (cli === 'codex') {
319
+ // One effective-store read handles file/keyring accounts and display
320
+ // identity. Older servers fall back to the native status command below.
321
+ const identity = await readCodexAccount(this.backend, this.config, executable, cwd, control);
322
+ if (identity)
323
+ return {
324
+ cli,
325
+ installed,
326
+ ...identity,
327
+ actions: this.backend.spawnTerminal ? actionsFor(cli, this.config) : [],
328
+ };
329
+ }
330
+ const claudeRoute = cli === 'claude' ? await readClaudeOwnRoute(cwd, control) : undefined;
331
+ const argv = cli === 'codex'
332
+ ? ['login', 'status']
333
+ : cli === 'claude'
334
+ ? ['auth', 'status', '--json']
335
+ : cli === 'kimi'
336
+ ? ['provider', 'list', '--json']
337
+ : ['auth', 'whoami'];
338
+ const child = this.backend.spawn({
339
+ argv: claudeRoute ? claudeRouteStatusArguments(executable, claudeRoute) : [executable, ...argv],
340
+ env: firstPartyEnvironment(cli),
341
+ cwd,
342
+ stdio: { stdin: 'ignore', stdout: 'pipe', stderr: 'pipe' },
343
+ graceMs: this.config.graceMs,
344
+ signal: control,
345
+ });
346
+ void child.done.catch(() => undefined);
347
+ let raw = '', bytes = 0;
348
+ const read = async (stream) => {
349
+ if (!stream)
350
+ return;
351
+ const decoder = new StringDecoder('utf8');
352
+ for await (const chunk of stream) {
353
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
354
+ bytes += buffer.byteLength;
355
+ if (bytes > 64 * 1024)
356
+ throw new Error('状态响应超过限制');
357
+ raw += decoder.write(buffer);
358
+ }
359
+ raw += decoder.end();
360
+ };
361
+ const readers = [read(child.stdout), read(child.stderr)];
362
+ try {
363
+ const [outcome] = await abortable(Promise.all([child.done, ...readers]), control);
364
+ const status = claudeRoute
365
+ ? claudeRouteIdentity(claudeRoute, raw, outcome.exitCode)
366
+ : cli === 'kimi' && outcome.exitCode === 0
367
+ ? await readKimiAccount(raw, control)
368
+ : summarize(cli, raw, outcome.exitCode);
369
+ return {
370
+ cli,
371
+ installed,
372
+ ...status,
373
+ actions: this.backend.spawnTerminal
374
+ ? actionsFor(cli, this.config).map((item) => cli === 'kimi' && item.id === 'login' && status.state === 'authenticated'
375
+ ? { ...item, label: '管理登录', description: '在原生终端管理 Kimi 登录' }
376
+ : item)
377
+ : [],
378
+ };
379
+ }
380
+ finally {
381
+ try {
382
+ child.terminate();
383
+ if (!(await child.waitForExit()))
384
+ throw new ProcessCleanupUnconfirmedError();
385
+ }
386
+ catch {
387
+ throw new ProcessCleanupUnconfirmedError();
388
+ }
389
+ await Promise.allSettled([child.done, ...readers]);
390
+ raw = '';
391
+ }
392
+ }
393
+ catch (error) {
394
+ if (error instanceof ProcessCleanupUnconfirmedError) {
395
+ this.unsafeQueries.add(cli);
396
+ return {
397
+ cli,
398
+ installed,
399
+ state: 'unavailable',
400
+ summary: '账号查询未完成清理,请重启应用',
401
+ actions: [],
402
+ };
403
+ }
404
+ if (signal.aborted || this.controller.signal.aborted)
405
+ throw new Error('账号状态查询已取消');
406
+ const code = error?.code;
407
+ if (error instanceof WindowsCliInstallationError)
408
+ return { cli, installed: error.installed, state: 'unavailable', summary: error.message, actions: [] };
409
+ if (error instanceof ClaudeOwnRouteError)
410
+ return {
411
+ cli,
412
+ installed,
413
+ state: 'unavailable',
414
+ summary: error.message,
415
+ actions: this.backend.spawnTerminal ? actionsFor(cli, this.config) : [],
416
+ };
417
+ if (error instanceof MimoConfigurationError)
418
+ return { cli, installed, state: 'unavailable', summary: error.message, actions: [] };
419
+ if (error instanceof HermesExecutableError)
420
+ return { cli, installed, state: 'unavailable', summary: error.message, actions: [] };
421
+ if (error instanceof HermesRenewalError)
422
+ return {
423
+ cli, installed, state: error.state === 'relogin' ? 'unauthenticated' : 'unavailable',
424
+ verification: 'local', summary: error.message, actions: actionsFor(cli, this.config),
425
+ };
426
+ if (code === 'CLI_LAUNCHER_UNSUPPORTED')
427
+ return {
428
+ cli,
429
+ installed: true,
430
+ state: 'unavailable',
431
+ summary: 'CLI 启动入口无法确认,请配置原生程序或固定 Node/Bun 入口',
432
+ actions: [],
433
+ };
434
+ if (code === 'CLI_IDENTITY_MISMATCH')
435
+ return {
436
+ cli,
437
+ installed: true,
438
+ state: 'unavailable',
439
+ summary: `${cli === 'omp' ? 'OMP' : 'Pi'} 执行入口身份不匹配,请检查该 CLI 的安装和路径`,
440
+ actions: [],
441
+ };
442
+ const failed = installed ||
443
+ this.hasCustomExecutable(cli) ||
444
+ timer.signal.aborted ||
445
+ (!!code && code !== 'ENOENT' && code !== 'ENOTDIR');
446
+ return {
447
+ cli,
448
+ installed,
449
+ state: failed ? 'unavailable' : 'unconfigured',
450
+ summary: installed
451
+ ? '账号状态暂不可用,可打开账号终端检查'
452
+ : this.hasCustomExecutable(cli)
453
+ ? 'CLI 执行路径配置失败,请检查可执行路径'
454
+ : timer.signal.aborted
455
+ ? 'CLI 安装探测超时,请刷新重试'
456
+ : failed
457
+ ? 'CLI 安装状态读取失败,请检查安装'
458
+ : '尚未安装 CLI,请检查安装',
459
+ actions: installed && this.backend.spawnTerminal ? actionsFor(cli, this.config) : [],
460
+ };
461
+ }
462
+ finally {
463
+ clearTimeout(timeout);
464
+ }
465
+ }
466
+ hasCustomExecutable(cli) {
467
+ if (cli === 'antigravity')
468
+ return this.config.executable !== 'agy';
469
+ if (cli === 'harness')
470
+ return false;
471
+ const configured = this.config[`${cli}Executable`];
472
+ return !!configured && configured !== cli;
473
+ }
474
+ start(parent, cli, action, cwd, signal, source, provider) {
475
+ validateCli(cli);
476
+ if (!ACTIONS.includes(action))
477
+ throw new Error('不支持的账号操作');
478
+ if (source !== undefined &&
479
+ (!['native', 'plugin'].includes(source) ||
480
+ action !== 'logout' ||
481
+ !['pi', 'omp', 'hermes'].includes(cli)))
482
+ throw new Error('不支持的账号来源');
483
+ if (provider !== undefined &&
484
+ (cli !== 'zcode' ||
485
+ action !== 'login' ||
486
+ source !== undefined ||
487
+ !['bigmodel', 'zai'].includes(provider)))
488
+ throw new Error('不支持的 ZCode 授权来源');
489
+ if (!parent || parent.length > 512)
490
+ throw new Error('无效的父会话');
491
+ this.controller.signal.throwIfAborted();
492
+ signal.throwIfAborted();
493
+ const capability = actionsFor(cli, this.config).find((item) => item.id === action);
494
+ if (!capability || capability.target === 'models')
495
+ throw new Error('此版本 CLI 未提供终端登录或账号管理入口');
496
+ if (!this.backend.spawnTerminal)
497
+ throw new Error('当前宿主不支持交互终端');
498
+ if (this.unsafeTerminals.has(cli))
499
+ throw new Error('账号终端尚未确认完全退出,请重启应用');
500
+ if (this.unsafeQueries.has(cli))
501
+ throw new ProcessCleanupUnconfirmedError();
502
+ if (this.reserved.has(cli))
503
+ throw new Error('此 CLI 已有账号终端,请先关闭后重试');
504
+ const directory = projectDirectory(cwd);
505
+ this.reserved.add(cli);
506
+ for (const query of this.statusQueries.values())
507
+ if (query.cli === cli)
508
+ query.controller.abort();
509
+ let scope = this.parents.get(parent);
510
+ if (!scope) {
511
+ scope = { controller: new AbortController(), pending: new Set() };
512
+ this.parents.set(parent, scope);
513
+ }
514
+ const operation = this.track(this.startSession(parent, cli, action, directory, signal, scope, source, provider));
515
+ scope.pending.add(operation);
516
+ void operation.finally(() => scope.pending.delete(operation)).catch(() => undefined);
517
+ return operation;
518
+ }
519
+ async startSession(parent, cli, action, cwd, signal, scope, source, provider) {
520
+ const timeout = new AbortController();
521
+ const timer = setTimeout(() => timeout.abort(), STARTUP_TIMEOUT);
522
+ timer.unref?.();
523
+ const startup = AbortSignal.any([signal, scope.controller.signal, this.controller.signal, timeout.signal]);
524
+ // Provider cancellation belongs to allocation, not the interactive login's
525
+ // lifetime. A completed login terminal must not inherit this request/timer.
526
+ const allocationControl = new AbortController();
527
+ const cancelAllocation = () => allocationControl.abort();
528
+ startup.addEventListener('abort', cancelAllocation, { once: true });
529
+ let session;
530
+ let allocation;
531
+ let release;
532
+ let instruction = instructionFor(cli, action);
533
+ try {
534
+ const executable = await resolveCliExecutable(cli, this.backend, this.config, startup);
535
+ if (cli === 'hermes')
536
+ await verifyHermesExecutable(executable, startup);
537
+ startup.throwIfAborted();
538
+ let launch = {
539
+ argv: [executable, ...argumentsFor(cli, action)],
540
+ env: firstPartyEnvironment(cli),
541
+ cwd,
542
+ };
543
+ if (cli === 'claude') {
544
+ const claudeAccountRoute = await readClaudeOwnRoute(cwd, startup);
545
+ startup.throwIfAborted();
546
+ // Login/logout always address the native official account; proxy settings remain untouched.
547
+ launch.argv = [
548
+ executable,
549
+ ...claudeNativeSettingsArguments(claudeAccountRoute.configDirectory),
550
+ ...argumentsFor(cli, action),
551
+ ...(action === 'login' ? ['--claudeai'] : []),
552
+ ];
553
+ instruction =
554
+ action === 'logout'
555
+ ? '退出 Claude 官方账号;本地代理设置保留。'
556
+ : '在原生终端登录 Claude 官方账号;本地代理设置保留。';
557
+ }
558
+ else if (cli === 'kimi') {
559
+ if (action === 'login') {
560
+ const current = await this.readStatus(cli, cwd, startup);
561
+ startup.throwIfAborted();
562
+ if (current.state === 'authenticated' && current.authMethod === 'oauth') {
563
+ launch.argv = [executable];
564
+ instruction = KIMI_MANAGE_LOGIN;
565
+ }
566
+ }
567
+ const prepared = await prepareKimiAccountDirectory(this.config.stateDirectory ??
568
+ join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'cliworker-now'), startup);
569
+ launch.cwd = prepared.cwd;
570
+ release = prepared.cleanup;
571
+ if (action !== 'login')
572
+ instruction = `若出现 Trust 提示,请自行确认此空账号目录;${instruction}`;
573
+ }
574
+ else if (cli === 'mimo') {
575
+ const prepared = await prepareMimoAccountTerminal(executable, action, this.config.stateDirectory ??
576
+ join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'cliworker-now'), startup);
577
+ launch = prepared;
578
+ release = prepared.cleanup;
579
+ }
580
+ else if (cli === 'zcode' || cli === 'grok') {
581
+ const prepared = await zcodeGrokAccountLaunch(cli, action, executable, this.config, this.backend, startup, { loginProvider: provider });
582
+ launch = prepared;
583
+ instruction = prepared.instruction;
584
+ }
585
+ else if (cli === 'pi' || cli === 'omp') {
586
+ const prepared = await preparePiOmpAccountTerminal({
587
+ cli,
588
+ action,
589
+ source,
590
+ executable,
591
+ project: cwd,
592
+ stateDirectory: this.config.stateDirectory ??
593
+ join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'cliworker-now'),
594
+ config: this.config,
595
+ signal: startup,
596
+ });
597
+ launch = prepared;
598
+ instruction = prepared.instruction;
599
+ release = prepared.cleanup;
600
+ }
601
+ else if (cli === 'hermes') {
602
+ const oldHome = effectiveHermesHome(this.config);
603
+ const releases = [await reserveHermesAccount(oldHome, startup)];
604
+ release = () => { for (const free of releases)
605
+ free(); };
606
+ const prepared = await prepareHermesAccount(action, executable, cwd, this.config, startup, source);
607
+ const cleanup = release;
608
+ release = async () => { await prepared.cleanup(); await cleanup(); };
609
+ const selectedHome = effectiveHermesHome(this.config);
610
+ if (selectedHome !== oldHome)
611
+ releases.push(await reserveHermesAccount(selectedHome, startup));
612
+ launch = prepared;
613
+ instruction = prepared.instruction;
614
+ }
615
+ else if (cli === 'opencode') {
616
+ const prepared = await prepareOpenCodeAccount(executable, action, this.config, startup);
617
+ launch = prepared;
618
+ instruction = prepared.instruction;
619
+ release = prepared.cleanup;
620
+ }
621
+ startup.throwIfAborted();
622
+ allocation = spawnAccountTerminal(this.backend, {
623
+ ...launch,
624
+ rows: 24,
625
+ cols: 80,
626
+ terminalType: 'xterm-256color',
627
+ graceMs: this.config.graceMs,
628
+ signal: allocationControl.signal,
629
+ }).then(async (terminal) => {
630
+ void terminal.done.catch(() => undefined);
631
+ session = {
632
+ id: randomUUID(),
633
+ parent,
634
+ scope,
635
+ cli,
636
+ terminal,
637
+ frames: [],
638
+ bytes: 0,
639
+ seq: 0,
640
+ state: 'running',
641
+ stopping: false,
642
+ changed: new Set(),
643
+ output: Promise.resolve(),
644
+ release,
645
+ };
646
+ this.sessions.set(session.id, session);
647
+ this.emit(session, { status: 'running' });
648
+ session.output = this.readTerminal(session);
649
+ void session.output.catch(() => undefined);
650
+ // Even if an uncooperative provider ignores cancellation, a late PTY
651
+ // is adopted and cleaned up before releasing its CLI reservation.
652
+ if (startup.aborted) {
653
+ await this.finish(session, 'closed', '启动已取消');
654
+ startup.throwIfAborted();
655
+ }
656
+ return session;
657
+ }, async (error) => {
658
+ try {
659
+ if (!(error instanceof ProcessCleanupUnconfirmedError))
660
+ await release?.();
661
+ }
662
+ finally {
663
+ if (error instanceof ProcessCleanupUnconfirmedError)
664
+ this.unsafeTerminals.set(cli, parent);
665
+ else
666
+ this.reserved.delete(cli);
667
+ }
668
+ throw error;
669
+ });
670
+ this.track(allocation);
671
+ scope.pending.add(allocation);
672
+ const pendingAllocation = allocation;
673
+ void allocation.finally(() => scope.pending.delete(pendingAllocation)).catch(() => undefined);
674
+ session = await abortable(allocation, startup);
675
+ if (startup.aborted) {
676
+ await this.finish(session, 'closed', '启动已取消');
677
+ startup.throwIfAborted();
678
+ }
679
+ const published = session;
680
+ session.deadline = setTimeout(() => {
681
+ void this.finish(published, 'closed', '账号终端已达到 30 分钟上限,请重新打开').catch(() => undefined);
682
+ }, TERMINAL_TIMEOUT);
683
+ session.deadline.unref?.();
684
+ session.attachDeadline = setTimeout(() => {
685
+ void this.finish(published, 'closed', '账号终端未连接,已自动关闭').catch(() => undefined);
686
+ }, 30_000);
687
+ session.attachDeadline.unref?.();
688
+ void session.terminal.done
689
+ .then((outcome) => this.finish(published, outcome.exitCode === 0 ? 'closed' : 'failed', outcome.exitCode === 0
690
+ ? '账号终端已结束'
691
+ : Number.isInteger(outcome.exitCode)
692
+ ? `账号终端异常退出(退出码 ${outcome.exitCode}),请在系统终端检查该 CLI 后重试`
693
+ : '账号终端连接已结束,未返回退出码;请在系统终端检查该 CLI 后重试'), () => this.finish(published, 'failed', '账号终端连接失败,请重新打开'))
694
+ .catch(() => undefined);
695
+ return { id: session.id, instruction };
696
+ }
697
+ catch (error) {
698
+ // An allocation still in flight may own a process already; never permit a
699
+ // second login until its late handle/rejection establishes cleanup.
700
+ if (!session && !allocation) {
701
+ try {
702
+ await release?.();
703
+ }
704
+ finally {
705
+ this.reserved.delete(cli);
706
+ }
707
+ }
708
+ if (session && startup.aborted && !session.cleanup)
709
+ await this.finish(session, 'closed', '启动已取消').catch(() => undefined);
710
+ if (error instanceof Error && error.message === '此 CLI 已有账号终端,请先关闭后重试')
711
+ throw error;
712
+ if (error instanceof ProcessCleanupUnconfirmedError)
713
+ throw new Error('账号终端尚未确认完全退出,请重启应用');
714
+ if (timeout.signal.aborted)
715
+ throw new Error('账号终端启动超时,正在回收启动过程;请稍后重试');
716
+ if (error instanceof ZCodeAccountCapabilityError)
717
+ throw error;
718
+ if (error instanceof WindowsAccountTerminalError)
719
+ throw error;
720
+ if (error instanceof WindowsCliInstallationError)
721
+ throw error;
722
+ if (error instanceof HermesExecutableError)
723
+ throw error;
724
+ if (error instanceof MimoConfigurationError)
725
+ throw error;
726
+ if (error?.code === 'CLI_IDENTITY_MISMATCH')
727
+ throw new Error(`${cli === 'omp' ? 'OMP' : 'Pi'} 执行入口身份不匹配,请检查该 CLI 的安装和路径`);
728
+ throw new Error(session || startup.aborted
729
+ ? '账号终端启动已取消或清理失败,请关闭设置后重试'
730
+ : '无法启动账号终端,请检查 CLI 安装');
731
+ }
732
+ finally {
733
+ clearTimeout(timer);
734
+ startup.removeEventListener('abort', cancelAllocation);
735
+ }
736
+ }
737
+ async readTerminal(session) {
738
+ const decoder = new StringDecoder('utf8');
739
+ try {
740
+ for await (const chunk of session.terminal.output) {
741
+ const text = typeof chunk === 'string' ? chunk : decoder.write(chunk);
742
+ for (let offset = 0; offset < text.length; offset += 4096)
743
+ this.emit(session, { data: text.slice(offset, offset + 4096) });
744
+ }
745
+ const tail = decoder.end();
746
+ if (tail)
747
+ this.emit(session, { data: tail });
748
+ }
749
+ catch {
750
+ // Do not await from this reader: finish waits for the reader to settle.
751
+ void this.finish(session, 'failed', '账号终端输出中断,请重新打开').catch(() => undefined);
752
+ }
753
+ }
754
+ emit(session, frame) {
755
+ const entry = { ...frame, seq: ++session.seq };
756
+ session.frames.push(entry);
757
+ session.bytes += Buffer.byteLength(entry.data ?? '');
758
+ while (session.bytes > MAX_OUTPUT_BYTES || session.frames.length > MAX_FRAMES) {
759
+ const removed = session.frames.shift();
760
+ session.bytes -= Buffer.byteLength(removed.data ?? '');
761
+ }
762
+ for (const notify of session.changed)
763
+ notify();
764
+ session.changed.clear();
765
+ }
766
+ owned(parent, id) {
767
+ const session = this.sessions.get(id);
768
+ if (!session || session.parent !== parent)
769
+ throw new Error('账号终端不存在或不属于当前会话');
770
+ return session;
771
+ }
772
+ async *watch(parent, id, signal) {
773
+ const session = this.owned(parent, id);
774
+ clearTimeout(session.attachDeadline);
775
+ let seq = 0;
776
+ try {
777
+ while (!signal.aborted) {
778
+ const frames = session.frames.filter((frame) => frame.seq > seq);
779
+ if (frames.length && frames[0].seq > seq + 1)
780
+ yield { seq: frames[0].seq - 1, message: '较早的终端输出已从内存释放' };
781
+ for (const frame of frames) {
782
+ seq = frame.seq;
783
+ yield frame;
784
+ }
785
+ if (session.state !== 'running' || signal.aborted)
786
+ return;
787
+ await new Promise((resolve) => {
788
+ const wake = () => {
789
+ signal.removeEventListener('abort', wake);
790
+ session.changed.delete(wake);
791
+ resolve();
792
+ };
793
+ session.changed.add(wake);
794
+ signal.addEventListener('abort', wake, { once: true });
795
+ if (signal.aborted || session.seq > seq || session.state !== 'running')
796
+ wake();
797
+ });
798
+ }
799
+ }
800
+ finally {
801
+ if (session.state === 'running')
802
+ await this.finish(session, 'closed', '账号终端连接已关闭');
803
+ }
804
+ }
805
+ async write(parent, id, data) {
806
+ const session = this.owned(parent, id);
807
+ if (typeof data !== 'string' || Buffer.byteLength(data) > MAX_INPUT_BYTES)
808
+ throw new Error('单次终端输入超过限制');
809
+ if (session.state !== 'running' || session.stopping)
810
+ throw new Error('账号终端已关闭');
811
+ try {
812
+ await session.terminal.write(data);
813
+ }
814
+ catch {
815
+ throw new Error('无法写入账号终端,请重新打开');
816
+ }
817
+ }
818
+ async resize(parent, id, cols, rows) {
819
+ const session = this.owned(parent, id);
820
+ if (!Number.isInteger(cols) ||
821
+ !Number.isInteger(rows) ||
822
+ cols < 10 ||
823
+ cols > 500 ||
824
+ rows < 5 ||
825
+ rows > 200)
826
+ throw new Error('无效的终端尺寸');
827
+ if (session.state !== 'running' || session.stopping)
828
+ return;
829
+ try {
830
+ await session.terminal.resize(cols, rows);
831
+ }
832
+ catch {
833
+ throw new Error('无法调整账号终端尺寸,请重新打开');
834
+ }
835
+ }
836
+ async stop(parent, id) {
837
+ await this.finish(this.owned(parent, id), 'closed', '账号终端已关闭');
838
+ }
839
+ /** Close one Agent lifetime, including startup; a later Agent with the same ID gets a fresh scope. */
840
+ closeParent(parent) {
841
+ const scope = this.parents.get(parent);
842
+ const previous = this.parentClosings.get(parent);
843
+ if (!scope && previous)
844
+ return previous;
845
+ if (scope) {
846
+ this.parents.delete(parent);
847
+ scope.controller.abort();
848
+ }
849
+ // Capture this generation before yielding. Never close a newly reactivated
850
+ // parent merely because its ID equals the one whose disposal is in flight.
851
+ const captured = [...this.sessions.values()].filter((session) => session.parent === parent && (!scope || session.scope === scope || session.state === 'failed'));
852
+ const operation = (async () => {
853
+ await Promise.allSettled(scope ? [...scope.pending] : []);
854
+ const owned = new Set(captured);
855
+ if (scope)
856
+ for (const session of this.sessions.values())
857
+ if (session.scope === scope)
858
+ owned.add(session);
859
+ const cleanup = [...owned].map(async (session) => {
860
+ await this.finish(session, 'closed', '主会话已关闭');
861
+ clearTimeout(session.expiry);
862
+ session.frames.length = 0;
863
+ this.sessions.delete(session.id);
864
+ });
865
+ if (previous)
866
+ cleanup.push(previous);
867
+ const results = await Promise.allSettled(cleanup);
868
+ if (results.some((result) => result.status === 'rejected') ||
869
+ [...this.unsafeTerminals.values()].includes(parent))
870
+ throw new Error('此主会话的账号终端尚未确认完全退出');
871
+ })();
872
+ this.parentClosings.set(parent, operation);
873
+ void operation
874
+ .finally(() => {
875
+ if (this.parentClosings.get(parent) === operation)
876
+ this.parentClosings.delete(parent);
877
+ })
878
+ .catch(() => undefined);
879
+ return operation;
880
+ }
881
+ finish(session, state, message) {
882
+ if (session.cleanup)
883
+ return session.cleanup;
884
+ session.stopping = true;
885
+ clearTimeout(session.deadline);
886
+ clearTimeout(session.attachDeadline);
887
+ session.cleanup = (async () => {
888
+ try {
889
+ await session.terminal.terminate();
890
+ await session.output;
891
+ await session.release?.();
892
+ session.state = state;
893
+ this.emit(session, { status: state, message, cleanupConfirmed: true });
894
+ this.reserved.delete(session.cli);
895
+ session.expiry = setTimeout(() => {
896
+ session.frames.length = 0;
897
+ this.sessions.delete(session.id);
898
+ }, 60_000);
899
+ session.expiry.unref?.();
900
+ const retained = [...this.sessions.values()].filter((item) => item.expiry !== undefined);
901
+ for (const stale of retained.slice(0, Math.max(0, retained.length - 16))) {
902
+ clearTimeout(stale.expiry);
903
+ stale.frames.length = 0;
904
+ this.sessions.delete(stale.id);
905
+ }
906
+ }
907
+ catch {
908
+ session.state = 'failed';
909
+ this.emit(session, { status: 'failed', message: '账号终端清理失败,请重试关闭' });
910
+ session.cleanup = undefined;
911
+ throw new Error('账号终端尚未确认完全退出');
912
+ }
913
+ })();
914
+ return session.cleanup;
915
+ }
916
+ track(operation) {
917
+ this.pending.add(operation);
918
+ void operation.finally(() => this.pending.delete(operation)).catch(() => undefined);
919
+ return operation;
920
+ }
921
+ close() {
922
+ if (this.closing)
923
+ return this.closing;
924
+ this.controller.abort();
925
+ this.closing = (async () => {
926
+ await Promise.allSettled([...this.pending]);
927
+ await Promise.allSettled([...this.parentClosings.values()]);
928
+ const results = await Promise.allSettled([...this.sessions.values()].map((session) => this.finish(session, 'closed', '插件已关闭')));
929
+ for (const session of this.sessions.values()) {
930
+ clearTimeout(session.deadline);
931
+ clearTimeout(session.attachDeadline);
932
+ clearTimeout(session.expiry);
933
+ session.frames.length = 0;
934
+ }
935
+ const failures = results.filter((result) => result.status === 'rejected');
936
+ if (failures.length || this.unsafeTerminals.size)
937
+ throw new Error('部分账号终端尚未确认完全退出');
938
+ this.sessions.clear();
939
+ this.reserved.clear();
940
+ this.parents.clear();
941
+ })();
942
+ return this.closing;
943
+ }
944
+ }
945
+ //# sourceMappingURL=accounts.js.map