@hasna/browser 0.2.3 → 0.2.4

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/dist/cli/index.js CHANGED
@@ -19370,7 +19370,8 @@ __export(exports_login_scripts, {
19370
19370
  listJobs: () => listJobs,
19371
19371
  getJob: () => getJob,
19372
19372
  deleteScript: () => deleteScript,
19373
- createUsestableScript: () => createUsestableScript
19373
+ createScriptFromJSON: () => createScriptFromJSON,
19374
+ createScriptFromFile: () => createScriptFromFile
19374
19375
  });
19375
19376
  import { randomUUID as randomUUID10 } from "crypto";
19376
19377
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync8, readdirSync as readdirSync4 } from "fs";
@@ -19648,56 +19649,30 @@ function runExtractStep(step, vars) {
19648
19649
  } catch {}
19649
19650
  }
19650
19651
  }
19651
- function createUsestableScript(email) {
19652
+ function createScriptFromJSON(jsonStr) {
19653
+ const parsed = JSON.parse(jsonStr);
19654
+ if (!parsed.name)
19655
+ throw new Error("Script must have a 'name' field");
19656
+ if (!parsed.domain)
19657
+ throw new Error("Script must have a 'domain' field");
19658
+ if (!parsed.steps || !Array.isArray(parsed.steps) || parsed.steps.length === 0) {
19659
+ throw new Error("Script must have a non-empty 'steps' array");
19660
+ }
19652
19661
  return {
19653
- name: "usestable",
19654
- domain: "dashboard.usestable.com",
19655
- description: "Login to Stable via magic link (email \u2192 Gmail \u2192 click link)",
19656
- variables: { email },
19657
- steps: [
19658
- { type: "browser", action: "navigate", url: "https://dashboard.usestable.com/login", description: "Go to login page" },
19659
- { type: "browser", action: "type", selector: "input", value: "{{email}}", description: "Enter email" },
19660
- { type: "browser", action: "click_text", text: "Continue", description: "Click Continue" },
19661
- { type: "wait", seconds: 1, description: "Wait for login options" },
19662
- { type: "browser", action: "click_text", text: "Send login link", description: "Request magic link" },
19663
- { type: "wait", seconds: 5, description: "Wait for email delivery" },
19664
- {
19665
- type: "connector",
19666
- connector: "gmail",
19667
- args: ["search", "from:authenticate.usestable.com newer_than:5m", "-n", "1"],
19668
- description: "Search Gmail for magic link email"
19669
- },
19670
- {
19671
- type: "extract",
19672
- pattern: "id:\\s*([a-f0-9]+)",
19673
- save_as: "email_id",
19674
- description: "Extract email ID from search results"
19675
- },
19676
- {
19677
- type: "connector",
19678
- connector: "gmail",
19679
- args: ["messages", "read", "{{email_id}}", "--body", "--html"],
19680
- save_as: "email_body",
19681
- description: "Read the magic link email"
19682
- },
19683
- {
19684
- type: "extract",
19685
- pattern: "href='(https://dashboard\\.usestable\\.com/login\\?[^']+)'",
19686
- check: "email_body",
19687
- save_as: "magic_link",
19688
- description: "Extract magic link URL from email HTML"
19689
- },
19690
- { type: "browser", action: "navigate", url: "{{magic_link}}", description: "Open magic link" },
19691
- { type: "wait", seconds: 2, description: "Wait for page to load" },
19692
- { type: "browser", action: "type", selector: "input", value: "{{email}}", description: "Re-enter email" },
19693
- { type: "browser", action: "click_text", text: "Login", description: "Click Login" },
19694
- { type: "browser", action: "wait_for_navigation", timeout: 15000, description: "Wait for redirect to dashboard" },
19695
- { type: "save_state", name: "usestable", description: "Save auth state" }
19696
- ],
19662
+ name: parsed.name,
19663
+ domain: parsed.domain,
19664
+ description: parsed.description ?? "",
19665
+ variables: parsed.variables ?? {},
19666
+ steps: parsed.steps,
19697
19667
  created_at: new Date().toISOString(),
19698
19668
  updated_at: new Date().toISOString()
19699
19669
  };
19700
19670
  }
19671
+ function createScriptFromFile(filePath) {
19672
+ if (!existsSync6(filePath))
19673
+ throw new Error(`File not found: ${filePath}`);
19674
+ return createScriptFromJSON(readFileSync4(filePath, "utf8"));
19675
+ }
19701
19676
  var activeJobs;
19702
19677
  var init_login_scripts = __esm(() => {
19703
19678
  init_schema();
@@ -45331,14 +45306,19 @@ scriptCmd.command("list").description("List saved login scripts").option("--json
45331
45306
  });
45332
45307
  }
45333
45308
  });
