yiyan-browser-agent 1.4.5 → 1.4.7

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/src/tools.js ADDED
@@ -0,0 +1,547 @@
1
+ // src/tools.js — All tools available to the AI agent
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { execSync } = require('child_process');
7
+ const http = require('http');
8
+ const https = require('https');
9
+ const config = require('./config');
10
+
11
+ // ─────────────────────────────────────────────
12
+ // Helpers
13
+ // ─────────────────────────────────────────────
14
+
15
+ /** Truncate long strings so they don't blow up the context window */
16
+ function truncate(str, max = config.MAX_OUTPUT_LENGTH) {
17
+ if (!str) return '';
18
+ const s = String(str);
19
+ if (s.length <= max) return s;
20
+ const half = Math.floor(max / 2);
21
+ return (
22
+ s.slice(0, half) +
23
+ `\n\n⚠ [OUTPUT TRUNCATED — ${s.length.toLocaleString()} chars total, showing first & last ${half} chars]\n\n` +
24
+ s.slice(-half)
25
+ );
26
+ }
27
+
28
+ /** Resolve a path relative to the working directory */
29
+ function resolve(filePath) {
30
+ if (path.isAbsolute(filePath)) return filePath;
31
+ return path.resolve(config.WORKING_DIR, filePath);
32
+ }
33
+
34
+ function formatBytes(bytes) {
35
+ if (bytes === 0) return '0 B';
36
+ const k = 1024;
37
+ const sizes = ['B', 'KB', 'MB', 'GB'];
38
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
39
+ return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
40
+ }
41
+
42
+ // ─────────────────────────────────────────────
43
+ // Tool definitions
44
+ // ─────────────────────────────────────────────
45
+
46
+ const TOOLS = {
47
+
48
+ // ── File Reading ────────────────────────────────────────────────────────────
49
+ read_file: {
50
+ description: 'Read the full contents of a file. Optionally read specific line ranges.',
51
+ parameters: {
52
+ path : { type: 'string', required: true, description: 'Path to the file' },
53
+ start_line : { type: 'number', required: false, description: 'First line to read (1-indexed)' },
54
+ end_line : { type: 'number', required: false, description: 'Last line to read (inclusive)' },
55
+ },
56
+ async execute({ path: filePath, start_line, end_line }) {
57
+ const abs = resolve(filePath);
58
+ if (!fs.existsSync(abs)) throw new Error(`File not found: ${filePath}`);
59
+ if (fs.statSync(abs).isDirectory()) throw new Error(`${filePath} is a directory`);
60
+
61
+ let content = fs.readFileSync(abs, 'utf8');
62
+
63
+ if (start_line != null || end_line != null) {
64
+ const lines = content.split('\n');
65
+ const s = Math.max(0, (start_line || 1) - 1);
66
+ const e = end_line != null ? end_line : lines.length;
67
+ content = lines.slice(s, e).map((l, i) => `${s + i + 1}: ${l}`).join('\n');
68
+ return `[${filePath} | lines ${s + 1}–${e}]\n${truncate(content)}`;
69
+ }
70
+
71
+ // Add line numbers for large files to help the AI reference lines
72
+ const lineCount = content.split('\n').length;
73
+ if (lineCount <= 300) {
74
+ const numbered = content.split('\n').map((l, i) => `${i + 1}: ${l}`).join('\n');
75
+ return `[${filePath} | ${lineCount} lines]\n${numbered}`;
76
+ }
77
+ return `[${filePath} | ${lineCount} lines — use start_line/end_line to read sections]\n${truncate(content)}`;
78
+ },
79
+ },
80
+
81
+ // ── File Writing ────────────────────────────────────────────────────────────
82
+ write_file: {
83
+ description: 'Write (create or overwrite) a file with given content. Creates parent directories automatically.',
84
+ parameters: {
85
+ path : { type: 'string', required: true, description: 'Destination file path' },
86
+ content : { type: 'string', required: true, description: 'Full file content to write' },
87
+ },
88
+ async execute({ path: filePath, content }) {
89
+ const abs = resolve(filePath);
90
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
91
+ fs.writeFileSync(abs, content, 'utf8');
92
+ const lineCount = content.split('\n').length;
93
+ return `✓ Wrote ${formatBytes(Buffer.byteLength(content, 'utf8'))} (${lineCount} lines) → ${filePath}`;
94
+ },
95
+ },
96
+
97
+ // ── Append to File ──────────────────────────────────────────────────────────
98
+ append_to_file: {
99
+ description: 'Append text to the end of an existing file (or create it if missing).',
100
+ parameters: {
101
+ path : { type: 'string', required: true, description: 'File path' },
102
+ content : { type: 'string', required: true, description: 'Text to append' },
103
+ },
104
+ async execute({ path: filePath, content }) {
105
+ const abs = resolve(filePath);
106
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
107
+ fs.appendFileSync(abs, content, 'utf8');
108
+ return `✓ Appended ${formatBytes(Buffer.byteLength(content, 'utf8'))} to ${filePath}`;
109
+ },
110
+ },
111
+
112
+ // ── Find & Replace in File ──────────────────────────────────────────────────
113
+ replace_in_file: {
114
+ description: 'Find and replace text in a file. Supports regex patterns.',
115
+ parameters: {
116
+ path : { type: 'string', required: true, description: 'File path' },
117
+ find : { type: 'string', required: true, description: 'Text to find' },
118
+ replace : { type: 'string', required: true, description: 'Replacement text' },
119
+ use_regex : { type: 'boolean', required: false, description: 'Treat "find" as a regex pattern (default: false)' },
120
+ all_occurrences: { type: 'boolean', required: false, description: 'Replace all occurrences (default: true)' },
121
+ },
122
+ async execute({ path: filePath, find, replace, use_regex = false, all_occurrences = true }) {
123
+ const abs = resolve(filePath);
124
+ let content = fs.readFileSync(abs, 'utf8');
125
+ const original = content;
126
+
127
+ if (use_regex) {
128
+ const re = new RegExp(find, all_occurrences ? 'g' : '');
129
+ content = content.replace(re, replace);
130
+ } else if (all_occurrences) {
131
+ content = content.split(find).join(replace);
132
+ } else {
133
+ content = content.replace(find, replace);
134
+ }
135
+
136
+ if (content === original) {
137
+ return `⚠ No matches found for "${find}" in ${filePath}`;
138
+ }
139
+
140
+ const count = (original.match(
141
+ new RegExp(use_regex ? find : find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
142
+ ) || []).length;
143
+
144
+ fs.writeFileSync(abs, content, 'utf8');
145
+ return `✓ Replaced ${count} occurrence(s) of "${find}" in ${filePath}`;
146
+ },
147
+ },
148
+
149
+ // ── Delete File ─────────────────────────────────────────────────────────────
150
+ delete_file: {
151
+ description: 'Permanently delete a file.',
152
+ parameters: {
153
+ path: { type: 'string', required: true, description: 'File to delete' },
154
+ },
155
+ async execute({ path: filePath }) {
156
+ const abs = resolve(filePath);
157
+ if (!fs.existsSync(abs)) throw new Error(`File not found: ${filePath}`);
158
+ fs.unlinkSync(abs);
159
+ return `✓ Deleted ${filePath}`;
160
+ },
161
+ },
162
+
163
+ // ── List Directory ──────────────────────────────────────────────────────────
164
+ list_directory: {
165
+ description: 'List files and folders in a directory, optionally recursive.',
166
+ parameters: {
167
+ path : { type: 'string', required: false, description: 'Directory to list (default: working dir)' },
168
+ recursive : { type: 'boolean', required: false, description: 'Recurse into sub-directories (default: false)' },
169
+ show_hidden : { type: 'boolean', required: false, description: 'Include hidden files starting with . (default: false)' },
170
+ },
171
+ async execute({ path: dirPath = '.', recursive = false, show_hidden = false }) {
172
+ const abs = resolve(dirPath);
173
+ if (!fs.existsSync(abs)) throw new Error(`Directory not found: ${dirPath}`);
174
+ if (!fs.statSync(abs).isDirectory()) throw new Error(`${dirPath} is not a directory`);
175
+
176
+ if (recursive) {
177
+ // Use Node.js for cross-platform compatibility
178
+ const excludeDirs = ['node_modules', '.git', 'dist', '.next', '__pycache__'];
179
+ let results = [];
180
+
181
+ function walk(dir, depth) {
182
+ if (depth > 10) return;
183
+ try {
184
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
185
+ for (const entry of entries) {
186
+ if (!show_hidden && entry.name.startsWith('.')) continue;
187
+ const fullPath = path.join(dir, entry.name);
188
+ if (entry.isDirectory()) {
189
+ if (excludeDirs.includes(entry.name)) continue;
190
+ results.push(fullPath);
191
+ walk(fullPath, depth + 1);
192
+ } else {
193
+ results.push(fullPath);
194
+ }
195
+ }
196
+ } catch {}
197
+ }
198
+
199
+ walk(abs, 1);
200
+ return results.slice(0, 300).join('\n') || '(empty)';
201
+ }
202
+
203
+ const entries = fs.readdirSync(abs, { withFileTypes: true });
204
+ const visible = show_hidden ? entries : entries.filter(e => !e.name.startsWith('.'));
205
+ visible.sort((a, b) => {
206
+ // Directories first, then alphabetical
207
+ if (a.isDirectory() && !b.isDirectory()) return -1;
208
+ if (!a.isDirectory() && b.isDirectory()) return 1;
209
+ return a.name.localeCompare(b.name);
210
+ });
211
+
212
+ if (visible.length === 0) return `(empty directory: ${dirPath})`;
213
+
214
+ const lines = visible.map(e => {
215
+ if (e.isDirectory()) {
216
+ return `📁 ${e.name}/`;
217
+ }
218
+ try {
219
+ const size = fs.statSync(path.join(abs, e.name)).size;
220
+ return `📄 ${e.name} ${formatBytes(size)}`;
221
+ } catch {
222
+ return `📄 ${e.name}`;
223
+ }
224
+ });
225
+
226
+ return `[${dirPath}] — ${visible.length} items\n${lines.join('\n')}`;
227
+ },
228
+ },
229
+
230
+ // ── Create Directory ────────────────────────────────────────────────────────
231
+ create_directory: {
232
+ description: 'Create a directory (and all necessary parent directories).',
233
+ parameters: {
234
+ path: { type: 'string', required: true, description: 'Directory path to create' },
235
+ },
236
+ async execute({ path: dirPath }) {
237
+ const abs = resolve(dirPath);
238
+ fs.mkdirSync(abs, { recursive: true });
239
+ return `✓ Created directory: ${dirPath}`;
240
+ },
241
+ },
242
+
243
+ // ── Move / Rename ───────────────────────────────────────────────────────────
244
+ move_file: {
245
+ description: 'Move or rename a file or directory.',
246
+ parameters: {
247
+ source : { type: 'string', required: true, description: 'Source path' },
248
+ destination : { type: 'string', required: true, description: 'Destination path' },
249
+ },
250
+ async execute({ source, destination }) {
251
+ const src = resolve(source);
252
+ const dest = resolve(destination);
253
+ if (!fs.existsSync(src)) throw new Error(`Source not found: ${source}`);
254
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
255
+ fs.renameSync(src, dest);
256
+ return `✓ Moved: ${source} → ${destination}`;
257
+ },
258
+ },
259
+
260
+ // ── Copy File ───────────────────────────────────────────────────────────────
261
+ copy_file: {
262
+ description: 'Copy a file to a new location.',
263
+ parameters: {
264
+ source : { type: 'string', required: true, description: 'Source file path' },
265
+ destination : { type: 'string', required: true, description: 'Destination file path' },
266
+ },
267
+ async execute({ source, destination }) {
268
+ const src = resolve(source);
269
+ const dest = resolve(destination);
270
+ if (!fs.existsSync(src)) throw new Error(`Source not found: ${source}`);
271
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
272
+ fs.copyFileSync(src, dest);
273
+ return `✓ Copied: ${source} → ${destination}`;
274
+ },
275
+ },
276
+
277
+ // ── File Info ───────────────────────────────────────────────────────────────
278
+ get_file_info: {
279
+ description: 'Get metadata about a file or directory (size, modified date, line count, etc.).',
280
+ parameters: {
281
+ path: { type: 'string', required: true, description: 'File or directory path' },
282
+ },
283
+ async execute({ path: filePath }) {
284
+ const abs = resolve(filePath);
285
+ if (!fs.existsSync(abs)) throw new Error(`Not found: ${filePath}`);
286
+ const stat = fs.statSync(abs);
287
+ const info = {
288
+ path : abs,
289
+ type : stat.isDirectory() ? 'directory' : 'file',
290
+ size : stat.size,
291
+ size_human : formatBytes(stat.size),
292
+ modified : stat.mtime.toISOString(),
293
+ created : stat.birthtime.toISOString(),
294
+ permissions : `0${(stat.mode & 0o777).toString(8)}`,
295
+ };
296
+ if (stat.isFile()) {
297
+ const content = fs.readFileSync(abs, 'utf8');
298
+ info.lines = content.split('\n').length;
299
+ info.encoding = 'utf-8';
300
+ }
301
+ return JSON.stringify(info, null, 2);
302
+ },
303
+ },
304
+
305
+ // ── Run Command ─────────────────────────────────────────────────────────────
306
+ run_command: {
307
+ description: 'Execute a shell command and return its output. Runs in the working directory by default.',
308
+ parameters: {
309
+ command : { type: 'string', required: true, description: 'Shell command to run' },
310
+ cwd : { type: 'string', required: false, description: 'Working directory for the command' },
311
+ timeout : { type: 'number', required: false, description: 'Timeout in milliseconds (default: 60000)' },
312
+ env : { type: 'object', required: false, description: 'Extra environment variables as key-value pairs' },
313
+ },
314
+ async execute({ command, cwd, timeout = 60_000, env = {} }) {
315
+ const workDir = cwd ? resolve(cwd) : config.WORKING_DIR;
316
+
317
+ try {
318
+ const output = execSync(command, {
319
+ cwd : workDir,
320
+ encoding : 'utf8',
321
+ timeout,
322
+ maxBuffer : 20 * 1024 * 1024,
323
+ env : { ...process.env, ...env },
324
+ stdio : ['pipe', 'pipe', 'pipe'],
325
+ });
326
+ const result = (output || '').trim();
327
+ return truncate(result || '(command completed with no output)');
328
+ } catch (err) {
329
+ const stdout = (err.stdout || '').trim();
330
+ const stderr = (err.stderr || '').trim();
331
+ const combined = [
332
+ stdout && `STDOUT:\n${stdout}`,
333
+ stderr && `STDERR:\n${stderr}`,
334
+ ].filter(Boolean).join('\n\n');
335
+ throw new Error(`Command failed (exit code ${err.status}):\n${truncate(combined || err.message)}`);
336
+ }
337
+ },
338
+ },
339
+
340
+ // ── Find Files ──────────────────────────────────────────────────────────────
341
+ find_files: {
342
+ description: 'Search for files by name pattern (glob-style, e.g. "*.js", "test_*").',
343
+ parameters: {
344
+ pattern : { type: 'string', required: true, description: 'Filename pattern (e.g. "*.ts")' },
345
+ directory : { type: 'string', required: false, description: 'Directory to search (default: working dir)' },
346
+ exclude : { type: 'string', required: false, description: 'Pattern to exclude from results' },
347
+ },
348
+ async execute({ pattern, directory = '.', exclude }) {
349
+ const dir = resolve(directory);
350
+ const excludeDirs = ['node_modules', '.git', 'dist', '.next', '__pycache__'];
351
+ let results = [];
352
+
353
+ // Convert glob pattern to regex
354
+ const regexPattern = new RegExp('^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$', 'i');
355
+
356
+ function walk(currentDir, depth) {
357
+ if (depth > 10) return;
358
+ try {
359
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
360
+ for (const entry of entries) {
361
+ const fullPath = path.join(currentDir, entry.name);
362
+ const relativePath = path.relative(dir, fullPath);
363
+
364
+ if (entry.isDirectory()) {
365
+ if (excludeDirs.includes(entry.name)) continue;
366
+ if (exclude && relativePath.includes(exclude)) continue;
367
+ walk(fullPath, depth + 1);
368
+ } else if (entry.isFile()) {
369
+ if (regexPattern.test(entry.name)) {
370
+ if (exclude && relativePath.includes(exclude)) continue;
371
+ results.push(fullPath);
372
+ }
373
+ }
374
+ }
375
+ } catch {}
376
+ }
377
+
378
+ walk(dir, 1);
379
+ return results.slice(0, 100).join('\n') || `No files matching "${pattern}" in ${directory}`;
380
+ },
381
+ },
382
+
383
+ // ── Search in Files (grep) ───────────────────────────────────────────────────
384
+ search_in_files: {
385
+ description: 'Search for text patterns inside files (like grep -r). Returns matching lines with filenames.',
386
+ parameters: {
387
+ pattern : { type: 'string', required: true, description: 'Text or regex to search for' },
388
+ directory : { type: 'string', required: false, description: 'Directory to search (default: working dir)' },
389
+ file_pattern : { type: 'string', required: false, description: 'Only search files matching this (e.g. "*.js")' },
390
+ case_sensitive: { type: 'boolean', required: false, description: 'Case-sensitive search (default: false)' },
391
+ context_lines : { type: 'number', required: false, description: 'Lines of context around each match (default: 2)' },
392
+ },
393
+ async execute({ pattern, directory = '.', file_pattern, case_sensitive = false, context_lines = 2 }) {
394
+ const dir = resolve(directory);
395
+ const excludeDirs = ['node_modules', '.git', 'dist', '.next', '__pycache__'];
396
+ const excludeExtensions = ['.lock', '.min.js', '.min.css'];
397
+ let results = [];
398
+
399
+ // Convert file_pattern glob to regex
400
+ const fileRegex = file_pattern
401
+ ? new RegExp('^' + file_pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$', 'i')
402
+ : null;
403
+
404
+ // Search pattern regex
405
+ const searchRegex = new RegExp(pattern, case_sensitive ? 'g' : 'gi');
406
+
407
+ function walk(currentDir, depth) {
408
+ if (depth > 10) return;
409
+ try {
410
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
411
+ for (const entry of entries) {
412
+ const fullPath = path.join(currentDir, entry.name);
413
+
414
+ if (entry.isDirectory()) {
415
+ if (excludeDirs.includes(entry.name)) continue;
416
+ walk(fullPath, depth + 1);
417
+ } else if (entry.isFile()) {
418
+ // Check file pattern
419
+ if (fileRegex && !fileRegex.test(entry.name)) continue;
420
+ // Skip large/binary files
421
+ if (entry.name.endsWith('.min.js') || entry.name.endsWith('.min.css')) continue;
422
+ if (excludeExtensions.some(ext => entry.name.endsWith(ext))) continue;
423
+
424
+ try {
425
+ const content = fs.readFileSync(fullPath, 'utf8');
426
+ const lines = content.split('\n');
427
+ const relativePath = path.relative(dir, fullPath);
428
+
429
+ for (let i = 0; i < lines.length; i++) {
430
+ const line = lines[i];
431
+ if (searchRegex.test(line)) {
432
+ const start = Math.max(0, i - context_lines);
433
+ const end = Math.min(lines.length, i + context_lines + 1);
434
+ const context = lines.slice(start, end).map((l, idx) => {
435
+ const lineNum = start + idx + 1;
436
+ const prefix = idx === i - start ? '>' : ' ';
437
+ return `${relativePath}:${lineNum}${prefix} ${l}`;
438
+ }).join('\n');
439
+ results.push(context);
440
+ // Reset regex lastIndex for each file
441
+ searchRegex.lastIndex = 0;
442
+ }
443
+ }
444
+ } catch {}
445
+ }
446
+ }
447
+ } catch {}
448
+ }
449
+
450
+ walk(dir, 1);
451
+ return truncate(results.slice(0, 150).join('\n\n')) || `No matches found for: ${pattern}`;
452
+ },
453
+ },
454
+
455
+ // ── Fetch URL ───────────────────────────────────────────────────────────────
456
+ read_url: {
457
+ description: 'Fetch the text content of a URL (useful for reading documentation, APIs, etc.).',
458
+ parameters: {
459
+ url: { type: 'string', required: true, description: 'Full URL to fetch (http or https)' },
460
+ },
461
+ async execute({ url }) {
462
+ return new Promise((resolve_p, reject) => {
463
+ const client = url.startsWith('https') ? https : http;
464
+ const options = {
465
+ headers: {
466
+ 'User-Agent': 'Mozilla/5.0 (compatible; YiyanAgent/1.0)',
467
+ 'Accept' : 'text/html,text/plain,application/json',
468
+ },
469
+ };
470
+
471
+ const req = client.get(url, options, (res) => {
472
+ // Follow redirects
473
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
474
+ return TOOLS.read_url.execute({ url: res.headers.location }).then(resolve_p).catch(reject);
475
+ }
476
+
477
+ let data = '';
478
+ res.on('data', chunk => { data += chunk; });
479
+ res.on('end', () => {
480
+ // Strip HTML tags for readability
481
+ const text = data
482
+ .replace(/<script[\s\S]*?<\/script>/gi, '')
483
+ .replace(/<style[\s\S]*?<\/style>/gi, '')
484
+ .replace(/<[^>]+>/g, ' ')
485
+ .replace(/\s{3,}/g, '\n\n')
486
+ .trim();
487
+ resolve_p(truncate(text));
488
+ });
489
+ });
490
+
491
+ req.on('error', reject);
492
+ req.setTimeout(15_000, () => { req.destroy(); reject(new Error('URL fetch timed out')); });
493
+ });
494
+ },
495
+ },
496
+
497
+ // ── Write Multiple Files (batch) ────────────────────────────────────────────
498
+ write_files: {
499
+ description: 'Write multiple files at once — useful for scaffolding projects.',
500
+ parameters: {
501
+ files: {
502
+ type : 'array',
503
+ required : true,
504
+ description: 'Array of {path, content} objects',
505
+ },
506
+ },
507
+ async execute({ files }) {
508
+ if (!Array.isArray(files)) throw new Error('"files" must be an array of {path, content}');
509
+ const results = [];
510
+ for (const { path: filePath, content } of files) {
511
+ const abs = resolve(filePath);
512
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
513
+ fs.writeFileSync(abs, content, 'utf8');
514
+ results.push(`✓ ${filePath}`);
515
+ }
516
+ return `Wrote ${results.length} files:\n${results.join('\n')}`;
517
+ },
518
+ },
519
+
520
+ };
521
+
522
+ // ─────────────────────────────────────────────
523
+ // Generate tool docs for the system prompt
524
+ // ─────────────────────────────────────────────
525
+ function getToolDescriptions() {
526
+ return Object.entries(TOOLS).map(([name, tool]) => {
527
+ const paramLines = Object.entries(tool.parameters || {}).map(([pName, p]) =>
528
+ ` - ${pName} (${p.type}${p.required ? ', REQUIRED' : ''}): ${p.description || ''}`
529
+ ).join('\n');
530
+
531
+ return `### ${name}\n ${tool.description}\n Parameters:\n${paramLines}`;
532
+ }).join('\n\n');
533
+ }
534
+
535
+ // ─────────────────────────────────────────────
536
+ // Execute a tool by name
537
+ // ─────────────────────────────────────────────
538
+ async function executeTool(name, args) {
539
+ const tool = TOOLS[name];
540
+ if (!tool) {
541
+ const available = Object.keys(TOOLS).join(', ');
542
+ throw new Error(`Unknown tool: "${name}". Available tools: ${available}`);
543
+ }
544
+ return await tool.execute(args);
545
+ }
546
+
547
+ module.exports = { TOOLS, executeTool, getToolDescriptions };
package/dist/cli.d.ts DELETED
@@ -1,27 +0,0 @@
1
- #!/usr/bin/env node
2
- import { C as CliOutput } from './types-BhQ78DYf.js';
3
-
4
- /** CLI 帮助信息 */
5
- declare function printHelp(): void;
6
- /** 解析后的 CLI 参数 */
7
- interface ParsedArgs {
8
- command: 'ask' | 'status' | 'reset' | 'login' | 'debug' | 'help' | null;
9
- question?: string;
10
- timeout?: number;
11
- headful?: boolean;
12
- verbose?: boolean;
13
- }
14
- /**
15
- * 解析 CLI 参数
16
- */
17
- declare function parseCliArgs(args: string[]): ParsedArgs;
18
- /**
19
- * 格式化 CLI 输出为 JSON
20
- */
21
- declare function formatCliOutput(output: CliOutput): string;
22
- /**
23
- * CLI 主入口
24
- */
25
- declare function runCli(args: string[]): Promise<void>;
26
-
27
- export { formatCliOutput, parseCliArgs, printHelp, runCli };