@hasna/browser 0.2.1 → 0.2.3

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
@@ -19363,14 +19363,24 @@ var init_dist = __esm(() => {
19363
19363
  var exports_login_scripts = {};
19364
19364
  __export(exports_login_scripts, {
19365
19365
  saveScript: () => saveScript,
19366
+ runScriptAsync: () => runScriptAsync,
19366
19367
  runScript: () => runScript,
19367
19368
  loadScript: () => loadScript,
19368
19369
  listScripts: () => listScripts,
19370
+ listJobs: () => listJobs,
19371
+ getJob: () => getJob,
19369
19372
  deleteScript: () => deleteScript,
19370
19373
  createUsestableScript: () => createUsestableScript
19371
19374
  });
19375
+ import { randomUUID as randomUUID10 } from "crypto";
19372
19376
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync8, readdirSync as readdirSync4 } from "fs";
19373
19377
  import { join as join9 } from "path";
19378
+ function getJob(jobId) {
19379
+ return activeJobs.get(jobId) ?? null;
19380
+ }
19381
+ function listJobs() {
19382
+ return Array.from(activeJobs.values());
19383
+ }
19374
19384
  function getScriptsDir() {
19375
19385
  const dir = join9(getDataDir(), "scripts");
19376
19386
  mkdirSync8(dir, { recursive: true });
@@ -19417,15 +19427,34 @@ function interpolate(template, vars) {
19417
19427
  return vars[key] ?? match;
19418
19428
  });
19419
19429
  }
