@owloops/browserbird 1.0.2 → 1.0.3
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 +4748 -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/cli/birds.ts
DELETED
|
@@ -1,396 +0,0 @@
|
|
|
1
|
-
/** @fileoverview Birds command: manage scheduled birds (cron jobs). */
|
|
2
|
-
|
|
3
|
-
import { parseArgs } from 'node:util';
|
|
4
|
-
import { logger } from '../core/logger.ts';
|
|
5
|
-
import { shortUid } from '../core/uid.ts';
|
|
6
|
-
import { formatDuration, deriveBirdName, printTable, unknownSubcommand } from '../core/utils.ts';
|
|
7
|
-
import { c } from './style.ts';
|
|
8
|
-
import {
|
|
9
|
-
openDatabase,
|
|
10
|
-
closeDatabase,
|
|
11
|
-
resolveByUid,
|
|
12
|
-
listCronJobs,
|
|
13
|
-
createCronJob,
|
|
14
|
-
updateCronJob,
|
|
15
|
-
setCronJobEnabled,
|
|
16
|
-
deleteCronJob,
|
|
17
|
-
listFlights,
|
|
18
|
-
resolveDbPathFromArgv,
|
|
19
|
-
} from '../db/index.ts';
|
|
20
|
-
import type { CronJobRow } from '../db/index.ts';
|
|
21
|
-
import { enqueue } from '../jobs.ts';
|
|
22
|
-
|
|
23
|
-
export const BIRDS_HELP = `
|
|
24
|
-
${c('cyan', 'usage:')} browserbird birds <subcommand> [options]
|
|
25
|
-
|
|
26
|
-
manage scheduled birds.
|
|
27
|
-
|
|
28
|
-
${c('dim', 'subcommands:')}
|
|
29
|
-
|
|
30
|
-
${c('cyan', 'list')} list all birds
|
|
31
|
-
${c('cyan', 'add')} <schedule> <prompt> add a new bird
|
|
32
|
-
${c('cyan', 'edit')} <uid> edit a bird
|
|
33
|
-
${c('cyan', 'remove')} <uid> remove a bird
|
|
34
|
-
${c('cyan', 'enable')} <uid> enable a bird
|
|
35
|
-
${c('cyan', 'disable')} <uid> disable a bird
|
|
36
|
-
${c('cyan', 'fly')} <uid> trigger a bird manually
|
|
37
|
-
${c('cyan', 'flights')} <uid> show flight history for a bird
|
|
38
|
-
|
|
39
|
-
${c('dim', 'options:')}
|
|
40
|
-
|
|
41
|
-
${c('yellow', '--channel')} <id> target slack channel
|
|
42
|
-
${c('yellow', '--agent')} <id> target agent id
|
|
43
|
-
${c('yellow', '--schedule')} <expr> cron schedule expression
|
|
44
|
-
${c('yellow', '--prompt')} <text> prompt text
|
|
45
|
-
${c('yellow', '--timezone')} <tz> IANA timezone (default: UTC)
|
|
46
|
-
${c('yellow', '--active-hours')} <range> restrict runs to a time window (e.g. "09:00-17:00")
|
|
47
|
-
${c('yellow', '--limit')} <n> number of flights to show (default: 10)
|
|
48
|
-
${c('yellow', '--json')} output as JSON (with list, flights)
|
|
49
|
-
${c('yellow', '--db')} <path> database file path (env: BROWSERBIRD_DB)
|
|
50
|
-
${c('yellow', '-h, --help')} show this help
|
|
51
|
-
`.trim();
|
|
52
|
-
|
|
53
|
-
function statusColor(status: string | null | undefined): string {
|
|
54
|
-
if (status == null) return '-';
|
|
55
|
-
switch (status) {
|
|
56
|
-
case 'success':
|
|
57
|
-
case 'completed':
|
|
58
|
-
return c('green', status);
|
|
59
|
-
case 'running':
|
|
60
|
-
return c('blue', status);
|
|
61
|
-
case 'failed':
|
|
62
|
-
return c('red', status);
|
|
63
|
-
default:
|
|
64
|
-
return status;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function parseActiveHours(raw: string): { start: string; end: string } | null {
|
|
69
|
-
const match = raw.match(/^(\d{1,2}:\d{2})\s*-\s*(\d{1,2}:\d{2})$/);
|
|
70
|
-
if (!match) return null;
|
|
71
|
-
return { start: match[1]!, end: match[2]! };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function resolveBird(uidPrefix: string): CronJobRow | undefined {
|
|
75
|
-
const result = resolveByUid<CronJobRow>('cron_jobs', uidPrefix);
|
|
76
|
-
if (!result) {
|
|
77
|
-
logger.error(`bird ${uidPrefix} not found`);
|
|
78
|
-
process.exitCode = 1;
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
if ('ambiguous' in result) {
|
|
82
|
-
logger.error(
|
|
83
|
-
`ambiguous bird ID "${uidPrefix}" matches ${result.count} birds, use a longer prefix`,
|
|
84
|
-
);
|
|
85
|
-
process.exitCode = 1;
|
|
86
|
-
return undefined;
|
|
87
|
-
}
|
|
88
|
-
return result.row;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function handleBirds(argv: string[]): void {
|
|
92
|
-
const subcommand = argv[0] ?? 'list';
|
|
93
|
-
const rest = argv.slice(1);
|
|
94
|
-
|
|
95
|
-
const { values, positionals } = parseArgs({
|
|
96
|
-
args: rest,
|
|
97
|
-
options: {
|
|
98
|
-
channel: { type: 'string' },
|
|
99
|
-
agent: { type: 'string' },
|
|
100
|
-
schedule: { type: 'string' },
|
|
101
|
-
prompt: { type: 'string' },
|
|
102
|
-
timezone: { type: 'string' },
|
|
103
|
-
'active-hours': { type: 'string' },
|
|
104
|
-
limit: { type: 'string' },
|
|
105
|
-
json: { type: 'boolean', default: false },
|
|
106
|
-
},
|
|
107
|
-
allowPositionals: true,
|
|
108
|
-
strict: false,
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
openDatabase(resolveDbPathFromArgv(argv));
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
switch (subcommand) {
|
|
115
|
-
case 'list': {
|
|
116
|
-
const result = listCronJobs(1, 100);
|
|
117
|
-
if (values.json) {
|
|
118
|
-
console.log(JSON.stringify(result.items, null, 2));
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
console.log(`birds (${result.totalItems} total):`);
|
|
122
|
-
if (result.items.length === 0) {
|
|
123
|
-
console.log('\n no birds configured');
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
console.log('');
|
|
127
|
-
const rows = result.items.map((job) => [
|
|
128
|
-
c('dim', shortUid(job.uid)),
|
|
129
|
-
job.enabled ? c('green', 'enabled') : c('yellow', 'disabled'),
|
|
130
|
-
job.schedule,
|
|
131
|
-
job.agent_id,
|
|
132
|
-
job.target_channel_id ?? '-',
|
|
133
|
-
statusColor(job.last_status),
|
|
134
|
-
job.prompt.slice(0, 50),
|
|
135
|
-
]);
|
|
136
|
-
printTable(['uid', 'status', 'schedule', 'agent', 'channel', 'last', 'prompt'], rows, [
|
|
137
|
-
undefined,
|
|
138
|
-
undefined,
|
|
139
|
-
undefined,
|
|
140
|
-
undefined,
|
|
141
|
-
undefined,
|
|
142
|
-
undefined,
|
|
143
|
-
50,
|
|
144
|
-
]);
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
case 'add': {
|
|
149
|
-
const schedule = positionals[0];
|
|
150
|
-
const prompt = positionals.slice(1).join(' ') || (values.prompt as string | undefined);
|
|
151
|
-
if (!schedule || !prompt) {
|
|
152
|
-
logger.error(
|
|
153
|
-
'usage: browserbird birds add <schedule> <prompt> [--channel <id>] [--agent <id>]',
|
|
154
|
-
);
|
|
155
|
-
process.exitCode = 1;
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
const activeHoursRaw = values['active-hours'] as string | undefined;
|
|
159
|
-
let activeStart: string | undefined;
|
|
160
|
-
let activeEnd: string | undefined;
|
|
161
|
-
if (activeHoursRaw) {
|
|
162
|
-
const parsed = parseActiveHours(activeHoursRaw);
|
|
163
|
-
if (!parsed) {
|
|
164
|
-
logger.error('--active-hours must be HH:MM-HH:MM (e.g. "09:00-17:00")');
|
|
165
|
-
process.exitCode = 1;
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
activeStart = parsed.start;
|
|
169
|
-
activeEnd = parsed.end;
|
|
170
|
-
}
|
|
171
|
-
const job = createCronJob(
|
|
172
|
-
deriveBirdName(prompt),
|
|
173
|
-
schedule,
|
|
174
|
-
prompt,
|
|
175
|
-
values.channel as string | undefined,
|
|
176
|
-
values.agent as string | undefined,
|
|
177
|
-
values.timezone as string | undefined,
|
|
178
|
-
activeStart,
|
|
179
|
-
activeEnd,
|
|
180
|
-
);
|
|
181
|
-
logger.success(`bird ${shortUid(job.uid)} created: "${schedule}"`);
|
|
182
|
-
process.stderr.write(
|
|
183
|
-
c('dim', ` hint: run 'browserbird birds fly ${shortUid(job.uid)}' to trigger it now`) +
|
|
184
|
-
'\n',
|
|
185
|
-
);
|
|
186
|
-
break;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
case 'edit': {
|
|
190
|
-
const uidPrefix = positionals[0];
|
|
191
|
-
if (!uidPrefix) {
|
|
192
|
-
logger.error(
|
|
193
|
-
'usage: browserbird birds edit <uid> [--schedule <expr>] [--prompt <text>] [--channel <id>] [--agent <id>] [--timezone <tz>] [--active-hours <range>]',
|
|
194
|
-
);
|
|
195
|
-
process.exitCode = 1;
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
const bird = resolveBird(uidPrefix);
|
|
199
|
-
if (!bird) return;
|
|
200
|
-
const channel = values.channel as string | undefined;
|
|
201
|
-
const agent = values.agent as string | undefined;
|
|
202
|
-
const schedule = values.schedule as string | undefined;
|
|
203
|
-
const prompt = values.prompt as string | undefined;
|
|
204
|
-
const timezone = values.timezone as string | undefined;
|
|
205
|
-
const editActiveHoursRaw = values['active-hours'] as string | undefined;
|
|
206
|
-
let editActiveStart: string | null | undefined;
|
|
207
|
-
let editActiveEnd: string | null | undefined;
|
|
208
|
-
if (editActiveHoursRaw === '') {
|
|
209
|
-
editActiveStart = null;
|
|
210
|
-
editActiveEnd = null;
|
|
211
|
-
} else if (editActiveHoursRaw) {
|
|
212
|
-
const parsed = parseActiveHours(editActiveHoursRaw);
|
|
213
|
-
if (!parsed) {
|
|
214
|
-
logger.error('--active-hours must be HH:MM-HH:MM (e.g. "09:00-17:00"), or "" to clear');
|
|
215
|
-
process.exitCode = 1;
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
editActiveStart = parsed.start;
|
|
219
|
-
editActiveEnd = parsed.end;
|
|
220
|
-
}
|
|
221
|
-
if (
|
|
222
|
-
!schedule &&
|
|
223
|
-
!prompt &&
|
|
224
|
-
!channel &&
|
|
225
|
-
!agent &&
|
|
226
|
-
!timezone &&
|
|
227
|
-
editActiveStart === undefined
|
|
228
|
-
) {
|
|
229
|
-
logger.error(
|
|
230
|
-
'provide at least one of: --schedule, --prompt, --channel, --agent, --timezone, --active-hours',
|
|
231
|
-
);
|
|
232
|
-
process.exitCode = 1;
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
const updated = updateCronJob(bird.uid, {
|
|
236
|
-
schedule,
|
|
237
|
-
prompt,
|
|
238
|
-
name: prompt ? deriveBirdName(prompt) : undefined,
|
|
239
|
-
targetChannelId: channel !== undefined ? channel || null : undefined,
|
|
240
|
-
agentId: agent,
|
|
241
|
-
timezone,
|
|
242
|
-
activeHoursStart: editActiveStart,
|
|
243
|
-
activeHoursEnd: editActiveEnd,
|
|
244
|
-
});
|
|
245
|
-
if (updated) {
|
|
246
|
-
logger.success(`bird ${shortUid(bird.uid)} updated`);
|
|
247
|
-
process.stderr.write(
|
|
248
|
-
c('dim', ` hint: run 'browserbird birds list' to see updated birds`) + '\n',
|
|
249
|
-
);
|
|
250
|
-
} else {
|
|
251
|
-
logger.error(`bird ${shortUid(bird.uid)} not found`);
|
|
252
|
-
process.exitCode = 1;
|
|
253
|
-
}
|
|
254
|
-
break;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
case 'remove': {
|
|
258
|
-
const uidPrefix = positionals[0];
|
|
259
|
-
if (!uidPrefix) {
|
|
260
|
-
logger.error('usage: browserbird birds remove <uid>');
|
|
261
|
-
process.exitCode = 1;
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
const bird = resolveBird(uidPrefix);
|
|
265
|
-
if (!bird) return;
|
|
266
|
-
if (deleteCronJob(bird.uid)) {
|
|
267
|
-
logger.success(`bird ${shortUid(bird.uid)} removed`);
|
|
268
|
-
} else {
|
|
269
|
-
logger.error(`bird ${shortUid(bird.uid)} not found`);
|
|
270
|
-
process.exitCode = 1;
|
|
271
|
-
}
|
|
272
|
-
break;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
case 'enable':
|
|
276
|
-
case 'disable': {
|
|
277
|
-
const uidPrefix = positionals[0];
|
|
278
|
-
if (!uidPrefix) {
|
|
279
|
-
logger.error(`usage: browserbird birds ${subcommand} <uid>`);
|
|
280
|
-
process.exitCode = 1;
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
const bird = resolveBird(uidPrefix);
|
|
284
|
-
if (!bird) return;
|
|
285
|
-
const enabled = subcommand === 'enable';
|
|
286
|
-
if (setCronJobEnabled(bird.uid, enabled)) {
|
|
287
|
-
logger.success(`bird ${shortUid(bird.uid)} ${enabled ? 'enabled' : 'disabled'}`);
|
|
288
|
-
if (enabled) {
|
|
289
|
-
process.stderr.write(
|
|
290
|
-
c(
|
|
291
|
-
'dim',
|
|
292
|
-
` hint: run 'browserbird birds fly ${shortUid(bird.uid)}' to trigger it now`,
|
|
293
|
-
) + '\n',
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
} else {
|
|
297
|
-
logger.error(`bird ${shortUid(bird.uid)} not found`);
|
|
298
|
-
process.exitCode = 1;
|
|
299
|
-
}
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
case 'fly': {
|
|
304
|
-
const uidPrefix = positionals[0];
|
|
305
|
-
if (!uidPrefix) {
|
|
306
|
-
logger.error('usage: browserbird birds fly <uid>');
|
|
307
|
-
process.exitCode = 1;
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
const cronJob = resolveBird(uidPrefix);
|
|
311
|
-
if (!cronJob) return;
|
|
312
|
-
const enqueuedJob = enqueue(
|
|
313
|
-
'cron_run',
|
|
314
|
-
{
|
|
315
|
-
cronJobUid: cronJob.uid,
|
|
316
|
-
prompt: cronJob.prompt,
|
|
317
|
-
channelId: cronJob.target_channel_id,
|
|
318
|
-
agentId: cronJob.agent_id,
|
|
319
|
-
},
|
|
320
|
-
{ cronJobUid: cronJob.uid },
|
|
321
|
-
);
|
|
322
|
-
logger.success(`enqueued job #${enqueuedJob.id} for bird ${shortUid(cronJob.uid)}`);
|
|
323
|
-
process.stderr.write(
|
|
324
|
-
c(
|
|
325
|
-
'dim',
|
|
326
|
-
` hint: run 'browserbird birds flights ${shortUid(cronJob.uid)}' to check results`,
|
|
327
|
-
) + '\n',
|
|
328
|
-
);
|
|
329
|
-
break;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
case 'flights': {
|
|
333
|
-
const uidPrefix = positionals[0];
|
|
334
|
-
if (!uidPrefix) {
|
|
335
|
-
logger.error('usage: browserbird birds flights <uid>');
|
|
336
|
-
process.exitCode = 1;
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
const bird = resolveBird(uidPrefix);
|
|
340
|
-
if (!bird) return;
|
|
341
|
-
const perPage = values.limit != null ? Number(values.limit) : 10;
|
|
342
|
-
if (!Number.isFinite(perPage) || perPage < 1) {
|
|
343
|
-
logger.error('--limit must be a positive number');
|
|
344
|
-
process.exitCode = 1;
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
const result = listFlights(1, perPage, { birdUid: bird.uid });
|
|
348
|
-
if (values.json) {
|
|
349
|
-
console.log(JSON.stringify(result.items, null, 2));
|
|
350
|
-
break;
|
|
351
|
-
}
|
|
352
|
-
console.log(`flight history for bird ${shortUid(bird.uid)} (${result.totalItems} total):`);
|
|
353
|
-
if (result.items.length === 0) {
|
|
354
|
-
console.log('\n no flights recorded');
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
console.log('');
|
|
358
|
-
const rows = result.items.map((flight) => {
|
|
359
|
-
const durationMs = flight.finished_at
|
|
360
|
-
? new Date(flight.finished_at).getTime() - new Date(flight.started_at).getTime()
|
|
361
|
-
: null;
|
|
362
|
-
const duration = durationMs == null ? '-' : formatDuration(durationMs);
|
|
363
|
-
return [
|
|
364
|
-
c('dim', shortUid(flight.uid)),
|
|
365
|
-
statusColor(flight.status),
|
|
366
|
-
duration,
|
|
367
|
-
flight.started_at.slice(0, 19),
|
|
368
|
-
flight.error ?? flight.result?.slice(0, 60) ?? '',
|
|
369
|
-
];
|
|
370
|
-
});
|
|
371
|
-
printTable(['flight', 'status', 'duration', 'started', 'error / result'], rows, [
|
|
372
|
-
undefined,
|
|
373
|
-
undefined,
|
|
374
|
-
undefined,
|
|
375
|
-
undefined,
|
|
376
|
-
60,
|
|
377
|
-
]);
|
|
378
|
-
break;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
default:
|
|
382
|
-
unknownSubcommand(subcommand, 'birds', [
|
|
383
|
-
'list',
|
|
384
|
-
'add',
|
|
385
|
-
'edit',
|
|
386
|
-
'remove',
|
|
387
|
-
'enable',
|
|
388
|
-
'disable',
|
|
389
|
-
'fly',
|
|
390
|
-
'flights',
|
|
391
|
-
]);
|
|
392
|
-
}
|
|
393
|
-
} finally {
|
|
394
|
-
closeDatabase();
|
|
395
|
-
}
|
|
396
|
-
}
|
package/src/cli/config.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/** @fileoverview Config command: display merged configuration. */
|
|
2
|
-
|
|
3
|
-
import { parseArgs } from 'node:util';
|
|
4
|
-
import { loadRawConfig } from '../config.ts';
|
|
5
|
-
import type { Config } from '../core/types.ts';
|
|
6
|
-
import { c } from './style.ts';
|
|
7
|
-
|
|
8
|
-
export const CONFIG_HELP = `
|
|
9
|
-
${c('cyan', 'usage:')} browserbird config [options]
|
|
10
|
-
|
|
11
|
-
view full configuration (defaults merged with browserbird.json).
|
|
12
|
-
|
|
13
|
-
${c('dim', 'options:')}
|
|
14
|
-
|
|
15
|
-
${c('yellow', '--config')} <path> config file path
|
|
16
|
-
${c('yellow', '-h, --help')} show this help
|
|
17
|
-
`.trim();
|
|
18
|
-
|
|
19
|
-
export function handleConfig(argv: string[]): void {
|
|
20
|
-
const { values } = parseArgs({
|
|
21
|
-
args: argv,
|
|
22
|
-
options: { config: { type: 'string' } },
|
|
23
|
-
allowPositionals: false,
|
|
24
|
-
strict: false,
|
|
25
|
-
});
|
|
26
|
-
printConfig(values.config as string | undefined);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function printConfig(configPath?: string): void {
|
|
30
|
-
const config = loadRawConfig(configPath) as unknown as Config;
|
|
31
|
-
|
|
32
|
-
console.log(c('cyan', 'config'));
|
|
33
|
-
console.log(c('dim', '------'));
|
|
34
|
-
console.log(`\n${c('dim', 'timezone:')} ${config.timezone}`);
|
|
35
|
-
|
|
36
|
-
console.log(`\n${c('cyan', 'agents:')}`);
|
|
37
|
-
for (const a of config.agents) {
|
|
38
|
-
console.log(` ${c('cyan', a.id)} (${a.name})`);
|
|
39
|
-
console.log(` ${c('dim', 'provider:')} ${a.provider}`);
|
|
40
|
-
console.log(` ${c('dim', 'model:')} ${a.model}`);
|
|
41
|
-
console.log(` ${c('dim', 'max turns:')} ${a.maxTurns}`);
|
|
42
|
-
console.log(` ${c('dim', 'channels:')} ${a.channels.join(', ') || '*'}`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
console.log(`\n${c('cyan', 'sessions:')}`);
|
|
46
|
-
console.log(` ${c('dim', 'max concurrent:')} ${config.sessions.maxConcurrent}`);
|
|
47
|
-
console.log(` ${c('dim', 'ttl:')} ${config.sessions.ttlHours}h`);
|
|
48
|
-
console.log(` ${c('dim', 'timeout:')} ${config.sessions.processTimeoutMs / 1000}s`);
|
|
49
|
-
|
|
50
|
-
console.log(`\n${c('cyan', 'slack:')}`);
|
|
51
|
-
console.log(` ${c('dim', 'require mention:')} ${config.slack.requireMention ? 'yes' : 'no'}`);
|
|
52
|
-
console.log(` ${c('dim', 'debounce:')} ${config.slack.coalesce.debounceMs}ms`);
|
|
53
|
-
console.log(
|
|
54
|
-
` ${c('dim', 'bypass dms:')} ${config.slack.coalesce.bypassDms ? 'yes' : 'no'}`,
|
|
55
|
-
);
|
|
56
|
-
console.log(` ${c('dim', 'channels:')} ${config.slack.channels.join(', ') || '(all)'}`);
|
|
57
|
-
console.log(
|
|
58
|
-
` ${c('dim', 'quiet hours:')} ${config.slack.quietHours.enabled ? `${config.slack.quietHours.start}-${config.slack.quietHours.end} (${config.slack.quietHours.timezone})` : 'disabled'}`,
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
console.log(`\n${c('cyan', 'birds:')}`);
|
|
62
|
-
console.log(` ${c('dim', 'max attempts:')} ${config.birds.maxAttempts}`);
|
|
63
|
-
|
|
64
|
-
console.log(`\n${c('cyan', 'browser:')}`);
|
|
65
|
-
console.log(` ${c('dim', 'enabled:')} ${config.browser.enabled ? 'yes' : 'no'}`);
|
|
66
|
-
if (config.browser.enabled) {
|
|
67
|
-
console.log(` ${c('dim', 'mode:')} ${process.env['BROWSER_MODE'] ?? 'persistent'}`);
|
|
68
|
-
console.log(` ${c('dim', 'vnc port:')} ${config.browser.vncPort}`);
|
|
69
|
-
console.log(` ${c('dim', 'novnc port:')} ${config.browser.novncPort}`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
console.log(`\n${c('cyan', 'database:')}`);
|
|
73
|
-
console.log(` ${c('dim', 'retention:')} ${config.database.retentionDays}d`);
|
|
74
|
-
|
|
75
|
-
console.log(`\n${c('cyan', 'web:')}`);
|
|
76
|
-
console.log(` ${c('dim', 'port:')} ${config.web.port}`);
|
|
77
|
-
}
|
package/src/cli/doctor.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/** @fileoverview Doctor command: checks system dependencies. */
|
|
2
|
-
|
|
3
|
-
import { execFileSync } from 'node:child_process';
|
|
4
|
-
import { logger } from '../core/logger.ts';
|
|
5
|
-
import { c } from './style.ts';
|
|
6
|
-
|
|
7
|
-
export const DOCTOR_HELP = `
|
|
8
|
-
${c('cyan', 'usage:')} browserbird doctor
|
|
9
|
-
|
|
10
|
-
check system dependencies (agent clis, node.js).
|
|
11
|
-
`.trim();
|
|
12
|
-
|
|
13
|
-
interface CliStatus {
|
|
14
|
-
available: boolean;
|
|
15
|
-
version: string | null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface DoctorResult {
|
|
19
|
-
claude: CliStatus;
|
|
20
|
-
opencode: CliStatus;
|
|
21
|
-
node: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function checkCli(binary: string, versionArgs: string[]): CliStatus {
|
|
25
|
-
try {
|
|
26
|
-
const output = execFileSync(binary, versionArgs, {
|
|
27
|
-
timeout: 5000,
|
|
28
|
-
encoding: 'utf-8',
|
|
29
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
30
|
-
});
|
|
31
|
-
const version = (output.trim().split('\n')[0] ?? '').replace(/\s*\(.*\)$/, '') || null;
|
|
32
|
-
return { available: true, version };
|
|
33
|
-
} catch {
|
|
34
|
-
return { available: false, version: null };
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function checkDoctor(): DoctorResult {
|
|
39
|
-
return {
|
|
40
|
-
claude: checkCli('claude', ['--version']),
|
|
41
|
-
opencode: checkCli('opencode', ['--version']),
|
|
42
|
-
node: process.version,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function handleDoctor(): void {
|
|
47
|
-
const result = checkDoctor();
|
|
48
|
-
console.log(c('cyan', 'doctor'));
|
|
49
|
-
console.log(c('dim', '------'));
|
|
50
|
-
if (result.claude.available) {
|
|
51
|
-
logger.success(`claude cli: ${result.claude.version}`);
|
|
52
|
-
} else {
|
|
53
|
-
logger.error('claude cli: not found');
|
|
54
|
-
process.stderr.write(' install: npm install -g @anthropic-ai/claude-code\n');
|
|
55
|
-
}
|
|
56
|
-
if (result.opencode.available) {
|
|
57
|
-
logger.success(`opencode cli: ${result.opencode.version}`);
|
|
58
|
-
} else {
|
|
59
|
-
logger.warn('opencode cli: not found (optional)');
|
|
60
|
-
process.stderr.write(' install: npm install -g opencode\n');
|
|
61
|
-
}
|
|
62
|
-
logger.success(`node.js: ${result.node}`);
|
|
63
|
-
}
|