webdriverio-execute 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vince Graics
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # wdiox
2
+
3
+ Interactive browser and app CLI for developers, powered by [WebdriverIO](https://webdriver.io).
4
+
5
+ ```
6
+ npm install -g webdriverio-execute
7
+ ```
8
+
9
+ ## How it works
10
+
11
+ Each command is stateless. Sessions are stored as JSON in `~/.wdio-x/sessions/`. Commands attach to an existing session by reading that file — no daemon, no background process.
12
+
13
+ `snapshot` captures all interactable elements and assigns short refs (`e1`, `e2`, …). Subsequent commands use those refs to act on elements.
14
+
15
+ ## Commands
16
+
17
+ ### `open` / `new` / `start`
18
+
19
+ Open a browser or Appium session.
20
+
21
+ ```bash
22
+ wdiox open https://example.com
23
+ wdiox open https://example.com --browser firefox
24
+ wdiox open --app /path/to/app.apk --device "emulator-5554"
25
+ wdiox open --app /path/to/app.ipa --device "iPhone 15"
26
+ ```
27
+
28
+ | Option | Default | Description |
29
+ |---|---|---|
30
+ | `--browser` | `chrome` | Browser to use (`chrome`, `firefox`, `edge`, `safari`) |
31
+ | `--app` | — | Path to mobile app (`.apk`, `.ipa`, `.app`) |
32
+ | `--device` | `emulator-5554` | Device name |
33
+ | `--platform` | auto-detected | `android` or `ios` |
34
+ | `--hostname` | `localhost` | WebDriver/Appium server hostname |
35
+ | `--port` | `4723` (mobile) / `4444` (browser) | Server port |
36
+ | `--grant-permissions` | `true` | Auto-grant app permissions (Appium) |
37
+ | `--accept-alert` | `true` | Auto-accept native alerts (Appium) |
38
+ | `--auto-dismiss` | `false` | Auto-dismiss native alerts (Appium) |
39
+ | `--session` | `default` | Session name |
40
+
41
+ If a session with the given name is already active, you'll be prompted to close it first.
42
+
43
+ ---
44
+
45
+ ### `snapshot`
46
+
47
+ Capture interactable elements on the current page or screen and assign numbered refs.
48
+
49
+ ```bash
50
+ wdiox snapshot
51
+ wdiox snapshot --no-visible # include off-screen elements
52
+ ```
53
+
54
+ ```
55
+ Page: https://example.com/login
56
+
57
+ e1 input[email] "Email address" #email
58
+ e2 input[password] "Password" #password
59
+ e3 button "Sign in" button*=Sign in
60
+
61
+ 3 elements - default session
62
+ ```
63
+
64
+ ---
65
+
66
+ ### `click`
67
+
68
+ Click an element by ref.
69
+
70
+ ```bash
71
+ wdiox click e3
72
+ ```
73
+
74
+ ---
75
+
76
+ ### `fill` / `type`
77
+
78
+ Clear and type into an input by ref.
79
+
80
+ ```bash
81
+ wdiox fill e1 "hello@example.com"
82
+ wdiox type e2 "mysecretpassword"
83
+ ```
84
+
85
+ ---
86
+
87
+ ### `screenshot`
88
+
89
+ Save a screenshot.
90
+
91
+ ```bash
92
+ wdiox screenshot
93
+ wdiox screenshot /tmp/login-page.png
94
+ ```
95
+
96
+ ---
97
+
98
+ ### `close` / `stop`
99
+
100
+ Close the current session.
101
+
102
+ ```bash
103
+ wdiox close
104
+ wdiox close --session myapp
105
+ ```
106
+
107
+ ---
108
+
109
+ ### `ls` / `session-list`
110
+
111
+ List all active sessions.
112
+
113
+ ```bash
114
+ wdiox ls
115
+ ```
116
+
117
+ ```
118
+ NAME BROWSER URL STATUS
119
+ default chrome https://example.com active
120
+ myapp Android /path/to/app.apk active
121
+ ```
122
+
123
+ ---
124
+
125
+ ## Multi-session
126
+
127
+ Every command accepts `--session <name>` (or `-s <name>`) to target a specific session. The `WDIO_SESSION` environment variable sets the default session name.
128
+
129
+ ```bash
130
+ wdiox open https://site-a.com --session a
131
+ wdiox open https://site-b.com --session b
132
+ wdiox snapshot --session a
133
+ wdiox click e1 --session a
134
+ wdiox close --session b
135
+ ```
136
+
137
+ ## Typical browser workflow
138
+
139
+ ```bash
140
+ wdiox open https://example.com
141
+ wdiox snapshot
142
+ wdiox fill e1 "user@example.com"
143
+ wdiox fill e2 "password"
144
+ wdiox click e3
145
+ wdiox screenshot
146
+ wdiox close
147
+ ```
148
+
149
+ ## Typical mobile workflow
150
+
151
+ ```bash
152
+ wdiox open --app ./app.apk --device "emulator-5554"
153
+ wdiox snapshot
154
+ wdiox click e1
155
+ wdiox fill e2 "hello"
156
+ wdiox close
157
+ ```
package/build/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/build/cli.js ADDED
@@ -0,0 +1,544 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/cli.ts
8
+ import yargs from "yargs";
9
+ import { hideBin } from "yargs/helpers";
10
+
11
+ // src/commands/open.ts
12
+ var open_exports = {};
13
+ __export(open_exports, {
14
+ builder: () => builder,
15
+ command: () => command,
16
+ desc: () => desc,
17
+ handler: () => handler
18
+ });
19
+ import readline from "readline/promises";
20
+ import { attach, remote } from "webdriverio";
21
+
22
+ // src/session.ts
23
+ import os from "os";
24
+ import fs from "fs/promises";
25
+ import path from "path";
26
+ var DEFAULT_SESSION_DIR = path.join(os.homedir(), ".wdio-x", "sessions");
27
+ function isEnoent(err) {
28
+ return err instanceof Error && "code" in err && err.code === "ENOENT";
29
+ }
30
+ function buildAttachOptions(meta) {
31
+ return {
32
+ sessionId: meta.sessionId,
33
+ capabilities: meta.capabilities,
34
+ options: {
35
+ hostname: meta.hostname,
36
+ port: meta.port,
37
+ logLevel: process.env.WDIO_LOG_LEVEL ?? "error"
38
+ }
39
+ };
40
+ }
41
+ function getSessionDir(baseDir) {
42
+ return baseDir ?? DEFAULT_SESSION_DIR;
43
+ }
44
+ function getSessionPath(name, baseDir) {
45
+ return path.join(getSessionDir(baseDir), `${name}.json`);
46
+ }
47
+ function getRefsPath(name, baseDir) {
48
+ return path.join(getSessionDir(baseDir), `${name}.refs.json`);
49
+ }
50
+ async function writeSession(name, metadata, baseDir) {
51
+ const dir = getSessionDir(baseDir);
52
+ await fs.mkdir(dir, { recursive: true });
53
+ const filePath = getSessionPath(name, baseDir);
54
+ await fs.writeFile(filePath, JSON.stringify(metadata, null, 2));
55
+ }
56
+ async function readSession(name, baseDir) {
57
+ const filePath = getSessionPath(name, baseDir);
58
+ try {
59
+ const content = await fs.readFile(filePath, "utf-8");
60
+ return JSON.parse(content);
61
+ } catch (err) {
62
+ if (isEnoent(err)) {
63
+ return null;
64
+ }
65
+ throw err;
66
+ }
67
+ }
68
+ async function deleteSessionFiles(name, baseDir) {
69
+ const sessionPath = getSessionPath(name, baseDir);
70
+ const refsPath = getRefsPath(name, baseDir);
71
+ await Promise.all([
72
+ fs.rm(sessionPath, { force: true }),
73
+ fs.rm(refsPath, { force: true })
74
+ ]);
75
+ }
76
+ async function listSessions(baseDir) {
77
+ const dir = getSessionDir(baseDir);
78
+ let files;
79
+ try {
80
+ files = await fs.readdir(dir);
81
+ } catch (err) {
82
+ if (isEnoent(err)) {
83
+ return [];
84
+ }
85
+ throw err;
86
+ }
87
+ const sessionFiles = files.filter(
88
+ (f) => f.endsWith(".json") && !f.endsWith(".refs.json")
89
+ );
90
+ return await Promise.all(
91
+ sessionFiles.map(async (f) => {
92
+ const name = f.replace(/\.json$/, "");
93
+ const metadata = await readSession(name, baseDir);
94
+ return { name, metadata };
95
+ })
96
+ );
97
+ }
98
+ function withSession(fn) {
99
+ return async function handler8(argv) {
100
+ const sessionName = argv.session;
101
+ const sessionsDir = argv._sessionsDir || getSessionDir();
102
+ const meta = await readSession(sessionName, sessionsDir);
103
+ if (!meta) {
104
+ console.error(`Error: No active session [${sessionName}]. Run wdiox open <url> first.`);
105
+ return;
106
+ }
107
+ await fn(argv, meta, sessionsDir);
108
+ };
109
+ }
110
+
111
+ // src/commands/open.ts
112
+ var command = ["open [url]", "new [url]", "start [url]"];
113
+ var desc = "Open a browser or Appium session";
114
+ var builder = (yargs2) => {
115
+ return yargs2.positional("url", {
116
+ type: "string",
117
+ describe: "URL to navigate to"
118
+ }).option("browser", {
119
+ alias: "b",
120
+ type: "string",
121
+ default: "chrome",
122
+ describe: "Browser to use (chrome, firefox, edge, safari)"
123
+ }).option("app", {
124
+ type: "string",
125
+ describe: "Path to mobile app (.apk, .ipa, .app)"
126
+ }).option("device", {
127
+ alias: "d",
128
+ type: "string",
129
+ describe: "Device name for mobile testing"
130
+ }).option("platform", {
131
+ type: "string",
132
+ describe: "Mobile platform (android, ios)"
133
+ }).option("path", {
134
+ type: "string",
135
+ describe: "WebDriver/Appium server session path (default: /)"
136
+ }).option("port", {
137
+ alias: "p",
138
+ type: "number",
139
+ describe: "WebDriver/Appium server port (default: 4723)"
140
+ }).option("hostname", {
141
+ type: "string",
142
+ describe: "WebDriver/Appium server hostname (default: localhost)"
143
+ }).option("grant-permissions", {
144
+ type: "boolean",
145
+ default: true,
146
+ describe: "Auto-grant app permissions (Appium only)"
147
+ }).option("accept-alert", {
148
+ type: "boolean",
149
+ default: true,
150
+ describe: "Auto-accept native alerts (Appium only)"
151
+ }).option("auto-dismiss", {
152
+ type: "boolean",
153
+ default: false,
154
+ describe: "Auto-dismiss native alerts (Appium only)"
155
+ });
156
+ };
157
+ async function handler(argv) {
158
+ const sessionName = argv.session;
159
+ const sessionsDir = argv._sessionsDir || getSessionDir();
160
+ const existing = await readSession(sessionName, sessionsDir);
161
+ if (existing) {
162
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
163
+ const context = existing.url || existing.capabilities["appium:app"] || "";
164
+ const answer = await rl.question(`Session [${sessionName}] is already active${context ? ` (${context})` : ""}.
165
+ Close it and start a new one? (y/N) `);
166
+ rl.close();
167
+ if (answer.trim().toLowerCase() !== "y") {
168
+ return;
169
+ }
170
+ try {
171
+ const old = await attach(buildAttachOptions(existing));
172
+ await old.deleteSession();
173
+ } catch {
174
+ }
175
+ await deleteSessionFiles(sessionName, sessionsDir);
176
+ }
177
+ const capabilities = {};
178
+ const isMobile = !!argv.app;
179
+ if (isMobile) {
180
+ const platform = argv.platform ?? (argv.app?.endsWith(".apk") ? "android" : "ios");
181
+ capabilities.platformName = platform === "ios" ? "iOS" : "Android";
182
+ capabilities["appium:app"] = argv.app;
183
+ capabilities["appium:deviceName"] = argv.device ?? "emulator-5554";
184
+ capabilities["appium:newCommandTimeout"] = 300;
185
+ capabilities["appium:automationName"] = platform === "ios" ? "XCUITest" : "UiAutomator2";
186
+ capabilities["appium:autoGrantPermissions"] = argv.grantPermissions;
187
+ capabilities["appium:autoAcceptAlerts"] = argv.acceptAlert;
188
+ capabilities["appium:autoDismissAlerts"] = argv.autoDismiss;
189
+ } else {
190
+ capabilities.browserName = argv.browser;
191
+ }
192
+ const remoteOpts = { capabilities, logLevel: process.env.WDIO_LOG_LEVEL ?? "error" };
193
+ if (argv.hostname || argv.port || isMobile) {
194
+ remoteOpts.hostname = argv.hostname ?? "localhost";
195
+ remoteOpts.port = argv.port ?? (isMobile ? 4723 : 4444);
196
+ remoteOpts.path = argv.path ?? "/";
197
+ }
198
+ const browser = await remote(remoteOpts);
199
+ if (argv.url) {
200
+ await browser.url(argv.url);
201
+ }
202
+ const opts = browser.options;
203
+ await writeSession(sessionName, {
204
+ sessionId: browser.sessionId,
205
+ hostname: opts?.hostname || "localhost",
206
+ port: opts?.port || 4444,
207
+ capabilities: { ...capabilities, ...browser.capabilities },
208
+ created: (/* @__PURE__ */ new Date()).toISOString(),
209
+ url: argv.url || ""
210
+ }, sessionsDir);
211
+ console.log(`Session "${sessionName}" started.`);
212
+ }
213
+
214
+ // src/commands/close.ts
215
+ var close_exports = {};
216
+ __export(close_exports, {
217
+ command: () => command2,
218
+ desc: () => desc2,
219
+ handler: () => handler2
220
+ });
221
+ import { attach as attach2 } from "webdriverio";
222
+ var command2 = ["close", "stop"];
223
+ var desc2 = "Close the current session";
224
+ var handler2 = withSession(async (argv, meta, sessionsDir) => {
225
+ const sessionName = argv.session;
226
+ try {
227
+ const browser = await attach2(buildAttachOptions(meta));
228
+ await browser.deleteSession();
229
+ } catch {
230
+ }
231
+ await deleteSessionFiles(sessionName, sessionsDir);
232
+ console.log(`Session "${sessionName}" closed.`);
233
+ });
234
+
235
+ // src/commands/snapshot.ts
236
+ var snapshot_exports = {};
237
+ __export(snapshot_exports, {
238
+ builder: () => builder2,
239
+ command: () => command3,
240
+ desc: () => desc3,
241
+ handler: () => handler3
242
+ });
243
+ import { attach as attach3 } from "webdriverio";
244
+
245
+ // src/refs.ts
246
+ import fs2 from "fs/promises";
247
+ function isEnoent2(err) {
248
+ return err instanceof Error && "code" in err && err.code === "ENOENT";
249
+ }
250
+ async function writeRefs(refsPath, refs) {
251
+ await fs2.writeFile(refsPath, JSON.stringify(refs, null, 2), "utf-8");
252
+ }
253
+ async function readRefs(refsPath) {
254
+ try {
255
+ const content = await fs2.readFile(refsPath, "utf-8");
256
+ return JSON.parse(content);
257
+ } catch (err) {
258
+ if (isEnoent2(err)) {
259
+ return null;
260
+ }
261
+ throw err;
262
+ }
263
+ }
264
+ async function lookupRef(refsPath, refKey) {
265
+ const refs = await readRefs(refsPath);
266
+ if (!refs) {
267
+ console.error("Error: No snapshot taken. Run wdiox snapshot first.");
268
+ return null;
269
+ }
270
+ const ref = refs[refKey];
271
+ if (!ref) {
272
+ const available = Object.keys(refs);
273
+ const range = available.length > 0 ? `Available: ${available[0]}-${available[available.length - 1]}` : "No refs available";
274
+ console.error(`Error: ${refKey} not found in snapshot. ${range}`);
275
+ return null;
276
+ }
277
+ if (!ref.selector) {
278
+ console.error(`Error: Could not resolve selector for ${refKey}`);
279
+ return null;
280
+ }
281
+ return { ref, selector: ref.selector };
282
+ }
283
+
284
+ // src/commands/snapshot.ts
285
+ import { getInteractableBrowserElements, getMobileVisibleElements } from "@wdio/mcp/snapshot";
286
+
287
+ // src/format.ts
288
+ function truncate(str, max = 80) {
289
+ return str.length <= max ? str : str.slice(0, max - 3) + "...";
290
+ }
291
+ function formatBrowserElement(ref, el) {
292
+ const tag = (el.role && el.role !== el.tagName ? el.role : el.tagName) + (el.type ? `[${el.type}]` : "");
293
+ const desc8 = [el.name && `"${truncate(el.name)}"`, el.href && `-> ${truncate(el.href)}`].filter(Boolean).join(" ");
294
+ return [ref.padEnd(4), tag, desc8, el.selector].filter(Boolean).join(" ");
295
+ }
296
+ function formatMobileElement(ref, el) {
297
+ const selector = el.accessibilityId ? `[accessibility-id: ${el.accessibilityId}]` : el.resourceId ? `[resource-id: ${el.resourceId}]` : truncate(el.selector);
298
+ const parts = [ref.padEnd(4), el.tagName.padEnd(28), el.text && `"${truncate(el.text)}"`, selector];
299
+ return parts.filter(Boolean).join(" ");
300
+ }
301
+ function formatSessionList(entries) {
302
+ if (entries.length === 0) return "No active sessions.";
303
+ const cols = ["name", "browser", "url", "status"].map((key) => ({
304
+ key,
305
+ header: key.toUpperCase(),
306
+ width: Math.max(key.length, ...entries.map((e) => e[key].length))
307
+ }));
308
+ const row = (e) => cols.map((c) => e[c.key].padEnd(c.width)).join(" ");
309
+ const header = cols.map((c) => c.header.padEnd(c.width)).join(" ");
310
+ return [header, ...entries.map(row)].join("\n");
311
+ }
312
+
313
+ // src/commands/snapshot.ts
314
+ var command3 = "snapshot";
315
+ var desc3 = "Capture interactive elements on the page or screen";
316
+ var builder2 = (yargs2) => {
317
+ return yargs2.option("visible", {
318
+ type: "boolean",
319
+ default: true,
320
+ describe: "Only show elements in viewport"
321
+ });
322
+ };
323
+ var handler3 = withSession(async (argv, meta, sessionsDir) => {
324
+ const sessionName = argv.session;
325
+ const browser = await attach3(buildAttachOptions(meta));
326
+ const isMobile = browser.isAndroid || browser.isIOS;
327
+ const refs = {};
328
+ if (isMobile) {
329
+ const platform = browser.isIOS ? "ios" : "android";
330
+ const elements = await getMobileVisibleElements(browser, platform);
331
+ const filtered = argv.visible ? elements.filter((el) => el.isInViewport) : elements;
332
+ const appName = meta.capabilities["appium:app"] || "unknown";
333
+ console.log(`
334
+ App: ${appName}
335
+ `);
336
+ filtered.forEach((el, i) => {
337
+ const ref = `e${i + 1}`;
338
+ console.log(formatMobileElement(ref, {
339
+ tagName: el.tagName,
340
+ text: el.text,
341
+ selector: el.selector,
342
+ accessibilityId: el.accessibilityId,
343
+ resourceId: el.resourceId
344
+ }));
345
+ refs[ref] = {
346
+ selector: el.selector,
347
+ tagName: el.tagName,
348
+ text: el.text
349
+ };
350
+ });
351
+ console.log(`
352
+ ${filtered.length} elements - ${sessionName} session
353
+ `);
354
+ } else {
355
+ const elements = await getInteractableBrowserElements(browser);
356
+ const filtered = argv.visible ? elements.filter((el) => el.isInViewport) : elements;
357
+ const currentUrl = await browser.getUrl();
358
+ console.log(`
359
+ Page: ${currentUrl}
360
+ `);
361
+ filtered.forEach((el, i) => {
362
+ const ref = `e${i + 1}`;
363
+ console.log(formatBrowserElement(ref, el));
364
+ refs[ref] = {
365
+ selector: el.selector,
366
+ tagName: el.tagName,
367
+ text: el.name || ""
368
+ };
369
+ });
370
+ console.log(`
371
+ ${filtered.length} elements - ${sessionName} session
372
+ `);
373
+ }
374
+ await writeRefs(getRefsPath(sessionName, sessionsDir), refs);
375
+ });
376
+
377
+ // src/commands/click.ts
378
+ var click_exports = {};
379
+ __export(click_exports, {
380
+ builder: () => builder3,
381
+ command: () => command4,
382
+ desc: () => desc4,
383
+ handler: () => handler4
384
+ });
385
+ import { attach as attach4 } from "webdriverio";
386
+ var command4 = "click <ref>";
387
+ var desc4 = "Click an element by snapshot reference (e.g., e1)";
388
+ var builder3 = (yargs2) => {
389
+ return yargs2.positional("ref", {
390
+ type: "string",
391
+ describe: "Element reference from snapshot (e.g., e1, a3)"
392
+ });
393
+ };
394
+ var handler4 = withSession(async (argv, meta, sessionsDir) => {
395
+ const sessionName = argv.session;
396
+ const refKey = argv.ref;
397
+ const result = await lookupRef(getRefsPath(sessionName, sessionsDir), refKey);
398
+ if (!result) {
399
+ return;
400
+ }
401
+ const browser = await attach4(buildAttachOptions(meta));
402
+ try {
403
+ await browser.$(result.selector).click();
404
+ console.log(`Clicked ${refKey} (${result.ref.selector})`);
405
+ } catch (err) {
406
+ const msg = err instanceof Error ? err.message : String(err);
407
+ console.error(`Error clicking ${refKey}: ${msg}`);
408
+ }
409
+ });
410
+
411
+ // src/commands/type.ts
412
+ var type_exports = {};
413
+ __export(type_exports, {
414
+ builder: () => builder4,
415
+ command: () => command5,
416
+ desc: () => desc5,
417
+ handler: () => handler5
418
+ });
419
+ import { attach as attach5 } from "webdriverio";
420
+ var command5 = ["type <ref> <text>", "fill <ref> <text>"];
421
+ var desc5 = "Clear and type text into an input element by snapshot reference";
422
+ var builder4 = (yargs2) => {
423
+ return yargs2.positional("ref", {
424
+ type: "string",
425
+ describe: "Element reference from snapshot (e.g., e1)"
426
+ }).positional("text", {
427
+ type: "string",
428
+ describe: "Text to type"
429
+ });
430
+ };
431
+ var handler5 = withSession(async (argv, meta, sessionsDir) => {
432
+ const sessionName = argv.session;
433
+ const refKey = argv.ref;
434
+ const result = await lookupRef(getRefsPath(sessionName, sessionsDir), refKey);
435
+ if (!result) {
436
+ return;
437
+ }
438
+ const browser = await attach5(buildAttachOptions(meta));
439
+ try {
440
+ const element = await browser.$(result.selector);
441
+ await element.clearValue();
442
+ await element.addValue(argv.text);
443
+ console.log(`Filled ${refKey} with "${argv.text}"`);
444
+ } catch (err) {
445
+ const msg = err instanceof Error ? err.message : String(err);
446
+ console.error(`Error: ${refKey} not found on page \u2014 the page may have changed. Run wdiox snapshot to refresh.
447
+ ${msg}`);
448
+ }
449
+ });
450
+
451
+ // src/commands/screenshot.ts
452
+ var screenshot_exports = {};
453
+ __export(screenshot_exports, {
454
+ builder: () => builder5,
455
+ command: () => command6,
456
+ desc: () => desc6,
457
+ handler: () => handler6
458
+ });
459
+ import { attach as attach6 } from "webdriverio";
460
+ var command6 = "screenshot [path]";
461
+ var desc6 = "Save a screenshot of the current page or screen";
462
+ var builder5 = (yargs2) => {
463
+ return yargs2.positional("path", {
464
+ type: "string",
465
+ describe: "File path to save screenshot (default: ./screenshot-<timestamp>.png)"
466
+ });
467
+ };
468
+ var handler6 = withSession(async (argv, meta) => {
469
+ const browser = await attach6(buildAttachOptions(meta));
470
+ const filePath = argv.path || `screenshot-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.png`;
471
+ await browser.saveScreenshot(filePath);
472
+ console.log(`Screenshot saved to ${filePath}`);
473
+ });
474
+
475
+ // src/commands/session-list.ts
476
+ var session_list_exports = {};
477
+ __export(session_list_exports, {
478
+ command: () => command7,
479
+ desc: () => desc7,
480
+ handler: () => handler7
481
+ });
482
+ import { attach as attach7 } from "webdriverio";
483
+ var command7 = ["session-list", "ls"];
484
+ var desc7 = "List all active sessions";
485
+ async function handler7(argv) {
486
+ const sessionsDir = argv._sessionsDir || getSessionDir();
487
+ const sessions = await listSessions(sessionsDir);
488
+ if (sessions.length === 0) {
489
+ console.log("No active sessions.");
490
+ return;
491
+ }
492
+ const entries = [];
493
+ for (const session of sessions) {
494
+ let status = "active";
495
+ try {
496
+ const browser = await attach7(buildAttachOptions(session.metadata));
497
+ await browser.getPageSource();
498
+ } catch {
499
+ status = "expired";
500
+ await deleteSessionFiles(session.name, sessionsDir);
501
+ }
502
+ const caps = session.metadata.capabilities;
503
+ const browserName = caps.browserName || caps.platformName || "unknown";
504
+ entries.push({
505
+ name: session.name,
506
+ browser: browserName,
507
+ url: session.metadata.url,
508
+ status
509
+ });
510
+ }
511
+ console.log(formatSessionList(entries));
512
+ }
513
+
514
+ // src/cli.ts
515
+ process.env.WDIO_LOG_LEVEL ??= "error";
516
+ var commands = [
517
+ open_exports,
518
+ close_exports,
519
+ snapshot_exports,
520
+ click_exports,
521
+ type_exports,
522
+ screenshot_exports,
523
+ session_list_exports
524
+ ];
525
+ async function main() {
526
+ process.on("unhandledRejection", () => {
527
+ });
528
+ let cli = yargs(hideBin(process.argv)).scriptName("wdiox").usage("$0 <command> [options]").option("session", {
529
+ alias: "s",
530
+ type: "string",
531
+ default: process.env.WDIO_SESSION || "default",
532
+ describe: "Session name"
533
+ });
534
+ for (const cmd of commands) {
535
+ cli = cli.command(cmd);
536
+ }
537
+ await cli.demandCommand(1, "You need to specify a command. Try: wdiox open <url>").strict().help().version().parse();
538
+ process.exit(0);
539
+ }
540
+ main().catch((err) => {
541
+ console.error("\u2717 WebdriverIO Execute failed:", err instanceof Error ? err.message : String(err));
542
+ process.exit(1);
543
+ });
544
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/commands/open.ts","../src/session.ts","../src/commands/close.ts","../src/commands/snapshot.ts","../src/refs.ts","../src/format.ts","../src/commands/click.ts","../src/commands/type.ts","../src/commands/screenshot.ts","../src/commands/session-list.ts"],"sourcesContent":["process.env.WDIO_LOG_LEVEL ??= 'error';\n\nimport yargs from 'yargs';\nimport type { CommandModule } from 'yargs';\nimport { hideBin } from 'yargs/helpers';\n\nimport * as openCmd from './commands/open.js';\nimport * as closeCmd from './commands/close.js';\nimport * as snapshotCmd from './commands/snapshot.js';\nimport * as clickCmd from './commands/click.js';\nimport * as fillCmd from './commands/type';\nimport * as screenshotCmd from './commands/screenshot.js';\nimport * as sessionListCmd from './commands/session-list.js';\n\nconst commands = [\n openCmd, closeCmd, snapshotCmd, clickCmd,\n fillCmd, screenshotCmd, sessionListCmd,\n] as unknown as CommandModule[];\n\nasync function main () {\n\n // webdriverio's attach() can spawn async BiDi connections that fail after\n // the function returns (e.g. stale session). Suppress these so the CLI\n // doesn't crash during close/reconnect of dead sessions.\n process.on('unhandledRejection', () => {});\n\n let cli = yargs(hideBin(process.argv))\n .scriptName('wdiox')\n .usage('$0 <command> [options]')\n .option('session', {\n alias: 's',\n type: 'string',\n default: process.env.WDIO_SESSION || 'default',\n describe: 'Session name',\n });\n\n for (const cmd of commands) {\n cli = cli.command(cmd);\n }\n\n await cli\n .demandCommand(1, 'You need to specify a command. Try: wdiox open <url>')\n .strict()\n .help()\n .version()\n .parse();\n\n // webdriverio keeps HTTP agents alive — force clean exit after command completes\n process.exit(0);\n}\n\nmain().catch((err: unknown) => {\n console.error('✗ WebdriverIO Execute failed:', err instanceof Error ? err.message : String(err));\n process.exit(1);\n});","import readline from 'node:readline/promises';\nimport type { ArgumentsCamelCase, Argv } from 'yargs';\nimport type { Capabilities } from '@wdio/types';\nimport { attach, remote } from 'webdriverio';\n\nimport { writeSession, readSession, getSessionDir, buildAttachOptions, deleteSessionFiles } from '../session.js';\n\nexport const command = ['open [url]', 'new [url]', 'start [url]'];\nexport const desc = 'Open a browser or Appium session';\n\nexport const builder = (yargs: Argv) => {\n return yargs\n .positional('url', {\n type: 'string',\n describe: 'URL to navigate to',\n })\n .option('browser', {\n alias: 'b',\n type: 'string',\n default: 'chrome',\n describe: 'Browser to use (chrome, firefox, edge, safari)',\n })\n .option('app', {\n type: 'string',\n describe: 'Path to mobile app (.apk, .ipa, .app)',\n })\n .option('device', {\n alias: 'd',\n type: 'string',\n describe: 'Device name for mobile testing',\n })\n .option('platform', {\n type: 'string',\n describe: 'Mobile platform (android, ios)',\n })\n .option('path', {\n type: 'string',\n describe: 'WebDriver/Appium server session path (default: /)',\n })\n .option('port', {\n alias: 'p',\n type: 'number',\n describe: 'WebDriver/Appium server port (default: 4723)',\n })\n .option('hostname', {\n type: 'string',\n describe: 'WebDriver/Appium server hostname (default: localhost)',\n })\n .option('grant-permissions', {\n type: 'boolean',\n default: true,\n describe: 'Auto-grant app permissions (Appium only)',\n })\n .option('accept-alert', {\n type: 'boolean',\n default: true,\n describe: 'Auto-accept native alerts (Appium only)',\n })\n .option('auto-dismiss', {\n type: 'boolean',\n default: false,\n describe: 'Auto-dismiss native alerts (Appium only)',\n });\n};\n\ninterface OpenArgs {\n url?: string\n browser: string\n session: string\n app?: string\n device?: string\n platform?: string\n port?: number\n hostname?: string\n grantPermissions: boolean\n acceptAlert: boolean\n autoDismiss: boolean\n _sessionsDir?: string\n}\n\nexport async function handler (argv: ArgumentsCamelCase<OpenArgs>) {\n const sessionName = argv.session as string;\n const sessionsDir = (argv._sessionsDir as string) || getSessionDir();\n\n const existing = await readSession(sessionName, sessionsDir);\n if (existing) {\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n const context = existing.url || (existing.capabilities['appium:app'] as string) || '';\n const answer = await rl.question(`Session [${sessionName}] is already active${context ? ` (${context})` : ''}.\\nClose it and start a new one? (y/N) `);\n rl.close();\n\n if (answer.trim().toLowerCase() !== 'y') {\n return;\n }\n\n try {\n const old = await attach(buildAttachOptions(existing));\n await old.deleteSession();\n } catch {\n // Already dead\n }\n await deleteSessionFiles(sessionName, sessionsDir);\n }\n\n const capabilities: Record<string, unknown> = {};\n\n const isMobile = !!argv.app;\n if (isMobile) {\n const platform = argv.platform ?? (argv.app?.endsWith('.apk') ? 'android' : 'ios');\n\n capabilities.platformName = platform === 'ios' ? 'iOS' : 'Android';\n capabilities['appium:app'] = argv.app;\n capabilities['appium:deviceName'] = argv.device ?? 'emulator-5554';\n capabilities['appium:newCommandTimeout'] = 300;\n capabilities['appium:automationName'] = platform === 'ios' ? 'XCUITest' : 'UiAutomator2';\n capabilities['appium:autoGrantPermissions'] = argv.grantPermissions;\n capabilities['appium:autoAcceptAlerts'] = argv.acceptAlert;\n capabilities['appium:autoDismissAlerts'] = argv.autoDismiss;\n } else {\n capabilities.browserName = argv.browser;\n }\n\n const remoteOpts: Record<string, unknown> = { capabilities, logLevel: process.env.WDIO_LOG_LEVEL ?? 'error' };\n // For mobile / Appium, explicit connection is required\n if (argv.hostname || argv.port || isMobile) {\n remoteOpts.hostname = argv.hostname ?? 'localhost';\n remoteOpts.port = argv.port ?? (isMobile ? 4723 : 4444);\n remoteOpts.path = argv.path ?? '/';\n }\n\n const browser = await remote(remoteOpts as unknown as Capabilities.WebdriverIOConfig);\n\n if (argv.url) {\n await browser.url(argv.url);\n }\n\n const opts = browser.options as Capabilities.WebdriverIOConfig;\n await writeSession(sessionName, {\n sessionId: browser.sessionId,\n hostname: opts?.hostname || 'localhost',\n port: opts?.port || 4444,\n capabilities: { ...capabilities, ...browser.capabilities as Record<string, unknown> },\n created: new Date().toISOString(),\n url: argv.url || '',\n }, sessionsDir);\n\n console.log(`Session \"${sessionName}\" started.`);\n}\n","import os from 'node:os';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport type { AttachOptions } from 'webdriverio';\nimport type { ArgumentsCamelCase } from 'yargs';\n\nexport interface SessionMetadata {\n sessionId: string\n hostname: string\n port: number\n capabilities: Record<string, unknown>\n created: string\n url: string\n}\n\nexport interface SessionEntry {\n name: string\n metadata: SessionMetadata\n}\n\nconst DEFAULT_SESSION_DIR = path.join(os.homedir(), '.wdio-x', 'sessions');\n\nfunction isEnoent(err: unknown): boolean {\n return err instanceof Error && 'code' in err && (err as NodeJS.ErrnoException).code === 'ENOENT';\n}\n\n/**\n * Build attach options from session metadata for use with webdriverio's `attach()`.\n */\nexport function buildAttachOptions(meta: SessionMetadata): AttachOptions {\n return {\n sessionId: meta.sessionId,\n capabilities: meta.capabilities as WebdriverIO.Capabilities,\n options: {\n hostname: meta.hostname,\n port: meta.port,\n logLevel: process.env.WDIO_LOG_LEVEL ?? 'error',\n },\n } as AttachOptions;\n}\n\n/**\n * Returns the session directory path. Defaults to ~/.wdio-x/sessions/.\n */\nexport function getSessionDir (baseDir?: string): string {\n return baseDir ?? DEFAULT_SESSION_DIR;\n}\n\n/**\n * Returns the file path for a session's metadata JSON file.\n */\nexport function getSessionPath (name: string, baseDir?: string): string {\n return path.join(getSessionDir(baseDir), `${name}.json`);\n}\n\n/**\n * Returns the file path for a session's refs JSON file.\n */\nexport function getRefsPath (name: string, baseDir?: string): string {\n return path.join(getSessionDir(baseDir), `${name}.refs.json`);\n}\n\n/**\n * Writes session metadata to disk, creating the directory if needed.\n */\nexport async function writeSession (\n name: string,\n metadata: SessionMetadata,\n baseDir?: string,\n): Promise<void> {\n const dir = getSessionDir(baseDir);\n await fs.mkdir(dir, { recursive: true });\n const filePath = getSessionPath(name, baseDir);\n await fs.writeFile(filePath, JSON.stringify(metadata, null, 2));\n}\n\n/**\n * Reads session metadata from disk. Returns null if the session file does not exist.\n */\nexport async function readSession (\n name: string,\n baseDir?: string,\n): Promise<SessionMetadata | null> {\n const filePath = getSessionPath(name, baseDir);\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n return JSON.parse(content) as SessionMetadata;\n } catch (err: unknown) {\n if (isEnoent(err)) {\n return null;\n }\n throw err;\n }\n}\n\n/**\n * Deletes both the .json and .refs.json files for a session.\n * Does not throw if the files do not exist.\n */\nexport async function deleteSessionFiles (\n name: string,\n baseDir?: string,\n): Promise<void> {\n const sessionPath = getSessionPath(name, baseDir);\n const refsPath = getRefsPath(name, baseDir);\n await Promise.all([\n fs.rm(sessionPath, { force: true }),\n fs.rm(refsPath, { force: true }),\n ]);\n}\n\n/**\n * Lists all sessions in the session directory.\n * Only considers .json files (excludes .refs.json).\n * Returns an empty array if the directory does not exist.\n */\nexport async function listSessions (baseDir?: string): Promise<SessionEntry[]> {\n const dir = getSessionDir(baseDir);\n let files: string[];\n try {\n files = await fs.readdir(dir);\n } catch (err: unknown) {\n if (isEnoent(err)) {\n return [];\n }\n throw err;\n }\n\n const sessionFiles = files.filter(\n (f) => f.endsWith('.json') && !f.endsWith('.refs.json'),\n );\n\n return await Promise.all(\n sessionFiles.map(async (f) => {\n const name = f.replace(/\\.json$/, '');\n const metadata = await readSession(name, baseDir);\n return { name, metadata: metadata! };\n }),\n );\n}\n\ninterface SessionArgs {\n session: string\n _sessionsDir?: string\n}\n\n/**\n * Wraps a command handler that requires an active session.\n * Reads the session from disk and exits early with an error if not found.\n */\nexport function withSession<T extends SessionArgs>(\n fn: (argv: ArgumentsCamelCase<T>, meta: SessionMetadata, sessionsDir: string) => Promise<void>,\n): (argv: ArgumentsCamelCase<T>) => Promise<void> {\n return async function handler(argv: ArgumentsCamelCase<T>): Promise<void> {\n const sessionName = argv.session as string;\n const sessionsDir = (argv._sessionsDir as string) || getSessionDir();\n\n const meta = await readSession(sessionName, sessionsDir);\n if (!meta) {\n console.error(`Error: No active session [${sessionName}]. Run wdiox open <url> first.`);\n return;\n }\n\n await fn(argv, meta, sessionsDir);\n };\n}\n","import type { ArgumentsCamelCase } from 'yargs';\nimport { attach } from 'webdriverio';\n\nimport { deleteSessionFiles, buildAttachOptions, withSession } from '../session.js';\n\nexport const command = ['close', 'stop'];\nexport const desc = 'Close the current session';\n\ninterface CloseArgs {\n session: string\n _sessionsDir?: string\n}\n\nexport const handler = withSession<CloseArgs>(async (argv: ArgumentsCamelCase<CloseArgs>, meta, sessionsDir) => {\n const sessionName = argv.session as string;\n\n try {\n const browser = await attach(buildAttachOptions(meta));\n await browser.deleteSession();\n } catch {\n // Session may already be dead - clean up anyway\n }\n\n await deleteSessionFiles(sessionName, sessionsDir);\n console.log(`Session \"${sessionName}\" closed.`);\n});\n","import type { ArgumentsCamelCase, Argv } from 'yargs';\nimport { attach } from 'webdriverio';\n\nimport { getRefsPath, buildAttachOptions, withSession } from '../session.js';\nimport { writeRefs, type RefMap } from '../refs.js';\nimport { getInteractableBrowserElements, getMobileVisibleElements } from '@wdio/mcp/snapshot';\nimport {\n formatBrowserElement,\n formatMobileElement,\n} from '../format.js';\n\nexport const command = 'snapshot';\nexport const desc = 'Capture interactive elements on the page or screen';\n\nexport const builder = (yargs: Argv) => {\n return yargs\n .option('visible', {\n type: 'boolean',\n default: true,\n describe: 'Only show elements in viewport',\n });\n};\n\ninterface SnapshotArgs {\n session: string\n visible: boolean\n _sessionsDir?: string\n}\n\nexport const handler = withSession<SnapshotArgs>(async (argv: ArgumentsCamelCase<SnapshotArgs>, meta, sessionsDir) => {\n const sessionName = argv.session as string;\n const browser = await attach(buildAttachOptions(meta));\n\n const isMobile = browser.isAndroid || browser.isIOS;\n const refs: RefMap = {};\n\n if (isMobile) {\n const platform = browser.isIOS ? 'ios' : 'android';\n const elements = await getMobileVisibleElements(browser, platform);\n const filtered = argv.visible\n ? elements.filter(el => el.isInViewport)\n : elements;\n\n const appName = (meta.capabilities['appium:app'] as string) || 'unknown';\n console.log(`\\n App: ${appName}\\n`);\n\n filtered.forEach((el, i) => {\n const ref = `e${i + 1}`;\n console.log(formatMobileElement(ref, {\n tagName: el.tagName,\n text: el.text,\n selector: el.selector,\n accessibilityId: el.accessibilityId,\n resourceId: el.resourceId,\n }));\n refs[ref] = {\n selector: el.selector,\n tagName: el.tagName,\n text: el.text,\n };\n });\n\n console.log(`\\n ${filtered.length} elements - ${sessionName} session\\n`);\n } else {\n const elements = await getInteractableBrowserElements(browser);\n const filtered = argv.visible\n ? elements.filter(el => el.isInViewport)\n : elements;\n\n const currentUrl = await browser.getUrl();\n console.log(`\\n Page: ${currentUrl}\\n`);\n\n filtered.forEach((el, i) => {\n const ref = `e${i + 1}`;\n console.log(formatBrowserElement(ref, el));\n refs[ref] = {\n selector: el.selector,\n tagName: el.tagName,\n text: el.name || '',\n };\n });\n\n console.log(`\\n ${filtered.length} elements - ${sessionName} session\\n`);\n }\n\n await writeRefs(getRefsPath(sessionName, sessionsDir), refs);\n});\n","import fs from 'node:fs/promises';\n\nexport interface RefEntry {\n selector?: string\n tagName: string\n text?: string\n placeholder?: string\n [key: string]: unknown\n}\n\nexport type RefMap = Record<string, RefEntry>;\n\nfunction isEnoent (err: unknown): boolean {\n return err instanceof Error && 'code' in err && (err as NodeJS.ErrnoException).code === 'ENOENT';\n}\n\n/**\n * Writes a ref map to disk as JSON.\n */\nexport async function writeRefs (refsPath: string, refs: RefMap): Promise<void> {\n await fs.writeFile(refsPath, JSON.stringify(refs, null, 2), 'utf-8');\n}\n\n/**\n * Reads a ref map from disk. Returns null if the file doesn't exist.\n */\nexport async function readRefs (refsPath: string): Promise<RefMap | null> {\n try {\n const content = await fs.readFile(refsPath, 'utf-8');\n return JSON.parse(content) as RefMap;\n } catch (err: unknown) {\n if (isEnoent(err)) {\n return null;\n }\n throw err;\n }\n}\n\n/**\n * Look up a ref by key: reads refs file, finds the entry, resolves its selector.\n * Logs appropriate error messages and returns null on failure.\n */\nexport async function lookupRef (\n refsPath: string,\n refKey: string,\n): Promise<{ ref: RefEntry; selector: string } | null> {\n const refs = await readRefs(refsPath);\n if (!refs) {\n console.error('Error: No snapshot taken. Run wdiox snapshot first.');\n return null;\n }\n\n const ref = refs[refKey];\n if (!ref) {\n const available = Object.keys(refs);\n const range = available.length > 0\n ? `Available: ${available[0]}-${available[available.length - 1]}`\n : 'No refs available';\n console.error(`Error: ${refKey} not found in snapshot. ${range}`);\n return null;\n }\n\n if (!ref.selector) {\n console.error(`Error: Could not resolve selector for ${refKey}`);\n return null;\n }\n\n return { ref, selector: ref.selector };\n}\n","export interface BrowserElementFormatInput {\n tagName: string\n role?: string\n type?: string\n name?: string\n href?: string\n selector: string\n}\n\nexport interface MobileElementFormatInput {\n tagName: string\n text?: string\n selector: string\n accessibilityId?: string\n resourceId?: string\n}\n\nexport interface SessionListEntry {\n name: string\n browser: string\n url: string\n status: string\n}\n\nfunction truncate(str: string, max = 80): string {\n return str.length <= max ? str : str.slice(0, max - 3) + '...';\n}\n\nexport function formatBrowserElement(ref: string, el: BrowserElementFormatInput): string {\n const tag = (el.role && el.role !== el.tagName ? el.role : el.tagName) + (el.type ? `[${el.type}]` : '');\n const desc = [el.name && `\"${truncate(el.name)}\"`, el.href && `-> ${truncate(el.href)}`].filter(Boolean).join(' ');\n return [ref.padEnd(4), tag, desc, el.selector].filter(Boolean).join(' ');\n}\n\nexport function formatMobileElement(ref: string, el: MobileElementFormatInput): string {\n const selector = el.accessibilityId ? `[accessibility-id: ${el.accessibilityId}]`\n : el.resourceId ? `[resource-id: ${el.resourceId}]`\n : truncate(el.selector);\n const parts = [ref.padEnd(4), el.tagName.padEnd(28), el.text && `\"${truncate(el.text)}\"`, selector];\n return parts.filter(Boolean).join(' ');\n}\n\nexport function formatSessionList(entries: SessionListEntry[]): string {\n if (entries.length === 0) return 'No active sessions.';\n\n const cols = (['name', 'browser', 'url', 'status'] as const).map((key) => ({\n key,\n header: key.toUpperCase(),\n width: Math.max(key.length, ...entries.map((e) => e[key].length)),\n }));\n\n const row = (e: SessionListEntry) => cols.map((c) => e[c.key].padEnd(c.width)).join(' ');\n const header = cols.map((c) => c.header.padEnd(c.width)).join(' ');\n\n return [header, ...entries.map(row)].join('\\n');\n}\n","import type { ArgumentsCamelCase, Argv } from 'yargs';\nimport { attach } from 'webdriverio';\n\nimport { buildAttachOptions, getRefsPath, withSession } from '../session.js';\nimport { lookupRef } from '../refs.js';\n\nexport const command = 'click <ref>';\nexport const desc = 'Click an element by snapshot reference (e.g., e1)';\n\nexport const builder = (yargs: Argv) => {\n return yargs.positional('ref', {\n type: 'string',\n describe: 'Element reference from snapshot (e.g., e1, a3)',\n });\n};\n\ninterface ClickArgs {\n ref: string\n session: string\n _sessionsDir?: string\n}\n\nexport const handler = withSession<ClickArgs>(async (argv: ArgumentsCamelCase<ClickArgs>, meta, sessionsDir) => {\n const sessionName = argv.session as string;\n const refKey = argv.ref as string;\n const result = await lookupRef(getRefsPath(sessionName, sessionsDir), refKey);\n if (!result) {\n return;\n }\n\n const browser = await attach(buildAttachOptions(meta));\n\n try {\n await browser.$(result.selector).click();\n console.log(`Clicked ${refKey} (${result.ref.selector})`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Error clicking ${refKey}: ${msg}`);\n }\n});\n","import type { ArgumentsCamelCase, Argv } from 'yargs';\nimport { attach } from 'webdriverio';\n\nimport { getRefsPath, buildAttachOptions, withSession } from '../session.js';\nimport { lookupRef } from '../refs.js';\n\nexport const command = ['type <ref> <text>', 'fill <ref> <text>'];\nexport const desc = 'Clear and type text into an input element by snapshot reference';\n\nexport const builder = (yargs: Argv) => {\n return yargs\n .positional('ref', {\n type: 'string',\n describe: 'Element reference from snapshot (e.g., e1)',\n })\n .positional('text', {\n type: 'string',\n describe: 'Text to type',\n });\n};\n\ninterface FillArgs {\n ref: string\n text: string\n session: string\n _sessionsDir?: string\n}\n\nexport const handler = withSession<FillArgs>(async (argv: ArgumentsCamelCase<FillArgs>, meta, sessionsDir) => {\n const sessionName = argv.session as string;\n const refKey = argv.ref as string;\n const result = await lookupRef(getRefsPath(sessionName, sessionsDir), refKey);\n if (!result) {\n return;\n }\n\n const browser = await attach(buildAttachOptions(meta));\n\n try {\n const element = await browser.$(result.selector);\n await element.clearValue();\n await element.addValue(argv.text as string);\n console.log(`Filled ${refKey} with \"${argv.text}\"`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Error: ${refKey} not found on page — the page may have changed. Run wdiox snapshot to refresh.\\n${msg}`);\n }\n});\n","import type { ArgumentsCamelCase, Argv } from 'yargs';\nimport { attach } from 'webdriverio';\n\nimport { buildAttachOptions, withSession } from '../session.js';\n\nexport const command = 'screenshot [path]';\nexport const desc = 'Save a screenshot of the current page or screen';\n\nexport const builder = (yargs: Argv) => {\n return yargs.positional('path', {\n type: 'string',\n describe: 'File path to save screenshot (default: ./screenshot-<timestamp>.png)',\n });\n};\n\ninterface ScreenshotArgs {\n path?: string\n session: string\n _sessionsDir?: string\n}\n\nexport const handler = withSession<ScreenshotArgs>(async (argv: ArgumentsCamelCase<ScreenshotArgs>, meta) => {\n const browser = await attach(buildAttachOptions(meta));\n\n const filePath = (argv.path as string) ||\n `screenshot-${new Date().toISOString().replace(/[:.]/g, '-')}.png`;\n\n await browser.saveScreenshot(filePath);\n console.log(`Screenshot saved to ${filePath}`);\n});\n","import type { ArgumentsCamelCase } from 'yargs';\nimport { attach } from 'webdriverio';\n\nimport { listSessions, getSessionDir, deleteSessionFiles, buildAttachOptions } from '../session.js';\nimport { formatSessionList, type SessionListEntry } from '../format.js';\n\nexport const command = ['session-list', 'ls'];\nexport const desc = 'List all active sessions';\n\ninterface SessionListArgs {\n _sessionsDir?: string\n}\n\nexport async function handler (argv: ArgumentsCamelCase<SessionListArgs>) {\n const sessionsDir = (argv._sessionsDir as string) || getSessionDir();\n const sessions = await listSessions(sessionsDir);\n\n if (sessions.length === 0) {\n console.log('No active sessions.');\n return;\n }\n\n const entries: SessionListEntry[] = [];\n\n for (const session of sessions) {\n let status = 'active';\n try {\n const browser = await attach(buildAttachOptions(session.metadata));\n await browser.getPageSource();\n } catch {\n status = 'expired';\n await deleteSessionFiles(session.name, sessionsDir);\n }\n\n const caps = session.metadata.capabilities;\n const browserName = (caps.browserName as string)\n || (caps.platformName as string)\n || 'unknown';\n entries.push({\n name: session.name,\n browser: browserName,\n url: session.metadata.url,\n status,\n });\n }\n\n console.log(formatSessionList(entries));\n}\n"],"mappings":"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","names":["handler","yargs","command","desc","handler","attach","command","desc","handler","attach","builder","command","desc","handler","attach","fs","isEnoent","desc","command","desc","builder","yargs","handler","attach","builder","command","desc","handler","attach","command","desc","builder","yargs","handler","attach","builder","command","desc","handler","attach","command","desc","builder","yargs","handler","attach","builder","command","desc","handler","attach","command","desc","builder","yargs","handler","attach","command","desc","handler","attach","command","desc","handler","attach"]}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "webdriverio-execute",
3
+ "version": "0.1.0",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/Winify/webdriverio-execute.git"
7
+ },
8
+ "description": "Interactive browser and app CLI for developers using WebdriverIO",
9
+ "author": "Vince Graics <wince17@gmail.com>",
10
+ "homepage": "https://github.com/Winify/webdriverio-execute",
11
+ "license": "MIT",
12
+ "bin": {
13
+ "wdiox": "./build/cli.js"
14
+ },
15
+ "engines": {
16
+ "node": ">=18.20.0"
17
+ },
18
+ "keywords": [
19
+ "webdriver",
20
+ "webdriverio",
21
+ "wdio",
22
+ "cli",
23
+ "browser",
24
+ "automation"
25
+ ],
26
+ "bugs": {
27
+ "url": "https://github.com/Winify/webdriverio-execute/issues"
28
+ },
29
+ "type": "module",
30
+ "files": [
31
+ "build",
32
+ "README.md"
33
+ ],
34
+ "types": "./dist/index.d.ts",
35
+ "scripts": {
36
+ "bundle": "rimraf build && tsup",
37
+ "postbundle": "pnpm pack",
38
+ "dev": "tsup --watch",
39
+ "test": "vitest run",
40
+ "typecheck": "tsc --noEmit",
41
+ "lint": "eslint --fix && pnpm run typecheck",
42
+ "link": "pnpm run bundle && pnpm i && pnpm link --global"
43
+ },
44
+ "dependencies": {
45
+ "@wdio/mcp": "file:/Users/vince/webdriverio/mcp/wdio-mcp-2.2.1.tgz",
46
+ "@wdio/types": "^9.24.0",
47
+ "webdriverio": "^9.24.0",
48
+ "yargs": "^17.7.2"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^24.10.13",
52
+ "@types/yargs": "^17.0.24",
53
+ "@wdio/eslint": "^0.1.3",
54
+ "eslint": "^9.39.2",
55
+ "husky": "^9.1.7",
56
+ "lint-staged": "^16.2.7",
57
+ "release-it": "^19.2.4",
58
+ "rimraf": "^6.1.2",
59
+ "tsup": "^8.0.0",
60
+ "typescript": "^5.0.0",
61
+ "vitest": "^2.0.0"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public"
65
+ },
66
+ "packageManager": "pnpm@10.26.2"
67
+ }