storyboard-bridge 0.9.13 → 0.9.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/cdp_flow.py +54 -2
  2. package/package.json +1 -1
package/cdp_flow.py CHANGED
@@ -348,6 +348,12 @@ def load_flow(path, cli_vars):
348
348
  steps.append(("waitfor", sub(raw["xpath"])))
349
349
  elif do == "goto":
350
350
  steps.append(("goto", sub(raw["url"])))
351
+ elif do == "read":
352
+ steps.append(("read", sub(raw["xpath"]), raw.get("var", "")))
353
+ elif do == "ask":
354
+ steps.append(("ask", sub(raw["prompt"]), raw.get("var", "")))
355
+ elif do == "drag":
356
+ steps.append(("drag", sub(raw["xpath"]), raw.get("to", "right"), raw.get("steps", 28), raw.get("duration", 0.6)))
351
357
  else:
352
358
  raise RuntimeError(f"flow step {i}: unknown 'do': {do!r}")
353
359
 
@@ -470,15 +476,19 @@ def main():
470
476
  json.dump({"tab_id": target_id, "context_id": ctx_id}, f)
471
477
  print(f"emitted tab/context ids to {args.emit}")
472
478
 
479
+ step_vars = {}
473
480
  for i, (kind, val) in enumerate(steps, 1):
474
481
  if kind == "type":
475
482
  shown = "***" if (len(val) > 2 and val[2]) else val[1]
476
483
  label = f"{val[0]} -> {shown}"
477
484
  else:
478
- label = val if isinstance(val, str) else " -> ".join(val)
485
+ label = val if isinstance(val, str) else " -> ".join(val[:1] if isinstance(val, tuple) else val)
479
486
  print(f"[{i}/{len(steps)}] {kind}: {label}")
480
487
  if kind == "type":
481
- step_type(page, val[0], val[1])
488
+ text_to_type = val[1]
489
+ for vname, vval in step_vars.items():
490
+ text_to_type = text_to_type.replace("{" + vname + "}", vval)
491
+ step_type(page, val[0], text_to_type)
482
492
  elif kind == "click":
483
493
  note = step_click(page, val)
484
494
  print(f" {note}")
@@ -490,6 +500,48 @@ def main():
490
500
  step_waitfor(page, val, timeout)
491
501
  elif kind == "goto":
492
502
  step_goto(page, val, timeout)
503
+ elif kind == "read":
504
+ xpath, var = val[0], val[1]
505
+ result = page.eval_js(f"document.evaluate({json.dumps(xpath)}, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue?.textContent || ''")
506
+ step_vars[var] = result
507
+ print(f" {var} = '{result[:50]}'")
508
+ elif kind == "ask":
509
+ prompt, var = val[0], val[1]
510
+ # Substitute variables in the prompt
511
+ ask_prompt = prompt
512
+ for vname, vval in step_vars.items():
513
+ ask_prompt = ask_prompt.replace("{" + vname + "}", vval)
514
+ try:
515
+ result = subprocess.run(["claude", "-p", ask_prompt], capture_output=True, text=True, timeout=30).stdout.strip()
516
+ step_vars[var] = result
517
+ print(f" {var} = '{result[:50]}'")
518
+ except Exception as e:
519
+ print(f" (ask failed: {e})", file=sys.stderr)
520
+ step_vars[var] = ""
521
+ elif kind == "drag":
522
+ xpath, direction, steps_count, duration = val[0], val[1], int(val[2]), float(val[3])
523
+ # Simple drag implementation: find element and simulate drag to the right
524
+ box = page.eval_js(f"""
525
+ (function() {{
526
+ const el = document.evaluate({json.dumps(xpath)}, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
527
+ if (!el) return null;
528
+ const rect = el.getBoundingClientRect();
529
+ return {{ x: rect.left + rect.width/2, y: rect.top + rect.height/2, width: rect.width }};
530
+ }})()
531
+ """)
532
+ if box:
533
+ start_x, start_y = int(box["x"]), int(box["y"])
534
+ end_x = start_x + (200 if direction == "right" else -200)
535
+ step_duration = duration / steps_count
536
+ for step_i in range(steps_count):
537
+ x = start_x + (end_x - start_x) * (step_i / steps_count)
538
+ page.cdp.call("Input.dispatchMouseEvent", {
539
+ "type": "mouseMoved",
540
+ "x": int(x),
541
+ "y": start_y,
542
+ })
543
+ time.sleep(step_duration)
544
+ print(f" dragged {direction}")
493
545
  time.sleep(delay)
494
546
  print("flow complete")
495
547
  if spawned_ctx and temp.get("dispose"):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storyboard-bridge",
3
- "version": "0.9.13",
3
+ "version": "0.9.15",
4
4
  "description": "Desktop bridge that powers a hosted Storyboard AI webapp with your own local Claude Code / Gemini CLI login (no API key).",
5
5
  "type": "module",
6
6
  "bin": {