@platless/axom-cli 0.5.9 → 0.6.52

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 (258) hide show
  1. package/README.md +25 -6
  2. package/dist/axom-patch/cli/README.md +129 -12
  3. package/dist/axom-patch/cli/account-limits/generate.py +15 -12
  4. package/dist/axom-patch/cli/account-limits/kimi-proxy.mjs +20 -3
  5. package/dist/axom-patch/cli/claude-account-limits.json +9 -9
  6. package/dist/axom-patch/cli/claude-replay-lifecycle.json +16 -0
  7. package/dist/axom-patch/cli/codex-account-limits.json +11 -11
  8. package/dist/axom-patch/cli/codex-terminal-output.json +16 -0
  9. package/dist/axom-patch/cli/commandcode-account-limits.json +9 -9
  10. package/dist/axom-patch/cli/kimi-quota.mjs +12 -0
  11. package/dist/axom-patch/cli/kimi-subagents.mjs +173 -0
  12. package/dist/axom-patch/cli/nightly-task.md +55 -13
  13. package/dist/axom-patch/cli/probes/claude-replay-lifecycle.mjs +31 -0
  14. package/dist/axom-patch/cli/probes/codex-terminal-output.mjs +33 -0
  15. package/dist/axom-patch/cli/probes/kimi-subagent-subwork.mjs +82 -0
  16. package/dist/axom-patch/cli/probes/zcode-env-account.mjs +81 -0
  17. package/dist/axom-patch/cli/probes/zcode-subagent-subwork.mjs +7 -0
  18. package/dist/axom-patch/cli/qoder/dist/axom-acp-compat.mjs +1687 -0
  19. package/dist/axom-patch/cli/qoder/releases/1.1.75.json +277 -0
  20. package/dist/axom-patch/cli/qoder-background-terminal.md +13 -6
  21. package/dist/axom-patch/cli/qoder-foreground-output.md +14 -8
  22. package/dist/axom-patch/cli/qoder-native-acp.json +227 -0
  23. package/dist/axom-patch/cli/qoder-steering.md +20 -25
  24. package/dist/axom-patch/cli/qoder-subagent-subwork.md +17 -68
  25. package/dist/axom-patch/cli/runtime.json +19 -19
  26. package/dist/axom-patch/cli/zcode/env-account.mjs +13 -0
  27. package/dist/axom-patch/cli/zcode/generate-env-account.py +52 -0
  28. package/dist/axom-patch/cli/zcode-account-limits.json +10 -11
  29. package/dist/axom-patch/cli/zcode-env-account.json +58 -0
  30. package/dist/axom-patch/cli/zcode-models.json +9 -10
  31. package/dist/axom-patch/cli/zcode-subagent-subwork.json +10 -11
  32. package/dist/cli.js +11 -45
  33. package/dist/commands/auth.js +4 -1
  34. package/dist/commands/knowledge.js +47 -0
  35. package/dist/commands/update.js +126 -3
  36. package/dist/commands/workstation.js +101 -164
  37. package/dist/commands/workstationRegistrations.js +116 -0
  38. package/dist/credentialBroker.js +13 -2
  39. package/dist/credentials.js +16 -0
  40. package/dist/install/claude/commands.js +15 -15
  41. package/dist/keepers/terminal/keeper.mjs +1089 -673
  42. package/dist/keepers/terminal/manifest.json +4 -4
  43. package/dist/mcp.js +1 -1
  44. package/dist/native/darwin-x64/axom-quic +0 -0
  45. package/dist/workstation/acp/acpAgents.js +113 -102
  46. package/dist/workstation/acp/acpClient.js +26 -0
  47. package/dist/workstation/acp/builds/install/npm.js +99 -10
  48. package/dist/workstation/acp/initializeResponse.js +6 -1
  49. package/dist/workstation/acp/managedPatches.js +189 -25
  50. package/dist/workstation/acp/managedRuntime.js +314 -0
  51. package/dist/workstation/acp/outbox.js +12 -5
  52. package/dist/workstation/acp/patchAssets.js +40 -0
  53. package/dist/workstation/acp/permission.js +2 -2
  54. package/dist/workstation/acp/processor/checkpoint.js +26 -34
  55. package/dist/workstation/acp/processor/control.js +7 -80
  56. package/dist/workstation/acp/processor/jsonrpc.js +8 -0
  57. package/dist/workstation/acp/processor/runtime.js +5 -81
  58. package/dist/workstation/acp/processor/session.js +54 -82
  59. package/dist/workstation/acp/processor/state.js +7 -13
  60. package/dist/workstation/acp/processor/step.js +3 -20
  61. package/dist/workstation/acp/processor/test/fixtureLog.js +9 -16
  62. package/dist/workstation/acp/processor/transport/local.js +19 -3
  63. package/dist/workstation/acp/processor/updates.js +156 -105
  64. package/dist/workstation/acp/processorSession.js +20 -173
  65. package/dist/workstation/acp/registry/catalog.js +65 -39
  66. package/dist/workstation/acp/registry/launch.js +16 -1
  67. package/dist/workstation/acp/switching.js +0 -3
  68. package/dist/workstation/acp/test/fakeAgent.js +13 -0
  69. package/dist/workstation/acp/test/keeperWorld.js +32 -3
  70. package/dist/workstation/acp/test/raw/sources.js +16 -130
  71. package/dist/workstation/acp/test/realAdapter.js +1 -47
  72. package/dist/workstation/acp/test/scriptAgent.js +11 -3
  73. package/dist/workstation/agentFlow.js +1 -1
  74. package/dist/workstation/agentSessions.js +114 -166
  75. package/dist/workstation/channels/agent.js +9 -1
  76. package/dist/workstation/channels/computer.js +27 -4
  77. package/dist/workstation/channels/management.js +130 -15
  78. package/dist/workstation/channels/url.js +1 -1
  79. package/dist/workstation/config.js +33 -9
  80. package/dist/workstation/connection/encryptedChannel.js +4 -1
  81. package/dist/workstation/control/bindingStore.js +204 -0
  82. package/dist/workstation/control/commands.js +125 -0
  83. package/dist/workstation/control/daemonIdentity.js +143 -0
  84. package/dist/workstation/control/deviceCommands.js +191 -0
  85. package/dist/workstation/control/deviceProtocol.js +68 -0
  86. package/dist/workstation/control/enrollment.js +106 -0
  87. package/dist/workstation/control/inspect.js +71 -0
  88. package/dist/workstation/control/locks.js +110 -0
  89. package/dist/workstation/control/protocol.js +40 -0
  90. package/dist/workstation/control/restartGuard.js +46 -0
  91. package/dist/workstation/control/rootFormat.js +16 -0
  92. package/dist/workstation/control/runtimeClient.js +152 -0
  93. package/dist/workstation/control/runtimeHost.js +235 -0
  94. package/dist/workstation/control/serverIdentity.js +35 -0
  95. package/dist/workstation/control/updateTransition.js +91 -0
  96. package/dist/workstation/daemon.js +266 -264
  97. package/dist/workstation/daemonctl.js +226 -25
  98. package/dist/workstation/desktopControl/audio.js +99 -0
  99. package/dist/workstation/desktopControl/automationDevice.js +219 -0
  100. package/dist/workstation/desktopControl/automationJson.js +74 -0
  101. package/dist/workstation/desktopControl/automationLocalState.js +23 -0
  102. package/dist/workstation/desktopControl/automationProtocol.js +253 -0
  103. package/dist/workstation/desktopControl/automationSchema.generated.js +1 -0
  104. package/dist/workstation/desktopControl/automationSession.js +81 -0
  105. package/dist/workstation/desktopControl/automationTransport.js +138 -0
  106. package/dist/workstation/desktopControl/capabilities.js +93 -0
  107. package/dist/workstation/desktopControl/captureModes.js +9 -0
  108. package/dist/workstation/desktopControl/captureTargetLabel.js +9 -0
  109. package/dist/workstation/desktopControl/deviceAdmission.js +229 -0
  110. package/dist/workstation/desktopControl/deviceOwnership.js +130 -0
  111. package/dist/workstation/desktopControl/deviceSockets.js +246 -0
  112. package/dist/workstation/desktopControl/helperProcess.js +602 -0
  113. package/dist/workstation/desktopControl/hostGrant.js +65 -0
  114. package/dist/workstation/desktopControl/lobby.js +131 -0
  115. package/dist/workstation/desktopControl/macProvider.js +156 -0
  116. package/dist/workstation/desktopControl/media.js +331 -0
  117. package/dist/workstation/desktopControl/nativeDeviceAdmission.js +207 -0
  118. package/dist/workstation/desktopControl/nativeProvider.js +1044 -0
  119. package/dist/workstation/desktopControl/networkEvidence.js +43 -0
  120. package/dist/workstation/desktopControl/overview.js +58 -0
  121. package/dist/workstation/desktopControl/ownershipProtocol.js +31 -0
  122. package/dist/workstation/desktopControl/protocol.js +311 -0
  123. package/dist/workstation/desktopControl/provider.js +1 -0
  124. package/dist/workstation/desktopControl/quicMedia.js +240 -0
  125. package/dist/workstation/desktopControl/rateController.js +226 -0
  126. package/dist/workstation/desktopControl/supervisor.js +1774 -0
  127. package/dist/workstation/desktopControl/transport.js +27 -0
  128. package/dist/workstation/desktopControl/wallpaper.js +98 -0
  129. package/dist/workstation/exec/policy.js +5 -0
  130. package/dist/workstation/files/client.js +10 -1
  131. package/dist/workstation/files/credentials.js +142 -0
  132. package/dist/workstation/files/host.js +9 -2
  133. package/dist/workstation/files/text.js +42 -8
  134. package/dist/workstation/journal.js +5 -2
  135. package/dist/workstation/keepers/acp/client.js +38 -2
  136. package/dist/workstation/keepers/kit/buildVersion.js +35 -11
  137. package/dist/workstation/keepers/kit/bundle.js +21 -17
  138. package/dist/workstation/keepers/kit/client.js +2 -2
  139. package/dist/workstation/keepers/kit/launcher.js +123 -7
  140. package/dist/workstation/keepers/kit/paths.js +2 -1
  141. package/dist/workstation/keepers/legacy.js +16 -2
  142. package/dist/workstation/keepers/policy.js +5 -34
  143. package/dist/workstation/keepers/terminal/client.js +115 -23
  144. package/dist/workstation/keepers/terminal/exec.js +6 -3
  145. package/dist/workstation/keepers/terminal/frames.js +73 -0
  146. package/dist/workstation/keepers/terminal/generations.js +5 -8
  147. package/dist/workstation/keepers/terminal/gridProtocol.js +16 -0
  148. package/dist/workstation/keepers/terminal/input.js +146 -0
  149. package/dist/workstation/keepers/terminal/keeper.js +22 -8
  150. package/dist/workstation/keepers/terminal/main.js +1 -1
  151. package/dist/workstation/keepers/terminal/metadata.js +138 -0
  152. package/dist/workstation/keepers/terminal/protocol.js +4 -28
  153. package/dist/workstation/keepers/terminal/pty.js +1 -330
  154. package/dist/workstation/keepers/terminal/screen.js +246 -0
  155. package/dist/workstation/keepers/terminal/sessions.js +526 -0
  156. package/dist/workstation/managedInstall.js +57 -0
  157. package/dist/workstation/oauth/deviceAuthorization.js +31 -17
  158. package/dist/workstation/protocol/agent.js +2 -15
  159. package/dist/workstation/protocol/management.js +113 -5
  160. package/dist/workstation/protocol/runtimeDiagnostics.js +15 -0
  161. package/dist/workstation/rd/artifact.js +120 -0
  162. package/dist/workstation/rd/control.js +117 -0
  163. package/dist/workstation/rd/display.js +29 -0
  164. package/dist/workstation/rd/host.js +213 -0
  165. package/dist/workstation/rd/migrationReadiness.js +46 -0
  166. package/dist/workstation/rd/provider.js +256 -0
  167. package/dist/workstation/rd/sessions.js +391 -0
  168. package/dist/workstation/rd/state.js +57 -0
  169. package/dist/workstation/rd/update.js +168 -0
  170. package/dist/workstation/rd/upkeep.js +80 -0
  171. package/dist/workstation/register.js +27 -37
  172. package/dist/workstation/registrations/enrollment.js +250 -0
  173. package/dist/workstation/registrations/enrollmentPending.js +361 -0
  174. package/dist/workstation/registrations/migration.js +356 -0
  175. package/dist/workstation/registrations/model.js +34 -0
  176. package/dist/workstation/registrations/removal.js +222 -0
  177. package/dist/workstation/registrations/store.js +737 -0
  178. package/dist/workstation/runtime/desktopBinding.js +20 -0
  179. package/dist/workstation/runtime/deviceDesktop.js +193 -0
  180. package/dist/workstation/runtime/deviceRuntime.js +568 -0
  181. package/dist/workstation/runtime/deviceServices.js +117 -0
  182. package/dist/workstation/runtime/events.js +20 -0
  183. package/dist/workstation/runtime/ownership.js +100 -0
  184. package/dist/workstation/runtime/registrationRuntime.js +575 -0
  185. package/dist/workstation/runtime/remoteDesktopAdapter.js +186 -0
  186. package/dist/workstation/runtime/removalActivity.js +184 -0
  187. package/dist/workstation/service/installer.js +197 -14
  188. package/dist/workstation/service/launchd.js +2 -0
  189. package/dist/workstation/service/systemd.js +1 -0
  190. package/dist/workstation/storageMetrics.js +118 -0
  191. package/dist/workstation/systemMetrics.js +47 -92
  192. package/dist/workstation/toolchain.js +94 -0
  193. package/dist/workstation/tools/browser.js +28 -4
  194. package/dist/workstation/tools/client.js +8 -27
  195. package/dist/workstation/tools/info.js +6 -13
  196. package/dist/workstation/tools/terminalRouter.js +17 -172
  197. package/dist/workstation/tools/validation.js +55 -1
  198. package/dist/workstation/update/archiveEvidence.js +206 -0
  199. package/dist/workstation/update/artifact.js +2 -1
  200. package/dist/workstation/update/coordinator.js +34 -13
  201. package/dist/workstation/update/deviceCompatibility.js +119 -0
  202. package/dist/workstation/update/deviceCoordinator.js +369 -0
  203. package/dist/workstation/update/deviceHealth.js +104 -0
  204. package/dist/workstation/update/deviceHealthProtocol.js +26 -0
  205. package/dist/workstation/update/deviceRecoveryLifecycle.js +46 -0
  206. package/dist/workstation/update/deviceRollback.js +78 -0
  207. package/dist/workstation/update/globalInstall.js +186 -0
  208. package/dist/workstation/update/legacyBridge.js +414 -0
  209. package/dist/workstation/update/legacyOwner.js +39 -0
  210. package/dist/workstation/update/npmInstaller.js +51 -25
  211. package/dist/workstation/update/privateDurability.js +61 -0
  212. package/dist/workstation/update/rdRollback.js +145 -0
  213. package/dist/workstation/update/recoveryPlan.js +124 -0
  214. package/dist/workstation/update/runUpdate.js +10 -3
  215. package/dist/workstation/update/taskRegistry.js +161 -3
  216. package/dist/workstation/update/watchdog.js +29806 -333
  217. package/dist/workstation/update/watchdogProcess.js +72 -0
  218. package/package.json +11 -7
  219. package/skills/axom/SKILL.md +11 -11
  220. package/skills/axom-definition/SKILL.md +3 -3
  221. package/skills/axom-platform/SKILL.md +3 -3
  222. package/dist/axom-patch/cli/probes/qoder-steering.mjs +0 -89
  223. package/dist/axom-patch/cli/probes/qoder-subagent-origin.mjs +0 -61
  224. package/dist/axom-patch/cli/qoder-steering.json +0 -52
  225. package/dist/axom-patch/cli/qoder-subagent-origin.json +0 -73
  226. package/dist/commands/guide.js +0 -38
  227. package/dist/runtime-deps/qoder-sdk/package-lock.json +0 -1189
  228. package/dist/runtime-deps/qoder-sdk/package.json +0 -8
  229. package/dist/workstation/acp/delegationMcp.js +0 -24
  230. package/dist/workstation/acp/processor/sideChannel.js +0 -80
  231. package/dist/workstation/acp/qoderStartup.js +0 -45
  232. package/dist/workstation/acp/test/raw/qoderCaptures.js +0 -125
  233. package/dist/workstation/qoderAdapter/backgroundProcess.js +0 -118
  234. package/dist/workstation/qoderAdapter/backgroundTerminal.js +0 -193
  235. package/dist/workstation/qoderAdapter/backgroundTerminalFold.js +0 -372
  236. package/dist/workstation/qoderAdapter/callbackContext.js +0 -76
  237. package/dist/workstation/qoderAdapter/completedContinuation.js +0 -139
  238. package/dist/workstation/qoderAdapter/contract.js +0 -78
  239. package/dist/workstation/qoderAdapter/foregroundOutput.js +0 -36
  240. package/dist/workstation/qoderAdapter/history.js +0 -85
  241. package/dist/workstation/qoderAdapter/identityIndex.js +0 -191
  242. package/dist/workstation/qoderAdapter/inputQueue.js +0 -42
  243. package/dist/workstation/qoderAdapter/main.js +0 -95
  244. package/dist/workstation/qoderAdapter/mapper.js +0 -117
  245. package/dist/workstation/qoderAdapter/observerPlugin.js +0 -245
  246. package/dist/workstation/qoderAdapter/observerReceiver.js +0 -187
  247. package/dist/workstation/qoderAdapter/permissions.js +0 -61
  248. package/dist/workstation/qoderAdapter/profile.js +0 -93
  249. package/dist/workstation/qoderAdapter/sdkDriver.js +0 -48
  250. package/dist/workstation/qoderAdapter/sdkRuntime.js +0 -93
  251. package/dist/workstation/qoderAdapter/sdkRuntimeInstall.js +0 -145
  252. package/dist/workstation/qoderAdapter/session.js +0 -319
  253. package/dist/workstation/qoderAdapter/subagentMeta.js +0 -28
  254. package/dist/workstation/qoderAdapter/subwork.js +0 -85
  255. package/dist/workstation/qoderAdapter/taskControl.js +0 -38
  256. package/dist/workstation/qoderAdapter/taskLedger.js +0 -163
  257. package/dist/workstation/qoderAdapter/usage.js +0 -37
  258. /package/dist/workstation/{qoderAdapter/types.js → desktopControl/automationPreparation.js} +0 -0
