@zenalexa/unicli 0.213.3 → 0.215.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 (188) hide show
  1. package/AGENTS.md +10 -10
  2. package/README.md +40 -27
  3. package/README.zh-CN.md +36 -36
  4. package/dist/adapters/_electron/desktop-shared.d.ts +12 -0
  5. package/dist/adapters/_electron/desktop-shared.d.ts.map +1 -0
  6. package/dist/adapters/_electron/desktop-shared.js +312 -0
  7. package/dist/adapters/_electron/desktop-shared.js.map +1 -0
  8. package/dist/adapters/electron-desktop/electron-desktop.d.ts +9 -0
  9. package/dist/adapters/electron-desktop/electron-desktop.d.ts.map +1 -0
  10. package/dist/adapters/electron-desktop/electron-desktop.js +56 -0
  11. package/dist/adapters/electron-desktop/electron-desktop.js.map +1 -0
  12. package/dist/browser/adapter-authoring.d.ts +20 -0
  13. package/dist/browser/adapter-authoring.d.ts.map +1 -0
  14. package/dist/browser/adapter-authoring.js +68 -0
  15. package/dist/browser/adapter-authoring.js.map +1 -0
  16. package/dist/browser/analyze.d.ts +54 -0
  17. package/dist/browser/analyze.d.ts.map +1 -0
  18. package/dist/browser/analyze.js +205 -0
  19. package/dist/browser/analyze.js.map +1 -0
  20. package/dist/browser/bridge.d.ts +1 -1
  21. package/dist/browser/bridge.d.ts.map +1 -1
  22. package/dist/browser/bridge.js +2 -0
  23. package/dist/browser/bridge.js.map +1 -1
  24. package/dist/browser/daemon-client.d.ts.map +1 -1
  25. package/dist/browser/daemon-client.js +12 -1
  26. package/dist/browser/daemon-client.js.map +1 -1
  27. package/dist/browser/network-cache.d.ts +58 -0
  28. package/dist/browser/network-cache.d.ts.map +1 -0
  29. package/dist/browser/network-cache.js +146 -0
  30. package/dist/browser/network-cache.js.map +1 -0
  31. package/dist/browser/page.d.ts +1 -1
  32. package/dist/browser/page.d.ts.map +1 -1
  33. package/dist/browser/page.js +2 -1
  34. package/dist/browser/page.js.map +1 -1
  35. package/dist/browser/protocol.d.ts +6 -2
  36. package/dist/browser/protocol.d.ts.map +1 -1
  37. package/dist/browser/site-memory.d.ts +57 -0
  38. package/dist/browser/site-memory.d.ts.map +1 -0
  39. package/dist/browser/site-memory.js +127 -0
  40. package/dist/browser/site-memory.js.map +1 -0
  41. package/dist/browser/verify-fixture.d.ts +30 -0
  42. package/dist/browser/verify-fixture.d.ts.map +1 -0
  43. package/dist/browser/verify-fixture.js +168 -0
  44. package/dist/browser/verify-fixture.js.map +1 -0
  45. package/dist/browser/workspace.d.ts +8 -0
  46. package/dist/browser/workspace.d.ts.map +1 -0
  47. package/dist/browser/workspace.js +15 -0
  48. package/dist/browser/workspace.js.map +1 -0
  49. package/dist/commands/browser-adapter-authoring.d.ts +3 -0
  50. package/dist/commands/browser-adapter-authoring.d.ts.map +1 -0
  51. package/dist/commands/browser-adapter-authoring.js +206 -0
  52. package/dist/commands/browser-adapter-authoring.js.map +1 -0
  53. package/dist/commands/browser-authoring-operator.d.ts +3 -0
  54. package/dist/commands/browser-authoring-operator.d.ts.map +1 -0
  55. package/dist/commands/browser-authoring-operator.js +110 -0
  56. package/dist/commands/browser-authoring-operator.js.map +1 -0
  57. package/dist/commands/browser-operator-runtime.d.ts +38 -0
  58. package/dist/commands/browser-operator-runtime.d.ts.map +1 -0
  59. package/dist/commands/browser-operator-runtime.js +281 -0
  60. package/dist/commands/browser-operator-runtime.js.map +1 -0
  61. package/dist/commands/browser-operator.d.ts +6 -0
  62. package/dist/commands/browser-operator.d.ts.map +1 -0
  63. package/dist/commands/browser-operator.js +374 -0
  64. package/dist/commands/browser-operator.js.map +1 -0
  65. package/dist/commands/browser.d.ts.map +1 -1
  66. package/dist/commands/browser.js +72 -1
  67. package/dist/commands/browser.js.map +1 -1
  68. package/dist/commands/daemon.d.ts.map +1 -1
  69. package/dist/commands/daemon.js +40 -4
  70. package/dist/commands/daemon.js.map +1 -1
  71. package/dist/commands/explore.d.ts.map +1 -1
  72. package/dist/commands/explore.js +4 -1
  73. package/dist/commands/explore.js.map +1 -1
  74. package/dist/commands/generate.d.ts.map +1 -1
  75. package/dist/commands/generate.js +4 -1
  76. package/dist/commands/generate.js.map +1 -1
  77. package/dist/commands/mcp.d.ts.map +1 -1
  78. package/dist/commands/mcp.js +14 -14
  79. package/dist/commands/mcp.js.map +1 -1
  80. package/dist/commands/migrate-schema.d.ts.map +1 -1
  81. package/dist/commands/migrate-schema.js +10 -0
  82. package/dist/commands/migrate-schema.js.map +1 -1
  83. package/dist/commands/operate.d.ts +4 -3
  84. package/dist/commands/operate.d.ts.map +1 -1
  85. package/dist/commands/operate.js +8 -483
  86. package/dist/commands/operate.js.map +1 -1
  87. package/dist/commands/record.d.ts.map +1 -1
  88. package/dist/commands/record.js +2 -1
  89. package/dist/commands/record.js.map +1 -1
  90. package/dist/commands/search.js +1 -1
  91. package/dist/commands/search.js.map +1 -1
  92. package/dist/commands/skills.js +1 -1
  93. package/dist/commands/skills.js.map +1 -1
  94. package/dist/commands/synthesize.d.ts.map +1 -1
  95. package/dist/commands/synthesize.js +33 -3
  96. package/dist/commands/synthesize.js.map +1 -1
  97. package/dist/discovery/aliases.d.ts.map +1 -1
  98. package/dist/discovery/aliases.js +76 -1
  99. package/dist/discovery/aliases.js.map +1 -1
  100. package/dist/discovery/loader.d.ts.map +1 -1
  101. package/dist/discovery/loader.js +3 -1
  102. package/dist/discovery/loader.js.map +1 -1
  103. package/dist/electron-apps.d.ts +22 -0
  104. package/dist/electron-apps.d.ts.map +1 -1
  105. package/dist/electron-apps.js +324 -0
  106. package/dist/electron-apps.js.map +1 -1
  107. package/dist/engine/download.d.ts.map +1 -1
  108. package/dist/engine/download.js +12 -5
  109. package/dist/engine/download.js.map +1 -1
  110. package/dist/engine/repair/engine.js +2 -2
  111. package/dist/engine/repair/engine.js.map +1 -1
  112. package/dist/engine/steps/desktop-ax.d.ts +10 -0
  113. package/dist/engine/steps/desktop-ax.d.ts.map +1 -1
  114. package/dist/engine/steps/desktop-ax.js +20 -0
  115. package/dist/engine/steps/desktop-ax.js.map +1 -1
  116. package/dist/engine/steps/exec.d.ts +1 -1
  117. package/dist/engine/steps/exec.d.ts.map +1 -1
  118. package/dist/engine/steps/exec.js +20 -1
  119. package/dist/engine/steps/exec.js.map +1 -1
  120. package/dist/engine/steps/write-temp.js +1 -1
  121. package/dist/engine/steps/write-temp.js.map +1 -1
  122. package/dist/engine/template.d.ts.map +1 -1
  123. package/dist/engine/template.js +10 -2
  124. package/dist/engine/template.js.map +1 -1
  125. package/dist/manifest-compact.txt +8 -7
  126. package/dist/manifest-search.json +1 -1
  127. package/dist/manifest.json +2161 -171
  128. package/dist/mcp/http-transport.d.ts.map +1 -1
  129. package/dist/mcp/http-transport.js +3 -6
  130. package/dist/mcp/http-transport.js.map +1 -1
  131. package/dist/mcp/streamable-http/handle-post.js +1 -1
  132. package/dist/mcp/tools.d.ts +2 -1
  133. package/dist/mcp/tools.d.ts.map +1 -1
  134. package/dist/mcp/tools.js +8 -4
  135. package/dist/mcp/tools.js.map +1 -1
  136. package/dist/output/envelope.d.ts +1 -5
  137. package/dist/output/envelope.d.ts.map +1 -1
  138. package/dist/output/envelope.js +1 -5
  139. package/dist/output/envelope.js.map +1 -1
  140. package/dist/output/formatter.d.ts +2 -2
  141. package/dist/output/formatter.js +3 -3
  142. package/dist/output/formatter.js.map +1 -1
  143. package/dist/transport/adapters/cua.d.ts +9 -10
  144. package/dist/transport/adapters/cua.d.ts.map +1 -1
  145. package/dist/transport/adapters/cua.js +22 -24
  146. package/dist/transport/adapters/cua.js.map +1 -1
  147. package/dist/transport/adapters/desktop-atspi.d.ts +1 -1
  148. package/dist/transport/adapters/desktop-atspi.js +2 -2
  149. package/dist/transport/adapters/desktop-atspi.js.map +1 -1
  150. package/dist/transport/adapters/desktop-ax-background-click-swift.d.ts +11 -0
  151. package/dist/transport/adapters/desktop-ax-background-click-swift.d.ts.map +1 -0
  152. package/dist/transport/adapters/desktop-ax-background-click-swift.js +136 -0
  153. package/dist/transport/adapters/desktop-ax-background-click-swift.js.map +1 -0
  154. package/dist/transport/adapters/desktop-ax-background-click.d.ts +4 -0
  155. package/dist/transport/adapters/desktop-ax-background-click.d.ts.map +1 -0
  156. package/dist/transport/adapters/desktop-ax-background-click.js +70 -0
  157. package/dist/transport/adapters/desktop-ax-background-click.js.map +1 -0
  158. package/dist/transport/adapters/desktop-ax-swift.d.ts +56 -0
  159. package/dist/transport/adapters/desktop-ax-swift.d.ts.map +1 -0
  160. package/dist/transport/adapters/desktop-ax-swift.js +458 -0
  161. package/dist/transport/adapters/desktop-ax-swift.js.map +1 -0
  162. package/dist/transport/adapters/desktop-ax.d.ts +14 -0
  163. package/dist/transport/adapters/desktop-ax.d.ts.map +1 -1
  164. package/dist/transport/adapters/desktop-ax.js +262 -32
  165. package/dist/transport/adapters/desktop-ax.js.map +1 -1
  166. package/dist/transport/adapters/desktop-uia.d.ts +1 -1
  167. package/dist/transport/adapters/desktop-uia.js +2 -2
  168. package/dist/transport/adapters/desktop-uia.js.map +1 -1
  169. package/dist/transport/adapters/http.js +1 -1
  170. package/dist/transport/adapters/http.js.map +1 -1
  171. package/dist/transport/capability.d.ts +1 -1
  172. package/dist/transport/capability.d.ts.map +1 -1
  173. package/dist/transport/capability.js +6 -1
  174. package/dist/transport/capability.js.map +1 -1
  175. package/package.json +2 -2
  176. package/src/adapters/_electron/desktop-shared.ts +383 -0
  177. package/src/adapters/docker/build.yaml +1 -1
  178. package/src/adapters/docker/images.yaml +1 -1
  179. package/src/adapters/docker/logs.yaml +1 -1
  180. package/src/adapters/docker/networks.yaml +1 -1
  181. package/src/adapters/docker/ps.yaml +1 -1
  182. package/src/adapters/docker/run.yaml +1 -1
  183. package/src/adapters/docker/volumes.yaml +1 -1
  184. package/src/adapters/electron-desktop/electron-desktop.ts +57 -0
  185. package/src/adapters/macos/caffeinate.yaml +2 -2
  186. package/src/adapters/macos/processes.yaml +3 -3
  187. package/src/adapters/macos/screen-recording.yaml +2 -2
  188. package/src/adapters/substack/trending.yaml +2 -1
