@owloops/browserbird 1.0.1 → 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/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
|
-
}
|
package/src/cli/sessions.ts
DELETED
|
@@ -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
|
-
}
|