harnery 0.31.6 → 0.33.0

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 (264) hide show
  1. package/README.md +2 -0
  2. package/dist/commander.d.ts.map +1 -1
  3. package/dist/commander.js +5 -1
  4. package/dist/commands/agents.d.ts +0 -9
  5. package/dist/commands/agents.d.ts.map +1 -1
  6. package/dist/commands/agents.js +280 -94
  7. package/dist/commands/browse-session.d.ts +21 -0
  8. package/dist/commands/browse-session.d.ts.map +1 -0
  9. package/dist/commands/browse-session.js +157 -0
  10. package/dist/commands/browse.d.ts.map +1 -1
  11. package/dist/commands/browse.js +205 -37
  12. package/dist/commands/checkpoint.js +1 -1
  13. package/dist/commands/claude-desktop.d.ts +2 -2
  14. package/dist/commands/claude-desktop.d.ts.map +1 -1
  15. package/dist/commands/claude-desktop.js +74 -3
  16. package/dist/commands/deinit.d.ts.map +1 -1
  17. package/dist/commands/deinit.js +4 -0
  18. package/dist/commands/docs.d.ts.map +1 -1
  19. package/dist/commands/docs.js +40 -0
  20. package/dist/commands/doctor.d.ts +4 -2
  21. package/dist/commands/doctor.d.ts.map +1 -1
  22. package/dist/commands/doctor.js +141 -19
  23. package/dist/commands/init.d.ts +2 -2
  24. package/dist/commands/init.d.ts.map +1 -1
  25. package/dist/commands/init.js +26 -5
  26. package/dist/commands/instructions.d.ts +4 -0
  27. package/dist/commands/instructions.d.ts.map +1 -0
  28. package/dist/commands/instructions.js +100 -0
  29. package/dist/commands/presence.d.ts.map +1 -1
  30. package/dist/commands/presence.js +4 -1
  31. package/dist/commands/tunnel.d.ts.map +1 -1
  32. package/dist/commands/tunnel.js +2 -3
  33. package/dist/core/agents/canonical-emit.d.ts +2 -3
  34. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  35. package/dist/core/agents/canonical-emit.js +6 -5
  36. package/dist/core/agents/cli.js +128 -89
  37. package/dist/core/agents/coord-client.d.ts +18 -3
  38. package/dist/core/agents/coord-client.d.ts.map +1 -1
  39. package/dist/core/agents/coord-client.js +5 -4
  40. package/dist/core/agents/finalization.d.ts +68 -0
  41. package/dist/core/agents/finalization.d.ts.map +1 -0
  42. package/dist/core/agents/finalization.js +443 -0
  43. package/dist/core/agents/git-hook.d.ts +51 -0
  44. package/dist/core/agents/git-hook.d.ts.map +1 -0
  45. package/dist/core/agents/git-hook.js +118 -0
  46. package/dist/core/agents/index.d.ts +1 -0
  47. package/dist/core/agents/index.d.ts.map +1 -1
  48. package/dist/core/agents/index.js +1 -0
  49. package/dist/core/agents/render/prompt-context.d.ts +12 -5
  50. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  51. package/dist/core/agents/render/prompt-context.js +70 -16
  52. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  53. package/dist/core/agents/render/session-context.js +23 -4
  54. package/dist/core/agents/rules/claim-conflict.js +3 -3
  55. package/dist/core/agents/rules/commit-conflict.d.ts +15 -6
  56. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  57. package/dist/core/agents/rules/commit-conflict.js +21 -5
  58. package/dist/core/agents/rules/stop-hook.d.ts +3 -0
  59. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  60. package/dist/core/agents/rules/stop-hook.js +59 -23
  61. package/dist/core/agents/session-events.d.ts +8 -16
  62. package/dist/core/agents/session-events.d.ts.map +1 -1
  63. package/dist/core/agents/session-events.js +12 -26
  64. package/dist/core/agents/state/heartbeat-projector.d.ts +14 -0
  65. package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
  66. package/dist/core/agents/state/heartbeat-projector.js +98 -1
  67. package/dist/core/agents/state/heartbeat-writer.d.ts +47 -5
  68. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  69. package/dist/core/agents/state/heartbeat-writer.js +86 -17
  70. package/dist/core/agents/state/names.d.ts +22 -1
  71. package/dist/core/agents/state/names.d.ts.map +1 -1
  72. package/dist/core/agents/state/names.js +40 -2
  73. package/dist/core/agents/state/session-state.d.ts +44 -0
  74. package/dist/core/agents/state/session-state.d.ts.map +1 -0
  75. package/dist/core/agents/state/session-state.js +81 -0
  76. package/dist/core/config.d.ts +30 -2
  77. package/dist/core/config.d.ts.map +1 -1
  78. package/dist/core/config.js +74 -11
  79. package/dist/core/governor/planning.d.ts.map +1 -1
  80. package/dist/core/governor/planning.js +12 -13
  81. package/dist/core/hooks/adapter/detect.d.ts +2 -4
  82. package/dist/core/hooks/adapter/detect.d.ts.map +1 -1
  83. package/dist/core/hooks/adapter/detect.js +4 -14
  84. package/dist/core/hooks/adapter/events.d.ts.map +1 -1
  85. package/dist/core/hooks/adapter/events.js +1 -0
  86. package/dist/core/hooks/adapter/output.d.ts +1 -1
  87. package/dist/core/hooks/adapter/output.d.ts.map +1 -1
  88. package/dist/core/hooks/adapter/output.js +5 -4
  89. package/dist/core/hooks/adapter/parse.d.ts +1 -1
  90. package/dist/core/hooks/adapter/parse.d.ts.map +1 -1
  91. package/dist/core/hooks/adapter/parse.js +2 -0
  92. package/dist/core/hooks/adapter/wiring.d.ts +23 -0
  93. package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
  94. package/dist/core/hooks/adapter/wiring.js +32 -0
  95. package/dist/core/hooks/cli.d.ts +1 -2
  96. package/dist/core/hooks/cli.d.ts.map +1 -1
  97. package/dist/core/hooks/cli.js +258 -32
  98. package/dist/core/hooks/codex-authorization.d.ts +30 -0
  99. package/dist/core/hooks/codex-authorization.d.ts.map +1 -0
  100. package/dist/core/hooks/codex-authorization.js +199 -0
  101. package/dist/core/hooks/codex-wsl-bridge.d.ts +46 -0
  102. package/dist/core/hooks/codex-wsl-bridge.d.ts.map +1 -0
  103. package/dist/core/hooks/codex-wsl-bridge.js +137 -0
  104. package/dist/core/hooks/effects/index.d.ts +1 -1
  105. package/dist/core/hooks/effects/index.js +3 -3
  106. package/dist/core/hooks/events/schema.d.ts +57 -1
  107. package/dist/core/hooks/events/schema.d.ts.map +1 -1
  108. package/dist/core/hooks/resolve/owner.d.ts +7 -1
  109. package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
  110. package/dist/core/hooks/resolve/owner.js +15 -0
  111. package/dist/core/hooks/resolve/transcript.d.ts +44 -0
  112. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  113. package/dist/core/hooks/resolve/transcript.js +176 -1
  114. package/dist/core/hooks/session-name-presence.d.ts +30 -0
  115. package/dist/core/hooks/session-name-presence.d.ts.map +1 -0
  116. package/dist/core/hooks/session-name-presence.js +43 -0
  117. package/dist/core/hooks/unsafe-cross-shell.d.ts +13 -0
  118. package/dist/core/hooks/unsafe-cross-shell.d.ts.map +1 -0
  119. package/dist/core/hooks/unsafe-cross-shell.js +151 -0
  120. package/dist/core/presence/blob.d.ts +4 -0
  121. package/dist/core/presence/blob.d.ts.map +1 -1
  122. package/dist/core/presence/blob.js +12 -0
  123. package/dist/core/presence/index.js +15 -1
  124. package/dist/core/work/state.d.ts +4 -5
  125. package/dist/core/work/state.d.ts.map +1 -1
  126. package/dist/core/work/state.js +5 -8
  127. package/dist/core/workflow/index.d.ts +1 -1
  128. package/dist/core/workflow/index.d.ts.map +1 -1
  129. package/dist/core/workflow/proof.d.ts +2 -2
  130. package/dist/core/workflow/proof.d.ts.map +1 -1
  131. package/dist/core/workflow/proof.js +2 -2
  132. package/dist/core/workflow/run-state.d.ts +2 -2
  133. package/dist/core/workflow/run-state.d.ts.map +1 -1
  134. package/dist/core/workflow/run-state.js +2 -2
  135. package/dist/core/workflow/types.d.ts +3 -3
  136. package/dist/core/workflow/types.d.ts.map +1 -1
  137. package/dist/core/workflow/workspaces/execution.d.ts +2 -2
  138. package/dist/core/workflow/workspaces/execution.d.ts.map +1 -1
  139. package/dist/core/workflow/workspaces/execution.js +5 -0
  140. package/dist/core/workflow/workspaces/index.d.ts +1 -1
  141. package/dist/core/workflow/workspaces/index.d.ts.map +1 -1
  142. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  143. package/dist/core/workflow/workspaces/local-git.js +1 -2
  144. package/dist/core/workflow/workspaces/paths.d.ts +2 -2
  145. package/dist/core/workflow/workspaces/types.d.ts +0 -6
  146. package/dist/core/workflow/workspaces/types.d.ts.map +1 -1
  147. package/dist/core/workflow/workspaces/validate.d.ts +0 -2
  148. package/dist/core/workflow/workspaces/validate.d.ts.map +1 -1
  149. package/dist/core/workflow/workspaces/validate.js +0 -2
  150. package/dist/lib/browser/client.d.ts +118 -1
  151. package/dist/lib/browser/client.d.ts.map +1 -1
  152. package/dist/lib/browser/client.js +435 -6
  153. package/dist/lib/browser/geometry.d.ts.map +1 -1
  154. package/dist/lib/browser/geometry.js +180 -37
  155. package/dist/lib/browser/index.d.ts +5 -1
  156. package/dist/lib/browser/index.d.ts.map +1 -1
  157. package/dist/lib/browser/index.js +5 -1
  158. package/dist/lib/browser/netscape-cookies.d.ts +6 -0
  159. package/dist/lib/browser/netscape-cookies.d.ts.map +1 -0
  160. package/dist/lib/browser/netscape-cookies.js +31 -0
  161. package/dist/lib/browser/proxy.d.ts +24 -0
  162. package/dist/lib/browser/proxy.d.ts.map +1 -0
  163. package/dist/lib/browser/proxy.js +84 -0
  164. package/dist/lib/browser/runts.d.ts +6 -0
  165. package/dist/lib/browser/runts.d.ts.map +1 -1
  166. package/dist/lib/browser/runts.js +21 -4
  167. package/dist/lib/browser/session-control.d.ts +112 -0
  168. package/dist/lib/browser/session-control.d.ts.map +1 -0
  169. package/dist/lib/browser/session-control.js +670 -0
  170. package/dist/lib/claude-desktop.d.ts +47 -0
  171. package/dist/lib/claude-desktop.d.ts.map +1 -1
  172. package/dist/lib/claude-desktop.js +169 -1
  173. package/dist/lib/docs-links.d.ts +108 -0
  174. package/dist/lib/docs-links.d.ts.map +1 -0
  175. package/dist/lib/docs-links.js +555 -0
  176. package/dist/lib/exec.d.ts +1 -1
  177. package/dist/lib/exec.js +1 -1
  178. package/dist/lib/identities/assume.d.ts +4 -2
  179. package/dist/lib/identities/assume.d.ts.map +1 -1
  180. package/dist/lib/identities/assume.js +15 -2
  181. package/dist/lib/instructions/bundle.d.ts +33 -0
  182. package/dist/lib/instructions/bundle.d.ts.map +1 -0
  183. package/dist/lib/instructions/bundle.js +149 -0
  184. package/dist/lib/instructions/git-hooks.d.ts +75 -0
  185. package/dist/lib/instructions/git-hooks.d.ts.map +1 -0
  186. package/dist/lib/instructions/git-hooks.js +238 -0
  187. package/dist/lib/instructions/splice.d.ts +10 -4
  188. package/dist/lib/instructions/splice.d.ts.map +1 -1
  189. package/dist/lib/instructions/splice.js +19 -11
  190. package/package.json +2 -2
  191. package/schemas/config.schema.json +32 -0
  192. package/src/commander.ts +5 -1
  193. package/src/commands/agents.ts +341 -113
  194. package/src/commands/browse-session.ts +245 -0
  195. package/src/commands/browse.ts +287 -44
  196. package/src/commands/checkpoint.ts +1 -1
  197. package/src/commands/claude-desktop.ts +106 -5
  198. package/src/commands/deinit.ts +7 -1
  199. package/src/commands/docs.ts +56 -0
  200. package/src/commands/doctor.ts +155 -21
  201. package/src/commands/init.ts +28 -9
  202. package/src/commands/instructions.ts +129 -0
  203. package/src/commands/presence.ts +7 -1
  204. package/src/commands/tunnel.ts +2 -5
  205. package/src/core/agents/canonical-emit.ts +7 -6
  206. package/src/core/agents/cli.ts +143 -89
  207. package/src/core/agents/coord-client.ts +21 -5
  208. package/src/core/agents/finalization.ts +595 -0
  209. package/src/core/agents/git-hook.ts +126 -0
  210. package/src/core/agents/index.ts +1 -0
  211. package/src/core/agents/render/prompt-context.ts +96 -16
  212. package/src/core/agents/render/session-context.ts +29 -4
  213. package/src/core/agents/rules/claim-conflict.ts +3 -3
  214. package/src/core/agents/rules/commit-conflict.ts +35 -8
  215. package/src/core/agents/rules/stop-hook.ts +71 -23
  216. package/src/core/agents/session-events.ts +14 -41
  217. package/src/core/agents/state/heartbeat-projector.ts +108 -1
  218. package/src/core/agents/state/heartbeat-writer.ts +129 -16
  219. package/src/core/agents/state/names.ts +54 -2
  220. package/src/core/agents/state/session-state.ts +131 -0
  221. package/src/core/config.ts +92 -12
  222. package/src/core/governor/planning.ts +17 -12
  223. package/src/core/hooks/adapter/detect.ts +4 -13
  224. package/src/core/hooks/adapter/events.ts +1 -0
  225. package/src/core/hooks/adapter/output.ts +9 -4
  226. package/src/core/hooks/adapter/parse.ts +3 -0
  227. package/src/core/hooks/adapter/wiring.ts +43 -0
  228. package/src/core/hooks/cli.ts +296 -27
  229. package/src/core/hooks/codex-authorization.ts +253 -0
  230. package/src/core/hooks/codex-wsl-bridge.ts +188 -0
  231. package/src/core/hooks/effects/index.ts +5 -5
  232. package/src/core/hooks/events/schema.ts +71 -0
  233. package/src/core/hooks/resolve/owner.ts +22 -1
  234. package/src/core/hooks/resolve/transcript.ts +164 -1
  235. package/src/core/hooks/session-name-presence.ts +52 -0
  236. package/src/core/hooks/unsafe-cross-shell.ts +160 -0
  237. package/src/core/presence/blob.ts +18 -0
  238. package/src/core/presence/index.ts +18 -1
  239. package/src/core/work/state.ts +8 -12
  240. package/src/core/workflow/engine.ts +2 -2
  241. package/src/core/workflow/index.ts +0 -1
  242. package/src/core/workflow/proof.ts +4 -4
  243. package/src/core/workflow/run-state.ts +5 -5
  244. package/src/core/workflow/types.ts +3 -3
  245. package/src/core/workflow/workspaces/execution.ts +11 -4
  246. package/src/core/workflow/workspaces/index.ts +0 -3
  247. package/src/core/workflow/workspaces/local-git.ts +1 -2
  248. package/src/core/workflow/workspaces/paths.ts +2 -2
  249. package/src/core/workflow/workspaces/types.ts +0 -9
  250. package/src/core/workflow/workspaces/validate.ts +1 -5
  251. package/src/lib/browser/client.ts +528 -6
  252. package/src/lib/browser/geometry.ts +197 -37
  253. package/src/lib/browser/index.ts +39 -0
  254. package/src/lib/browser/netscape-cookies.ts +39 -0
  255. package/src/lib/browser/proxy.ts +105 -0
  256. package/src/lib/browser/runts.ts +27 -3
  257. package/src/lib/browser/session-control.ts +892 -0
  258. package/src/lib/claude-desktop.ts +232 -1
  259. package/src/lib/docs-links.ts +674 -0
  260. package/src/lib/exec.ts +1 -1
  261. package/src/lib/identities/assume.ts +22 -1
  262. package/src/lib/instructions/bundle.ts +199 -0
  263. package/src/lib/instructions/git-hooks.ts +259 -0
  264. package/src/lib/instructions/splice.ts +41 -11
