@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.
Files changed (56) hide show
  1. package/bin/browserbird +7 -1
  2. package/dist/db-BsYEYsul.mjs +1011 -0
  3. package/dist/index.mjs +4748 -0
  4. package/package.json +6 -3
  5. package/src/channel/blocks.ts +0 -485
  6. package/src/channel/coalesce.ts +0 -79
  7. package/src/channel/commands.ts +0 -216
  8. package/src/channel/handler.ts +0 -272
  9. package/src/channel/slack.ts +0 -573
  10. package/src/channel/types.ts +0 -59
  11. package/src/cli/banner.ts +0 -10
  12. package/src/cli/birds.ts +0 -396
  13. package/src/cli/config.ts +0 -77
  14. package/src/cli/doctor.ts +0 -63
  15. package/src/cli/index.ts +0 -5
  16. package/src/cli/jobs.ts +0 -166
  17. package/src/cli/logs.ts +0 -67
  18. package/src/cli/run.ts +0 -148
  19. package/src/cli/sessions.ts +0 -158
  20. package/src/cli/style.ts +0 -19
  21. package/src/config.ts +0 -291
  22. package/src/core/logger.ts +0 -78
  23. package/src/core/redact.ts +0 -75
  24. package/src/core/types.ts +0 -83
  25. package/src/core/uid.ts +0 -26
  26. package/src/core/utils.ts +0 -137
  27. package/src/cron/parse.ts +0 -146
  28. package/src/cron/scheduler.ts +0 -242
  29. package/src/daemon.ts +0 -169
  30. package/src/db/auth.ts +0 -49
  31. package/src/db/birds.ts +0 -357
  32. package/src/db/core.ts +0 -377
  33. package/src/db/index.ts +0 -10
  34. package/src/db/jobs.ts +0 -289
  35. package/src/db/logs.ts +0 -64
  36. package/src/db/messages.ts +0 -79
  37. package/src/db/path.ts +0 -30
  38. package/src/db/sessions.ts +0 -165
  39. package/src/jobs.ts +0 -140
  40. package/src/provider/claude.test.ts +0 -95
  41. package/src/provider/claude.ts +0 -196
  42. package/src/provider/opencode.test.ts +0 -169
  43. package/src/provider/opencode.ts +0 -248
  44. package/src/provider/session.ts +0 -65
  45. package/src/provider/spawn.ts +0 -173
  46. package/src/provider/stream.ts +0 -67
  47. package/src/provider/types.ts +0 -24
  48. package/src/server/auth.ts +0 -135
  49. package/src/server/health.ts +0 -87
  50. package/src/server/http.ts +0 -132
  51. package/src/server/index.ts +0 -6
  52. package/src/server/lifecycle.ts +0 -135
  53. package/src/server/routes.ts +0 -1199
  54. package/src/server/sse.ts +0 -54
  55. package/src/server/static.ts +0 -45
  56. package/src/server/vnc-proxy.ts +0 -75
