@opentabs-dev/cli 0.0.1

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 (57) hide show
  1. package/dist/cli.d.ts +3 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +26 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/build.d.ts +10 -0
  6. package/dist/commands/build.d.ts.map +1 -0
  7. package/dist/commands/build.js +285 -0
  8. package/dist/commands/build.js.map +1 -0
  9. package/dist/commands/config.d.ts +7 -0
  10. package/dist/commands/config.d.ts.map +1 -0
  11. package/dist/commands/config.js +157 -0
  12. package/dist/commands/config.js.map +1 -0
  13. package/dist/commands/dev.d.ts +10 -0
  14. package/dist/commands/dev.d.ts.map +1 -0
  15. package/dist/commands/dev.js +128 -0
  16. package/dist/commands/dev.js.map +1 -0
  17. package/dist/commands/doctor.d.ts +7 -0
  18. package/dist/commands/doctor.d.ts.map +1 -0
  19. package/dist/commands/doctor.js +212 -0
  20. package/dist/commands/doctor.js.map +1 -0
  21. package/dist/commands/index.d.ts +10 -0
  22. package/dist/commands/index.d.ts.map +1 -0
  23. package/dist/commands/index.js +10 -0
  24. package/dist/commands/index.js.map +1 -0
  25. package/dist/commands/logs.d.ts +10 -0
  26. package/dist/commands/logs.d.ts.map +1 -0
  27. package/dist/commands/logs.js +103 -0
  28. package/dist/commands/logs.js.map +1 -0
  29. package/dist/commands/plugin.d.ts +6 -0
  30. package/dist/commands/plugin.d.ts.map +1 -0
  31. package/dist/commands/plugin.js +199 -0
  32. package/dist/commands/plugin.js.map +1 -0
  33. package/dist/commands/reload.d.ts +7 -0
  34. package/dist/commands/reload.d.ts.map +1 -0
  35. package/dist/commands/reload.js +88 -0
  36. package/dist/commands/reload.js.map +1 -0
  37. package/dist/commands/setup.d.ts +6 -0
  38. package/dist/commands/setup.d.ts.map +1 -0
  39. package/dist/commands/setup.js +70 -0
  40. package/dist/commands/setup.js.map +1 -0
  41. package/dist/commands/status.d.ts +7 -0
  42. package/dist/commands/status.d.ts.map +1 -0
  43. package/dist/commands/status.js +108 -0
  44. package/dist/commands/status.js.map +1 -0
  45. package/dist/config.d.ts +11 -0
  46. package/dist/config.d.ts.map +1 -0
  47. package/dist/config.js +37 -0
  48. package/dist/config.js.map +1 -0
  49. package/dist/parse-port.d.ts +15 -0
  50. package/dist/parse-port.d.ts.map +1 -0
  51. package/dist/parse-port.js +32 -0
  52. package/dist/parse-port.js.map +1 -0
  53. package/dist/scaffold.d.ts +21 -0
  54. package/dist/scaffold.d.ts.map +1 -0
  55. package/dist/scaffold.js +273 -0
  56. package/dist/scaffold.js.map +1 -0
  57. package/package.json +33 -0
