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.
- package/README.md +69 -37
- package/app/desktopapp/dist/main/index.js +10 -161
- package/app/desktopapp/dist/main/ipc.js +0 -20
- package/app/desktopapp/dist/main/preload.js +0 -2
- package/app/desktopapp/dist/main/tabs.js +0 -10
- package/app/desktopapp/dist/shared/args.js +21 -0
- package/app/desktopapp/package.json +1 -1
- package/app/replay/dist/replay.css +1 -1
- package/app/replay/dist/replay.js +20 -20
- package/app/webapp/.next/BUILD_ID +1 -1
- package/app/webapp/.next/app-build-manifest.json +2 -2
- package/app/webapp/.next/app-path-routes-manifest.json +1 -1
- package/app/webapp/.next/build-manifest.json +2 -2
- package/app/webapp/.next/next-minimal-server.js.nft.json +1 -1
- package/app/webapp/.next/next-server.js.nft.json +1 -1
- package/app/webapp/.next/prerender-manifest.json +1 -1
- package/app/webapp/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/app/webapp/.next/server/app/_not-found.html +1 -1
- package/app/webapp/.next/server/app/_not-found.rsc +1 -1
- package/app/webapp/.next/server/app/app/page.js +3 -7
- package/app/webapp/.next/server/app/app/page_client-reference-manifest.js +1 -1
- package/app/webapp/.next/server/app/app.html +1 -1
- package/app/webapp/.next/server/app/app.rsc +2 -2
- package/app/webapp/.next/server/app/index.html +1 -1
- package/app/webapp/.next/server/app/index.rsc +1 -1
- package/app/webapp/.next/server/app/page_client-reference-manifest.js +1 -1
- package/app/webapp/.next/server/app-paths-manifest.json +2 -2
- package/app/webapp/.next/server/chunks/708.js +1 -1
- package/app/webapp/.next/server/functions-config-manifest.json +1 -1
- package/app/webapp/.next/server/pages/404.html +1 -1
- package/app/webapp/.next/server/pages/500.html +1 -1
- package/app/webapp/.next/server/server-reference-manifest.json +1 -1
- package/app/webapp/.next/static/chunks/app/app/page-0e096497dcb81dae.js +1 -0
- package/app/webapp/.next/static/css/{869d3ff23c36c4b5.css → 38219627f55424f2.css} +1 -1
- package/app/webapp/.next/trace +2 -2
- package/app/webapp/package.json +7 -2
- package/app/webapp/src/shared/api/chat.ts +2 -11
- package/app/webapp/src/shared/components/AppShell.tsx +21 -1
- package/app/webapp/src/shared/components/SessionView.tsx +37 -65
- package/app/webapp/src/shared/lib/browser/mouse.ts +1 -1
- package/app/webapp/src/shared/lib/browser/recorder.ts +3 -3
- package/app/webapp/src/shared/types/global.d.ts +0 -3
- package/bin/app/desktopapp/src/shared/args.js +21 -0
- package/bin/bangonit.js +259 -96
- package/bin/src/cli/bangonit.js +767 -0
- package/package.json +6 -4
- package/app/webapp/.next/static/chunks/app/app/page-d38c1e48d37def82.js +0 -1
- /package/app/webapp/.next/static/{TaLpPsk5rC30wNNcyfUN3 → Qq0OvlQijtcR84Dg9Dgp0}/_buildManifest.js +0 -0
- /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
|
|
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
|
|
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 ===
|
|
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.
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
-
|
|
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
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
-
|
|
606
|
-
|
|
607
|
-
|
|
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
|
-
|
|
639
|
-
|
|
640
|
-
|
|
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
|
-
|
|
735
|
-
|
|
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
|
-
|
|
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";
|