rl-rockcli 0.0.13 → 0.0.15

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.
@@ -99,6 +99,7 @@ import {
99
99
  isBuiltinCommand,
100
100
  executeBuiltinCommand,
101
101
  checkInteractiveCommand,
102
+ mergePluginAttachCommands,
102
103
  } from './builtinCommands.js';
103
104
  import { initConsoleLogger, clearConsoleLogger, consoleLogger } from './utils/consoleLogger.js';
104
105
  import { copyToClipboard } from './utils/clipboard.js';
@@ -122,6 +123,24 @@ export function InkREPL({
122
123
  const { exit } = useApp();
123
124
  const { stdout } = useStdout();
124
125
 
126
+ // Merge plugin attach commands on mount
127
+ const hasMergedPluginCommands = useRef(false);
128
+ useEffect(() => {
129
+ if (hasMergedPluginCommands.current) return;
130
+ hasMergedPluginCommands.current = true;
131
+
132
+ try {
133
+ const PluginManager = require('../../../utils/plugin-manager');
134
+ const pluginManager = PluginManager.getInstance();
135
+ const attachCommands = pluginManager.getRegisteredAttachCommands();
136
+ if (attachCommands.length > 0) {
137
+ mergePluginAttachCommands(attachCommands);
138
+ }
139
+ } catch (e) {
140
+ // PluginManager not initialized, ignore
141
+ }
142
+ }, []);
143
+
125
144
  // Load history outputs on mount
126
145
  const [initialOutputs, setInitialOutputs] = useState(null);
127
146
  const [initialCommandHistory, setInitialCommandHistory] = useState(null);
@@ -13,7 +13,6 @@ export const COMMAND_HELP = {
13
13
  // Primary commands
14
14
  status: 'Show sandbox status',
15
15
  sessions: 'List attach sessions',
16
- log: 'View sandbox logs. Usage: /log [-f file] [-k keyword] [-n lines]',
17
16
  tail: 'View last lines of a file. Usage: /tail [-n lines] [-k keyword] [-f] <file>',
18
17
  upload: 'Upload file/directory to sandbox. Usage: /upload @<local> @<remote>',
19
18
  download: 'Download file from sandbox. Usage: /download @<remote> @<local>',
@@ -379,91 +378,6 @@ export const commandHandlers = {
379
378
  }
380
379
  },
381
380
 
382
- /**
383
- * /log - View sandbox logs
384
- */
385
- log: async (ctx, args) => {
386
- let keyword = null;
387
- let lines = 100;
388
- let logFile = 'command.log';
389
-
390
- for (let i = 0; i < args.length; i++) {
391
- if (args[i] === '-k' && args[i + 1]) {
392
- keyword = args[++i];
393
- } else if (args[i] === '-n' && args[i + 1]) {
394
- lines = clampNumber(args[++i], { min: 1, max: 5000, fallback: 100 });
395
- } else if ((args[i] === '-f' || args[i] === '--file') && args[i + 1]) {
396
- logFile = args[++i];
397
- }
398
- }
399
-
400
- // Reject obvious shell injection for user-provided values. This is not an auto-triggered path,
401
- // but we still keep it safe since it runs in the sandbox shell.
402
- if (hasDangerousShellChars(logFile) || (keyword && hasDangerousShellChars(keyword))) {
403
- return { output: 'Invalid log parameters.', exitCode: 1 };
404
- }
405
-
406
- try {
407
- // Try Loghouse first (only supports safe basenames for logFile)
408
- const safeLoghouseFile = isSafeLogFileName(logFile) ? logFile : null;
409
- const handleLogSearch =
410
- typeof ctx.handleLogSearch === 'function'
411
- ? ctx.handleLogSearch
412
- : require('../../log/search').handleLogSearch;
413
-
414
- const searchArgv = {
415
- sandboxId: ctx.sandboxId,
416
- logFile: safeLoghouseFile || 'command.log',
417
- minutes: 60,
418
- limit: lines,
419
- keyword: keyword || undefined,
420
- raw: false,
421
- highlight: true,
422
- highlightSandboxId: false,
423
- truncate: 200,
424
- };
425
-
426
- // Capture console output
427
- const output = [];
428
- const originalLog = console.log;
429
- const originalError = console.error;
430
-
431
- console.log = (...args) => output.push(args.join(' '));
432
- console.error = (...args) => output.push(args.join(' '));
433
-
434
- try {
435
- await handleLogSearch(searchArgv);
436
- } finally {
437
- console.log = originalLog;
438
- console.error = originalError;
439
- }
440
-
441
- if (output.length > 0) {
442
- return { output: output.join('\n'), exitCode: 0 };
443
- }
444
- return { output: 'No logs found.', exitCode: 0 };
445
- } catch (error) {
446
- // Fallback to local log
447
- try {
448
- const remotePath = resolveRemoteLogPath(logFile);
449
- if (hasDangerousShellChars(remotePath)) {
450
- return { output: 'Invalid log file path.', exitCode: 1 };
451
- }
452
-
453
- let command = `tail -n ${lines} -- ${shellEscapePosix(remotePath)} 2>/dev/null`;
454
- if (keyword) {
455
- command = `${command} | grep -i -- ${shellEscapePosix(keyword)}`;
456
- }
457
- command = `${command} || echo ${shellEscapePosix(keyword ? 'No matching logs' : 'No logs available')}`;
458
-
459
- const result = await ctx.sessionManager.execute(command);
460
- return { output: result.output || 'No logs available', exitCode: 0 };
461
- } catch (e) {
462
- return { output: `Failed to get logs: ${e.message}`, exitCode: 1 };
463
- }
464
- }
465
- },
466
-
467
381
  /**
468
382
  * /upload - Upload file or directory to sandbox
469
383
  * Supports @ prefix for paths (e.g., /upload @local.txt @/remote.txt)
@@ -1251,3 +1165,32 @@ export function checkInteractiveCommand(command) {
1251
1165
 
1252
1166
  return { isInteractive: false, cmdName, alternative: null };
1253
1167
  }
1168
+
1169
+ /**
1170
+ * Merge plugin attach commands into builtin commands
1171
+ * @param {Array<Object>} pluginCommands - Array of plugin command definitions
1172
+ * @param {string} pluginCommands[].name - Command name (without / prefix)
1173
+ * @param {string} pluginCommands[].description - Command description
1174
+ * @param {Function} pluginCommands[].handler - Command handler function
1175
+ */
1176
+ export function mergePluginAttachCommands(pluginCommands) {
1177
+ if (!pluginCommands || !Array.isArray(pluginCommands)) {
1178
+ return;
1179
+ }
1180
+
1181
+ for (const cmd of pluginCommands) {
1182
+ if (!cmd || !cmd.name) {
1183
+ continue;
1184
+ }
1185
+
1186
+ // Merge into COMMAND_HELP
1187
+ if (cmd.description) {
1188
+ COMMAND_HELP[cmd.name] = cmd.description;
1189
+ }
1190
+
1191
+ // Merge into commandHandlers
1192
+ if (cmd.handler && typeof cmd.handler === 'function') {
1193
+ commandHandlers[cmd.name] = cmd.handler;
1194
+ }
1195
+ }
1196
+ }
@@ -15,7 +15,7 @@ import { Console } from './components/Console.tsx';
15
15
  import { Toast } from './components/Toast.tsx';
16
16
  import { ConnectingScreen } from './components/ConnectingScreen.tsx';
17
17
  import { useResources } from './hooks/useResources.ts';
18
- import { executeBuiltinCommand, isBuiltinCommand, checkInteractiveCommand } from './builtinCommands.ts';
18
+ import { executeBuiltinCommand, isBuiltinCommand, checkInteractiveCommand, mergePluginAttachCommands } from './builtinCommands.ts';
19
19
  import {
20
20
  parseAtContext,
21
21
  getLocalCompletions,
@@ -76,6 +76,20 @@ function ReplShell(props: { onExit?: () => void }) {
76
76
  // Stats tracking
77
77
  const stats = { startTime: Date.now(), shellCommands: 0, builtinCommands: 0 };
78
78
 
79
+ // Merge plugin attach commands on mount
80
+ onMount(() => {
81
+ try {
82
+ const PluginManager = require('../../../utils/plugin-manager');
83
+ const pluginManager = PluginManager.getInstance();
84
+ const attachCommands = pluginManager.getRegisteredAttachCommands();
85
+ if (attachCommands.length > 0) {
86
+ mergePluginAttachCommands(attachCommands);
87
+ }
88
+ } catch (e) {
89
+ // PluginManager not initialized, ignore
90
+ }
91
+ });
92
+
79
93
  // AbortController for cancelling execution
80
94
  let abortController: AbortController | null = null;
81
95
 
@@ -78,3 +78,15 @@ export async function checkInteractiveCommand(
78
78
  const mod = getBuiltinModule();
79
79
  return mod.checkInteractiveCommand(command);
80
80
  }
81
+
82
+ export interface AttachCommandDef {
83
+ name: string;
84
+ description: string;
85
+ handler: (context: BuiltinContext, args: string[]) => Promise<BuiltinResult>;
86
+ pluginName?: string;
87
+ }
88
+
89
+ export function mergePluginAttachCommands(commands: AttachCommandDef[]): void {
90
+ const mod = getBuiltinModule();
91
+ return mod.mergePluginAttachCommands(commands);
92
+ }
@@ -20,9 +20,9 @@ const DEFAULT_OPEN_SOURCE_BASE_URL = 'http://127.0.0.1:8080';
20
20
 
21
21
  /**
22
22
  * List of all builtin commands available in the REPL
23
+ * Note: /log is registered by loghouse plugin via registerAttachCommand
23
24
  */
24
25
  const BUILTIN_COMMANDS = [
25
- { name: 'log', description: 'View sandbox logs' },
26
26
  { name: 'upload', description: 'Upload file to sandbox' },
27
27
  { name: 'download', description: 'Download file from sandbox' },
28
28
  { name: 'status', description: 'Show sandbox status' },
@@ -69,7 +69,8 @@ const sandboxExports = module.exports = {
69
69
  .option('api-key', {
70
70
  describe: 'API key for authentication',
71
71
  type: 'string',
72
- group: 'Command Options:'
72
+ group: 'Command Options:',
73
+ hidden: isOpenSource // Hide in open source version
73
74
  })
74
75
  .option('base-url', {
75
76
  describe: 'Base URL for sandbox service',
@@ -109,17 +110,20 @@ const sandboxExports = module.exports = {
109
110
  alias: 'c',
110
111
  describe: 'Cluster ID (sets X-Cluster header)',
111
112
  type: 'string',
112
- group: 'Command Options:'
113
+ group: 'Command Options:',
114
+ hidden: isOpenSource // Hide in open source version
113
115
  })
114
116
  .option('experiment-id', {
115
117
  describe: 'Experiment ID (sets X-Experiment-Id header)',
116
118
  type: 'string',
117
- group: 'Command Options:'
119
+ group: 'Command Options:',
120
+ hidden: isOpenSource // Hide in open source version
118
121
  })
119
122
  .option('user-id', {
120
123
  describe: 'User ID (sets X-User-Id header)',
121
124
  type: 'string',
122
- group: 'Command Options:'
125
+ group: 'Command Options:',
126
+ hidden: isOpenSource // Hide in open source version
123
127
  })
124
128
  .option('extra-header', {
125
129
  alias: 'H',
@@ -147,68 +151,79 @@ const sandboxExports = module.exports = {
147
151
  default: false,
148
152
  group: 'Command Options:'
149
153
  })
154
+ // Log Options - hidden in open source version (log command not supported)
150
155
  .option('n', {
151
156
  alias: 'lines',
152
157
  describe: 'Number of recent log lines to display (default: 10)',
153
158
  type: 'number',
154
159
  default: 10,
155
- group: 'Log Options:'
160
+ group: 'Log Options:',
161
+ hidden: isOpenSource
156
162
  })
157
163
  .option('f', {
158
164
  alias: 'follow',
159
165
  describe: 'Follow mode: continuously monitor logs (like tail -f)',
160
166
  type: 'boolean',
161
167
  default: false,
162
- group: 'Log Options:'
168
+ group: 'Log Options:',
169
+ hidden: isOpenSource
163
170
  })
164
171
  .option('sleep-interval', {
165
172
  alias: 'i',
166
173
  describe: 'Refresh interval in seconds for follow mode (default: 5)',
167
174
  type: 'number',
168
175
  default: 5,
169
- group: 'Log Options:'
176
+ group: 'Log Options:',
177
+ hidden: isOpenSource
170
178
  })
171
179
  .option('q', {
172
180
  alias: 'quiet',
173
181
  describe: 'Quiet mode: hide internal fields (timestamp, time_iso8601, etc.)',
174
182
  type: 'boolean',
175
183
  default: false,
176
- group: 'Log Options:'
184
+ group: 'Log Options:',
185
+ hidden: isOpenSource
177
186
  })
178
187
  .option('keyword', {
179
188
  alias: 'k',
180
189
  describe: 'Filter logs by keyword',
181
190
  type: 'string',
182
- group: 'Log Options:'
191
+ group: 'Log Options:',
192
+ hidden: isOpenSource
183
193
  })
184
194
  .option('log-file', {
185
195
  describe: 'Filter by log file name',
186
196
  type: 'string',
187
- group: 'Log Options:'
197
+ group: 'Log Options:',
198
+ hidden: isOpenSource
188
199
  })
189
200
  .option('debug', {
190
201
  describe: 'Enable debug mode to display SQL query',
191
202
  type: 'boolean',
192
203
  default: false,
193
- group: 'Log Options:'
204
+ group: 'Log Options:',
205
+ hidden: isOpenSource
194
206
  })
195
207
  .option('raw', {
196
208
  describe: 'Display raw output (flatten to single line)',
197
209
  type: 'boolean',
198
210
  default: false,
199
- group: 'Log Options:'
211
+ group: 'Log Options:',
212
+ hidden: isOpenSource
200
213
  })
201
214
  .option('multilines', {
202
215
  describe: 'Display multi-line logs with separators',
203
216
  type: 'boolean',
204
217
  default: false,
205
- group: 'Log Options:'
218
+ group: 'Log Options:',
219
+ hidden: isOpenSource
206
220
  })
207
221
  .option('highlight', {
208
222
  describe: 'Enable keyword highlighting (default: true)',
209
223
  type: 'boolean',
210
224
  default: true,
211
- group: 'Log Options:'
225
+ group: 'Log Options:',
226
+ hidden: isOpenSource
212
227
  })
213
228
  .example([
214
229
  ['$0 sandbox start --image python:3.11', 'Start a new sandbox'],
package/help/renderer.js CHANGED
@@ -36,24 +36,24 @@ function renderHelp(commandPath, pluginManager = null) {
36
36
  // 检查是否是插件命令(带有 helpConfig)
37
37
  if (pluginManager) {
38
38
  const pluginCommands = pluginManager.getRegisteredCommands();
39
-
39
+
40
40
  // 查找父命令(如 "log")
41
41
  const parentCmd = pluginCommands.find(cmd => cmd.command === command);
42
-
42
+
43
43
  if (parentCmd && parentCmd.helpConfig) {
44
44
  // 使用插件的 helpConfig 渲染帮助
45
45
  return renderPluginHelpConfig(command, parentCmd.helpConfig, subcommand, rest);
46
46
  }
47
-
47
+
48
48
  // 首先检查是否是插件一级命令(有子命令)
49
49
  const hasSubCommands = pluginCommands.some(cmd => cmd.command.startsWith(command + ' '));
50
-
50
+
51
51
  if (hasSubCommands && !subcommand) {
52
52
  // 这是一个有子命令的父命令,显示子命令列表
53
53
  const subCommands = pluginCommands.filter(cmd => cmd.command.startsWith(command + ' '));
54
54
  return renderPluginSubcommandsHelp(command, subCommands, pluginManager);
55
55
  }
56
-
56
+
57
57
  // 检查是否是插件二级命令
58
58
  if (hasSubCommands && subcommand) {
59
59
  const fullCommand = `${command} ${subcommand}`;
@@ -66,11 +66,11 @@ function renderHelp(commandPath, pluginManager = null) {
66
66
  return renderPluginCommandHelp(pluginCmd, pluginManager);
67
67
  }
68
68
  }
69
-
69
+
70
70
  // 否则查找匹配的一级命令
71
71
  // 优先查找精确匹配的父命令(如 "formula"),而不是子命令(如 "formula list")
72
72
  let pluginCmd = pluginCommands.find(cmd => cmd.command === command);
73
-
73
+
74
74
  // 如果没有精确匹配,再查找命令名称匹配的
75
75
  if (!pluginCmd) {
76
76
  pluginCmd = pluginCommands.find(cmd => {
@@ -134,6 +134,9 @@ function getAsciiLogo() {
134
134
  * 渲染一级帮助(根帮助)
135
135
  */
136
136
  function renderRootHelp(pluginManager = null) {
137
+ // Check if running in open source mode (evaluated at function call time)
138
+ const isOpenSource = process.env.ROCKCLI_MODE === 'opensource';
139
+
137
140
  const lines = [];
138
141
 
139
142
  // Logo (with leading empty line like opencode)
@@ -176,14 +179,14 @@ function renderRootHelp(pluginManager = null) {
176
179
  if (pluginManager) {
177
180
  const pluginCommands = pluginManager.getRegisteredCommands();
178
181
  const existingCommands = helpConfig.groups.flatMap(g => g.commands);
179
-
182
+
180
183
  // 按命令层级分组:一级命令和二级命令
181
184
  const topLevelCommands = [];
182
185
  const subCommands = new Map(); // key: parent command, value: sub commands list
183
-
186
+
184
187
  for (const cmd of pluginCommands) {
185
188
  const parts = cmd.command.split(' ');
186
-
189
+
187
190
  // 判断是否是一级命令
188
191
  // 如果只有一个部分,或者第二个部分以 [ 或 < 开头(参数),则是一级命令
189
192
  if (parts.length === 1 || (parts.length === 2 && (parts[1].startsWith('[') || parts[1].startsWith('<')))) {
@@ -198,7 +201,7 @@ function renderRootHelp(pluginManager = null) {
198
201
  subCommands.get(parent).push(cmd);
199
202
  }
200
203
  }
201
-
204
+
202
205
  // 创建父命令的描述(用于显示)
203
206
  // 优先使用插件注册的父命令描述
204
207
  const parentCommandDescriptions = new Map();
@@ -214,20 +217,20 @@ function renderRootHelp(pluginManager = null) {
214
217
  }
215
218
  }
216
219
  }
217
-
220
+
218
221
  // 过滤掉与内置命令冲突的一级命令(排除纯父命令描述)
219
222
  const filteredTopLevel = topLevelCommands.filter(cmd => {
220
223
  const cmdName = cmd.command.split(' ')[0];
221
224
  return !existingCommands.includes(cmdName);
222
225
  });
223
-
226
+
224
227
  // 创建一个包含所有一级命令名称的集合(包括有子命令的父命令)
225
228
  const allTopLevelNames = new Set(filteredTopLevel.map(cmd => cmd.command.split(' ')[0]));
226
229
  subCommands.forEach((_, parent) => allTopLevelNames.add(parent));
227
-
230
+
228
231
  if (allTopLevelNames.size > 0) {
229
232
  lines.push('插件命令');
230
-
233
+
231
234
  // 按名称排序并显示所有一级命令
232
235
  const sortedNames = Array.from(allTopLevelNames).sort();
233
236
  for (const name of sortedNames) {
@@ -242,7 +245,7 @@ function renderRootHelp(pluginManager = null) {
242
245
  lines.push(INDENT + padEnd(name, CMD_WIDTH) + desc);
243
246
  }
244
247
  }
245
-
248
+
246
249
  lines.push('');
247
250
  }
248
251
  }
@@ -250,7 +253,11 @@ function renderRootHelp(pluginManager = null) {
250
253
  // 全局选项
251
254
  lines.push('全局选项');
252
255
  for (const opt of helpConfig.globalOptions) {
253
- const defaultStr = opt.default ? ` (默认: ${opt.default})` : '';
256
+ // Hide --api-key and --cluster in open source version
257
+ if (isOpenSource && (opt.flags.includes('--api-key') || opt.flags.includes('--cluster'))) {
258
+ continue;
259
+ }
260
+ const defaultStr = opt.default ? ` (默认:${opt.default})` : '';
254
261
  lines.push(INDENT + padEnd(opt.flags, FLAG_WIDTH) + opt.description + defaultStr);
255
262
  }
256
263
  lines.push('');
@@ -275,9 +282,12 @@ function renderRootHelp(pluginManager = null) {
275
282
  * 渲染命令帮助(二级)
276
283
  */
277
284
  function renderCommandHelp(command) {
285
+ // Check if running in open source mode (evaluated at function call time)
286
+ const isOpenSource = process.env.ROCKCLI_MODE === 'opensource';
287
+
278
288
  const cmdConfig = helpConfig.commands[command];
279
289
  if (!cmdConfig) {
280
- return `未知命令: ${command}`;
290
+ return `未知命令:${command}`;
281
291
  }
282
292
 
283
293
  const lines = [];
@@ -302,7 +312,7 @@ function renderCommandHelp(command) {
302
312
  if (cmdConfig.subcommands) {
303
313
  lines.push('子命令');
304
314
  for (const [name, subcmd] of Object.entries(cmdConfig.subcommands)) {
305
- const aliasStr = subcmd.aliases ? ` (别名: ${subcmd.aliases.join(', ')})` : '';
315
+ const aliasStr = subcmd.aliases ? ` (别名:${subcmd.aliases.join(', ')})` : '';
306
316
  lines.push(INDENT + padEnd(name, CMD_WIDTH) + subcmd.description + aliasStr);
307
317
  }
308
318
  lines.push('');
@@ -312,13 +322,17 @@ function renderCommandHelp(command) {
312
322
  if (cmdConfig.commonOptions && cmdConfig.commonOptions.length > 0) {
313
323
  lines.push('通用选项');
314
324
  for (const opt of cmdConfig.commonOptions) {
315
- const defaultStr = opt.default ? ` (默认: ${opt.default})` : '';
325
+ // Hide --api-key and --cluster in open source version
326
+ if (isOpenSource && (opt.flags.includes('--api-key') || opt.flags.includes('--cluster'))) {
327
+ continue;
328
+ }
329
+ const defaultStr = opt.default ? ` (默认:${opt.default})` : '';
316
330
  const requiredStr = opt.required ? ' (必填)' : '';
317
331
  lines.push(INDENT + padEnd(opt.flags, FLAG_WIDTH) + opt.description + defaultStr + requiredStr);
318
332
  }
319
333
  lines.push('');
320
334
  }
321
-
335
+
322
336
  // 选项 (适用于无子命令的命令,如 config)
323
337
  if (cmdConfig.options && cmdConfig.options.length > 0) {
324
338
  lines.push('选项');
@@ -481,11 +495,11 @@ function renderPluginSubcommandsHelp(command, subCommands, pluginManager) {
481
495
  // 有真正的子命令,显示子命令列表
482
496
  lines.push(parentDescribe);
483
497
  lines.push('');
484
-
498
+
485
499
  lines.push('用法');
486
500
  lines.push(INDENT + `rockcli ${command} <子命令> [选项]`);
487
501
  lines.push('');
488
-
502
+
489
503
  lines.push('子命令');
490
504
  for (const subCmd of subCommands) {
491
505
  const parts = subCmd.command.split(' ');
@@ -496,7 +510,7 @@ function renderPluginSubcommandsHelp(command, subCommands, pluginManager) {
496
510
  }
497
511
  }
498
512
  lines.push('');
499
-
513
+
500
514
  lines.push('来源');
501
515
  lines.push(INDENT + `插件: ${pluginName}`);
502
516
  lines.push('');
@@ -507,7 +521,7 @@ function renderPluginSubcommandsHelp(command, subCommands, pluginManager) {
507
521
  lines.push(INDENT + `rockcli ${subCmd.command}`);
508
522
  });
509
523
  lines.push('');
510
-
524
+
511
525
  // 显示第一个参数变体的选项
512
526
  if (subCommands[0].options) {
513
527
  lines.push('选项');
@@ -518,7 +532,7 @@ function renderPluginSubcommandsHelp(command, subCommands, pluginManager) {
518
532
  }
519
533
  lines.push('');
520
534
  }
521
-
535
+
522
536
  lines.push('来源');
523
537
  lines.push(INDENT + `插件: ${pluginName}`);
524
538
  lines.push('');
@@ -533,12 +547,12 @@ function renderPluginSubcommandsHelp(command, subCommands, pluginManager) {
533
547
  function renderPluginCommandHelp(cmd, pluginManager = null) {
534
548
  const lines = [];
535
549
  const parts = cmd.command.split(' ');
536
-
550
+
537
551
  // 检查是否是父命令(有子命令)
538
552
  if (pluginManager && parts.length === 1) {
539
553
  const allPluginCommands = pluginManager.getRegisteredCommands();
540
554
  const relatedCommands = allPluginCommands.filter(c => c.command.startsWith(cmd.command + ' '));
541
-
555
+
542
556
  if (relatedCommands.length > 0) {
543
557
  // 检查这些相关命令的第二个部分是否都是参数标记(子命令还是参数变体)
544
558
  const hasRealSubcommands = relatedCommands.some(c => {
@@ -548,16 +562,16 @@ function renderPluginCommandHelp(cmd, pluginManager = null) {
548
562
  // 如果第二个部分以 [ 或 < 开头,则是参数变体,不是子命令
549
563
  return !secondPart.startsWith('[') && !secondPart.startsWith('<');
550
564
  });
551
-
565
+
552
566
  if (hasRealSubcommands) {
553
567
  // 有真正的子命令,显示子命令列表
554
568
  lines.push(cmd.describe || '插件命令');
555
569
  lines.push('');
556
-
570
+
557
571
  lines.push('用法');
558
572
  lines.push(INDENT + `rockcli ${cmd.command} <子命令> [选项]`);
559
573
  lines.push('');
560
-
574
+
561
575
  lines.push('子命令');
562
576
  for (const subCmd of relatedCommands) {
563
577
  const parts = subCmd.command.split(' ');
@@ -568,13 +582,13 @@ function renderPluginCommandHelp(cmd, pluginManager = null) {
568
582
  }
569
583
  }
570
584
  lines.push('');
571
-
585
+
572
586
  if (cmd.pluginName) {
573
587
  lines.push('来源');
574
588
  lines.push(INDENT + `插件: ${cmd.pluginName}`);
575
589
  lines.push('');
576
590
  }
577
-
591
+
578
592
  return lines.join('\n');
579
593
  } else {
580
594
  // 没有真正的子命令,都是参数变体,显示命令列表
@@ -583,7 +597,7 @@ function renderPluginCommandHelp(cmd, pluginManager = null) {
583
597
  lines.push(INDENT + `rockcli ${relCmd.command}`);
584
598
  });
585
599
  lines.push('');
586
-
600
+
587
601
  // 显示第一个参数变体的选项
588
602
  if (relatedCommands[0].options) {
589
603
  lines.push('选项');
@@ -594,18 +608,18 @@ function renderPluginCommandHelp(cmd, pluginManager = null) {
594
608
  }
595
609
  lines.push('');
596
610
  }
597
-
611
+
598
612
  if (cmd.pluginName) {
599
613
  lines.push('来源');
600
614
  lines.push(INDENT + `插件: ${cmd.pluginName}`);
601
615
  lines.push('');
602
616
  }
603
-
617
+
604
618
  return lines.join('\n');
605
619
  }
606
620
  }
607
621
  }
608
-
622
+
609
623
  // 单个命令或没有子命令的情况
610
624
  lines.push(cmd.describe || '插件命令');
611
625
  lines.push('');
@@ -648,19 +662,19 @@ function renderPluginHelpConfig(command, helpConfig, subcommand, rest) {
648
662
  // 渲染父命令帮助
649
663
  return renderPluginCommandHelpConfig(command, helpConfig);
650
664
  }
651
-
665
+
652
666
  // 渲染子命令帮助
653
667
  if (helpConfig.subcommands && helpConfig.subcommands[subcommand]) {
654
668
  const subcmdConfig = helpConfig.subcommands[subcommand];
655
-
669
+
656
670
  // 检查是否有更深层的嵌套子命令
657
671
  if (rest.length > 0 && subcmdConfig.subcommands && subcmdConfig.subcommands[rest[0]]) {
658
672
  return renderPluginNestedSubcommandHelpConfig(command, subcommand, rest[0], helpConfig);
659
673
  }
660
-
674
+
661
675
  return renderPluginSubcommandHelpConfig(command, subcommand, subcmdConfig);
662
676
  }
663
-
677
+
664
678
  // 没有找到子命令配置,回退到父命令
665
679
  return renderPluginCommandHelpConfig(command, helpConfig);
666
680
  }
package/index.js CHANGED
@@ -22,7 +22,6 @@ const { hideBin } = require('yargs/helpers');
22
22
 
23
23
  // ⚠️ 关键:只引用 Core 命令,不引用 Internal
24
24
  // 这确保打包时不会包含内网代码
25
- const logCommand = require('./commands/log'); // 已支持 ROCKCLI_MODE 过滤
26
25
  const sandboxCommand = require('./commands/sandbox'); // 已支持 ROCKCLI_MODE 过滤
27
26
 
28
27
  // ASCII Art Logo
@@ -41,7 +40,6 @@ function run() {
41
40
  cli
42
41
  .scriptName('rockcli')
43
42
  .usage(LOGO + '\n\nUsage: $0 <command> [options]')
44
- .command(logCommand)
45
43
  .command(sandboxCommand)
46
44
  .demandCommand(1, '请提供一个有效的命令。使用 --help 查看可用命令。')
47
45
  .strict()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rl-rockcli",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "description": "Open-source ROCK CLI - Sandbox and Log management tool",
5
5
  "bin": {
6
6
  "rockcli": "./index.js"