@dupecom/botcha-cli 0.1.0

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/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # @dupecom/botcha-cli
2
+
3
+ > CLI tool for testing and debugging BOTCHA-protected endpoints
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @dupecom/botcha-cli
9
+ # or use directly with npx
10
+ npx botcha <command>
11
+ ```
12
+
13
+ ## Commands
14
+
15
+ ### Test an Endpoint
16
+
17
+ Check if an endpoint is BOTCHA-protected and test verification:
18
+
19
+ ```bash
20
+ botcha test https://api.example.com/agent-only
21
+ ```
22
+
23
+ Options:
24
+ - `--json` - Output as JSON
25
+ - `-v, --verbose` - Show detailed output
26
+ - `-q, --quiet` - Minimal output
27
+
28
+ ### Solve Challenges
29
+
30
+ Solve a specific challenge type:
31
+
32
+ ```bash
33
+ # Get a token
34
+ botcha solve token --url https://botcha.ai/v1/token
35
+
36
+ # Solve a speed challenge
37
+ botcha solve speed --url https://botcha.ai/api/speed-challenge
38
+ ```
39
+
40
+ Options:
41
+ - `--url <url>` - URL to solve challenge from (required)
42
+ - `--json` - Output as JSON
43
+ - `-v, --verbose` - Show detailed output
44
+ - `-q, --quiet` - Minimal output
45
+
46
+ ### Benchmark
47
+
48
+ Test performance and reliability:
49
+
50
+ ```bash
51
+ botcha benchmark https://api.example.com/agent-only --iterations 100
52
+ ```
53
+
54
+ Options:
55
+ - `-n, --iterations <number>` - Number of iterations (default: 10)
56
+ - `--json` - Output as JSON
57
+ - `-v, --verbose` - Show each iteration result
58
+ - `-q, --quiet` - Minimal output
59
+
60
+ ### Check Headers
61
+
62
+ Inspect BOTCHA headers on any URL:
63
+
64
+ ```bash
65
+ botcha headers https://botcha.ai/api
66
+ ```
67
+
68
+ Options:
69
+ - `--json` - Output as JSON
70
+ - `-v, --verbose` - Show all headers (not just BOTCHA)
71
+ - `-q, --quiet` - Minimal output
72
+
73
+ ## Examples
74
+
75
+ ### CI/CD Integration
76
+
77
+ ```yaml
78
+ # GitHub Actions
79
+ - name: Test BOTCHA Protection
80
+ run: |
81
+ npx botcha test ${{ env.API_URL }}/protected
82
+ npx botcha benchmark ${{ env.API_URL }}/protected --iterations 10
83
+ ```
84
+
85
+ ### Debugging
86
+
87
+ ```bash
88
+ # See detailed information about challenge solving
89
+ botcha test https://api.example.com/endpoint --verbose
90
+ ```
91
+
92
+ ### Scripting
93
+
94
+ ```bash
95
+ # Get token and use in other commands
96
+ TOKEN=$(botcha solve token --url https://botcha.ai/v1/token --json | jq -r '.token')
97
+ curl -H "Authorization: Bearer $TOKEN" https://botcha.ai/agent-only
98
+ ```
99
+
100
+ ## License
101
+
102
+ MIT
package/bin/botcha.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/index.js';
@@ -0,0 +1,8 @@
1
+ export interface BenchmarkOptions {
2
+ iterations?: number;
3
+ json?: boolean;
4
+ verbose?: boolean;
5
+ quiet?: boolean;
6
+ }
7
+ export declare function benchmarkCommand(url: string, options: BenchmarkOptions): Promise<void>;
8
+ //# sourceMappingURL=benchmark.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"benchmark.d.ts","sourceRoot":"","sources":["../../src/commands/benchmark.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AASD,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6F5F"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Benchmark command - Test performance and reliability
3
+ */
4
+ import { BotchaClient } from '@dupecom/botcha/client';
5
+ import { Output } from '../lib/output.js';
6
+ export async function benchmarkCommand(url, options) {
7
+ const output = new Output(options);
8
+ const iterations = options.iterations || 10;
9
+ output.header(`\n🏃 Running ${iterations} iterations...\n`);
10
+ const client = new BotchaClient({ baseUrl: new URL(url).origin });
11
+ const results = [];
12
+ const startTime = Date.now();
13
+ for (let i = 0; i < iterations; i++) {
14
+ const spinner = output.spinner(`Iteration ${i + 1}/${iterations}...`);
15
+ const iterStart = Date.now();
16
+ try {
17
+ const response = await client.fetch(url);
18
+ const timeMs = Date.now() - iterStart;
19
+ results.push({
20
+ iteration: i + 1,
21
+ success: response.ok,
22
+ timeMs,
23
+ });
24
+ spinner.stop();
25
+ if (options.verbose) {
26
+ const status = response.ok ? '✅' : '❌';
27
+ output.info(`${status} Iteration ${i + 1}: ${timeMs}ms (${response.status})`);
28
+ }
29
+ }
30
+ catch (error) {
31
+ const timeMs = Date.now() - iterStart;
32
+ results.push({
33
+ iteration: i + 1,
34
+ success: false,
35
+ timeMs,
36
+ error: error instanceof Error ? error.message : String(error),
37
+ });
38
+ spinner.stop();
39
+ if (options.verbose) {
40
+ output.error(`❌ Iteration ${i + 1}: ${error instanceof Error ? error.message : String(error)}`);
41
+ }
42
+ }
43
+ }
44
+ const totalTime = Date.now() - startTime;
45
+ const successCount = results.filter(r => r.success).length;
46
+ const successRate = (successCount / iterations) * 100;
47
+ const times = results.filter(r => r.success).map(r => r.timeMs);
48
+ if (times.length === 0) {
49
+ output.error('All iterations failed!');
50
+ process.exit(1);
51
+ }
52
+ const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
53
+ const minTime = Math.min(...times);
54
+ const maxTime = Math.max(...times);
55
+ // Calculate P95
56
+ const sortedTimes = [...times].sort((a, b) => a - b);
57
+ const p95Index = Math.ceil(sortedTimes.length * 0.95) - 1;
58
+ const p95Time = sortedTimes[p95Index] || sortedTimes[sortedTimes.length - 1];
59
+ if (options.json) {
60
+ output.json({
61
+ url,
62
+ iterations,
63
+ successRate,
64
+ successCount,
65
+ failureCount: iterations - successCount,
66
+ avgTimeMs: Math.round(avgTime),
67
+ minTimeMs: minTime,
68
+ maxTimeMs: maxTime,
69
+ p95TimeMs: p95Time,
70
+ totalTimeMs: totalTime,
71
+ requestsPerSec: (iterations / (totalTime / 1000)).toFixed(2),
72
+ results,
73
+ });
74
+ }
75
+ else {
76
+ console.log('\n' + '─'.repeat(50));
77
+ output.header('Results:');
78
+ output.section('Success Rate', `${successRate.toFixed(1)}% (${successCount}/${iterations})`);
79
+ output.section('Avg Time', `${Math.round(avgTime)}ms`);
80
+ output.section('Min Time', `${minTime}ms`);
81
+ output.section('Max Time', `${maxTime}ms`);
82
+ output.section('P95 Time', `${p95Time}ms`);
83
+ console.log();
84
+ output.section('Total Time', `${(totalTime / 1000).toFixed(2)}s`);
85
+ output.section('Requests/sec', (iterations / (totalTime / 1000)).toFixed(2));
86
+ console.log();
87
+ }
88
+ }
89
+ //# sourceMappingURL=benchmark.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"benchmark.js","sourceRoot":"","sources":["../../src/commands/benchmark.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export interface HeadersOptions {
2
+ json?: boolean;
3
+ verbose?: boolean;
4
+ quiet?: boolean;
5
+ }
6
+ export declare function headersCommand(url: string, options: HeadersOptions): Promise<void>;
7
+ //# sourceMappingURL=headers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../../src/commands/headers.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmExF"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Headers command - Show BOTCHA headers from a URL
3
+ */
4
+ import { Output, formatUrl } from '../lib/output.js';
5
+ export async function headersCommand(url, options) {
6
+ const output = new Output(options);
7
+ output.header(`\n🔍 Fetching headers from ${formatUrl(url)}...\n`);
8
+ try {
9
+ const response = await fetch(url, {
10
+ method: 'HEAD',
11
+ });
12
+ const botchaHeaders = {};
13
+ const allHeaders = {};
14
+ response.headers.forEach((value, key) => {
15
+ allHeaders[key] = value;
16
+ if (key.toLowerCase().startsWith('x-botcha-')) {
17
+ botchaHeaders[key] = value;
18
+ }
19
+ });
20
+ if (options.json) {
21
+ output.json({
22
+ url,
23
+ statusCode: response.status,
24
+ botchaHeaders,
25
+ allHeaders: options.verbose ? allHeaders : undefined,
26
+ });
27
+ return;
28
+ }
29
+ if (Object.keys(botchaHeaders).length === 0) {
30
+ output.warn('No BOTCHA headers found');
31
+ if (options.verbose) {
32
+ console.log('\nAll headers:');
33
+ for (const [key, value] of Object.entries(allHeaders)) {
34
+ console.log(` ${key}: ${value}`);
35
+ }
36
+ }
37
+ }
38
+ else {
39
+ output.success('BOTCHA headers found:');
40
+ console.log();
41
+ for (const [key, value] of Object.entries(botchaHeaders)) {
42
+ console.log(` ${key}: ${value}`);
43
+ }
44
+ if (options.verbose) {
45
+ console.log('\nAll headers:');
46
+ for (const [key, value] of Object.entries(allHeaders)) {
47
+ if (!key.toLowerCase().startsWith('x-botcha-')) {
48
+ console.log(` ${key}: ${value}`);
49
+ }
50
+ }
51
+ }
52
+ }
53
+ }
54
+ catch (error) {
55
+ if (options.json) {
56
+ output.json({
57
+ url,
58
+ success: false,
59
+ error: error instanceof Error ? error.message : String(error),
60
+ });
61
+ }
62
+ else {
63
+ output.error(`Failed to fetch headers: ${error instanceof Error ? error.message : String(error)}`);
64
+ }
65
+ process.exit(1);
66
+ }
67
+ }
68
+ //# sourceMappingURL=headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.js","sourceRoot":"","sources":["../../src/commands/headers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface SolveOptions {
2
+ url: string;
3
+ json?: boolean;
4
+ verbose?: boolean;
5
+ quiet?: boolean;
6
+ }
7
+ export declare function solveCommand(type: string, options: SolveOptions): Promise<void>;
8
+ //# sourceMappingURL=solve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solve.d.ts","sourceRoot":"","sources":["../../src/commands/solve.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA2ErF"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Solve command - Solve BOTCHA challenges and output tokens
3
+ */
4
+ import { BotchaClient } from '@dupecom/botcha/client';
5
+ import { Output } from '../lib/output.js';
6
+ export async function solveCommand(type, options) {
7
+ const output = new Output(options);
8
+ if (!options.url) {
9
+ output.error('--url is required');
10
+ process.exit(1);
11
+ }
12
+ const client = new BotchaClient({ baseUrl: new URL(options.url).origin });
13
+ const startTime = Date.now();
14
+ try {
15
+ switch (type.toLowerCase()) {
16
+ case 'speed': {
17
+ output.debug('Solving speed challenge...');
18
+ const { id, answers } = await client.solveChallenge();
19
+ const solveTime = Date.now() - startTime;
20
+ if (options.json) {
21
+ output.json({
22
+ type: 'speed',
23
+ challengeId: id,
24
+ answers,
25
+ solveTimeMs: solveTime,
26
+ });
27
+ }
28
+ else {
29
+ output.success(`Solved in ${solveTime}ms!`);
30
+ output.section('Challenge ID', id);
31
+ output.section('Answers', answers.length);
32
+ if (options.verbose) {
33
+ console.log('\nAnswers:', answers);
34
+ }
35
+ }
36
+ break;
37
+ }
38
+ case 'token': {
39
+ output.debug('Getting token...');
40
+ const token = await client.getToken();
41
+ const solveTime = Date.now() - startTime;
42
+ if (options.json) {
43
+ output.json({
44
+ type: 'token',
45
+ token,
46
+ solveTimeMs: solveTime,
47
+ });
48
+ }
49
+ else {
50
+ output.success(`Token acquired in ${solveTime}ms!`);
51
+ console.log('\nToken:');
52
+ console.log(token);
53
+ console.log('\nUse with:');
54
+ console.log(` Authorization: Bearer ${token}`);
55
+ }
56
+ break;
57
+ }
58
+ default:
59
+ output.error(`Unknown challenge type: ${type}`);
60
+ output.info('Supported types: speed, token');
61
+ process.exit(1);
62
+ }
63
+ }
64
+ catch (error) {
65
+ if (options.json) {
66
+ output.json({
67
+ type,
68
+ success: false,
69
+ error: error instanceof Error ? error.message : String(error),
70
+ });
71
+ }
72
+ else {
73
+ output.error(`Solve failed: ${error instanceof Error ? error.message : String(error)}`);
74
+ }
75
+ process.exit(1);
76
+ }
77
+ }
78
+ //# sourceMappingURL=solve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solve.js","sourceRoot":"","sources":["../../src/commands/solve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export interface TestOptions {
2
+ json?: boolean;
3
+ verbose?: boolean;
4
+ quiet?: boolean;
5
+ }
6
+ export declare function testCommand(url: string, options: TestOptions): Promise<void>;
7
+ //# sourceMappingURL=test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/commands/test.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAiFlF"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Test command - Check if URL is BOTCHA-protected and test verification
3
+ */
4
+ import { BotchaClient } from '@dupecom/botcha/client';
5
+ import { Output, formatUrl } from '../lib/output.js';
6
+ export async function testCommand(url, options) {
7
+ const output = new Output(options);
8
+ output.header(`\n🔍 Testing ${formatUrl(url)}...\n`);
9
+ const client = new BotchaClient({ baseUrl: new URL(url).origin });
10
+ const startTime = Date.now();
11
+ try {
12
+ // First, make a request to see if BOTCHA is required
13
+ output.debug('Making initial request...');
14
+ const response = await client.fetch(url);
15
+ const totalTime = Date.now() - startTime;
16
+ // Check for BOTCHA headers
17
+ const botchaHeaders = {};
18
+ response.headers.forEach((value, key) => {
19
+ if (key.toLowerCase().startsWith('x-botcha-')) {
20
+ botchaHeaders[key] = value;
21
+ }
22
+ });
23
+ const hasBotcha = Object.keys(botchaHeaders).length > 0;
24
+ if (options.json) {
25
+ const result = {
26
+ url,
27
+ protected: hasBotcha,
28
+ statusCode: response.status,
29
+ success: response.ok,
30
+ headers: botchaHeaders,
31
+ totalTimeMs: totalTime,
32
+ };
33
+ output.json(result);
34
+ return;
35
+ }
36
+ if (hasBotcha) {
37
+ output.success('BOTCHA Detected!');
38
+ if (botchaHeaders['x-botcha-version']) {
39
+ output.section('Version', botchaHeaders['x-botcha-version']);
40
+ }
41
+ if (botchaHeaders['x-botcha-methods']) {
42
+ output.section('Methods', botchaHeaders['x-botcha-methods']);
43
+ }
44
+ }
45
+ else {
46
+ output.warn('No BOTCHA protection detected');
47
+ }
48
+ console.log();
49
+ if (response.ok) {
50
+ output.success(`Access Granted! (${response.status})`);
51
+ output.timing('Total Time', totalTime);
52
+ // Try to show response body
53
+ const contentType = response.headers.get('content-type') || '';
54
+ if (contentType.includes('application/json')) {
55
+ const data = await response.json();
56
+ console.log('\nResponse:');
57
+ console.log(JSON.stringify(data, null, 2));
58
+ }
59
+ }
60
+ else {
61
+ output.error(`Access Denied! (${response.status} ${response.statusText})`);
62
+ output.timing('Total Time', totalTime);
63
+ process.exit(1);
64
+ }
65
+ }
66
+ catch (error) {
67
+ if (options.json) {
68
+ output.json({
69
+ url,
70
+ success: false,
71
+ error: error instanceof Error ? error.message : String(error),
72
+ });
73
+ }
74
+ else {
75
+ output.error(`Test failed: ${error instanceof Error ? error.message : String(error)}`);
76
+ }
77
+ process.exit(1);
78
+ }
79
+ }
80
+ //# sourceMappingURL=test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../../src/commands/test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * BOTCHA CLI - Test, debug, and interact with BOTCHA from the command line
4
+ */
5
+ import { Command } from 'commander';
6
+ import { testCommand } from './commands/test.js';
7
+ import { solveCommand } from './commands/solve.js';
8
+ import { benchmarkCommand } from './commands/benchmark.js';
9
+ import { headersCommand } from './commands/headers.js';
10
+ const program = new Command();
11
+ program
12
+ .name('botcha')
13
+ .description('CLI tool for testing and debugging BOTCHA-protected endpoints')
14
+ .version('0.1.0');
15
+ // Test command
16
+ program
17
+ .command('test <url>')
18
+ .description('Check if URL is BOTCHA-protected and test verification')
19
+ .option('--json', 'Output as JSON')
20
+ .option('-v, --verbose', 'Show detailed output')
21
+ .option('-q, --quiet', 'Minimal output')
22
+ .action(testCommand);
23
+ // Solve command
24
+ program
25
+ .command('solve <type>')
26
+ .description('Solve a BOTCHA challenge (types: speed, token)')
27
+ .requiredOption('--url <url>', 'URL to solve challenge from')
28
+ .option('--json', 'Output as JSON')
29
+ .option('-v, --verbose', 'Show detailed output')
30
+ .option('-q, --quiet', 'Minimal output')
31
+ .action(solveCommand);
32
+ // Benchmark command
33
+ program
34
+ .command('benchmark <url>')
35
+ .description('Test performance and reliability')
36
+ .option('-n, --iterations <number>', 'Number of iterations to run', '10')
37
+ .option('--json', 'Output as JSON')
38
+ .option('-v, --verbose', 'Show detailed output')
39
+ .option('-q, --quiet', 'Minimal output')
40
+ .action((url, options) => {
41
+ benchmarkCommand(url, {
42
+ ...options,
43
+ iterations: parseInt(options.iterations, 10),
44
+ });
45
+ });
46
+ // Headers command
47
+ program
48
+ .command('headers <url>')
49
+ .description('Show BOTCHA headers from a URL')
50
+ .option('--json', 'Output as JSON')
51
+ .option('-v, --verbose', 'Show all headers')
52
+ .option('-q, --quiet', 'Minimal output')
53
+ .action(headersCommand);
54
+ // Parse and execute
55
+ program.parse();
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;GAEG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,+DAA+D,CAAC;KAC5E,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,eAAe;AACf,OAAO;KACJ,OAAO,CAAC,YAAY,CAAC;KACrB,WAAW,CAAC,wDAAwD,CAAC;KACrE,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;KAClC,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC;KAC/C,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC;KACvC,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,gBAAgB;AAChB,OAAO;KACJ,OAAO,CAAC,cAAc,CAAC;KACvB,WAAW,CAAC,gDAAgD,CAAC;KAC7D,cAAc,CAAC,aAAa,EAAE,6BAA6B,CAAC;KAC5D,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;KAClC,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC;KAC/C,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC;KACvC,MAAM,CAAC,YAAY,CAAC,CAAC;AAExB,oBAAoB;AACpB,OAAO;KACJ,OAAO,CAAC,iBAAiB,CAAC;KAC1B,WAAW,CAAC,kCAAkC,CAAC;KAC/C,MAAM,CAAC,2BAA2B,EAAE,6BAA6B,EAAE,IAAI,CAAC;KACxE,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;KAClC,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC;KAC/C,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC;KACvC,MAAM,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE;IACvB,gBAAgB,CAAC,GAAG,EAAE;QACpB,GAAG,OAAO;QACV,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;KAC7C,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL,kBAAkB;AAClB,OAAO;KACJ,OAAO,CAAC,eAAe,CAAC;KACxB,WAAW,CAAC,gCAAgC,CAAC;KAC7C,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;KAClC,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC;KAC3C,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC;KACvC,MAAM,CAAC,cAAc,CAAC,CAAC;AAE1B,oBAAoB;AACpB,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,26 @@
1
+ export interface OutputOptions {
2
+ json?: boolean;
3
+ verbose?: boolean;
4
+ quiet?: boolean;
5
+ }
6
+ export declare class Output {
7
+ private options;
8
+ constructor(options?: OutputOptions);
9
+ private suppressNonJson;
10
+ success(message: string): void;
11
+ error(message: string): void;
12
+ info(message: string): void;
13
+ warn(message: string): void;
14
+ debug(message: string): void;
15
+ header(message: string): void;
16
+ section(label: string, value: string | number): void;
17
+ json(data: unknown): void;
18
+ table(headers: string[], rows: string[][]): void;
19
+ spinner(message: string): {
20
+ stop: (finalMessage?: string) => void;
21
+ };
22
+ timing(label: string, ms: number): void;
23
+ }
24
+ export declare function formatUrl(url: string): string;
25
+ export declare function formatHeaders(headers: Record<string, string>): void;
26
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,MAAM;IACL,OAAO,CAAC,OAAO;gBAAP,OAAO,GAAE,aAAkB;IAE/C,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK9B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK5B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK3B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK5B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK7B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKpD,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAIzB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI;IAiBhD,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE;IA4BnE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;CAMxC;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAOnE"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Terminal output utilities with colored and formatted output
3
+ */
4
+ import chalk from 'chalk';
5
+ export class Output {
6
+ options;
7
+ constructor(options = {}) {
8
+ this.options = options;
9
+ }
10
+ suppressNonJson() {
11
+ return Boolean(this.options.quiet || this.options.json);
12
+ }
13
+ success(message) {
14
+ if (this.suppressNonJson())
15
+ return;
16
+ console.log(chalk.green('✅'), message);
17
+ }
18
+ error(message) {
19
+ if (this.suppressNonJson())
20
+ return;
21
+ console.error(chalk.red('❌'), message);
22
+ }
23
+ info(message) {
24
+ if (this.suppressNonJson())
25
+ return;
26
+ console.log(chalk.blue('ℹ️ '), message);
27
+ }
28
+ warn(message) {
29
+ if (this.suppressNonJson())
30
+ return;
31
+ console.warn(chalk.yellow('⚠️ '), message);
32
+ }
33
+ debug(message) {
34
+ if (this.suppressNonJson() || !this.options.verbose)
35
+ return;
36
+ console.log(chalk.gray('DEBUG:'), message);
37
+ }
38
+ header(message) {
39
+ if (this.suppressNonJson())
40
+ return;
41
+ console.log(chalk.bold.cyan(message));
42
+ }
43
+ section(label, value) {
44
+ if (this.suppressNonJson())
45
+ return;
46
+ console.log(` ${chalk.dim(label)}: ${value}`);
47
+ }
48
+ json(data) {
49
+ console.log(JSON.stringify(data, null, 2));
50
+ }
51
+ table(headers, rows) {
52
+ if (this.suppressNonJson())
53
+ return;
54
+ const colWidths = headers.map((h, i) => {
55
+ const values = [h, ...rows.map(r => r[i] || '')];
56
+ return Math.max(...values.map(v => v.length));
57
+ });
58
+ const formatRow = (row) => {
59
+ return row.map((cell, i) => cell.padEnd(colWidths[i])).join(' ');
60
+ };
61
+ console.log(chalk.bold(formatRow(headers)));
62
+ console.log(chalk.dim('─'.repeat(colWidths.reduce((a, b) => a + b + 2, 0))));
63
+ rows.forEach(row => console.log(formatRow(row)));
64
+ }
65
+ spinner(message) {
66
+ if (this.suppressNonJson()) {
67
+ return { stop: () => { } };
68
+ }
69
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
70
+ let i = 0;
71
+ let stopped = false;
72
+ const interval = setInterval(() => {
73
+ if (!stopped) {
74
+ process.stdout.write(`\r${chalk.cyan(frames[i])} ${message}`);
75
+ i = (i + 1) % frames.length;
76
+ }
77
+ }, 80);
78
+ return {
79
+ stop: (finalMessage) => {
80
+ stopped = true;
81
+ clearInterval(interval);
82
+ process.stdout.write('\r' + ' '.repeat(message.length + 2) + '\r');
83
+ if (finalMessage) {
84
+ console.log(finalMessage);
85
+ }
86
+ },
87
+ };
88
+ }
89
+ timing(label, ms) {
90
+ if (this.suppressNonJson())
91
+ return;
92
+ const color = ms < 100 ? chalk.green : ms < 500 ? chalk.yellow : chalk.red;
93
+ console.log(` ${chalk.dim(label)}: ${color(`${ms}ms`)}`);
94
+ }
95
+ }
96
+ export function formatUrl(url) {
97
+ return chalk.underline.blue(url);
98
+ }
99
+ export function formatHeaders(headers) {
100
+ console.log(chalk.bold('\nHeaders:'));
101
+ for (const [key, value] of Object.entries(headers)) {
102
+ if (key.toLowerCase().startsWith('x-botcha-')) {
103
+ console.log(` ${chalk.cyan(key)}: ${value}`);
104
+ }
105
+ }
106
+ }
107
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@dupecom/botcha-cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI tool for testing and debugging BOTCHA-protected endpoints",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "botcha": "./bin/botcha.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "bin",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "dev": "tsx src/index.ts",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "keywords": [
22
+ "botcha",
23
+ "cli",
24
+ "bot",
25
+ "ai",
26
+ "agent",
27
+ "testing",
28
+ "debug"
29
+ ],
30
+ "author": "Ramin <ramin@dupe.com>",
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/i8ramin/botcha"
35
+ },
36
+ "homepage": "https://botcha.ai",
37
+ "dependencies": {
38
+ "@dupecom/botcha": "^0.4.1",
39
+ "chalk": "^5.3.0",
40
+ "commander": "^12.1.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^20.19.0",
44
+ "tsx": "^4.7.0",
45
+ "typescript": "^5.3.0"
46
+ },
47
+ "engines": {
48
+ "node": ">=18"
49
+ }
50
+ }