19420
- async function runScript(script, page, overrides = {}) {
19430
+ function stepDescription(step) {
19431
+ return step.description ?? `${step.type}${step.action ? `:${step.action}` : ""}${step.connector ? `:${step.connector}` : ""}`;
19432
+ }
19433
+ async function runScript(script, page, overrides = {}, jobId) {
19421
19434
  const t0 = Date.now();
19422
19435
  const vars = { ...script.variables, ...overrides };
19423
19436
  const errors = [];
19424
19437
  let executed = 0;
19425
19438
  let failed = 0;
19439
+ const job = jobId && activeJobs.has(jobId) ? activeJobs.get(jobId) : {
19440
+ id: jobId ?? randomUUID10(),
19441
+ script_name: script.name,
19442
+ status: "running",
19443
+ current_step: 0,
19444
+ total_steps: script.steps.length,
19445
+ current_step_description: "Starting...",
19446
+ steps_log: [],
19447
+ started_at: new Date().toISOString()
19448
+ };
19449
+ activeJobs.set(job.id, job);
19426
19450
  for (let i = 0;i < script.steps.length; i++) {
19427
19451
  const step = script.steps[i];
19452
+ const desc = stepDescription(step);
19428
19453
  executed++;
19454
+ job.current_step = i + 1;
19455
+ job.current_step_description = desc;
19456
+ job.steps_log.push({ step: i + 1, type: step.type, description: desc, status: "running" });
19457
+ const stepStart = Date.now();
19429
19458
  try {
19430
19459
  switch (step.type) {
19431
19460
  case "browser":
@@ -19462,15 +19491,22 @@ async function runScript(script, page, overrides = {}) {
19462
19491
  break;
19463
19492
  }
19464
19493
  }
19494
+ const logEntry = job.steps_log[job.steps_log.length - 1];
19495
+ logEntry.status = "ok";
19496
+ logEntry.duration_ms = Date.now() - stepStart;
19465
19497
  } catch (err) {
19466
19498
  failed++;
19467
19499
  const msg = `Step ${i + 1} (${step.type}/${step.action ?? step.connector ?? ""}): ${err instanceof Error ? err.message : String(err)}`;
19468
19500
  errors.push(msg);
19501
+ const logEntry = job.steps_log[job.steps_log.length - 1];
19502
+ logEntry.status = "failed";
19503
+ logEntry.error = err instanceof Error ? err.message : String(err);
19504
+ logEntry.duration_ms = Date.now() - stepStart;
19469
19505
  if (step.type === "browser" && step.action === "navigate")
19470
19506
  break;
19471
19507
  }
19472
19508
  }
19473
- return {
19509
+ const result = {
19474
19510
  success: failed === 0,
19475
19511
  steps_executed: executed,
19476
19512
  steps_failed: failed,
@@ -19478,6 +19514,28 @@ async function runScript(script, page, overrides = {}) {
19478
19514
  errors,
19479
19515
  duration_ms: Date.now() - t0
19480
19516
  };
19517
+ job.status = failed === 0 ? "completed" : "failed";
19518
+ job.result = result;
19519
+ return result;
19520
+ }
19521
+ function runScriptAsync(script, page, overrides = {}) {
19522
+ const jobId = randomUUID10();
19523
+ const job = {
19524
+ id: jobId,
19525
+ script_name: script.name,
19526
+ status: "running",
19527
+ current_step: 0,
19528
+ total_steps: script.steps.length,
19529
+ current_step_description: "Starting...",
19530
+ steps_log: [],
19531
+ started_at: new Date().toISOString()
19532
+ };
19533
+ activeJobs.set(jobId, job);
19534
+ runScript(script, page, overrides, jobId).catch((err) => {
19535
+ job.status = "failed";
19536
+ job.current_step_description = `Fatal error: ${err instanceof Error ? err.message : String(err)}`;
19537
+ });
19538
+ return jobId;
19481
19539
  }
19482
19540
  async function runBrowserStep(step, page, vars) {
19483
19541
  const action = step.action;
@@ -19565,6 +19623,9 @@ async function runConnectorStep(step, vars) {
19565
19623
  vars[step.save_as] = result.stdout;
19566
19624
  }
19567
19625
  }
19626
+ function decodeHtmlEntities(str) {
19627
+ return str.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&#x27;/g, "'");
19628
+ }
19568
19629
  function runExtractStep(step, vars) {
19569
19630
  const saveTo = step.save_as ?? "extracted";
19570
19631
  if (step.pattern) {
@@ -19572,7 +19633,7 @@ function runExtractStep(step, vars) {
19572
19633
  const regex = new RegExp(step.pattern);
19573
19634
  const match = regex.exec(source);
19574
19635
  if (match) {
19575
- vars[saveTo] = match[1] ?? match[0];
19636
+ vars[saveTo] = decodeHtmlEntities(match[1] ?? match[0]);
19576
19637
  }
19577
19638
  }
19578
19639
  if (step.json_path) {
@@ -19637,8 +19698,10 @@ function createUsestableScript(email) {
19637
19698
  updated_at: new Date().toISOString()
19638
19699
  };
19639
19700
  }
19701
+ var activeJobs;
19640
19702
  var init_login_scripts = __esm(() => {
19641
19703
  init_schema();
19704
+ activeJobs = new Map;
19642
19705
  });
19643
19706
 
19644
19707
  // src/lib/daemon-client.ts
@@ -23767,7 +23830,7 @@ __export(exports_downloads, {
23767
23830
  deleteDownload: () => deleteDownload,
23768
23831
  cleanStaleDownloads: () => cleanStaleDownloads
23769
23832
  });
23770
- import { randomUUID as randomUUID10 } from "crypto";
23833
+ import { randomUUID as randomUUID11 } from "crypto";
23771
23834
  import { join as join11, basename, extname } from "path";
23772
23835
  import { mkdirSync as mkdirSync9, existsSync as existsSync8, readdirSync as readdirSync5, statSync as statSync2, unlinkSync as unlinkSync2, copyFileSync, writeFileSync as writeFileSync4, readFileSync as readFileSync6 } from "fs";
23773
23836
  import { homedir as homedir10 } from "os";
@@ -23788,7 +23851,7 @@ function metaPath(filePath) {
23788
23851
  }
23789
23852
  function saveToDownloads(buffer, filename, opts) {
23790
23853
  const dir = getDownloadsDir(opts?.sessionId);
23791
- const id = randomUUID10();
23854
+ const id = randomUUID11();
23792
23855
  const ext = extname(filename) || "";
23793
23856
  const stem = basename(filename, ext);
23794
23857
  const uniqueName = `${stem}-${id.slice(0, 8)}${ext}`;
@@ -24196,10 +24259,10 @@ __export(exports_workflows, {
24196
24259
  getWorkflow: () => getWorkflow,
24197
24260
  deleteWorkflow: () => deleteWorkflow
24198
24261
  });
24199
- import { randomUUID as randomUUID11 } from "crypto";
24262
+ import { randomUUID as randomUUID12 } from "crypto";
24200
24263
  function saveWorkflow(data) {
24201
24264
  const db = getDatabase();
24202
- const id = randomUUID11();
24265
+ const id = randomUUID12();
24203
24266
  db.prepare("INSERT OR REPLACE INTO workflows (id, name, description, steps, start_url) VALUES (?, ?, ?, ?, ?)").run(id, data.name, data.description ?? null, JSON.stringify(data.steps), data.startUrl ?? null);
24204
24267
  return getWorkflow(id);
24205
24268
  }
@@ -24359,10 +24422,10 @@ __export(exports_auth_flow, {
24359
24422
  getAuthFlow: () => getAuthFlow,
24360
24423
  deleteAuthFlow: () => deleteAuthFlow
24361
24424
  });
24362
- import { randomUUID as randomUUID12 } from "crypto";
24425
+ import { randomUUID as randomUUID13 } from "crypto";
24363
24426
  function saveAuthFlow(data) {
24364
24427
  const db = getDatabase();
24365
- const id = randomUUID12();
24428
+ const id = randomUUID13();
24366
24429
  db.prepare("INSERT OR REPLACE INTO auth_flows (id, name, domain, recording_id, storage_state_path) VALUES (?, ?, ?, ?, ?)").run(id, data.name, data.domain, data.recordingId ?? null, data.storageStatePath ?? null);
24367
24430
  return getAuthFlow(id);
24368
24431
  }
@@ -24542,13 +24605,13 @@ __export(exports_datasets, {
24542
24605
  exportDataset: () => exportDataset,
24543
24606
  deleteDataset: () => deleteDataset
24544
24607
  });
24545
- import { randomUUID as randomUUID13 } from "crypto";
24608
+ import { randomUUID as randomUUID14 } from "crypto";
24546
24609
  import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync11 } from "fs";
24547
24610
  import { join as join13 } from "path";
24548
24611
  import { homedir as homedir12 } from "os";
24549
24612
  function saveDataset(data) {
24550
24613
  const db = getDatabase();
24551
- const id = randomUUID13();
24614
+ const id = randomUUID14();
24552
24615
  const existing = db.query("SELECT id FROM datasets WHERE name = ?").get(data.name);
24553
24616
  if (existing) {
24554
24617
  db.prepare("UPDATE datasets SET data = ?, row_count = ?, source_url = ?, schema = ?, last_refresh = datetime('now'), updated_at = datetime('now') WHERE name = ?").run(JSON.stringify(data.rows), data.rows.length, data.sourceUrl ?? null, data.schema ? JSON.stringify(data.schema) : null, data.name);
@@ -29349,7 +29412,7 @@ import { Database as Database3 } from "bun:sqlite";
29349
29412
  import { mkdirSync as mkdirSync13 } from "fs";
29350
29413
  import { join as join15, dirname as dirname6 } from "path";
29351
29414
  import { homedir as homedir14 } from "os";
29352
- import { randomUUID as randomUUID14 } from "crypto";
29415
+ import { randomUUID as randomUUID15 } from "crypto";
29353
29416
  import { mkdirSync as mkdirSync23, copyFileSync as copyFileSync3, statSync as statSync3 } from "fs";
29354
29417
  import { join as join34 } from "path";
29355
29418
  import { homedir as homedir33 } from "os";
@@ -29729,7 +29792,7 @@ function guessMimeType(name) {
29729
29792
  function sendMessage(opts) {
29730
29793
  const db2 = getDb();
29731
29794
  const explicitSession = opts.session_id && opts.session_id.trim().length > 0 ? opts.session_id : undefined;
29732
- const sessionId = explicitSession ?? (opts.space ? `space:${opts.space}` : `${[opts.from, opts.to].sort().join("-")}-${randomUUID14().slice(0, 8)}`);
29795
+ const sessionId = explicitSession ?? (opts.space ? `space:${opts.space}` : `${[opts.from, opts.to].sort().join("-")}-${randomUUID15().slice(0, 8)}`);
29733
29796
  const metadata = opts.metadata ? JSON.stringify(opts.metadata) : null;
29734
29797
  const normalizedPriority = opts.priority === "low" || opts.priority === "normal" || opts.priority === "high" || opts.priority === "urgent" ? opts.priority : "normal";
29735
29798
  const blocking = opts.blocking ? 1 : 0;
@@ -41418,7 +41481,7 @@ __export(exports_cron_manager, {
41418
41481
  deleteCronJob: () => deleteCronJob,
41419
41482
  createCronJob: () => createCronJob
41420
41483
  });
41421
- import { randomUUID as randomUUID15 } from "crypto";
41484
+ import { randomUUID as randomUUID16 } from "crypto";
41422
41485
  function ensureCronTable() {
41423
41486
  const db2 = getDatabase();
41424
41487
  db2.exec(`
@@ -41447,7 +41510,7 @@ function ensureCronTable() {
41447
41510
  function createCronJob(schedule, task, name) {
41448
41511
  ensureCronTable();
41449
41512
  const db2 = getDatabase();
41450
- const id = randomUUID15();
41513
+ const id = randomUUID16();
41451
41514
  db2.prepare(`
41452
41515
  INSERT INTO cron_jobs (id, name, schedule, task_json, enabled)
41453
41516
  VALUES (?, ?, ?, ?, 1)
@@ -41505,7 +41568,7 @@ function getCronEvents(jobId, limit = 10) {
41505
41568
  async function executeCronJob(job) {
41506
41569
  ensureCronTable();
41507
41570
  const db2 = getDatabase();
41508
- const eventId = randomUUID15();
41571
+ const eventId = randomUUID16();
41509
41572
  const startedAt = new Date().toISOString();
41510
41573
  db2.prepare("INSERT INTO cron_events (id, job_id, started_at) VALUES (?, ?, ?)").run(eventId, job.id, startedAt);
41511
41574
  try {
@@ -41596,7 +41659,7 @@ __export(exports_url_watcher, {
41596
41659
  deleteWatchJob: () => deleteWatchJob,
41597
41660
  createWatchJob: () => createWatchJob
41598
41661
  });
41599
- import { randomUUID as randomUUID16 } from "crypto";
41662
+ import { randomUUID as randomUUID17 } from "crypto";
41600
41663
  import { createHash } from "crypto";
41601
41664
  function ensureWatchTables() {
41602
41665
  const db2 = getDatabase();
@@ -41628,7 +41691,7 @@ function ensureWatchTables() {
41628
41691
  function createWatchJob(url, schedule, opts) {
41629
41692
  ensureWatchTables();
41630
41693
  const db2 = getDatabase();
41631
- const id = randomUUID16();
41694
+ const id = randomUUID17();
41632
41695
  db2.prepare(`
41633
41696
  INSERT INTO watch_jobs (id, name, url, schedule, selector, extract_schema, enabled)
41634
41697
  VALUES (?, ?, ?, ?, ?, ?, 1)
@@ -41664,7 +41727,7 @@ function getWatchEvents(watchId, limit = 20) {
41664
41727
  async function checkWatchJob(job) {
41665
41728
  ensureWatchTables();
41666
41729
  const db2 = getDatabase();
41667
- const eventId = randomUUID16();
41730
+ const eventId = randomUUID17();
41668
41731
  const checkedAt = new Date().toISOString();
41669
41732
  let newContent = "";
41670
41733
  try {
@@ -43571,13 +43634,13 @@ var init_mcp = __esm(async () => {
43571
43634
  return err(e);
43572
43635
  }
43573
43636
  });
43574
- server.tool("browser_script_run", "Run a saved login script \u2014 multi-step workflow combining browser actions + connector calls (e.g. magic link login via Gmail). One command, fully automated.", {
43637
+ server.tool("browser_script_run", "Run a saved login script asynchronously. Returns a job_id immediately \u2014 poll with browser_script_status for step-by-step progress. Combines browser actions + connector calls (e.g. magic link login via Gmail).", {
43575
43638
  name: exports_external.string().describe("Script name (e.g. 'usestable')"),
43576
43639
  session_id: exports_external.string().optional(),
43577
43640
  variables: exports_external.record(exports_external.string()).optional().describe("Override script variables (e.g. {email: 'foo@bar.com'})")
43578
43641
  }, async ({ name, session_id, variables }) => {
43579
43642
  try {
43580
- const { loadScript: loadScript2, runScript: runScript2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
43643
+ const { loadScript: loadScript2, runScriptAsync: runScriptAsync2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
43581
43644
  const script = loadScript2(name);
43582
43645
  if (!script)
43583
43646
  return err(new Error(`Script '${name}' not found. Use browser_script_list to see available scripts.`));
@@ -43587,12 +43650,29 @@ var init_mcp = __esm(async () => {
43587
43650
  sid = resolveSessionId(session_id);
43588
43651
  page = getSessionPage(sid);
43589
43652
  } else {
43590
- const result2 = await createSession2({ headless: true });
43591
- sid = result2.session.id;
43592
- page = result2.page;
43653
+ const result = await createSession2({ headless: true });
43654
+ sid = result.session.id;
43655
+ page = result.page;
43593
43656
  }
43594
- const result = await runScript2(script, page, variables ?? {});
43595
- return json({ ...result, session_id: sid, url: page.url() });
43657
+ const jobId = runScriptAsync2(script, page, variables ?? {});
43658
+ return json({ job_id: jobId, session_id: sid, script: name, total_steps: script.steps.length, message: "Script running in background. Poll with browser_script_status for progress." });
43659
+ } catch (e) {
43660
+ return err(e);
43661
+ }
43662
+ });
43663
+ server.tool("browser_script_status", "Check the progress of a running login script. Shows current step, step-by-step log with durations, and final result when complete.", { job_id: exports_external.string() }, async ({ job_id }) => {
43664
+ try {
43665
+ const { getJob: getJob2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
43666
+ const job = getJob2(job_id);
43667
+ if (!job)
43668
+ return err(new Error(`Job '${job_id}' not found`));
43669
+ return json({
43670
+ status: job.status,
43671
+ progress: `${job.current_step}/${job.total_steps}`,
43672
+ current_step: job.current_step_description,
43673
+ steps_log: job.steps_log,
43674
+ result: job.result ?? undefined
43675
+ });
43596
43676
  } catch (e) {
43597
43677
  return err(e);
43598
43678
  }
@@ -44362,10 +44442,10 @@ __export(exports_snapshots, {
44362
44442
  deleteSnapshot: () => deleteSnapshot,
44363
44443
  createSnapshot: () => createSnapshot
44364
44444
  });
44365
- import { randomUUID as randomUUID17 } from "crypto";
44445
+ import { randomUUID as randomUUID18 } from "crypto";
44366
44446
  function createSnapshot(data) {
44367
44447
  const db2 = getDatabase();
44368
- const id = randomUUID17();
44448
+ const id = randomUUID18();
44369
44449
  db2.prepare("INSERT INTO snapshots (id, session_id, url, title, html, screenshot_path) VALUES (?, ?, ?, ?, ?, ?)").run(id, data.session_id, data.url, data.title ?? null, data.html ?? null, data.screenshot_path ?? null);
44370
44450
  return getSnapshot(id);
44371
44451
  }
@@ -45,6 +45,26 @@ export interface ScriptRunResult {
45
45
  errors: string[];
46
46
  duration_ms: number;
47
47
  }
48
+ export interface ScriptJob {
49
+ id: string;
50
+ script_name: string;
51
+ status: "running" | "completed" | "failed";
52
+ current_step: number;
53
+ total_steps: number;
54
+ current_step_description: string;
55
+ steps_log: Array<{
56
+ step: number;
57
+ type: string;
58
+ description: string;
59
+ status: "ok" | "failed" | "running";
60
+ duration_ms?: number;
61
+ error?: string;
62
+ }>;
63
+ result?: ScriptRunResult;
64
+ started_at: string;
65
+ }
66
+ export declare function getJob(jobId: string): ScriptJob | null;
67
+ export declare function listJobs(): ScriptJob[];
48
68
  export declare function saveScript(script: LoginScript): string;
49
69
  export declare function loadScript(name: string): LoginScript | null;
50
70
  export declare function listScripts(): Array<{
@@ -54,6 +74,11 @@ export declare function listScripts(): Array<{
54
74
  steps: number;
55
75
  }>;
56
76
  export declare function deleteScript(name: string): boolean;
57
- export declare function runScript(script: LoginScript, page: Page, overrides?: Record<string, string>): Promise<ScriptRunResult>;
77
+ export declare function runScript(script: LoginScript, page: Page, overrides?: Record<string, string>, jobId?: string): Promise<ScriptRunResult>;
78
+ /**
79
+ * Run a script asynchronously — returns immediately with a job ID.
80
+ * Poll with getJob(jobId) for progress.
81
+ */
82
+ export declare function runScriptAsync(script: LoginScript, page: Page, overrides?: Record<string, string>): string;
58
83
  export declare function createUsestableScript(email: string): LoginScript;
59
84
  //# 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":"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;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;AAYD,wBAAsB,SAAS,CAC7B,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,IAAI,EACV,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACrC,OAAO,CAAC,eAAe,CAAC,CAmE1B;AAmID,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CA2ChE"}
1
+ {"version":3,"file":"login-scripts.d.ts","sourceRoot":"","sources":["../../src/lib/login-scripts.ts"],"names":[],"mappings":"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"}
package/dist/mcp/index.js CHANGED
@@ -17120,14 +17120,24 @@ var init_dist = __esm(() => {
17120
17120
  var exports_login_scripts = {};
17121
17121
  __export(exports_login_scripts, {
17122
17122
  saveScript: () => saveScript,
17123
+ runScriptAsync: () => runScriptAsync,
17123
17124
  runScript: () => runScript,
17124
17125
  loadScript: () => loadScript,
17125
17126
  listScripts: () => listScripts,
17127
+ listJobs: () => listJobs,
17128
+ getJob: () => getJob,
17126
17129
  deleteScript: () => deleteScript,
17127
17130
  createUsestableScript: () => createUsestableScript
17128
17131
  });
17132
+ import { randomUUID as randomUUID12 } from "crypto";
17129
17133
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync10, readdirSync as readdirSync5 } from "fs";
17130
17134
  import { join as join10 } from "path";
17135
+ function getJob(jobId) {
17136
+ return activeJobs.get(jobId) ?? null;
17137
+ }
17138
+ function listJobs() {
17139
+ return Array.from(activeJobs.values());
17140
+ }
17131
17141
  function getScriptsDir() {
17132
17142
  const dir = join10(getDataDir(), "scripts");
17133
17143
  mkdirSync10(dir, { recursive: true });
@@ -17174,15 +17184,34 @@ function interpolate(template, vars) {
17174
17184
  return vars[key] ?? match;
17175
17185
  });
17176
17186
  }
17177
- async function runScript(script, page, overrides = {}) {
17187
+ function stepDescription(step) {
17188
+ return step.description ?? `${step.type}${step.action ? `:${step.action}` : ""}${step.connector ? `:${step.connector}` : ""}`;
17189
+ }
17190
+ async function runScript(script, page, overrides = {}, jobId) {
17178
17191
  const t0 = Date.now();
17179
17192
  const vars = { ...script.variables, ...overrides };
17180
17193
  const errors2 = [];
17181
17194
  let executed = 0;
17182
17195
  let failed = 0;
17196
+ const job = jobId && activeJobs.has(jobId) ? activeJobs.get(jobId) : {
17197
+ id: jobId ?? randomUUID12(),
17198
+ script_name: script.name,
17199
+ status: "running",
17200
+ current_step: 0,
17201
+ total_steps: script.steps.length,
17202
+ current_step_description: "Starting...",
17203
+ steps_log: [],
17204
+ started_at: new Date().toISOString()
17205
+ };
17206
+ activeJobs.set(job.id, job);
17183
17207
  for (let i = 0;i < script.steps.length; i++) {
17184
17208
  const step = script.steps[i];
17209
+ const desc = stepDescription(step);
17185
17210
  executed++;
17211
+ job.current_step = i + 1;
17212
+ job.current_step_description = desc;
17213
+ job.steps_log.push({ step: i + 1, type: step.type, description: desc, status: "running" });
17214
+ const stepStart = Date.now();
17186
17215
  try {
17187
17216
  switch (step.type) {
17188
17217
  case "browser":
@@ -17219,15 +17248,22 @@ async function runScript(script, page, overrides = {}) {
17219
17248
  break;
17220
17249
  }
17221
17250
  }
17251
+ const logEntry = job.steps_log[job.steps_log.length - 1];
17252
+ logEntry.status = "ok";
17253
+ logEntry.duration_ms = Date.now() - stepStart;
17222
17254
  } catch (err) {
17223
17255
  failed++;
17224
17256
  const msg = `Step ${i + 1} (${step.type}/${step.action ?? step.connector ?? ""}): ${err instanceof Error ? err.message : String(err)}`;
17225
17257
  errors2.push(msg);
17258
+ const logEntry = job.steps_log[job.steps_log.length - 1];
17259
+ logEntry.status = "failed";
17260
+ logEntry.error = err instanceof Error ? err.message : String(err);
17261
+ logEntry.duration_ms = Date.now() - stepStart;
17226
17262
  if (step.type === "browser" && step.action === "navigate")
17227
17263
  break;
17228
17264
  }
17229
17265
  }
17230
- return {
17266
+ const result = {
17231
17267
  success: failed === 0,
17232
17268
  steps_executed: executed,
17233
17269
  steps_failed: failed,
@@ -17235,6 +17271,28 @@ async function runScript(script, page, overrides = {}) {
17235
17271
  errors: errors2,
17236
17272
  duration_ms: Date.now() - t0
17237
17273
  };
17274
+ job.status = failed === 0 ? "completed" : "failed";
17275
+ job.result = result;
17276
+ return result;
17277
+ }
17278
+ function runScriptAsync(script, page, overrides = {}) {
17279
+ const jobId = randomUUID12();
17280
+ const job = {
17281
+ id: jobId,
17282
+ script_name: script.name,
17283
+ status: "running",
17284
+ current_step: 0,
17285
+ total_steps: script.steps.length,
17286
+ current_step_description: "Starting...",
17287
+ steps_log: [],
17288
+ started_at: new Date().toISOString()
17289
+ };
17290
+ activeJobs.set(jobId, job);
17291
+ runScript(script, page, overrides, jobId).catch((err) => {
17292
+ job.status = "failed";
17293
+ job.current_step_description = `Fatal error: ${err instanceof Error ? err.message : String(err)}`;
17294
+ });
17295
+ return jobId;
17238
17296
  }
17239
17297
  async function runBrowserStep(step, page, vars) {
17240
17298
  const action = step.action;
@@ -17322,6 +17380,9 @@ async function runConnectorStep(step, vars) {
17322
17380
  vars[step.save_as] = result.stdout;
17323
17381
  }
17324
17382
  }
17383
+ function decodeHtmlEntities(str) {
17384
+ return str.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&#x27;/g, "'");
17385
+ }
17325
17386
  function runExtractStep(step, vars) {
17326
17387
  const saveTo = step.save_as ?? "extracted";
17327
17388
  if (step.pattern) {
@@ -17329,7 +17390,7 @@ function runExtractStep(step, vars) {
17329
17390
  const regex = new RegExp(step.pattern);
17330
17391
  const match = regex.exec(source);
17331
17392
  if (match) {
17332
- vars[saveTo] = match[1] ?? match[0];
17393
+ vars[saveTo] = decodeHtmlEntities(match[1] ?? match[0]);
17333
17394
  }
17334
17395
  }
17335
17396
  if (step.json_path) {
@@ -17394,8 +17455,10 @@ function createUsestableScript(email) {
17394
17455
  updated_at: new Date().toISOString()
17395
17456
  };
17396
17457
  }
17458
+ var activeJobs;
17397
17459
  var init_login_scripts = __esm(() => {
17398
17460
  init_schema();
17461
+ activeJobs = new Map;
17399
17462
  });
17400
17463
 
17401
17464
  // src/lib/api-detector.ts
@@ -17404,7 +17467,7 @@ __export(exports_api_detector, {
17404
17467
  listDiscoveredAPIs: () => listDiscoveredAPIs,
17405
17468
  detectAPIs: () => detectAPIs
17406
17469
  });
17407
- import { randomUUID as randomUUID12 } from "crypto";
17470
+ import { randomUUID as randomUUID13 } from "crypto";
17408
17471
  function detectAPIs(sessionId) {
17409
17472
  const db = getDatabase();
17410
17473
  const requests = db.query(`SELECT method, url, status_code, response_headers, body_size
@@ -17440,7 +17503,7 @@ function detectAPIs(sessionId) {
17440
17503
  }
17441
17504
  const apis = Array.from(seen.values());
17442
17505
  for (const api of apis) {
17443
- const id = randomUUID12();
17506
+ const id = randomUUID13();
17444
17507
  db.prepare("INSERT OR IGNORE INTO api_endpoints (id, session_id, url, method, status_code, content_type) VALUES (?, ?, ?, ?, ?, ?)").run(id, sessionId, api.url, api.method, api.status_code, api.content_type);
17445
17508
  }
17446
17509
  return apis;
@@ -17545,13 +17608,13 @@ __export(exports_datasets, {
17545
17608
  exportDataset: () => exportDataset,
17546
17609
  deleteDataset: () => deleteDataset
17547
17610
  });
17548
- import { randomUUID as randomUUID13 } from "crypto";
17611
+ import { randomUUID as randomUUID14 } from "crypto";
17549
17612
  import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync11 } from "fs";
17550
17613
  import { join as join11 } from "path";
17551
17614
  import { homedir as homedir10 } from "os";
17552
17615
  function saveDataset(data) {
17553
17616
  const db = getDatabase();
17554
- const id = randomUUID13();
17617
+ const id = randomUUID14();
17555
17618
  const existing = db.query("SELECT id FROM datasets WHERE name = ?").get(data.name);
17556
17619
  if (existing) {
17557
17620
  db.prepare("UPDATE datasets SET data = ?, row_count = ?, source_url = ?, schema = ?, last_refresh = datetime('now'), updated_at = datetime('now') WHERE name = ?").run(JSON.stringify(data.rows), data.rows.length, data.sourceUrl ?? null, data.schema ? JSON.stringify(data.schema) : null, data.name);
@@ -22442,7 +22505,7 @@ import { Database as Database3 } from "bun:sqlite";
22442
22505
  import { mkdirSync as mkdirSync13 } from "fs";
22443
22506
  import { join as join14, dirname as dirname6 } from "path";
22444
22507
  import { homedir as homedir13 } from "os";
22445
- import { randomUUID as randomUUID14 } from "crypto";
22508
+ import { randomUUID as randomUUID15 } from "crypto";
22446
22509
  import { mkdirSync as mkdirSync23, copyFileSync as copyFileSync3, statSync as statSync3 } from "fs";
22447
22510
  import { join as join34 } from "path";
22448
22511
  import { homedir as homedir33 } from "os";
@@ -22822,7 +22885,7 @@ function guessMimeType(name) {
22822
22885
  function sendMessage(opts) {
22823
22886
  const db2 = getDb();
22824
22887
  const explicitSession = opts.session_id && opts.session_id.trim().length > 0 ? opts.session_id : undefined;
22825
- const sessionId = explicitSession ?? (opts.space ? `space:${opts.space}` : `${[opts.from, opts.to].sort().join("-")}-${randomUUID14().slice(0, 8)}`);
22888
+ const sessionId = explicitSession ?? (opts.space ? `space:${opts.space}` : `${[opts.from, opts.to].sort().join("-")}-${randomUUID15().slice(0, 8)}`);
22826
22889
  const metadata = opts.metadata ? JSON.stringify(opts.metadata) : null;
22827
22890
  const normalizedPriority = opts.priority === "low" || opts.priority === "normal" || opts.priority === "high" || opts.priority === "urgent" ? opts.priority : "normal";
22828
22891
  const blocking = opts.blocking ? 1 : 0;
@@ -34511,7 +34574,7 @@ __export(exports_cron_manager, {
34511
34574
  deleteCronJob: () => deleteCronJob,
34512
34575
  createCronJob: () => createCronJob
34513
34576
  });
34514
- import { randomUUID as randomUUID15 } from "crypto";
34577
+ import { randomUUID as randomUUID16 } from "crypto";
34515
34578
  function ensureCronTable() {
34516
34579
  const db2 = getDatabase();
34517
34580
  db2.exec(`
@@ -34540,7 +34603,7 @@ function ensureCronTable() {
34540
34603
  function createCronJob(schedule, task, name) {
34541
34604
  ensureCronTable();
34542
34605
  const db2 = getDatabase();
34543
- const id = randomUUID15();
34606
+ const id = randomUUID16();
34544
34607
  db2.prepare(`
34545
34608
  INSERT INTO cron_jobs (id, name, schedule, task_json, enabled)
34546
34609
  VALUES (?, ?, ?, ?, 1)
@@ -34598,7 +34661,7 @@ function getCronEvents(jobId, limit = 10) {
34598
34661
  async function executeCronJob(job) {
34599
34662
  ensureCronTable();
34600
34663
  const db2 = getDatabase();
34601
- const eventId = randomUUID15();
34664
+ const eventId = randomUUID16();
34602
34665
  const startedAt = new Date().toISOString();
34603
34666
  db2.prepare("INSERT INTO cron_events (id, job_id, started_at) VALUES (?, ?, ?)").run(eventId, job.id, startedAt);
34604
34667
  try {
@@ -34689,7 +34752,7 @@ __export(exports_url_watcher, {
34689
34752
  deleteWatchJob: () => deleteWatchJob,
34690
34753
  createWatchJob: () => createWatchJob
34691
34754
  });
34692
- import { randomUUID as randomUUID16 } from "crypto";
34755
+ import { randomUUID as randomUUID17 } from "crypto";
34693
34756
  import { createHash } from "crypto";
34694
34757
  function ensureWatchTables() {
34695
34758
  const db2 = getDatabase();
@@ -34721,7 +34784,7 @@ function ensureWatchTables() {
34721
34784
  function createWatchJob(url, schedule, opts) {
34722
34785
  ensureWatchTables();
34723
34786
  const db2 = getDatabase();
34724
- const id = randomUUID16();
34787
+ const id = randomUUID17();
34725
34788
  db2.prepare(`
34726
34789
  INSERT INTO watch_jobs (id, name, url, schedule, selector, extract_schema, enabled)
34727
34790
  VALUES (?, ?, ?, ?, ?, ?, 1)
@@ -34757,7 +34820,7 @@ function getWatchEvents(watchId, limit = 20) {
34757
34820
  async function checkWatchJob(job) {
34758
34821
  ensureWatchTables();
34759
34822
  const db2 = getDatabase();
34760
- const eventId = randomUUID16();
34823
+ const eventId = randomUUID17();
34761
34824
  const checkedAt = new Date().toISOString();
34762
34825
  let newContent = "";
34763
34826
  try {
@@ -41384,13 +41447,13 @@ server.tool("browser_profile_delete", "Delete a saved browser profile", { name:
41384
41447
  return err(e);
41385
41448
  }
41386
41449
  });
41387
- server.tool("browser_script_run", "Run a saved login script \u2014 multi-step workflow combining browser actions + connector calls (e.g. magic link login via Gmail). One command, fully automated.", {
41450
+ server.tool("browser_script_run", "Run a saved login script asynchronously. Returns a job_id immediately \u2014 poll with browser_script_status for step-by-step progress. Combines browser actions + connector calls (e.g. magic link login via Gmail).", {
41388
41451
  name: exports_external.string().describe("Script name (e.g. 'usestable')"),
41389
41452
  session_id: exports_external.string().optional(),
41390
41453
  variables: exports_external.record(exports_external.string()).optional().describe("Override script variables (e.g. {email: 'foo@bar.com'})")
41391
41454
  }, async ({ name, session_id, variables }) => {
41392
41455
  try {
41393
- const { loadScript: loadScript2, runScript: runScript2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
41456
+ const { loadScript: loadScript2, runScriptAsync: runScriptAsync2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
41394
41457
  const script = loadScript2(name);
41395
41458
  if (!script)
41396
41459
  return err(new Error(`Script '${name}' not found. Use browser_script_list to see available scripts.`));
@@ -41400,12 +41463,29 @@ server.tool("browser_script_run", "Run a saved login script \u2014 multi-step wo
41400
41463
  sid = resolveSessionId(session_id);
41401
41464
  page = getSessionPage(sid);
41402
41465
  } else {
41403
- const result2 = await createSession2({ headless: true });
41404
- sid = result2.session.id;
41405
- page = result2.page;
41466
+ const result = await createSession2({ headless: true });
41467
+ sid = result.session.id;
41468
+ page = result.page;
41406
41469
  }
41407
- const result = await runScript2(script, page, variables ?? {});
41408
- return json({ ...result, session_id: sid, url: page.url() });
41470
+ const jobId = runScriptAsync2(script, page, variables ?? {});
41471
+ return json({ job_id: jobId, session_id: sid, script: name, total_steps: script.steps.length, message: "Script running in background. Poll with browser_script_status for progress." });
41472
+ } catch (e) {
41473
+ return err(e);
41474
+ }
41475
+ });
41476
+ server.tool("browser_script_status", "Check the progress of a running login script. Shows current step, step-by-step log with durations, and final result when complete.", { job_id: exports_external.string() }, async ({ job_id }) => {
41477
+ try {
41478
+ const { getJob: getJob2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
41479
+ const job = getJob2(job_id);
41480
+ if (!job)
41481
+ return err(new Error(`Job '${job_id}' not found`));
41482
+ return json({
41483
+ status: job.status,
41484
+ progress: `${job.current_step}/${job.total_steps}`,
41485
+ current_step: job.current_step_description,
41486
+ steps_log: job.steps_log,
41487
+ result: job.result ?? undefined
41488
+ });
41409
41489
  } catch (e) {
41410
41490
  return err(e);
41411
41491
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/browser",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
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",