@@ -5072,375 +5072,10 @@ ${i3.join("\n")}
5072
5072
  }
5073
5073
  });
5074
5074
 
5075
- // node_modules/@xterm/addon-serialize/lib/addon-serialize.js
5076
- var require_addon_serialize = __commonJS({
5077
- "node_modules/@xterm/addon-serialize/lib/addon-serialize.js"(exports, module) {
5078
- !(function(t, e) {
5079
- "object" == typeof exports && "object" == typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define([], e) : "object" == typeof exports ? exports.SerializeAddon = e() : t.SerializeAddon = e();
5080
- })(globalThis, (() => (() => {
5081
- "use strict";
5082
- var t = { 992: (t2, e2, s2) => {
5083
- Object.defineProperty(e2, "__esModule", { value: true }), e2.DEFAULT_ANSI_COLORS = void 0;
5084
- const r2 = s2(993);
5085
- e2.DEFAULT_ANSI_COLORS = Object.freeze((() => {
5086
- const t3 = [r2.css.toColor("#2e3436"), r2.css.toColor("#cc0000"), r2.css.toColor("#4e9a06"), r2.css.toColor("#c4a000"), r2.css.toColor("#3465a4"), r2.css.toColor("#75507b"), r2.css.toColor("#06989a"), r2.css.toColor("#d3d7cf"), r2.css.toColor("#555753"), r2.css.toColor("#ef2929"), r2.css.toColor("#8ae234"), r2.css.toColor("#fce94f"), r2.css.toColor("#729fcf"), r2.css.toColor("#ad7fa8"), r2.css.toColor("#34e2e2"), r2.css.toColor("#eeeeec")], e3 = [0, 95, 135, 175, 215, 255];
5087
- for (let s3 = 0; s3 < 216; s3++) {
5088
- const i = e3[s3 / 36 % 6 | 0], o = e3[s3 / 6 % 6 | 0], n = e3[s3 % 6];
5089
- t3.push({ css: r2.channels.toCss(i, o, n), rgba: r2.channels.toRgba(i, o, n) });
5090
- }
5091
- for (let e4 = 0; e4 < 24; e4++) {
5092
- const s3 = 8 + 10 * e4;
5093
- t3.push({ css: r2.channels.toCss(s3, s3, s3), rgba: r2.channels.toRgba(s3, s3, s3) });
5094
- }
5095
- return t3;
5096
- })());
5097
- }, 993: (t2, e2) => {
5098
- Object.defineProperty(e2, "__esModule", { value: true }), e2.rgba = e2.rgb = e2.css = e2.color = e2.channels = e2.NULL_COLOR = void 0, e2.toPaddedHex = c, e2.contrastRatio = _;
5099
- let s2 = 0, r2 = 0, i = 0, o = 0;
5100
- var n, l, a, u, h;
5101
- function c(t3) {
5102
- const e3 = t3.toString(16);
5103
- return e3.length < 2 ? "0" + e3 : e3;
5104
- }
5105
- function _(t3, e3) {
5106
- return t3 < e3 ? (e3 + 0.05) / (t3 + 0.05) : (t3 + 0.05) / (e3 + 0.05);
5107
- }
5108
- e2.NULL_COLOR = { css: "#00000000", rgba: 0 }, (function(t3) {
5109
- t3.toCss = function(t4, e3, s3, r3) {
5110
- return void 0 !== r3 ? `#${c(t4)}${c(e3)}${c(s3)}${c(r3)}` : `#${c(t4)}${c(e3)}${c(s3)}`;
5111
- }, t3.toRgba = function(t4, e3, s3, r3 = 255) {
5112
- return (t4 << 24 | e3 << 16 | s3 << 8 | r3) >>> 0;
5113
- }, t3.toColor = function(e3, s3, r3, i2) {
5114
- return { css: t3.toCss(e3, s3, r3, i2), rgba: t3.toRgba(e3, s3, r3, i2) };
5115
- };
5116
- })(n || (e2.channels = n = {})), (function(t3) {
5117
- function e3(t4, e4) {
5118
- return o = Math.round(255 * e4), [s2, r2, i] = h.toChannels(t4.rgba), { css: n.toCss(s2, r2, i, o), rgba: n.toRgba(s2, r2, i, o) };
5119
- }
5120
- t3.blend = function(t4, e4) {
5121
- if (o = (255 & e4.rgba) / 255, 1 === o) return { css: e4.css, rgba: e4.rgba };
5122
- const l2 = e4.rgba >> 24 & 255, a2 = e4.rgba >> 16 & 255, u2 = e4.rgba >> 8 & 255, h2 = t4.rgba >> 24 & 255, c2 = t4.rgba >> 16 & 255, _2 = t4.rgba >> 8 & 255;
5123
- return s2 = h2 + Math.round((l2 - h2) * o), r2 = c2 + Math.round((a2 - c2) * o), i = _2 + Math.round((u2 - _2) * o), { css: n.toCss(s2, r2, i), rgba: n.toRgba(s2, r2, i) };
5124
- }, t3.isOpaque = function(t4) {
5125
- return !(255 & ~t4.rgba);
5126
- }, t3.ensureContrastRatio = function(t4, e4, s3) {
5127
- const r3 = h.ensureContrastRatio(t4.rgba, e4.rgba, s3);
5128
- if (r3) return n.toColor(r3 >> 24 & 255, r3 >> 16 & 255, r3 >> 8 & 255);
5129
- }, t3.opaque = function(t4) {
5130
- const e4 = (255 | t4.rgba) >>> 0;
5131
- return [s2, r2, i] = h.toChannels(e4), { css: n.toCss(s2, r2, i), rgba: e4 };
5132
- }, t3.opacity = e3, t3.multiplyOpacity = function(t4, s3) {
5133
- return o = 255 & t4.rgba, e3(t4, o * s3 / 255);
5134
- }, t3.toColorRGB = function(t4) {
5135
- return [t4.rgba >> 24 & 255, t4.rgba >> 16 & 255, t4.rgba >> 8 & 255];
5136
- };
5137
- })(l || (e2.color = l = {})), (function(t3) {
5138
- let e3, l2;
5139
- try {
5140
- const t4 = document.createElement("canvas");
5141
- t4.width = 1, t4.height = 1;
5142
- const s3 = t4.getContext("2d", { willReadFrequently: true });
5143
- s3 && (e3 = s3, e3.globalCompositeOperation = "copy", l2 = e3.createLinearGradient(0, 0, 1, 1));
5144
- } catch {
5145
- }
5146
- t3.toColor = function(t4) {
5147
- if (t4.match(/#[\da-f]{3,8}/i)) switch (t4.length) {
5148
- case 4:
5149
- return s2 = parseInt(t4.slice(1, 2).repeat(2), 16), r2 = parseInt(t4.slice(2, 3).repeat(2), 16), i = parseInt(t4.slice(3, 4).repeat(2), 16), n.toColor(s2, r2, i);
5150
- case 5:
5151
- return s2 = parseInt(t4.slice(1, 2).repeat(2), 16), r2 = parseInt(t4.slice(2, 3).repeat(2), 16), i = parseInt(t4.slice(3, 4).repeat(2), 16), o = parseInt(t4.slice(4, 5).repeat(2), 16), n.toColor(s2, r2, i, o);
5152
- case 7:
5153
- return { css: t4, rgba: (parseInt(t4.slice(1), 16) << 8 | 255) >>> 0 };
5154
- case 9:
5155
- return { css: t4, rgba: parseInt(t4.slice(1), 16) >>> 0 };
5156
- }
5157
- const a2 = t4.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(,\s*(0|1|\d?\.(\d+))\s*)?\)/);
5158
- if (a2) return s2 = parseInt(a2[1]), r2 = parseInt(a2[2]), i = parseInt(a2[3]), o = Math.round(255 * (void 0 === a2[5] ? 1 : parseFloat(a2[5]))), n.toColor(s2, r2, i, o);
5159
- if (!e3 || !l2) throw new Error("css.toColor: Unsupported css format");
5160
- if (e3.fillStyle = l2, e3.fillStyle = t4, "string" != typeof e3.fillStyle) throw new Error("css.toColor: Unsupported css format");
5161
- if (e3.fillRect(0, 0, 1, 1), [s2, r2, i, o] = e3.getImageData(0, 0, 1, 1).data, 255 !== o) throw new Error("css.toColor: Unsupported css format");
5162
- return { rgba: n.toRgba(s2, r2, i, o), css: t4 };
5163
- };
5164
- })(a || (e2.css = a = {})), (function(t3) {
5165
- function e3(t4, e4, s3) {
5166
- const r3 = t4 / 255, i2 = e4 / 255, o2 = s3 / 255;
5167
- return 0.2126 * (r3 <= 0.03928 ? r3 / 12.92 : Math.pow((r3 + 0.055) / 1.055, 2.4)) + 0.7152 * (i2 <= 0.03928 ? i2 / 12.92 : Math.pow((i2 + 0.055) / 1.055, 2.4)) + 0.0722 * (o2 <= 0.03928 ? o2 / 12.92 : Math.pow((o2 + 0.055) / 1.055, 2.4));
5168
- }
5169
- t3.relativeLuminance = function(t4) {
5170
- return e3(t4 >> 16 & 255, t4 >> 8 & 255, 255 & t4);
5171
- }, t3.relativeLuminance2 = e3;
5172
- })(u || (e2.rgb = u = {})), (function(t3) {
5173
- function e3(t4, e4, s3) {
5174
- const r3 = t4 >> 24 & 255, i2 = t4 >> 16 & 255, o2 = t4 >> 8 & 255;
5175
- let n2 = e4 >> 24 & 255, l3 = e4 >> 16 & 255, a2 = e4 >> 8 & 255, h2 = _(u.relativeLuminance2(n2, l3, a2), u.relativeLuminance2(r3, i2, o2));
5176
- for (; h2 < s3 && (n2 > 0 || l3 > 0 || a2 > 0); ) n2 -= Math.max(0, Math.ceil(0.1 * n2)), l3 -= Math.max(0, Math.ceil(0.1 * l3)), a2 -= Math.max(0, Math.ceil(0.1 * a2)), h2 = _(u.relativeLuminance2(n2, l3, a2), u.relativeLuminance2(r3, i2, o2));
5177
- return (n2 << 24 | l3 << 16 | a2 << 8 | 255) >>> 0;
5178
- }
5179
- function l2(t4, e4, s3) {
5180
- const r3 = t4 >> 24 & 255, i2 = t4 >> 16 & 255, o2 = t4 >> 8 & 255;
5181
- let n2 = e4 >> 24 & 255, l3 = e4 >> 16 & 255, a2 = e4 >> 8 & 255, h2 = _(u.relativeLuminance2(n2, l3, a2), u.relativeLuminance2(r3, i2, o2));
5182
- for (; h2 < s3 && (n2 < 255 || l3 < 255 || a2 < 255); ) n2 = Math.min(255, n2 + Math.ceil(0.1 * (255 - n2))), l3 = Math.min(255, l3 + Math.ceil(0.1 * (255 - l3))), a2 = Math.min(255, a2 + Math.ceil(0.1 * (255 - a2))), h2 = _(u.relativeLuminance2(n2, l3, a2), u.relativeLuminance2(r3, i2, o2));
5183
- return (n2 << 24 | l3 << 16 | a2 << 8 | 255) >>> 0;
5184
- }
5185
- t3.blend = function(t4, e4) {
5186
- if (o = (255 & e4) / 255, 1 === o) return e4;
5187
- const l3 = e4 >> 24 & 255, a2 = e4 >> 16 & 255, u2 = e4 >> 8 & 255, h2 = t4 >> 24 & 255, c2 = t4 >> 16 & 255, _2 = t4 >> 8 & 255;
5188
- return s2 = h2 + Math.round((l3 - h2) * o), r2 = c2 + Math.round((a2 - c2) * o), i = _2 + Math.round((u2 - _2) * o), n.toRgba(s2, r2, i);
5189
- }, t3.ensureContrastRatio = function(t4, s3, r3) {
5190
- const i2 = u.relativeLuminance(t4 >> 8), o2 = u.relativeLuminance(s3 >> 8);
5191
- if (_(i2, o2) < r3) {
5192
- if (o2 < i2) {
5193
- const o3 = e3(t4, s3, r3), n3 = _(i2, u.relativeLuminance(o3 >> 8));
5194
- if (n3 < r3) {
5195
- const e4 = l2(t4, s3, r3);
5196
- return n3 > _(i2, u.relativeLuminance(e4 >> 8)) ? o3 : e4;
5197
- }
5198
- return o3;
5199
- }
5200
- const n2 = l2(t4, s3, r3), a2 = _(i2, u.relativeLuminance(n2 >> 8));
5201
- if (a2 < r3) {
5202
- const o3 = e3(t4, s3, r3);
5203
- return a2 > _(i2, u.relativeLuminance(o3 >> 8)) ? n2 : o3;
5204
- }
5205
- return n2;
5206
- }
5207
- }, t3.reduceLuminance = e3, t3.increaseLuminance = l2, t3.toChannels = function(t4) {
5208
- return [t4 >> 24 & 255, t4 >> 16 & 255, t4 >> 8 & 255, 255 & t4];
5209
- };
5210
- })(h || (e2.rgba = h = {}));
5211
- } }, e = {};
5212
- function s(r2) {
5213
- var i = e[r2];
5214
- if (void 0 !== i) return i.exports;
5215
- var o = e[r2] = { exports: {} };
5216
- return t[r2](o, o.exports, s), o.exports;
5217
- }
5218
- var r = {};
5219
- return (() => {
5220
- var t2 = r;
5221
- Object.defineProperty(t2, "__esModule", { value: true }), t2.HTMLSerializeHandler = t2.SerializeAddon = void 0;
5222
- const e2 = s(992);
5223
- function i(t3, e3, s2) {
5224
- return Math.max(e3, Math.min(t3, s2));
5225
- }
5226
- class o {
5227
- constructor(t3) {
5228
- this._buffer = t3;
5229
- }
5230
- serialize(t3, e3) {
5231
- const s2 = this._buffer.getNullCell(), r2 = this._buffer.getNullCell();
5232
- let i2 = s2;
5233
- const o2 = t3.start.y, n2 = t3.end.y, l2 = t3.start.x, a2 = t3.end.x;
5234
- this._beforeSerialize(n2 - o2, o2, n2);
5235
- for (let e4 = o2; e4 <= n2; e4++) {
5236
- const o3 = this._buffer.getLine(e4);
5237
- if (o3) {
5238
- const n3 = e4 === t3.start.y ? l2 : 0, u2 = e4 === t3.end.y ? a2 : o3.length;
5239
- for (let t4 = n3; t4 < u2; t4++) {
5240
- const n4 = o3.getCell(t4, i2 === s2 ? r2 : s2);
5241
- n4 ? (this._nextCell(n4, i2, e4, t4), i2 = n4) : console.warn(`Can't get cell at row=${e4}, col=${t4}`);
5242
- }
5243
- }
5244
- this._rowEnd(e4, e4 === n2);
5245
- }
5246
- return this._afterSerialize(), this._serializeString(e3);
5247
- }
5248
- _nextCell(t3, e3, s2, r2) {
5249
- }
5250
- _rowEnd(t3, e3) {
5251
- }
5252
- _beforeSerialize(t3, e3, s2) {
5253
- }
5254
- _afterSerialize() {
5255
- }
5256
- _serializeString(t3) {
5257
- return "";
5258
- }
5259
- }
5260
- function n(t3, e3) {
5261
- return t3.getFgColorMode() === e3.getFgColorMode() && t3.getFgColor() === e3.getFgColor();
5262
- }
5263
- function l(t3, e3) {
5264
- return t3.getBgColorMode() === e3.getBgColorMode() && t3.getBgColor() === e3.getBgColor();
5265
- }
5266
- function a(t3, e3) {
5267
- return t3.isInverse() === e3.isInverse() && t3.isBold() === e3.isBold() && t3.isUnderline() === e3.isUnderline() && t3.isOverline() === e3.isOverline() && t3.isBlink() === e3.isBlink() && t3.isInvisible() === e3.isInvisible() && t3.isItalic() === e3.isItalic() && t3.isDim() === e3.isDim() && t3.isStrikethrough() === e3.isStrikethrough();
5268
- }
5269
- class u extends o {
5270
- constructor(t3, e3) {
5271
- super(t3), this._terminal = e3, this._rowIndex = 0, this._allRows = new Array(), this._allRowSeparators = new Array(), this._currentRow = "", this._nullCellCount = 0, this._cursorStyle = this._buffer.getNullCell(), this._cursorStyleRow = 0, this._cursorStyleCol = 0, this._backgroundCell = this._buffer.getNullCell(), this._firstRow = 0, this._lastCursorRow = 0, this._lastCursorCol = 0, this._lastContentCursorRow = 0, this._lastContentCursorCol = 0, this._thisRowLastChar = this._buffer.getNullCell(), this._thisRowLastSecondChar = this._buffer.getNullCell(), this._nextRowFirstChar = this._buffer.getNullCell();
5272
- }
5273
- _beforeSerialize(t3, e3, s2) {
5274
- this._allRows = new Array(t3), this._lastContentCursorRow = e3, this._lastCursorRow = e3, this._firstRow = e3;
5275
- }
5276
- _rowEnd(t3, e3) {
5277
- this._nullCellCount > 0 && !l(this._cursorStyle, this._backgroundCell) && (this._currentRow += `\x1B[${this._nullCellCount}X`);
5278
- let s2 = "";
5279
- if (!e3) {
5280
- t3 - this._firstRow >= this._terminal.rows && this._buffer.getLine(this._cursorStyleRow)?.getCell(this._cursorStyleCol, this._backgroundCell);
5281
- const e4 = this._buffer.getLine(t3), r2 = this._buffer.getLine(t3 + 1);
5282
- if (r2.isWrapped) {
5283
- s2 = "";
5284
- const i2 = e4.getCell(e4.length - 1, this._thisRowLastChar), o2 = e4.getCell(e4.length - 2, this._thisRowLastSecondChar), n2 = r2.getCell(0, this._nextRowFirstChar), a2 = n2.getWidth() > 1;
5285
- let u2 = false;
5286
- (n2.getChars() && a2 ? this._nullCellCount <= 1 : this._nullCellCount <= 0) && ((i2.getChars() || 0 === i2.getWidth()) && l(i2, n2) && (u2 = true), a2 && (o2.getChars() || 0 === o2.getWidth()) && l(i2, n2) && l(o2, n2) && (u2 = true)), u2 || (s2 = "-".repeat(this._nullCellCount + 1), s2 += "\x1B[1D\x1B[1X", this._nullCellCount > 0 && (s2 += "\x1B[A", s2 += `\x1B[${e4.length - this._nullCellCount}C`, s2 += `\x1B[${this._nullCellCount}X`, s2 += `\x1B[${e4.length - this._nullCellCount}D`, s2 += "\x1B[B"), this._lastContentCursorRow = t3 + 1, this._lastContentCursorCol = 0, this._lastCursorRow = t3 + 1, this._lastCursorCol = 0);
5287
- } else s2 = "\r\n", this._lastCursorRow = t3 + 1, this._lastCursorCol = 0;
5288
- }
5289
- this._allRows[this._rowIndex] = this._currentRow, this._allRowSeparators[this._rowIndex++] = s2, this._currentRow = "", this._nullCellCount = 0;
5290
- }
5291
- _diffStyle(t3, e3) {
5292
- const s2 = [], r2 = !n(t3, e3), i2 = !l(t3, e3), o2 = !a(t3, e3);
5293
- if (r2 || i2 || o2) if (t3.isAttributeDefault()) e3.isAttributeDefault() || s2.push(0);
5294
- else {
5295
- if (r2) {
5296
- const e4 = t3.getFgColor();
5297
- t3.isFgRGB() ? s2.push(38, 2, e4 >>> 16 & 255, e4 >>> 8 & 255, 255 & e4) : t3.isFgPalette() ? e4 >= 16 ? s2.push(38, 5, e4) : s2.push(8 & e4 ? 90 + (7 & e4) : 30 + (7 & e4)) : s2.push(39);
5298
- }
5299
- if (i2) {
5300
- const e4 = t3.getBgColor();
5301
- t3.isBgRGB() ? s2.push(48, 2, e4 >>> 16 & 255, e4 >>> 8 & 255, 255 & e4) : t3.isBgPalette() ? e4 >= 16 ? s2.push(48, 5, e4) : s2.push(8 & e4 ? 100 + (7 & e4) : 40 + (7 & e4)) : s2.push(49);
5302
- }
5303
- o2 && (t3.isInverse() !== e3.isInverse() && s2.push(t3.isInverse() ? 7 : 27), t3.isBold() !== e3.isBold() && s2.push(t3.isBold() ? 1 : 22), t3.isUnderline() !== e3.isUnderline() && s2.push(t3.isUnderline() ? 4 : 24), t3.isOverline() !== e3.isOverline() && s2.push(t3.isOverline() ? 53 : 55), t3.isBlink() !== e3.isBlink() && s2.push(t3.isBlink() ? 5 : 25), t3.isInvisible() !== e3.isInvisible() && s2.push(t3.isInvisible() ? 8 : 28), t3.isItalic() !== e3.isItalic() && s2.push(t3.isItalic() ? 3 : 23), t3.isDim() !== e3.isDim() && s2.push(t3.isDim() ? 2 : 22), t3.isStrikethrough() !== e3.isStrikethrough() && s2.push(t3.isStrikethrough() ? 9 : 29));
5304
- }
5305
- return s2;
5306
- }
5307
- _nextCell(t3, e3, s2, r2) {
5308
- if (0 === t3.getWidth()) return;
5309
- const i2 = "" === t3.getChars(), o2 = this._diffStyle(t3, this._cursorStyle);
5310
- if (i2 ? !l(this._cursorStyle, t3) : o2.length > 0) {
5311
- this._nullCellCount > 0 && (l(this._cursorStyle, this._backgroundCell) || (this._currentRow += `\x1B[${this._nullCellCount}X`), this._currentRow += `\x1B[${this._nullCellCount}C`, this._nullCellCount = 0), this._lastContentCursorRow = this._lastCursorRow = s2, this._lastContentCursorCol = this._lastCursorCol = r2, this._currentRow += `\x1B[${o2.join(";")}m`;
5312
- const t4 = this._buffer.getLine(s2);
5313
- void 0 !== t4 && (t4.getCell(r2, this._cursorStyle), this._cursorStyleRow = s2, this._cursorStyleCol = r2);
5314
- }
5315
- i2 ? this._nullCellCount += t3.getWidth() : (this._nullCellCount > 0 && (l(this._cursorStyle, this._backgroundCell) || (this._currentRow += `\x1B[${this._nullCellCount}X`), this._currentRow += `\x1B[${this._nullCellCount}C`, this._nullCellCount = 0), this._currentRow += t3.getChars(), this._lastContentCursorRow = this._lastCursorRow = s2, this._lastContentCursorCol = this._lastCursorCol = r2 + t3.getWidth());
5316
- }
5317
- _serializeString(t3) {
5318
- let e3 = this._allRows.length;
5319
- this._buffer.length - this._firstRow <= this._terminal.rows && (e3 = this._lastContentCursorRow + 1 - this._firstRow, this._lastCursorCol = this._lastContentCursorCol, this._lastCursorRow = this._lastContentCursorRow);
5320
- let s2 = "";
5321
- for (let t4 = 0; t4 < e3; t4++) s2 += this._allRows[t4], t4 + 1 < e3 && (s2 += this._allRowSeparators[t4]);
5322
- if (!t3) {
5323
- const t4 = this._buffer.baseY + this._buffer.cursorY, e4 = this._buffer.cursorX, i3 = (t5) => {
5324
- t5 > 0 ? s2 += `\x1B[${t5}C` : t5 < 0 && (s2 += `\x1B[${-t5}D`);
5325
- };
5326
- (t4 !== this._lastCursorRow || e4 !== this._lastCursorCol) && ((r2 = t4 - this._lastCursorRow) > 0 ? s2 += `\x1B[${r2}B` : r2 < 0 && (s2 += `\x1B[${-r2}A`), i3(e4 - this._lastCursorCol));
5327
- }
5328
- var r2;
5329
- const i2 = this._terminal._core._inputHandler._curAttrData, o2 = this._diffStyle(i2, this._cursorStyle);
5330
- return o2.length > 0 && (s2 += `\x1B[${o2.join(";")}m`), s2;
5331
- }
5332
- }
5333
- t2.SerializeAddon = class {
5334
- activate(t3) {
5335
- this._terminal = t3;
5336
- }
5337
- _serializeBufferByScrollback(t3, e3, s2) {
5338
- const r2 = e3.length, o2 = void 0 === s2 ? r2 : i(s2 + t3.rows, 0, r2);
5339
- return this._serializeBufferByRange(t3, e3, { start: r2 - o2, end: r2 - 1 }, false);
5340
- }
5341
- _serializeBufferByRange(t3, e3, s2, r2) {
5342
- return new u(e3, t3).serialize({ start: { x: 0, y: "number" == typeof s2.start ? s2.start : s2.start.line }, end: { x: t3.cols, y: "number" == typeof s2.end ? s2.end : s2.end.line } }, r2);
5343
- }
5344
- _serializeBufferAsHTML(t3, e3) {
5345
- const s2 = t3.buffer.active, r2 = new h(s2, t3, e3), o2 = e3.onlySelection ?? false, n2 = e3.range;
5346
- if (n2) return r2.serialize({ start: { x: n2.startCol, y: (n2.startLine, n2.startLine) }, end: { x: t3.cols, y: (n2.endLine, n2.endLine) } });
5347
- if (!o2) {
5348
- const o3 = s2.length, n3 = e3.scrollback, l3 = void 0 === n3 ? o3 : i(n3 + t3.rows, 0, o3);
5349
- return r2.serialize({ start: { x: 0, y: o3 - l3 }, end: { x: t3.cols, y: o3 - 1 } });
5350
- }
5351
- const l2 = this._terminal?.getSelectionPosition();
5352
- return void 0 !== l2 ? r2.serialize({ start: { x: l2.start.x, y: l2.start.y }, end: { x: l2.end.x, y: l2.end.y } }) : "";
5353
- }
5354
- _serializeModes(t3) {
5355
- let e3 = "";
5356
- const s2 = t3.modes;
5357
- if (s2.applicationCursorKeysMode && (e3 += "\x1B[?1h"), s2.applicationKeypadMode && (e3 += "\x1B[?66h"), s2.bracketedPasteMode && (e3 += "\x1B[?2004h"), s2.insertMode && (e3 += "\x1B[4h"), s2.originMode && (e3 += "\x1B[?6h"), s2.reverseWraparoundMode && (e3 += "\x1B[?45h"), s2.sendFocusMode && (e3 += "\x1B[?1004h"), false === s2.wraparoundMode && (e3 += "\x1B[?7l"), "none" !== s2.mouseTrackingMode) switch (s2.mouseTrackingMode) {
5358
- case "x10":
5359
- e3 += "\x1B[?9h";
5360
- break;
5361
- case "vt200":
5362
- e3 += "\x1B[?1000h";
5363
- break;
5364
- case "drag":
5365
- e3 += "\x1B[?1002h";
5366
- break;
5367
- case "any":
5368
- e3 += "\x1B[?1003h";
5369
- }
5370
- return e3;
5371
- }
5372
- serialize(t3) {
5373
- if (!this._terminal) throw new Error("Cannot use addon until it has been loaded");
5374
- let e3 = t3?.range ? this._serializeBufferByRange(this._terminal, this._terminal.buffer.normal, t3.range, true) : this._serializeBufferByScrollback(this._terminal, this._terminal.buffer.normal, t3?.scrollback);
5375
- return t3?.excludeAltBuffer || "alternate" !== this._terminal.buffer.active.type || (e3 += `\x1B[?1049h\x1B[H${this._serializeBufferByScrollback(this._terminal, this._terminal.buffer.alternate, void 0)}`), t3?.excludeModes || (e3 += this._serializeModes(this._terminal)), e3;
5376
- }
5377
- serializeAsHTML(t3) {
5378
- if (!this._terminal) throw new Error("Cannot use addon until it has been loaded");
5379
- return this._serializeBufferAsHTML(this._terminal, t3 || {});
5380
- }
5381
- dispose() {
5382
- }
5383
- };
5384
- class h extends o {
5385
- constructor(t3, s2, r2) {
5386
- super(t3), this._terminal = s2, this._options = r2, this._currentRow = "", this._htmlContent = "", s2._core._themeService ? this._ansiColors = s2._core._themeService.colors.ansi : this._ansiColors = e2.DEFAULT_ANSI_COLORS;
5387
- }
5388
- _padStart(t3, e3, s2) {
5389
- return e3 |= 0, s2 = s2 ?? " ", t3.length > e3 ? t3 : ((e3 -= t3.length) > s2.length && (s2 += s2.repeat(e3 / s2.length)), s2.slice(0, e3) + t3);
5390
- }
5391
- _beforeSerialize(t3, e3, s2) {
5392
- this._htmlContent += "<html><body><!--StartFragment--><pre>";
5393
- let r2 = "#000000", i2 = "#ffffff";
5394
- this._options.includeGlobalBackground && (r2 = this._terminal.options.theme?.foreground ?? "#ffffff", i2 = this._terminal.options.theme?.background ?? "#000000");
5395
- const o2 = [];
5396
- o2.push("color: " + r2 + ";"), o2.push("background-color: " + i2 + ";"), o2.push("font-family: " + this._terminal.options.fontFamily + ";"), o2.push("font-size: " + this._terminal.options.fontSize + "px;"), this._htmlContent += "<div style='" + o2.join(" ") + "'>";
5397
- }
5398
- _afterSerialize() {
5399
- this._htmlContent += "</div>", this._htmlContent += "</pre><!--EndFragment--></body></html>";
5400
- }
5401
- _rowEnd(t3, e3) {
5402
- this._htmlContent += "<div><span>" + this._currentRow + "</span></div>", this._currentRow = "";
5403
- }
5404
- _getHexColor(t3, e3) {
5405
- const s2 = e3 ? t3.getFgColor() : t3.getBgColor();
5406
- return (e3 ? t3.isFgRGB() : t3.isBgRGB()) ? "#" + [s2 >> 16 & 255, s2 >> 8 & 255, 255 & s2].map(((t4) => this._padStart(t4.toString(16), 2, "0"))).join("") : (e3 ? t3.isFgPalette() : t3.isBgPalette()) ? this._ansiColors[s2].css : void 0;
5407
- }
5408
- _diffStyle(t3, e3) {
5409
- const s2 = [], r2 = !n(t3, e3), i2 = !l(t3, e3), o2 = !a(t3, e3);
5410
- if (r2 || i2 || o2) {
5411
- const e4 = this._getHexColor(t3, true);
5412
- e4 && s2.push("color: " + e4 + ";");
5413
- const r3 = this._getHexColor(t3, false);
5414
- return r3 && s2.push("background-color: " + r3 + ";"), t3.isInverse() && s2.push("color: #000000; background-color: #BFBFBF;"), t3.isBold() && s2.push("font-weight: bold;"), t3.isUnderline() && t3.isOverline() ? s2.push("text-decoration: overline underline;") : t3.isUnderline() ? s2.push("text-decoration: underline;") : t3.isOverline() && s2.push("text-decoration: overline;"), t3.isBlink() && s2.push("text-decoration: blink;"), t3.isInvisible() && s2.push("visibility: hidden;"), t3.isItalic() && s2.push("font-style: italic;"), t3.isDim() && s2.push("opacity: 0.5;"), t3.isStrikethrough() && s2.push("text-decoration: line-through;"), s2;
5415
- }
5416
- }
5417
- _nextCell(t3, e3, s2, r2) {
5418
- if (0 === t3.getWidth()) return;
5419
- const i2 = "" === t3.getChars(), o2 = this._diffStyle(t3, e3);
5420
- o2 && (this._currentRow += 0 === o2.length ? "</span><span>" : "</span><span style='" + o2.join(" ") + "'>"), this._currentRow += i2 ? " " : (function(t4) {
5421
- switch (t4) {
5422
- case "&":
5423
- return "&amp;";
5424
- case "<":
5425
- return "&lt;";
5426
- }
5427
- return t4;
5428
- })(t3.getChars());
5429
- }
5430
- _serializeString() {
5431
- return this._htmlContent;
5432
- }
5433
- }
5434
- t2.HTMLSerializeHandler = h;
5435
- })(), r;
5436
- })()));
5437
- }
5438
- });
5439
-
5440
5075
  // src/workstation/keepers/terminal/main.ts
