rl-rockcli 0.0.8 → 0.0.10

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 (162) hide show
  1. package/commands/attach/basic-repl.js +212 -0
  2. package/commands/attach/cleanup-history.js +189 -0
  3. package/commands/attach/cleanup-manager.js +163 -0
  4. package/commands/attach/copy-ui/copyRepl.js +195 -0
  5. package/commands/attach/copy-ui/index.js +7 -0
  6. package/commands/attach/copy-ui/render/outputBlock.js +25 -0
  7. package/commands/attach/copy-ui/viewport/viewport.js +23 -0
  8. package/commands/attach/copy-ui/viewport/wheel.js +14 -0
  9. package/commands/attach/history-manager.js +507 -0
  10. package/commands/attach/history-session.js +48 -0
  11. package/commands/attach/ink-repl/InkREPL.js +1507 -0
  12. package/commands/attach/ink-repl/builtinCommands.js +1253 -0
  13. package/commands/attach/ink-repl/components/ConnectingScreen.js +76 -0
  14. package/commands/attach/ink-repl/components/Console.js +191 -0
  15. package/commands/attach/ink-repl/components/DetailView.js +148 -0
  16. package/commands/attach/ink-repl/components/DropdownMenu.js +86 -0
  17. package/commands/attach/ink-repl/components/InputArea.js +125 -0
  18. package/commands/attach/ink-repl/components/InputLine.js +18 -0
  19. package/commands/attach/ink-repl/components/OutputArea.js +22 -0
  20. package/commands/attach/ink-repl/components/OutputItem.js +96 -0
  21. package/commands/attach/ink-repl/components/ShellLayout.js +61 -0
  22. package/commands/attach/ink-repl/components/Spinner.js +79 -0
  23. package/commands/attach/ink-repl/components/StatusBar.js +106 -0
  24. package/commands/attach/ink-repl/components/WelcomeBanner.js +48 -0
  25. package/commands/attach/ink-repl/contexts/LayoutContext.js +12 -0
  26. package/commands/attach/ink-repl/contexts/ThemeContext.js +43 -0
  27. package/commands/attach/ink-repl/hooks/useFunctionKeys.js +70 -0
  28. package/commands/attach/ink-repl/hooks/useMouse.js +162 -0
  29. package/commands/attach/ink-repl/hooks/useResources.js +132 -0
  30. package/commands/attach/ink-repl/hooks/useSpinner.js +49 -0
  31. package/commands/attach/ink-repl/index.js +112 -0
  32. package/commands/attach/ink-repl/package.json +3 -0
  33. package/commands/attach/ink-repl/replState.js +947 -0
  34. package/commands/attach/ink-repl/shortcuts/defaultKeybindings.js +138 -0
  35. package/commands/attach/ink-repl/shortcuts/index.js +332 -0
  36. package/commands/attach/ink-repl/themes/defaultDark.js +18 -0
  37. package/commands/attach/ink-repl/themes/defaultLight.js +18 -0
  38. package/commands/attach/ink-repl/themes/index.js +4 -0
  39. package/commands/attach/ink-repl/themes/themeManager.js +45 -0
  40. package/commands/attach/ink-repl/themes/themeTokens.js +15 -0
  41. package/commands/attach/ink-repl/utils/atCompletion.js +346 -0
  42. package/commands/attach/ink-repl/utils/clipboard.js +50 -0
  43. package/commands/attach/ink-repl/utils/consoleLogger.js +81 -0
  44. package/commands/attach/ink-repl/utils/exitCodeHandler.js +49 -0
  45. package/commands/attach/ink-repl/utils/exitCodeTips.js +56 -0
  46. package/commands/attach/ink-repl/utils/formatTime.js +12 -0
  47. package/commands/attach/ink-repl/utils/outputSelection.js +120 -0
  48. package/commands/attach/ink-repl/utils/outputViewport.js +77 -0
  49. package/commands/attach/ink-repl/utils/paginatedFileLoading.js +76 -0
  50. package/commands/attach/ink-repl/utils/paramHint.js +60 -0
  51. package/commands/attach/ink-repl/utils/parseError.js +174 -0
  52. package/commands/attach/ink-repl/utils/pathCompletion.js +167 -0
  53. package/commands/attach/ink-repl/utils/remotePathSafety.js +56 -0
  54. package/commands/attach/ink-repl/utils/replSelection.js +205 -0
  55. package/commands/attach/ink-repl/utils/responseFormatter.js +127 -0
  56. package/commands/attach/ink-repl/utils/textWrap.js +117 -0
  57. package/commands/attach/ink-repl/utils/truncate.js +115 -0
  58. package/commands/attach/opentui-repl/App.tsx +891 -0
  59. package/commands/attach/opentui-repl/builtinCommands.ts +80 -0
  60. package/commands/attach/opentui-repl/components/ConfirmDialog.tsx +116 -0
  61. package/commands/attach/opentui-repl/components/ConnectingScreen.tsx +131 -0
  62. package/commands/attach/opentui-repl/components/Console.tsx +73 -0
  63. package/commands/attach/opentui-repl/components/DetailView.tsx +45 -0
  64. package/commands/attach/opentui-repl/components/DropdownMenu.tsx +130 -0
  65. package/commands/attach/opentui-repl/components/ExecutionStatus.tsx +66 -0
  66. package/commands/attach/opentui-repl/components/Header.tsx +24 -0
  67. package/commands/attach/opentui-repl/components/OutputArea.tsx +25 -0
  68. package/commands/attach/opentui-repl/components/OutputBlock.tsx +108 -0
  69. package/commands/attach/opentui-repl/components/PromptInput.tsx +109 -0
  70. package/commands/attach/opentui-repl/components/StatusBar.tsx +63 -0
  71. package/commands/attach/opentui-repl/components/Toast.tsx +65 -0
  72. package/commands/attach/opentui-repl/components/WelcomeBanner.tsx +41 -0
  73. package/commands/attach/opentui-repl/contexts/ReplContext.tsx +137 -0
  74. package/commands/attach/opentui-repl/contexts/SessionContext.tsx +32 -0
  75. package/commands/attach/opentui-repl/contexts/ThemeContext.tsx +70 -0
  76. package/commands/attach/opentui-repl/contexts/ToastContext.tsx +69 -0
  77. package/commands/attach/opentui-repl/contexts/toast-logic.js +71 -0
  78. package/commands/attach/opentui-repl/hooks/useResources.ts +102 -0
  79. package/commands/attach/opentui-repl/hooks/useSpinner.ts +46 -0
  80. package/commands/attach/opentui-repl/index.js +99 -0
  81. package/commands/attach/opentui-repl/keybindings.ts +39 -0
  82. package/commands/attach/opentui-repl/package.json +3 -0
  83. package/commands/attach/opentui-repl/render.tsx +72 -0
  84. package/commands/attach/opentui-repl/tsconfig.json +12 -0
  85. package/commands/attach/repl.js +791 -0
  86. package/commands/attach/sandbox-id-resolver.js +56 -0
  87. package/commands/attach/session-manager.js +307 -0
  88. package/commands/attach/ui-mode.js +146 -0
  89. package/commands/log/core/constants.js +237 -0
  90. package/commands/log/core/display.js +370 -0
  91. package/commands/log/core/search.js +330 -0
  92. package/commands/log/core/tail.js +216 -0
  93. package/commands/log/core/utils.js +424 -0
  94. package/commands/log.js +298 -0
  95. package/commands/sandbox/core/log-bridge.js +119 -0
  96. package/commands/sandbox/core/replay/analyzer.js +311 -0
  97. package/commands/sandbox/core/replay/batch-orchestrator.js +536 -0
  98. package/commands/sandbox/core/replay/batch-task.js +369 -0
  99. package/commands/sandbox/core/replay/concurrent-display.js +70 -0
  100. package/commands/sandbox/core/replay/concurrent-orchestrator.js +170 -0
  101. package/commands/sandbox/core/replay/data-source.js +86 -0
  102. package/commands/sandbox/core/replay/display.js +231 -0
  103. package/commands/sandbox/core/replay/executor.js +634 -0
  104. package/commands/sandbox/core/replay/history-fetcher.js +124 -0
  105. package/commands/sandbox/core/replay/index.js +338 -0
  106. package/commands/sandbox/core/replay/loghouse-data-source.js +177 -0
  107. package/commands/sandbox/core/replay/pid-mapping.js +26 -0
  108. package/commands/sandbox/core/replay/request.js +109 -0
  109. package/commands/sandbox/core/replay/worker.js +166 -0
  110. package/commands/sandbox/core/session.js +346 -0
  111. package/commands/sandbox/log-bridge.js +2 -0
  112. package/commands/sandbox/ray.js +2 -0
  113. package/commands/sandbox/replay/analyzer.js +311 -0
  114. package/commands/sandbox/replay/batch-orchestrator.js +536 -0
  115. package/commands/sandbox/replay/batch-task.js +369 -0
  116. package/commands/sandbox/replay/concurrent-display.js +70 -0
  117. package/commands/sandbox/replay/concurrent-orchestrator.js +170 -0
  118. package/commands/sandbox/replay/display.js +231 -0
  119. package/commands/sandbox/replay/executor.js +634 -0
  120. package/commands/sandbox/replay/history-fetcher.js +118 -0
  121. package/commands/sandbox/replay/index.js +338 -0
  122. package/commands/sandbox/replay/pid-mapping.js +26 -0
  123. package/commands/sandbox/replay/request.js +109 -0
  124. package/commands/sandbox/replay/worker.js +166 -0
  125. package/commands/sandbox/replay.js +2 -0
  126. package/commands/sandbox/session.js +2 -0
  127. package/commands/sandbox-original.js +1393 -0
  128. package/commands/sandbox.js +499 -0
  129. package/help/help.json +1071 -0
  130. package/help/middleware.js +71 -0
  131. package/help/renderer.js +800 -0
  132. package/index.js +5 -15
  133. package/lib/plugin-context.js +40 -0
  134. package/package.json +2 -2
  135. package/sdks/sandbox/core/client.js +845 -0
  136. package/sdks/sandbox/core/config.js +70 -0
  137. package/sdks/sandbox/core/types.js +74 -0
  138. package/sdks/sandbox/httpLogger.js +251 -0
  139. package/sdks/sandbox/index.js +9 -0
  140. package/utils/asciiArt.js +138 -0
  141. package/utils/bun-compat.js +59 -0
  142. package/utils/ciPipelines.js +138 -0
  143. package/utils/cli.js +17 -0
  144. package/utils/command-router.js +79 -0
  145. package/utils/configManager.js +503 -0
  146. package/utils/dependency-resolver.js +135 -0
  147. package/utils/eagleeye_traceid.js +151 -0
  148. package/utils/envDetector.js +78 -0
  149. package/utils/execution_logger.js +415 -0
  150. package/utils/featureManager.js +68 -0
  151. package/utils/firstTimeTip.js +44 -0
  152. package/utils/hook-manager.js +125 -0
  153. package/utils/http-logger.js +264 -0
  154. package/utils/i18n.js +139 -0
  155. package/utils/image-progress.js +159 -0
  156. package/utils/logger.js +154 -0
  157. package/utils/plugin-loader.js +124 -0
  158. package/utils/plugin-manager.js +348 -0
  159. package/utils/ray_cli_wrapper.js +746 -0
  160. package/utils/sandbox-client.js +419 -0
  161. package/utils/terminal.js +32 -0
  162. package/utils/tips.js +106 -0
