bangonit 0.3.0 → 0.3.1

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 (49) hide show
  1. package/README.md +69 -37
  2. package/app/desktopapp/dist/main/index.js +10 -161
  3. package/app/desktopapp/dist/main/ipc.js +0 -20
  4. package/app/desktopapp/dist/main/preload.js +0 -2
  5. package/app/desktopapp/dist/main/tabs.js +0 -10
  6. package/app/desktopapp/dist/shared/args.js +21 -0
  7. package/app/desktopapp/package.json +1 -1
  8. package/app/replay/dist/replay.css +1 -1
  9. package/app/replay/dist/replay.js +20 -20
  10. package/app/webapp/.next/BUILD_ID +1 -1
  11. package/app/webapp/.next/app-build-manifest.json +2 -2
  12. package/app/webapp/.next/app-path-routes-manifest.json +1 -1
  13. package/app/webapp/.next/build-manifest.json +2 -2
  14. package/app/webapp/.next/next-minimal-server.js.nft.json +1 -1
  15. package/app/webapp/.next/next-server.js.nft.json +1 -1
  16. package/app/webapp/.next/prerender-manifest.json +1 -1
  17. package/app/webapp/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/app/webapp/.next/server/app/_not-found.html +1 -1
  19. package/app/webapp/.next/server/app/_not-found.rsc +1 -1
  20. package/app/webapp/.next/server/app/app/page.js +3 -7
  21. package/app/webapp/.next/server/app/app/page_client-reference-manifest.js +1 -1
  22. package/app/webapp/.next/server/app/app.html +1 -1
  23. package/app/webapp/.next/server/app/app.rsc +2 -2
  24. package/app/webapp/.next/server/app/index.html +1 -1
  25. package/app/webapp/.next/server/app/index.rsc +1 -1
  26. package/app/webapp/.next/server/app/page_client-reference-manifest.js +1 -1
  27. package/app/webapp/.next/server/app-paths-manifest.json +2 -2
  28. package/app/webapp/.next/server/chunks/708.js +1 -1
  29. package/app/webapp/.next/server/functions-config-manifest.json +1 -1
  30. package/app/webapp/.next/server/pages/404.html +1 -1
  31. package/app/webapp/.next/server/pages/500.html +1 -1
  32. package/app/webapp/.next/server/server-reference-manifest.json +1 -1
  33. package/app/webapp/.next/static/chunks/app/app/page-0e096497dcb81dae.js +1 -0
  34. package/app/webapp/.next/static/css/{869d3ff23c36c4b5.css → 38219627f55424f2.css} +1 -1
  35. package/app/webapp/.next/trace +2 -2
  36. package/app/webapp/package.json +7 -2
  37. package/app/webapp/src/shared/api/chat.ts +2 -11
  38. package/app/webapp/src/shared/components/AppShell.tsx +21 -1
  39. package/app/webapp/src/shared/components/SessionView.tsx +37 -65
  40. package/app/webapp/src/shared/lib/browser/mouse.ts +1 -1
  41. package/app/webapp/src/shared/lib/browser/recorder.ts +3 -3
  42. package/app/webapp/src/shared/types/global.d.ts +0 -3
  43. package/bin/app/desktopapp/src/shared/args.js +21 -0
  44. package/bin/bangonit.js +259 -96
  45. package/bin/src/cli/bangonit.js +767 -0
  46. package/package.json +6 -4
  47. package/app/webapp/.next/static/chunks/app/app/page-d38c1e48d37def82.js +0 -1
  48. /package/app/webapp/.next/static/{TaLpPsk5rC30wNNcyfUN3 → Qq0OvlQijtcR84Dg9Dgp0}/_buildManifest.js +0 -0
  49. /package/app/webapp/.next/static/{TaLpPsk5rC30wNNcyfUN3 → Qq0OvlQijtcR84Dg9Dgp0}/_ssgManifest.js +0 -0
@@ -20,7 +20,8 @@ function loadProjectPrompt(cwd: string): string {
20
20
  const bangDir = findBangDir(cwd);
21
21
  if (!bangDir) return "";
22
22
  return fs.readFileSync(path.join(bangDir, "system_prompt.md"), "utf-8").trim();
23
- } catch {
23
+ } catch (e) {
24
+ console.error(e);
24
25
  return "";
25
26
  }
