@proagentstore/cli 0.1.21 → 0.1.23

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.
@@ -7,7 +7,7 @@ import { RunnerInputError } from "./errors.js";
7
7
  * matches, hidden checkboxes, and hidden file inputs. Pure over `page`; the
8
8
  * runner passes its clickRobustly helper. The caller handles post-action settle.
9
9
  */
10
- export async function performBrowserAction(page, action, clickRobustly) {
10
+ export async function performBrowserAction(page, action, clickRobustly, resumeFile) {
11
11
  const locate = () => {
12
12
  const role = action.role;
13
13
  let loc = role
@@ -135,9 +135,15 @@ export async function performBrowserAction(page, action, clickRobustly) {
135
135
  // Always attach via Playwright — never a native dialog. Handles both a
136
136
  // direct <input type=file> (set files on it) and a styled "Upload"
137
137
  // button that opens a native chooser (intercept the filechooser).
138
- const file = resolve(String(action.file ?? ""));
139
- if (!file || !existsSync(file))
140
- throw new RunnerInputError("upload requires an existing local file path");
138
+ // The LLM doesn't know the runner's local path, so prefer the résumé the
139
+ // runner already resolved (downloaded from the platform); fall back to an
140
+ // explicit action.file only if it's a real local file.
141
+ const explicit = action.file ? resolve(String(action.file)) : "";
142
+ const file = resumeFile && existsSync(resumeFile)
143
+ ? resumeFile
144
+ : explicit && existsSync(explicit) ? explicit : "";
145
+ if (!file)
146
+ throw new RunnerInputError("no résumé available to upload — upload one in the console (Knowledge → Résumé)");
141
147
  let done = false;
142
148
  // 1. Label-associated input (some forms).
143
149
  if (action.name) {
@@ -1,5 +1,5 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import { existsSync, mkdirSync, rmSync } from "node:fs";
2
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { createRequire } from "node:module";
4
4
  import { basename, dirname, join, resolve } from "node:path";
5
5
  import { pathToFileURL } from "node:url";
@@ -393,6 +393,37 @@ export class LocalRunner {
393
393
  chooser.setFiles(filePath).catch(() => undefined);
394
394
  });
395
395
  }
396
+ resumeFileCache = new Map();
397
+ /**
398
+ * Resolve a résumé reference to a LOCAL file path. A remote runner gets a
399
+ * signed URL (the file lives in the platform's R2) — download it once and
400
+ * cache it. A same-machine run may still pass a local path.
401
+ */
402
+ async resolveResumeFile(ref) {
403
+ if (/^https?:\/\//i.test(ref)) {
404
+ const cached = this.resumeFileCache.get(ref);
405
+ if (cached && existsSync(cached))
406
+ return cached;
407
+ try {
408
+ const res = await fetch(ref);
409
+ if (!res.ok)
410
+ return null;
411
+ const buf = Buffer.from(await res.arrayBuffer());
412
+ const disp = res.headers.get("content-disposition") || "";
413
+ const name = (disp.match(/filename="?([^"]+)"?/i)?.[1] || "resume.pdf").replace(/[^\w.\-]/g, "_");
414
+ const dir = join(this.config.dataDir, "uploads");
415
+ mkdirSync(dir, { recursive: true });
416
+ const dest = join(dir, name);
417
+ writeFileSync(dest, buf);
418
+ this.resumeFileCache.set(ref, dest);
419
+ return dest;
420
+ }
421
+ catch {
422
+ return null;
423
+ }
424
+ }
425
+ return existsSync(resolve(ref)) ? resolve(ref) : null;
426
+ }
396
427
  /** End a takeover session (human finished or gave up). */
397
428
  async endTakeover(taskId) {
398
429
  const session = this.takeovers.get(taskId);
@@ -472,9 +503,14 @@ export class LocalRunner {
472
503
  headless: this.config.headless,
473
504
  acceptDownloads: true,
474
505
  downloadsPath,
506
+ // viewport:null + --start-maximized → the page uses the FULL window size
507
+ // instead of Playwright's cramped 1280x720 default. Without this a
508
+ // reCAPTCHA challenge popup near the edge overflows and gets cut off the
509
+ // right in the takeover view, so it can't be solved.
510
+ viewport: null,
475
511
  // Light anti-detection: drop the most obvious automation tell so fewer
476
512
  // CAPTCHAs trigger in the first place (the human still solves the rest).
477
- args: ["--disable-blink-features=AutomationControlled"],
513
+ args: ["--disable-blink-features=AutomationControlled", "--start-maximized", "--window-size=1512,982"],
478
514
  };
479
515
  // Prefer the real Chrome build (better TLS/fingerprint → fewer CAPTCHAs);
480
516
  // fall back to bundled Chromium if Chrome isn't installed. Disable with
@@ -583,14 +619,18 @@ export class LocalRunner {
583
619
  async browserAct(action, resumePath) {
584
620
  const page = await this.getActivePage();
585
621
  // Arm résumé auto-attach so a file chooser never blocks the flow (see method).
586
- if (resumePath && existsSync(resolve(resumePath))) {
587
- this.applyResumePath = resolve(resumePath);
622
+ // resumePath may be a signed URL (remote runner) or a local path — resolve to
623
+ // a real local file either way.
624
+ if (resumePath) {
625
+ const local = await this.resolveResumeFile(resumePath);
626
+ if (local)
627
+ this.applyResumePath = local;
588
628
  }
589
629
  if (this.applyResumePath)
590
630
  this.armFileAutoAttach(page, this.applyResumePath);
591
631
  // The selector-free action switch lives in browser-actions.ts (kept this
592
632
  // file from ballooning); the post-action settle + result stay here.
593
- await performBrowserAction(page, action, (p, loc) => this.clickRobustly(p, loc));
633
+ await performBrowserAction(page, action, (p, loc) => this.clickRobustly(p, loc), this.applyResumePath);
594
634
  // A click may trigger SPA navigation or open a new tab/popup. Give it a beat
595
635
  // to settle and follow the (possibly new) active page, so the next snapshot
596
636
  // reflects the new state — not the element the brain just clicked.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proagentstore/cli",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "description": "CLI for creating, publishing, and running ProAgentStore agents",
5
5
  "license": "MIT",
6
6
  "type": "module",