@@ -0,0 +1,499 @@
1
+ const { SandboxClient, SandboxConfig } = require('../utils/sandbox-client');
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const logger = require('../utils/logger');
5
+ const { routeSandboxCommand, showDeprecatedWarning } = require('../utils/command-router');
6
+ const { handleSandboxLogSearch, handleSandboxLogTail } = require('./sandbox/log-bridge');
7
+ const { gracefulExit } = require('../utils/execution_logger');
8
+ const configManager = require('../utils/configManager');
9
+ const { printNextStep, TIPS } = require('../utils/tips');
10
+
11
+ const isOpenSource = process.env.ROCKCLI_MODE === 'opensource';
12
+ const DEFAULT_OPEN_SOURCE_BASE_URL = 'http://127.0.0.1:8080';
13
+ const DEFAULT_OPEN_SOURCE_IMAGE = 'python:3.11';
14
+
15
+ // Import existing sandbox.js handlers
16
+ const sandboxOriginal = require('./sandbox-original');
17
+
18
+ /**
19
+ * 读取配置文件中的 sandbox 配置
20
+ * @param {Object} argv - Command line arguments
21
+ * @returns {Object} - Sandbox config with proper priority for all parameters
22
+ */
23
+ function readSandboxConfig(argv = {}) {
24
+ const config = configManager.readConfig();
25
+ const sandboxConfig = config?.sandbox || {};
26
+ const envBaseUrl = process.env.ROCKCLI_BASE_URL;
27
+
28
+ const baseUrl =
29
+ argv.baseUrl ||
30
+ envBaseUrl ||
31
+ sandboxConfig.base_url ||
32
+ (isOpenSource ? DEFAULT_OPEN_SOURCE_BASE_URL : 'http://127.0.0.1:8080');
33
+
34
+ // Get default image from internal config or use open source default
35
+ let defaultImage;
36
+ if (isOpenSource) {
37
+ defaultImage = DEFAULT_OPEN_SOURCE_IMAGE;
38
+ } else {
39
+ const { getInternalSandboxConfig } = require('../sdks/sandbox/core/config');
40
+ const internalDefaults = getInternalSandboxConfig();
41
+ defaultImage = internalDefaults.image;
42
+ }
43
+
44
+ return {
45
+ base_url: baseUrl,
46
+ image: defaultImage,
47
+ api_key: configManager.getApiKeyWithPriority(argv),
48
+ cluster: configManager.getClusterWithPriority(argv),
49
+ user_id: configManager.getUserIdWithPriority(argv),
50
+ experiment_id: configManager.getExperimentIdWithPriority(argv),
51
+ };
52
+ }
53
+
54
+ // Sandbox command module for yargs - NEW FORMAT
55
+ const sandboxExports = module.exports = {
56
+ command: 'sandbox [id] [command]',
57
+ describe: 'Sandbox instance management',
58
+ builder: (yargs) => {
59
+ return yargs
60
+ .positional('id', {
61
+ describe: 'Sandbox ID (for operations on existing sandbox)',
62
+ type: 'string',
63
+ })
64
+ .positional('command', {
65
+ describe: 'Sandbox command to perform',
66
+ type: 'string',
67
+ })
68
+ .strict(false) // Allow unknown options (they will be passed to subcommands like log search)
69
+ .option('api-key', {
70
+ describe: 'API key for authentication',
71
+ type: 'string',
72
+ group: 'Command Options:'
73
+ })
74
+ .option('base-url', {
75
+ describe: 'Base URL for sandbox service',
76
+ type: 'string',
77
+ group: 'Command Options:'
78
+ })
79
+ .option('image', {
80
+ describe: 'Docker image to use',
81
+ type: 'string',
82
+ group: 'Command Options:'
83
+ })
84
+ .option('memory', {
85
+ describe: 'Memory limit',
86
+ type: 'string',
87
+ default: '8g',
88
+ group: 'Command Options:'
89
+ })
90
+ .option('cpus', {
91
+ describe: 'CPU limit',
92
+ type: 'number',
93
+ default: 2.0,
94
+ group: 'Command Options:'
95
+ })
96
+ .option('timeout', {
97
+ describe: 'Startup timeout in seconds',
98
+ type: 'number',
99
+ default: 120,
100
+ group: 'Command Options:'
101
+ })
102
+ .option('auto-clear', {
103
+ describe: 'Auto clear time in seconds',
104
+ type: 'number',
105
+ default: 300,
106
+ group: 'Command Options:'
107
+ })
108
+ .option('cluster', {
109
+ alias: 'c',
110
+ describe: 'Cluster ID (sets X-Cluster header)',
111
+ type: 'string',
112
+ group: 'Command Options:'
113
+ })
114
+ .option('experiment-id', {
115
+ describe: 'Experiment ID (sets X-Experiment-Id header)',
116
+ type: 'string',
117
+ group: 'Command Options:'
118
+ })
119
+ .option('user-id', {
120
+ describe: 'User ID (sets X-User-Id header)',
121
+ type: 'string',
122
+ group: 'Command Options:'
123
+ })
124
+ .option('extra-header', {
125
+ alias: 'H',
126
+ type: 'array',
127
+ description: 'Extra HTTP headers in format "Key=value". Can be used multiple times.',
128
+ group: 'Command Options:',
129
+ coerce: (headers) => {
130
+ const result = {};
131
+ if (headers) {
132
+ headers.forEach(header => {
133
+ if (header.includes('=')) {
134
+ const [key, value] = header.split('=', 2);
135
+ result[key.trim()] = value.trim();
136
+ } else {
137
+ logger.warn(`Invalid header format: ${header}. Expected format: 'Key=Value'`);
138
+ }
139
+ });
140
+ }
141
+ return result;
142
+ }
143
+ })
144
+ .option('wait-for-alive', {
145
+ describe: 'Wait for sandbox to be alive before returning',
146
+ type: 'boolean',
147
+ default: false,
148
+ group: 'Command Options:'
149
+ })
150
+ .option('n', {
151
+ alias: 'lines',
152
+ describe: 'Number of recent log lines to display (default: 10)',
153
+ type: 'number',
154
+ default: 10,
155
+ group: 'Log Options:'
156
+ })
157
+ .option('f', {
158
+ alias: 'follow',
159
+ describe: 'Follow mode: continuously monitor logs (like tail -f)',
160
+ type: 'boolean',
161
+ default: false,
162
+ group: 'Log Options:'
163
+ })
164
+ .option('sleep-interval', {
165
+ alias: 'i',
166
+ describe: 'Refresh interval in seconds for follow mode (default: 5)',
167
+ type: 'number',
168
+ default: 5,
169
+ group: 'Log Options:'
170
+ })
171
+ .option('q', {
172
+ alias: 'quiet',
173
+ describe: 'Quiet mode: hide internal fields (timestamp, time_iso8601, etc.)',
174
+ type: 'boolean',
175
+ default: false,
176
+ group: 'Log Options:'
177
+ })
178
+ .option('keyword', {
179
+ alias: 'k',
180
+ describe: 'Filter logs by keyword',
181
+ type: 'string',
182
+ group: 'Log Options:'
183
+ })
184
+ .option('log-file', {
185
+ describe: 'Filter by log file name',
186
+ type: 'string',
187
+ group: 'Log Options:'
188
+ })
189
+ .option('debug', {
190
+ describe: 'Enable debug mode to display SQL query',
191
+ type: 'boolean',
192
+ default: false,
193
+ group: 'Log Options:'
194
+ })
195
+ .option('raw', {
196
+ describe: 'Display raw output (flatten to single line)',
197
+ type: 'boolean',
198
+ default: false,
199
+ group: 'Log Options:'
200
+ })
201
+ .option('multilines', {
202
+ describe: 'Display multi-line logs with separators',
203
+ type: 'boolean',
204
+ default: false,
205
+ group: 'Log Options:'
206
+ })
207
+ .option('highlight', {
208
+ describe: 'Enable keyword highlighting (default: true)',
209
+ type: 'boolean',
210
+ default: true,
211
+ group: 'Log Options:'
212
+ })
213
+ .example([
214
+ ['$0 sandbox start --image python:3.11', 'Start a new sandbox'],
215
+ ['$0 sandbox <id> status', 'Get sandbox status'],
216
+ ['$0 sandbox <id> attach', 'Attach to sandbox (interactive REPL)'],
217
+ ['$0 sandbox <id> exec -- ls -la', 'Execute command in sandbox'],
218
+ ]);
219
+ },
220
+ handler: async (argv) => {
221
+ try {
222
+ // Rebuild argv._ from process.argv to preserve all original arguments
223
+ // This is needed because yargs parses positional parameters separately
224
+ const rawArgs = process.argv.slice(2);
225
+
226
+ // Filter out options (args starting with -) from argv._
227
+ // This is needed because global options like -vvv should not be treated as positional args
228
+ argv._ = rawArgs.filter(arg => !arg.startsWith('-'));
229
+
230
+ const commandType = routeSandboxCommand(argv);
231
+
232
+ if (commandType === 'help') {
233
+ const { renderHelp } = require('../help/renderer');
234
+ console.log(renderHelp(['sandbox']));
235
+ gracefulExit(0);
236
+ return;
237
+ }
238
+
239
+ if (commandType === 'sandbox-start') {
240
+ // Handle start command
241
+ await sandboxOriginal.handler({
242
+ ...argv,
243
+ action: 'start'
244
+ });
245
+ return;
246
+ }
247
+
248
+ if (commandType === 'sandbox-id-first') {
249
+ // Handle <id> <command> format
250
+ const id = argv.id;
251
+ if (!id) {
252
+ console.error('Error: Sandbox ID is required');
253
+ gracefulExit(1);
254
+ return;
255
+ }
256
+
257
+ const command = argv.command;
258
+ if (!command) {
259
+ console.error('Error: Command is required');
260
+ gracefulExit(1);
261
+ return;
262
+ }
263
+
264
+ // Route to appropriate handler
265
+ await handleIdFirstCommand(id, command, argv);
266
+ return;
267
+ }
268
+
269
+ if (commandType === 'sandbox-session') {
270
+ // Handle session commands (deprecated)
271
+ showDeprecatedWarning('sandbox session', 'sandbox <id> exec --session <action>');
272
+
273
+ // Use original handler for now
274
+ await sandboxOriginal.handler(argv);
275
+ return;
276
+ }
277
+
278
+ // Default: use original handler
279
+ await sandboxOriginal.handler(argv);
280
+
281
+ } catch (error) {
282
+ if (error.response && error.response.status === 401) {
283
+ logger.error('Authentication failed: Invalid or missing API key');
284
+ console.error('\n💡 Please configure your API key:');
285
+ console.error(' Option 1: export ROCK_API_KEY=<your-key>');
286
+ console.error(' Option 2: Use --api-key flag: rc sandbox --id <id> --api-key <your-key>\n');
287
+ } else {
288
+ logger.error(`Sandbox action failed: ${error.message}`);
289
+ }
290
+ gracefulExit(1);
291
+ }
292
+ },
293
+ };
294
+
295
+ /**
296
+ * Handle <id> <command> format
297
+ * @param {string} id - Sandbox ID
298
+ * @param {string} command - Command to execute
299
+ * @param {Object} argv - Command line arguments
300
+ */
301
+ async function handleIdFirstCommand(id, command, argv) {
302
+ // Set sandboxId in argv
303
+ argv.sandboxId = id;
304
+ argv.id = id;
305
+
306
+ switch (command) {
307
+ case 'status':
308
+ await sandboxOriginal.handler({
309
+ ...argv,
310
+ action: 'status',
311
+ sandboxId: id
312
+ });
313
+ break;
314
+
315
+ case 'stop':
316
+ await sandboxOriginal.handler({
317
+ ...argv,
318
+ action: 'stop',
319
+ sandboxId: id
320
+ });
321
+ break;
322
+
323
+ case 'attach':
324
+ // Route to attach command
325
+ const { handler: attachHandler } = require('./attach');
326
+ await attachHandler({
327
+ ...argv,
328
+ sandboxId: id
329
+ });
330
+ break;
331
+
332
+ case 'exec':
333
+ case 'execute':
334
+ // Extract command arguments from process.argv
335
+ // This is needed because yargs parses options like -l separately
336
+ const execRawArgs = process.argv.slice(2); // Skip 'node' and script path
337
+ const execSandboxIndex = execRawArgs.indexOf('sandbox');
338
+ const execIndex = execRawArgs.indexOf('exec');
339
+
340
+ if (execIndex !== -1) {
341
+ // Get all arguments after 'exec'
342
+ const execArgs = execRawArgs.slice(execIndex + 1);
343
+
344
+ // Parse --session parameter from exec args
345
+ let sessionName = null;
346
+ let commandArgs = [];
347
+
348
+ for (let i = 0; i < execArgs.length; i++) {
349
+ if (execArgs[i] === '--session' && i + 1 < execArgs.length) {
350
+ sessionName = execArgs[i + 1];
351
+ i++; // Skip session value
352
+ } else if (execArgs[i] && !execArgs[i].startsWith('-')) {
353
+ commandArgs.push(execArgs[i]);
354
+ }
355
+ }
356
+
357
+ const command = commandArgs.join(' ');
358
+
359
+ if (sessionName) {
360
+ // Auto-create session before running command
361
+ const sessionHandler = require('./sandbox/session');
362
+
363
+ // Try to create session (ignore errors - session may already exist or sandbox may not be running)
364
+ await sessionHandler.handler({
365
+ ...argv,
366
+ subaction: 'create',
367
+ sandboxId: id,
368
+ session: sessionName,
369
+ envEnable: true,
370
+ }).catch(() => {
371
+ // Ignore errors from create
372
+ });
373
+
374
+ // Run command in session
375
+ await sessionHandler.handler({
376
+ ...argv,
377
+ subaction: 'run',
378
+ sandboxId: id,
379
+ session: sessionName,
380
+ command: command,
381
+ });
382
+ } else {
383
+ await sandboxOriginal.handler({
384
+ ...argv,
385
+ action: 'execute',
386
+ sandboxId: id,
387
+ command: command || undefined
388
+ });
389
+ }
390
+ } else {
391
+ await sandboxOriginal.handler({
392
+ ...argv,
393
+ action: 'execute',
394
+ sandboxId: id
395
+ });
396
+ }
397
+ break;
398
+
399
+ case 'upload':
400
+ await sandboxOriginal.handler({
401
+ ...argv,
402
+ action: 'upload',
403
+ sandboxId: id
404
+ });
405
+ break;
406
+
407
+ case 'download':
408
+ await sandboxOriginal.handler({
409
+ ...argv,
410
+ action: 'download',
411
+ sandboxId: id
412
+ });
413
+ break;
414
+
415
+ case 'history':
416
+ // History is the same as log history
417
+ const { handleLogHistory } = require('./log/history');
418
+ await handleLogHistory({
419
+ ...argv,
420
+ sandboxId: id
421
+ });
422
+ break;
423
+
424
+ case 'log':
425
+ // Log operations require additional handling
426
+ const logSubcommand = argv._[argv._.indexOf('log') + 1];
427
+
428
+ // Extract additional arguments from process.argv (beyond yargs parsing)
429
+ const rawArgs = process.argv.slice(2);
430
+ const sandboxIndex = rawArgs.indexOf('sandbox');
431
+
432
+ // Find the log file name (first non-option argument after 'log' or after 'tail')
433
+ let logFile = null;
434
+ let foundLog = false;
435
+ let foundTail = false;
436
+
437
+ for (let i = sandboxIndex + 1; i < rawArgs.length; i++) {
438
+ const arg = rawArgs[i];
439
+
440
+ if (arg === 'log') {
441
+ foundLog = true;
442
+ continue;
443
+ }
444
+
445
+ if (foundLog && arg === 'tail') {
446
+ foundTail = true;
447
+ continue;
448
+ }
449
+
450
+ if (foundTail && !arg.startsWith('-')) {
451
+ logFile = arg;
452
+ break;
453
+ }
454
+ }
455
+
456
+ if (logFile) {
457
+ argv.logFile = logFile;
458
+ }
459
+
460
+ if (logSubcommand === 'search') {
461
+ await handleSandboxLogSearch({
462
+ ...argv,
463
+ sandboxId: id
464
+ });
465
+ } else if (logSubcommand === 'tail') {
466
+ await handleSandboxLogTail({
467
+ ...argv,
468
+ sandboxId: id
469
+ });
470
+ } else {
471
+ console.error(`错误:缺少 log 子命令\n`);
472
+ console.error(`可用的子命令:`);
473
+ console.error(` search - 搜索日志`);
474
+ console.error(` tail - 实时监控日志\n`);
475
+ console.error(`使用示例:`);
476
+ console.error(` rockcli sandbox ${id} log search -k "error"`);
477
+ console.error(` rockcli sandbox ${id} log tail -f\n`);
478
+ console.error(`查看帮助:`);
479
+ console.error(` rockcli sandbox --help`);
480
+ gracefulExit(1);
481
+ }
482
+ break;
483
+
484
+ default:
485
+ console.error(`Unknown command: ${command}`);
486
+ gracefulExit(1);
487
+ }
488
+ }
489
+
490
+ // Export internal functions for testing
491
+ sandboxExports.readSandboxConfig = readSandboxConfig;
492
+
493
+ // Re-export functions from backup module for backward compatibility
494
+ if (sandboxOriginal.getRemoteFileList) {
495
+ sandboxExports.getRemoteFileList = sandboxOriginal.getRemoteFileList;
496
+ }
497
+ if (sandboxOriginal.validateAuth) {
498
+ sandboxExports.validateAuth = sandboxOriginal.validateAuth;
499
+ }