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.
Files changed (69) hide show
  1. package/CLI/cli.ts +42 -0
  2. package/README.md +137 -0
  3. package/SETUP.md +584 -0
  4. package/agent/action-tracker.ts +45 -0
  5. package/agent/agent-tools.ts +111 -0
  6. package/agent/approval.ts +137 -0
  7. package/agent/diff-view.ts +26 -0
  8. package/agent/orchestrator.ts +186 -0
  9. package/agent/tool-executor.ts +463 -0
  10. package/agent/types.ts +69 -0
  11. package/ask/orchestrator.ts +244 -0
  12. package/auth/auth.ts +567 -0
  13. package/auth/config-store.ts +77 -0
  14. package/auth/crypto.ts +51 -0
  15. package/auth/env-writer.ts +82 -0
  16. package/bin/jerob.js +28 -0
  17. package/config/ai.config.ts +163 -0
  18. package/email_ops/email-tools.ts +178 -0
  19. package/email_ops/email_functions.ts +443 -0
  20. package/email_ops/email_init.ts +92 -0
  21. package/email_ops/email_pass_store.ts +61 -0
  22. package/email_ops/email_server.ts +29 -0
  23. package/email_ops/types.ts +88 -0
  24. package/index.ts +176 -0
  25. package/package.json +88 -0
  26. package/plan/browser-agent/README.md +118 -0
  27. package/plan/browser-agent/USAGE.md +308 -0
  28. package/plan/browser-agent/evaluator.ts +353 -0
  29. package/plan/browser-agent/executor.ts +372 -0
  30. package/plan/browser-agent/index.ts +13 -0
  31. package/plan/browser-agent/orchestrator.ts +323 -0
  32. package/plan/browser-agent/planner.ts +200 -0
  33. package/plan/browser-agent/types.ts +62 -0
  34. package/plan/browser-tool.ts +128 -0
  35. package/plan/index.ts +12 -0
  36. package/plan/orchestrator.ts +214 -0
  37. package/plan/planner.ts +183 -0
  38. package/plan/selection.ts +50 -0
  39. package/plan/types.ts +13 -0
  40. package/plan/web-tools.ts +119 -0
  41. package/scheduler/ARCHITECTURE.md +263 -0
  42. package/scheduler/README.md +200 -0
  43. package/scheduler/SETUP-READY.sql +84 -0
  44. package/scheduler/check-status.sql +124 -0
  45. package/scheduler/config-sync.ts +91 -0
  46. package/scheduler/db-migrate.ts +271 -0
  47. package/scheduler/db.ts +162 -0
  48. package/scheduler/debug.ts +184 -0
  49. package/scheduler/orchestrator.ts +438 -0
  50. package/scheduler/planner.ts +170 -0
  51. package/scheduler/update-task-email.ts +70 -0
  52. package/supabase/.temp/cli-latest +1 -0
  53. package/supabase/.temp/gotrue-version +1 -0
  54. package/supabase/.temp/linked-project.json +1 -0
  55. package/supabase/.temp/pooler-url +1 -0
  56. package/supabase/.temp/postgres-version +1 -0
  57. package/supabase/.temp/project-ref +1 -0
  58. package/supabase/.temp/rest-version +1 -0
  59. package/supabase/.temp/storage-migration +1 -0
  60. package/supabase/.temp/storage-version +1 -0
  61. package/supabase/deploy.ps1 +50 -0
  62. package/supabase/functions/scheduler-tick/index.ts +496 -0
  63. package/supabase/supabase/.temp/linked-project.json +1 -0
  64. package/tsconfig.json +33 -0
  65. package/tui/spinner.ts +33 -0
  66. package/tui/spinup.ts +67 -0
  67. package/tui/terminal-render.ts +16 -0
  68. package/utils/llm-error.ts +185 -0
  69. 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