@@ -0,0 +1,245 @@
1
+ import type { Readable } from "node:stream";
2
+ import type { Command } from "commander";
3
+ import type { EmitContext } from "../commander.ts";
4
+ import type { BrowserSessionLocator } from "../lib/browser/client.ts";
5
+ import {
6
+ BROWSER_SESSION_MAX_FILL_BYTES,
7
+ BrowserSessionError,
8
+ sendBrowserSessionRequest,
9
+ } from "../lib/browser/session-control.ts";
10
+
11
+ interface ControlFileOpts {
12
+ controlFile: string;
13
+ }
14
+
15
+ interface LocatorOpts extends ControlFileOpts {
16
+ selector?: string;
17
+ role?: string;
18
+ name?: string;
19
+ label?: string;
20
+ text?: string;
21
+ partial?: boolean;
22
+ }
23
+
24
+ export function registerBrowseSessionCommand(program: Command, emit: EmitContext): void {
25
+ const session = program
26
+ .command("browse-session")
27
+ .description("Control an opted-in headed browse session through an owner-only descriptor");
28
+
29
+ session
30
+ .command("status")
31
+ .requiredOption("--control-file <path>", "Owner-only session descriptor")
32
+ .action((opts: ControlFileOpts) => runAction(emit, opts.controlFile, "status", {}));
33
+
34
+ addLocatorOptions(
35
+ session
36
+ .command("inspect")
37
+ .description("Inspect sanitized text, controls, focus, and active-tab state")
38
+ .requiredOption("--control-file <path>", "Owner-only session descriptor"),
39
+ ).action((opts: LocatorOpts) => {
40
+ const locator = parseLocatorOptions(opts, false);
41
+ return runAction(emit, opts.controlFile, "inspect", locator ? { locator } : {});
42
+ });
43
+
44
+ session
45
+ .command("screenshot")
46
+ .requiredOption("--control-file <path>", "Owner-only session descriptor")
47
+ .requiredOption("--out <path>", "New PNG path in an existing directory")
48
+ .action((opts: ControlFileOpts & { out: string }) =>
49
+ runAction(emit, opts.controlFile, "screenshot", { out: opts.out }),
50
+ );
51
+
52
+ session
53
+ .command("tabs")
54
+ .requiredOption("--control-file <path>", "Owner-only session descriptor")
55
+ .action((opts: ControlFileOpts) => runAction(emit, opts.controlFile, "tabs", {}));
56
+
57
+ session
58
+ .command("select-tab")
59
+ .requiredOption("--control-file <path>", "Owner-only session descriptor")
60
+ .requiredOption("--index <n>", "Stable session-local tab index", parseIndex)
61
+ .action((opts: ControlFileOpts & { index: number }) =>
62
+ runAction(emit, opts.controlFile, "select_tab", { index: opts.index }),
63
+ );
64
+
65
+ session
66
+ .command("open-tab <url>")
67
+ .requiredOption("--control-file <path>", "Owner-only session descriptor")
68
+ .action((url: string, opts: ControlFileOpts) =>
69
+ runAction(emit, opts.controlFile, "open_tab", { url }),
70
+ );
71
+
72
+ session
73
+ .command("close-tab")
74
+ .requiredOption("--control-file <path>", "Owner-only session descriptor")
75
+ .requiredOption("--index <n>", "Stable session-local tab index", parseIndex)
76
+ .action((opts: ControlFileOpts & { index: number }) =>
77
+ runAction(emit, opts.controlFile, "close_tab", { index: opts.index }),
78
+ );
79
+
80
+ session
81
+ .command("goto <url>")
82
+ .requiredOption("--control-file <path>", "Owner-only session descriptor")
83
+ .action((url: string, opts: ControlFileOpts) =>
84
+ runAction(emit, opts.controlFile, "goto", { url }),
85
+ );
86
+
87
+ session
88
+ .command("reload")
89
+ .requiredOption("--control-file <path>", "Owner-only session descriptor")
90
+ .action((opts: ControlFileOpts) => runAction(emit, opts.controlFile, "reload", {}));
91
+
92
+ addLocatorOptions(
93
+ session
94
+ .command("click")
95
+ .requiredOption("--control-file <path>", "Owner-only session descriptor"),
96
+ ).action((opts: LocatorOpts) =>
97
+ runAction(emit, opts.controlFile, "click", { locator: parseLocatorOptions(opts, true) }),
98
+ );
99
+
100
+ addLocatorOptions(
101
+ session
102
+ .command("fill")
103
+ .description("Fill one strict locator with a value read once from standard input")
104
+ .requiredOption("--control-file <path>", "Owner-only session descriptor"),
105
+ ).action(async (opts: LocatorOpts) => {
106
+ const value = await readFillValue(process.stdin);
107
+ return runAction(emit, opts.controlFile, "fill", {
108
+ locator: parseLocatorOptions(opts, true),
109
+ value,
110
+ });
111
+ });
112
+
113
+ session
114
+ .command("press <key>")
115
+ .requiredOption("--control-file <path>", "Owner-only session descriptor")
116
+ .action((key: string, opts: ControlFileOpts) =>
117
+ runAction(emit, opts.controlFile, "press", { key }),
118
+ );
119
+
120
+ addLocatorOptions(
121
+ session
122
+ .command("wait")
123
+ .requiredOption("--control-file <path>", "Owner-only session descriptor"),
124
+ ).action((opts: LocatorOpts) =>
125
+ runAction(emit, opts.controlFile, "wait", { locator: parseLocatorOptions(opts, true) }),
126
+ );
127
+
128
+ session
129
+ .command("close")
130
+ .requiredOption("--control-file <path>", "Owner-only session descriptor")
131
+ .action((opts: ControlFileOpts) => runAction(emit, opts.controlFile, "close", {}));
132
+ }
133
+
134
+ function addLocatorOptions(command: Command): Command {
135
+ return command
136
+ .option("--selector <css>", "CSS locator")
137
+ .option("--role <role>", "Accessible role locator")
138
+ .option("--name <name>", "Accessible name used with --role")
139
+ .option("--label <text>", "Associated label locator")
140
+ .option("--text <text>", "Visible text locator")
141
+ .option("--partial", "Allow partial accessible-name, label, or text matching");
142
+ }
143
+
144
+ export function parseLocatorOptions(opts: LocatorOpts, required: true): BrowserSessionLocator;
145
+ export function parseLocatorOptions(
146
+ opts: LocatorOpts,
147
+ required: false,
148
+ ): BrowserSessionLocator | undefined;
149
+ export function parseLocatorOptions(
150
+ opts: LocatorOpts,
151
+ required: boolean,
152
+ ): BrowserSessionLocator | undefined {
153
+ const forms = [opts.selector, opts.role, opts.label, opts.text].filter(
154
+ (value): value is string => value !== undefined,
155
+ );
156
+ if (forms.length === 0) {
157
+ if (!required) {
158
+ if (opts.name !== undefined || opts.partial) {
159
+ throw new BrowserSessionError(
160
+ "invalid_locator",
161
+ "--name and --partial require a locator form.",
162
+ );
163
+ }
164
+ return undefined;
165
+ }
166
+ throw new BrowserSessionError(
167
+ "invalid_locator",
168
+ "Choose exactly one locator: --selector, --role, --label, or --text.",
169
+ );
170
+ }
171
+ if (forms.length !== 1) {
172
+ throw new BrowserSessionError(
173
+ "invalid_locator",
174
+ "Choose exactly one locator: --selector, --role, --label, or --text.",
175
+ );
176
+ }
177
+ if (opts.name !== undefined && opts.role === undefined) {
178
+ throw new BrowserSessionError("invalid_locator", "--name is valid only with --role.");
179
+ }
180
+ if (opts.selector !== undefined) {
181
+ if (opts.partial) {
182
+ throw new BrowserSessionError("invalid_locator", "--partial is not valid with --selector.");
183
+ }
184
+ return { kind: "selector", value: opts.selector, partial: false };
185
+ }
186
+ if (opts.role !== undefined) {
187
+ return {
188
+ kind: "role",
189
+ value: opts.role,
190
+ ...(opts.name === undefined ? {} : { name: opts.name }),
191
+ partial: Boolean(opts.partial),
192
+ };
193
+ }
194
+ if (opts.label !== undefined) {
195
+ return { kind: "label", value: opts.label, partial: Boolean(opts.partial) };
196
+ }
197
+ return { kind: "text", value: opts.text!, partial: Boolean(opts.partial) };
198
+ }
199
+
200
+ export async function readFillValue(input: Readable): Promise<string> {
201
+ const chunks: Buffer[] = [];
202
+ let size = 0;
203
+ for await (const chunk of input) {
204
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
205
+ size += bytes.length;
206
+ if (size > BROWSER_SESSION_MAX_FILL_BYTES) {
207
+ throw new BrowserSessionError("fill_too_large", "Fill input exceeds the 64 KiB limit.");
208
+ }
209
+ chunks.push(bytes);
210
+ }
211
+ const value = Buffer.concat(chunks)
212
+ .toString("utf8")
213
+ .replace(/\r?\n$/, "");
214
+ if (value.length === 0) {
215
+ throw new BrowserSessionError("fill_empty", "Fill input from standard input was empty.");
216
+ }
217
+ return value;
218
+ }
219
+
220
+ async function runAction(
221
+ emit: EmitContext,
222
+ controlFile: string,
223
+ action: Parameters<typeof sendBrowserSessionRequest>[1],
224
+ args: Record<string, unknown>,
225
+ ): Promise<void> {
226
+ try {
227
+ emit.data(await sendBrowserSessionRequest(controlFile, action, args));
228
+ } catch (error) {
229
+ if (error instanceof BrowserSessionError) {
230
+ emit.error({ code: error.code, message: error.message });
231
+ emit.setExitCode(1);
232
+ return;
233
+ }
234
+ emit.error({ code: "browser_session_failed", message: "Browser session command failed." });
235
+ emit.setExitCode(1);
236
+ }
237
+ }
238
+
239
+ function parseIndex(value: string): number {
240
+ const parsed = Number(value);
241
+ if (!Number.isSafeInteger(parsed) || parsed < 0) {
242
+ throw new BrowserSessionError("invalid_index", "Tab index must be a non-negative integer.");
243
+ }
244
+ return parsed;
245
+ }