@@ -0,0 +1,128 @@
1
+ /**
2
+ * `opentabs dev` command — starts the MCP server with hot reload.
3
+ *
4
+ * Server output is written to both the terminal and a log file at
5
+ * ~/.opentabs/server.log (or $OPENTABS_CONFIG_DIR/server.log).
6
+ * The `opentabs logs` command tails this file.
7
+ */
8
+ import { getConfigDir, getLogFilePath } from '../config.js';
9
+ import { parsePort, resolvePort } from '../parse-port.js';
10
+ import { InvalidArgumentError } from 'commander';
11
+ import pc from 'picocolors';
12
+ import { mkdirSync, createWriteStream } from 'node:fs';
13
+ import { resolve, dirname } from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+ const LOG_LEVELS = ['debug', 'info', 'warn', 'error', 'silent'];
16
+ const resolveServerEntry = () => {
17
+ try {
18
+ return fileURLToPath(import.meta.resolve('@opentabs-dev/mcp-server'));
19
+ }
20
+ catch {
21
+ const cliDir = dirname(fileURLToPath(import.meta.url));
22
+ return resolve(cliDir, '..', '..', '..', 'mcp-server', 'dist', 'index.js');
23
+ }
24
+ };
25
+ const isPortInUse = async (port) => {
26
+ try {
27
+ await fetch(`http://localhost:${port}/health`, {
28
+ signal: AbortSignal.timeout(1_000),
29
+ });
30
+ return true;
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ };
36
+ /**
37
+ * Pipe a readable stream to both a terminal writable and a log file stream.
38
+ * Returns when the readable stream ends.
39
+ */
40
+ const teeStream = async (readable, terminal, logFile) => {
41
+ const reader = readable.getReader();
42
+ for (;;) {
43
+ const { done, value } = await reader.read();
44
+ if (done)
45
+ break;
46
+ terminal.write(value);
47
+ logFile.write(value);
48
+ }
49
+ };
50
+ const handleDev = async (options) => {
51
+ const serverEntry = resolveServerEntry();
52
+ if (!(await Bun.file(serverEntry).exists())) {
53
+ console.error(pc.red(`Error: MCP server entry not found at ${serverEntry}`));
54
+ console.error('Run bun run build from the project root first.');
55
+ process.exit(1);
56
+ }
57
+ const port = resolvePort(options);
58
+ if (await isPortInUse(port)) {
59
+ console.error(pc.red(`Error: Port ${port} is already in use.`));
60
+ console.error(port === 9515
61
+ ? 'Another OpenTabs server may already be running. Use --port to specify a different port.'
62
+ : `Use a different port with: opentabs dev --port <number>`);
63
+ process.exit(1);
64
+ }
65
+ const env = { ...process.env };
66
+ env.PORT = String(port);
67
+ if (options.verbose)
68
+ env.OPENTABS_LOG_LEVEL = 'debug';
69
+ else if (options.logLevel)
70
+ env.OPENTABS_LOG_LEVEL = options.logLevel;
71
+ const logFilePath = getLogFilePath();
72
+ mkdirSync(getConfigDir(), { recursive: true });
73
+ const logStream = createWriteStream(logFilePath, { flags: 'a' });
74
+ console.log(`Starting OpenTabs MCP server on port ${pc.bold(String(port))}...`);
75
+ console.log('');
76
+ console.log(` ${pc.cyan('MCP endpoint:')} http://localhost:${port}/mcp`);
77
+ console.log(` ${pc.cyan('Health check:')} http://localhost:${port}/health`);
78
+ console.log(` ${pc.cyan('Log file:')} ${logFilePath}`);
79
+ console.log('');
80
+ console.log(pc.dim(' MCP client config (~/.claude/settings/mcp.json):'));
81
+ console.log(pc.dim(` { "mcpServers": { "opentabs": { "type": "streamable-http", "url": "http://127.0.0.1:${port}/mcp" } } }`));
82
+ console.log('');
83
+ if (options.verbose)
84
+ console.log(pc.yellow(' Debug logging enabled'));
85
+ console.log(pc.dim(' Press Ctrl+C to stop'));
86
+ console.log('');
87
+ const proc = Bun.spawn(['bun', '--hot', serverEntry], {
88
+ env: env,
89
+ stdio: ['inherit', 'pipe', 'pipe'],
90
+ });
91
+ const stdoutPipe = teeStream(proc.stdout, process.stdout, logStream);
92
+ const stderrPipe = teeStream(proc.stderr, process.stderr, logStream);
93
+ process.on('SIGINT', () => proc.kill('SIGINT'));
94
+ process.on('SIGTERM', () => proc.kill('SIGTERM'));
95
+ await Promise.all([stdoutPipe, stderrPipe]);
96
+ logStream.end();
97
+ const exitCode = await proc.exited;
98
+ process.exit(exitCode);
99
+ };
100
+ export const registerDevCommand = (program) => {
101
+ program
102
+ .command('dev')
103
+ .description('Start the MCP server with hot reload')
104
+ .option('--port <number>', 'Server port (default: 9515)', parsePort)
105
+ .option('--verbose', 'Enable debug logging (shorthand for --log-level debug)')
106
+ .option('--log-level <level>', 'Set log level', (value) => {
107
+ if (!LOG_LEVELS.includes(value)) {
108
+ throw new InvalidArgumentError(`Must be one of: ${LOG_LEVELS.join(', ')}`);
109
+ }
110
+ return value;
111
+ })
112
+ .addHelpText('after', `
113
+ Examples:
114
+ $ opentabs dev
115
+ $ opentabs dev --port 3000
116
+ $ opentabs dev --verbose
117
+ $ opentabs dev --log-level warn`)
118
+ .action((_options, command) => {
119
+ const opts = command.optsWithGlobals();
120
+ if (opts.verbose && opts.logLevel) {
121
+ console.error(pc.red('Error: --verbose and --log-level cannot be used together.'));
122
+ console.error('Use --verbose for debug logging, or --log-level to set a specific level.');
123
+ process.exit(1);
124
+ }
125
+ return handleDev(opts);
126
+ });
127
+ };
128
+ //# sourceMappingURL=dev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `opentabs doctor` command — diagnoses the entire OpenTabs setup.
3
+ */
4
+ import type { Command } from 'commander';
5
+ declare const registerDoctorCommand: (program: Command) => void;
6
+ export { registerDoctorCommand };
7
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6PzC,QAAA,MAAM,qBAAqB,GAAI,SAAS,OAAO,KAAG,IAajD,CAAC;AAEF,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
@@ -0,0 +1,212 @@
1
+ /**
2
+ * `opentabs doctor` command — diagnoses the entire OpenTabs setup.
3
+ */
4
+ import { getConfigPath, getPluginsFromConfig, readConfig, resolvePluginPath } from '../config.js';
5
+ import { parsePort, resolvePort } from '../parse-port.js';
6
+ import pc from 'picocolors';
7
+ import { existsSync } from 'node:fs';
8
+ import { homedir } from 'node:os';
9
+ import { join, dirname } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ const pass = (label, detail) => ({ label, ok: true, detail, fatal: false });
12
+ const warn = (label, detail, hint) => ({
13
+ label,
14
+ ok: false,
15
+ detail,
16
+ hint,
17
+ fatal: false,
18
+ });
19
+ const fail = (label, detail, hint) => ({
20
+ label,
21
+ ok: false,
22
+ detail,
23
+ hint,
24
+ fatal: true,
25
+ });
26
+ const checkBunVersion = () => {
27
+ const bunVersion = typeof Bun !== 'undefined' ? Bun.version : undefined;
28
+ if (bunVersion) {
29
+ return pass('Bun runtime', `v${bunVersion}`);
30
+ }
31
+ return fail('Bun runtime', 'not detected', 'Install Bun: https://bun.sh');
32
+ };
33
+ const checkConfigFile = async () => {
34
+ const configPath = getConfigPath();
35
+ const config = await readConfig(configPath);
36
+ if (config) {
37
+ return { result: pass('Config file', configPath), config };
38
+ }
39
+ return {
40
+ result: warn('Config file', `not found at ${configPath}`, 'Run opentabs dev to create one automatically'),
41
+ config: null,
42
+ };
43
+ };
44
+ const checkServerHealth = async (port) => {
45
+ const url = `http://localhost:${port}/health`;
46
+ try {
47
+ const res = await fetch(url, { signal: AbortSignal.timeout(3_000) });
48
+ if (!res.ok) {
49
+ return {
50
+ result: fail('MCP server', `HTTP ${res.status}`, 'Check server logs for errors'),
51
+ data: null,
52
+ };
53
+ }
54
+ const data = (await res.json());
55
+ const version = typeof data.version === 'string' ? data.version : 'unknown';
56
+ return { result: pass('MCP server', `running (v${version}) on port ${port}`), data };
57
+ }
58
+ catch {
59
+ const portSuffix = port !== 9515 ? ` --port ${port}` : '';
60
+ const hint = `Start it with: opentabs dev${portSuffix}`;
61
+ return {
62
+ result: fail('MCP server', 'not reachable', hint),
63
+ data: null,
64
+ };
65
+ }
66
+ };
67
+ const checkExtensionConnected = (data) => {
68
+ if (!data) {
69
+ return warn('Extension connection', 'unknown (server not reachable)', 'Start the MCP server first');
70
+ }
71
+ if (data.extensionConnected === true) {
72
+ return pass('Extension connection', 'connected');
73
+ }
74
+ return warn('Extension connection', 'not connected', 'Open Chrome and ensure the OpenTabs extension is loaded and enabled');
75
+ };
76
+ const checkExtensionInstalled = async () => {
77
+ const extensionDir = join(homedir(), '.opentabs', 'extension');
78
+ const manifestPath = join(extensionDir, 'manifest.json');
79
+ if (!existsSync(manifestPath)) {
80
+ return {
81
+ result: warn('Extension installed', `not found at ${extensionDir}`, 'Run opentabs setup to install the extension'),
82
+ versionFile: null,
83
+ };
84
+ }
85
+ const versionPath = join(extensionDir, '.opentabs-version');
86
+ const versionFile = (await Bun.file(versionPath).exists()) ? await Bun.file(versionPath).text() : null;
87
+ return {
88
+ result: pass('Extension installed', extensionDir),
89
+ versionFile: versionFile?.trim() ?? null,
90
+ };
91
+ };
92
+ const checkExtensionVersion = async (installedVersion) => {
93
+ const cliDir = dirname(fileURLToPath(import.meta.url));
94
+ let cliVersion = 'unknown';
95
+ try {
96
+ const pkgJson = JSON.parse(await Bun.file(join(cliDir, '..', '..', 'package.json')).text());
97
+ cliVersion = pkgJson.version;
98
+ }
99
+ catch {
100
+ return warn('Extension version', 'could not read CLI version', 'Ensure @opentabs-dev/cli package.json is accessible');
101
+ }
102
+ if (!installedVersion) {
103
+ return warn('Extension version', 'no version marker found', 'Run opentabs setup to install a versioned extension');
104
+ }
105
+ if (installedVersion === cliVersion) {
106
+ return pass('Extension version', `v${cliVersion} (matches CLI)`);
107
+ }
108
+ return warn('Extension version', `v${installedVersion} (CLI is v${cliVersion})`, 'Run opentabs setup to update the extension');
109
+ };
110
+ const checkPlugins = async (config) => {
111
+ if (!config) {
112
+ return [warn('Plugins', 'no config to check', 'Create a config file first')];
113
+ }
114
+ const configPath = getConfigPath();
115
+ const pluginPaths = getPluginsFromConfig(config);
116
+ if (pluginPaths.length === 0) {
117
+ return [warn('Plugins', 'none configured', 'Add a plugin with: opentabs plugin add <path>')];
118
+ }
119
+ const results = [];
120
+ for (const pluginPath of pluginPaths) {
121
+ const resolvedPath = resolvePluginPath(pluginPath, configPath);
122
+ if (!existsSync(resolvedPath)) {
123
+ results.push(fail(`Plugin ${pluginPath}`, 'directory not found', `Check path: ${resolvedPath}`));
124
+ continue;
125
+ }
126
+ const manifestPath = join(resolvedPath, 'opentabs-plugin.json');
127
+ if (!existsSync(manifestPath)) {
128
+ results.push(warn(`Plugin ${pluginPath}`, 'manifest not found', 'Run opentabs build in the plugin directory'));
129
+ continue;
130
+ }
131
+ const iifePath = join(resolvedPath, 'dist', 'adapter.iife.js');
132
+ if (!existsSync(iifePath)) {
133
+ results.push(warn(`Plugin ${pluginPath}`, 'adapter IIFE not found', 'Run opentabs build in the plugin directory'));
134
+ continue;
135
+ }
136
+ let manifestName = pluginPath;
137
+ try {
138
+ const data = await Bun.file(manifestPath).json();
139
+ if (data !== null && typeof data === 'object' && 'name' in data) {
140
+ const d = data;
141
+ manifestName = d.name;
142
+ }
143
+ }
144
+ catch {
145
+ // Manifest unreadable — fall back to path
146
+ }
147
+ results.push(pass(`Plugin ${manifestName}`, 'manifest + IIFE present'));
148
+ }
149
+ return results;
150
+ };
151
+ const handleDoctor = async (options) => {
152
+ const port = resolvePort(options);
153
+ const results = [];
154
+ // 1. Bun version
155
+ results.push(checkBunVersion());
156
+ // 2. Config file
157
+ const { result: configResult, config } = await checkConfigFile();
158
+ results.push(configResult);
159
+ // 3. MCP server health
160
+ const { result: serverResult, data: healthData } = await checkServerHealth(port);
161
+ results.push(serverResult);
162
+ // 4. Extension connected
163
+ results.push(checkExtensionConnected(healthData));
164
+ // 5. Extension installed
165
+ const { result: installedResult, versionFile } = await checkExtensionInstalled();
166
+ results.push(installedResult);
167
+ // 6. Extension version matches CLI
168
+ results.push(await checkExtensionVersion(versionFile));
169
+ // 7. Plugin checks
170
+ const pluginResults = await checkPlugins(config);
171
+ results.push(...pluginResults);
172
+ // Print results
173
+ console.log(pc.bold('OpenTabs Doctor'));
174
+ console.log('');
175
+ let hasFatal = false;
176
+ for (const r of results) {
177
+ const icon = r.ok ? pc.green('\u2713') : r.fatal ? pc.red('\u2717') : pc.yellow('!');
178
+ const label = r.ok ? r.label : r.fatal ? pc.red(r.label) : pc.yellow(r.label);
179
+ console.log(` ${icon} ${label}: ${r.detail}`);
180
+ if (!r.ok && r.hint) {
181
+ console.log(` ${pc.dim(r.hint)}`);
182
+ }
183
+ if (r.fatal)
184
+ hasFatal = true;
185
+ }
186
+ console.log('');
187
+ const passed = results.filter(r => r.ok).length;
188
+ const total = results.length;
189
+ if (hasFatal) {
190
+ console.log(pc.red(`${passed}/${total} checks passed. Fix the errors above to proceed.`));
191
+ process.exit(1);
192
+ }
193
+ else if (passed < total) {
194
+ console.log(pc.yellow(`${passed}/${total} checks passed. Some warnings above may need attention.`));
195
+ }
196
+ else {
197
+ console.log(pc.green(`All ${total} checks passed. Your OpenTabs setup looks good!`));
198
+ }
199
+ };
200
+ const registerDoctorCommand = (program) => {
201
+ program
202
+ .command('doctor')
203
+ .description('Diagnose your OpenTabs setup')
204
+ .option('--port <number>', 'MCP server port to check (default: 9515)', parsePort)
205
+ .addHelpText('after', `
206
+ Examples:
207
+ $ opentabs doctor
208
+ $ opentabs doctor --port 3000`)
209
+ .action((_options, command) => handleDoctor(command.optsWithGlobals()));
210
+ };
211
+ export { registerDoctorCommand };
212
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export { registerBuildCommand } from './build.js';
2
+ export { registerConfigCommand } from './config.js';
3
+ export { registerDevCommand } from './dev.js';
4
+ export { registerDoctorCommand } from './doctor.js';
5
+ export { registerLogsCommand } from './logs.js';
6
+ export { registerReloadCommand } from './reload.js';
7
+ export { registerStatusCommand } from './status.js';
8
+ export { registerSetupCommand } from './setup.js';
9
+ export { registerPluginCommand } from './plugin.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,10 @@
1
+ export { registerBuildCommand } from './build.js';
2
+ export { registerConfigCommand } from './config.js';
3
+ export { registerDevCommand } from './dev.js';
4
+ export { registerDoctorCommand } from './doctor.js';
5
+ export { registerLogsCommand } from './logs.js';
6
+ export { registerReloadCommand } from './reload.js';
7
+ export { registerStatusCommand } from './status.js';
8
+ export { registerSetupCommand } from './setup.js';
9
+ export { registerPluginCommand } from './plugin.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `opentabs logs` command — tails the MCP server log output.
3
+ *
4
+ * The log file is written by `opentabs dev` at ~/.opentabs/server.log
5
+ * (or $OPENTABS_CONFIG_DIR/server.log).
6
+ */
7
+ import type { Command } from 'commander';
8
+ declare const registerLogsCommand: (program: Command) => void;
9
+ export { registerLogsCommand };
10
+ //# sourceMappingURL=logs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8FzC,QAAA,MAAM,mBAAmB,GAAI,SAAS,OAAO,KAAG,IAe/C,CAAC;AAEF,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * `opentabs logs` command — tails the MCP server log output.
3
+ *
4
+ * The log file is written by `opentabs dev` at ~/.opentabs/server.log
5
+ * (or $OPENTABS_CONFIG_DIR/server.log).
6
+ */
7
+ import { getLogFilePath } from '../config.js';
8
+ import { InvalidArgumentError } from 'commander';
9
+ import pc from 'picocolors';
10
+ import { existsSync, statSync, createReadStream, watch } from 'node:fs';
11
+ const DEFAULT_LINES = 50;
12
+ /** Read buffer size for reverse-seek tail (64KB bounds memory usage) */
13
+ const TAIL_BUFFER_SIZE = 64 * 1024;
14
+ /**
15
+ * Read the last N lines from a file using a reverse-seek approach.
16
+ * Reads at most TAIL_BUFFER_SIZE bytes from the end instead of the entire file.
17
+ * Returns the tail content as a string and the file size (for follow offset).
18
+ */
19
+ const tailFile = async (filePath, lineCount) => {
20
+ const file = Bun.file(filePath);
21
+ const fileSize = file.size;
22
+ if (lineCount <= 0 || fileSize === 0)
23
+ return { content: '', fileSize };
24
+ const readStart = Math.max(0, fileSize - TAIL_BUFFER_SIZE);
25
+ const chunk = await file.slice(readStart, fileSize).text();
26
+ const lines = chunk.split('\n');
27
+ // If we didn't read from the start, the first line may be partial — skip it
28
+ if (readStart > 0)
29
+ lines.shift();
30
+ const tail = lines.slice(-lineCount);
31
+ return { content: tail.join('\n'), fileSize };
32
+ };
33
+ /**
34
+ * Follow (tail -f) a log file. Watches for changes and streams new content.
35
+ * Returns a promise that never resolves (runs until the process exits).
36
+ */
37
+ const followFile = async (filePath, initialOffset) => {
38
+ let offset = initialOffset;
39
+ const readNewContent = () => {
40
+ const currentSize = statSync(filePath).size;
41
+ if (currentSize < offset) {
42
+ // File was truncated (e.g., new server start) — read from beginning
43
+ offset = 0;
44
+ }
45
+ if (currentSize <= offset)
46
+ return;
47
+ const stream = createReadStream(filePath, { start: offset, encoding: 'utf-8' });
48
+ stream.on('data', (chunk) => {
49
+ process.stdout.write(chunk);
50
+ });
51
+ offset = currentSize;
52
+ };
53
+ const watcher = watch(filePath, () => readNewContent());
54
+ process.on('SIGINT', () => {
55
+ watcher.close();
56
+ process.exit(0);
57
+ });
58
+ process.on('SIGTERM', () => {
59
+ watcher.close();
60
+ process.exit(0);
61
+ });
62
+ return new Promise(() => {
63
+ // Runs forever until interrupted
64
+ });
65
+ };
66
+ const parseLines = (value) => {
67
+ const n = Number(value);
68
+ if (!Number.isInteger(n) || n < 0) {
69
+ throw new InvalidArgumentError('Must be a non-negative integer.');
70
+ }
71
+ return n;
72
+ };
73
+ const handleLogs = async (options) => {
74
+ const logFilePath = getLogFilePath();
75
+ if (!existsSync(logFilePath)) {
76
+ console.error(pc.red('No log file found.'));
77
+ console.error(pc.dim(`Expected at: ${logFilePath}`));
78
+ console.error(pc.dim('Start the server with: opentabs dev'));
79
+ process.exit(1);
80
+ }
81
+ const lineCount = options.lines ?? DEFAULT_LINES;
82
+ const { content, fileSize } = await tailFile(logFilePath, lineCount);
83
+ if (content)
84
+ process.stdout.write(content);
85
+ if (options.follow !== false) {
86
+ await followFile(logFilePath, fileSize);
87
+ }
88
+ };
89
+ const registerLogsCommand = (program) => {
90
+ program
91
+ .command('logs')
92
+ .description('Tail the MCP server log output')
93
+ .option('--lines <n>', `Number of lines to show (default: ${DEFAULT_LINES})`, parseLines)
94
+ .option('--no-follow', 'Print recent lines and exit (do not follow)')
95
+ .addHelpText('after', `
96
+ Examples:
97
+ $ opentabs logs # Tail logs (follows new output)
98
+ $ opentabs logs --lines 100 # Show last 100 lines then follow
99
+ $ opentabs logs --no-follow # Show last 50 lines and exit`)
100
+ .action((options) => handleLogs(options));
101
+ };
102
+ export { registerLogsCommand };
103
+ //# sourceMappingURL=logs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * `opentabs plugin` command — manage plugins (list, add, remove, create).
3
+ */
4
+ import type { Command } from 'commander';
5
+ export declare const registerPluginCommand: (program: Command) => void;
6
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/commands/plugin.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+JzC,eAAO,MAAM,qBAAqB,GAAI,SAAS,OAAO,KAAG,IA6ExD,CAAC"}