45334
- scriptCmd.command("create-usestable").description("Create the usestable.com login script (magic link via Gmail)").option("--email <email>", "Email address", "andrei@hasna.com").action(async (opts) => {
45335
- const { createUsestableScript: createUsestableScript2, saveScript: saveScript2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
45336
- const script = createUsestableScript2(opts.email);
45337
- const path = saveScript2(script);
45338
- console.log(chalk.green(`\u2713 Script saved: ${script.name}`));
45339
- console.log(chalk.gray(` Path: ${path}`));
45340
- console.log(chalk.gray(` Steps: ${script.steps.length}`));
45341
- console.log(chalk.gray(` Run with: browser script run usestable`));
45309
+ scriptCmd.command("create <file>").description("Create a script from a JSON file (see docs for schema)").action(async (file) => {
45310
+ const { createScriptFromFile: createScriptFromFile2, saveScript: saveScript2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
45311
+ try {
45312
+ const script = createScriptFromFile2(file);
45313
+ const path = saveScript2(script);
45314
+ console.log(chalk.green(`\u2713 Script saved: ${script.name}`));
45315
+ console.log(chalk.gray(` Domain: ${script.domain}`));
45316
+ console.log(chalk.gray(` Steps: ${script.steps.length}`));
45317
+ console.log(chalk.gray(` Path: ${path}`));
45318
+ console.log(chalk.gray(` Run with: browser script run ${script.name}`));
45319
+ } catch (err2) {
45320
+ console.log(chalk.red(`Error: ${err2 instanceof Error ? err2.message : String(err2)}`));
45321
+ }
45342
45322
  });
45343
45323
  scriptCmd.command("show <name>").description("Show script details").action(async (name) => {
45344
45324
  const { loadScript: loadScript2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
@@ -80,5 +80,6 @@ export declare function runScript(script: LoginScript, page: Page, overrides?: R
80
80
  * Poll with getJob(jobId) for progress.
81
81
  */
82
82
  export declare function runScriptAsync(script: LoginScript, page: Page, overrides?: Record<string, string>): string;
83
- export declare function createUsestableScript(email: string): LoginScript;
83
+ export declare function createScriptFromJSON(jsonStr: string): LoginScript;
84
+ export declare function createScriptFromFile(filePath: string): LoginScript;
84
85
  //# sourceMappingURL=login-scripts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"login-scripts.d.ts","sourceRoot":"","sources":["../../src/lib/login-scripts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"login-scripts.d.ts","sourceRoot":"","sources":["../../src/lib/login-scripts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAIvC,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,YAAY,CAAC;AAEjG,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,YAAY,GAAG,qBAAqB,GAAG,eAAe,GAAG,UAAU,CAAC;IAC7G,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,wBAAwB,EAAE,MAAM,CAAC;IACjC,SAAS,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,IAAI,GAAG,QAAQ,GAAG,SAAS,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjJ,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;CACpB;AAID,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAEtD;AAED,wBAAgB,QAAQ,IAAI,SAAS,EAAE,CAEtC;AAUD,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAOtD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAI3D;AAED,wBAAgB,WAAW,IAAI,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAY1G;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMlD;AAgBD,wBAAsB,SAAS,CAC7B,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,IAAI,EACV,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACtC,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,eAAe,CAAC,CA0G1B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,IAAI,EACV,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACrC,MAAM,CAqBR;AA8ID,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAgBjE;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAGlE"}
package/dist/mcp/index.js CHANGED
@@ -17127,7 +17127,8 @@ __export(exports_login_scripts, {
17127
17127
  listJobs: () => listJobs,
17128
17128
  getJob: () => getJob,
17129
17129
  deleteScript: () => deleteScript,
17130
- createUsestableScript: () => createUsestableScript
17130
+ createScriptFromJSON: () => createScriptFromJSON,
17131
+ createScriptFromFile: () => createScriptFromFile
17131
17132
  });
17132
17133
  import { randomUUID as randomUUID12 } from "crypto";
17133
17134
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync10, readdirSync as readdirSync5 } from "fs";
@@ -17405,56 +17406,30 @@ function runExtractStep(step, vars) {
17405
17406
  } catch {}
17406
17407
  }
17407
17408
  }
17408
- function createUsestableScript(email) {
17409
+ function createScriptFromJSON(jsonStr) {
17410
+ const parsed = JSON.parse(jsonStr);
17411
+ if (!parsed.name)
17412
+ throw new Error("Script must have a 'name' field");
17413
+ if (!parsed.domain)
17414
+ throw new Error("Script must have a 'domain' field");
17415
+ if (!parsed.steps || !Array.isArray(parsed.steps) || parsed.steps.length === 0) {
17416
+ throw new Error("Script must have a non-empty 'steps' array");
17417
+ }
17409
17418
  return {
17410
- name: "usestable",
17411
- domain: "dashboard.usestable.com",
17412
- description: "Login to Stable via magic link (email \u2192 Gmail \u2192 click link)",
17413
- variables: { email },
17414
- steps: [
17415
- { type: "browser", action: "navigate", url: "https://dashboard.usestable.com/login", description: "Go to login page" },
17416
- { type: "browser", action: "type", selector: "input", value: "{{email}}", description: "Enter email" },
17417
- { type: "browser", action: "click_text", text: "Continue", description: "Click Continue" },
17418
- { type: "wait", seconds: 1, description: "Wait for login options" },
17419
- { type: "browser", action: "click_text", text: "Send login link", description: "Request magic link" },
17420
- { type: "wait", seconds: 5, description: "Wait for email delivery" },
17421
- {
17422
- type: "connector",
17423
- connector: "gmail",
17424
- args: ["search", "from:authenticate.usestable.com newer_than:5m", "-n", "1"],
17425
- description: "Search Gmail for magic link email"
17426
- },
17427
- {
17428
- type: "extract",
17429
- pattern: "id:\\s*([a-f0-9]+)",
17430
- save_as: "email_id",
17431
- description: "Extract email ID from search results"
17432
- },
17433
- {
17434
- type: "connector",
17435
- connector: "gmail",
17436
- args: ["messages", "read", "{{email_id}}", "--body", "--html"],
17437
- save_as: "email_body",
17438
- description: "Read the magic link email"
17439
- },
17440
- {
17441
- type: "extract",
17442
- pattern: "href='(https://dashboard\\.usestable\\.com/login\\?[^']+)'",
17443
- check: "email_body",
17444
- save_as: "magic_link",
17445
- description: "Extract magic link URL from email HTML"
17446
- },
17447
- { type: "browser", action: "navigate", url: "{{magic_link}}", description: "Open magic link" },
17448
- { type: "wait", seconds: 2, description: "Wait for page to load" },
17449
- { type: "browser", action: "type", selector: "input", value: "{{email}}", description: "Re-enter email" },
17450
- { type: "browser", action: "click_text", text: "Login", description: "Click Login" },
17451
- { type: "browser", action: "wait_for_navigation", timeout: 15000, description: "Wait for redirect to dashboard" },
17452
- { type: "save_state", name: "usestable", description: "Save auth state" }
17453
- ],
17419
+ name: parsed.name,
17420
+ domain: parsed.domain,
17421
+ description: parsed.description ?? "",
17422
+ variables: parsed.variables ?? {},
17423
+ steps: parsed.steps,
17454
17424
  created_at: new Date().toISOString(),
17455
17425
  updated_at: new Date().toISOString()
17456
17426
  };
17457
17427
  }
17428
+ function createScriptFromFile(filePath) {
17429
+ if (!existsSync6(filePath))
17430
+ throw new Error(`File not found: ${filePath}`);
17431
+ return createScriptFromJSON(readFileSync4(filePath, "utf8"));
17432
+ }
17458
17433
  var activeJobs;
17459
17434
  var init_login_scripts = __esm(() => {
17460
17435
  init_schema();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/browser",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "General-purpose browser agent toolkit — Playwright, Chrome DevTools Protocol, Lightpanda with auto engine selection. CLI + MCP + REST + SDK.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",