26
27
  }
@@ -221,16 +222,6 @@ export function createChatHandler() {
221
222
  }
222
223
  }
223
224
  }),
224
- bash: tool({
225
- description: "Run a shell command. Only for non-browser tasks (file operations, scripts, etc). Do NOT use for browser automation — use the browser tool instead.",
226
- inputSchema: jsonSchema({
227
- type: "object",
228
- properties: {
229
- command: { type: "string", description: "The shell command to run" },
230
- },
231
- required: ["command"],
232
- }),
233
- }),
234
225
  todos: tool({
235
226
  description: "Track progress on multi-step tasks. Call with the FULL list of todos to update, or omit 'todos' to read the current list.",
236
227
  inputSchema: jsonSchema({
@@ -360,6 +360,12 @@ function TestPrompt({
360
360
  }
361
361
  };
362
362
 
363
+ const examples = [
364
+ "Go to example.com and verify the homepage loads with a heading",
365
+ "Navigate to my-app.com/login, sign in with test@example.com / password123, and verify the dashboard loads",
366
+ "Go to localhost:3000, add an item to the cart, proceed to checkout, and verify the order summary",
367
+ ];
368
+
363
369
  return (
364
370
  <div className="absolute inset-0 flex items-center justify-center z-10 bg-zinc-950">
365
371
  <div className="w-full max-w-xl px-6">
@@ -370,7 +376,7 @@ function TestPrompt({
370
376
  value={prompt}
371
377
  onChange={(e) => setPrompt(e.target.value)}
372
378
  onKeyDown={handleKeyDown}
373
- placeholder="Describe what to test, e.g. &quot;Go to example.com and verify the homepage loads&quot;"
379
+ placeholder="Describe what to test in plain English..."
374
380
  className="w-full px-4 py-3 bg-zinc-800 border border-zinc-700 rounded-lg text-sm text-zinc-200 placeholder-zinc-500 resize-none focus:outline-none focus:border-zinc-500 transition-colors"
375
381
  rows={4}
376
382
  />
@@ -385,6 +391,20 @@ function TestPrompt({
385
391
  <p className="text-xs text-zinc-600 mt-2">
386
392
  Press Enter to run. Shift+Enter for a new line.
387
393
  </p>
394
+ <div className="mt-4">
395
+ <p className="text-xs text-zinc-600 mb-2">Examples:</p>
396
+ <div className="flex flex-col gap-1.5">
397
+ {examples.map((ex, i) => (
398
+ <button
399
+ key={i}
400
+ onClick={() => setPrompt(ex)}
401
+ className="text-left text-xs text-zinc-500 hover:text-zinc-300 bg-zinc-900 hover:bg-zinc-800 px-3 py-2 rounded border border-zinc-800 transition-colors"
402
+ >
403
+ {ex}
404
+ </button>
405
+ ))}
406
+ </div>
407
+ </div>
388
408
  </div>
389
409
  </div>
390
410
  );
@@ -51,8 +51,6 @@ export default function SessionView({
51
51
  ]
52
52
  );
53
53
  const [activeTabId, setActiveTabId] = useState(initialActiveTabId ?? 0);
54
- const activeTabIdRef = useRef(activeTabId);
55
- activeTabIdRef.current = activeTabId;
56
54
  const [urlInput, setUrlInput] = useState(() => {
57
55
  const activeTab = initialTabs?.find((t) => t.id === (initialActiveTabId ?? 0));
58
56
  return activeTab?.url || "";
@@ -127,7 +125,8 @@ export default function SessionView({
127
125
  await window.bangonit?.saveReplayData?.({ runDir, data: JSON.stringify(replayData) });
128
126
  await window.bangonit?.generateReplayHtml?.({ runDir });
129
127
  }
130
- } catch (e: any) {
128
+ } catch (e) {
129
+ console.error(e);
131
130
  }
132
131
  }
133
132
 
@@ -171,12 +170,12 @@ export default function SessionView({
171
170
  actions: input?.actions || [],
172
171
  observe: input?.observe,
173
172
  prompts: input?.prompts,
174
- }) : {error: "Error: browser tools not available"};
173
+ }) : { error: "Error: browser tools not available" };
175
174
  sessionRecorderRef.current?.addEvent({ ts: Date.now(), agentId, type: "tool-end", data: { tool: "browser" } });
176
175
  addToolOutput({
177
176
  tool: tc.toolName,
178
177
  toolCallId: tc.toolCallId,
179
- output: stoppedRef.current ? {error: "[Stopped by user]"} : JSON.stringify(raw),
178
+ output: stoppedRef.current ? { error: "[Stopped by user]" } : JSON.stringify(raw),
180
179
  });
181
180
  } catch (err: any) {
182
181
  addToolOutput({
@@ -189,25 +188,6 @@ export default function SessionView({
189
188
  }
190
189
  return;
191
190
  }
192
- // bash
193
- try {
194
- const command = (tc.input as any)?.command;
195
- emit("tool", `bash: ${(command || "").slice(0, 100)}`);
196
- const raw = (await window.bangonit?.bashExec(agentId, command)) || "OK";
197
- addToolOutput({
198
- tool: tc.toolName,
199
- toolCallId: tc.toolCallId,
200
- output: stoppedRef.current ? "[Stopped by user]" : truncateMiddle(raw),
201
- });
202
- } catch (err: any) {
203
- addToolOutput({
204
- tool: tc.toolName,
205
- toolCallId: tc.toolCallId,
206
- ...(stoppedRef.current
207
- ? { output: "[Stopped by user]" }
208
- : { state: "output-error" as const, errorText: err.message }),
209
- });
210
- }
211
191
  },
212
192
  });
213
193
 
@@ -446,27 +426,30 @@ export default function SessionView({
446
426
  if (wcId) {
447
427
  registeredTabsRef.current.add(tabId);
448
428
  window.bangonit?.registerTab(agentId, tabId, wcId, initialUrl);
449
- if (tabId === activeTabIdRef.current) {
429
+ if (tabId === activeTabId) {
450
430
  window.bangonit?.setActiveTab(agentId, tabId);
451
431
  }
452
432
  }
453
- } catch {}
433
+ } catch (e) { console.error(e); }
454
434
  };
455
435
  register();
456
436
  el.addEventListener("did-attach", register);
457
437
  el.addEventListener("close", () => closeTabRef.current(tabId));
458
- // Forward browser console messages to main process + session recorder
459
- el.addEventListener("console-message", (e: any) => {
460
- window.bangonit?.emitConsoleMessage?.({
461
- agentId,
462
- level: e.level,
463
- message: e.message,
464
- url: e.sourceId || "",
465
- line: e.lineNumber || 0,
438
+ // Forward browser console messages to main process + session recorder (once per webview)
439
+ if (!el._consoleListenerAttached) {
440
+ el._consoleListenerAttached = true;
441
+ el.addEventListener("console-message", (e: any) => {
442
+ window.bangonit?.emitConsoleMessage?.({
443
+ agentId,
444
+ level: e.level,
445
+ message: e.message,
446
+ url: e.sourceId || "",
447
+ line: e.lineNumber || 0,
448
+ });
449
+ sessionRecorderRef.current?.addConsoleLog(agentId, e.level, e.message, e.sourceId || "", e.lineNumber || 0);
466
450
  });
467
- sessionRecorderRef.current?.addConsoleLog(agentId, e.level, e.message, e.sourceId || "", e.lineNumber || 0);
468
- });
469
- }, [agentId]);
451
+ }
452
+ }, [agentId, activeTabId]);
470
453
 
471
454
  const formatTime = (s: number) => `${Math.floor(s / 60)}:${(s % 60).toString().padStart(2, "0")}`;
472
455
 
@@ -539,11 +522,10 @@ export default function SessionView({
539
522
 
540
523
  {/* Test result banner */}
541
524
  {testResult && (
542
- <div className={`px-4 py-2 text-sm border-b flex items-center gap-3 ${
543
- testResult.result === "pass"
544
- ? "bg-green-950/30 border-green-900/50"
545
- : "bg-red-950/30 border-red-900/50"
546
- }`}>
525
+ <div className={`px-4 py-2 text-sm border-b flex items-center gap-3 ${testResult.result === "pass"
526
+ ? "bg-green-950/30 border-green-900/50"
527
+ : "bg-red-950/30 border-red-900/50"
528
+ }`}>
547
529
  <span className={`font-medium ${testResult.result === "pass" ? "text-green-400" : "text-red-400"}`}>
548
530
  {testResult.result === "pass" ? "PASSED" : "FAILED"}
549
531
  </span>
@@ -601,11 +583,10 @@ export default function SessionView({
601
583
  setDevtoolsOpen(!devtoolsOpen);
602
584
  }
603
585
  }}
604
- className={`text-xs px-2 py-1 rounded border transition-colors ${
605
- devtoolsOpen
606
- ? "border-blue-600 text-blue-400 bg-blue-950/30"
607
- : "border-zinc-700 hover:border-zinc-500 text-zinc-500 hover:text-zinc-300"
608
- }`}
586
+ className={`text-xs px-2 py-1 rounded border transition-colors ${devtoolsOpen
587
+ ? "border-blue-600 text-blue-400 bg-blue-950/30"
588
+ : "border-zinc-700 hover:border-zinc-500 text-zinc-500 hover:text-zinc-300"
589
+ }`}
609
590
  title="Toggle DevTools"
610
591
  >
611
592
  DevTools
@@ -634,11 +615,10 @@ export default function SessionView({
634
615
  setDevtoolsOpen(!devtoolsOpen);
635
616
  }
636
617
  }}
637
- className={`text-xs px-2 py-1 rounded border transition-colors ${
638
- devtoolsOpen
639
- ? "border-blue-600 text-blue-400 bg-blue-950/30"
640
- : "border-zinc-700 hover:border-zinc-500 text-zinc-500 hover:text-zinc-300"
641
- }`}
618
+ className={`text-xs px-2 py-1 rounded border transition-colors ${devtoolsOpen
619
+ ? "border-blue-600 text-blue-400 bg-blue-950/30"
620
+ : "border-zinc-700 hover:border-zinc-500 text-zinc-500 hover:text-zinc-300"
621
+ }`}
642
622
  title="Toggle DevTools"
643
623
  >
644
624
  DevTools
@@ -706,14 +686,6 @@ function MessageView({ message, agentName }: { message: any; agentName: string }
706
686
  );
707
687
  }
708
688
 
709
- const TRUNCATE_HEAD = 32768;
710
- const TRUNCATE_TAIL = 32768;
711
- function truncateMiddle(text: string): string {
712
- if (text.length <= TRUNCATE_HEAD + TRUNCATE_TAIL) return text;
713
- const removed = text.length - TRUNCATE_HEAD - TRUNCATE_TAIL;
714
- return text.slice(0, TRUNCATE_HEAD) + `\n\n[... truncated ${removed} characters ...]\n\n` + text.slice(-TRUNCATE_TAIL);
715
- }
716
-
717
689
  function ToolCallView({ part }: { part: any }) {
718
690
  const [expanded, setExpanded] = useState(false);
719
691
  const input = part.input || {};
@@ -724,15 +696,15 @@ function ToolCallView({ part }: { part: any }) {
724
696
 
725
697
  const command = state === "input-streaming" ? ""
726
698
  : toolName === "browser"
727
- ? (() => {
699
+ ? (() => {
728
700
  const parts = (input.actions || []).map((a: any) => {
729
701
  switch (a.action) {
730
702
  case "navigate": return `navigate ${a.url || ""}`;
731
703
  case "mouse": {
732
704
  const summary = (a.mouseActions || []).map((s: any) =>
733
705
  s.action === "wait" ? `wait ${s.ms}ms` :
734
- s.action === "wheel" ? `wheel ${s.ref || `${s.x},${s.y}`}` :
735
- `${s.action} ${s.ref || `${s.x},${s.y}`}`
706
+ s.action === "wheel" ? `wheel ${s.ref || `${s.x},${s.y}`}` :
707
+ `${s.action} ${s.ref || `${s.x},${s.y}`}`
736
708
  ).join(" → ");
737
709
  return `mouse ${summary}`;
738
710
  }
@@ -745,7 +717,7 @@ function ToolCallView({ part }: { part: any }) {
745
717
  if (input.observe) parts.push(input.observe);
746
718
  return parts.join(", ");
747
719
  })()
748
- : input.command || (Object.keys(input).length > 0 ? JSON.stringify(input) : "");
720
+ : input.command || (Object.keys(input).length > 0 ? JSON.stringify(input) : "");
749
721
 
750
722
  return (
751
723
  <div className="text-sm">
@@ -785,7 +757,7 @@ function ToolCallView({ part }: { part: any }) {
785
757
  </>
786
758
  );
787
759
  }
788
- } catch {}
760
+ } catch (e) { console.error(e); }
789
761
  }
790
762
  return <span className="text-zinc-500">{typeof result === "string" ? result : JSON.stringify(result, null, 2)}</span>;
791
763
  })()}
@@ -107,7 +107,7 @@ export async function resolveTarget(
107
107
  }
108
108
  throw new Error(`Element ref "${target.ref}" is not visible in the viewport — use only refs or scroll manually first`);
109
109
  } finally {
110
- dmj.cdpSend(agentId, "Runtime.releaseObject", { objectId: result.objectId }).catch(() => {});
110
+ dmj.cdpSend(agentId, "Runtime.releaseObject", { objectId: result.objectId }).catch((e) => console.error(e));
111
111
  }
112
112
  }
113
113
 
@@ -143,7 +143,7 @@ export class BrowserRecorder {
143
143
  scrollOffsetY: metadata?.scrollOffsetY || 0,
144
144
  });
145
145
 
146
- dmj.cdpSend(this.agentId, "Page.screencastFrameAck", { sessionId }).catch(() => {});
146
+ dmj.cdpSend(this.agentId, "Page.screencastFrameAck", { sessionId }).catch((e) => console.error(e));
147
147
  };
148
148
 
149
149
  this.cdpCleanup = dmj.onCdpEvent(handler);
@@ -161,7 +161,7 @@ export class BrowserRecorder {
161
161
  this.recording = false;
162
162
  const endTime = Date.now();
163
163
 
164
- await window.bangonit!.cdpSend(this.agentId, "Page.stopScreencast").catch(() => {});
164
+ await window.bangonit!.cdpSend(this.agentId, "Page.stopScreencast").catch((e) => console.error(e));
165
165
 
166
166
  if (this.cdpCleanup) {
167
167
  this.cdpCleanup();
@@ -196,7 +196,7 @@ export class BrowserRecorder {
196
196
  this.cdpCleanup();
197
197
  this.cdpCleanup = null;
198
198
  }
199
- await window.bangonit?.cdpSend(this.agentId, "Page.stopScreencast").catch(() => {});
199
+ await window.bangonit?.cdpSend(this.agentId, "Page.stopScreencast").catch((e) => console.error(e));
200
200
  }
201
201
 
202
202
  private async compositeAndEncode(frames: CapturedFrame[]): Promise<Blob | null> {
@@ -29,9 +29,6 @@ interface BangOnIt {
29
29
  setAgentSession: (agentId: string, session: AgentSessionData) => Promise<void>;
30
30
  deleteAgentSession: (agentId: string) => Promise<void>;
31
31
 
32
- // Bash (per-agent)
33
- bashExec: (agentId: string, command: string) => Promise<string>;
34
-
35
32
  // --- Low-level browser primitives ---
36
33
 
37
34
  // CDP: send a raw CDP command to the active tab
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ELECTRON_ARGS_ENV = exports.electronArgsSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ exports.electronArgsSchema = zod_1.z.object({
6
+ testPlanFiles: zod_1.z.array(zod_1.z.string()),
7
+ headless: zod_1.z.boolean(),
8
+ exit: zod_1.z.boolean(),
9
+ json: zod_1.z.boolean(),
10
+ console: zod_1.z.boolean(),
11
+ record: zod_1.z.boolean(),
12
+ retries: zod_1.z.number(),
13
+ output: zod_1.z.string().nullable(),
14
+ plan: zod_1.z.string().nullable(),
15
+ prompt: zod_1.z.string().nullable(),
16
+ concurrency: zod_1.z.number(),
17
+ timeout: zod_1.z.number(),
18
+ cwd: zod_1.z.string(),
19
+ recordingsDir: zod_1.z.string().nullable(),
20
+ });
21
+ exports.ELECTRON_ARGS_ENV = "BANGONIT_ARGS";