@canaryai/cli 0.2.8 → 0.2.9

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 (39) hide show
  1. package/README.md +77 -92
  2. package/dist/chunk-C2PGZRYK.js +167 -0
  3. package/dist/chunk-C2PGZRYK.js.map +1 -0
  4. package/dist/{chunk-K2OB72B6.js → chunk-LC7ZVXPH.js} +2 -2
  5. package/dist/{chunk-6WWHXWCS.js → chunk-QLFSJG5O.js} +33 -5
  6. package/dist/chunk-QLFSJG5O.js.map +1 -0
  7. package/dist/{chunk-FK3EZADZ.js → chunk-XGO62PO2.js} +1829 -868
  8. package/dist/chunk-XGO62PO2.js.map +1 -0
  9. package/dist/{debug-workflow-55G4Y6YT.js → debug-workflow-I3F36JBL.js} +57 -36
  10. package/dist/debug-workflow-I3F36JBL.js.map +1 -0
  11. package/dist/{docs-RPFT7ZJB.js → docs-REHST3YB.js} +2 -2
  12. package/dist/{feature-flag-2FDSKOVX.js → feature-flag-3HB5NTMY.js} +3 -2
  13. package/dist/{feature-flag-2FDSKOVX.js.map → feature-flag-3HB5NTMY.js.map} +1 -1
  14. package/dist/index.js +22 -9
  15. package/dist/index.js.map +1 -1
  16. package/dist/{issues-6ZDNDSD6.js → issues-YU57CHXS.js} +3 -2
  17. package/dist/{issues-6ZDNDSD6.js.map → issues-YU57CHXS.js.map} +1 -1
  18. package/dist/{knobs-MZRTYS3P.js → knobs-QJ4IBLCT.js} +3 -2
  19. package/dist/{knobs-MZRTYS3P.js.map → knobs-QJ4IBLCT.js.map} +1 -1
  20. package/dist/{local-browser-X7J27IGS.js → local-browser-MKTJ36KY.js} +3 -3
  21. package/dist/{mcp-4JVLADZL.js → mcp-ZOKM2AUE.js} +49 -238
  22. package/dist/mcp-ZOKM2AUE.js.map +1 -0
  23. package/dist/{record-4OX7HXWQ.js → record-TNDBT3NY.js} +130 -28
  24. package/dist/record-TNDBT3NY.js.map +1 -0
  25. package/dist/session-RNLKFS2Z.js +751 -0
  26. package/dist/session-RNLKFS2Z.js.map +1 -0
  27. package/dist/skill-CZ7SHI3P.js +156 -0
  28. package/dist/skill-CZ7SHI3P.js.map +1 -0
  29. package/dist/{src-I4EXB5OD.js → src-2WSMYBMJ.js} +18 -2
  30. package/package.json +1 -1
  31. package/dist/chunk-6WWHXWCS.js.map +0 -1
  32. package/dist/chunk-FK3EZADZ.js.map +0 -1
  33. package/dist/debug-workflow-55G4Y6YT.js.map +0 -1
  34. package/dist/mcp-4JVLADZL.js.map +0 -1
  35. package/dist/record-4OX7HXWQ.js.map +0 -1
  36. /package/dist/{chunk-K2OB72B6.js.map → chunk-LC7ZVXPH.js.map} +0 -0
  37. /package/dist/{docs-RPFT7ZJB.js.map → docs-REHST3YB.js.map} +0 -0
  38. /package/dist/{local-browser-X7J27IGS.js.map → local-browser-MKTJ36KY.js.map} +0 -0
  39. /package/dist/{src-I4EXB5OD.js.map → src-2WSMYBMJ.js.map} +0 -0