5441
5076
  import { realpathSync } from "node:fs";
5442
- import { createRequire as createRequire2 } from "node:module";
5443
- import path5, { sep } from "node:path";
5077
+ import { createRequire as createRequire3 } from "node:module";
5078
+ import path6, { sep } from "node:path";
5444
5079
 
5445
5080
  // src/workstation/keepers/kit/bootstrap.ts
5446
5081
  import { readFileSync, unlinkSync as unlinkSync2 } from "node:fs";
@@ -5954,19 +5589,19 @@ var Connection = class {
5954
5589
  this.subscriptions.get(frame.stream)?.grant(frame.n, frame.bytes);
5955
5590
  return;
5956
5591
  }
5957
- const reply = (ok, payload, then) => this.send(ok ? { t: "res", id: frame.id, ok: true, result: payload } : { t: "res", id: frame.id, ok: false, error: payload }, then);
5958
- const fail = (code, message, then) => reply(false, wireError(code, message), then);
5592
+ const reply = (ok2, payload, then) => this.send(ok2 ? { t: "res", id: frame.id, ok: true, result: payload } : { t: "res", id: frame.id, ok: false, error: payload }, then);
5593
+ const fail2 = (code, message, then) => reply(false, wireError(code, message), then);
5959
5594
  const { method } = frame;
