@armadra/agent 0.0.0-stage → 0.2.1

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 (407) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/LICENSE +21 -0
  3. package/README.md +426 -2
  4. package/dist/agent/agent.d.ts +56 -0
  5. package/dist/agent/agent.js +129 -0
  6. package/dist/agent/loop.d.ts +51 -0
  7. package/dist/agent/loop.js +158 -0
  8. package/dist/agent/queue.d.ts +25 -0
  9. package/dist/agent/queue.js +55 -0
  10. package/dist/agent/retry.d.ts +22 -0
  11. package/dist/agent/retry.js +92 -0
  12. package/dist/agent/schema.d.ts +27 -0
  13. package/dist/agent/schema.js +186 -0
  14. package/dist/agent/session-cache.d.ts +100 -0
  15. package/dist/agent/session-cache.js +511 -0
  16. package/dist/agent/session-compaction.d.ts +49 -0
  17. package/dist/agent/session-compaction.js +251 -0
  18. package/dist/agent/session-core.d.ts +103 -0
  19. package/dist/agent/session-core.js +7 -0
  20. package/dist/agent/session-run.d.ts +55 -0
  21. package/dist/agent/session-run.js +246 -0
  22. package/dist/agent/session-state.d.ts +39 -0
  23. package/dist/agent/session-state.js +118 -0
  24. package/dist/agent/session-subagent.d.ts +54 -0
  25. package/dist/agent/session-subagent.js +157 -0
  26. package/dist/agent/session-sync.d.ts +20 -0
  27. package/dist/agent/session-sync.js +104 -0
  28. package/dist/agent/session-tools.d.ts +24 -0
  29. package/dist/agent/session-tools.js +277 -0
  30. package/dist/agent/session.d.ts +117 -0
  31. package/dist/agent/session.js +484 -0
  32. package/dist/agent/system-prompt.d.ts +49 -0
  33. package/dist/agent/system-prompt.js +121 -0
  34. package/dist/agent/tool-runner.d.ts +64 -0
  35. package/dist/agent/tool-runner.js +309 -0
  36. package/dist/agent/transform.d.ts +26 -0
  37. package/dist/agent/transform.js +171 -0
  38. package/dist/agent/types.d.ts +378 -0
  39. package/dist/agent/types.js +21 -0
  40. package/dist/ai/apis/anthropic-messages.d.ts +26 -0
  41. package/dist/ai/apis/anthropic-messages.js +214 -0
  42. package/dist/ai/apis/anthropic-request.d.ts +42 -0
  43. package/dist/ai/apis/anthropic-request.js +282 -0
  44. package/dist/ai/apis/api.d.ts +28 -0
  45. package/dist/ai/apis/api.js +102 -0
  46. package/dist/ai/apis/cache-params.d.ts +48 -0
  47. package/dist/ai/apis/cache-params.js +163 -0
  48. package/dist/ai/apis/google-generative-ai.d.ts +32 -0
  49. package/dist/ai/apis/google-generative-ai.js +249 -0
  50. package/dist/ai/apis/google-request.d.ts +40 -0
  51. package/dist/ai/apis/google-request.js +255 -0
  52. package/dist/ai/apis/openai-compat.d.ts +35 -0
  53. package/dist/ai/apis/openai-compat.js +142 -0
  54. package/dist/ai/apis/openai-completions.d.ts +28 -0
  55. package/dist/ai/apis/openai-completions.js +246 -0
  56. package/dist/ai/apis/openai-request.d.ts +35 -0
  57. package/dist/ai/apis/openai-request.js +303 -0
  58. package/dist/ai/apis/openai-responses-request.d.ts +52 -0
  59. package/dist/ai/apis/openai-responses-request.js +266 -0
  60. package/dist/ai/apis/openai-responses.d.ts +28 -0
  61. package/dist/ai/apis/openai-responses.js +303 -0
  62. package/dist/ai/apis/shared.d.ts +56 -0
  63. package/dist/ai/apis/shared.js +179 -0
  64. package/dist/ai/cache/economics.d.ts +24 -0
  65. package/dist/ai/cache/economics.js +69 -0
  66. package/dist/ai/cache/fingerprint.d.ts +17 -0
  67. package/dist/ai/cache/fingerprint.js +39 -0
  68. package/dist/ai/cache/miss.d.ts +49 -0
  69. package/dist/ai/cache/miss.js +96 -0
  70. package/dist/ai/cache/reporting.d.ts +39 -0
  71. package/dist/ai/cache/reporting.js +102 -0
  72. package/dist/ai/cache/types.d.ts +94 -0
  73. package/dist/ai/cache/types.js +19 -0
  74. package/dist/ai/cache/warmer.d.ts +94 -0
  75. package/dist/ai/cache/warmer.js +224 -0
  76. package/dist/ai/context.d.ts +35 -0
  77. package/dist/ai/context.js +114 -0
  78. package/dist/ai/cost.d.ts +21 -0
  79. package/dist/ai/cost.js +60 -0
  80. package/dist/ai/event-stream.d.ts +33 -0
  81. package/dist/ai/event-stream.js +101 -0
  82. package/dist/ai/fake/fake-provider.d.ts +68 -0
  83. package/dist/ai/fake/fake-provider.js +278 -0
  84. package/dist/ai/fake/fake-script.d.ts +69 -0
  85. package/dist/ai/fake/fake-script.js +142 -0
  86. package/dist/ai/http.d.ts +54 -0
  87. package/dist/ai/http.js +212 -0
  88. package/dist/ai/json-partial.d.ts +17 -0
  89. package/dist/ai/json-partial.js +243 -0
  90. package/dist/ai/overflow.d.ts +22 -0
  91. package/dist/ai/overflow.js +61 -0
  92. package/dist/ai/providers/auth.d.ts +71 -0
  93. package/dist/ai/providers/auth.js +202 -0
  94. package/dist/ai/providers/builtin.d.ts +23 -0
  95. package/dist/ai/providers/builtin.js +148 -0
  96. package/dist/ai/providers/catalog-data.d.ts +5 -0
  97. package/dist/ai/providers/catalog-data.js +19 -0
  98. package/dist/ai/providers/catalog.d.ts +28 -0
  99. package/dist/ai/providers/catalog.js +166 -0
  100. package/dist/ai/providers/registry.d.ts +83 -0
  101. package/dist/ai/providers/registry.js +305 -0
  102. package/dist/ai/sse.d.ts +32 -0
  103. package/dist/ai/sse.js +140 -0
  104. package/dist/ai/thinking.d.ts +42 -0
  105. package/dist/ai/thinking.js +101 -0
  106. package/dist/ai/types.d.ts +428 -0
  107. package/dist/ai/types.js +19 -0
  108. package/dist/bundle/ama-sandbox.cjs +324 -0
  109. package/dist/bundle/ama.cjs +25770 -0
  110. package/dist/bundle.d.ts +9 -0
  111. package/dist/bundle.js +15 -0
  112. package/dist/cli/args.d.ts +82 -0
  113. package/dist/cli/args.js +381 -0
  114. package/dist/cli/bootstrap.d.ts +18 -0
  115. package/dist/cli/bootstrap.js +398 -0
  116. package/dist/cli/compose-providers.d.ts +37 -0
  117. package/dist/cli/compose-providers.js +104 -0
  118. package/dist/cli/compose-session.d.ts +69 -0
  119. package/dist/cli/compose-session.js +370 -0
  120. package/dist/cli/compose-store.d.ts +24 -0
  121. package/dist/cli/compose-store.js +125 -0
  122. package/dist/cli/compose.d.ts +106 -0
  123. package/dist/cli/compose.js +206 -0
  124. package/dist/cli/default-model.d.ts +16 -0
  125. package/dist/cli/default-model.js +24 -0
  126. package/dist/cli/deps.d.ts +205 -0
  127. package/dist/cli/deps.js +12 -0
  128. package/dist/cli/exit-codes.d.ts +29 -0
  129. package/dist/cli/exit-codes.js +40 -0
  130. package/dist/cli/main.d.ts +26 -0
  131. package/dist/cli/main.js +165 -0
  132. package/dist/cli/runtime.d.ts +90 -0
  133. package/dist/cli/runtime.js +8 -0
  134. package/dist/cli/startup-screen.d.ts +15 -0
  135. package/dist/cli/startup-screen.js +56 -0
  136. package/dist/cli/startup-steps.d.ts +35 -0
  137. package/dist/cli/startup-steps.js +197 -0
  138. package/dist/cli/subcommands/auth.d.ts +12 -0
  139. package/dist/cli/subcommands/auth.js +91 -0
  140. package/dist/cli/subcommands/config.d.ts +44 -0
  141. package/dist/cli/subcommands/config.js +201 -0
  142. package/dist/cli/subcommands/context.d.ts +21 -0
  143. package/dist/cli/subcommands/context.js +41 -0
  144. package/dist/cli/subcommands/doctor.d.ts +11 -0
  145. package/dist/cli/subcommands/doctor.js +246 -0
  146. package/dist/cli/subcommands/models-cache-probe.d.ts +34 -0
  147. package/dist/cli/subcommands/models-cache-probe.js +190 -0
  148. package/dist/cli/subcommands/models-discover.d.ts +40 -0
  149. package/dist/cli/subcommands/models-discover.js +204 -0
  150. package/dist/cli/subcommands/models.d.ts +40 -0
  151. package/dist/cli/subcommands/models.js +144 -0
  152. package/dist/cli/subcommands/sessions.d.ts +11 -0
  153. package/dist/cli/subcommands/sessions.js +100 -0
  154. package/dist/codemode/capability.d.ts +37 -0
  155. package/dist/codemode/capability.js +55 -0
  156. package/dist/codemode/declarations.d.ts +44 -0
  157. package/dist/codemode/declarations.js +111 -0
  158. package/dist/codemode/host-side.d.ts +59 -0
  159. package/dist/codemode/host-side.js +253 -0
  160. package/dist/codemode/modes.d.ts +23 -0
  161. package/dist/codemode/modes.js +40 -0
  162. package/dist/codemode/protocol.d.ts +91 -0
  163. package/dist/codemode/protocol.js +114 -0
  164. package/dist/codemode/sandbox-entry.d.ts +25 -0
  165. package/dist/codemode/sandbox-entry.js +323 -0
  166. package/dist/codemode/store.d.ts +26 -0
  167. package/dist/codemode/store.js +53 -0
  168. package/dist/codemode/tool.d.ts +105 -0
  169. package/dist/codemode/tool.js +283 -0
  170. package/dist/compaction/branch-summary.d.ts +30 -0
  171. package/dist/compaction/branch-summary.js +79 -0
  172. package/dist/compaction/breaker.d.ts +32 -0
  173. package/dist/compaction/breaker.js +65 -0
  174. package/dist/compaction/cut-point.d.ts +23 -0
  175. package/dist/compaction/cut-point.js +70 -0
  176. package/dist/compaction/estimate.d.ts +30 -0
  177. package/dist/compaction/estimate.js +114 -0
  178. package/dist/compaction/prune-tier.d.ts +31 -0
  179. package/dist/compaction/prune-tier.js +83 -0
  180. package/dist/compaction/serialize.d.ts +20 -0
  181. package/dist/compaction/serialize.js +115 -0
  182. package/dist/compaction/summarize-tier.d.ts +111 -0
  183. package/dist/compaction/summarize-tier.js +305 -0
  184. package/dist/config/auth-file.d.ts +39 -0
  185. package/dist/config/auth-file.js +92 -0
  186. package/dist/config/context-files.d.ts +30 -0
  187. package/dist/config/context-files.js +77 -0
  188. package/dist/config/load.d.ts +45 -0
  189. package/dist/config/load.js +97 -0
  190. package/dist/config/merge.d.ts +73 -0
  191. package/dist/config/merge.js +248 -0
  192. package/dist/config/paths.d.ts +38 -0
  193. package/dist/config/paths.js +108 -0
  194. package/dist/config/profile.d.ts +27 -0
  195. package/dist/config/profile.js +65 -0
  196. package/dist/config/schema.d.ts +34 -0
  197. package/dist/config/schema.js +413 -0
  198. package/dist/config/trust.d.ts +42 -0
  199. package/dist/config/trust.js +122 -0
  200. package/dist/config/types.d.ts +172 -0
  201. package/dist/config/types.js +27 -0
  202. package/dist/config/write.d.ts +10 -0
  203. package/dist/config/write.js +25 -0
  204. package/dist/errors.d.ts +28 -0
  205. package/dist/errors.js +30 -0
  206. package/dist/hooks/config.d.ts +49 -0
  207. package/dist/hooks/config.js +102 -0
  208. package/dist/hooks/dispatcher.d.ts +41 -0
  209. package/dist/hooks/dispatcher.js +104 -0
  210. package/dist/hooks/matcher.d.ts +22 -0
  211. package/dist/hooks/matcher.js +108 -0
  212. package/dist/hooks/protocol.d.ts +33 -0
  213. package/dist/hooks/protocol.js +240 -0
  214. package/dist/hooks/runner.d.ts +30 -0
  215. package/dist/hooks/runner.js +143 -0
  216. package/dist/hooks/types.d.ts +147 -0
  217. package/dist/hooks/types.js +20 -0
  218. package/dist/host/api-impl.d.ts +75 -0
  219. package/dist/host/api-impl.js +173 -0
  220. package/dist/host/loader.d.ts +31 -0
  221. package/dist/host/loader.js +147 -0
  222. package/dist/host/types.d.ts +169 -0
  223. package/dist/host/types.js +14 -0
  224. package/dist/host.d.ts +5 -0
  225. package/dist/host.js +4 -0
  226. package/dist/index.d.ts +41 -0
  227. package/dist/index.js +28 -0
  228. package/dist/modes/commands-core.d.ts +44 -0
  229. package/dist/modes/commands-core.js +171 -0
  230. package/dist/modes/interactive/approval-dialog.d.ts +48 -0
  231. package/dist/modes/interactive/approval-dialog.js +202 -0
  232. package/dist/modes/interactive/commands.d.ts +41 -0
  233. package/dist/modes/interactive/commands.js +202 -0
  234. package/dist/modes/interactive/completion.d.ts +53 -0
  235. package/dist/modes/interactive/completion.js +157 -0
  236. package/dist/modes/interactive/interactive-mode.d.ts +46 -0
  237. package/dist/modes/interactive/interactive-mode.js +487 -0
  238. package/dist/modes/interactive/key-dispatch.d.ts +29 -0
  239. package/dist/modes/interactive/key-dispatch.js +107 -0
  240. package/dist/modes/interactive/line/line-editor.d.ts +50 -0
  241. package/dist/modes/interactive/line/line-editor.js +232 -0
  242. package/dist/modes/interactive/line/line-mode.d.ts +20 -0
  243. package/dist/modes/interactive/line/line-mode.js +216 -0
  244. package/dist/modes/interactive/line/line-render.d.ts +35 -0
  245. package/dist/modes/interactive/line/line-render.js +171 -0
  246. package/dist/modes/interactive/line/paste-state.d.ts +26 -0
  247. package/dist/modes/interactive/line/paste-state.js +72 -0
  248. package/dist/modes/interactive/message-view.d.ts +77 -0
  249. package/dist/modes/interactive/message-view.js +269 -0
  250. package/dist/modes/interactive/pickers.d.ts +42 -0
  251. package/dist/modes/interactive/pickers.js +115 -0
  252. package/dist/modes/interactive/startup-ui.d.ts +28 -0
  253. package/dist/modes/interactive/startup-ui.js +238 -0
  254. package/dist/modes/interactive/status-bar.d.ts +48 -0
  255. package/dist/modes/interactive/status-bar.js +139 -0
  256. package/dist/modes/interactive/tool-view.d.ts +95 -0
  257. package/dist/modes/interactive/tool-view.js +344 -0
  258. package/dist/modes/print/json-event.d.ts +15 -0
  259. package/dist/modes/print/json-event.js +39 -0
  260. package/dist/modes/print/print-mode.d.ts +15 -0
  261. package/dist/modes/print/print-mode.js +96 -0
  262. package/dist/modes/rpc/commands.d.ts +40 -0
  263. package/dist/modes/rpc/commands.js +258 -0
  264. package/dist/modes/rpc/jsonl.d.ts +16 -0
  265. package/dist/modes/rpc/jsonl.js +55 -0
  266. package/dist/modes/rpc/rpc-mode.d.ts +22 -0
  267. package/dist/modes/rpc/rpc-mode.js +153 -0
  268. package/dist/modes/session-report.d.ts +55 -0
  269. package/dist/modes/session-report.js +278 -0
  270. package/dist/modes/shared.d.ts +13 -0
  271. package/dist/modes/shared.js +52 -0
  272. package/dist/modes/startup-ui-text.d.ts +16 -0
  273. package/dist/modes/startup-ui-text.js +103 -0
  274. package/dist/permissions/broker.d.ts +47 -0
  275. package/dist/permissions/broker.js +107 -0
  276. package/dist/permissions/dangerous.d.ts +47 -0
  277. package/dist/permissions/dangerous.js +370 -0
  278. package/dist/permissions/pipeline.d.ts +51 -0
  279. package/dist/permissions/pipeline.js +181 -0
  280. package/dist/permissions/preview.d.ts +43 -0
  281. package/dist/permissions/preview.js +463 -0
  282. package/dist/permissions/rules.d.ts +58 -0
  283. package/dist/permissions/rules.js +249 -0
  284. package/dist/permissions/types.d.ts +100 -0
  285. package/dist/permissions/types.js +18 -0
  286. package/dist/rpc.d.ts +171 -0
  287. package/dist/rpc.js +13 -0
  288. package/dist/sdk.d.ts +98 -0
  289. package/dist/sdk.js +195 -0
  290. package/dist/session/manager.d.ts +70 -0
  291. package/dist/session/manager.js +278 -0
  292. package/dist/session/migrate.d.ts +19 -0
  293. package/dist/session/migrate.js +53 -0
  294. package/dist/session/projection.d.ts +50 -0
  295. package/dist/session/projection.js +180 -0
  296. package/dist/session/store.d.ts +47 -0
  297. package/dist/session/store.js +210 -0
  298. package/dist/session/tree.d.ts +21 -0
  299. package/dist/session/tree.js +88 -0
  300. package/dist/session/types.d.ts +199 -0
  301. package/dist/session/types.js +18 -0
  302. package/dist/skills/discover.d.ts +60 -0
  303. package/dist/skills/discover.js +160 -0
  304. package/dist/skills/expand.d.ts +35 -0
  305. package/dist/skills/expand.js +46 -0
  306. package/dist/skills/frontmatter.d.ts +27 -0
  307. package/dist/skills/frontmatter.js +157 -0
  308. package/dist/skills/index-prompt.d.ts +13 -0
  309. package/dist/skills/index-prompt.js +42 -0
  310. package/dist/skills/templates.d.ts +52 -0
  311. package/dist/skills/templates.js +177 -0
  312. package/dist/tools/bash.d.ts +49 -0
  313. package/dist/tools/bash.js +222 -0
  314. package/dist/tools/edit-fuzzy.d.ts +36 -0
  315. package/dist/tools/edit-fuzzy.js +129 -0
  316. package/dist/tools/edit.d.ts +38 -0
  317. package/dist/tools/edit.js +241 -0
  318. package/dist/tools/file-mutex.d.ts +8 -0
  319. package/dist/tools/file-mutex.js +29 -0
  320. package/dist/tools/glob.d.ts +44 -0
  321. package/dist/tools/glob.js +217 -0
  322. package/dist/tools/grep.d.ts +39 -0
  323. package/dist/tools/grep.js +179 -0
  324. package/dist/tools/ignore.d.ts +40 -0
  325. package/dist/tools/ignore.js +174 -0
  326. package/dist/tools/ls.d.ts +21 -0
  327. package/dist/tools/ls.js +96 -0
  328. package/dist/tools/output-accumulator.d.ts +48 -0
  329. package/dist/tools/output-accumulator.js +106 -0
  330. package/dist/tools/paths.d.ts +20 -0
  331. package/dist/tools/paths.js +58 -0
  332. package/dist/tools/presets.d.ts +71 -0
  333. package/dist/tools/presets.js +128 -0
  334. package/dist/tools/process-tree.d.ts +35 -0
  335. package/dist/tools/process-tree.js +125 -0
  336. package/dist/tools/read.d.ts +28 -0
  337. package/dist/tools/read.js +181 -0
  338. package/dist/tools/registry.d.ts +43 -0
  339. package/dist/tools/registry.js +106 -0
  340. package/dist/tools/shell.d.ts +38 -0
  341. package/dist/tools/shell.js +91 -0
  342. package/dist/tools/task.d.ts +29 -0
  343. package/dist/tools/task.js +107 -0
  344. package/dist/tools/todo.d.ts +28 -0
  345. package/dist/tools/todo.js +107 -0
  346. package/dist/tools/truncate.d.ts +44 -0
  347. package/dist/tools/truncate.js +175 -0
  348. package/dist/tools/types.d.ts +135 -0
  349. package/dist/tools/types.js +19 -0
  350. package/dist/tools/write.d.ts +20 -0
  351. package/dist/tools/write.js +84 -0
  352. package/dist/tui/ansi.d.ts +29 -0
  353. package/dist/tui/ansi.js +420 -0
  354. package/dist/tui/component.d.ts +38 -0
  355. package/dist/tui/component.js +13 -0
  356. package/dist/tui/components/box.d.ts +26 -0
  357. package/dist/tui/components/box.js +65 -0
  358. package/dist/tui/components/container.d.ts +14 -0
  359. package/dist/tui/components/container.js +39 -0
  360. package/dist/tui/components/editor-buffer.d.ts +67 -0
  361. package/dist/tui/components/editor-buffer.js +300 -0
  362. package/dist/tui/components/editor-paste.d.ts +29 -0
  363. package/dist/tui/components/editor-paste.js +64 -0
  364. package/dist/tui/components/editor.d.ts +114 -0
  365. package/dist/tui/components/editor.js +530 -0
  366. package/dist/tui/components/key-value.d.ts +29 -0
  367. package/dist/tui/components/key-value.js +48 -0
  368. package/dist/tui/components/loader.d.ts +35 -0
  369. package/dist/tui/components/loader.js +71 -0
  370. package/dist/tui/components/markdown.d.ts +90 -0
  371. package/dist/tui/components/markdown.js +462 -0
  372. package/dist/tui/components/meter.d.ts +29 -0
  373. package/dist/tui/components/meter.js +47 -0
  374. package/dist/tui/components/overlay.d.ts +24 -0
  375. package/dist/tui/components/overlay.js +52 -0
  376. package/dist/tui/components/select-list.d.ts +57 -0
  377. package/dist/tui/components/select-list.js +179 -0
  378. package/dist/tui/components/spacer.d.ts +11 -0
  379. package/dist/tui/components/spacer.js +16 -0
  380. package/dist/tui/components/text.d.ts +30 -0
  381. package/dist/tui/components/text.js +65 -0
  382. package/dist/tui/keybindings.d.ts +66 -0
  383. package/dist/tui/keybindings.js +120 -0
  384. package/dist/tui/keys.d.ts +38 -0
  385. package/dist/tui/keys.js +275 -0
  386. package/dist/tui/stdin-buffer.d.ts +41 -0
  387. package/dist/tui/stdin-buffer.js +206 -0
  388. package/dist/tui/terminal.d.ts +79 -0
  389. package/dist/tui/terminal.js +153 -0
  390. package/dist/tui/theme.d.ts +31 -0
  391. package/dist/tui/theme.js +199 -0
  392. package/dist/tui/tui.d.ts +103 -0
  393. package/dist/tui/tui.js +363 -0
  394. package/dist/tui/vt-screen.d.ts +46 -0
  395. package/dist/tui/vt-screen.js +251 -0
  396. package/dist/tui.d.ts +30 -0
  397. package/dist/tui.js +31 -0
  398. package/dist/version.d.ts +8 -0
  399. package/dist/version.js +24 -0
  400. package/docs/codemode.md +102 -0
  401. package/docs/hooks.md +196 -0
  402. package/docs/host-api.md +161 -0
  403. package/docs/providers.md +316 -0
  404. package/docs/rpc.md +253 -0
  405. package/docs/session-format.md +119 -0
  406. package/docs/tui.md +168 -0
  407. package/package.json +101 -4
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 键解析(设计 §12.3)。[B4]
3
+ *
4
+ * 支持:C0 控制字符(Ctrl 组合)、CSI / SS3 光标与功能键、xterm 修饰键参数(`ESC[1;5A`)、
5
+ * `CSI u`(`ESC[13;2u`)与 modifyOtherKeys(`ESC[27;2;13~`)两种扩展形式、Alt 前缀(`ESC x`)。
6
+ * **不查询 Kitty 键盘协议**(tmux 下回包会污染输入);只被动解析终端自发的扩展序列。
7
+ *
8
+ * KeyId 形如 `ctrl+shift+up`:修饰键按 ctrl → alt → shift 排序,键名小写;
9
+ * 可打印字符的键名就是字符本身(`a`、`/`、`中`),空格为 `space`。
10
+ */
11
+ export interface KeyEvent {
12
+ /** 键名:`enter`、`up`、`f5`、`a`、`中` …… */
13
+ readonly name: string;
14
+ readonly ctrl: boolean;
15
+ readonly alt: boolean;
16
+ readonly shift: boolean;
17
+ /** 规范化 KeyId:修饰键 + 键名。 */
18
+ readonly id: string;
19
+ /** 产生可打印文本时为该文本(不含修饰键组合)。 */
20
+ readonly text?: string;
21
+ }
22
+ export declare const PASTE_START = "\u001B[200~";
23
+ export declare const PASTE_END = "\u001B[201~";
24
+ /**
25
+ * 解析一个完整的键序列(StdinBuffer 已按序列切分)。无法识别返回 undefined。
26
+ * 多个字符的纯文本(例如非括号粘贴)不算单个键,同样返回 undefined。
27
+ */
28
+ export declare function parseKey(data: string): KeyEvent | undefined;
29
+ /** 规范化 KeyId 字符串:修饰键重排、别名(return→enter、esc→escape、del→delete)。 */
30
+ export declare function normalizeKeyId(id: string): string;
31
+ /** 数据是否匹配给定 KeyId(`shift+a` 与大写 `A` 视为同一键)。 */
32
+ export declare function matchesKey(data: string, keyId: string): boolean;
33
+ /** 是否为括号粘贴包裹的数据(StdinBuffer 产出的一次 paste 事件)。 */
34
+ export declare function isPasteData(data: string): boolean;
35
+ /** 取出括号粘贴的正文。 */
36
+ export declare function unwrapPaste(data: string): string;
37
+ /** 数据是否为可直接插入的文本(不含控制字符与转义序列)。 */
38
+ export declare function isPrintableText(data: string): boolean;
@@ -0,0 +1,275 @@
1
+ /**
2
+ * 键解析(设计 §12.3)。[B4]
3
+ *
4
+ * 支持:C0 控制字符(Ctrl 组合)、CSI / SS3 光标与功能键、xterm 修饰键参数(`ESC[1;5A`)、
5
+ * `CSI u`(`ESC[13;2u`)与 modifyOtherKeys(`ESC[27;2;13~`)两种扩展形式、Alt 前缀(`ESC x`)。
6
+ * **不查询 Kitty 键盘协议**(tmux 下回包会污染输入);只被动解析终端自发的扩展序列。
7
+ *
8
+ * KeyId 形如 `ctrl+shift+up`:修饰键按 ctrl → alt → shift 排序,键名小写;
9
+ * 可打印字符的键名就是字符本身(`a`、`/`、`中`),空格为 `space`。
10
+ */
11
+ export const PASTE_START = "\x1b[200~";
12
+ export const PASTE_END = "\x1b[201~";
13
+ const NO_MODS = { ctrl: false, alt: false, shift: false };
14
+ function makeKey(name, mods, text) {
15
+ const parts = [];
16
+ if (mods.ctrl)
17
+ parts.push("ctrl");
18
+ if (mods.alt)
19
+ parts.push("alt");
20
+ if (mods.shift)
21
+ parts.push("shift");
22
+ parts.push(name);
23
+ const base = { name, ctrl: mods.ctrl, alt: mods.alt, shift: mods.shift, id: parts.join("+") };
24
+ return text === undefined ? base : { ...base, text };
25
+ }
26
+ /** xterm 修饰参数:值 = 1 + 位掩码(1 shift,2 alt,4 ctrl,8 meta→按 alt 处理)。 */
27
+ function modsFromParam(param) {
28
+ if (param === undefined || !Number.isFinite(param) || param < 1)
29
+ return NO_MODS;
30
+ const bits = param - 1;
31
+ return {
32
+ shift: (bits & 1) !== 0,
33
+ alt: (bits & 2) !== 0 || (bits & 8) !== 0,
34
+ ctrl: (bits & 4) !== 0,
35
+ };
36
+ }
37
+ const CSI_LETTER = {
38
+ A: "up",
39
+ B: "down",
40
+ C: "right",
41
+ D: "left",
42
+ H: "home",
43
+ F: "end",
44
+ E: "clear",
45
+ P: "f1",
46
+ Q: "f2",
47
+ R: "f3",
48
+ S: "f4",
49
+ };
50
+ const CSI_TILDE = {
51
+ 1: "home",
52
+ 2: "insert",
53
+ 3: "delete",
54
+ 4: "end",
55
+ 5: "pageup",
56
+ 6: "pagedown",
57
+ 7: "home",
58
+ 8: "end",
59
+ 11: "f1",
60
+ 12: "f2",
61
+ 13: "f3",
62
+ 14: "f4",
63
+ 15: "f5",
64
+ 17: "f6",
65
+ 18: "f7",
66
+ 19: "f8",
67
+ 20: "f9",
68
+ 21: "f10",
69
+ 23: "f11",
70
+ 24: "f12",
71
+ };
72
+ /** 码点(CSI u / modifyOtherKeys)→ 键名。 */
73
+ function nameFromCodePoint(cp) {
74
+ switch (cp) {
75
+ case 13:
76
+ return "enter";
77
+ case 9:
78
+ return "tab";
79
+ case 27:
80
+ return "escape";
81
+ case 127:
82
+ case 8:
83
+ return "backspace";
84
+ case 32:
85
+ return "space";
86
+ default:
87
+ if (cp < 32)
88
+ return undefined;
89
+ return String.fromCodePoint(cp).toLowerCase();
90
+ }
91
+ }
92
+ function parseCsi(body, final) {
93
+ // body:ESC[ 与终止字节之间的参数部分
94
+ if (body.startsWith("<") || body.startsWith("?") || body.startsWith(">"))
95
+ return undefined;
96
+ const params = body.split(";").map((p) => (p === "" ? undefined : Number(p.split(":")[0])));
97
+ if (final === "u") {
98
+ const cp = params[0];
99
+ if (cp === undefined)
100
+ return undefined;
101
+ const name = nameFromCodePoint(cp);
102
+ return name === undefined ? undefined : makeKey(name, modsFromParam(params[1]));
103
+ }
104
+ if (final === "~") {
105
+ const first = params[0];
106
+ if (first === 27 && params.length >= 3) {
107
+ // modifyOtherKeys:ESC[27;<mod>;<code>~
108
+ const name = nameFromCodePoint(params[2] ?? -1);
109
+ return name === undefined ? undefined : makeKey(name, modsFromParam(params[1]));
110
+ }
111
+ if (first === 200 || first === 201)
112
+ return undefined;
113
+ const name = first === undefined ? undefined : CSI_TILDE[first];
114
+ return name === undefined ? undefined : makeKey(name, modsFromParam(params[1]));
115
+ }
116
+ if (final === "Z")
117
+ return makeKey("tab", { ...modsFromParam(params[1]), shift: true });
118
+ const name = CSI_LETTER[final];
119
+ if (name === undefined)
120
+ return undefined;
121
+ return makeKey(name, modsFromParam(params.length > 1 ? params[1] : undefined));
122
+ }
123
+ const SS3 = {
124
+ A: "up",
125
+ B: "down",
126
+ C: "right",
127
+ D: "left",
128
+ H: "home",
129
+ F: "end",
130
+ P: "f1",
131
+ Q: "f2",
132
+ R: "f3",
133
+ S: "f4",
134
+ M: "enter",
135
+ };
136
+ function parseControl(code) {
137
+ switch (code) {
138
+ case 0x0d:
139
+ return makeKey("enter", NO_MODS);
140
+ case 0x0a:
141
+ return makeKey("j", { ...NO_MODS, ctrl: true });
142
+ case 0x09:
143
+ return makeKey("tab", NO_MODS);
144
+ case 0x7f:
145
+ case 0x08:
146
+ return makeKey("backspace", NO_MODS);
147
+ case 0x1b:
148
+ return makeKey("escape", NO_MODS);
149
+ case 0x00:
150
+ return makeKey("space", { ...NO_MODS, ctrl: true });
151
+ case 0x1c:
152
+ return makeKey("\\", { ...NO_MODS, ctrl: true });
153
+ case 0x1d:
154
+ return makeKey("]", { ...NO_MODS, ctrl: true });
155
+ case 0x1e:
156
+ return makeKey("^", { ...NO_MODS, ctrl: true });
157
+ case 0x1f:
158
+ return makeKey("-", { ...NO_MODS, ctrl: true });
159
+ default:
160
+ if (code >= 0x01 && code <= 0x1a) {
161
+ return makeKey(String.fromCharCode(code + 0x60), { ...NO_MODS, ctrl: true });
162
+ }
163
+ return undefined;
164
+ }
165
+ }
166
+ function withAlt(key) {
167
+ return makeKey(key.name, { ctrl: key.ctrl, alt: true, shift: key.shift });
168
+ }
169
+ function parsePrintable(data) {
170
+ const cp = data.codePointAt(0);
171
+ if (cp === undefined || cp < 0x20 || cp === 0x7f)
172
+ return undefined;
173
+ if (data === " ")
174
+ return makeKey("space", NO_MODS, " ");
175
+ const lower = data.toLowerCase();
176
+ const shift = lower !== data && data.length === 1;
177
+ return makeKey(lower, { ...NO_MODS, shift }, data);
178
+ }
179
+ /**
180
+ * 解析一个完整的键序列(StdinBuffer 已按序列切分)。无法识别返回 undefined。
181
+ * 多个字符的纯文本(例如非括号粘贴)不算单个键,同样返回 undefined。
182
+ */
183
+ export function parseKey(data) {
184
+ if (data.length === 0)
185
+ return undefined;
186
+ const first = data.charCodeAt(0);
187
+ if (first !== 0x1b) {
188
+ if (data.length === 1)
189
+ return parseControl(first) ?? parsePrintable(data);
190
+ // 单个非 BMP 字符或组合字素
191
+ if ([...data].length === 1 || isSingleGrapheme(data))
192
+ return parsePrintable(data);
193
+ return undefined;
194
+ }
195
+ if (data.length === 1)
196
+ return makeKey("escape", NO_MODS);
197
+ const second = data[1];
198
+ if (second === "[") {
199
+ const final = data[data.length - 1];
200
+ if (data.length >= 3)
201
+ return parseCsi(data.slice(2, -1), final);
202
+ return makeKey("[", { ...NO_MODS, alt: true }, undefined);
203
+ }
204
+ if (second === "O" && data.length === 3) {
205
+ const name = SS3[data[2]];
206
+ return name === undefined ? undefined : makeKey(name, NO_MODS);
207
+ }
208
+ // Alt 前缀:ESC + 另一序列
209
+ const rest = data.slice(1);
210
+ const inner = parseKey(rest);
211
+ if (inner === undefined)
212
+ return undefined;
213
+ return withAlt(inner);
214
+ }
215
+ const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
216
+ function isSingleGrapheme(data) {
217
+ let count = 0;
218
+ for (const _ of graphemeSegmenter.segment(data)) {
219
+ count++;
220
+ if (count > 1)
221
+ return false;
222
+ }
223
+ return count === 1;
224
+ }
225
+ /** 规范化 KeyId 字符串:修饰键重排、别名(return→enter、esc→escape、del→delete)。 */
226
+ export function normalizeKeyId(id) {
227
+ const parts = id.toLowerCase().split("+");
228
+ // "ctrl++" 这类以 + 为键名的写法
229
+ if (id.endsWith("++")) {
230
+ parts.splice(parts.length - 2, 2, "+");
231
+ }
232
+ const name = parts.pop() ?? "";
233
+ const mods = new Set(parts.map((p) => (p === "option" || p === "meta" ? "alt" : p)));
234
+ const alias = {
235
+ return: "enter",
236
+ esc: "escape",
237
+ del: "delete",
238
+ bs: "backspace",
239
+ pgup: "pageup",
240
+ pgdn: "pagedown",
241
+ " ": "space",
242
+ };
243
+ const finalName = alias[name] ?? name;
244
+ return makeKey(finalName, {
245
+ ctrl: mods.has("ctrl"),
246
+ alt: mods.has("alt"),
247
+ shift: mods.has("shift"),
248
+ }).id;
249
+ }
250
+ /** 数据是否匹配给定 KeyId(`shift+a` 与大写 `A` 视为同一键)。 */
251
+ export function matchesKey(data, keyId) {
252
+ const key = parseKey(data);
253
+ if (key === undefined)
254
+ return false;
255
+ return key.id === normalizeKeyId(keyId);
256
+ }
257
+ /** 是否为括号粘贴包裹的数据(StdinBuffer 产出的一次 paste 事件)。 */
258
+ export function isPasteData(data) {
259
+ return data.startsWith(PASTE_START) && data.endsWith(PASTE_END);
260
+ }
261
+ /** 取出括号粘贴的正文。 */
262
+ export function unwrapPaste(data) {
263
+ return data.slice(PASTE_START.length, data.length - PASTE_END.length);
264
+ }
265
+ /** 数据是否为可直接插入的文本(不含控制字符与转义序列)。 */
266
+ export function isPrintableText(data) {
267
+ if (data.length === 0)
268
+ return false;
269
+ for (let i = 0; i < data.length; i++) {
270
+ const c = data.charCodeAt(i);
271
+ if (c < 0x20 || c === 0x7f)
272
+ return false;
273
+ }
274
+ return true;
275
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * 标准输入缓冲(设计 §12.3)。[B4]
3
+ *
4
+ * - 把分片到达的原始输入切成「一个键一个序列」:完整的 CSI / SS3 / OSC / APC / DCS、
5
+ * Alt 前缀(`ESC x`、`ESC ESC [ A`)、单个字素簇。
6
+ * - 括号粘贴 `ESC[200~ … ESC[201~` 跨任意多个 data 块累积,结束时作为**一次** paste 事件发出;
7
+ * 粘贴内部的 `\r` / `\n` / ESC 都是数据,不解析为键。
8
+ * - 块末尾的不完整转义序列先挂起;在 `AMA_TUI_ESC_TIMEOUT` 毫秒内没有后续数据就原样发出
9
+ * (孤立 ESC 即 Escape 键)。缺省:SSH / tmux 下 100 ms,本地 10 ms。
10
+ */
11
+ export interface StdinBufferOptions {
12
+ /** 每个完整键序列。 */
13
+ onData(sequence: string): void;
14
+ /** 括号粘贴正文(不含 200~/201~ 包裹)。 */
15
+ onPaste(text: string): void;
16
+ /** 孤立 ESC 判定超时(ms);缺省见 `defaultEscTimeout()`。 */
17
+ escTimeoutMs?: number;
18
+ }
19
+ /** 读环境:`AMA_TUI_ESC_TIMEOUT` 优先;否则 SSH / tmux 100 ms,本地 10 ms。 */
20
+ export declare function defaultEscTimeout(env?: NodeJS.ProcessEnv): number;
21
+ export declare class StdinBuffer {
22
+ private readonly options;
23
+ private buffer;
24
+ private pasteBuffer;
25
+ private timer;
26
+ private readonly escTimeoutMs;
27
+ private destroyed;
28
+ constructor(options: StdinBufferOptions);
29
+ /** 是否处于括号粘贴中(等待 ESC[201~)。 */
30
+ get inPaste(): boolean;
31
+ process(chunk: string | Buffer): void;
32
+ /** 立即发出所有挂起数据(超时或停止时调用)。 */
33
+ flush(): void;
34
+ destroy(): void;
35
+ private clearTimer;
36
+ private drain;
37
+ /** 粘贴模式:找结束标记;标记可能被切在块边界上,保留可能的前缀等下一块。 */
38
+ private drainPaste;
39
+ /** 非转义数据:一个字素簇为一个序列(控制字符单独成序列)。 */
40
+ private nextTextLength;
41
+ }
@@ -0,0 +1,206 @@
1
+ /**
2
+ * 标准输入缓冲(设计 §12.3)。[B4]
3
+ *
4
+ * - 把分片到达的原始输入切成「一个键一个序列」:完整的 CSI / SS3 / OSC / APC / DCS、
5
+ * Alt 前缀(`ESC x`、`ESC ESC [ A`)、单个字素簇。
6
+ * - 括号粘贴 `ESC[200~ … ESC[201~` 跨任意多个 data 块累积,结束时作为**一次** paste 事件发出;
7
+ * 粘贴内部的 `\r` / `\n` / ESC 都是数据,不解析为键。
8
+ * - 块末尾的不完整转义序列先挂起;在 `AMA_TUI_ESC_TIMEOUT` 毫秒内没有后续数据就原样发出
9
+ * (孤立 ESC 即 Escape 键)。缺省:SSH / tmux 下 100 ms,本地 10 ms。
10
+ */
11
+ import { PASTE_END, PASTE_START } from "./keys.js";
12
+ /** 读环境:`AMA_TUI_ESC_TIMEOUT` 优先;否则 SSH / tmux 100 ms,本地 10 ms。 */
13
+ export function defaultEscTimeout(env = process.env) {
14
+ const raw = env["AMA_TUI_ESC_TIMEOUT"];
15
+ if (raw !== undefined && raw.trim() !== "") {
16
+ const n = Number(raw);
17
+ if (Number.isFinite(n) && n >= 0)
18
+ return n;
19
+ }
20
+ if (env["SSH_CONNECTION"] || env["SSH_TTY"] || env["SSH_CLIENT"] || env["TMUX"])
21
+ return 100;
22
+ return 10;
23
+ }
24
+ const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
25
+ /** 从 0 开始扫描一个转义序列(buf[0] === ESC)。 */
26
+ function scanEscape(buf) {
27
+ if (buf.length === 1)
28
+ return { kind: "incomplete" };
29
+ const second = buf[1];
30
+ if (second === "[") {
31
+ for (let j = 2; j < buf.length; j++) {
32
+ const c = buf.charCodeAt(j);
33
+ if (c >= 0x40 && c <= 0x7e) {
34
+ const seq = buf.slice(0, j + 1);
35
+ if (seq === PASTE_START)
36
+ return { kind: "paste-start", length: j + 1 };
37
+ return { kind: "complete", length: j + 1 };
38
+ }
39
+ // CSI 参数 / 中间字节之外的字符:序列被打断,只取 ESC[
40
+ if (c < 0x20 || c > 0x7e)
41
+ return { kind: "complete", length: 2 };
42
+ }
43
+ return { kind: "incomplete" };
44
+ }
45
+ if (second === "O") {
46
+ if (buf.length < 3)
47
+ return { kind: "incomplete" };
48
+ return { kind: "complete", length: 3 };
49
+ }
50
+ if (second === "]" || second === "_" || second === "P" || second === "^" || second === "X") {
51
+ for (let j = 2; j < buf.length; j++) {
52
+ const c = buf.charCodeAt(j);
53
+ if (c === 0x07)
54
+ return { kind: "complete", length: j + 1 };
55
+ if (c === 0x1b) {
56
+ if (j + 1 >= buf.length)
57
+ return { kind: "incomplete" };
58
+ if (buf[j + 1] === "\\")
59
+ return { kind: "complete", length: j + 2 };
60
+ }
61
+ }
62
+ return { kind: "incomplete" };
63
+ }
64
+ if (second === "\x1b") {
65
+ // ESC ESC [ A:Alt + 转义序列;ESC ESC 单独出现为 Alt+Escape
66
+ if (buf.length === 2)
67
+ return { kind: "incomplete" };
68
+ const inner = scanEscape(buf.slice(1));
69
+ if (inner.kind === "complete")
70
+ return { kind: "complete", length: inner.length + 1 };
71
+ if (inner.kind === "paste-start")
72
+ return { kind: "complete", length: 2 };
73
+ return inner;
74
+ }
75
+ // Alt 前缀 + 单个字素
76
+ const rest = buf.slice(1);
77
+ const first = segmenter.segment(rest.slice(0, 64))[Symbol.iterator]().next();
78
+ const segment = first.done ? rest[0] : first.value.segment;
79
+ return { kind: "complete", length: 1 + segment.length };
80
+ }
81
+ export class StdinBuffer {
82
+ options;
83
+ buffer = "";
84
+ pasteBuffer = null;
85
+ timer = null;
86
+ escTimeoutMs;
87
+ destroyed = false;
88
+ constructor(options) {
89
+ this.options = options;
90
+ this.escTimeoutMs = options.escTimeoutMs ?? defaultEscTimeout();
91
+ }
92
+ /** 是否处于括号粘贴中(等待 ESC[201~)。 */
93
+ get inPaste() {
94
+ return this.pasteBuffer !== null;
95
+ }
96
+ process(chunk) {
97
+ if (this.destroyed)
98
+ return;
99
+ this.clearTimer();
100
+ this.buffer += typeof chunk === "string" ? chunk : chunk.toString("utf8");
101
+ this.drain();
102
+ }
103
+ /** 立即发出所有挂起数据(超时或停止时调用)。 */
104
+ flush() {
105
+ this.clearTimer();
106
+ if (this.pasteBuffer !== null) {
107
+ // 粘贴结束标记一直没到:按已收到的正文发出,不丢数据
108
+ const text = this.pasteBuffer + this.buffer;
109
+ this.pasteBuffer = null;
110
+ this.buffer = "";
111
+ this.options.onPaste(text);
112
+ return;
113
+ }
114
+ if (this.buffer.length > 0) {
115
+ const pending = this.buffer;
116
+ this.buffer = "";
117
+ this.options.onData(pending);
118
+ }
119
+ }
120
+ destroy() {
121
+ this.clearTimer();
122
+ this.destroyed = true;
123
+ this.buffer = "";
124
+ this.pasteBuffer = null;
125
+ }
126
+ clearTimer() {
127
+ if (this.timer !== null) {
128
+ clearTimeout(this.timer);
129
+ this.timer = null;
130
+ }
131
+ }
132
+ drain() {
133
+ while (this.buffer.length > 0) {
134
+ if (this.pasteBuffer !== null) {
135
+ if (!this.drainPaste())
136
+ return;
137
+ continue;
138
+ }
139
+ const buf = this.buffer;
140
+ if (buf.charCodeAt(0) === 0x1b) {
141
+ const scan = scanEscape(buf);
142
+ if (scan.kind === "incomplete") {
143
+ this.timer = setTimeout(() => {
144
+ this.timer = null;
145
+ this.flush();
146
+ }, this.escTimeoutMs);
147
+ return;
148
+ }
149
+ this.buffer = buf.slice(scan.length);
150
+ if (scan.kind === "paste-start")
151
+ this.pasteBuffer = "";
152
+ else
153
+ this.options.onData(buf.slice(0, scan.length));
154
+ continue;
155
+ }
156
+ const length = this.nextTextLength(buf);
157
+ this.buffer = buf.slice(length);
158
+ this.options.onData(buf.slice(0, length));
159
+ }
160
+ }
161
+ /** 粘贴模式:找结束标记;标记可能被切在块边界上,保留可能的前缀等下一块。 */
162
+ drainPaste() {
163
+ const combined = this.pasteBuffer + this.buffer;
164
+ const end = combined.indexOf(PASTE_END);
165
+ if (end === -1) {
166
+ const keep = partialSuffixLength(combined, PASTE_END);
167
+ this.pasteBuffer = combined.slice(0, combined.length - keep);
168
+ this.buffer = combined.slice(combined.length - keep);
169
+ // buffer 里只剩可能的结束标记前缀:等下一块
170
+ return false;
171
+ }
172
+ const text = combined.slice(0, end);
173
+ this.pasteBuffer = null;
174
+ this.buffer = combined.slice(end + PASTE_END.length);
175
+ this.options.onPaste(text);
176
+ return true;
177
+ }
178
+ /** 非转义数据:一个字素簇为一个序列(控制字符单独成序列)。 */
179
+ nextTextLength(buf) {
180
+ const c = buf.charCodeAt(0);
181
+ if (c < 0x20 || c === 0x7f)
182
+ return 1;
183
+ const first = segmenter.segment(buf.slice(0, 64))[Symbol.iterator]().next();
184
+ if (first.done)
185
+ return 1;
186
+ // 字素簇不能吞掉后面的 ESC 或控制字符
187
+ let len = first.value.segment.length;
188
+ for (let i = 1; i < len; i++) {
189
+ const cc = buf.charCodeAt(i);
190
+ if (cc < 0x20 || cc === 0x7f) {
191
+ len = i;
192
+ break;
193
+ }
194
+ }
195
+ return len;
196
+ }
197
+ }
198
+ /** s 的后缀与 marker 的前缀最长重合长度(< marker.length)。 */
199
+ function partialSuffixLength(s, marker) {
200
+ const max = Math.min(s.length, marker.length - 1);
201
+ for (let len = max; len > 0; len--) {
202
+ if (marker.startsWith(s.slice(s.length - len)))
203
+ return len;
204
+ }
205
+ return 0;
206
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * 终端抽象(设计 §12.2、§12.3、§12.8)。[B4]
3
+ *
4
+ * - `Terminal`:TUI 只依赖这个接口——尺寸、写入、输入回调、尺寸变化回调。
5
+ * - `ProcessTerminal`:真实 TTY。raw 模式、括号粘贴 `?2004`、stdout `resize`(SIGWINCH);
6
+ * 输入经 `StdinBuffer` 切分,括号粘贴以 `ESC[200~正文ESC[201~` 一次交给 onInput。
7
+ * 停止时关括号粘贴、显示光标、恢复 cooked 模式;**不清屏**(回滚保留对话)。
8
+ * - `MemoryTerminal`:测试用。记录每次写入,并用 `VirtualScreen` 还原屏幕与回滚。
9
+ */
10
+ import { VirtualScreen } from "./vt-screen.js";
11
+ export interface Terminal {
12
+ readonly columns: number;
13
+ readonly rows: number;
14
+ /** 进入 raw 模式、开括号粘贴、开始投递输入与尺寸变化。 */
15
+ start(onInput: (data: string) => void, onResize: () => void): void;
16
+ /** 关括号粘贴、恢复 cooked 模式、停止投递;不清屏。 */
17
+ stop(): void;
18
+ write(data: string): void;
19
+ }
20
+ export declare const BRACKETED_PASTE_ON = "\u001B[?2004h";
21
+ export declare const BRACKETED_PASTE_OFF = "\u001B[?2004l";
22
+ export declare const SHOW_CURSOR = "\u001B[?25h";
23
+ export declare const HIDE_CURSOR = "\u001B[?25l";
24
+ export interface ProcessTerminalOptions {
25
+ stdin?: NodeJS.ReadStream;
26
+ stdout?: NodeJS.WriteStream;
27
+ escTimeoutMs?: number;
28
+ }
29
+ export declare class ProcessTerminal implements Terminal {
30
+ private readonly stdin;
31
+ private readonly stdout;
32
+ private readonly escTimeoutMs;
33
+ private buffer;
34
+ private wasRaw;
35
+ private dataListener;
36
+ private resizeListener;
37
+ constructor(options?: ProcessTerminalOptions);
38
+ get columns(): number;
39
+ get rows(): number;
40
+ start(onInput: (data: string) => void, onResize: () => void): void;
41
+ stop(): void;
42
+ write(data: string): void;
43
+ }
44
+ export interface MemoryTerminalOptions {
45
+ columns?: number;
46
+ rows?: number;
47
+ /** 测试里孤立 ESC 的超时;缺省 10 ms。 */
48
+ escTimeoutMs?: number;
49
+ }
50
+ export declare class MemoryTerminal implements Terminal {
51
+ readonly screen: VirtualScreen;
52
+ /** 每次 write 的原始数据(按顺序)。 */
53
+ readonly writes: string[];
54
+ started: boolean;
55
+ rawMode: boolean;
56
+ private onInput;
57
+ private onResize;
58
+ private buffer;
59
+ constructor(options?: MemoryTerminalOptions);
60
+ get columns(): number;
61
+ get rows(): number;
62
+ start(onInput: (data: string) => void, onResize: () => void): void;
63
+ stop(): void;
64
+ write(data: string): void;
65
+ /** 模拟一个 stdin data 块(经 StdinBuffer 切分,与真实终端同路径)。 */
66
+ sendInput(chunk: string): void;
67
+ /** 立即发出挂起的不完整序列(替代等待 ESC 超时)。 */
68
+ flushInput(): void;
69
+ /** 改尺寸并触发 resize 回调(模拟 SIGWINCH)。 */
70
+ resize(columns: number, rows: number): void;
71
+ /** 全部写入拼成一个字符串。 */
72
+ get output(): string;
73
+ /** 取走并清空写入记录(便于断言「这一帧写了什么」)。 */
74
+ takeWrites(): string;
75
+ /** 可见屏幕各行(去掉右侧空白)。 */
76
+ viewport(): string[];
77
+ /** 回滚 + 屏幕(去掉底部空行)。 */
78
+ transcript(): string[];
79
+ }