@@ -0,0 +1,751 @@
1
+ import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
2
+ import {
3
+ downloadStorageState,
4
+ fetchList
5
+ } from "./chunk-QLFSJG5O.js";
6
+ import {
7
+ callTool,
8
+ createSession,
9
+ deleteAllSessions,
10
+ deleteSession,
11
+ getSession,
12
+ listSessions,
13
+ resolveTargetSession
14
+ } from "./chunk-C2PGZRYK.js";
15
+ import {
16
+ getArgValue,
17
+ hasFlag,
18
+ resolveConfig
19
+ } from "./chunk-PWWQGYFG.js";
20
+ import {
21
+ BrowserToolExecutor,
22
+ PlaywrightClient,
23
+ dispatchBrowserTool
24
+ } from "./chunk-XGO62PO2.js";
25
+ import "./chunk-XAA5VQ5N.js";
26
+ import {
27
+ consoleLogger
28
+ } from "./chunk-P5Z2Y5VV.js";
29
+ import "./chunk-VKVL7WBN.js";
30
+
31
+ // src/session/index.ts
32
+ import process3 from "process";
33
+
34
+ // src/session/credentials.ts
35
+ import process2 from "process";
36
+ async function resolveCredential(argv, credentialArg) {
37
+ const { apiUrl, token } = await resolveConfig(argv);
38
+ const credentials = await fetchList(
39
+ apiUrl,
40
+ token,
41
+ "/org/credentials",
42
+ "credentials"
43
+ );
44
+ if (credentials.length === 0) {
45
+ console.error("No credentials found for this organization.");
46
+ return null;
47
+ }
48
+ let credential;
49
+ if (!credentialArg) {
50
+ console.log("Select a credential:");
51
+ for (let i = 0; i < credentials.length; i++) {
52
+ const c = credentials[i];
53
+ const propLabel = c.propertyName ? ` (${c.propertyName})` : "";
54
+ console.log(` ${i + 1}. ${c.name}${propLabel}`);
55
+ }
56
+ const readline = await import("readline");
57
+ const rl = readline.createInterface({ input: process2.stdin, output: process2.stdout });
58
+ const answer = await new Promise((resolve) => {
59
+ rl.question("> ", (ans) => {
60
+ rl.close();
61
+ resolve(ans.trim());
62
+ });
63
+ });
64
+ const idx = parseInt(answer, 10) - 1;
65
+ if (isNaN(idx) || idx < 0 || idx >= credentials.length) {
66
+ console.error("Invalid selection.");
67
+ return null;
68
+ }
69
+ credential = credentials[idx];
70
+ } else {
71
+ credential = credentials.find(
72
+ (c) => c.id === credentialArg || c.name.toLowerCase() === credentialArg.toLowerCase()
73
+ );
74
+ if (!credential) {
75
+ console.error(`Credential "${credentialArg}" not found.`);
76
+ console.error("Available credentials:");
77
+ for (const c of credentials) {
78
+ console.error(` - ${c.name} (${c.id})`);
79
+ }
80
+ return null;
81
+ }
82
+ }
83
+ console.log(`Fetching credential "${credential.name}"...`);
84
+ let storageStatePath;
85
+ if (credential.storageStateS3Key) {
86
+ storageStatePath = await downloadStorageState({
87
+ apiUrl,
88
+ token,
89
+ propertyId: credential.propertyId,
90
+ credentialId: credential.id,
91
+ prefix: "session-ss"
92
+ });
93
+ if (storageStatePath) {
94
+ console.log("Downloaded storage state.");
95
+ } else {
96
+ console.warn("Warning: Could not download storage state. Starting without auth.");
97
+ }
98
+ } else {
99
+ console.log("Credential has no storage state. Starting without auth.");
100
+ }
101
+ return {
102
+ storageStatePath,
103
+ credentialName: credential.name
104
+ };
105
+ }
106
+
107
+ // src/session/daemon.ts
108
+ import { createServer } from "http";
109
+ import fs from "fs/promises";
110
+ import path from "path";
111
+ import os from "os";
112
+ var PIDFILE_DIR = path.join(os.homedir(), ".config", "canary-cli");
113
+ var PIDFILE_PATH = path.join(PIDFILE_DIR, "daemon.json");
114
+ var IDLE_TIMEOUT_MS = 6e4;
115
+ var sessions = /* @__PURE__ */ new Map();
116
+ var nextId = 1;
117
+ var idleTimer = null;
118
+ function resetIdleTimer() {
119
+ if (idleTimer) clearTimeout(idleTimer);
120
+ if (sessions.size === 0) {
121
+ idleTimer = setTimeout(async () => {
122
+ if (sessions.size === 0) {
123
+ await removePidfile();
124
+ process.exit(0);
125
+ }
126
+ }, IDLE_TIMEOUT_MS);
127
+ }
128
+ }
129
+ function generateId() {
130
+ return `s${nextId++}`;
131
+ }
132
+ async function getSessionUrl(session) {
133
+ try {
134
+ return await session.client.getCurrentUrl();
135
+ } catch {
136
+ return void 0;
137
+ }
138
+ }
139
+ async function toSessionInfo(s) {
140
+ return {
141
+ id: s.id,
142
+ name: s.name,
143
+ mode: s.mode,
144
+ credentialName: s.credentialName,
145
+ url: await getSessionUrl(s),
146
+ startedAt: s.startedAt
147
+ };
148
+ }
149
+ async function readBody(req) {
150
+ const chunks = [];
151
+ for await (const chunk of req) {
152
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
153
+ }
154
+ return Buffer.concat(chunks).toString("utf-8");
155
+ }
156
+ function parseJson(body) {
157
+ if (!body.trim()) return {};
158
+ try {
159
+ return JSON.parse(body);
160
+ } catch {
161
+ return {};
162
+ }
163
+ }
164
+ function json(res, status, data) {
165
+ const body = JSON.stringify(data);
166
+ res.writeHead(status, {
167
+ "Content-Type": "application/json",
168
+ "Content-Length": Buffer.byteLength(body)
169
+ });
170
+ res.end(body);
171
+ }
172
+ function parseRoute(url) {
173
+ const [pathname] = url.split("?");
174
+ const segments = pathname.split("/").filter(Boolean);
175
+ return { path: segments, query: {} };
176
+ }
177
+ async function handleHealth(res) {
178
+ json(res, 200, { ok: true, sessions: sessions.size });
179
+ }
180
+ async function handleCreateSession(body, res) {
181
+ const params = parseJson(body);
182
+ const id = generateId();
183
+ const name = params.name ?? id;
184
+ const mode = params.headless ? "headless" : "headed";
185
+ try {
186
+ const client = new PlaywrightClient({ logger: consoleLogger });
187
+ await client.connect({
188
+ browserMode: mode,
189
+ storageStatePath: params.storageStatePath
190
+ });
191
+ if (params.url) {
192
+ await client.navigate(params.url);
193
+ }
194
+ const executor = new BrowserToolExecutor(client, {
195
+ autoSnapshotAfterAction: true,
196
+ includeScreenshotWithSnapshot: true,
197
+ clickValidation: true,
198
+ logger: consoleLogger
199
+ });
200
+ const session = {
201
+ id,
202
+ name,
203
+ mode,
204
+ credentialName: params.credentialName,
205
+ client,
206
+ executor,
207
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
208
+ };
209
+ sessions.set(id, session);
210
+ if (idleTimer) {
211
+ clearTimeout(idleTimer);
212
+ idleTimer = null;
213
+ }
214
+ const result = {
215
+ ok: true,
216
+ data: await toSessionInfo(session)
217
+ };
218
+ json(res, 201, result);
219
+ } catch (err) {
220
+ const result = {
221
+ ok: false,
222
+ error: err instanceof Error ? err.message : String(err)
223
+ };
224
+ json(res, 500, result);
225
+ }
226
+ }
227
+ async function handleListSessions(res) {
228
+ const list = await Promise.all(Array.from(sessions.values()).map(toSessionInfo));
229
+ json(res, 200, { ok: true, data: list });
230
+ }
231
+ async function handleGetSession(sessionId, res) {
232
+ const session = sessions.get(sessionId);
233
+ if (!session) {
234
+ json(res, 404, { ok: false, error: `Session "${sessionId}" not found` });
235
+ return;
236
+ }
237
+ json(res, 200, { ok: true, data: await toSessionInfo(session) });
238
+ }
239
+ async function handleDeleteSession(sessionId, res) {
240
+ const session = sessions.get(sessionId);
241
+ if (!session) {
242
+ json(res, 404, { ok: false, error: `Session "${sessionId}" not found` });
243
+ return;
244
+ }
245
+ try {
246
+ await session.client.disconnect();
247
+ } catch {
248
+ }
249
+ sessions.delete(sessionId);
250
+ json(res, 200, { ok: true });
251
+ resetIdleTimer();
252
+ }
253
+ async function handleDeleteAllSessions(res) {
254
+ for (const session of sessions.values()) {
255
+ try {
256
+ await session.client.disconnect();
257
+ } catch {
258
+ }
259
+ }
260
+ sessions.clear();
261
+ json(res, 200, { ok: true });
262
+ resetIdleTimer();
263
+ }
264
+ async function handleToolCall(sessionId, toolName, body, res) {
265
+ const session = sessions.get(sessionId);
266
+ if (!session) {
267
+ json(res, 404, { ok: false, error: `Session "${sessionId}" not found` });
268
+ return;
269
+ }
270
+ const args = parseJson(body);
271
+ const fullToolName = toolName.startsWith("browser_") ? toolName : `browser_${toolName}`;
272
+ try {
273
+ const result = await dispatchBrowserTool(session.executor, fullToolName, args);
274
+ if (typeof result === "string") {
275
+ json(res, 200, { ok: true, text: result });
276
+ } else {
277
+ json(res, 200, {
278
+ ok: true,
279
+ text: result.text,
280
+ images: result.images
281
+ });
282
+ }
283
+ } catch (err) {
284
+ json(res, 500, {
285
+ ok: false,
286
+ error: err instanceof Error ? err.message : String(err)
287
+ });
288
+ }
289
+ }
290
+ async function handleRequest(req, res) {
291
+ const method = req.method ?? "GET";
292
+ const { path: segments } = parseRoute(req.url ?? "/");
293
+ const body = method === "POST" || method === "DELETE" ? await readBody(req) : "";
294
+ try {
295
+ if (method === "GET" && segments[0] === "health") {
296
+ await handleHealth(res);
297
+ return;
298
+ }
299
+ if (method === "POST" && segments[0] === "sessions" && segments.length === 1) {
300
+ await handleCreateSession(body, res);
301
+ return;
302
+ }
303
+ if (method === "GET" && segments[0] === "sessions" && segments.length === 1) {
304
+ await handleListSessions(res);
305
+ return;
306
+ }
307
+ if (method === "GET" && segments[0] === "sessions" && segments.length === 2) {
308
+ await handleGetSession(segments[1], res);
309
+ return;
310
+ }
311
+ if (method === "DELETE" && segments[0] === "sessions" && segments.length === 1) {
312
+ await handleDeleteAllSessions(res);
313
+ return;
314
+ }
315
+ if (method === "DELETE" && segments[0] === "sessions" && segments.length === 2) {
316
+ await handleDeleteSession(segments[1], res);
317
+ return;
318
+ }
319
+ if (method === "POST" && segments[0] === "sessions" && segments[2] === "tools" && segments.length === 4) {
320
+ await handleToolCall(segments[1], segments[3], body, res);
321
+ return;
322
+ }
323
+ json(res, 404, { ok: false, error: "Not found" });
324
+ } catch (err) {
325
+ json(res, 500, { ok: false, error: err instanceof Error ? err.message : String(err) });
326
+ }
327
+ }
328
+ async function writePidfile(port) {
329
+ await fs.mkdir(PIDFILE_DIR, { recursive: true, mode: 448 });
330
+ const state = {
331
+ pid: process.pid,
332
+ port,
333
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
334
+ };
335
+ await fs.writeFile(PIDFILE_PATH, JSON.stringify(state, null, 2), { mode: 384 });
336
+ }
337
+ async function removePidfile() {
338
+ try {
339
+ await fs.unlink(PIDFILE_PATH);
340
+ } catch {
341
+ }
342
+ }
343
+ async function cleanup() {
344
+ for (const session of sessions.values()) {
345
+ try {
346
+ await session.client.disconnect();
347
+ } catch {
348
+ }
349
+ }
350
+ sessions.clear();
351
+ await removePidfile();
352
+ }
353
+ async function startDaemon() {
354
+ const server = createServer(handleRequest);
355
+ return new Promise((resolve, reject) => {
356
+ server.listen(0, "127.0.0.1", async () => {
357
+ const addr = server.address();
358
+ if (!addr || typeof addr === "string") {
359
+ reject(new Error("Failed to get server address"));
360
+ return;
361
+ }
362
+ const port = addr.port;
363
+ await writePidfile(port);
364
+ resetIdleTimer();
365
+ process.on("SIGINT", async () => {
366
+ await cleanup();
367
+ process.exit(0);
368
+ });
369
+ process.on("SIGTERM", async () => {
370
+ await cleanup();
371
+ process.exit(0);
372
+ });
373
+ process.on("exit", () => {
374
+ removePidfile().catch(() => {
375
+ });
376
+ });
377
+ resolve({ port });
378
+ });
379
+ server.on("error", reject);
380
+ });
381
+ }
382
+ async function runDaemon() {
383
+ const { port } = await startDaemon();
384
+ process.stdout.write(`DAEMON_READY:${port}
385
+ `);
386
+ }
387
+
388
+ // src/session/index.ts
389
+ function formatUptime(startedAt) {
390
+ const seconds = Math.floor((Date.now() - new Date(startedAt).getTime()) / 1e3);
391
+ if (seconds < 60) return `${seconds}s`;
392
+ const minutes = Math.floor(seconds / 60);
393
+ const secs = seconds % 60;
394
+ if (minutes < 60) return `${minutes}m ${secs}s`;
395
+ const hours = Math.floor(minutes / 60);
396
+ const mins = minutes % 60;
397
+ return `${hours}h ${mins}m`;
398
+ }
399
+ function printSessionTable(sessions2) {
400
+ if (sessions2.length === 0) {
401
+ console.log("No active sessions.");
402
+ return;
403
+ }
404
+ const header = ["ID", "NAME", "MODE", "CREDENTIAL", "URL", "UPTIME"];
405
+ const rows = sessions2.map((s) => [
406
+ s.id,
407
+ s.name,
408
+ s.mode,
409
+ s.credentialName ?? "(none)",
410
+ s.url ?? "(none)",
411
+ formatUptime(s.startedAt)
412
+ ]);
413
+ const widths = header.map(
414
+ (h, i) => Math.max(h.length, ...rows.map((r) => r[i].length))
415
+ );
416
+ const pad = (str, width) => str.padEnd(width);
417
+ console.log(header.map((h, i) => pad(h, widths[i])).join(" "));
418
+ for (const row of rows) {
419
+ console.log(row.map((cell, i) => pad(cell, widths[i])).join(" "));
420
+ }
421
+ }
422
+ function printToolResult(result, jsonMode) {
423
+ if (jsonMode) {
424
+ console.log(JSON.stringify(result, null, 2));
425
+ return;
426
+ }
427
+ if (!result.ok) {
428
+ console.error(`Error: ${result.error}`);
429
+ process3.exitCode = 1;
430
+ return;
431
+ }
432
+ if (result.text) {
433
+ console.log(result.text);
434
+ }
435
+ if (result.images?.length) {
436
+ console.log(`[${result.images.length} image(s) captured]`);
437
+ }
438
+ }
439
+ async function handleStart(argv) {
440
+ const headless = hasFlag(argv, "--headless");
441
+ const name = getArgValue(argv, "--name");
442
+ const url = getArgValue(argv, "--url");
443
+ const storageStatePath = getArgValue(argv, "--storage-state");
444
+ const jsonMode = hasFlag(argv, "--json");
445
+ const credentialIdx = argv.indexOf("--credential");
446
+ let resolvedCred = null;
447
+ if (credentialIdx !== -1) {
448
+ const nextArg = argv[credentialIdx + 1];
449
+ const credentialArg = nextArg && !nextArg.startsWith("--") ? nextArg : void 0;
450
+ resolvedCred = await resolveCredential(argv, credentialArg);
451
+ if (!resolvedCred) {
452
+ process3.exitCode = 1;
453
+ return;
454
+ }
455
+ }
456
+ const result = await createSession({
457
+ headless,
458
+ name,
459
+ url,
460
+ storageStatePath: resolvedCred?.storageStatePath ?? storageStatePath,
461
+ credentialName: resolvedCred?.credentialName
462
+ });
463
+ if (jsonMode) {
464
+ console.log(JSON.stringify(result, null, 2));
465
+ return;
466
+ }
467
+ if (!result.ok) {
468
+ console.error(`Error: ${result.error}`);
469
+ process3.exitCode = 1;
470
+ return;
471
+ }
472
+ const session = result.data;
473
+ console.log(`Session "${session.id}" started (${session.mode})`);
474
+ if (session.credentialName) {
475
+ console.log(` Credential: ${session.credentialName}`);
476
+ }
477
+ if (session.url) {
478
+ console.log(` URL: ${session.url}`);
479
+ }
480
+ }
481
+ async function handleList(argv) {
482
+ const jsonMode = hasFlag(argv, "--json");
483
+ const result = await listSessions();
484
+ if (jsonMode) {
485
+ console.log(JSON.stringify(result, null, 2));
486
+ return;
487
+ }
488
+ if (!result.ok) {
489
+ console.error(`Error: ${result.error}`);
490
+ process3.exitCode = 1;
491
+ return;
492
+ }
493
+ printSessionTable(result.data ?? []);
494
+ }
495
+ async function handleStatus(argv) {
496
+ const sessionId = argv[0] && !argv[0].startsWith("--") ? argv[0] : void 0;
497
+ const jsonMode = hasFlag(argv, "--json");
498
+ try {
499
+ const target = await resolveTargetSession(sessionId);
500
+ const result = await getSession(target.id);
501
+ if (jsonMode) {
502
+ console.log(JSON.stringify(result, null, 2));
503
+ return;
504
+ }
505
+ if (!result.ok || !result.data) {
506
+ console.error(`Error: ${result.error}`);
507
+ process3.exitCode = 1;
508
+ return;
509
+ }
510
+ const s = result.data;
511
+ console.log(`Session: ${s.id} (${s.name})`);
512
+ console.log(` Mode: ${s.mode}`);
513
+ console.log(` Credential: ${s.credentialName ?? "(none)"}`);
514
+ console.log(` URL: ${s.url ?? "(none)"}`);
515
+ console.log(` Uptime: ${formatUptime(s.startedAt)}`);
516
+ console.log(` Started: ${s.startedAt}`);
517
+ } catch (err) {
518
+ console.error(err instanceof Error ? err.message : String(err));
519
+ process3.exitCode = 1;
520
+ }
521
+ }
522
+ async function handleStop(argv) {
523
+ const jsonMode = hasFlag(argv, "--json");
524
+ if (hasFlag(argv, "--all")) {
525
+ const result = await deleteAllSessions();
526
+ if (jsonMode) {
527
+ console.log(JSON.stringify(result, null, 2));
528
+ } else if (result.ok) {
529
+ console.log("All sessions stopped.");
530
+ } else {
531
+ console.error(`Error: ${result.error}`);
532
+ process3.exitCode = 1;
533
+ }
534
+ return;
535
+ }
536
+ const sessionId = argv[0] && !argv[0].startsWith("--") ? argv[0] : void 0;
537
+ try {
538
+ const target = await resolveTargetSession(sessionId);
539
+ const result = await deleteSession(target.id);
540
+ if (jsonMode) {
541
+ console.log(JSON.stringify(result, null, 2));
542
+ } else if (result.ok) {
543
+ console.log(`Session "${target.id}" stopped.`);
544
+ } else {
545
+ console.error(`Error: ${result.error}`);
546
+ process3.exitCode = 1;
547
+ }
548
+ } catch (err) {
549
+ console.error(err instanceof Error ? err.message : String(err));
550
+ process3.exitCode = 1;
551
+ }
552
+ }
553
+ async function handleToolCommand(toolName, argv) {
554
+ const sessionIdOrName = getArgValue(argv, "--session");
555
+ const jsonMode = hasFlag(argv, "--json");
556
+ try {
557
+ const target = await resolveTargetSession(sessionIdOrName);
558
+ const args = buildToolArgs(toolName, argv);
559
+ const result = await callTool(target.id, toolName, args);
560
+ printToolResult(result, jsonMode);
561
+ } catch (err) {
562
+ console.error(err instanceof Error ? err.message : String(err));
563
+ process3.exitCode = 1;
564
+ }
565
+ }
566
+ function buildToolArgs(toolName, argv) {
567
+ const args = {};
568
+ switch (toolName) {
569
+ case "navigate":
570
+ args.url = getArgValue(argv, "--url");
571
+ break;
572
+ case "snapshot":
573
+ args.search = getArgValue(argv, "--search");
574
+ args.mode = getArgValue(argv, "--mode") ?? "tree";
575
+ break;
576
+ case "screenshot":
577
+ args.fullPage = hasFlag(argv, "--full-page");
578
+ args.label = getArgValue(argv, "--label");
579
+ args.returnImage = true;
580
+ break;
581
+ case "evaluate":
582
+ args.function = getArgValue(argv, "--js");
583
+ break;
584
+ case "click": {
585
+ const ref = getArgValue(argv, "--ref");
586
+ const x = getArgValue(argv, "--x");
587
+ const y = getArgValue(argv, "--y");
588
+ if (ref) args.ref = ref;
589
+ if (x) args.x = parseInt(x, 10);
590
+ if (y) args.y = parseInt(y, 10);
591
+ args.element = getArgValue(argv, "--element") ?? "";
592
+ break;
593
+ }
594
+ case "type":
595
+ args.ref = getArgValue(argv, "--ref");
596
+ args.text = getArgValue(argv, "--text");
597
+ args.element = getArgValue(argv, "--element") ?? "";
598
+ args.submit = hasFlag(argv, "--submit");
599
+ break;
600
+ case "fill_form": {
601
+ const fieldsStr = getArgValue(argv, "--fields");
602
+ if (fieldsStr) {
603
+ try {
604
+ args.fields = JSON.parse(fieldsStr);
605
+ } catch {
606
+ console.error("Error: --fields must be valid JSON");
607
+ process3.exitCode = 1;
608
+ }
609
+ }
610
+ break;
611
+ }
612
+ case "select_option":
613
+ args.ref = getArgValue(argv, "--ref");
614
+ args.value = getArgValue(argv, "--value");
615
+ args.element = getArgValue(argv, "--element") ?? "";
616
+ break;
617
+ case "press_key":
618
+ args.key = getArgValue(argv, "--key");
619
+ break;
620
+ case "scroll":
621
+ args.direction = getArgValue(argv, "--direction") ?? "down";
622
+ {
623
+ const amount = getArgValue(argv, "--amount");
624
+ if (amount) args.amount = parseInt(amount, 10);
625
+ }
626
+ break;
627
+ case "hover":
628
+ args.ref = getArgValue(argv, "--ref");
629
+ args.element = getArgValue(argv, "--element") ?? "";
630
+ break;
631
+ case "tabs": {
632
+ args.action = getArgValue(argv, "--action") ?? "list";
633
+ const index = getArgValue(argv, "--index");
634
+ if (index) args.index = parseInt(index, 10);
635
+ break;
636
+ }
637
+ case "wait_for": {
638
+ args.text = getArgValue(argv, "--text");
639
+ const timeout = getArgValue(argv, "--timeout");
640
+ if (timeout) args.timeout = parseInt(timeout, 10);
641
+ break;
642
+ }
643
+ case "console_messages":
644
+ args.onlyErrors = hasFlag(argv, "--only-errors");
645
+ break;
646
+ }
647
+ return args;
648
+ }
649
+ var TOOL_ALIASES = {
650
+ navigate: "navigate",
651
+ back: "navigate_back",
652
+ snapshot: "snapshot",
653
+ screenshot: "screenshot",
654
+ evaluate: "evaluate",
655
+ click: "click",
656
+ type: "type",
657
+ fill: "fill_form",
658
+ select: "select_option",
659
+ press: "press_key",
660
+ scroll: "scroll",
661
+ hover: "hover",
662
+ drag: "drag",
663
+ tabs: "tabs",
664
+ wait: "wait_for",
665
+ console: "console_messages",
666
+ network: "network_requests"
667
+ };
668
+ function printHelp() {
669
+ console.log(
670
+ [
671
+ "Usage: canary session <subcommand> [options]",
672
+ "",
673
+ "Session lifecycle:",
674
+ " start [options] Start a new browser session",
675
+ " list List active sessions",
676
+ " status [<id>] Show session details",
677
+ " stop [<id>] Stop a session (or --all)",
678
+ "",
679
+ "Browser actions:",
680
+ " navigate --url <url> Navigate to URL",
681
+ " back Navigate back",
682
+ " snapshot [--search <text>] [--mode tree|screenshot|both]",
683
+ " screenshot [--full-page] [--label <text>]",
684
+ " evaluate --js <expression> Evaluate JavaScript",
685
+ " click --ref <ref> [--element <desc>]",
686
+ " click --x <n> --y <n> [--element <desc>]",
687
+ " type --ref <ref> --text <text> [--submit]",
688
+ ` fill --fields '[{"ref":"e1","value":"test"}]'`,
689
+ " select --ref <ref> --value <val> [--element <desc>]",
690
+ " press --key <key> Press keyboard key",
691
+ " scroll --direction <dir> [--amount <n>]",
692
+ " hover --ref <ref> [--element <desc>]",
693
+ " tabs [--action list|new|close|select] [--index <n>]",
694
+ " wait --text <text> [--timeout <ms>]",
695
+ " console [--only-errors] Show console messages",
696
+ " network Show network requests",
697
+ "",
698
+ "Start options:",
699
+ " --credential [<name|id>] Use a credential (interactive if no value)",
700
+ " --storage-state <path> Use a local storage state file",
701
+ " --headless Run browser without visible UI",
702
+ " --url <url> Navigate to URL after launch",
703
+ " --name <label> Name the session",
704
+ "",
705
+ "Common options:",
706
+ " --session <id|name> Target a specific session (when multiple)",
707
+ " --json Output raw JSON",
708
+ " --env <env> Environment for credential resolution",
709
+ " -h, --help Show this help"
710
+ ].join("\n")
711
+ );
712
+ }
713
+ async function runSession(argv) {
714
+ const [subcommand, ...rest] = argv;
715
+ if (!subcommand || subcommand === "help" || hasFlag(argv, "-h", "--help")) {
716
+ printHelp();
717
+ return;
718
+ }
719
+ if (subcommand === "daemon") {
720
+ await runDaemon();
721
+ return;
722
+ }
723
+ switch (subcommand) {
724
+ case "start":
725
+ await handleStart(rest);
726
+ break;
727
+ case "list":
728
+ await handleList(rest);
729
+ break;
730
+ case "status":
731
+ await handleStatus(rest);
732
+ break;
733
+ case "stop":
734
+ await handleStop(rest);
735
+ break;
736
+ default: {
737
+ const toolName = TOOL_ALIASES[subcommand];
738
+ if (toolName) {
739
+ await handleToolCommand(toolName, rest);
740
+ } else {
741
+ console.error(`Unknown session subcommand: ${subcommand}`);
742
+ printHelp();
743
+ process3.exitCode = 1;
744
+ }
745
+ }
746
+ }
747
+ }
748
+ export {
749
+ runSession
750
+ };
751
+ //# sourceMappingURL=session-RNLKFS2Z.js.map