package/src/cli/jobs.ts DELETED
@@ -1,166 +0,0 @@
1
- /** @fileoverview Jobs command: inspect and manage the job queue. */
2
-
3
- import { parseArgs } from 'node:util';
4
- import { logger } from '../core/logger.ts';
5
- import { printTable, unknownSubcommand } from '../core/utils.ts';
6
- import { c } from './style.ts';
7
- import {
8
- openDatabase,
9
- closeDatabase,
10
- listJobs,
11
- getJobStats,
12
- retryJob,
13
- retryAllFailedJobs,
14
- deleteJob,
15
- clearJobs,
16
- resolveDbPathFromArgv,
17
- } from '../db/index.ts';
18
-
19
- export const JOBS_HELP = `
20
- ${c('cyan', 'usage:')} browserbird jobs [subcommand] [options]
21
-
22
- inspect and manage the job queue.
23
-
24
- ${c('dim', 'subcommands:')}
25
-
26
- ${c('cyan', 'list')} list job queue entries (default)
27
- ${c('cyan', 'stats')} show job queue statistics
28
- ${c('cyan', 'retry')} <id> retry a failed job (or --all-failed)
29
- ${c('cyan', 'delete')} <id> delete a job
30
- ${c('cyan', 'clear')} clear completed or failed jobs
31
-
32
- ${c('dim', 'options:')}
33
-
34
- ${c('yellow', '--status')} <s> filter by status: pending, running, completed, failed (with list)
35
- ${c('yellow', '--name')} <s> filter by job name (with list)
36
- ${c('yellow', '--all-failed')} retry all failed jobs (with retry)
37
- ${c('yellow', '--completed')} clear completed jobs (with clear)
38
- ${c('yellow', '--failed')} clear failed jobs (with clear)
39
- ${c('yellow', '--json')} output as JSON (with list, stats)
40
- ${c('yellow', '--db')} <path> database file path (env: BROWSERBIRD_DB)
41
- ${c('yellow', '--config')} <path> config file path
42
- ${c('yellow', '-h, --help')} show this help
43
- `.trim();
44
-
45
- export function handleJobs(argv: string[]): void {
46
- const { values, positionals } = parseArgs({
47
- args: argv,
48
- options: {
49
- status: { type: 'string' },
50
- name: { type: 'string' },
51
- 'all-failed': { type: 'boolean', default: false },
52
- completed: { type: 'boolean', default: false },
53
- failed: { type: 'boolean', default: false },
54
- json: { type: 'boolean', default: false },
55
- },
56
- allowPositionals: true,
57
- strict: false,
58
- });
59
-
60
- const subcommand = positionals[0] ?? 'list';
61
- openDatabase(resolveDbPathFromArgv(argv));
62
- try {
63
- switch (subcommand) {
64
- case 'list': {
65
- const result = listJobs(1, 100, {
66
- status: values.status as string | undefined,
67
- name: values.name as string | undefined,
68
- });
69
- if (values.json) {
70
- console.log(JSON.stringify(result.items, null, 2));
71
- break;
72
- }
73
- console.log(`jobs (${result.totalItems} total):`);
74
- if (result.items.length === 0) {
75
- console.log('\n no jobs found');
76
- return;
77
- }
78
- console.log('');
79
- const rows = result.items.map((job) => [
80
- String(job.id),
81
- job.status,
82
- job.name,
83
- `${job.attempts}/${job.max_attempts}`,
84
- job.created_at.slice(0, 19),
85
- job.error ?? '',
86
- ]);
87
- printTable(['id', 'status', 'name', 'attempts', 'created', 'error'], rows, [
88
- undefined,
89
- undefined,
90
- 30,
91
- undefined,
92
- undefined,
93
- 40,
94
- ]);
95
- break;
96
- }
97
- case 'stats': {
98
- const stats = getJobStats();
99
- if (values.json) {
100
- console.log(JSON.stringify(stats, null, 2));
101
- break;
102
- }
103
- console.log('job queue statistics');
104
- console.log('');
105
- console.log(` pending: ${stats.pending}`);
106
- console.log(` running: ${stats.running}`);
107
- console.log(` completed: ${stats.completed}`);
108
- console.log(` failed: ${stats.failed}`);
109
- console.log(' --');
110
- console.log(` total: ${stats.total}`);
111
- break;
112
- }
113
- case 'retry': {
114
- if (values['all-failed']) {
115
- const count = retryAllFailedJobs();
116
- logger.success(`reset ${count} failed job(s) to pending`);
117
- return;
118
- }
119
- const id = Number(positionals[1]);
120
- if (!Number.isFinite(id)) {
121
- logger.error('usage: browserbird jobs retry <id> | --all-failed');
122
- process.exitCode = 1;
123
- return;
124
- }
125
- if (retryJob(id)) {
126
- logger.success(`job #${id} reset to pending`);
127
- } else {
128
- logger.error(`job #${id} not found or not in failed state`);
129
- process.exitCode = 1;
130
- }
131
- break;
132
- }
133
- case 'delete': {
134
- const id = Number(positionals[1]);
135
- if (!Number.isFinite(id)) {
136
- logger.error('usage: browserbird jobs delete <id>');
137
- process.exitCode = 1;
138
- return;
139
- }
140
- if (deleteJob(id)) {
141
- logger.success(`job #${id} deleted`);
142
- } else {
143
- logger.error(`job #${id} not found`);
144
- process.exitCode = 1;
145
- }
146
- break;
147
- }
148
- case 'clear': {
149
- if (!values.completed && !values.failed) {
150
- logger.error('usage: browserbird jobs clear --completed | --failed');
151
- process.exitCode = 1;
152
- return;
153
- }
154
- let total = 0;
155
- if (values.completed) total += clearJobs('completed');
156
- if (values.failed) total += clearJobs('failed');
157
- logger.success(`cleared ${total} job(s)`);
158
- break;
159
- }
160
- default:
161
- unknownSubcommand(subcommand, 'jobs', ['stats', 'retry', 'delete', 'clear']);
162
- }
163
- } finally {
164
- closeDatabase();
165
- }
166
- }
package/src/cli/logs.ts DELETED
@@ -1,67 +0,0 @@
1
- /** @fileoverview Logs command: show recent log entries from the database. */
2
-
3
- import { parseArgs } from 'node:util';
4
- import { logger } from '../core/logger.ts';
5
- import { printTable } from '../core/utils.ts';
6
- import { c } from './style.ts';
7
- import { openDatabase, closeDatabase, getRecentLogs, resolveDbPathFromArgv } from '../db/index.ts';
8
-
9
- export const LOGS_HELP = `
10
- ${c('cyan', 'usage:')} browserbird logs [options]
11
-
12
- show recent log entries (default: error level).
13
-
14
- ${c('dim', 'options:')}
15
-
16
- ${c('yellow', '--level')} <lvl> filter by level: error, warn, info (default: error)
17
- ${c('yellow', '--limit')} <n> number of entries to show (default: 20)
18
- ${c('yellow', '--json')} output as JSON
19
- ${c('yellow', '--db')} <path> database file path (env: BROWSERBIRD_DB)
20
- ${c('yellow', '--config')} <path> config file path
21
- ${c('yellow', '-h, --help')} show this help
22
- `.trim();
23
-
24
- export function handleLogs(argv: string[]): void {
25
- const { values } = parseArgs({
26
- args: argv,
27
- options: {
28
- level: { type: 'string' },
29
- limit: { type: 'string' },
30
- json: { type: 'boolean', default: false },
31
- },
32
- allowPositionals: true,
33
- strict: false,
34
- });
35
-
36
- const level = values.level as string | undefined;
37
- const perPage = values.limit != null ? Number(values.limit) : 20;
38
- if (!Number.isFinite(perPage) || perPage < 1) {
39
- logger.error('--limit must be a positive number');
40
- process.exitCode = 1;
41
- return;
42
- }
43
-
44
- openDatabase(resolveDbPathFromArgv(argv));
45
- try {
46
- const result = getRecentLogs(1, perPage, level ?? 'error');
47
- if (values.json) {
48
- console.log(JSON.stringify(result.items, null, 2));
49
- return;
50
- }
51
- console.log(`logs (${result.totalItems} total, showing ${result.items.length}):`);
52
- if (result.items.length === 0) {
53
- console.log('\n no logs recorded');
54
- return;
55
- }
56
- console.log('');
57
- const rows = result.items.map((entry) => [
58
- entry.created_at.slice(11, 19),
59
- entry.level,
60
- entry.source,
61
- entry.message,
62
- ]);
63
- printTable(['time', 'level', 'source', 'message'], rows, [undefined, undefined, undefined, 80]);
64
- } finally {
65
- closeDatabase();
66
- }
67
- }
package/src/cli/run.ts DELETED
@@ -1,148 +0,0 @@
1
- /** @fileoverview CLI entry point: argument parsing and command routing. */
2
-
3
- import { parseArgs } from 'node:util';
4
- import type { Command } from '../core/types.ts';
5
- import { COMMANDS } from '../core/types.ts';
6
- import { logger } from '../core/logger.ts';
7
- import { unknownSubcommand } from '../core/utils.ts';
8
- import { startDaemon } from '../daemon.ts';
9
- import { BANNER, VERSION } from './banner.ts';
10
- import { c } from './style.ts';
11
- import { handleSessions } from './sessions.ts';
12
- import { SESSIONS_HELP } from './sessions.ts';
13
- import { handleBirds } from './birds.ts';
14
- import { BIRDS_HELP } from './birds.ts';
15
- import { handleLogs } from './logs.ts';
16
- import { LOGS_HELP } from './logs.ts';
17
- import { handleJobs } from './jobs.ts';
18
- import { JOBS_HELP } from './jobs.ts';
19
- import { handleConfig } from './config.ts';
20
- import { CONFIG_HELP } from './config.ts';
21
- import { handleDoctor } from './doctor.ts';
22
- import { DOCTOR_HELP } from './doctor.ts';
23
-
24
- const MAIN_HELP =
25
- BANNER +
26
- `
27
- ${c('cyan', 'usage:')} browserbird [command] [options]
28
-
29
- ${c('dim', 'commands:')}
30
-
31
- ${c('cyan', 'sessions')} manage sessions
32
- ${c('cyan', 'birds')} manage scheduled birds
33
- ${c('cyan', 'config')} view configuration
34
- ${c('cyan', 'logs')} show recent log entries
35
- ${c('cyan', 'jobs')} inspect and manage the job queue
36
- ${c('cyan', 'doctor')} check system dependencies
37
-
38
- ${c('dim', 'options:')}
39
-
40
- ${c('yellow', '-h, --help')} show this help
41
- ${c('yellow', '-v, --version')} show version
42
- ${c('yellow', '--verbose')} enable debug logging
43
- ${c('yellow', '--config')} config file path (env: BROWSERBIRD_CONFIG)
44
- ${c('yellow', '--db')} database file path (env: BROWSERBIRD_DB)
45
-
46
- run 'browserbird <command> --help' for command-specific options.`.trimEnd();
47
-
48
- const COMMAND_HELP: Record<string, string> = {
49
- sessions: SESSIONS_HELP,
50
- birds: BIRDS_HELP,
51
- config: CONFIG_HELP,
52
- logs: LOGS_HELP,
53
- jobs: JOBS_HELP,
54
- doctor: DOCTOR_HELP,
55
- };
56
-
57
- export async function run(argv: string[]): Promise<void> {
58
- const command = argv[0];
59
-
60
- if (command === '--help' || command === '-h') {
61
- console.log(MAIN_HELP);
62
- return;
63
- }
64
-
65
- if (!command) {
66
- await startDaemon(parseGlobalFlags(argv));
67
- return;
68
- }
69
-
70
- if (command === '--version' || command === '-v') {
71
- console.log(VERSION);
72
- return;
73
- }
74
-
75
- const rest = argv.slice(1);
76
- const isHelp = rest.includes('--help') || rest.includes('-h');
77
-
78
- if (command === '--verbose' || command === '--config' || command === '--no-color') {
79
- await startDaemon(parseGlobalFlags(argv));
80
- return;
81
- }
82
-
83
- switch (command as Command) {
84
- case COMMANDS.SESSIONS:
85
- if (isHelp) {
86
- console.log(COMMAND_HELP.sessions);
87
- return;
88
- }
89
- handleSessions(rest);
90
- break;
91
- case COMMANDS.BIRDS:
92
- if (isHelp) {
93
- console.log(COMMAND_HELP.birds);
94
- return;
95
- }
96
- handleBirds(rest);
97
- break;
98
- case COMMANDS.CONFIG:
99
- if (isHelp) {
100
- console.log(COMMAND_HELP.config);
101
- return;
102
- }
103
- handleConfig(rest);
104
- break;
105
- case COMMANDS.LOGS:
106
- if (isHelp) {
107
- console.log(COMMAND_HELP.logs);
108
- return;
109
- }
110
- handleLogs(rest);
111
- break;
112
- case COMMANDS.JOBS:
113
- if (isHelp) {
114
- console.log(COMMAND_HELP.jobs);
115
- return;
116
- }
117
- handleJobs(rest);
118
- break;
119
- case COMMANDS.DOCTOR:
120
- handleDoctor();
121
- break;
122
- default:
123
- unknownSubcommand(command, '', ['sessions', 'birds', 'config', 'logs', 'jobs', 'doctor']);
124
- }
125
- }
126
-
127
- function parseGlobalFlags(argv: string[]): {
128
- flags: { verbose: boolean; config?: string; db?: string };
129
- } {
130
- const { values } = parseArgs({
131
- args: argv,
132
- options: {
133
- verbose: { type: 'boolean', default: false },
134
- config: { type: 'string' },
135
- db: { type: 'string' },
136
- },
137
- allowPositionals: true,
138
- strict: false,
139
- });
140
- if (values.verbose) logger.setLevel('debug');
141
- return {
142
- flags: {
143
- verbose: values.verbose as boolean,
144
- config: values.config as string | undefined,
145
- db: values.db as string | undefined,
146
- },
147
- };
148
- }
@@ -1,158 +0,0 @@
1
- /** @fileoverview Sessions command: list and inspect sessions. */
2
-
3
- import { parseArgs } from 'node:util';
4
- import { logger } from '../core/logger.ts';
5
- import { shortUid } from '../core/uid.ts';
6
- import { printTable, unknownSubcommand } from '../core/utils.ts';
7
- import { c } from './style.ts';
8
- import {
9
- openDatabase,
10
- closeDatabase,
11
- resolveByUid,
12
- listSessions,
13
- getSessionMessages,
14
- getSessionTokenStats,
15
- resolveDbPathFromArgv,
16
- } from '../db/index.ts';
17
- import type { SessionRow } from '../db/index.ts';
18
-
19
- export const SESSIONS_HELP = `
20
- ${c('cyan', 'usage:')} browserbird sessions <subcommand> [options]
21
-
22
- manage sessions.
23
-
24
- ${c('dim', 'subcommands:')}
25
-
26
- ${c('cyan', 'list')} list recent sessions
27
- ${c('cyan', 'logs')} <uid> show session detail and message history
28
-
29
- ${c('dim', 'options:')}
30
-
31
- ${c('yellow', '--json')} output as JSON (with list, logs)
32
- ${c('yellow', '--db')} <path> database file path (env: BROWSERBIRD_DB)
33
- ${c('yellow', '-h, --help')} show this help
34
- `.trim();
35
-
36
- export function handleSessions(argv: string[]): void {
37
- const subcommand = argv[0] ?? 'list';
38
- const args = argv.slice(1);
39
-
40
- const dbPath = resolveDbPathFromArgv(argv);
41
-
42
- const { values, positionals } = parseArgs({
43
- args,
44
- options: {
45
- json: { type: 'boolean', default: false },
46
- },
47
- allowPositionals: true,
48
- strict: false,
49
- });
50
-
51
- if (subcommand === 'list') {
52
- openDatabase(dbPath);
53
- try {
54
- const { items, totalItems } = listSessions(1, 20);
55
- if (values.json) {
56
- console.log(JSON.stringify(items, null, 2));
57
- return;
58
- }
59
- console.log(`sessions (${totalItems} total):`);
60
- if (items.length === 0) {
61
- console.log('\n no sessions found');
62
- return;
63
- }
64
- console.log('');
65
- const rows = items.map((s) => [
66
- c('dim', shortUid(s.uid)),
67
- s.channel_id,
68
- s.agent_id,
69
- String(s.message_count),
70
- s.last_active.slice(0, 19),
71
- ]);
72
- printTable(['uid', 'channel', 'agent', 'messages', 'last active'], rows);
73
- } finally {
74
- closeDatabase();
75
- }
76
- return;
77
- }
78
-
79
- if (subcommand !== 'logs') {
80
- unknownSubcommand(subcommand, 'sessions', ['list', 'logs']);
81
- return;
82
- }
83
-
84
- const uidPrefix = positionals[0];
85
- if (!uidPrefix) {
86
- logger.error('usage: browserbird sessions logs <uid>');
87
- process.exitCode = 1;
88
- return;
89
- }
90
-
91
- openDatabase(dbPath);
92
-
93
- try {
94
- const result = resolveByUid<SessionRow>('sessions', uidPrefix);
95
- if (!result) {
96
- logger.error(`session ${uidPrefix} not found`);
97
- process.exitCode = 1;
98
- return;
99
- }
100
- if ('ambiguous' in result) {
101
- logger.error(
102
- `ambiguous session ID "${uidPrefix}" matches ${result.count} sessions, use a longer prefix`,
103
- );
104
- process.exitCode = 1;
105
- return;
106
- }
107
- const session = result.row;
108
-
109
- const tokenStats = getSessionTokenStats(session.channel_id, session.thread_id);
110
-
111
- if (values.json) {
112
- const msgResult = getSessionMessages(session.channel_id, session.thread_id, 1, 50);
113
- console.log(JSON.stringify({ session, messages: msgResult.items }, null, 2));
114
- return;
115
- }
116
-
117
- console.log(`session ${c('cyan', shortUid(session.uid))}`);
118
- console.log(c('dim', '------------------'));
119
- console.log(`${c('dim', 'uid:')} ${session.uid}`);
120
- console.log(`${c('dim', 'channel:')} ${session.channel_id}`);
121
- console.log(`${c('dim', 'thread:')} ${session.thread_id ?? '(none)'}`);
122
- console.log(`${c('dim', 'agent:')} ${session.agent_id}`);
123
- console.log(`${c('dim', 'provider id:')} ${session.provider_session_id}`);
124
- console.log(`${c('dim', 'created:')} ${session.created_at}`);
125
- console.log(`${c('dim', 'last active:')} ${session.last_active}`);
126
- console.log(`${c('dim', 'messages:')} ${session.message_count}`);
127
- console.log(
128
- `${c('dim', 'tokens:')} ${tokenStats.totalTokensIn} in / ${tokenStats.totalTokensOut} out`,
129
- );
130
- console.log('');
131
-
132
- const msgResult = getSessionMessages(session.channel_id, session.thread_id, 1, 50);
133
-
134
- if (msgResult.items.length === 0) {
135
- console.log('no messages recorded.');
136
- return;
137
- }
138
-
139
- console.log(
140
- `messages (${msgResult.totalItems} total, showing page 1 of ${msgResult.totalPages}):`,
141
- );
142
- console.log('------------------');
143
-
144
- for (const msg of msgResult.items) {
145
- const dir = msg.direction === 'in' ? c('green', '->') : c('cyan', '<-');
146
- const tokens =
147
- msg.tokens_in != null || msg.tokens_out != null
148
- ? ` [in:${msg.tokens_in ?? 0} out:${msg.tokens_out ?? 0}]`
149
- : '';
150
- const preview = (msg.content ?? '').slice(0, 120);
151
- const truncated = (msg.content?.length ?? 0) > 120 ? '...' : '';
152
- console.log(`${dir} ${msg.user_id} ${msg.created_at}${tokens}`);
153
- if (preview) console.log(` ${preview}${truncated}`);
154
- }
155
- } finally {
156
- closeDatabase();
157
- }
158
- }
package/src/cli/style.ts DELETED
@@ -1,19 +0,0 @@
1
- /** @fileoverview CLI color utilities. Safe palette per terminal theme compatibility. */
2
-
3
- import { styleText } from 'node:util';
4
-
5
- function shouldUseColor(): boolean {
6
- if (process.env['NO_COLOR'] !== undefined) return false;
7
- if (process.env['TERM'] === 'dumb') return false;
8
- if (process.argv.includes('--no-color')) return false;
9
- return process.stdout.isTTY === true;
10
- }
11
-
12
- const useColor = shouldUseColor();
13
-
14
- type Format = Parameters<typeof styleText>[0];
15
-
16
- export function c(format: Format, text: string): string {
17
- if (!useColor) return text;
18
- return styleText(format, text);
19
- }