@@ -0,0 +1,281 @@
1
+ import { homedir } from "node:os";
2
+ import { isAbsolute, relative, resolve } from "node:path";
3
+ import { BrowserBridge } from "../browser/bridge.js";
4
+ import { resolveBrowserWorkspace } from "../browser/workspace.js";
5
+ import { generateInterceptorJs, generateReadInterceptedJs, } from "../engine/interceptor.js";
6
+ import { buildSensitivePathDenial, isSensitivePathRealpath, } from "../permissions/sensitive-paths.js";
7
+ import { detectFormat, format } from "../output/formatter.js";
8
+ import { makeCtx } from "../output/envelope.js";
9
+ import { errorTypeToCode, mapErrorToExitCode } from "../output/error-map.js";
10
+ export function validateRef(ref) {
11
+ if (!/^\d+$/.test(ref)) {
12
+ throw new Error(`Invalid ref "${ref}". Expected a number from the state output.`);
13
+ }
14
+ return ref;
15
+ }
16
+ function getRootOpts(root) {
17
+ return root.opts();
18
+ }
19
+ export function resolveWorkspace(root, namespace) {
20
+ const opts = getRootOpts(root);
21
+ return resolveBrowserWorkspace(namespace, {
22
+ workspace: opts.workspace,
23
+ isolated: opts.isolated,
24
+ sharedSession: opts.sharedSession,
25
+ });
26
+ }
27
+ export async function withBrowserOperatorEnv(root, fn) {
28
+ const opts = getRootOpts(root);
29
+ const prevPort = process.env.UNICLI_DAEMON_PORT;
30
+ const prevFocus = process.env.UNICLI_WINDOW_FOCUSED;
31
+ if (opts.daemonPort) {
32
+ process.env.UNICLI_DAEMON_PORT = opts.daemonPort;
33
+ }
34
+ if (opts.focus) {
35
+ process.env.UNICLI_WINDOW_FOCUSED = "1";
36
+ }
37
+ else if (opts.background) {
38
+ process.env.UNICLI_WINDOW_FOCUSED = "0";
39
+ }
40
+ try {
41
+ return await fn();
42
+ }
43
+ finally {
44
+ if (opts.daemonPort) {
45
+ if (prevPort === undefined)
46
+ delete process.env.UNICLI_DAEMON_PORT;
47
+ else
48
+ process.env.UNICLI_DAEMON_PORT = prevPort;
49
+ }
50
+ if (opts.focus || opts.background) {
51
+ if (prevFocus === undefined)
52
+ delete process.env.UNICLI_WINDOW_FOCUSED;
53
+ else
54
+ process.env.UNICLI_WINDOW_FOCUSED = prevFocus;
55
+ }
56
+ }
57
+ }
58
+ export async function getOperatorPage(root, namespace) {
59
+ const bridge = new BrowserBridge();
60
+ const page = await bridge.connect({
61
+ timeout: 30_000,
62
+ workspace: resolveWorkspace(root, namespace),
63
+ });
64
+ return page;
65
+ }
66
+ export async function operatorAction(program, root, namespace, name, fn) {
67
+ const startedAt = Date.now();
68
+ const ctx = makeCtx(`${namespace}.${name.split(" ").join("_")}`, startedAt);
69
+ const fmt = detectFormat(program.opts().format);
70
+ try {
71
+ const result = await withBrowserOperatorEnv(root, fn);
72
+ let data;
73
+ if (result === undefined || result === null) {
74
+ data = { ok: true };
75
+ }
76
+ else if (typeof result === "string") {
77
+ data = { value: result };
78
+ }
79
+ else if (Array.isArray(result)) {
80
+ data = result;
81
+ }
82
+ else if (typeof result === "object") {
83
+ data = result;
84
+ }
85
+ else {
86
+ data = { value: String(result) };
87
+ }
88
+ ctx.duration_ms = Date.now() - startedAt;
89
+ console.log(format(data, undefined, fmt, ctx));
90
+ }
91
+ catch (err) {
92
+ const message = err instanceof Error ? err.message : String(err);
93
+ const tagged = err;
94
+ ctx.error = {
95
+ code: tagged.code ?? errorTypeToCode(err),
96
+ message,
97
+ ...(tagged.suggestion ? { suggestion: tagged.suggestion } : {}),
98
+ retryable: /timeout|ETIMEDOUT|ECONNREFUSED|ECONNRESET|daemon failed/i.test(message),
99
+ };
100
+ ctx.duration_ms = Date.now() - startedAt;
101
+ console.error(format(null, undefined, fmt, ctx));
102
+ process.exitCode = mapErrorToExitCode(err);
103
+ }
104
+ }
105
+ export async function ensureNetworkCapture(page) {
106
+ const pageAny = page;
107
+ let captureStarted = false;
108
+ if (typeof pageAny.startNetworkCapture === "function") {
109
+ captureStarted = (await pageAny.startNetworkCapture()) !== false;
110
+ }
111
+ if (!captureStarted) {
112
+ try {
113
+ await page.evaluate(generateInterceptorJs("", { captureText: true }));
114
+ }
115
+ catch {
116
+ // Best effort only.
117
+ }
118
+ }
119
+ }
120
+ export async function readNetworkEntries(page) {
121
+ const pageAny = page;
122
+ if (typeof pageAny.readNetworkCapture === "function") {
123
+ const rawEntries = (await pageAny.readNetworkCapture()) ?? [];
124
+ if (rawEntries.length > 0) {
125
+ return {
126
+ raw: rawEntries,
127
+ normalized: rawEntries.map((entry) => ({
128
+ url: entry.url,
129
+ method: entry.method,
130
+ status: entry.status,
131
+ contentType: entry.contentType,
132
+ bodySize: entry.size,
133
+ ...(entry.responseBody ? { body: entry.responseBody } : {}),
134
+ })),
135
+ };
136
+ }
137
+ }
138
+ try {
139
+ const raw = (await page.evaluate(generateReadInterceptedJs()));
140
+ const parsed = JSON.parse(raw);
141
+ if (parsed.length > 0) {
142
+ return {
143
+ raw: parsed,
144
+ normalized: parsed.map((entry) => ({
145
+ url: entry.url,
146
+ method: entry.method ?? "GET",
147
+ status: entry.status ?? 200,
148
+ contentType: entry.type === "text" ? "text/plain" : "application/json",
149
+ bodySize: entry.data == null ? 0 : JSON.stringify(entry.data).length,
150
+ ...(entry.data !== undefined ? { body: entry.data } : {}),
151
+ })),
152
+ };
153
+ }
154
+ }
155
+ catch {
156
+ // Interceptor not installed or buffer malformed.
157
+ }
158
+ const requests = await page.networkRequests();
159
+ return {
160
+ raw: requests,
161
+ normalized: requests.map((request) => ({
162
+ url: request.url,
163
+ method: request.method,
164
+ status: request.status,
165
+ contentType: request.type,
166
+ bodySize: request.size,
167
+ })),
168
+ };
169
+ }
170
+ export async function readFrames(page) {
171
+ const raw = (await page.sendCDP("Page.getFrameTree"));
172
+ const frames = [];
173
+ function walk(tree, includeSelf) {
174
+ if (!tree)
175
+ return;
176
+ if (includeSelf && tree.frame?.id) {
177
+ frames.push({
178
+ index: frames.length,
179
+ frameId: tree.frame.id,
180
+ parentFrameId: tree.frame.parentId,
181
+ url: tree.frame.url ?? "",
182
+ });
183
+ }
184
+ for (const child of tree.childFrames ?? []) {
185
+ walk(child, true);
186
+ }
187
+ }
188
+ walk(raw.frameTree ?? null, false);
189
+ return frames;
190
+ }
191
+ export function buildFindJs(selector, limit, textMax) {
192
+ const selectorJson = JSON.stringify(selector);
193
+ return `(() => {
194
+ const matches = Array.from(document.querySelectorAll(${selectorJson})).slice(0, ${String(limit)});
195
+ let maxRef = 0;
196
+ for (const node of document.querySelectorAll('[data-unicli-ref]')) {
197
+ const value = parseInt(node.getAttribute('data-unicli-ref') || '0', 10);
198
+ if (!Number.isNaN(value) && value > maxRef) maxRef = value;
199
+ }
200
+ return matches.map((el, index) => {
201
+ let ref = el.getAttribute('data-unicli-ref');
202
+ if (!ref) {
203
+ ref = String(++maxRef);
204
+ el.setAttribute('data-unicli-ref', ref);
205
+ }
206
+ const style = window.getComputedStyle(el);
207
+ const rect = el.getBoundingClientRect();
208
+ const visible =
209
+ style.display !== 'none' &&
210
+ style.visibility !== 'hidden' &&
211
+ rect.width > 0 &&
212
+ rect.height > 0;
213
+ const attrs = {};
214
+ for (const name of ['id', 'name', 'type', 'href', 'src', 'placeholder', 'role', 'aria-label', 'data-testid']) {
215
+ const value = el.getAttribute(name);
216
+ if (value) attrs[name] = value;
217
+ }
218
+ return {
219
+ nth: index,
220
+ ref,
221
+ tag: el.tagName.toLowerCase(),
222
+ role: el.getAttribute('role') || el.tagName.toLowerCase(),
223
+ text: (el.innerText || el.textContent || '').trim().slice(0, ${String(textMax)}),
224
+ visible,
225
+ attrs,
226
+ };
227
+ });
228
+ })()`;
229
+ }
230
+ export function buildExtractJs(selector) {
231
+ const selectorJson = selector && selector.trim() ? JSON.stringify(selector.trim()) : "null";
232
+ return `(() => {
233
+ const picks = [];
234
+ const explicit = ${selectorJson};
235
+ if (explicit) picks.push(explicit);
236
+ picks.push('main', 'article', '[role="main"]', 'body');
237
+ let target = null;
238
+ let resolved = 'body';
239
+ for (const pick of picks) {
240
+ const node = document.querySelector(pick);
241
+ if (node) {
242
+ target = node;
243
+ resolved = pick;
244
+ break;
245
+ }
246
+ }
247
+ const text = (target?.innerText || document.body?.innerText || '').replace(/\\u00a0/g, ' ').trim();
248
+ return {
249
+ selector: resolved,
250
+ title: document.title || '',
251
+ url: location.href,
252
+ content: text,
253
+ };
254
+ })()`;
255
+ }
256
+ function isSameOrDescendantPath(parent, candidate) {
257
+ const rel = relative(parent, candidate);
258
+ return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
259
+ }
260
+ export function resolveAllowedUploadPath(filePath) {
261
+ const absolutePath = resolve(filePath);
262
+ if (isSensitivePathRealpath(absolutePath)) {
263
+ const denial = buildSensitivePathDenial(absolutePath);
264
+ const err = new Error("upload blocked by sensitive-path guard");
265
+ err.code = "permission_denied";
266
+ err.suggestion = denial.hint;
267
+ throw err;
268
+ }
269
+ const cwd = process.cwd();
270
+ const home = homedir();
271
+ if (!isSameOrDescendantPath(cwd, absolutePath) &&
272
+ !isSameOrDescendantPath(home, absolutePath)) {
273
+ const err = new Error(`upload blocked: path ${absolutePath} is outside workspace and home directory`);
274
+ err.code = "permission_denied";
275
+ err.suggestion =
276
+ "Copy the file under the current working directory or $HOME before uploading.";
277
+ throw err;
278
+ }
279
+ return absolutePath;
280
+ }
281
+ //# sourceMappingURL=browser-operator-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-operator-runtime.js","sourceRoot":"","sources":["../../src/commands/browser-operator-runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { Command } from "commander";
2
+ import { withBrowserOperatorEnv } from "./browser-operator-runtime.js";
3
+ export { withBrowserOperatorEnv };
4
+ export declare function applyBrowserOperatorRootOptions(command: Command): void;
5
+ export declare function registerBrowserOperatorSubcommands(root: Command, program: Command, namespace: "browser" | "operate"): void;
6
+ //# sourceMappingURL=browser-operator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-operator.d.ts","sourceRoot":"","sources":["../../src/commands/browser-operator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,OAAO,EAUL,sBAAsB,EACvB,MAAM,+BAA+B,CAAC;AAIvC,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAElC,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoBtE;AAED,wBAAgB,kCAAkC,CAChD,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,SAAS,GAAG,SAAS,GAC/B,IAAI,CAodN"}
@@ -0,0 +1,374 @@
1
+ import { appendFileSync, mkdirSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname as pathDirname, join } from "node:path";
4
+ import chalk from "chalk";
5
+ import { FINGERPRINT_PERSIST_JS, verifyRef, } from "../browser/snapshot-identity.js";
6
+ import { rankCandidates } from "../browser/observe.js";
7
+ import { buildExtractJs, buildFindJs, ensureNetworkCapture, getOperatorPage, operatorAction, readFrames, resolveAllowedUploadPath, resolveWorkspace, validateRef, withBrowserOperatorEnv, } from "./browser-operator-runtime.js";
8
+ import { sendCommand } from "../browser/daemon-client.js";
9
+ import { registerBrowserAuthoringSubcommands } from "./browser-authoring-operator.js";
10
+ export { withBrowserOperatorEnv };
11
+ export function applyBrowserOperatorRootOptions(command) {
12
+ command
13
+ .option("--workspace <name>", "Reuse a named automation workspace instead of the default shared session")
14
+ .option("--isolated", "Use a unique per-command workspace to avoid cross-command interference")
15
+ .option("--shared-session", "Force the default shared browser workspace")
16
+ .option("--daemon-port <port>", "Route through a specific daemon port for multi-profile setups")
17
+ .option("--focus", "Allow the automation window to take focus")
18
+ .option("--background", "Prefer background operation and avoid focus-stealing where possible");
19
+ }
20
+ export function registerBrowserOperatorSubcommands(root, program, namespace) {
21
+ root
22
+ .command("open <url>")
23
+ .description("Navigate to URL in daemon browser")
24
+ .action((url) => operatorAction(program, root, namespace, "open", async () => {
25
+ const page = await getOperatorPage(root, namespace);
26
+ await ensureNetworkCapture(page);
27
+ await page.goto(url, { settleMs: 2000 });
28
+ const title = await page.title();
29
+ return {
30
+ ok: true,
31
+ url,
32
+ title,
33
+ workspace: resolveWorkspace(root, namespace),
34
+ };
35
+ }));
36
+ root
37
+ .command("back")
38
+ .description("Navigate back in history")
39
+ .action(() => operatorAction(program, root, namespace, "back", async () => {
40
+ const page = await getOperatorPage(root, namespace);
41
+ await page.evaluate("history.back()");
42
+ await page.wait(2);
43
+ return { ok: true, url: await page.url() };
44
+ }));
45
+ root
46
+ .command("state")
47
+ .description("Get DOM accessibility tree snapshot")
48
+ .option("--interactive", "only show interactive elements")
49
+ .option("--compact", "omit decorative nodes")
50
+ .action((opts) => operatorAction(program, root, namespace, "state", async () => {
51
+ const page = await getOperatorPage(root, namespace);
52
+ const url = await page.url();
53
+ const snapshot = await page.snapshot({
54
+ interactive: opts.interactive,
55
+ compact: opts.compact,
56
+ });
57
+ console.error(chalk.dim(`URL: ${url}`));
58
+ return { url, snapshot };
59
+ }));
60
+ root
61
+ .command("screenshot [path]")
62
+ .description("Capture page screenshot")
63
+ .option("--full-page", "capture full scrollable page")
64
+ .action((path, opts) => operatorAction(program, root, namespace, "screenshot", async () => {
65
+ const page = await getOperatorPage(root, namespace);
66
+ const buf = await page.screenshot({
67
+ fullPage: opts.fullPage,
68
+ path: path ?? undefined,
69
+ });
70
+ if (path) {
71
+ return { ok: true, path, size: buf.length };
72
+ }
73
+ return buf.toString("base64");
74
+ }));
75
+ root
76
+ .command("click <ref>")
77
+ .description("Click element by ref number from state")
78
+ .action((ref) => operatorAction(program, root, namespace, "click", async () => {
79
+ validateRef(ref);
80
+ const page = await getOperatorPage(root, namespace);
81
+ const selector = `[data-unicli-ref="${ref}"]`;
82
+ await verifyRef(page, selector);
83
+ await page.click(selector);
84
+ return { ok: true, clicked: ref };
85
+ }));
86
+ root
87
+ .command("type <ref> <text>")
88
+ .description("Type text into element by ref number")
89
+ .action((ref, text) => operatorAction(program, root, namespace, "type", async () => {
90
+ validateRef(ref);
91
+ const page = await getOperatorPage(root, namespace);
92
+ const selector = `[data-unicli-ref="${ref}"]`;
93
+ await verifyRef(page, selector);
94
+ await page.click(selector);
95
+ await page.wait(0.3);
96
+ await page.insertText(text);
97
+ return { ok: true, ref, text };
98
+ }));
99
+ root
100
+ .command("keys <key>")
101
+ .description("Press keyboard key (e.g., Enter, Escape, Control+a)")
102
+ .action((key) => operatorAction(program, root, namespace, "keys", async () => {
103
+ const page = await getOperatorPage(root, namespace);
104
+ if (key.includes("+")) {
105
+ const parts = key.split("+");
106
+ const actualKey = parts.pop();
107
+ await page.press(actualKey, parts.map((modifier) => modifier.toLowerCase()));
108
+ }
109
+ else {
110
+ await page.press(key);
111
+ }
112
+ return { ok: true, key };
113
+ }));
114
+ root
115
+ .command("scroll [direction]")
116
+ .description("Scroll page (down, up, bottom, top)")
117
+ .option("--auto", "auto-scroll to bottom")
118
+ .option("--max <n>", "max scroll iterations for auto", "10")
119
+ .action((direction, opts) => operatorAction(program, root, namespace, "scroll", async () => {
120
+ const page = await getOperatorPage(root, namespace);
121
+ if (opts.auto) {
122
+ await page.autoScroll({
123
+ maxScrolls: parseInt(opts.max, 10),
124
+ delay: 1000,
125
+ });
126
+ }
127
+ else {
128
+ await page.scroll((direction ?? "down"));
129
+ }
130
+ return { ok: true, direction: direction ?? "down" };
131
+ }));
132
+ const get = root
133
+ .command("get")
134
+ .description("Get page data (title, url, text, value, html, attributes)");
135
+ get
136
+ .command("title")
137
+ .description("Get page title")
138
+ .action(() => operatorAction(program, root, namespace, "get title", async () => {
139
+ const page = await getOperatorPage(root, namespace);
140
+ return await page.title();
141
+ }));
142
+ get
143
+ .command("url")
144
+ .description("Get current URL")
145
+ .action(() => operatorAction(program, root, namespace, "get url", async () => {
146
+ const page = await getOperatorPage(root, namespace);
147
+ return await page.url();
148
+ }));
149
+ get
150
+ .command("text <ref>")
151
+ .description("Get text content of element by ref")
152
+ .action((ref) => operatorAction(program, root, namespace, "get text", async () => {
153
+ validateRef(ref);
154
+ const page = await getOperatorPage(root, namespace);
155
+ return await page.evaluate(`document.querySelector('[data-unicli-ref="${ref}"]')?.textContent?.trim() ?? null`);
156
+ }));
157
+ get
158
+ .command("value <ref>")
159
+ .description("Get value of input element by ref")
160
+ .action((ref) => operatorAction(program, root, namespace, "get value", async () => {
161
+ validateRef(ref);
162
+ const page = await getOperatorPage(root, namespace);
163
+ return await page.evaluate(`document.querySelector('[data-unicli-ref="${ref}"]')?.value ?? null`);
164
+ }));
165
+ get
166
+ .command("html [selector]")
167
+ .description("Get outerHTML of element (or full page)")
168
+ .action((selector) => operatorAction(program, root, namespace, "get html", async () => {
169
+ const page = await getOperatorPage(root, namespace);
170
+ if (selector) {
171
+ const selectorStr = JSON.stringify(selector);
172
+ return await page.evaluate(`document.querySelector(${selectorStr})?.outerHTML?.slice(0, 50000) ?? null`);
173
+ }
174
+ return await page.evaluate("document.documentElement.outerHTML.slice(0, 50000)");
175
+ }));
176
+ get
177
+ .command("attributes <ref>")
178
+ .description("Get all attributes of element by ref")
179
+ .action((ref) => operatorAction(program, root, namespace, "get attributes", async () => {
180
+ validateRef(ref);
181
+ const page = await getOperatorPage(root, namespace);
182
+ return await page.evaluate(`(() => { const el = document.querySelector('[data-unicli-ref="${ref}"]'); if (!el) return null; const attrs = {}; for (const a of el.attributes) attrs[a.name] = a.value; return attrs; })()`);
183
+ }));
184
+ root
185
+ .command("wait <type> [value]")
186
+ .description("Wait for condition (time <ms>, selector <sel>, text <str>)")
187
+ .option("--timeout <ms>", "timeout in ms", "10000")
188
+ .action((type, value, opts) => operatorAction(program, root, namespace, "wait", async () => {
189
+ const page = await getOperatorPage(root, namespace);
190
+ const timeout = parseInt(opts.timeout, 10);
191
+ switch (type) {
192
+ case "time":
193
+ await page.wait(parseInt(value ?? "1000", 10) / 1000);
194
+ break;
195
+ case "selector":
196
+ if (!value)
197
+ throw new Error("selector value required");
198
+ await page.waitForSelector(value, timeout);
199
+ break;
200
+ case "text": {
201
+ if (!value)
202
+ throw new Error("text value required");
203
+ const deadline = Date.now() + timeout;
204
+ const valueStr = JSON.stringify(value);
205
+ while (Date.now() < deadline) {
206
+ const found = await page.evaluate(`document.body.innerText.includes(${valueStr})`);
207
+ if (found)
208
+ return { ok: true, found: true };
209
+ await new Promise((resolve) => setTimeout(resolve, 200));
210
+ }
211
+ throw new Error(`Text "${value}" not found within ${String(timeout)}ms`);
212
+ }
213
+ default:
214
+ throw new Error(`Unknown wait type: ${type}. Use: time, selector, text`);
215
+ }
216
+ return { ok: true };
217
+ }));
218
+ root
219
+ .command("eval <js>")
220
+ .description("Execute JavaScript in page context")
221
+ .action((js) => operatorAction(program, root, namespace, "eval", async () => {
222
+ const page = await getOperatorPage(root, namespace);
223
+ return await page.evaluate(js);
224
+ }));
225
+ registerBrowserAuthoringSubcommands(root, program, namespace);
226
+ root
227
+ .command("select <ref> <option>")
228
+ .description("Select option in dropdown by ref")
229
+ .action((ref, option) => operatorAction(program, root, namespace, "select", async () => {
230
+ validateRef(ref);
231
+ const page = await getOperatorPage(root, namespace);
232
+ const optionStr = JSON.stringify(option);
233
+ await page.evaluate(`(() => {
234
+ const el = document.querySelector('[data-unicli-ref="${ref}"]');
235
+ if (!el || el.tagName !== 'SELECT') throw new Error('Not a <select> element');
236
+ el.value = ${optionStr};
237
+ el.dispatchEvent(new Event('change', { bubbles: true }));
238
+ })()`);
239
+ return { ok: true, ref, option };
240
+ }));
241
+ root
242
+ .command("upload <ref> <path>")
243
+ .description("Upload file to file input element by ref number")
244
+ .action((ref, filePath) => operatorAction(program, root, namespace, "upload", async () => {
245
+ validateRef(ref);
246
+ const selector = `[data-unicli-ref="${ref}"]`;
247
+ const absolutePath = resolveAllowedUploadPath(filePath);
248
+ const page = await getOperatorPage(root, namespace);
249
+ await page.setFileInput(selector, [absolutePath]);
250
+ return { ok: true, ref, path: absolutePath };
251
+ }));
252
+ root
253
+ .command("hover <ref>")
254
+ .description("Hover over element by ref number")
255
+ .action((ref) => operatorAction(program, root, namespace, "hover", async () => {
256
+ validateRef(ref);
257
+ const selector = `[data-unicli-ref="${ref}"]`;
258
+ const selectorJson = JSON.stringify(selector);
259
+ const page = await getOperatorPage(root, namespace);
260
+ await page.evaluate(`(() => {
261
+ const el = document.querySelector(${selectorJson});
262
+ if (!el) throw new Error('Element not found: ' + ${selectorJson});
263
+ el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
264
+ el.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
265
+ })()`);
266
+ return { ok: true, ref };
267
+ }));
268
+ root
269
+ .command("observe <query>")
270
+ .description("Preview ranked candidate actions for a natural-language goal")
271
+ .option("--top-k <n>", "Number of candidates to return", "5")
272
+ .option("--cache <path>", "Cache file (default ~/.unicli/observe-cache.jsonl)")
273
+ .action((query, opts) => operatorAction(program, root, namespace, "observe", async () => {
274
+ const page = await getOperatorPage(root, namespace);
275
+ const rawSnapshot = await page.snapshot({
276
+ interactive: true,
277
+ raw: true,
278
+ });
279
+ let parsed = { refs: [] };
280
+ if (typeof rawSnapshot === "string") {
281
+ try {
282
+ parsed = JSON.parse(rawSnapshot);
283
+ }
284
+ catch {
285
+ // Ignore malformed raw snapshot payloads.
286
+ }
287
+ }
288
+ else {
289
+ parsed = rawSnapshot;
290
+ }
291
+ const refs = Array.isArray(parsed.refs) ? parsed.refs : [];
292
+ const topK = parseInt(opts.topK ?? "5", 10) || 5;
293
+ const candidates = rankCandidates(refs, query, topK);
294
+ const cachePath = opts.cache ?? join(homedir(), ".unicli", "observe-cache.jsonl");
295
+ try {
296
+ mkdirSync(pathDirname(cachePath), { recursive: true });
297
+ appendFileSync(cachePath, JSON.stringify({
298
+ ts: new Date().toISOString(),
299
+ url: await page.url(),
300
+ query,
301
+ candidates,
302
+ }) + "\n", "utf-8");
303
+ }
304
+ catch {
305
+ // Cache failures are non-fatal.
306
+ }
307
+ return { query, candidates };
308
+ }));
309
+ root
310
+ .command("find")
311
+ .description("Find elements by CSS selector and allocate refs on demand")
312
+ .requiredOption("--css <selector>", "CSS selector to query")
313
+ .option("--limit <n>", "Maximum matches to return", "20")
314
+ .option("--text-max <n>", "Maximum text length per row", "120")
315
+ .action((opts) => operatorAction(program, root, namespace, "find", async () => {
316
+ const page = await getOperatorPage(root, namespace);
317
+ const results = (await page.evaluate(buildFindJs(opts.css, parseInt(opts.limit, 10) || 20, parseInt(opts.textMax, 10) || 120)));
318
+ try {
319
+ await page.evaluate(FINGERPRINT_PERSIST_JS);
320
+ }
321
+ catch {
322
+ // Best-effort only.
323
+ }
324
+ return results;
325
+ }));
326
+ root
327
+ .command("frames")
328
+ .description("List iframe frame tree entries for the current page")
329
+ .action(() => operatorAction(program, root, namespace, "frames", async () => {
330
+ const page = await getOperatorPage(root, namespace);
331
+ return await readFrames(page);
332
+ }));
333
+ root
334
+ .command("extract")
335
+ .description("Extract long-form page text with chunked pagination")
336
+ .option("--selector <css>", "Optional content root selector")
337
+ .option("--chunk-size <n>", "Maximum chars to return", "8000")
338
+ .option("--start <n>", "Start offset", "0")
339
+ .action((opts) => operatorAction(program, root, namespace, "extract", async () => {
340
+ const page = await getOperatorPage(root, namespace);
341
+ const result = (await page.evaluate(buildExtractJs(opts.selector)));
342
+ const start = Math.max(0, parseInt(opts.start, 10) || 0);
343
+ const chunkSize = Math.max(256, parseInt(opts.chunkSize, 10) || 8000);
344
+ const end = Math.min(result.content.length, start + chunkSize);
345
+ return {
346
+ url: result.url,
347
+ title: result.title,
348
+ selector: result.selector,
349
+ total_chars: result.content.length,
350
+ chunk_size: chunkSize,
351
+ start,
352
+ end,
353
+ next_start_char: end < result.content.length ? end : null,
354
+ content: result.content.slice(start, end),
355
+ };
356
+ }));
357
+ root
358
+ .command("tabs")
359
+ .description("List tabs for the current browser workspace")
360
+ .action(() => operatorAction(program, root, namespace, "tabs", async () => {
361
+ const workspace = resolveWorkspace(root, namespace);
362
+ const result = await sendCommand("tabs", { workspace });
363
+ return Array.isArray(result) ? result : [];
364
+ }));
365
+ root
366
+ .command("close")
367
+ .description("Close the automation browser window")
368
+ .action(() => operatorAction(program, root, namespace, "close", async () => {
369
+ const page = await getOperatorPage(root, namespace);
370
+ await page.closeWindow();
371
+ return { ok: true, workspace: resolveWorkspace(root, namespace) };
372
+ }));
373
+ }
374
+ //# sourceMappingURL=browser-operator.js.map