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,200 @@
|
|
|
1
|
+
# Jimmy Scheduler
|
|
2
|
+
|
|
3
|
+
Fully automated task scheduler with AI planning. Tasks run either locally (`jimmy daemon`) or serverless (Supabase Edge Functions + pg_cron).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **AI Task Planning** — describe a task in plain English, LLM breaks it into steps + cron schedule
|
|
8
|
+
- **Step Types** — web_search, web_crawl, custom (pure LLM), email_send, browser (local only)
|
|
9
|
+
- **Auto Re-auth** — Gmail refresh token auto-syncs to Supabase on re-auth, zero manual updates
|
|
10
|
+
- **Run History** — all runs logged to Supabase with full step results
|
|
11
|
+
- **Email Summaries** — optional email digest after each run
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Setup (One Time)
|
|
16
|
+
|
|
17
|
+
### 1. Create Supabase tables
|
|
18
|
+
|
|
19
|
+
Already done if you ran the main setup SQL. Verify these exist:
|
|
20
|
+
```sql
|
|
21
|
+
scheduler_tasks
|
|
22
|
+
scheduler_runs
|
|
23
|
+
user_config
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 2. Deploy Edge Function (serverless mode)
|
|
27
|
+
|
|
28
|
+
```powershell
|
|
29
|
+
# Install Supabase CLI
|
|
30
|
+
npm i -g supabase
|
|
31
|
+
|
|
32
|
+
# Login
|
|
33
|
+
supabase login
|
|
34
|
+
|
|
35
|
+
# Link your project
|
|
36
|
+
supabase link --project-ref <YOUR_PROJECT_REF>
|
|
37
|
+
|
|
38
|
+
# Deploy + sync credentials
|
|
39
|
+
.\supabase\deploy.ps1
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The script:
|
|
43
|
+
- Deploys the `scheduler-tick` Edge Function
|
|
44
|
+
- Sets `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` as secrets
|
|
45
|
+
- Syncs all API keys (OpenRouter, Groq, Firecrawl, Google) to `user_config` table
|
|
46
|
+
|
|
47
|
+
### 3. Register pg_cron job
|
|
48
|
+
|
|
49
|
+
Run the SQL in `supabase/functions/scheduler-tick/setup.sql` in your Supabase SQL editor.
|
|
50
|
+
|
|
51
|
+
Replace `<PROJECT_REF>` with your actual project ref, and set your service role key in the last line.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
### Add a Task
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
jimmy jet → CLI → Scheduler → Add new task
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Describe it naturally:
|
|
64
|
+
> "Every morning at 9am, search for top AI news from the last 24 hours and email me a summary"
|
|
65
|
+
|
|
66
|
+
The LLM plans:
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"name": "Daily AI News Digest",
|
|
70
|
+
"cron": "0 9 * * *",
|
|
71
|
+
"steps": [
|
|
72
|
+
{ "order": 1, "type": "web_search", "instruction": "Find top AI news from last 24 hours" },
|
|
73
|
+
{ "order": 2, "type": "email_send", "instruction": "Send summary to user" }
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
You can edit the cron expression before saving.
|
|
79
|
+
|
|
80
|
+
### Task Management
|
|
81
|
+
|
|
82
|
+
- **List** — see all tasks, their status, run count, next run time
|
|
83
|
+
- **Manage** — enable/disable, edit cron/email, view run history, run now (manual trigger), delete
|
|
84
|
+
|
|
85
|
+
### Execution Modes
|
|
86
|
+
|
|
87
|
+
**Serverless (Default)** — Tasks run in Supabase Edge Function every minute via pg_cron. Your machine can be off.
|
|
88
|
+
|
|
89
|
+
**Supported Step Types:**
|
|
90
|
+
- `web_search` - Search via Firecrawl API
|
|
91
|
+
- `web_crawl` - Scrape URLs via Firecrawl API
|
|
92
|
+
- `custom` - LLM-powered tasks (OpenRouter → Groq fallback)
|
|
93
|
+
- `email_send` - Gmail API (auto-refresh token)
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## How Auto Re-auth Works
|
|
98
|
+
|
|
99
|
+
When you re-authenticate Gmail via `jimmy jet`:
|
|
100
|
+
|
|
101
|
+
1. New refresh token saved to `~/.cccontrol/googleAuth/google_config.json`
|
|
102
|
+
2. **Automatically synced to Supabase `user_config` table**
|
|
103
|
+
3. Edge Function reads from `user_config` on every run
|
|
104
|
+
4. No manual `supabase secrets set` needed ever again
|
|
105
|
+
|
|
106
|
+
Same applies to all API keys — update `.env` and run `jimmy sync-credentials` to push changes.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Cron Expression Examples
|
|
111
|
+
|
|
112
|
+
| Expression | Meaning |
|
|
113
|
+
|---|---|
|
|
114
|
+
| `* * * * *` | Every minute |
|
|
115
|
+
| `0 * * * *` | Every hour on the hour |
|
|
116
|
+
| `0 9 * * *` | Every day at 9:00 AM |
|
|
117
|
+
| `0 9 * * 1` | Every Monday at 9:00 AM |
|
|
118
|
+
| `*/15 * * * *` | Every 15 minutes |
|
|
119
|
+
| `0 0 1 * *` | First day of every month at midnight |
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Troubleshooting
|
|
124
|
+
|
|
125
|
+
### Quick Debug
|
|
126
|
+
|
|
127
|
+
Run the built-in debug tool:
|
|
128
|
+
```powershell
|
|
129
|
+
jimmy scheduler-debug
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
This checks:
|
|
133
|
+
- ✓ Credentials synced to Supabase
|
|
134
|
+
- ✓ Tasks and their run history
|
|
135
|
+
- ✓ Manually triggers Edge Function for testing
|
|
136
|
+
|
|
137
|
+
### Check in Supabase Dashboard
|
|
138
|
+
|
|
139
|
+
**View Task Runs:**
|
|
140
|
+
```sql
|
|
141
|
+
select * from scheduler_runs order by started_at desc limit 20;
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Check Cron Job Status:**
|
|
145
|
+
```sql
|
|
146
|
+
-- See if cron job is registered
|
|
147
|
+
select * from cron.job;
|
|
148
|
+
|
|
149
|
+
-- View recent cron executions
|
|
150
|
+
select * from cron.job_run_details
|
|
151
|
+
where jobname = 'jimmy-scheduler-tick'
|
|
152
|
+
order by start_time desc
|
|
153
|
+
limit 10;
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**View Tasks:**
|
|
157
|
+
```sql
|
|
158
|
+
select id, name, enabled, cron, next_run_at, last_run_at, run_count
|
|
159
|
+
from scheduler_tasks;
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Complete Status Check:**
|
|
163
|
+
Open `scheduler/check-status.sql` and copy-paste sections into Supabase SQL Editor.
|
|
164
|
+
|
|
165
|
+
### Common Issues
|
|
166
|
+
|
|
167
|
+
**Tasks not running?**
|
|
168
|
+
|
|
169
|
+
1. Check `supabase secrets list` — ensure `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` are set
|
|
170
|
+
2. Query `user_config` table — verify credentials are present
|
|
171
|
+
3. Check `scheduler_runs` table — look for error messages
|
|
172
|
+
4. View Edge Function logs: `supabase functions logs scheduler-tick`
|
|
173
|
+
5. Verify pg_cron job: `select * from cron.job;`
|
|
174
|
+
|
|
175
|
+
**Gmail sends failing?**
|
|
176
|
+
|
|
177
|
+
Run `jimmy sync-credentials` to push the latest refresh token to Supabase.
|
|
178
|
+
|
|
179
|
+
**Task says "completed" but I see no output?**
|
|
180
|
+
|
|
181
|
+
Check the `scheduler_runs` table for detailed `step_results` and `output` fields.
|
|
182
|
+
|
|
183
|
+
**Cron job not registered?**
|
|
184
|
+
|
|
185
|
+
You need to run the setup SQL once: `supabase/functions/scheduler-tick/setup.sql`
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Architecture
|
|
190
|
+
|
|
191
|
+
All tasks run **100% serverless in Supabase** via Edge Functions + pg_cron. Your machine can be completely off.
|
|
192
|
+
|
|
193
|
+
**Step Types (all serverless):**
|
|
194
|
+
|
|
195
|
+
| Type | What it does |
|
|
196
|
+
|------|-------------|
|
|
197
|
+
| `web_search` | Search via Firecrawl API |
|
|
198
|
+
| `web_crawl` | Scrape URLs via Firecrawl API |
|
|
199
|
+
| `custom` | LLM-powered tasks (OpenRouter → Groq fallback) |
|
|
200
|
+
| `email_send` | Send via Gmail API (auto-refresh token) |
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
-- ════════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
-- Jimmy Scheduler — One-Time Setup SQL
|
|
3
|
+
-- 1. Go to your Supabase project → SQL Editor
|
|
4
|
+
-- 2. Paste this entire file and click RUN
|
|
5
|
+
-- ════════════════════════════════════════════════════════════════════════════════
|
|
6
|
+
|
|
7
|
+
-- 1. Extensions
|
|
8
|
+
CREATE EXTENSION IF NOT EXISTS pg_cron;
|
|
9
|
+
GRANT USAGE ON SCHEMA cron TO postgres;
|
|
10
|
+
|
|
11
|
+
-- 2. scheduler_tasks table
|
|
12
|
+
CREATE TABLE IF NOT EXISTS scheduler_tasks (
|
|
13
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
14
|
+
name TEXT NOT NULL,
|
|
15
|
+
description TEXT NOT NULL DEFAULT '',
|
|
16
|
+
cron TEXT NOT NULL,
|
|
17
|
+
enabled BOOLEAN NOT NULL DEFAULT true,
|
|
18
|
+
steps JSONB NOT NULL DEFAULT '[]',
|
|
19
|
+
summary_email TEXT,
|
|
20
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
21
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
22
|
+
last_run_at TIMESTAMPTZ,
|
|
23
|
+
next_run_at TIMESTAMPTZ,
|
|
24
|
+
run_count INTEGER NOT NULL DEFAULT 0
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
-- 3. scheduler_runs table
|
|
28
|
+
CREATE TABLE IF NOT EXISTS scheduler_runs (
|
|
29
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
30
|
+
task_id UUID NOT NULL REFERENCES scheduler_tasks(id) ON DELETE CASCADE,
|
|
31
|
+
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
32
|
+
finished_at TIMESTAMPTZ,
|
|
33
|
+
status TEXT NOT NULL DEFAULT 'running' CHECK (status IN ('running','success','failed')),
|
|
34
|
+
output TEXT,
|
|
35
|
+
error TEXT,
|
|
36
|
+
step_results JSONB NOT NULL DEFAULT '[]'
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
-- 4. user_config table (stores API keys synced from your machine)
|
|
40
|
+
CREATE TABLE IF NOT EXISTS user_config (
|
|
41
|
+
key TEXT PRIMARY KEY,
|
|
42
|
+
value TEXT NOT NULL,
|
|
43
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
-- 5. Row Level Security — service role only (your app uses service role key)
|
|
47
|
+
ALTER TABLE scheduler_tasks ENABLE ROW LEVEL SECURITY;
|
|
48
|
+
ALTER TABLE scheduler_runs ENABLE ROW LEVEL SECURITY;
|
|
49
|
+
ALTER TABLE user_config ENABLE ROW LEVEL SECURITY;
|
|
50
|
+
|
|
51
|
+
DROP POLICY IF EXISTS "Service role full access" ON scheduler_tasks;
|
|
52
|
+
DROP POLICY IF EXISTS "Service role full access" ON scheduler_runs;
|
|
53
|
+
DROP POLICY IF EXISTS "Service role full access" ON user_config;
|
|
54
|
+
|
|
55
|
+
CREATE POLICY "Service role full access" ON scheduler_tasks FOR ALL USING (auth.role() = 'service_role');
|
|
56
|
+
CREATE POLICY "Service role full access" ON scheduler_runs FOR ALL USING (auth.role() = 'service_role');
|
|
57
|
+
CREATE POLICY "Service role full access" ON user_config FOR ALL USING (auth.role() = 'service_role');
|
|
58
|
+
|
|
59
|
+
-- 6. Schedule the Edge Function to fire every minute
|
|
60
|
+
-- Replace YOUR_PROJECT_REF and YOUR_SERVICE_ROLE_KEY below before running.
|
|
61
|
+
-- Get them from: Supabase Dashboard → Settings → API
|
|
62
|
+
SELECT cron.unschedule('jimmy-scheduler-tick') WHERE EXISTS (
|
|
63
|
+
SELECT 1 FROM cron.job WHERE jobname = 'jimmy-scheduler-tick'
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
SELECT cron.schedule(
|
|
67
|
+
'jimmy-scheduler-tick',
|
|
68
|
+
'* * * * *',
|
|
69
|
+
$$
|
|
70
|
+
SELECT net.http_post(
|
|
71
|
+
url := 'https://YOUR_PROJECT_REF.supabase.co/functions/v1/scheduler-tick',
|
|
72
|
+
headers := jsonb_build_object(
|
|
73
|
+
'Content-Type', 'application/json',
|
|
74
|
+
'Authorization', 'Bearer YOUR_SERVICE_ROLE_KEY'
|
|
75
|
+
),
|
|
76
|
+
body := '{}'::jsonb
|
|
77
|
+
);
|
|
78
|
+
$$
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
-- ════════════════════════════════════════════════════════════════════════════════
|
|
82
|
+
-- Done! Verify setup:
|
|
83
|
+
SELECT jobid, jobname, schedule, active FROM cron.job;
|
|
84
|
+
-- ════════════════════════════════════════════════════════════════════════════════
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
-- ════════════════════════════════════════════════════════════════════════════════
|
|
2
|
+
-- Jimmy Scheduler Status Check
|
|
3
|
+
-- Copy and paste each section into Supabase SQL Editor to debug your scheduler
|
|
4
|
+
-- ════════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
-- 1. CHECK IF PG_CRON JOB EXISTS
|
|
7
|
+
-- Should show: jimmy-scheduler-tick job running every minute
|
|
8
|
+
select
|
|
9
|
+
jobid,
|
|
10
|
+
jobname,
|
|
11
|
+
schedule,
|
|
12
|
+
active,
|
|
13
|
+
database
|
|
14
|
+
from cron.job;
|
|
15
|
+
|
|
16
|
+
-- 2. CHECK RECENT CRON JOB RUNS
|
|
17
|
+
-- Should show executions every minute with status and return messages
|
|
18
|
+
select
|
|
19
|
+
jobname,
|
|
20
|
+
runid,
|
|
21
|
+
status,
|
|
22
|
+
return_message,
|
|
23
|
+
start_time,
|
|
24
|
+
end_time
|
|
25
|
+
from cron.job_run_details
|
|
26
|
+
where jobname = 'jimmy-scheduler-tick'
|
|
27
|
+
order by start_time desc
|
|
28
|
+
limit 10;
|
|
29
|
+
|
|
30
|
+
-- 3. CHECK YOUR TASKS
|
|
31
|
+
-- Shows all tasks, when they should run next, and run counts
|
|
32
|
+
select
|
|
33
|
+
id,
|
|
34
|
+
name,
|
|
35
|
+
enabled,
|
|
36
|
+
cron,
|
|
37
|
+
next_run_at,
|
|
38
|
+
last_run_at,
|
|
39
|
+
run_count,
|
|
40
|
+
summary_email,
|
|
41
|
+
jsonb_array_length(steps::jsonb) as step_count
|
|
42
|
+
from scheduler_tasks
|
|
43
|
+
order by created_at desc;
|
|
44
|
+
|
|
45
|
+
-- 4. CHECK TASK RUNS (EXECUTION HISTORY)
|
|
46
|
+
-- Shows actual task executions with results
|
|
47
|
+
select
|
|
48
|
+
r.id,
|
|
49
|
+
t.name as task_name,
|
|
50
|
+
r.status,
|
|
51
|
+
r.started_at,
|
|
52
|
+
r.finished_at,
|
|
53
|
+
r.output,
|
|
54
|
+
r.error,
|
|
55
|
+
jsonb_array_length(r.step_results::jsonb) as steps_executed
|
|
56
|
+
from scheduler_runs r
|
|
57
|
+
left join scheduler_tasks t on r.task_id = t.id
|
|
58
|
+
order by r.started_at desc
|
|
59
|
+
limit 20;
|
|
60
|
+
|
|
61
|
+
-- 5. CHECK CREDENTIALS IN USER_CONFIG
|
|
62
|
+
-- Should show all your API keys (values hidden for security)
|
|
63
|
+
select
|
|
64
|
+
key,
|
|
65
|
+
case
|
|
66
|
+
when key like '%token%' or key like '%secret%' or key like '%key%'
|
|
67
|
+
then '***HIDDEN***'
|
|
68
|
+
else left(value, 20) || '...'
|
|
69
|
+
end as value_preview,
|
|
70
|
+
updated_at
|
|
71
|
+
from user_config
|
|
72
|
+
order by key;
|
|
73
|
+
|
|
74
|
+
-- 6. CHECK IF TASKS ARE DUE NOW
|
|
75
|
+
-- Shows tasks that should run in the next execution
|
|
76
|
+
select
|
|
77
|
+
id,
|
|
78
|
+
name,
|
|
79
|
+
cron,
|
|
80
|
+
next_run_at,
|
|
81
|
+
now() as current_time,
|
|
82
|
+
next_run_at <= now() as is_due
|
|
83
|
+
from scheduler_tasks
|
|
84
|
+
where enabled = true;
|
|
85
|
+
|
|
86
|
+
-- ════════════════════════════════════════════════════════════════════════════════
|
|
87
|
+
-- TROUBLESHOOTING TIPS:
|
|
88
|
+
-- ════════════════════════════════════════════════════════════════════════════════
|
|
89
|
+
--
|
|
90
|
+
-- ❌ No rows in cron.job?
|
|
91
|
+
-- → You haven't run the setup SQL yet. Run: supabase/functions/scheduler-tick/setup.sql
|
|
92
|
+
--
|
|
93
|
+
-- ❌ cron.job exists but no runs in job_run_details?
|
|
94
|
+
-- → Check if pg_cron is enabled: CREATE EXTENSION IF NOT EXISTS pg_cron;
|
|
95
|
+
-- → Check Edge Function logs: supabase functions logs scheduler-tick
|
|
96
|
+
--
|
|
97
|
+
-- ❌ Cron runs but scheduler_runs table is empty?
|
|
98
|
+
-- → Edge Function might be failing. Check logs with: supabase functions logs scheduler-tick
|
|
99
|
+
-- → Verify secrets are set: supabase secrets list
|
|
100
|
+
--
|
|
101
|
+
-- ❌ scheduler_runs shows "failed" status?
|
|
102
|
+
-- → Check the error column in scheduler_runs
|
|
103
|
+
-- → Check if credentials are in user_config (query #5 above)
|
|
104
|
+
-- → Run: jimmy sync-credentials
|
|
105
|
+
--
|
|
106
|
+
-- ❌ Task says "completed" but no email sent?
|
|
107
|
+
-- → Check if summary_email is set on the task
|
|
108
|
+
-- → Verify google_refresh_token is in user_config
|
|
109
|
+
-- → Check step_results in scheduler_runs for email step errors
|
|
110
|
+
--
|
|
111
|
+
-- ════════════════════════════════════════════════════════════════════════════════
|
|
112
|
+
|
|
113
|
+
-- MANUAL EDGE FUNCTION TRIGGER (for testing):
|
|
114
|
+
-- Replace <PROJECT_REF> and <SERVICE_ROLE_KEY> with your actual values
|
|
115
|
+
/*
|
|
116
|
+
select net.http_post(
|
|
117
|
+
url := 'https://<PROJECT_REF>.supabase.co/functions/v1/scheduler-tick',
|
|
118
|
+
headers := jsonb_build_object(
|
|
119
|
+
'Content-Type', 'application/json',
|
|
120
|
+
'Authorization', 'Bearer <SERVICE_ROLE_KEY>'
|
|
121
|
+
),
|
|
122
|
+
body := '{}'::jsonb
|
|
123
|
+
) as response;
|
|
124
|
+
*/
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Syncs local credentials to Supabase user_config table.
|
|
3
|
+
* The Edge Function reads from here instead of static secrets,
|
|
4
|
+
* so re-auth is fully automatic — no manual `supabase secrets set` needed.
|
|
5
|
+
*
|
|
6
|
+
* Required SQL (run once in Supabase SQL editor):
|
|
7
|
+
* create table user_config (
|
|
8
|
+
* key text primary key,
|
|
9
|
+
* value text not null,
|
|
10
|
+
* updated_at timestamptz default now()
|
|
11
|
+
* );
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { createClient } from "@supabase/supabase-js";
|
|
15
|
+
|
|
16
|
+
function getDb() {
|
|
17
|
+
const url = process.env.SUPABASE_URL;
|
|
18
|
+
const key = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_KEY;
|
|
19
|
+
if (!url || !key) return null;
|
|
20
|
+
return createClient(url, key);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Upsert a single key-value pair into user_config */
|
|
24
|
+
async function upsertConfig(key: string, value: string): Promise<void> {
|
|
25
|
+
const db = getDb();
|
|
26
|
+
if (!db) return; // Supabase not configured yet, skip silently
|
|
27
|
+
const { error } = await db.from("user_config").upsert(
|
|
28
|
+
{ key, value, updated_at: new Date().toISOString() },
|
|
29
|
+
{ onConflict: "key" }
|
|
30
|
+
);
|
|
31
|
+
if (error) {
|
|
32
|
+
// Non-fatal — log but don't crash the auth flow
|
|
33
|
+
console.warn(`[config-sync] Failed to sync "${key}": ${error.message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Read a value from user_config */
|
|
38
|
+
export async function readRemoteConfig(key: string): Promise<string | null> {
|
|
39
|
+
const db = getDb();
|
|
40
|
+
if (!db) return null;
|
|
41
|
+
const { data, error } = await db
|
|
42
|
+
.from("user_config")
|
|
43
|
+
.select("value")
|
|
44
|
+
.eq("key", key)
|
|
45
|
+
.single();
|
|
46
|
+
if (error || !data) return null;
|
|
47
|
+
return (data as any).value as string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Called after Gmail OAuth completes.
|
|
52
|
+
* Pushes the new refresh token to Supabase so the Edge Function picks it up automatically.
|
|
53
|
+
*/
|
|
54
|
+
export async function syncGoogleRefreshToken(refreshToken: string): Promise<void> {
|
|
55
|
+
await upsertConfig("google_refresh_token", refreshToken);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Sync all relevant env vars to Supabase user_config.
|
|
60
|
+
* Call this on first deploy or when API keys change.
|
|
61
|
+
* Keys stored: openrouter_key, groq_api_key, firecrawl_key,
|
|
62
|
+
* google_client_id, google_client_secret, google_refresh_token
|
|
63
|
+
*/
|
|
64
|
+
export async function syncAllSecrets(): Promise<void> {
|
|
65
|
+
const pairs: [string, string | undefined][] = [
|
|
66
|
+
["openrouter_key", process.env.OPENROUTER_KEY],
|
|
67
|
+
["openrouter_model", process.env.OPENROUTER_MODEL],
|
|
68
|
+
["groq_api_key", process.env.GROQ_API_KEY],
|
|
69
|
+
["firecrawl_key", process.env.FIRECRAWL_KEY],
|
|
70
|
+
["google_api_key", process.env.GOOGLE_GENERATIVE_AI_API_KEY], // for Gemini in Edge Function
|
|
71
|
+
["google_client_id", process.env.GOOGLE_CLIENT_ID],
|
|
72
|
+
["google_client_secret", process.env.GOOGLE_CLIENT_SECRET],
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
// Also sync Google refresh token from local file if it exists
|
|
76
|
+
try {
|
|
77
|
+
const { loadConfig } = await import("../email_ops/email_pass_store");
|
|
78
|
+
const googleConfig = loadConfig();
|
|
79
|
+
if (googleConfig?.refresh_token) {
|
|
80
|
+
pairs.push(["google_refresh_token", googleConfig.refresh_token]);
|
|
81
|
+
}
|
|
82
|
+
} catch (e) {
|
|
83
|
+
console.warn("[config-sync] Could not load Google refresh token from local file");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
await Promise.allSettled(
|
|
87
|
+
pairs
|
|
88
|
+
.filter(([, v]) => v)
|
|
89
|
+
.map(([k, v]) => upsertConfig(k, v!))
|
|
90
|
+
);
|
|
91
|
+
}
|