5960
5595
  const params = frame.params ?? {};
5961
5596
  if (method === "keeper.hello") {
5962
5597
  const token = typeof params.token === "string" ? Buffer.from(params.token) : Buffer.alloc(0);
5963
5598
  const expected = Buffer.from(this.server.token);
5964
5599
  if (token.length !== expected.length || !timingSafeEqual(token, expected)) {
5965
- fail(ErrorCode.badToken, "Invalid keeper token", () => this.destroy());
5600
+ fail2(ErrorCode.badToken, "Invalid keeper token", () => this.destroy());
5966
5601
  return;
5967
5602
  }
5968
5603
  if (!isProtocolWindow(params.daemonProtocol)) {
5969
- fail(ErrorCode.badRequest, "daemonProtocol must be [min, max]");
5604
+ fail2(ErrorCode.badRequest, "daemonProtocol must be [min, max]");
5970
5605
  return;
5971
5606
  }
5972
5607
  this.authenticated = true;
@@ -5976,7 +5611,7 @@ var Connection = class {
5976
5611
  return;
5977
5612
  }
5978
5613
  if (!this.authenticated && method !== "keeper.ping") {
5979
- fail(ErrorCode.unauthenticated, "keeper.hello required");
5614
+ fail2(ErrorCode.unauthenticated, "keeper.hello required");
5980
5615
  return;
5981
5616
  }
5982
5617
  const context = {
@@ -5993,8 +5628,8 @@ var Connection = class {
5993
5628
  this.startSubscriptions();
5994
5629
  } catch (error) {
5995
5630
  this.discardStarts();
5996
- if (error instanceof KeeperMethodError) fail(error.code, error.message);
5997
- else fail(ErrorCode.internal, error instanceof Error ? error.message : String(error));
5631
+ if (error instanceof KeeperMethodError) fail2(error.code, error.message);
5632
+ else fail2(ErrorCode.internal, error instanceof Error ? error.message : String(error));
5998
5633
  }
5999
5634
  })();
6000
5635
  }
