jerob 1.0.0
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/CLI/cli.ts +42 -0
- package/README.md +137 -0
- package/SETUP.md +584 -0
- package/agent/action-tracker.ts +45 -0
- package/agent/agent-tools.ts +111 -0
- package/agent/approval.ts +137 -0
- package/agent/diff-view.ts +26 -0
- package/agent/orchestrator.ts +186 -0
- package/agent/tool-executor.ts +463 -0
- package/agent/types.ts +69 -0
- package/ask/orchestrator.ts +244 -0
- package/auth/auth.ts +567 -0
- package/auth/config-store.ts +77 -0
- package/auth/crypto.ts +51 -0
- package/auth/env-writer.ts +82 -0
- package/bin/jerob.js +28 -0
- package/config/ai.config.ts +163 -0
- package/email_ops/email-tools.ts +178 -0
- package/email_ops/email_functions.ts +443 -0
- package/email_ops/email_init.ts +92 -0
- package/email_ops/email_pass_store.ts +61 -0
- package/email_ops/email_server.ts +29 -0
- package/email_ops/types.ts +88 -0
- package/index.ts +176 -0
- package/package.json +88 -0
- package/plan/browser-agent/README.md +118 -0
- package/plan/browser-agent/USAGE.md +308 -0
- package/plan/browser-agent/evaluator.ts +353 -0
- package/plan/browser-agent/executor.ts +372 -0
- package/plan/browser-agent/index.ts +13 -0
- package/plan/browser-agent/orchestrator.ts +323 -0
- package/plan/browser-agent/planner.ts +200 -0
- package/plan/browser-agent/types.ts +62 -0
- package/plan/browser-tool.ts +128 -0
- package/plan/index.ts +12 -0
- package/plan/orchestrator.ts +214 -0
- package/plan/planner.ts +183 -0
- package/plan/selection.ts +50 -0
- package/plan/types.ts +13 -0
- package/plan/web-tools.ts +119 -0
- package/scheduler/ARCHITECTURE.md +263 -0
- package/scheduler/README.md +200 -0
- package/scheduler/SETUP-READY.sql +84 -0
- package/scheduler/check-status.sql +124 -0
- package/scheduler/config-sync.ts +91 -0
- package/scheduler/db-migrate.ts +271 -0
- package/scheduler/db.ts +162 -0
- package/scheduler/debug.ts +184 -0
- package/scheduler/orchestrator.ts +438 -0
- package/scheduler/planner.ts +170 -0
- package/scheduler/update-task-email.ts +70 -0
- package/supabase/.temp/cli-latest +1 -0
- package/supabase/.temp/gotrue-version +1 -0
- package/supabase/.temp/linked-project.json +1 -0
- package/supabase/.temp/pooler-url +1 -0
- package/supabase/.temp/postgres-version +1 -0
- package/supabase/.temp/project-ref +1 -0
- package/supabase/.temp/rest-version +1 -0
- package/supabase/.temp/storage-migration +1 -0
- package/supabase/.temp/storage-version +1 -0
- package/supabase/deploy.ps1 +50 -0
- package/supabase/functions/scheduler-tick/index.ts +496 -0
- package/supabase/supabase/.temp/linked-project.json +1 -0
- package/tsconfig.json +33 -0
- package/tui/spinner.ts +33 -0
- package/tui/spinup.ts +67 -0
- package/tui/terminal-render.ts +16 -0
- package/utils/llm-error.ts +185 -0
- package/utils/model-validator.ts +247 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export type SendMailInput = {
|
|
2
|
+
to: string | string[];
|
|
3
|
+
cc?: string[];
|
|
4
|
+
bcc?: string[];
|
|
5
|
+
subject: string;
|
|
6
|
+
body: string;
|
|
7
|
+
html?: string;
|
|
8
|
+
attachments?: string[];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type ReadMailInput = {
|
|
12
|
+
messageId: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type SearchMailInput = {
|
|
16
|
+
query: string; // Gmail search query e.g. "from:foo@bar.com is:unread"
|
|
17
|
+
maxResults?: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type ReplyMailInput = {
|
|
21
|
+
messageId: string; // original message to reply to
|
|
22
|
+
body: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type DraftMailInput = {
|
|
26
|
+
to: string | string[];
|
|
27
|
+
cc?: string[];
|
|
28
|
+
bcc?: string[];
|
|
29
|
+
subject: string;
|
|
30
|
+
body: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type DeleteMailInput = {
|
|
34
|
+
messageId: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type ArchiveMailInput = {
|
|
38
|
+
messageId: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type LabelMailInput = {
|
|
42
|
+
messageId: string;
|
|
43
|
+
labelIds: string[]; // Gmail label IDs to add
|
|
44
|
+
removeLabelIds?: string[]; // optional labels to remove
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type WatchInput = {
|
|
48
|
+
topicName: string; // Google Cloud Pub/Sub topic
|
|
49
|
+
labelIds?: string[];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type ClassifyInput = {
|
|
53
|
+
messageId: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type ExtractTasksInput = {
|
|
57
|
+
messageId: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export type BulkActionInput = {
|
|
61
|
+
messageIds: string[];
|
|
62
|
+
action: "delete" | "archive" | "markRead" | "markUnread" | "label";
|
|
63
|
+
labelIds?: string[];
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export type DigestInput = {
|
|
67
|
+
maxResults?: number;
|
|
68
|
+
query?: string;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export type ScheduleSendInput = SendMailInput & {
|
|
72
|
+
sendAt: Date; // when to send
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export type ThreadInput = {
|
|
76
|
+
threadId: string;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export type EmailMessage = {
|
|
80
|
+
id: string;
|
|
81
|
+
threadId: string;
|
|
82
|
+
from: string;
|
|
83
|
+
to: string;
|
|
84
|
+
subject: string;
|
|
85
|
+
body: string;
|
|
86
|
+
date: string;
|
|
87
|
+
labelIds: string[];
|
|
88
|
+
};
|
package/index.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import {program} from "commander";
|
|
4
|
+
import { startArena } from "./tui/spinup";
|
|
5
|
+
import { authenticate, getAllKeys, updateApiKey, resetAuth, switchModelFlow } from "./auth/auth";
|
|
6
|
+
import { writeEnvFile } from "./auth/env-writer";
|
|
7
|
+
import dotenv from "dotenv";
|
|
8
|
+
dotenv.config({ path: "./.env" });
|
|
9
|
+
|
|
10
|
+
program.name("jerob").description(" Your personal Assistant sits in your computer").version("0.1.0")
|
|
11
|
+
|
|
12
|
+
program.command("jet")
|
|
13
|
+
.description("agent spin up command")
|
|
14
|
+
.action(
|
|
15
|
+
async()=>{
|
|
16
|
+
try {
|
|
17
|
+
const { config, password } = await authenticate();
|
|
18
|
+
const keys = getAllKeys(config, password);
|
|
19
|
+
|
|
20
|
+
// Write .env so subprocesses and tools that read it directly work too
|
|
21
|
+
writeEnvFile(keys);
|
|
22
|
+
|
|
23
|
+
// Apply all keys to process.env — set present values, clear stale ones
|
|
24
|
+
for (const [k, v] of Object.entries(keys)) {
|
|
25
|
+
if (v) {
|
|
26
|
+
process.env[k] = v;
|
|
27
|
+
} else {
|
|
28
|
+
delete process.env[k];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
process.env.OPENROUTER_MODEL = process.env.OPENROUTER_MODEL || "openrouter/free";
|
|
32
|
+
|
|
33
|
+
// Auto-sync credentials to Supabase on every login — non-fatal
|
|
34
|
+
if (keys.SUPABASE_URL && keys.SUPABASE_SERVICE_ROLE_KEY) {
|
|
35
|
+
import("./scheduler/config-sync")
|
|
36
|
+
.then(({ syncAllSecrets }) => syncAllSecrets())
|
|
37
|
+
.catch(() => {}); // silent — don't block startup
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
await startArena()
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error instanceof Error && error.message.includes("cancelled")) {
|
|
43
|
+
console.log(chalk.yellow("\n✓ Exited cleanly\n"));
|
|
44
|
+
} else {
|
|
45
|
+
console.log(chalk.red("Authentication failed"));
|
|
46
|
+
console.log(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
47
|
+
}
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
program.command("switch-model")
|
|
54
|
+
.description("Switch the active model for any provider (OpenRouter, Gemini, Claude, OpenAI, Groq)")
|
|
55
|
+
.action(async () => {
|
|
56
|
+
try {
|
|
57
|
+
const { config, password: pwd } = await authenticate();
|
|
58
|
+
await switchModelFlow(config, pwd);
|
|
59
|
+
// Rewrite .env with the updated model overrides
|
|
60
|
+
const keys = getAllKeys(config, pwd);
|
|
61
|
+
writeEnvFile(keys);
|
|
62
|
+
for (const [k, v] of Object.entries(keys)) {
|
|
63
|
+
if (v) {
|
|
64
|
+
process.env[k] = v;
|
|
65
|
+
} else {
|
|
66
|
+
delete process.env[k];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (error instanceof Error && error.message.includes("cancelled")) {
|
|
71
|
+
console.log(chalk.yellow("\n✓ Cancelled\n"));
|
|
72
|
+
} else {
|
|
73
|
+
console.log(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
74
|
+
}
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
program.command("set-key")
|
|
80
|
+
.description("Update stored API keys or model preferences")
|
|
81
|
+
.action(async () => {
|
|
82
|
+
try {
|
|
83
|
+
await updateApiKey();
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (error instanceof Error && error.message.includes("cancelled")) {
|
|
86
|
+
console.log(chalk.yellow("\n✓ Update cancelled\n"));
|
|
87
|
+
} else {
|
|
88
|
+
console.log(chalk.red("Failed to update"));
|
|
89
|
+
console.log(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
90
|
+
}
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
program.command("reset-auth")
|
|
96
|
+
.description("Remove stored auth config and API key")
|
|
97
|
+
.action(async () => {
|
|
98
|
+
try {
|
|
99
|
+
resetAuth();
|
|
100
|
+
console.log(chalk.green("\n✓ Auth reset complete. Stored login and key removed.\n"));
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.log(chalk.red("Failed to reset auth"));
|
|
103
|
+
console.log(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
104
|
+
process.exit(0);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
program.command("sync-credentials")
|
|
109
|
+
.description("Sync API keys and credentials to Supabase user_config table")
|
|
110
|
+
.action(async () => {
|
|
111
|
+
try {
|
|
112
|
+
const { syncAllSecrets } = await import("./scheduler/config-sync");
|
|
113
|
+
await syncAllSecrets();
|
|
114
|
+
console.log(chalk.green("\n✓ Credentials synced to Supabase user_config table\n"));
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.log(chalk.red("Sync failed"));
|
|
117
|
+
console.log(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
program.command("setup-db")
|
|
123
|
+
.description("Run Supabase schema migrations (creates tables, RLS policies, pg_cron schedule)")
|
|
124
|
+
.action(async () => {
|
|
125
|
+
try {
|
|
126
|
+
const { config, password } = await authenticate();
|
|
127
|
+
const keys = getAllKeys(config, password);
|
|
128
|
+
const url = keys.SUPABASE_URL;
|
|
129
|
+
const key = keys.SUPABASE_SERVICE_ROLE_KEY;
|
|
130
|
+
let token = keys.SUPABASE_ACCESS_TOKEN;
|
|
131
|
+
|
|
132
|
+
if (!url || !key) {
|
|
133
|
+
console.log(chalk.yellow("\n⚠ Supabase URL or service role key not configured."));
|
|
134
|
+
console.log(chalk.dim(" Run `jerob set-key` to add them first.\n"));
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// If no stored token, prompt for it now
|
|
139
|
+
if (!token) {
|
|
140
|
+
const { text: promptText, isCancel } = await import("@clack/prompts");
|
|
141
|
+
console.log(chalk.dim("\n Get your personal access token at: https://supabase.com/dashboard/account/tokens\n"));
|
|
142
|
+
const t = await promptText({
|
|
143
|
+
message: "Supabase personal access token:",
|
|
144
|
+
placeholder: "sbp_...",
|
|
145
|
+
validate: (v) => { if (!v?.trim()) return "Token required"; },
|
|
146
|
+
});
|
|
147
|
+
if (isCancel(t)) { console.log(chalk.yellow("\n✓ Cancelled\n")); return; }
|
|
148
|
+
token = String(t).trim();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const { runDbMigrations } = await import("./scheduler/db-migrate");
|
|
152
|
+
await runDbMigrations(url, key, token);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
if (error instanceof Error && error.message.includes("cancelled")) {
|
|
155
|
+
console.log(chalk.yellow("\n✓ Cancelled\n"));
|
|
156
|
+
} else {
|
|
157
|
+
console.log(chalk.red("Migration failed"));
|
|
158
|
+
console.log(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
159
|
+
}
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
program.command("scheduler-debug")
|
|
165
|
+
.description("Debug scheduler: check tasks, credentials, and test Edge Function")
|
|
166
|
+
.action(async () => {
|
|
167
|
+
try {
|
|
168
|
+
await import("./scheduler/debug");
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.log(chalk.red("Scheduler debug failed"));
|
|
171
|
+
console.log(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
await program.parseAsync(process.argv)
|
package/package.json
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jerob",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Personal AI agent CLI — autonomous code agent, planner, browser automation, Q&A, and serverless scheduler",
|
|
5
|
+
"module": "index.ts",
|
|
6
|
+
"private": false,
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"jerob": "./bin/jerob.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin/",
|
|
13
|
+
"index.ts",
|
|
14
|
+
"agent/",
|
|
15
|
+
"ask/",
|
|
16
|
+
"auth/",
|
|
17
|
+
"CLI/",
|
|
18
|
+
"config/",
|
|
19
|
+
"email_ops/",
|
|
20
|
+
"plan/",
|
|
21
|
+
"scheduler/",
|
|
22
|
+
"supabase/",
|
|
23
|
+
"tui/",
|
|
24
|
+
"utils/",
|
|
25
|
+
"tsconfig.json",
|
|
26
|
+
"README.md",
|
|
27
|
+
"SETUP.md"
|
|
28
|
+
],
|
|
29
|
+
"keywords": [
|
|
30
|
+
"ai",
|
|
31
|
+
"agent",
|
|
32
|
+
"cli",
|
|
33
|
+
"llm",
|
|
34
|
+
"automation",
|
|
35
|
+
"browser-automation",
|
|
36
|
+
"scheduler",
|
|
37
|
+
"supabase",
|
|
38
|
+
"openrouter",
|
|
39
|
+
"gemini",
|
|
40
|
+
"claude",
|
|
41
|
+
"terminal"
|
|
42
|
+
],
|
|
43
|
+
"author": "Abhishek Sharma",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/ABHI-Theq/Jerob-Personal_AI_Operator.git"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/ABHI-Theq/Jerob-Personal_AI_Operator#readme",
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=18"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/bun": "latest"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"typescript": "^5"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"@ai-sdk/anthropic": "^3.0.81",
|
|
61
|
+
"@ai-sdk/google": "^3.0.80",
|
|
62
|
+
"@ai-sdk/groq": "^3.0.39",
|
|
63
|
+
"@ai-sdk/openai": "^3.0.68",
|
|
64
|
+
"@browserbasehq/stagehand": "^3.4.0",
|
|
65
|
+
"@clack/core": "^1.4.0",
|
|
66
|
+
"@clack/prompts": "^1.5.0",
|
|
67
|
+
"@mendable/firecrawl-js": "^4.25.1",
|
|
68
|
+
"@openrouter/ai-sdk-provider": "^2.9.0",
|
|
69
|
+
"@supabase/supabase-js": "^2.107.0",
|
|
70
|
+
"@types/express": "^5.0.6",
|
|
71
|
+
"@types/marked-terminal": "^6.1.1",
|
|
72
|
+
"@types/node": "^25.9.1",
|
|
73
|
+
"ai": "^6.0.193",
|
|
74
|
+
"chalk": "^5.6.2",
|
|
75
|
+
"commander": "^15.0.0",
|
|
76
|
+
"crypto": "^1.0.1",
|
|
77
|
+
"diff": "^9.0.0",
|
|
78
|
+
"dotenv": "^17.4.2",
|
|
79
|
+
"express": "^5.2.1",
|
|
80
|
+
"figlet": "^1.11.0",
|
|
81
|
+
"googleapis": "^173.0.0",
|
|
82
|
+
"marked": "^18.0.4",
|
|
83
|
+
"marked-terminal": "^7.3.0",
|
|
84
|
+
"open": "^11.0.0",
|
|
85
|
+
"supabase": "^2.105.0",
|
|
86
|
+
"telegraf": "^4.16.3"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Browser Agent
|
|
2
|
+
|
|
3
|
+
Autonomous browser automation using Stagehand's agent API with an iterative Plan → Execute → Evaluate loop (up to 5 cycles).
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
User Query
|
|
9
|
+
│
|
|
10
|
+
▼
|
|
11
|
+
┌─────────────────────────────────────┐
|
|
12
|
+
│ ITERATION LOOP (max 5) │
|
|
13
|
+
│ │
|
|
14
|
+
│ 1. PLANNER │
|
|
15
|
+
│ LLM generates automation plan │
|
|
16
|
+
│ (with feedback on retry) │
|
|
17
|
+
│ │ │
|
|
18
|
+
│ ▼ │
|
|
19
|
+
│ 2. EXECUTOR │
|
|
20
|
+
│ Stagehand agent() runs the task │
|
|
21
|
+
│ autonomously via DOM mode │
|
|
22
|
+
│ │ │
|
|
23
|
+
│ ▼ │
|
|
24
|
+
│ 3. EVALUATOR │
|
|
25
|
+
│ Scores result 0-100 │
|
|
26
|
+
│ Checks completeness & accuracy │
|
|
27
|
+
│ │ │
|
|
28
|
+
│ ▼ │
|
|
29
|
+
│ 4. DECISION │
|
|
30
|
+
│ score ≥ 80 → done │
|
|
31
|
+
│ max iters → return best │
|
|
32
|
+
│ otherwise → feedback → retry │
|
|
33
|
+
└─────────────────────────────────────┘
|
|
34
|
+
│
|
|
35
|
+
▼
|
|
36
|
+
Final Result (console + optional JSON/MD save)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Files
|
|
40
|
+
|
|
41
|
+
| File | Role |
|
|
42
|
+
|------|------|
|
|
43
|
+
| `orchestrator.ts` | Entry point, manages iteration loop, renders report |
|
|
44
|
+
| `planner.ts` | LLM generates a `BrowserPlan` (steps + goal + reasoning) |
|
|
45
|
+
| `executor.ts` | Runs plan via Stagehand `agent()` in DOM mode, parses messages |
|
|
46
|
+
| `evaluator.ts` | Scores execution, extracts feedback for next iteration |
|
|
47
|
+
| `types.ts` | Shared TypeScript types |
|
|
48
|
+
| `index.ts` | Re-exports all public API |
|
|
49
|
+
|
|
50
|
+
## Browser Setup
|
|
51
|
+
|
|
52
|
+
Uses Brave browser with a persistent profile for sites that require login (e.g. LinkedIn):
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
localBrowserLaunchOptions: {
|
|
56
|
+
executablePath: "C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe",
|
|
57
|
+
args: [
|
|
58
|
+
'--user-data-dir=C:\\Users\\YOUR_USERNAME\\AppData\\Local\\BraveSoftware\\Brave-Browser\\User Data',
|
|
59
|
+
'--profile-directory=Default',
|
|
60
|
+
],
|
|
61
|
+
headless: false,
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Configuration
|
|
66
|
+
|
|
67
|
+
In `orchestrator.ts`:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
{
|
|
71
|
+
maxIterations: 5,
|
|
72
|
+
timeout: 120000,
|
|
73
|
+
model: "google/gemini-3.1-flash-lite-preview",
|
|
74
|
+
evaluationThreshold: 80 // score out of 100 to mark as satisfied
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Environment Variables
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
GOOGLE_GENERATIVE_AI_API_KEY=...
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Usage
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
jimmy jet
|
|
88
|
+
# Select "Browser Agent" from the menu
|
|
89
|
+
# Enter your query
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Example queries:
|
|
93
|
+
```
|
|
94
|
+
Find top 5 AI jobs on LinkedIn with full descriptions
|
|
95
|
+
Get transcript of a YouTube video
|
|
96
|
+
Extract product prices from a homepage
|
|
97
|
+
Search flights NYC to LA under $300
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Supported Step Actions (Planner)
|
|
101
|
+
|
|
102
|
+
`navigate` · `click` · `type` · `extract` · `observe` · `wait` · `scroll`
|
|
103
|
+
|
|
104
|
+
> The executor doesn't run these step-by-step — it passes the plan goal directly to Stagehand's `agent()` which handles navigation autonomously.
|
|
105
|
+
|
|
106
|
+
## Output
|
|
107
|
+
|
|
108
|
+
After each run you can optionally save:
|
|
109
|
+
- JSON file with full iteration log + extracted data
|
|
110
|
+
- Markdown report with scores and agent output
|
|
111
|
+
|
|
112
|
+
## Dependencies
|
|
113
|
+
|
|
114
|
+
- `@browserbasehq/stagehand` — browser automation
|
|
115
|
+
- `ai` + `@openrouter/ai-sdk-provider` — LLM calls
|
|
116
|
+
- `@clack/prompts` — CLI prompts
|
|
117
|
+
- `chalk` — terminal colors
|
|
118
|
+
- `zod` — schema validation
|