@owloops/browserbird 1.0.2 → 1.0.4
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/bin/browserbird +7 -1
- package/dist/db-BsYEYsul.mjs +1011 -0
- package/dist/index.mjs +4749 -0
- package/package.json +6 -3
- package/src/channel/blocks.ts +0 -485
- package/src/channel/coalesce.ts +0 -79
- package/src/channel/commands.ts +0 -216
- package/src/channel/handler.ts +0 -272
- package/src/channel/slack.ts +0 -573
- package/src/channel/types.ts +0 -59
- package/src/cli/banner.ts +0 -10
- package/src/cli/birds.ts +0 -396
- package/src/cli/config.ts +0 -77
- package/src/cli/doctor.ts +0 -63
- package/src/cli/index.ts +0 -5
- package/src/cli/jobs.ts +0 -166
- package/src/cli/logs.ts +0 -67
- package/src/cli/run.ts +0 -148
- package/src/cli/sessions.ts +0 -158
- package/src/cli/style.ts +0 -19
- package/src/config.ts +0 -291
- package/src/core/logger.ts +0 -78
- package/src/core/redact.ts +0 -75
- package/src/core/types.ts +0 -83
- package/src/core/uid.ts +0 -26
- package/src/core/utils.ts +0 -137
- package/src/cron/parse.ts +0 -146
- package/src/cron/scheduler.ts +0 -242
- package/src/daemon.ts +0 -169
- package/src/db/auth.ts +0 -49
- package/src/db/birds.ts +0 -357
- package/src/db/core.ts +0 -377
- package/src/db/index.ts +0 -10
- package/src/db/jobs.ts +0 -289
- package/src/db/logs.ts +0 -64
- package/src/db/messages.ts +0 -79
- package/src/db/path.ts +0 -30
- package/src/db/sessions.ts +0 -165
- package/src/jobs.ts +0 -140
- package/src/provider/claude.test.ts +0 -95
- package/src/provider/claude.ts +0 -196
- package/src/provider/opencode.test.ts +0 -169
- package/src/provider/opencode.ts +0 -248
- package/src/provider/session.ts +0 -65
- package/src/provider/spawn.ts +0 -173
- package/src/provider/stream.ts +0 -67
- package/src/provider/types.ts +0 -24
- package/src/server/auth.ts +0 -135
- package/src/server/health.ts +0 -87
- package/src/server/http.ts +0 -132
- package/src/server/index.ts +0 -6
- package/src/server/lifecycle.ts +0 -135
- package/src/server/routes.ts +0 -1199
- package/src/server/sse.ts +0 -54
- package/src/server/static.ts +0 -45
- package/src/server/vnc-proxy.ts +0 -75
package/src/db/birds.ts
DELETED
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
/** @fileoverview Bird (cron job) and flight (cron run) persistence. */
|
|
2
|
-
|
|
3
|
-
import type { PaginatedResult } from './core.ts';
|
|
4
|
-
import {
|
|
5
|
-
getDb,
|
|
6
|
-
paginate,
|
|
7
|
-
parseSort,
|
|
8
|
-
buildSearchClause,
|
|
9
|
-
DEFAULT_PER_PAGE,
|
|
10
|
-
MAX_PER_PAGE,
|
|
11
|
-
} from './core.ts';
|
|
12
|
-
import { generateUid, UID_PREFIX } from '../core/uid.ts';
|
|
13
|
-
|
|
14
|
-
export const SYSTEM_CRON_PREFIX = '__bb_';
|
|
15
|
-
|
|
16
|
-
export interface CronJobRow {
|
|
17
|
-
uid: string;
|
|
18
|
-
name: string;
|
|
19
|
-
agent_id: string;
|
|
20
|
-
schedule: string;
|
|
21
|
-
prompt: string;
|
|
22
|
-
target_channel_id: string | null;
|
|
23
|
-
active_hours_start: string | null;
|
|
24
|
-
active_hours_end: string | null;
|
|
25
|
-
timezone: string;
|
|
26
|
-
enabled: number;
|
|
27
|
-
failure_count: number;
|
|
28
|
-
last_run: string | null;
|
|
29
|
-
last_status: string | null;
|
|
30
|
-
created_at: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface CronRunRow {
|
|
34
|
-
uid: string;
|
|
35
|
-
job_uid: string;
|
|
36
|
-
started_at: string;
|
|
37
|
-
finished_at: string | null;
|
|
38
|
-
status: 'running' | 'success' | 'error';
|
|
39
|
-
result: string | null;
|
|
40
|
-
error: string | null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface FlightRow {
|
|
44
|
-
uid: string;
|
|
45
|
-
job_uid: string;
|
|
46
|
-
bird_uid: string;
|
|
47
|
-
bird_name: string;
|
|
48
|
-
started_at: string;
|
|
49
|
-
finished_at: string | null;
|
|
50
|
-
status: 'running' | 'success' | 'error';
|
|
51
|
-
result: string | null;
|
|
52
|
-
error: string | null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface ListFlightsFilters {
|
|
56
|
-
birdUid?: string;
|
|
57
|
-
status?: string;
|
|
58
|
-
system?: boolean;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export interface UpdateCronJobFields {
|
|
62
|
-
name?: string;
|
|
63
|
-
schedule?: string;
|
|
64
|
-
prompt?: string;
|
|
65
|
-
targetChannelId?: string | null;
|
|
66
|
-
agentId?: string;
|
|
67
|
-
timezone?: string;
|
|
68
|
-
activeHoursStart?: string | null;
|
|
69
|
-
activeHoursEnd?: string | null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const CRON_SORT_COLUMNS = new Set([
|
|
73
|
-
'uid',
|
|
74
|
-
'name',
|
|
75
|
-
'schedule',
|
|
76
|
-
'agent_id',
|
|
77
|
-
'enabled',
|
|
78
|
-
'last_run',
|
|
79
|
-
'created_at',
|
|
80
|
-
]);
|
|
81
|
-
const CRON_SEARCH_COLUMNS = ['uid', 'name', 'prompt', 'schedule'] as const;
|
|
82
|
-
|
|
83
|
-
export function listCronJobs(
|
|
84
|
-
page = 1,
|
|
85
|
-
perPage = DEFAULT_PER_PAGE,
|
|
86
|
-
includeSystem = false,
|
|
87
|
-
sort?: string,
|
|
88
|
-
search?: string,
|
|
89
|
-
): PaginatedResult<CronJobRow> {
|
|
90
|
-
const where = includeSystem ? '' : `name NOT LIKE '${SYSTEM_CRON_PREFIX}%'`;
|
|
91
|
-
return paginate<CronJobRow>('cron_jobs', page, perPage, {
|
|
92
|
-
where,
|
|
93
|
-
defaultSort: 'created_at ASC',
|
|
94
|
-
sort,
|
|
95
|
-
search,
|
|
96
|
-
allowedSortColumns: CRON_SORT_COLUMNS,
|
|
97
|
-
searchColumns: CRON_SEARCH_COLUMNS,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function getEnabledCronJobs(): CronJobRow[] {
|
|
102
|
-
return getDb()
|
|
103
|
-
.prepare('SELECT * FROM cron_jobs WHERE enabled = 1 ORDER BY created_at')
|
|
104
|
-
.all() as unknown as CronJobRow[];
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export function createCronJob(
|
|
108
|
-
name: string,
|
|
109
|
-
schedule: string,
|
|
110
|
-
prompt: string,
|
|
111
|
-
targetChannelId?: string,
|
|
112
|
-
agentId?: string,
|
|
113
|
-
timezone?: string,
|
|
114
|
-
activeHoursStart?: string,
|
|
115
|
-
activeHoursEnd?: string,
|
|
116
|
-
): CronJobRow {
|
|
117
|
-
const uid = generateUid(UID_PREFIX.bird);
|
|
118
|
-
const stmt = getDb().prepare(
|
|
119
|
-
`INSERT INTO cron_jobs (uid, name, schedule, prompt, target_channel_id, agent_id, timezone, active_hours_start, active_hours_end)
|
|
120
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
121
|
-
RETURNING *`,
|
|
122
|
-
);
|
|
123
|
-
return stmt.get(
|
|
124
|
-
uid,
|
|
125
|
-
name,
|
|
126
|
-
schedule,
|
|
127
|
-
prompt,
|
|
128
|
-
targetChannelId ?? null,
|
|
129
|
-
agentId ?? 'default',
|
|
130
|
-
timezone ?? 'UTC',
|
|
131
|
-
activeHoursStart ?? null,
|
|
132
|
-
activeHoursEnd ?? null,
|
|
133
|
-
) as unknown as CronJobRow;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export function updateCronJobStatus(jobUid: string, status: string, failureCount: number): void {
|
|
137
|
-
const stmt = getDb().prepare(
|
|
138
|
-
`UPDATE cron_jobs SET last_run = datetime('now'), last_status = ?, failure_count = ? WHERE uid = ?`,
|
|
139
|
-
);
|
|
140
|
-
stmt.run(status, failureCount, jobUid);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export function getCronJob(jobUid: string): CronJobRow | undefined {
|
|
144
|
-
return getDb().prepare('SELECT * FROM cron_jobs WHERE uid = ?').get(jobUid) as unknown as
|
|
145
|
-
| CronJobRow
|
|
146
|
-
| undefined;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export function setCronJobEnabled(jobUid: string, enabled: boolean): boolean {
|
|
150
|
-
const result = getDb()
|
|
151
|
-
.prepare('UPDATE cron_jobs SET enabled = ? WHERE uid = ?')
|
|
152
|
-
.run(enabled ? 1 : 0, jobUid);
|
|
153
|
-
return Number(result.changes) > 0;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export function updateCronJob(jobUid: string, fields: UpdateCronJobFields): CronJobRow | undefined {
|
|
157
|
-
const sets: string[] = [];
|
|
158
|
-
const params: (string | number | null)[] = [];
|
|
159
|
-
|
|
160
|
-
if (fields.name !== undefined) {
|
|
161
|
-
sets.push('name = ?');
|
|
162
|
-
params.push(fields.name);
|
|
163
|
-
}
|
|
164
|
-
if (fields.schedule !== undefined) {
|
|
165
|
-
sets.push('schedule = ?');
|
|
166
|
-
params.push(fields.schedule);
|
|
167
|
-
}
|
|
168
|
-
if (fields.prompt !== undefined) {
|
|
169
|
-
sets.push('prompt = ?');
|
|
170
|
-
params.push(fields.prompt);
|
|
171
|
-
}
|
|
172
|
-
if (fields.targetChannelId !== undefined) {
|
|
173
|
-
sets.push('target_channel_id = ?');
|
|
174
|
-
params.push(fields.targetChannelId);
|
|
175
|
-
}
|
|
176
|
-
if (fields.agentId !== undefined) {
|
|
177
|
-
sets.push('agent_id = ?');
|
|
178
|
-
params.push(fields.agentId);
|
|
179
|
-
}
|
|
180
|
-
if (fields.timezone !== undefined) {
|
|
181
|
-
sets.push('timezone = ?');
|
|
182
|
-
params.push(fields.timezone);
|
|
183
|
-
}
|
|
184
|
-
if (fields.activeHoursStart !== undefined) {
|
|
185
|
-
sets.push('active_hours_start = ?');
|
|
186
|
-
params.push(fields.activeHoursStart);
|
|
187
|
-
}
|
|
188
|
-
if (fields.activeHoursEnd !== undefined) {
|
|
189
|
-
sets.push('active_hours_end = ?');
|
|
190
|
-
params.push(fields.activeHoursEnd);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (sets.length === 0) return getCronJob(jobUid);
|
|
194
|
-
|
|
195
|
-
params.push(jobUid);
|
|
196
|
-
return getDb()
|
|
197
|
-
.prepare(`UPDATE cron_jobs SET ${sets.join(', ')} WHERE uid = ? RETURNING *`)
|
|
198
|
-
.get(...params) as unknown as CronJobRow | undefined;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export function deleteCronJob(jobUid: string): boolean {
|
|
202
|
-
const d = getDb();
|
|
203
|
-
d.exec('BEGIN');
|
|
204
|
-
try {
|
|
205
|
-
d.prepare('DELETE FROM cron_runs WHERE job_uid = ?').run(jobUid);
|
|
206
|
-
d.prepare('UPDATE jobs SET cron_job_uid = NULL WHERE cron_job_uid = ?').run(jobUid);
|
|
207
|
-
const result = d.prepare('DELETE FROM cron_jobs WHERE uid = ?').run(jobUid);
|
|
208
|
-
d.exec('COMMIT');
|
|
209
|
-
return Number(result.changes) > 0;
|
|
210
|
-
} catch (err) {
|
|
211
|
-
d.exec('ROLLBACK');
|
|
212
|
-
throw err;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const FLIGHT_SORT_COLUMNS = new Set(['uid', 'started_at', 'finished_at', 'status', 'bird_name']);
|
|
217
|
-
const FLIGHT_SORT_MAP: Record<string, string> = { bird_name: 'j.name' };
|
|
218
|
-
const FLIGHT_SEARCH_COLUMNS = [
|
|
219
|
-
'r.uid',
|
|
220
|
-
'r.job_uid',
|
|
221
|
-
'j.uid',
|
|
222
|
-
'j.name',
|
|
223
|
-
'r.error',
|
|
224
|
-
'r.result',
|
|
225
|
-
] as const;
|
|
226
|
-
|
|
227
|
-
export function listFlights(
|
|
228
|
-
page = 1,
|
|
229
|
-
perPage = DEFAULT_PER_PAGE,
|
|
230
|
-
filters: ListFlightsFilters = {},
|
|
231
|
-
sort?: string,
|
|
232
|
-
search?: string,
|
|
233
|
-
): PaginatedResult<FlightRow> {
|
|
234
|
-
const pp = Math.min(Math.max(perPage, 1), MAX_PER_PAGE);
|
|
235
|
-
const p = Math.max(page, 1);
|
|
236
|
-
const offset = (p - 1) * pp;
|
|
237
|
-
|
|
238
|
-
const conditions: string[] = [];
|
|
239
|
-
const params: (string | number)[] = [];
|
|
240
|
-
|
|
241
|
-
if (!filters.system) {
|
|
242
|
-
conditions.push(`j.name NOT LIKE '${SYSTEM_CRON_PREFIX}%'`);
|
|
243
|
-
}
|
|
244
|
-
if (filters.birdUid != null) {
|
|
245
|
-
conditions.push('r.job_uid = ?');
|
|
246
|
-
params.push(filters.birdUid);
|
|
247
|
-
}
|
|
248
|
-
if (filters.status) {
|
|
249
|
-
conditions.push('r.status = ?');
|
|
250
|
-
params.push(filters.status);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (search) {
|
|
254
|
-
const sc = buildSearchClause(search, FLIGHT_SEARCH_COLUMNS);
|
|
255
|
-
if (sc.sql) {
|
|
256
|
-
conditions.push(sc.sql);
|
|
257
|
-
params.push(...sc.params);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
262
|
-
|
|
263
|
-
let orderBy = parseSort(sort, FLIGHT_SORT_COLUMNS, 'r.started_at DESC');
|
|
264
|
-
for (const [key, qualified] of Object.entries(FLIGHT_SORT_MAP)) {
|
|
265
|
-
orderBy = orderBy.replaceAll(key, qualified);
|
|
266
|
-
}
|
|
267
|
-
orderBy = orderBy.replace(/(?<![a-z.])\b(uid|started_at|finished_at|status)\b/g, 'r.$1');
|
|
268
|
-
|
|
269
|
-
const countRow = getDb()
|
|
270
|
-
.prepare(
|
|
271
|
-
`SELECT COUNT(*) as count FROM cron_runs r JOIN cron_jobs j ON j.uid = r.job_uid ${where}`,
|
|
272
|
-
)
|
|
273
|
-
.get(...params) as unknown as { count: number };
|
|
274
|
-
const totalItems = countRow.count;
|
|
275
|
-
const totalPages = Math.max(Math.ceil(totalItems / pp), 1);
|
|
276
|
-
|
|
277
|
-
const items = getDb()
|
|
278
|
-
.prepare(
|
|
279
|
-
`SELECT r.uid, r.job_uid, j.uid as bird_uid, j.name as bird_name,
|
|
280
|
-
r.started_at, r.finished_at, r.status, r.result, r.error
|
|
281
|
-
FROM cron_runs r
|
|
282
|
-
JOIN cron_jobs j ON j.uid = r.job_uid
|
|
283
|
-
${where}
|
|
284
|
-
ORDER BY ${orderBy}
|
|
285
|
-
LIMIT ? OFFSET ?`,
|
|
286
|
-
)
|
|
287
|
-
.all(...params, pp, offset) as unknown as FlightRow[];
|
|
288
|
-
|
|
289
|
-
return { items, page: p, perPage: pp, totalItems, totalPages };
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
export function createCronRun(jobUid: string): CronRunRow {
|
|
293
|
-
const uid = generateUid(UID_PREFIX.flight);
|
|
294
|
-
const stmt = getDb().prepare('INSERT INTO cron_runs (uid, job_uid) VALUES (?, ?) RETURNING *');
|
|
295
|
-
return stmt.get(uid, jobUid) as unknown as CronRunRow;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
export function completeCronRun(
|
|
299
|
-
runUid: string,
|
|
300
|
-
status: 'success' | 'error',
|
|
301
|
-
result?: string,
|
|
302
|
-
error?: string,
|
|
303
|
-
): void {
|
|
304
|
-
const stmt = getDb().prepare(
|
|
305
|
-
`UPDATE cron_runs SET finished_at = datetime('now'), status = ?, result = ?, error = ? WHERE uid = ?`,
|
|
306
|
-
);
|
|
307
|
-
stmt.run(status, result ?? null, error ?? null, runUid);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export interface FlightStats {
|
|
311
|
-
running: number;
|
|
312
|
-
completed: number;
|
|
313
|
-
failed: number;
|
|
314
|
-
total: number;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
export function getFlightStats(): FlightStats {
|
|
318
|
-
const rows = getDb()
|
|
319
|
-
.prepare(
|
|
320
|
-
`SELECT r.status, COUNT(*) as count
|
|
321
|
-
FROM cron_runs r
|
|
322
|
-
JOIN cron_jobs j ON j.uid = r.job_uid
|
|
323
|
-
WHERE j.name NOT LIKE '${SYSTEM_CRON_PREFIX}%'
|
|
324
|
-
GROUP BY r.status`,
|
|
325
|
-
)
|
|
326
|
-
.all() as unknown as Array<{ status: string; count: number }>;
|
|
327
|
-
|
|
328
|
-
const stats: FlightStats = { running: 0, completed: 0, failed: 0, total: 0 };
|
|
329
|
-
for (const row of rows) {
|
|
330
|
-
if (row.status === 'running') stats.running = row.count;
|
|
331
|
-
else if (row.status === 'success') stats.completed = row.count;
|
|
332
|
-
else if (row.status === 'error') stats.failed = row.count;
|
|
333
|
-
stats.total += row.count;
|
|
334
|
-
}
|
|
335
|
-
return stats;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
export function deleteOldCronRuns(retentionDays: number): number {
|
|
339
|
-
const stmt = getDb().prepare(
|
|
340
|
-
`DELETE FROM cron_runs WHERE started_at < datetime('now', ? || ' days')`,
|
|
341
|
-
);
|
|
342
|
-
const result = stmt.run(`-${retentionDays}`);
|
|
343
|
-
return Number(result.changes);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
export function ensureSystemCronJob(name: string, schedule: string, prompt: string): void {
|
|
347
|
-
const existing = getDb().prepare('SELECT uid FROM cron_jobs WHERE name = ?').get(name) as
|
|
348
|
-
| unknown
|
|
349
|
-
| undefined;
|
|
350
|
-
if (existing) return;
|
|
351
|
-
const uid = generateUid(UID_PREFIX.bird);
|
|
352
|
-
getDb()
|
|
353
|
-
.prepare(
|
|
354
|
-
`INSERT INTO cron_jobs (uid, name, schedule, prompt, agent_id) VALUES (?, ?, ?, ?, 'system')`,
|
|
355
|
-
)
|
|
356
|
-
.run(uid, name, schedule, prompt);
|
|
357
|
-
}
|