@@ -6675,11 +6310,11 @@ var StreamLog = class _StreamLog {
6675
6310
  * (a retry would duplicate it), so the error is reported through `onError` and `stats().syncErrors`/`lastSyncError`
6676
6311
  * instead of being thrown, and the record stays counted as unsynced. Only a failure to write the line itself throws.
6677
6312
  */
6678
- append(input, options = {}) {
6313
+ append(input2, options = {}) {
6679
6314
  this.assertOpen();
6680
- if (typeof input.k !== "string" || input.k === "") throw new TypeError("A stream record needs a non-empty kind `k`");
6681
- if ("o" in input || "t" in input) throw new TypeError("`o` and `t` are stamped by the stream log");
6682
- const { k, ...rest } = input;
6315
+ if (typeof input2.k !== "string" || input2.k === "") throw new TypeError("A stream record needs a non-empty kind `k`");
6316
+ if ("o" in input2 || "t" in input2) throw new TypeError("`o` and `t` are stamped by the stream log");
6317
+ const { k, ...rest } = input2;
6683
6318
  const record = { o: this.nextOffset, t: this.now(), k, ...rest };
6684
6319
  const line = Buffer.from(`${JSON.stringify(record)}
6685
6320
  `);
@@ -7148,24 +6783,7 @@ function environment(value) {
7148
6783
  }
7149
6784
 
7150
6785
  // src/workstation/keepers/terminal/protocol.ts
7151
- var PTY_CAPABILITY = "pty";
7152
- var PTY_UNAVAILABLE_ERROR = "Terminal native dependency unavailable; reinstall CLI with optional node-pty support";
7153
- var PTY_HISTORY_BYTES = 1024 * 1024;
7154
- var PTY_MAX_POLL_BYTES = 64 * 1024;
7155
- var PTY_SCROLLBACK = 1e3;
7156
- var PTY_RESULT_BYTES = 2 * 1024 * 1024;
7157
- var PTY_SESSION_LIMIT = 8;
7158
- var PTY_INPUT_BYTES = 64 * 1024;
7159
- var PTY_LABEL_CHARS = 64;
7160
- var PtyErrorCode = {
7161
- unavailable: "pty_unavailable",
7162
- badRequest: "bad_request",
7163
- notFound: "pty_not_found",
7164
- notController: "pty_not_controller",
7165
- limit: "pty_limit",
7166
- exited: "pty_exited",
7167
- snapshotTooLarge: "pty_snapshot_too_large"
7168
- };
6786
+ var PTY_CAPABILITY = "pty-grid-v1";
7169
6787
  var EXEC_CAPABILITY = "exec";
7170
6788
  var execStream = (execId) => `exec/${execId}`;
7171
6789
  var EXEC_RUNNING_LIMIT = 128;
@@ -7199,7 +6817,7 @@ var MAX_READ_RECORDS = 1024;
7199
6817
  var MAX_READ_BYTES = 1536 * 1024;
7200
6818
  var MAX_GRACE_MS = 10 * 60 * 1e3;
7201
6819
  var EXEC_ID = /^[A-Za-z0-9_.:-]{1,128}$/;
7202
- var START_KEYS = ["execId", "argv", "cwd", "env", "deadlineEpochMs", "killGraceMs", "retainHeadBytes", "maxUncommittedBytes", "onKeeperLoss", "label", "jobId"];
6820
+ var START_KEYS = ["execId", "argv", "cwd", "env", "deadlineEpochMs", "killGraceMs", "retainHeadBytes", "maxUncommittedBytes", "onKeeperLoss", "label", "jobId", "registrationId"];
7203
6821
  function survivesCommit(record, committed) {
7204
6822
  return record.o > committed || record.k === "spawn" || record.k === "out" || record.k === "err";
7205
6823
  }
@@ -7441,6 +7059,8 @@ var ExecHost = class {
7441
7059
  const jobId = params.jobId;
7442
7060
  if (jobId !== void 0 && (typeof jobId !== "string" || !jobId || jobId.length > 256)) badRequest("Invalid jobId");
7443
7061
  if (this.runningCount() >= (this.options.runningLimit ?? EXEC_RUNNING_LIMIT)) throw new KeeperMethodError(ExecErrorCode.limit, "Too many running commands in keeper-terminal");
7062
+ const registrationId = params.registrationId;
7063
+ if (registrationId !== void 0 && (typeof registrationId !== "string" || !registrationId || registrationId.length > 128 || /[\x00-\x1f]/.test(registrationId))) badRequest("Invalid registrationId");
7444
7064
  const meta = {
7445
7065
  v: 1,
7446
7066
  execId,
@@ -7456,6 +7076,7 @@ var ExecHost = class {
7456
7076
  onKeeperLoss,
7457
7077
  createdAtMs: this.now(),
7458
7078
  owner: this.options.owner,
7079
+ ...typeof registrationId === "string" ? { registrationId } : {},
7459
7080
  spawn: null,
7460
7081
  exit: null,
7461
7082
  logReclaimed: null
@@ -7666,14 +7287,14 @@ var ExecHost = class {
7666
7287
  }
7667
7288
  }
7668
7289
  appendOutput(entry, stream, bytes, head) {
7669
- const input = { k: stream, so: entry.streamBytes[stream], b: bytes.toString("base64") };
7290
+ const input2 = { k: stream, so: entry.streamBytes[stream], b: bytes.toString("base64") };
7670
7291
  entry.streamBytes[stream] += bytes.length;
7671
7292
  if (head) {
7672
7293
  entry.headBytes[stream] += bytes.length;
7673
- this.appendKept(entry, input);
7294
+ this.appendKept(entry, input2);
7674
7295
  return;
7675
7296
  }
7676
- const record = entry.log.append(input);
7297
+ const record = entry.log.append(input2);
7677
7298
  const cost = bytes.length + RECORD_OVERHEAD;
7678
7299
  const last = entry.runs[entry.runs.length - 1];
7679
7300
  if (last && last.cost < RUN_COST) {
@@ -7720,8 +7341,8 @@ var ExecHost = class {
7720
7341
  }
7721
7342
  }
7722
7343
  /** Appends to the log AND to keep.jsonl (spawn, gap, exit, head output): records the quota never drops. */
7723
- appendKept(entry, input) {
7724
- const record = entry.log.append(input);
7344
+ appendKept(entry, input2) {
7345
+ const record = entry.log.append(input2);
7725
7346
  if (entry.keepFd === void 0) entry.keepFd = openSync2(path3.join(entry.dir, KEEP_FILE), "a", 384);
7726
7347
  writeAllSync(entry.keepFd, Buffer.from(`${JSON.stringify(record)}
7727
7348
  `));
@@ -7911,6 +7532,7 @@ var ExecHost = class {
7911
7532
  return {
7912
7533
  execId: meta.execId,
7913
7534
  label: meta.label,
7535
+ ...meta.registrationId ? { registrationId: meta.registrationId } : {},
7914
7536
  ...meta.jobId ? { jobId: meta.jobId } : {},
7915
7537
  state: this.stateOf(entry),
7916
7538
  pid: meta.spawn?.pid ?? null,
@@ -8077,314 +7699,1097 @@ function messageOf(error) {
8077
7699
 
8078
7700
  // src/workstation/keepers/terminal/keeper.ts
8079
7701
  import { constants, promises as fs3 } from "node:fs";
7702
+ import { createRequire as createRequire2 } from "node:module";
8080
7703
 
8081
- // src/workstation/keepers/terminal/pty.ts
8082
- var import_headless = __toESM(require_xterm_headless(), 1);
8083
- var import_addon_serialize = __toESM(require_addon_serialize(), 1);
8084
- import { execFile as execFile3 } from "node:child_process";
7704
+ // src/workstation/keepers/terminal/sessions.ts
7705
+ import { randomUUID as randomUUID3 } from "node:crypto";
7706
+ import { execFile as execFile4 } from "node:child_process";
8085
7707
  import { createRequire } from "node:module";
8086
- import path4 from "node:path";
7708
+ import path5 from "node:path";
7709
+
7710
+ // src/workstation/keepers/terminal/gridProtocol.ts
7711
+ var TERMINAL_GRID_LIMITS = {
7712
+ minCols: 20,
7713
+ maxCols: 300,
7714
+ minRows: 5,
7715
+ maxRows: 100,
7716
+ maxSessions: 8,
7717
+ maxAttachments: 16,
7718
+ leaseMs: 15e3,
7719
+ scrollbackLines: 1e4,
7720
+ maxInputBytes: 64 * 1024,
7721
+ maxRequestBytes: 128 * 1024,
7722
+ maxResultBytes: 2 * 1024 * 1024,
7723
+ maxCachedFrames: 8,
7724
+ maxFrameCacheBytes: 8 * 1024 * 1024
7725
+ };
7726
+ var TERMINAL_STYLE_FLAGS = {
7727
+ bold: 1,
7728
+ dim: 2,
7729
+ italic: 4,
7730
+ underline: 8,
7731
+ blink: 16,
7732
+ inverse: 32,
7733
+ invisible: 64,
7734
+ strike: 128
7735
+ };
7736
+ var TERMINAL_RGB_COLOR_BASE = 16777216;
7737
+
7738
+ // src/workstation/keepers/terminal/frames.ts
7739
+ var TerminalFrames = class {
7740
+ cache = [];
7741
+ bytes = 0;
7742
+ /** A failed/oversized capture leaves both the revision and recovery cache untouched. */
7743
+ publish(snapshot, session, geometry) {
7744
+ validate(snapshot, session);
7745
+ const previous = this.cache.at(-1)?.frame;
7746
+ const sameSession = previous?.incarnation === session.incarnation && previous.id === session.id;
7747
+ const nextGeometry = geometry ?? (sameSession ? previous.geometry + Number(previous.cols !== snapshot.cols || previous.rows !== snapshot.rows) : 1);
7748
+ if (!Number.isSafeInteger(nextGeometry) || nextGeometry < 1 || sameSession && nextGeometry < previous.geometry) throw new Error("Invalid terminal geometry");
7749
+ const next = structuredClone({
7750
+ ...snapshot,
7751
+ schema: 1,
7752
+ id: session.id,
7753
+ incarnation: session.incarnation,
7754
+ revision: sameSession ? previous.revision + 1 : 1,
7755
+ baseRevision: 0,
7756
+ geometry: nextGeometry,
7757
+ full: true,
7758
+ session
7759
+ });
7760
+ if (sameSession && equalContent(previous, next)) return;
7761
+ const bytes = resultBytes(next);
7762
+ if (!sameSession) {
7763
+ this.cache = [];
7764
+ this.bytes = 0;
7765
+ }
7766
+ this.cache.push({ frame: next, bytes });
7767
+ this.bytes += bytes;
7768
+ while (this.cache.length > TERMINAL_GRID_LIMITS.maxCachedFrames || this.bytes > TERMINAL_GRID_LIMITS.maxFrameCacheBytes) this.bytes -= this.cache.shift().bytes;
7769
+ }
7770
+ poll(after) {
7771
+ const current = this.cache.at(-1)?.frame;
7772
+ if (!current) throw new Error("Terminal frame has not been captured");
7773
+ if (after === current.revision) return structuredClone({ ...current, full: false, baseRevision: after, styles: [], changedRows: [] });
7774
+ const base = after === 0 ? void 0 : this.cache.find((entry) => entry.frame.revision === after)?.frame;
7775
+ if (!base || base.geometry !== current.geometry || base.buffer !== current.buffer || base.incarnation !== current.incarnation) return structuredClone(current);
7776
+ const changedRows = current.changedRows.filter((row, index) => semanticRow(row, current) !== semanticRow(base.changedRows[index], base));
7777
+ const delta = { ...current, full: false, baseRevision: after, changedRows };
7778
+ resultBytes(delta);
7779
+ return structuredClone(delta);
7780
+ }
7781
+ cacheStats() {
7782
+ return { frames: this.cache.length, bytes: this.bytes };
7783
+ }
7784
+ };
7785
+ function semanticRow(row, frame) {
7786
+ return JSON.stringify([row.wrapped ?? false, row.runs.flatMap((run) => run.cells.map((cell) => [cell.text, cell.width, frame.styles[run.style]]))]);
7787
+ }
7788
+ function equalContent(a, b) {
7789
+ const header = (frame) => JSON.stringify([
7790
+ frame.geometry,
7791
+ frame.cols,
7792
+ frame.rows,
7793
+ frame.buffer,
7794
+ frame.viewport,
7795
+ frame.cursor,
7796
+ frame.modes,
7797
+ frame.session
7798
+ ]);
7799
+ return header(a) === header(b) && a.changedRows.every((row, index) => semanticRow(row, a) === semanticRow(b.changedRows[index], b));
7800
+ }
7801
+ function resultBytes(frame) {
7802
+ const bytes = Buffer.byteLength(JSON.stringify(frame));
7803
+ if (bytes > TERMINAL_GRID_LIMITS.maxResultBytes - 1024) throw new KeeperMethodError("pty_snapshot_too_large", "Terminal frame exceeds result limit; resize to a smaller viewport");
7804
+ return bytes;
7805
+ }
7806
+ function validate(snapshot, session) {
7807
+ if (snapshot.cols < TERMINAL_GRID_LIMITS.minCols || snapshot.cols > TERMINAL_GRID_LIMITS.maxCols || snapshot.rows < TERMINAL_GRID_LIMITS.minRows || snapshot.rows > TERMINAL_GRID_LIMITS.maxRows || snapshot.cols !== session.cols || snapshot.rows !== session.rows || snapshot.changedRows.length !== snapshot.rows) throw new Error("Terminal snapshot must contain a complete matching grid");
7808
+ for (const [index, row] of snapshot.changedRows.entries()) {
7809
+ if (row.index !== index || row.runs.reduce((count, run) => count + run.cells.length, 0) !== snapshot.cols || row.runs.some((run) => !snapshot.styles[run.style] || run.cells.some((cell) => typeof cell.text !== "string" || ![0, 1, 2].includes(cell.width)))) throw new Error("Terminal snapshot must contain complete rows and styles");
7810
+ }
7811
+ }
7812
+
7813
+ // src/workstation/keepers/terminal/screen.ts
7814
+ var import_headless = __toESM(require_xterm_headless(), 1);
8087
7815
  var { Terminal } = import_headless.default;
8088
- var { SerializeAddon } = import_addon_serialize.default;
8089
- var darwinShellName = (pid) => new Promise((resolve) => {
8090
- execFile3("ps", ["-o", "ucomm=", "-p", String(pid)], { timeout: 1e3 }, (error, stdout) => resolve(error ? null : stdout.trim() || null));
8091
- });
8092
- var defaultShellProcessName = (pid) => process.platform === "darwin" ? darwinShellName(pid) : Promise.resolve(null);
8093
- var bad = (message) => {
8094
- throw new KeeperMethodError(PtyErrorCode.badRequest, message);
7816
+ var mouseEncodings = { 1005: "utf8", 1006: "sgr", 1015: "urxvt", 1016: "sgr-pixels" };
7817
+ var TerminalScreen = class {
7818
+ terminal;
7819
+ settled = Promise.resolve();
7820
+ resizeBarrier;
7821
+ pending = /* @__PURE__ */ new Set();
7822
+ closed = false;
7823
+ normalRestoreTop;
7824
+ alternateScroll = false;
7825
+ mouseEncoding = "default";
7826
+ constructor(cols, rows) {
7827
+ this.terminal = new Terminal({ cols, rows, scrollback: TERMINAL_GRID_LIMITS.scrollbackLines, allowProposedApi: true });
7828
+ for (const final of ["h", "l"]) this.terminal.parser.registerCsiHandler({ prefix: "?", final }, (params) => {
7829
+ for (const param of params) {
7830
+ if (param === 1007) this.alternateScroll = final === "h";
7831
+ const encoding = typeof param === "number" ? mouseEncodings[param] : void 0;
7832
+ if (encoding) this.mouseEncoding = final === "h" ? encoding : "default";
7833
+ }
7834
+ return false;
7835
+ });
7836
+ this.terminal.parser.registerEscHandler({ final: "c" }, () => {
7837
+ this.alternateScroll = false;
7838
+ this.mouseEncoding = "default";
7839
+ return false;
7840
+ });
7841
+ this.terminal.buffer.onBufferChange((buffer) => {
7842
+ const top = buffer.type === "normal" ? this.normalRestoreTop ?? buffer.viewportY : buffer.viewportY;
7843
+ if (buffer.type === "normal") this.normalRestoreTop = void 0;
7844
+ this.terminal.scrollToBottom();
7845
+ this.terminal.scrollToLine(top);
7846
+ });
7847
+ }
7848
+ write(data) {
7849
+ if (this.closed) return Promise.reject(new Error("Terminal screen is disposed"));
7850
+ const barrier = this.resizeBarrier;
7851
+ const operation = new Promise((resolve, reject) => {
7852
+ this.pending.add(reject);
7853
+ const write = () => {
7854
+ if (this.closed) return;
7855
+ try {
7856
+ this.terminal.write(data, () => {
7857
+ this.pending.delete(reject);
7858
+ resolve();
7859
+ });
7860
+ } catch (error) {
7861
+ this.pending.delete(reject);
7862
+ reject(error);
7863
+ }
7864
+ };
7865
+ if (barrier) void barrier.then(write, (error) => {
7866
+ this.pending.delete(reject);
7867
+ reject(error);
7868
+ });
7869
+ else write();
7870
+ });
7871
+ this.settled = operation;
7872
+ return operation;
7873
+ }
7874
+ resize(cols, rows) {
7875
+ if (this.closed) return Promise.reject(new Error("Terminal screen is disposed"));
7876
+ const previous = this.settled;
7877
+ const operation = previous.then(() => {
7878
+ if (this.closed) throw new Error("Terminal screen is disposed");
7879
+ const normal = this.terminal.buffer.normal;
7880
+ const anchor = this.anchor(normal);
7881
+ const live = (this.normalRestoreTop ?? normal.viewportY) === normal.baseY;
7882
+ this.terminal.resize(cols, rows);
7883
+ const top = live ? normal.baseY : anchor ? this.anchorLine(anchor, normal) : 0;
7884
+ if (this.terminal.buffer.active.type === "normal") {
7885
+ this.terminal.scrollToBottom();
7886
+ this.terminal.scrollToLine(top);
7887
+ } else {
7888
+ this.normalRestoreTop = Math.min(top, normal.baseY);
7889
+ }
7890
+ anchor?.marker.dispose();
7891
+ });
7892
+ this.resizeBarrier = operation;
7893
+ this.settled = operation;
7894
+ void operation.then(() => {
7895
+ if (this.resizeBarrier === operation) this.resizeBarrier = void 0;
7896
+ }, () => {
7897
+ if (this.resizeBarrier === operation) this.resizeBarrier = void 0;
7898
+ });
7899
+ return operation;
7900
+ }
7901
+ scroll(lines) {
7902
+ this.assertOpen();
7903
+ this.terminal.scrollLines(lines);
7904
+ }
7905
+ /** Only settled state may be published; callers await write/resize before capture. */
7906
+ capture() {
7907
+ this.assertOpen();
7908
+ if (this.pending.size || this.resizeBarrier) throw new Error("Terminal screen has pending writes or resize");
7909
+ const terminal = this.terminal;
7910
+ const buffer = terminal.buffer.active;
7911
+ const core = terminal._core;
7912
+ const styles = [];
7913
+ const styleIndices = /* @__PURE__ */ new Map();
7914
+ const changedRows = Array.from({ length: terminal.rows }, (_, index) => {
7915
+ const line = buffer.getLine(buffer.viewportY + index);
7916
+ const runs = [];
7917
+ for (let x = 0; x < terminal.cols; x++) {
7918
+ const cell = line.getCell(x);
7919
+ const style = cellStyle(cell);
7920
+ const key = `${style.fg},${style.bg},${style.flags}`;
7921
+ let styleIndex = styleIndices.get(key);
7922
+ if (styleIndex === void 0) {
7923
+ styleIndex = styles.length;
7924
+ styles.push(style);
7925
+ styleIndices.set(key, styleIndex);
7926
+ }
7927
+ let run = runs[runs.length - 1];
7928
+ if (!run || run.style !== styleIndex) {
7929
+ run = { style: styleIndex, cells: [] };
7930
+ runs.push(run);
7931
+ }
7932
+ run.cells.push({ text: cell.getChars(), width: cell.getWidth() });
7933
+ }
7934
+ return { index, runs, wrapped: line.isWrapped };
7935
+ });
7936
+ const y = buffer.baseY + buffer.cursorY - buffer.viewportY;
7937
+ return {
7938
+ cols: terminal.cols,
7939
+ rows: terminal.rows,
7940
+ buffer: buffer.type,
7941
+ viewport: { top: buffer.viewportY, totalLines: buffer.length, live: buffer.viewportY === buffer.baseY },
7942
+ cursor: {
7943
+ x: Math.min(buffer.cursorX, terminal.cols - 1),
7944
+ y,
7945
+ visible: !core.coreService.isCursorHidden && y >= 0 && y < terminal.rows,
7946
+ shape: core.coreService.decPrivateModes.cursorStyle ?? terminal.options.cursorStyle ?? "block",
7947
+ blink: core.coreService.decPrivateModes.cursorBlink ?? terminal.options.cursorBlink ?? false
7948
+ },
7949
+ modes: this.state().modes,
7950
+ styles,
7951
+ changedRows
7952
+ };
7953
+ }
7954
+ /** Cheap parsed state for input routing; never extracts viewport cells. */
7955
+ state() {
7956
+ this.assertOpen();
7957
+ if (this.pending.size || this.resizeBarrier) throw new Error("Terminal screen has pending writes or resize");
7958
+ const terminal = this.terminal;
7959
+ const modes = terminal.modes;
7960
+ return {
7961
+ buffer: terminal.buffer.active.type,
7962
+ modes: {
7963
+ applicationCursor: modes.applicationCursorKeysMode,
7964
+ applicationKeypad: modes.applicationKeypadMode,
7965
+ bracketedPaste: modes.bracketedPasteMode,
7966
+ alternateScroll: this.alternateScroll,
7967
+ focusReporting: modes.sendFocusMode,
7968
+ mouseTracking: modes.mouseTrackingMode,
7969
+ mouseEncoding: this.mouseEncoding
7970
+ }
7971
+ };
7972
+ }
7973
+ onTitleChange(listener) {
7974
+ return this.terminal.onTitleChange(listener);
7975
+ }
7976
+ onCwdChange(listener) {
7977
+ return this.terminal.parser.registerOscHandler(7, (uri) => {
7978
+ listener(uri);
7979
+ return true;
7980
+ });
7981
+ }
7982
+ onReply(listener) {
7983
+ return this.terminal.onData(listener);
7984
+ }
7985
+ dispose() {
7986
+ if (this.closed) return;
7987
+ this.closed = true;
7988
+ for (const reject of this.pending) reject(new Error("Terminal screen is disposed"));
7989
+ this.pending.clear();
7990
+ this.terminal.dispose();
7991
+ }
7992
+ assertOpen() {
7993
+ if (this.closed) throw new Error("Terminal screen is disposed");
7994
+ }
7995
+ anchor(buffer) {
7996
+ const top = this.normalRestoreTop ?? buffer.viewportY;
7997
+ if (top === buffer.baseY) return void 0;
7998
+ let end = top + 1;
7999
+ while (buffer.getLine(end)?.isWrapped) end++;
8000
+ let cellsFromEnd = this.trimmedLength(buffer, end - 1);
8001
+ for (let line = top; line < end - 1; line++) cellsFromEnd += this.wrappedLength(buffer, line);
8002
+ const marker = this.terminal.buffer.active.type === "normal" ? this.terminal.registerMarker(end - buffer.baseY - buffer.cursorY) : this.terminal._core.buffers.normal.addMarker(end);
8003
+ return marker ? { marker, cellsFromEnd } : void 0;
8004
+ }
8005
+ anchorLine(anchor, buffer) {
8006
+ if (anchor.marker.isDisposed) return 0;
8007
+ let line = Math.min(anchor.marker.line, buffer.length) - 1;
8008
+ if (line < 0) return 0;
8009
+ let remaining = anchor.cellsFromEnd - this.trimmedLength(buffer, line);
8010
+ while (remaining > 0 && line > 0 && buffer.getLine(line)?.isWrapped) {
8011
+ line--;
8012
+ remaining -= this.wrappedLength(buffer, line);
8013
+ }
8014
+ return line;
8015
+ }
8016
+ trimmedLength(buffer, line) {
8017
+ const row = buffer.getLine(line);
8018
+ for (let x = this.terminal.cols - 1; x >= 0; x--) {
8019
+ const cell = row.getCell(x);
8020
+ if (cell.getChars()) return x + cell.getWidth();
8021
+ }
8022
+ return 0;
8023
+ }
8024
+ wrappedLength(buffer, line) {
8025
+ const last = buffer.getLine(line).getCell(this.terminal.cols - 1);
8026
+ return this.terminal.cols - (last.getChars() === "" && last.getWidth() === 1 && buffer.getLine(line + 1)?.getCell(0)?.getWidth() === 2 ? 1 : 0);
8027
+ }
8095
8028
  };
8096
- function keys2(params, allowed) {
8097
- if (Object.keys(params).some((key) => !allowed.includes(key))) bad("Unexpected tool parameter");
8029
+ function cellStyle(cell) {
8030
+ const flags = TERMINAL_STYLE_FLAGS;
8031
+ return {
8032
+ fg: cell.isFgDefault() ? -1 : cell.getFgColor() + (cell.isFgRGB() ? TERMINAL_RGB_COLOR_BASE : 0),
8033
+ bg: cell.isBgDefault() ? -1 : cell.getBgColor() + (cell.isBgRGB() ? TERMINAL_RGB_COLOR_BASE : 0),
8034
+ flags: (cell.isBold() ? flags.bold : 0) | (cell.isDim() ? flags.dim : 0) | (cell.isItalic() ? flags.italic : 0) | (cell.isUnderline() ? flags.underline : 0) | (cell.isBlink() ? flags.blink : 0) | (cell.isInverse() ? flags.inverse : 0) | (cell.isInvisible() ? flags.invisible : 0) | (cell.isStrikethrough() ? flags.strike : 0)
8035
+ };
8098
8036
  }
8099
- function identifier(params, name) {
8100
- const value = params[name];
8101
- if (typeof value !== "string" || !value.length || value.includes("\0") || value.length > 128 || !/^[A-Za-z0-9_.:-]+$/.test(value)) bad(`Invalid ${name}`);
8102
- return value;
8037
+
8038
+ // src/workstation/keepers/terminal/input.ts
8039
+ var esc = "\x1B";
8040
+ var modifiers = (mask) => 1 + (mask & 1) + (mask & 2) + (mask & 4) + (mask & 8);
8041
+ var arrows = { 79: "C", 80: "D", 81: "B", 82: "A", 74: "H", 77: "F" };
8042
+ var tilde = { 73: 2, 76: 3, 75: 5, 78: 6, 62: 15, 63: 17, 64: 18, 65: 19, 66: 20, 67: 21, 68: 23, 69: 24 };
8043
+ var keypad = {
8044
+ 84: ["/", "o"],
8045
+ 85: ["*", "j"],
8046
+ 86: ["-", "m"],
8047
+ 87: ["+", "k"],
8048
+ 88: ["\r", "M"],
8049
+ 89: ["1", "q"],
8050
+ 90: ["2", "r"],
8051
+ 91: ["3", "s"],
8052
+ 92: ["4", "t"],
8053
+ 93: ["5", "u"],
8054
+ 94: ["6", "v"],
8055
+ 95: ["7", "w"],
8056
+ 96: ["8", "x"],
8057
+ 97: ["9", "y"],
8058
+ 98: ["0", "p"],
8059
+ 99: [".", "n"],
8060
+ 103: ["=", "X"]
8061
+ };
8062
+ var TerminalInputEncoder = class {
8063
+ buttons = /* @__PURE__ */ new Set();
8064
+ release() {
8065
+ this.buttons.clear();
8066
+ }
8067
+ encode(event, screen) {
8068
+ switch (event.kind) {
8069
+ case "release":
8070
+ this.release();
8071
+ return "";
8072
+ case "text":
8073
+ return event.text;
8074
+ case "paste":
8075
+ return screen.modes.bracketedPaste ? `${esc}[200~${event.text}${esc}[201~` : event.text;
8076
+ case "key":
8077
+ return event.down ? encodeKey(event.usage, event.modifiers, screen.modes) : "";
8078
+ case "button": {
8079
+ if (event.down) this.buttons.add(event.button);
8080
+ else this.buttons.delete(event.button);
8081
+ if (screen.modes.mouseTracking === "none" || !event.down && screen.modes.mouseTracking === "x10") return "";
8082
+ return mouse(event.button, event.x, event.y, event.modifiers, screen.modes, !event.down);
8083
+ }
8084
+ case "pointer": {
8085
+ const tracking = screen.modes.mouseTracking;
8086
+ if (tracking !== "any" && !(tracking === "drag" && this.buttons.size)) return "";
8087
+ return mouse(32 + (this.buttons.values().next().value ?? 3), event.x, event.y, event.modifiers, screen.modes);
8088
+ }
8089
+ }
8090
+ }
8091
+ };
8092
+ function createTerminalInput(screen) {
8093
+ const clients = /* @__PURE__ */ new Map();
8094
+ const encoder = (id) => {
8095
+ let value = clients.get(id);
8096
+ if (!value) {
8097
+ value = new TerminalInputEncoder();
8098
+ clients.set(id, value);
8099
+ }
8100
+ return value;
8101
+ };
8102
+ return {
8103
+ input(event, clientId, context) {
8104
+ return encoder(clientId).encode(event, context);
8105
+ },
8106
+ scroll(gesture, _clientId, context) {
8107
+ if (!gesture.lines) return "";
8108
+ const state = screen.state();
8109
+ if (context.modes.mouseTracking === "x10") return "";
8110
+ if (context.modes.mouseTracking !== "none") return repeat(mouse(gesture.lines < 0 ? 64 : 65, gesture.x, gesture.y, gesture.modifiers, context.modes), Math.abs(gesture.lines));
8111
+ if (state.buffer === "normal") return void 0;
8112
+ if (!context.modes.alternateScroll) return "";
8113
+ return repeat(encodeKey(gesture.lines < 0 ? 82 : 81, gesture.modifiers, context.modes), Math.abs(gesture.lines));
8114
+ },
8115
+ release(clientId) {
8116
+ clients.get(clientId)?.release();
8117
+ clients.delete(clientId);
8118
+ },
8119
+ dispose() {
8120
+ clients.clear();
8121
+ }
8122
+ };
8103
8123
  }
8104
- function integer2(params, name, min, max, fallback) {
8105
- const value = params[name] === void 0 ? fallback : params[name];
8106
- if (!Number.isSafeInteger(value) || value < min || value > max) bad(`Invalid ${name}`);
8107
- return value;
8124
+ function repeat(bytes, count) {
8125
+ if (!bytes) return "";
8126
+ if (count > Math.floor(TERMINAL_GRID_LIMITS.maxInputBytes / Buffer.byteLength(bytes))) throw new KeeperMethodError("bad_request", "Terminal input exceeds limit");
8127
+ if (typeof bytes === "string") return bytes.repeat(count);
8128
+ return Buffer.concat(Array.from({ length: count }, () => bytes));
8108
8129
  }
8109
- var processBasename = (name) => path4.basename(name.trim()).replace(/^-/, "");
8110
- var CREATE_KEYS = ["id", "clientId", "name", "cwd", "cols", "rows"];
8111
- var PtySessions = class {
8112
- constructor(options = {}) {
8130
+ function mouse(button, x, y, mask, modes, release = false) {
8131
+ if (modes.mouseEncoding === "sgr-pixels") return "";
8132
+ const mods = modes.mouseTracking === "x10" ? 0 : (mask & 1 ? 4 : 0) + (mask & 2 ? 8 : 0) + (mask & 4 ? 16 : 0);
8133
+ if (modes.mouseEncoding === "sgr") return `${esc}[<${button + mods};${x + 1};${y + 1}${release ? "m" : "M"}`;
8134
+ const code = (release ? 3 : button) + mods + 32;
8135
+ if (modes.mouseEncoding === "urxvt") return `${esc}[${code};${x + 1};${y + 1}M`;
8136
+ if (modes.mouseEncoding === "default" && (x > 222 || y > 222)) return "";
8137
+ if (modes.mouseEncoding === "default") return Buffer.from([27, 91, 77, code, x + 33, y + 33]);
8138
+ return `${esc}[M${String.fromCharCode(code, x + 33, y + 33)}`;
8139
+ }
8140
+ function encodeKey(usage, mask, modes) {
8141
+ const mod = modifiers(mask);
8142
+ if (arrows[usage]) return mod > 1 ? `${esc}[1;${mod}${arrows[usage]}` : `${esc}${modes.applicationCursor ? "O" : "["}${arrows[usage]}`;
8143
+ if (tilde[usage]) return `${esc}[${tilde[usage]}${mod > 1 ? `;${mod}` : ""}~`;
8144
+ if (usage >= 58 && usage <= 61) return mod > 1 ? `${esc}[1;${mod}${String.fromCharCode(80 + usage - 58)}` : `${esc}O${String.fromCharCode(80 + usage - 58)}`;
8145
+ if (keypad[usage]) {
8146
+ if (mask & 8 && usage !== 88) return "";
8147
+ return modes.applicationKeypad ? mod > 1 ? `${esc}[1;${mod}${keypad[usage][1]}` : `${esc}O${keypad[usage][1]}` : prefixed(keypad[usage][0], mask);
8148
+ }
8149
+ if (usage === 40) return prefixed("\r", mask);
8150
+ if (usage === 41) return esc;
8151
+ if (usage === 42) return prefixed(mask & 4 ? "\b" : "\x7F", mask);
8152
+ if (usage === 43) return mask & 1 ? `${esc}[Z` : prefixed(" ", mask);
8153
+ if (mask & 8) return "";
8154
+ let text = "";
8155
+ if (usage >= 4 && usage <= 29) text = String.fromCharCode((mask & 1 ? 65 : 97) + usage - 4);
8156
+ else if (usage >= 30 && usage <= 39) text = (mask & 1 ? "!@#$%^&*()" : "1234567890")[usage - 30];
8157
+ else {
8158
+ const punctuation = { 44: [" ", " "], 45: ["-", "_"], 46: ["=", "+"], 47: ["[", "{"], 48: ["]", "}"], 49: ["\\", "|"], 50: ["#", "~"], 51: [";", ":"], 52: ["'", '"'], 53: ["`", "~"], 54: [",", "<"], 55: [".", ">"], 56: ["/", "?"] };
8159
+ text = punctuation[usage]?.[mask & 1 ? 1 : 0] ?? "";
8160
+ }
8161
+ if (mask & 4) {
8162
+ const upper = text.toUpperCase();
8163
+ if (upper >= "@" && upper <= "_") text = String.fromCharCode(upper.charCodeAt(0) - 64);
8164
+ else if (text === " " || text === "2" || text === "`") text = "\0";
8165
+ else if (text === "3") text = esc;
8166
+ else if (text === "4") text = "";
8167
+ else if (text === "5") text = "";
8168
+ else if (text === "6") text = "";
8169
+ else if (text === "7" || text === "/") text = "";
8170
+ else if (text === "8" || text === "?") text = "\x7F";
8171
+ }
8172
+ return prefixed(text, mask);
8173
+ }
8174
+ function prefixed(text, mask) {
8175
+ return text && mask & 2 ? esc + text : text;
8176
+ }
8177
+
8178
+ // src/workstation/keepers/terminal/metadata.ts
8179
+ import { execFile as execFile3 } from "node:child_process";
8180
+ import { readlink } from "node:fs/promises";
8181
+ import { hostname as localHostname } from "node:os";
8182
+ import path4 from "node:path";
8183
+ var clean = (value) => [...value.replace(/[\x00-\x1f\x7f-\x9f]/g, "").trim()].slice(0, 256).join("");
8184
+ var validCwd = (value) => value.startsWith("/") && !/[\x00-\x1f\x7f-\x9f]/.test(value);
8185
+ var TerminalMetadata = class {
8186
+ constructor(options) {
8113
8187
  this.options = options;
8188
+ this.cwd = options.cwd;
8114
8189
  this.now = options.now ?? Date.now;
8115
- try {
8116
- this.ptyModule = (options.loadPty ?? (() => createRequire(import.meta.url)("node-pty")))();
8117
- } catch {
8118
- this.error = PTY_UNAVAILABLE_ERROR;
8119
- }
8190
+ this.hostname = (options.hostname ?? localHostname()).toLowerCase();
8191
+ this.listeners = [options.screen.onTitleChange((title) => {
8192
+ if (this.closed) return;
8193
+ const previous = this.title;
8194
+ this.explicitTitle = clean(title);
8195
+ if (this.title !== previous) options.changed();
8196
+ }), options.screen.onCwdChange((uri) => this.osc7(uri))];
8120
8197
  }
8121
8198
  options;
8122
- sessions = /* @__PURE__ */ new Map();
8123
- ptyModule;
8124
- error;
8199
+ cwd;
8200
+ cwdSource = "launch";
8201
+ explicitTitle = "";
8202
+ listeners;
8125
8203
  now;
8126
- get available() {
8127
- return !!this.ptyModule;
8204
+ hostname;
8205
+ lastLookup = -Infinity;
8206
+ oscRevision = 0;
8207
+ closed = false;
8208
+ pending;
8209
+ cancelLookup;
8210
+ timer;
8211
+ get title() {
8212
+ return this.explicitTitle || clean(path4.posix.basename(this.cwd) || this.cwd) || "Terminal";
8128
8213
  }
8129
- descriptor(s) {
8130
- return { id: s.id, name: s.name, cwd: s.cwd, cols: s.cols, rows: s.rows, exited: s.exited, exitCode: s.exitCode, controllerId: s.controllerId };
8131
- }
8132
- control(s, clientId) {
8133
- if (s.controllerId !== clientId) throw new KeeperMethodError(PtyErrorCode.notController, "Terminal is controlled by another client; take control explicitly");
8134
- }
8135
- resize(s, p) {
8136
- const cols = integer2(p, "cols", 20, 300, s.cols);
8137
- const rows = integer2(p, "rows", 5, 100, s.rows);
8138
- if (cols !== s.cols || rows !== s.rows) {
8139
- if (!s.exited) s.pty.resize(cols, rows);
8140
- s.terminal.resize(cols, rows);
8141
- s.cols = cols;
8142
- s.rows = rows;
8214
+ read() {
8215
+ return { title: this.title, cwd: this.cwd, cwdSource: this.cwdSource };
8216
+ }
8217
+ /** Called once by the host factory, independently of output and attachments. */
8218
+ start() {
8219
+ if (this.closed || this.timer) return;
8220
+ void this.refresh();
8221
+ this.timer = setInterval(() => {
8222
+ void this.refresh();
8223
+ }, 2e3);
8224
+ this.timer.unref();
8225
+ }
8226
+ refresh() {
8227
+ if (this.closed || this.options.live?.() === false) return Promise.resolve();
8228
+ if (this.pending) return this.pending;
8229
+ if (this.now() - this.lastLookup < 2e3) return Promise.resolve();
8230
+ this.lastLookup = this.now();
8231
+ const revision = this.oscRevision;
8232
+ let timeout;
8233
+ const bounded = new Promise((resolve) => {
8234
+ timeout = setTimeout(() => resolve(null), 1e3);
8235
+ this.cancelLookup = () => resolve(null);
8236
+ try {
8237
+ void (this.options.lookup ?? lookupProcessCwd)(this.options.pid).then(resolve, () => resolve(null));
8238
+ } catch {
8239
+ resolve(null);
8240
+ }
8241
+ });
8242
+ this.pending = bounded.then((cwd) => {
8243
+ if (!this.closed && this.options.live?.() !== false && revision === this.oscRevision && cwd && validCwd(cwd)) this.updateCwd(cwd, "process");
8244
+ }).finally(() => {
8245
+ clearTimeout(timeout);
8246
+ this.cancelLookup = void 0;
8247
+ this.pending = void 0;
8248
+ });
8249
+ return this.pending;
8250
+ }
8251
+ dispose() {
8252
+ if (this.closed) return;
8253
+ this.closed = true;
8254
+ clearInterval(this.timer);
8255
+ this.timer = void 0;
8256
+ this.cancelLookup?.();
8257
+ for (const listener of this.listeners) listener.dispose();
8258
+ }
8259
+ osc7(uri) {
8260
+ if (this.closed) return;
8261
+ try {
8262
+ if (/[\x00-\x1f\x7f-\x9f]/.test(uri)) return;
8263
+ const url = new URL(uri);
8264
+ if (url.protocol !== "file:" || url.username || url.password || url.port || uri.includes("?") || uri.includes("#")) return;
8265
+ const host = url.hostname.toLowerCase();
8266
+ if (host && host !== "localhost" && host !== this.hostname) return;
8267
+ const cwd = decodeURIComponent(url.pathname);
8268
+ if (!validCwd(cwd)) return;
8269
+ this.oscRevision++;
8270
+ this.updateCwd(cwd, "osc7");
8271
+ } catch {
8143
8272
  }
8144
8273
  }
8145
- snapshot(s) {
8146
- for (let scrollback = PTY_SCROLLBACK; ; scrollback = Math.floor(scrollback / 2)) {
8147
- const snapshot = "\x1Bc" + s.serializer.serialize({ scrollback });
8148
- if (Buffer.byteLength(JSON.stringify({ snapshot })) < PTY_RESULT_BYTES - 8192) return snapshot;
8149
- if (!scrollback) throw new KeeperMethodError(PtyErrorCode.snapshotTooLarge, "Terminal screen exceeds snapshot limit; resize to a smaller viewport");
8274
+ updateCwd(cwd, source) {
8275
+ if (cwd === this.cwd && source === this.cwdSource) return;
8276
+ this.cwd = cwd;
8277
+ this.cwdSource = source;
8278
+ this.options.changed();
8279
+ }
8280
+ };
8281
+ async function lookupProcessCwd(pid, platform = process.platform) {
8282
+ if (!Number.isSafeInteger(pid) || pid <= 0) return null;
8283
+ if (platform === "linux") {
8284
+ try {
8285
+ return await readlink(`/proc/${pid}/cwd`);
8286
+ } catch {
8287
+ return null;
8150
8288
  }
8151
8289
  }
8152
- output(s, data) {
8153
- if (!data || s.closed) return;
8154
- const bytes = Buffer.byteLength(data);
8155
- s.pendingBytes += bytes;
8156
- if (s.pendingBytes > 256 * 1024) s.pty.pause();
8157
- s.rendered = new Promise((resolve) => {
8158
- s.terminal.write(data, () => {
8159
- s.seq += 1;
8160
- s.ring.push({ seq: s.seq, data, bytes });
8161
- s.ringBytes += bytes;
8162
- const limit = this.options.historyBytes ?? PTY_HISTORY_BYTES;
8163
- while (s.ringBytes > limit && s.ring.length) s.ringBytes -= s.ring.shift().bytes;
8164
- s.pendingBytes -= bytes;
8165
- if (s.pendingBytes < 64 * 1024 && !s.exited) s.pty.resume();
8166
- resolve();
8167
- });
8290
+ if (platform !== "darwin") return null;
8291
+ return new Promise((resolve) => {
8292
+ execFile3("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], { timeout: 1e3, maxBuffer: 64 * 1024 }, (error, stdout) => {
8293
+ resolve(error ? null : stdout.split("\n").find((line) => line.startsWith("n/"))?.slice(1) ?? null);
8168
8294
  });
8295
+ });
8296
+ }
8297
+ function createTerminalMetadata(context) {
8298
+ const initial = context.session();
8299
+ const metadata = new TerminalMetadata({
8300
+ screen: context.screen,
8301
+ cwd: initial.cwd,
8302
+ pid: context.pty.pid,
8303
+ changed: context.changed,
8304
+ live: () => !context.session().exited
8305
+ });
8306
+ metadata.start();
8307
+ return metadata;
8308
+ }
8309
+
8310
+ // src/workstation/keepers/terminal/sessions.ts
8311
+ var NATIVE_PTY_OPERATIONS = /* @__PURE__ */ new Set(["attach", "poll", "resize", "input", "scroll", "detach", "close"]);
8312
+ var bad = (message) => {
8313
+ throw new KeeperMethodError("bad_request", message);
8314
+ };
8315
+ var fail = (code, message) => {
8316
+ throw new KeeperMethodError(code, message);
8317
+ };
8318
+ var ok = { ok: true };
8319
+ var nativeScheduler = {
8320
+ schedule(callback, delay) {
8321
+ const timer = setTimeout(callback, delay);
8322
+ timer.unref();
8323
+ return { dispose: () => clearTimeout(timer) };
8169
8324
  }
8170
- unavailable() {
8171
- throw new KeeperMethodError(PtyErrorCode.unavailable, this.error ?? "Terminal unavailable");
8172
- }
8173
- list(p) {
8174
- keys2(p, []);
8175
- return { sessions: [...this.sessions.values()].map((s) => this.descriptor(s)) };
8325
+ };
8326
+ var basename = (name) => path5.basename(name.trim()).replace(/^-/, "");
8327
+ var NativePtySessions = class {
8328
+ constructor(options = {}) {
8329
+ this.options = options;
8330
+ this.now = options.now ?? Date.now;
8331
+ this.scheduler = options.scheduler ?? nativeScheduler;
8332
+ try {
8333
+ this.module = (options.loadPty ?? (() => createRequire(import.meta.url)("node-pty")))();
8334
+ } catch {
8335
+ this.unavailable = "Terminal native dependency unavailable; reinstall CLI with optional node-pty support";
8336
+ }
8176
8337
  }
8177
- /**
8178
- * Creates session `id`, or returns the existing one (idempotent). `allowedKeys` is the caller's parameter set; `launch`
8179
- * supplies cwd, shell and env and runs only when a session is really started (after the duplicate and limit checks,
8180
- * which are re-checked afterwards because `launch` may be asynchronous).
8181
- */
8182
- async create(p, allowedKeys, launch) {
8183
- if (!this.ptyModule) this.unavailable();
8184
- const id = identifier(p, "id");
8185
- const clientId = identifier(p, "clientId");
8186
- keys2(p, allowedKeys);
8187
- const cols = integer2(p, "cols", 20, 300, 100);
8188
- const rows = integer2(p, "rows", 5, 100, 30);
8189
- if (p.name !== void 0 && (typeof p.name !== "string" || !p.name.length || p.name.length > 128 || /[\x00-\x1f]/.test(p.name))) bad("Invalid terminal name");
8190
- const existing = () => {
8191
- const old2 = this.sessions.get(id);
8192
- return old2 && this.descriptor(old2);
8193
- };
8194
- const limit = () => {
8195
- if (this.sessions.size >= PTY_SESSION_LIMIT) throw new KeeperMethodError(PtyErrorCode.limit, "Terminal session limit exceeded");
8196
- };
8197
- let old = existing();
8198
- if (old) return old;
8199
- limit();
8200
- const { cwd, shell, env } = await launch();
8201
- old = existing();
8202
- if (old) return old;
8203
- limit();
8204
- const terminal = new Terminal({ cols, rows, scrollback: PTY_SCROLLBACK, allowProposedApi: true });
8205
- const serializer = new SerializeAddon();
8206
- terminal.loadAddon(serializer);
8338
+ options;
8339
+ sessions = /* @__PURE__ */ new Map();
8340
+ now;
8341
+ scheduler;
8342
+ module;
8343
+ unavailable;
8344
+ disposed = false;
8345
+ list() {
8346
+ return { schema: 1, limits: { ...TERMINAL_GRID_LIMITS }, sessions: [...this.sessions.values()].map((s) => this.descriptor(s)) };
8347
+ }
8348
+ async create(params, launch) {
8349
+ this.available();
8350
+ request(params);
8351
+ keys2(params, ["id", "cwd", "registrationId"]);
8352
+ const id = identifier(params, "id");
8353
+ if (params.registrationId !== void 0) identifier(params, "registrationId");
8354
+ if (params.cwd !== void 0 && (typeof params.cwd !== "string" || params.cwd.includes("\0"))) bad("Invalid cwd");
8355
+ const existing = this.sessions.get(id);
8356
+ if (existing) return this.descriptor(existing);
8357
+ this.checkLimit();
8358
+ const resolved = await launch();
8359
+ this.available();
8360
+ const concurrent = this.sessions.get(id);
8361
+ if (concurrent) return this.descriptor(concurrent);
8362
+ this.checkLimit();
8363
+ const cols = 100;
8364
+ const rows = 30;
8365
+ const screen = new TerminalScreen(cols, rows);
8207
8366
  let pty;
8208
8367
  try {
8209
- pty = this.ptyModule.spawn(shell, ["-l"], { name: "xterm-256color", cwd, cols, rows, env: { ...env, TERM: "xterm-256color", COLORTERM: "truecolor" } });
8368
+ pty = this.module.spawn(resolved.shell, ["-l"], { name: "xterm-256color", cwd: resolved.cwd, cols, rows, env: { ...resolved.env, TERM: "xterm-256color", COLORTERM: "truecolor" } });
8210
8369
  } catch (error) {
8211
- terminal.dispose();
8370
+ screen.dispose();
8212
8371
  throw error;
8213
8372
  }
8214
- const created = this.now();
8215
- const shellName = processBasename(shell);
8216
- const session = {
8217
- id,
8218
- name: p.name ?? "Terminal",
8219
- cwd,
8220
- cols,
8221
- rows,
8222
- exited: false,
8223
- exitCode: null,
8224
- controllerId: clientId,
8373
+ const createdAt = this.now();
8374
+ const shell = basename(resolved.shell);
8375
+ const s = {
8376
+ value: { id, ...typeof params.registrationId === "string" ? { registrationId: params.registrationId } : {}, incarnation: randomUUID3(), title: "Terminal", cwd: resolved.cwd, cwdSource: "launch", cols, rows, createdAt, exited: false, exitCode: null },
8377
+ shell,
8378
+ shellNames: /* @__PURE__ */ new Set([shell]),
8379
+ shellNameStarted: false,
8225
8380
  pty,
8381
+ screen,
8382
+ frames: new TerminalFrames(),
8383
+ attachments: /* @__PURE__ */ new Map(),
8384
+ geometry: 1,
8226
8385
  closed: false,
8227
- terminal,
8228
- serializer,
8229
- seq: 0,
8230
- ring: [],
8231
- ringBytes: 0,
8232
- pendingBytes: 0,
8386
+ queue: Promise.resolve(),
8233
8387
  rendered: Promise.resolve(),
8234
- shellName,
8388
+ pendingBytes: 0,
8389
+ dirty: true,
8390
+ capturedAt: -Infinity,
8391
+ listeners: [],
8392
+ input: createTerminalInput(screen),
8235
8393
  exitedAt: 0,
8236
- shellNames: /* @__PURE__ */ new Set([shellName]),
8237
- observed: { label: shellName, state: "idle", since: created }
8394
+ observed: { label: shell, state: "idle", since: createdAt }
8238
8395
  };
8239
- pty.onData((data) => this.output(session, data));
8240
- pty.onExit((event) => {
8241
- session.exited = true;
8242
- session.exitCode = event.exitCode;
8243
- session.exitedAt = this.now();
8244
- });
8245
- terminal.onData((data) => {
8246
- if (!session.exited) pty.write(data);
8247
- });
8248
- this.sessions.set(id, session);
8249
- const learn = setTimeout(() => {
8250
- void this.learnShellName(session);
8251
- }, this.options.shellNameDelayMs ?? 250);
8252
- learn.unref();
8253
- return this.descriptor(session);
8254
- }
8255
- /** attach / poll / write / resize / takeover / detach / close on an existing session. */
8256
- async operate(op, p) {
8257
- if (!this.ptyModule) this.unavailable();
8258
- const id = identifier(p, "id");
8259
- const clientId = identifier(p, "clientId");
8396
+ const context = { pty, screen, session: () => this.descriptor(s), changed: () => this.dirty(s) };
8397
+ try {
8398
+ s.input = this.options.createInput?.(context) ?? s.input;
8399
+ s.metadata = (this.options.createMetadata ?? createTerminalMetadata)(context);
8400
+ this.capture(s, true);
8401
+ s.listeners.push(disposable(pty.onData((data) => this.output(s, data))), disposable(pty.onExit((event) => {
8402
+ if (s.closed) return;
8403
+ s.shellNameTimer?.dispose();
8404
+ s.shellNameTimer = void 0;
8405
+ s.value.exited = true;
8406
+ s.value.exitCode = event.exitCode;
8407
+ s.exitedAt = this.now();
8408
+ this.dirty(s);
8409
+ })), screen.onReply((data) => {
8410
+ if (!s.closed && !s.value.exited) {
8411
+ try {
8412
+ pty.write(data);
8413
+ } catch (error) {
8414
+ this.failed(s, error);
8415
+ }
8416
+ }
8417
+ }));
8418
+ this.sessions.set(id, s);
8419
+ } catch (error) {
8420
+ await this.close(s);
8421
+ throw error;
8422
+ }
8423
+ return this.descriptor(s);
8424
+ }
8425
+ async operate(op, params) {
8426
+ request(params);
8427
+ const id = identifier(params, "id");
8428
+ const incarnation = identifier(params, "incarnation");
8260
8429
  const s = this.sessions.get(id);
8261
- if (!s) throw new KeeperMethodError(PtyErrorCode.notFound, "Terminal session not found; keeper restart loses live PTYs");
8262
- switch (op) {
8263
- case "attach": {
8264
- keys2(p, ["id", "clientId", "cols", "rows"]);
8265
- integer2(p, "cols", 20, 300, s.cols);
8266
- integer2(p, "rows", 5, 100, s.rows);
8267
- await s.rendered;
8268
- if (s.controllerId === null) s.controllerId = clientId;
8269
- if (s.controllerId === clientId) this.resize(s, p);
8270
- return { session: this.descriptor(s), seq: s.seq, snapshot: this.snapshot(s), control: s.controllerId === clientId };
8430
+ if (!s) {
8431
+ if (op === "close" || op === "detach") return ok;
8432
+ return fail("pty_not_found", "Terminal session not found; keeper restart loses live PTYs");
8433
+ }
8434
+ if (s.value.incarnation !== incarnation) return fail("pty_incarnation_mismatch", "Terminal incarnation no longer matches");
8435
+ return this.ordered(s, async () => {
8436
+ if (s.closed) {
8437
+ if (op === "close" || op === "detach") return ok;
8438
+ return fail("pty_not_found", "Terminal session is closed");
8439
+ }
8440
+ if (op === "close") {
8441
+ keys2(params, ["id", "incarnation"]);
8442
+ await this.close(s);
8443
+ return ok;
8271
8444
  }
8272
- case "poll": {
8273
- keys2(p, ["id", "clientId", "after"]);
8274
- const after = integer2(p, "after", 0, Number.MAX_SAFE_INTEGER);
8275
- await s.rendered;
8276
- const common = { exited: s.exited, exitCode: s.exitCode, control: s.controllerId === clientId };
8277
- if (after > s.seq || after < (s.ring[0]?.seq ?? s.seq + 1) - 1) return { ...common, seq: s.seq, data: "", reset: true, snapshot: this.snapshot(s) };
8278
- let data = "";
8279
- let bytes = 0;
8280
- let seq = after;
8281
- for (const output of s.ring) {
8282
- if (output.seq <= after) continue;
8283
- if (bytes + output.bytes > PTY_MAX_POLL_BYTES) {
8284
- if (!bytes) return { ...common, seq: s.seq, data: "", reset: true, snapshot: this.snapshot(s) };
8285
- break;
8286
- }
8287
- data += output.data;
8288
- bytes += output.bytes;
8289
- seq = output.seq;
8445
+ await this.expire(s);
8446
+ const clientId = identifier(params, "clientId");
8447
+ const ref = ["id", "incarnation", "clientId"];
8448
+ if (op === "detach") {
8449
+ keys2(params, ref);
8450
+ this.release(s, clientId);
8451
+ await this.capacity(s);
8452
+ return ok;
8453
+ }
8454
+ if (op === "attach") {
8455
+ keys2(params, [...ref, "cols", "rows"]);
8456
+ const cols = integer2(params, "cols", TERMINAL_GRID_LIMITS.minCols, TERMINAL_GRID_LIMITS.maxCols);
8457
+ const rows = integer2(params, "rows", TERMINAL_GRID_LIMITS.minRows, TERMINAL_GRID_LIMITS.maxRows);
8458
+ if (!s.attachments.has(clientId) && s.attachments.size >= TERMINAL_GRID_LIMITS.maxAttachments) return fail("pty_limit", "Terminal attachment limit exceeded");
8459
+ const previous = s.attachments.get(clientId);
8460
+ s.attachments.set(clientId, { cols, rows, expires: this.now() + TERMINAL_GRID_LIMITS.leaseMs });
8461
+ try {
8462
+ await this.capacity(s);
8463
+ await this.settle(s);
8464
+ this.capture(s, true);
8465
+ } catch (error) {
8466
+ if (previous) s.attachments.set(clientId, previous);
8467
+ else this.release(s, clientId);
8468
+ try {
8469
+ await this.capacity(s);
8470
+ } catch (rollback) {
8471
+ this.failed(s, rollback);
8472
+ }
8473
+ throw error;
8290
8474
  }
8291
- return { ...common, seq, data, reset: false };
8475
+ this.scheduleLease(s);
8476
+ return s.frames.poll(0);
8292
8477
  }
8293
- case "takeover":
8294
- keys2(p, ["id", "clientId"]);
8295
- s.controllerId = clientId;
8296
- return { ok: true };
8297
- case "detach":
8298
- keys2(p, ["id", "clientId"]);
8299
- if (s.controllerId === clientId) s.controllerId = null;
8300
- return { ok: true };
8301
- case "write":
8302
- keys2(p, ["id", "clientId", "data"]);
8303
- this.control(s, clientId);
8304
- if (s.exited) throw new KeeperMethodError(PtyErrorCode.exited, "Terminal has exited");
8305
- if (typeof p.data !== "string" || Buffer.byteLength(p.data) > PTY_INPUT_BYTES) bad("Invalid terminal input");
8306
- s.pty.write(p.data);
8307
- return { ok: true };
8308
- case "resize":
8309
- keys2(p, ["id", "clientId", "cols", "rows"]);
8310
- this.control(s, clientId);
8311
- integer2(p, "cols", 20, 300);
8312
- integer2(p, "rows", 5, 100);
8313
- await s.rendered;
8314
- this.resize(s, p);
8315
- return { ok: true };
8316
- case "close":
8317
- keys2(p, ["id", "clientId"]);
8318
- this.control(s, clientId);
8319
- this.sessions.delete(id);
8320
- s.closed = true;
8321
- if (!s.exited) s.pty.kill();
8322
- await s.rendered;
8323
- s.terminal.dispose();
8324
- return { ok: true };
8325
- default:
8326
- return bad("Unsupported terminal tool");
8478
+ const attachment = s.attachments.get(clientId);
8479
+ if (!attachment) return fail("pty_attachment_expired", "Terminal attachment expired; attach again");
8480
+ if (op === "poll") {
8481
+ keys2(params, [...ref, "after"]);
8482
+ const after = integer2(params, "after", 0, Number.MAX_SAFE_INTEGER);
8483
+ attachment.expires = this.now() + TERMINAL_GRID_LIMITS.leaseMs;
8484
+ this.scheduleLease(s);
8485
+ await this.captureDue(s);
8486
+ return s.frames.poll(after);
8487
+ }
8488
+ if (op === "resize") {
8489
+ keys2(params, [...ref, "cols", "rows"]);
8490
+ const cols = integer2(params, "cols", TERMINAL_GRID_LIMITS.minCols, TERMINAL_GRID_LIMITS.maxCols);
8491
+ const rows = integer2(params, "rows", TERMINAL_GRID_LIMITS.minRows, TERMINAL_GRID_LIMITS.maxRows);
8492
+ const previous = { ...attachment };
8493
+ attachment.cols = cols;
8494
+ attachment.rows = rows;
8495
+ try {
8496
+ await this.capacity(s);
8497
+ } catch (error) {
8498
+ Object.assign(attachment, previous);
8499
+ throw error;
8500
+ }
8501
+ } else if (op === "input") {
8502
+ const event = input(params, ref);
8503
+ await this.settle(s);
8504
+ this.assertRunning(s);
8505
+ if (event.kind === "pointer" || event.kind === "button") this.coordinates(s, event);
8506
+ const data = s.input.input(event, clientId, this.inputContext(s));
8507
+ if (data) s.pty.write(data);
8508
+ } else if (op === "scroll") {
8509
+ keys2(params, [...ref, "lines", "x", "y", "modifiers", "geometry"]);
8510
+ const gesture = { lines: integer2(params, "lines", -Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER), ...position(params), modifiers: integer2(params, "modifiers", 0, 15) };
8511
+ await this.settle(s);
8512
+ this.coordinates(s, gesture);
8513
+ const data = s.input.scroll(gesture, clientId, this.inputContext(s));
8514
+ if (data) {
8515
+ this.assertRunning(s);
8516
+ s.pty.write(data);
8517
+ } else if (data === void 0 && s.screen.state().buffer === "normal") {
8518
+ s.screen.scroll(gesture.lines);
8519
+ this.dirty(s);
8520
+ }
8521
+ } else return bad("Unsupported terminal operation");
8522
+ attachment.expires = this.now() + TERMINAL_GRID_LIMITS.leaseMs;
8523
+ this.scheduleLease(s);
8524
+ return ok;
8525
+ });
8526
+ }
8527
+ activity() {
8528
+ const metrics = { ptyOpen: 0, ptyActive: 0, ptyExited: 0 };
8529
+ const items = [...this.sessions.values()].map((s) => {
8530
+ if (s.value.exited) {
8531
+ metrics.ptyExited++;
8532
+ return { type: "pty", id: s.value.id, label: s.shell.slice(0, 64), state: "exited", sinceEpochMs: s.exitedAt };
8533
+ }
8534
+ metrics.ptyOpen++;
8535
+ let foreground = "";
8536
+ try {
8537
+ foreground = basename(s.pty.process);
8538
+ } catch {
8539
+ }
8540
+ const active = foreground !== "" && !s.shellNames.has(foreground);
8541
+ const label = (active ? foreground : s.shell).slice(0, 64);
8542
+ const state = active ? "active" : "idle";
8543
+ if (active) metrics.ptyActive++;
8544
+ if (s.observed.label !== label || s.observed.state !== state) s.observed = { label, state, since: this.now() };
8545
+ return { type: "pty", id: s.value.id, label, state, sinceEpochMs: s.observed.since };
8546
+ });
8547
+ return { metrics, items };
8548
+ }
8549
+ async dispose() {
8550
+ if (this.disposed) return;
8551
+ this.disposed = true;
8552
+ await Promise.all([...this.sessions.values()].map((s) => this.ordered(s, () => this.close(s))));
8553
+ }
8554
+ descriptor(s) {
8555
+ return { ...s.value, ...s.metadata?.read() };
8556
+ }
8557
+ available() {
8558
+ if (this.disposed) fail("pty_unavailable", "Terminal host is disposed");
8559
+ if (!this.module) fail("pty_unavailable", this.unavailable ?? "Terminal native dependency unavailable");
8560
+ }
8561
+ checkLimit() {
8562
+ if (this.sessions.size >= TERMINAL_GRID_LIMITS.maxSessions) fail("pty_limit", "Terminal session limit exceeded");
8563
+ }
8564
+ scheduleShellName(s, attempt = 0) {
8565
+ const live = () => !s.closed && !s.value.exited && !this.disposed;
8566
+ if (!live()) return;
8567
+ s.shellNameTimer = this.scheduler.schedule(() => {
8568
+ s.shellNameTimer = void 0;
8569
+ if (!live()) return;
8570
+ const retry = () => {
8571
+ if (attempt < 2) this.scheduleShellName(s, attempt + 1);
8572
+ };
8573
+ void Promise.resolve().then(() => live() ? (this.options.shellProcessName ?? defaultShellProcessName)(s.pty.pid) : null).then((name) => {
8574
+ if (!live()) return;
8575
+ const shell = name && basename(name);
8576
+ if (shell && shell !== "spawn-helper") s.shellNames.add(shell);
8577
+ else retry();
8578
+ }, retry);
8579
+ }, this.options.shellNameDelayMs ?? 250);
8580
+ }
8581
+ ordered(s, action) {
8582
+ const operation = s.queue.then(action);
8583
+ s.queue = operation.catch(() => {
8584
+ });
8585
+ return operation;
8586
+ }
8587
+ async settle(s) {
8588
+ for (; ; ) {
8589
+ const rendered = s.rendered;
8590
+ await rendered;
8591
+ if (rendered === s.rendered) break;
8327
8592
  }
8593
+ if (s.failure) throw s.failure;
8328
8594
  }
8329
- /** The tty's foreground process basename, '' when it cannot be read (node-pty falls back to the shell path itself). */
8330
- foreground(s) {
8595
+ output(s, data) {
8596
+ if (s.closed || !data || s.failure) return;
8597
+ if (!s.shellNameStarted) {
8598
+ s.shellNameStarted = true;
8599
+ this.scheduleShellName(s);
8600
+ }
8601
+ const bytes = Buffer.byteLength(data);
8602
+ s.pendingBytes += bytes;
8331
8603
  try {
8332
- return processBasename(String(s.pty.process ?? ""));
8333
- } catch {
8334
- return "";
8604
+ if (s.pendingBytes > 256 * 1024) s.pty.pause();
8605
+ s.metadata?.onData?.(data);
8606
+ s.rendered = s.screen.write(data);
8607
+ void s.rendered.then(() => {
8608
+ s.pendingBytes -= bytes;
8609
+ try {
8610
+ if (!s.closed && !s.value.exited && s.pendingBytes < 64 * 1024) s.pty.resume();
8611
+ } catch (error) {
8612
+ this.failed(s, error);
8613
+ }
8614
+ this.dirty(s);
8615
+ }, (error) => this.failed(s, error));
8616
+ this.dirty(s);
8617
+ } catch (error) {
8618
+ this.failed(s, error);
8335
8619
  }
8336
8620
  }
8337
- /**
8338
- * Learns once per session, in the background shortly after spawn, what the tty calls the shell itself (macOS `p_comm`).
8339
- * Until it resolves (or when it fails) the configured basename is the only shell name: activity never waits on a subprocess.
8340
- */
8341
- async learnShellName(s) {
8342
- if (s.closed || s.exited) return;
8343
- try {
8344
- const name = await (this.options.shellProcessName ?? defaultShellProcessName)(s.pty.pid);
8345
- if (name) s.shellNames.add(processBasename(name));
8346
- } catch {
8621
+ failed(s, error) {
8622
+ if (!s.closed) {
8623
+ s.failure = error;
8624
+ s.captureTimer?.dispose();
8625
+ s.captureTimer = void 0;
8347
8626
  }
8348
8627
  }
8349
- /**
8350
- * A live sample for `keeper.activity` (spec §3.5/§4.1): `ptyOpen` live sessions, `ptyActive` live sessions whose foreground
8351
- * process is not their shell, `ptyExited` exited sessions still listed. One item per session, labelled with the foreground
8352
- * process basename (never its arguments), `sinceEpochMs` = when this label/state was first observed.
8353
- */
8354
- activity() {
8355
- const now = this.now();
8356
- let open = 0;
8357
- let active = 0;
8358
- let exited = 0;
8359
- const items = [];
8360
- for (const s of this.sessions.values()) {
8361
- if (s.exited) {
8362
- exited += 1;
8363
- items.push({ type: "pty", id: s.id, label: s.shellName.slice(0, PTY_LABEL_CHARS), state: "exited", sinceEpochMs: s.exitedAt });
8364
- continue;
8628
+ dirty(s) {
8629
+ if (s.closed || s.failure) return;
8630
+ s.dirty = true;
8631
+ if (!s.captureTimer) s.captureTimer = this.scheduler.schedule(() => {
8632
+ s.captureTimer = void 0;
8633
+ void this.ordered(s, async () => {
8634
+ if (s.closed) return;
8635
+ await this.settle(s);
8636
+ this.capture(s);
8637
+ }).catch((error) => {
8638
+ if (!(error instanceof KeeperMethodError && error.code === "pty_snapshot_too_large")) this.failed(s, error);
8639
+ });
8640
+ }, Math.max(0, 50 - (this.now() - s.capturedAt)));
8641
+ }
8642
+ async captureDue(s) {
8643
+ if (s.dirty && this.now() - s.capturedAt < 50) {
8644
+ s.captureTimer?.dispose();
8645
+ s.captureTimer = void 0;
8646
+ await new Promise((resolve) => this.scheduler.schedule(resolve, 50 - (this.now() - s.capturedAt)));
8647
+ }
8648
+ await this.settle(s);
8649
+ this.capture(s, true);
8650
+ }
8651
+ capture(s, force = false) {
8652
+ if (!s.dirty || !force && this.now() - s.capturedAt < 50) return;
8653
+ s.frames.publish(s.screen.capture(), this.descriptor(s), s.geometry);
8654
+ s.dirty = false;
8655
+ s.capturedAt = this.now();
8656
+ s.captureTimer?.dispose();
8657
+ s.captureTimer = void 0;
8658
+ }
8659
+ async capacity(s) {
8660
+ if (!s.attachments.size) return;
8661
+ const cols = Math.min(...[...s.attachments.values()].map((a) => a.cols));
8662
+ const rows = Math.min(...[...s.attachments.values()].map((a) => a.rows));
8663
+ if (cols === s.value.cols && rows === s.value.rows) return;
8664
+ await this.settle(s);
8665
+ if (!s.value.exited) s.pty.resize(cols, rows);
8666
+ try {
8667
+ await s.screen.resize(cols, rows);
8668
+ await this.settle(s);
8669
+ } catch (error) {
8670
+ this.failed(s, error);
8671
+ try {
8672
+ if (!s.value.exited) s.pty.resize(s.value.cols, s.value.rows);
8673
+ } catch (rollback) {
8674
+ this.failed(s, rollback);
8365
8675
  }
8366
- open += 1;
8367
- const fg = this.foreground(s);
8368
- const busy = fg !== "" && !s.shellNames.has(fg);
8369
- const label = (busy ? fg : s.shellName).slice(0, PTY_LABEL_CHARS);
8370
- const state = busy ? "active" : "idle";
8371
- if (s.observed.label !== label || s.observed.state !== state) s.observed = { label, state, since: now };
8372
- if (busy) active += 1;
8373
- items.push({ type: "pty", id: s.id, label, state, sinceEpochMs: s.observed.since });
8676
+ throw error;
8374
8677
  }
8375
- return { metrics: { ptyOpen: open, ptyActive: active, ptyExited: exited }, items };
8376
- }
8377
- /** Kills every session (the keeper is stopping). */
8378
- async dispose() {
8379
- for (const session of this.sessions.values()) {
8380
- session.closed = true;
8381
- if (!session.exited) session.pty.kill();
8382
- await session.rendered;
8383
- session.terminal.dispose();
8678
+ s.value.cols = cols;
8679
+ s.value.rows = rows;
8680
+ s.geometry++;
8681
+ this.dirty(s);
8682
+ }
8683
+ inputContext(s) {
8684
+ return { cols: s.value.cols, rows: s.value.rows, geometry: s.geometry, modes: s.screen.state().modes };
8685
+ }
8686
+ coordinates(s, event) {
8687
+ if (event.geometry !== s.geometry) fail("pty_geometry_mismatch", "Terminal geometry changed");
8688
+ if (event.x >= s.value.cols || event.y >= s.value.rows) bad("Terminal coordinates outside geometry");
8689
+ }
8690
+ assertRunning(s) {
8691
+ if (s.value.exited) fail("pty_exited", "Terminal has exited");
8692
+ }
8693
+ release(s, clientId) {
8694
+ if (s.attachments.delete(clientId)) s.input.release(clientId);
8695
+ this.scheduleLease(s);
8696
+ }
8697
+ async expire(s) {
8698
+ let changed = false;
8699
+ for (const [id, attachment] of s.attachments) if (attachment.expires <= this.now()) {
8700
+ this.release(s, id);
8701
+ changed = true;
8702
+ }
8703
+ if (changed) await this.capacity(s);
8704
+ }
8705
+ scheduleLease(s) {
8706
+ s.leaseTimer?.dispose();
8707
+ s.leaseTimer = void 0;
8708
+ if (s.closed || this.disposed || !s.attachments.size) return;
8709
+ const expires = Math.min(...[...s.attachments.values()].map((attachment) => attachment.expires));
8710
+ s.leaseTimer = this.scheduler.schedule(() => {
8711
+ s.leaseTimer = void 0;
8712
+ void this.ordered(s, () => this.expire(s)).catch((error) => this.failed(s, error)).finally(() => this.scheduleLease(s));
8713
+ }, Math.max(0, expires - this.now()));
8714
+ }
8715
+ async close(s) {
8716
+ if (s.closed) return;
8717
+ s.closed = true;
8718
+ if (this.sessions.get(s.value.id) === s) this.sessions.delete(s.value.id);
8719
+ s.shellNameTimer?.dispose();
8720
+ s.shellNameTimer = void 0;
8721
+ s.captureTimer?.dispose();
8722
+ s.captureTimer = void 0;
8723
+ s.leaseTimer?.dispose();
8724
+ s.leaseTimer = void 0;
8725
+ for (const listener of s.listeners) listener.dispose();
8726
+ for (const id of [...s.attachments.keys()]) this.release(s, id);
8727
+ s.input.dispose();
8728
+ s.metadata?.dispose?.();
8729
+ try {
8730
+ if (!s.value.exited) s.pty.kill();
8731
+ } finally {
8732
+ s.screen.dispose();
8733
+ await s.rendered.catch(() => {
8734
+ });
8384
8735
  }
8385
- this.sessions.clear();
8386
8736
  }
8387
8737
  };
8738
+ function disposable(value) {
8739
+ return value && typeof value.dispose === "function" ? value : { dispose: () => {
8740
+ } };
8741
+ }
8742
+ function request(params) {
8743
+ if (Buffer.byteLength(JSON.stringify(params)) > TERMINAL_GRID_LIMITS.maxRequestBytes) bad("Terminal request exceeds limit");
8744
+ }
8745
+ function keys2(params, allowed) {
8746
+ if (Object.keys(params).some((key) => !allowed.includes(key))) bad("Unexpected terminal parameter");
8747
+ }
8748
+ function identifier(params, name) {
8749
+ const value = params[name];
8750
+ if (typeof value !== "string" || !/^[A-Za-z0-9_.:-]{1,128}$/.test(value)) return bad(`Invalid ${name}`);
8751
+ return value;
8752
+ }
8753
+ function integer2(params, name, min, max) {
8754
+ const value = params[name];
8755
+ if (!Number.isSafeInteger(value) || value < min || value > max) return bad(`Invalid ${name}`);
8756
+ return value;
8757
+ }
8758
+ function position(params) {
8759
+ return { x: integer2(params, "x", 0, TERMINAL_GRID_LIMITS.maxCols - 1), y: integer2(params, "y", 0, TERMINAL_GRID_LIMITS.maxRows - 1), geometry: integer2(params, "geometry", 1, Number.MAX_SAFE_INTEGER) };
8760
+ }
8761
+ function input(params, ref) {
8762
+ switch (params.kind) {
8763
+ case "text":
8764
+ case "paste":
8765
+ keys2(params, [...ref, "kind", "text"]);
8766
+ if (typeof params.text !== "string" || Buffer.byteLength(params.text) > TERMINAL_GRID_LIMITS.maxInputBytes) return bad("Terminal input exceeds limit");
8767
+ return { kind: params.kind, text: params.text };
8768
+ case "key":
8769
+ keys2(params, [...ref, "kind", "usage", "down", "modifiers"]);
8770
+ if (typeof params.down !== "boolean") return bad("Invalid down");
8771
+ return { kind: "key", usage: integer2(params, "usage", 0, 65535), down: params.down, modifiers: integer2(params, "modifiers", 0, 15) };
8772
+ case "pointer":
8773
+ case "button": {
8774
+ keys2(params, [...ref, "kind", "x", "y", "geometry", "modifiers", ...params.kind === "button" ? ["button", "down"] : []]);
8775
+ const point = { ...position(params), modifiers: integer2(params, "modifiers", 0, 15) };
8776
+ if (params.kind === "pointer") return { kind: "pointer", ...point };
8777
+ if (typeof params.down !== "boolean") return bad("Invalid down");
8778
+ return { kind: "button", ...point, button: integer2(params, "button", 0, 2), down: params.down };
8779
+ }
8780
+ case "release":
8781
+ keys2(params, [...ref, "kind"]);
8782
+ return { kind: "release" };
8783
+ default:
8784
+ return bad("Invalid terminal input kind");
8785
+ }
8786
+ }
8787
+ function defaultShellProcessName(pid) {
8788
+ if (process.platform !== "darwin") return Promise.resolve(null);
8789
+ return new Promise((resolve) => {
8790
+ execFile4("ps", ["-o", "ucomm=", "-p", String(pid)], { timeout: 1e3 }, (error, stdout) => resolve(error ? null : stdout.trim() || null));
8791
+ });
8792
+ }
8388
8793
 
8389
8794
  // src/workstation/keepers/terminal/keeper.ts
8390
8795
  async function launchOf(p) {
@@ -8397,18 +8802,29 @@ async function launchOf(p) {
8397
8802
  if (!executable) badRequest("Terminal shell is not an executable file");
8398
8803
  return { cwd, shell, env };
8399
8804
  }
8400
- var KEEPER_CREATE_KEYS = [...CREATE_KEYS, "shell", "env"];
8805
+ var KEEPER_CREATE_KEYS = ["id", "cwd", "shell", "env", "registrationId"];
8401
8806
  function createTerminalKeeper(options = {}) {
8402
- const sessions = new PtySessions(options);
8807
+ let available = false;
8808
+ const sessions = new NativePtySessions({ ...options, loadPty: () => {
8809
+ const module = (options.loadPty ?? (() => createRequire2(import.meta.url)("node-pty")))();
8810
+ available = true;
8811
+ return module;
8812
+ } });
8403
8813
  const exec = options.exec;
8404
8814
  return {
8405
8815
  sessions,
8406
8816
  exec,
8407
- capabilities: [...sessions.available ? [PTY_CAPABILITY] : [], ...exec ? [EXEC_CAPABILITY] : []],
8817
+ capabilities: [...available ? [PTY_CAPABILITY] : [], ...exec ? [EXEC_CAPABILITY] : []],
8408
8818
  install(server) {
8409
- server.register("pty.create", (params) => sessions.create(params, KEEPER_CREATE_KEYS, () => launchOf(params)), { startsWork: true });
8410
- server.register("pty.list", (params) => sessions.list(params));
8411
- for (const op of ["attach", "poll", "write", "resize", "takeover", "detach", "close"])
8819
+ server.register("pty.create", (params) => {
8820
+ if (Object.keys(params).some((key) => !KEEPER_CREATE_KEYS.includes(key))) badRequest("Unexpected tool parameter");
8821
+ return sessions.create({ id: params.id, ...params.cwd !== void 0 ? { cwd: params.cwd } : {}, ...params.registrationId !== void 0 ? { registrationId: params.registrationId } : {} }, () => launchOf(params));
8822
+ }, { startsWork: true });
8823
+ server.register("pty.list", (params) => {
8824
+ if (Object.keys(params).length) badRequest("Unexpected tool parameter");
8825
+ return sessions.list();
8826
+ });
8827
+ for (const op of NATIVE_PTY_OPERATIONS)
8412
8828
  server.register(`pty.${op}`, (params) => sessions.operate(op, params));
8413
8829
  server.addActivitySampler(() => sessions.activity());
8414
8830
  if (!exec) return;
@@ -8438,9 +8854,9 @@ function createTerminalKeeper(options = {}) {
8438
8854
  }
8439
8855
 
8440
8856
  // src/workstation/keepers/terminal/main.ts
8441
- var protocolVersion = 1;
8857
+ var protocolVersion = 2;
8442
8858
  function loadBundledPty(bundleDir) {
8443
- const load = createRequire2(import.meta.url);
8859
+ const load = createRequire3(import.meta.url);
8444
8860
  const resolved = realpathSync(load.resolve("node-pty"));
8445
8861
  if (!resolved.startsWith(realpathSync(bundleDir) + sep)) throw new Error(`node-pty resolves outside the keeper bundle (${resolved})`);
8446
8862
  return load("node-pty");
@@ -8449,7 +8865,7 @@ await runKeeperMain({
8449
8865
  kind: "terminal",
8450
8866
  protocolVersion,
8451
8867
  create: async (config, { log }) => {
8452
- const exec = new ExecHost({ root: path5.join(config.dataDir, "exec"), owner: { pid: process.pid, gen: config.gen, bundleDir: config.bundleDir }, log });
8868
+ const exec = new ExecHost({ root: path6.join(config.dataDir, "exec"), owner: { pid: process.pid, gen: config.gen, bundleDir: config.bundleDir }, log });
8453
8869
  await exec.open();
8454
8870
  return createTerminalKeeper({
8455
8871
  exec,