rl-rockcli 0.0.2 → 0.0.3
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 +400 -0
- package/index.js +51 -21
- package/package.json +1 -1
- package/commands/log/core/constants.js +0 -237
- package/commands/log/core/display.js +0 -370
- package/commands/log/core/search.js +0 -330
- package/commands/log/core/tail.js +0 -216
- package/commands/log/core/utils.js +0 -424
- package/commands/log.js +0 -298
- package/commands/sandbox/core/log-bridge.js +0 -119
- package/commands/sandbox/core/replay/analyzer.js +0 -311
- package/commands/sandbox/core/replay/batch-orchestrator.js +0 -536
- package/commands/sandbox/core/replay/batch-task.js +0 -369
- package/commands/sandbox/core/replay/concurrent-display.js +0 -70
- package/commands/sandbox/core/replay/concurrent-orchestrator.js +0 -170
- package/commands/sandbox/core/replay/data-source.js +0 -86
- package/commands/sandbox/core/replay/display.js +0 -231
- package/commands/sandbox/core/replay/executor.js +0 -634
- package/commands/sandbox/core/replay/history-fetcher.js +0 -124
- package/commands/sandbox/core/replay/index.js +0 -338
- package/commands/sandbox/core/replay/loghouse-data-source.js +0 -177
- package/commands/sandbox/core/replay/pid-mapping.js +0 -26
- package/commands/sandbox/core/replay/request.js +0 -109
- package/commands/sandbox/core/replay/worker.js +0 -166
- package/commands/sandbox/core/session.js +0 -346
- package/commands/sandbox/log-bridge.js +0 -2
- package/commands/sandbox/ray.js +0 -2
- package/commands/sandbox/replay/analyzer.js +0 -311
- package/commands/sandbox/replay/batch-orchestrator.js +0 -536
- package/commands/sandbox/replay/batch-task.js +0 -369
- package/commands/sandbox/replay/concurrent-display.js +0 -70
- package/commands/sandbox/replay/concurrent-orchestrator.js +0 -170
- package/commands/sandbox/replay/display.js +0 -231
- package/commands/sandbox/replay/executor.js +0 -634
- package/commands/sandbox/replay/history-fetcher.js +0 -118
- package/commands/sandbox/replay/index.js +0 -338
- package/commands/sandbox/replay/pid-mapping.js +0 -26
- package/commands/sandbox/replay/request.js +0 -109
- package/commands/sandbox/replay/worker.js +0 -166
- package/commands/sandbox/replay.js +0 -2
- package/commands/sandbox/session.js +0 -2
- package/commands/sandbox-original.js +0 -1393
- package/commands/sandbox.js +0 -499
- package/help/help.json +0 -1071
- package/help/middleware.js +0 -71
- package/help/renderer.js +0 -800
- package/lib/plugin-context.js +0 -40
- package/sdks/sandbox/core/client.js +0 -845
- package/sdks/sandbox/core/config.js +0 -70
- package/sdks/sandbox/core/types.js +0 -74
- package/sdks/sandbox/httpLogger.js +0 -251
- package/sdks/sandbox/index.js +0 -9
- package/utils/asciiArt.js +0 -138
- package/utils/bun-compat.js +0 -59
- package/utils/ciPipelines.js +0 -138
- package/utils/cli.js +0 -17
- package/utils/command-router.js +0 -79
- package/utils/configManager.js +0 -503
- package/utils/dependency-resolver.js +0 -135
- package/utils/eagleeye_traceid.js +0 -151
- package/utils/envDetector.js +0 -78
- package/utils/execution_logger.js +0 -415
- package/utils/featureManager.js +0 -68
- package/utils/firstTimeTip.js +0 -44
- package/utils/hook-manager.js +0 -125
- package/utils/http-logger.js +0 -264
- package/utils/i18n.js +0 -139
- package/utils/image-progress.js +0 -159
- package/utils/logger.js +0 -154
- package/utils/plugin-loader.js +0 -124
- package/utils/plugin-manager.js +0 -348
- package/utils/ray_cli_wrapper.js +0 -746
- package/utils/sandbox-client.js +0 -419
- package/utils/terminal.js +0 -32
- package/utils/tips.js +0 -106
package/utils/sandbox-client.js
DELETED
|
@@ -1,419 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sandbox Client Adapter
|
|
3
|
-
*
|
|
4
|
-
* 适配 rl-rock 的 Sandbox 类,提供与原 sdks/sandbox 兼容的 API
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { Sandbox: RlRockSandbox } = require('rl-rock');
|
|
8
|
-
const logger = require('./logger');
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* SandboxConfig 配置类(兼容旧 API)
|
|
12
|
-
*/
|
|
13
|
-
class SandboxConfig {
|
|
14
|
-
constructor(options = {}) {
|
|
15
|
-
this.image = options.image || 'python:3.11';
|
|
16
|
-
this.autoClearSeconds = options.autoClearSeconds || 300;
|
|
17
|
-
this.startupTimeout = options.startupTimeout || 120;
|
|
18
|
-
this.memory = options.memory || '8g';
|
|
19
|
-
this.cpus = options.cpus !== undefined ? options.cpus : 2.0;
|
|
20
|
-
this.baseUrl = options.baseUrl;
|
|
21
|
-
this.xrlAuthorization = options.xrlAuthorization;
|
|
22
|
-
this.extraHeaders = options.extraHeaders || {};
|
|
23
|
-
this.cluster = options.cluster || 'zb';
|
|
24
|
-
this.namespace = options.namespace || null;
|
|
25
|
-
this.userId = options.userId || null;
|
|
26
|
-
this.experimentId = options.experimentId || null;
|
|
27
|
-
this.waitForAlive = options.waitForAlive !== undefined ? options.waitForAlive : false;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
validate() {
|
|
31
|
-
if (!this.baseUrl) {
|
|
32
|
-
throw new Error('baseUrl is required');
|
|
33
|
-
}
|
|
34
|
-
if (!this.image) {
|
|
35
|
-
throw new Error('image is required');
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* SandboxClient - 适配 rl-rock 的 Sandbox 类
|
|
42
|
-
*
|
|
43
|
-
* 提供与原 sdks/sandbox/client.js 兼容的 API
|
|
44
|
-
*/
|
|
45
|
-
class SandboxClient {
|
|
46
|
-
constructor(config, options = {}) {
|
|
47
|
-
// 支持 SandboxConfig 实例或普通对象
|
|
48
|
-
if (config instanceof SandboxConfig) {
|
|
49
|
-
this._config = config;
|
|
50
|
-
} else {
|
|
51
|
-
this._config = new SandboxConfig(config);
|
|
52
|
-
}
|
|
53
|
-
this._config.validate();
|
|
54
|
-
|
|
55
|
-
// 创建 rl-rock Sandbox 实例(只传递非 null 的参数)
|
|
56
|
-
// 注意:rl-rock 不自动处理 xrlAuthorization,需要通过 extraHeaders 传递
|
|
57
|
-
const extraHeaders = {
|
|
58
|
-
...this._config.extraHeaders,
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// 将 xrlAuthorization 添加到 extraHeaders(rl-rock 要求通过 headers 传递)
|
|
62
|
-
if (this._config.xrlAuthorization) {
|
|
63
|
-
extraHeaders['XRL-Authorization'] = `Bearer ${this._config.xrlAuthorization}`;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const sandboxConfig = {
|
|
67
|
-
baseUrl: this._config.baseUrl,
|
|
68
|
-
image: this._config.image,
|
|
69
|
-
autoClearSeconds: this._config.autoClearSeconds,
|
|
70
|
-
startupTimeout: this._config.startupTimeout,
|
|
71
|
-
memory: this._config.memory,
|
|
72
|
-
cpus: this._config.cpus,
|
|
73
|
-
cluster: this._config.cluster,
|
|
74
|
-
extraHeaders,
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// 只添加非 null 的可选参数(rl-rock 会自动添加到 headers)
|
|
78
|
-
if (this._config.userId) {
|
|
79
|
-
sandboxConfig.userId = this._config.userId;
|
|
80
|
-
}
|
|
81
|
-
if (this._config.experimentId) {
|
|
82
|
-
sandboxConfig.experimentId = this._config.experimentId;
|
|
83
|
-
}
|
|
84
|
-
if (this._config.namespace) {
|
|
85
|
-
sandboxConfig.namespace = this._config.namespace;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
this._sandbox = new RlRockSandbox(sandboxConfig);
|
|
89
|
-
|
|
90
|
-
// 兼容旧 API:允许直接设置 sandboxId
|
|
91
|
-
this._sandboxId = null;
|
|
92
|
-
this._onLog = options.onLog || null;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* 兼容旧 API:直接设置 sandboxId
|
|
97
|
-
*/
|
|
98
|
-
set _sandboxId(value) {
|
|
99
|
-
this._sandbox.sandboxId = value;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
get _sandboxId() {
|
|
103
|
-
return this._sandbox.sandboxId;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
get sandboxId() {
|
|
107
|
-
return this._sandbox.getSandboxId();
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
get hostName() {
|
|
111
|
-
return this._sandbox.getHostName();
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
get hostIp() {
|
|
115
|
-
return this._sandbox.getHostIp();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* 公开的 config getter(用于测试和外部访问)
|
|
120
|
-
*/
|
|
121
|
-
get config() {
|
|
122
|
-
return this._config;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Log a message
|
|
127
|
-
*/
|
|
128
|
-
_log(level, message) {
|
|
129
|
-
if (this._onLog) {
|
|
130
|
-
this._onLog(level, message);
|
|
131
|
-
} else {
|
|
132
|
-
logger[level](message);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Start a new sandbox instance
|
|
138
|
-
*/
|
|
139
|
-
async start() {
|
|
140
|
-
await this._sandbox.start();
|
|
141
|
-
|
|
142
|
-
const sandboxId = this._sandbox.getSandboxId();
|
|
143
|
-
const hostName = this._sandbox.getHostName();
|
|
144
|
-
const hostIp = this._sandbox.getHostIp();
|
|
145
|
-
|
|
146
|
-
let isAlive = false;
|
|
147
|
-
if (this._config.waitForAlive) {
|
|
148
|
-
// Wait for sandbox to be alive
|
|
149
|
-
await this._waitForAlive();
|
|
150
|
-
isAlive = true;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return {
|
|
154
|
-
sandboxId,
|
|
155
|
-
hostName,
|
|
156
|
-
hostIp,
|
|
157
|
-
isAlive,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Wait for sandbox to be alive
|
|
163
|
-
*/
|
|
164
|
-
async _waitForAlive() {
|
|
165
|
-
const startTime = Date.now();
|
|
166
|
-
const timeout = this._config.startupTimeout * 1000;
|
|
167
|
-
const loadingChars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
168
|
-
let loadingIndex = 0;
|
|
169
|
-
let lastLogTime = 0;
|
|
170
|
-
|
|
171
|
-
this._log('info', 'Waiting for sandbox to be ready...');
|
|
172
|
-
|
|
173
|
-
while (Date.now() - startTime < timeout) {
|
|
174
|
-
const status = await this._sandbox.getStatus();
|
|
175
|
-
|
|
176
|
-
if (status.isAlive) {
|
|
177
|
-
this._log('info', `Sandbox is ready (ID: ${this._sandbox.getSandboxId()})`);
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const now = Date.now();
|
|
182
|
-
if (now - lastLogTime > 3000) {
|
|
183
|
-
loadingIndex = (loadingIndex + 1) % loadingChars.length;
|
|
184
|
-
const elapsed = Math.floor((now - startTime) / 1000);
|
|
185
|
-
this._log('info', `${loadingChars[loadingIndex]} Starting sandbox... (${elapsed}s elapsed)`);
|
|
186
|
-
lastLogTime = now;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
throw new Error(`Sandbox did not become alive within ${this._config.startupTimeout}s`);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Get sandbox status
|
|
197
|
-
*/
|
|
198
|
-
async getStatus() {
|
|
199
|
-
return this._sandbox.getStatus();
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Check if sandbox is alive
|
|
204
|
-
*/
|
|
205
|
-
async isAlive() {
|
|
206
|
-
try {
|
|
207
|
-
const status = await this.getStatus();
|
|
208
|
-
return {
|
|
209
|
-
isAlive: status.isAlive,
|
|
210
|
-
message: status.hostName || '',
|
|
211
|
-
};
|
|
212
|
-
} catch (error) {
|
|
213
|
-
logger.warn(`Failed to check isAlive: ${error.message}`);
|
|
214
|
-
throw new Error(`Failed to check isAlive: ${error.message}`);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Execute a command in the sandbox
|
|
220
|
-
*/
|
|
221
|
-
async execute(command) {
|
|
222
|
-
let commandArray;
|
|
223
|
-
if (Array.isArray(command)) {
|
|
224
|
-
commandArray = command;
|
|
225
|
-
} else {
|
|
226
|
-
commandArray = ['sh', '-c', command];
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const result = await this._sandbox.execute({ command: commandArray });
|
|
230
|
-
|
|
231
|
-
return {
|
|
232
|
-
stdout: result.stdout,
|
|
233
|
-
stderr: result.stderr,
|
|
234
|
-
exit_code: result.exitCode,
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Stop the sandbox
|
|
240
|
-
*/
|
|
241
|
-
async stop() {
|
|
242
|
-
if (!this._sandbox.sandboxId) {
|
|
243
|
-
logger.warn('No sandbox ID to stop');
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
await this._sandbox.stop();
|
|
247
|
-
logger.info(`Sandbox ${this._sandbox.sandboxId} stopped successfully`);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Create a bash session
|
|
252
|
-
*/
|
|
253
|
-
async createSession(options = {}) {
|
|
254
|
-
return this._sandbox.createSession({
|
|
255
|
-
session: options.session || 'default',
|
|
256
|
-
startupSource: options.startupSource || [],
|
|
257
|
-
envEnable: options.envEnable || false,
|
|
258
|
-
env: options.env,
|
|
259
|
-
remoteUser: options.remoteUser,
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Run a command in a session
|
|
265
|
-
*/
|
|
266
|
-
async runInSession(action) {
|
|
267
|
-
const result = await this._sandbox.arun(action.command, {
|
|
268
|
-
session: action.session || 'default',
|
|
269
|
-
mode: 'normal',
|
|
270
|
-
timeout: action.timeout,
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
return {
|
|
274
|
-
output: result.output || '',
|
|
275
|
-
exit_code: result.exitCode,
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Run a command asynchronously in a session using nohup
|
|
281
|
-
*/
|
|
282
|
-
async runInSessionAsync(action) {
|
|
283
|
-
const result = await this._sandbox.arun(action.command, {
|
|
284
|
-
session: action.session || 'default',
|
|
285
|
-
mode: 'nohup',
|
|
286
|
-
waitTimeout: (action.timeout || 3600000) / 1000,
|
|
287
|
-
waitInterval: (action.pollInterval || 5000) / 1000,
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
return {
|
|
291
|
-
output: result.output || '',
|
|
292
|
-
exit_code: result.exitCode || 0,
|
|
293
|
-
timeout: result.failureReason?.includes('timeout') || false,
|
|
294
|
-
warning: result.failureReason || null,
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Write content to a file
|
|
300
|
-
*/
|
|
301
|
-
async writeFile(content, path) {
|
|
302
|
-
const result = await this._sandbox.write_file({ content, path });
|
|
303
|
-
return {
|
|
304
|
-
success: result.success,
|
|
305
|
-
message: result.message,
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Read a file (with optional line range)
|
|
311
|
-
*/
|
|
312
|
-
async readFile(filePath, startLine, endLine) {
|
|
313
|
-
if (startLine && endLine) {
|
|
314
|
-
if (startLine < 1 || endLine < startLine) {
|
|
315
|
-
throw new Error(`Invalid line range: startLine=${startLine}, endLine=${endLine}`);
|
|
316
|
-
}
|
|
317
|
-
if (endLine - startLine > 1000) {
|
|
318
|
-
throw new Error(`Line range too large: ${endLine - startLine} lines (max 1000)`);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const command = `sed -n ${startLine},${endLine}p ${filePath}`;
|
|
322
|
-
const result = await this.execute(command);
|
|
323
|
-
|
|
324
|
-
if (result.exit_code !== 0) {
|
|
325
|
-
throw new Error(`Failed to read file ${filePath}: ${result.stderr}`);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
return { content: result.stdout };
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// No line range, use read_file directly
|
|
332
|
-
const result = await this._sandbox.read_file({ path: filePath });
|
|
333
|
-
return { content: result.content };
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Upload a file to the sandbox
|
|
338
|
-
*/
|
|
339
|
-
async uploadFile(localPath, targetPath) {
|
|
340
|
-
const result = await this._sandbox.uploadByPath(localPath, targetPath);
|
|
341
|
-
return result;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Download a file from the sandbox
|
|
346
|
-
*/
|
|
347
|
-
async downloadFile(filePath) {
|
|
348
|
-
const result = await this._sandbox.read_file({ path: filePath });
|
|
349
|
-
return result;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Close a session
|
|
354
|
-
*/
|
|
355
|
-
async closeSession(session = 'default') {
|
|
356
|
-
return this._sandbox.closeSession({ session });
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Close the sandbox
|
|
361
|
-
*/
|
|
362
|
-
async close() {
|
|
363
|
-
await this.stop();
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Make a generic HTTP request to the sandbox API
|
|
368
|
-
*/
|
|
369
|
-
async makeRequest(request) {
|
|
370
|
-
const { method, uri, requestBody, headers } = request;
|
|
371
|
-
const url = `${this._config.baseUrl}${uri}`;
|
|
372
|
-
|
|
373
|
-
const axios = require('axios');
|
|
374
|
-
const requestHeaders = {
|
|
375
|
-
'X-Cluster': this._config.cluster,
|
|
376
|
-
'Content-Type': 'application/json',
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
if (this._config.xrlAuthorization) {
|
|
380
|
-
requestHeaders['XRL-Authorization'] = `Bearer ${this._config.xrlAuthorization}`;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if (this._config.userId) {
|
|
384
|
-
requestHeaders['X-User-Id'] = this._config.userId;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (this._config.experimentId) {
|
|
388
|
-
requestHeaders['X-Experiment-Id'] = this._config.experimentId;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
if (this._config.extraHeaders) {
|
|
392
|
-
Object.assign(requestHeaders, this._config.extraHeaders);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
if (headers) {
|
|
396
|
-
Object.assign(requestHeaders, headers);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
const axiosConfig = {
|
|
400
|
-
method: method.toLowerCase(),
|
|
401
|
-
url,
|
|
402
|
-
headers: requestHeaders,
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
if (requestBody) {
|
|
406
|
-
axiosConfig.data = requestBody;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
const response = await axios(axiosConfig);
|
|
410
|
-
|
|
411
|
-
return {
|
|
412
|
-
data: response.data,
|
|
413
|
-
headers: response.headers,
|
|
414
|
-
status: response.status,
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
module.exports = { SandboxClient, SandboxConfig };
|
package/utils/terminal.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 终端工具函数
|
|
5
|
-
*
|
|
6
|
-
* 提供统一的终端宽度获取和分隔线创建功能
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* 获取终端宽度
|
|
11
|
-
* @param {number} [defaultWidth=80] - 默认宽度(当无法获取终端宽度时使用)
|
|
12
|
-
* @returns {number} 终端宽度
|
|
13
|
-
*/
|
|
14
|
-
function getTerminalWidth(defaultWidth = 80) {
|
|
15
|
-
return process.stdout.columns || defaultWidth;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* 创建分隔线
|
|
20
|
-
* @param {string} [char='─'] - 分隔线字符
|
|
21
|
-
* @param {number} [width] - 分隔线宽度(默认使用终端宽度)
|
|
22
|
-
* @returns {string} 分隔线字符串
|
|
23
|
-
*/
|
|
24
|
-
function createSeparator(char = '─', width = null) {
|
|
25
|
-
const w = width || getTerminalWidth();
|
|
26
|
-
return char.repeat(Math.max(1, w));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
module.exports = {
|
|
30
|
-
getTerminalWidth,
|
|
31
|
-
createSeparator,
|
|
32
|
-
};
|
package/utils/tips.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Tips module - Display helpful tips after command output
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const chalk = require('chalk');
|
|
8
|
-
const { CLI_NAME } = require('./cli');
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Format a tip with consistent styling
|
|
12
|
-
* @param {string} message - The tip message
|
|
13
|
-
* @param {string} command - Optional command to highlight
|
|
14
|
-
* @returns {string} Formatted tip string
|
|
15
|
-
*/
|
|
16
|
-
function formatTip(message, command = null) {
|
|
17
|
-
const prefix = chalk.dim('💡 Tip:');
|
|
18
|
-
if (command) {
|
|
19
|
-
return `${prefix} ${message}\n ${chalk.cyan(command)}`;
|
|
20
|
-
}
|
|
21
|
-
return `${prefix} ${message}`;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Format a "next step" suggestion
|
|
26
|
-
* @param {string} message - The suggestion message
|
|
27
|
-
* @param {string} command - The command to run
|
|
28
|
-
* @returns {string} Formatted next step string
|
|
29
|
-
*/
|
|
30
|
-
function formatNextStep(message, command) {
|
|
31
|
-
const prefix = chalk.dim('→');
|
|
32
|
-
return `${prefix} ${message}\n ${chalk.cyan(command)}`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Print a tip to console
|
|
37
|
-
* @param {string} message - The tip message
|
|
38
|
-
* @param {string} command - Optional command to highlight
|
|
39
|
-
*/
|
|
40
|
-
function printTip(message, command = null) {
|
|
41
|
-
console.log('');
|
|
42
|
-
console.log(formatTip(message, command));
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Print a "next step" suggestion to console
|
|
47
|
-
* @param {string} message - The suggestion message
|
|
48
|
-
* @param {string} command - The command to run
|
|
49
|
-
*/
|
|
50
|
-
function printNextStep(message, command) {
|
|
51
|
-
console.log('');
|
|
52
|
-
console.log(formatNextStep(message, command));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Print multiple tips in a box
|
|
57
|
-
* @param {Array<{message: string, command?: string}>} tips - Array of tips
|
|
58
|
-
*/
|
|
59
|
-
function printTipsBox(tips) {
|
|
60
|
-
console.log('');
|
|
61
|
-
console.log(chalk.dim('─'.repeat(60)));
|
|
62
|
-
for (const tip of tips) {
|
|
63
|
-
if (tip.command) {
|
|
64
|
-
console.log(` ${tip.message}`);
|
|
65
|
-
console.log(` ${chalk.cyan(tip.command)}`);
|
|
66
|
-
} else {
|
|
67
|
-
console.log(` ${tip.message}`);
|
|
68
|
-
}
|
|
69
|
-
console.log('');
|
|
70
|
-
}
|
|
71
|
-
console.log(chalk.dim('─'.repeat(60)));
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Common tips for various commands
|
|
76
|
-
*/
|
|
77
|
-
const TIPS = {
|
|
78
|
-
afterStart: (sandboxId) => ({
|
|
79
|
-
message: 'To connect to this sandbox:',
|
|
80
|
-
command: `${CLI_NAME} sandbox ${sandboxId} attach`,
|
|
81
|
-
}),
|
|
82
|
-
|
|
83
|
-
afterStop: () => ({
|
|
84
|
-
message: 'To start a new sandbox:',
|
|
85
|
-
command: `${CLI_NAME} start`,
|
|
86
|
-
}),
|
|
87
|
-
|
|
88
|
-
afterStatus: (sandboxId) => ({
|
|
89
|
-
message: 'To connect to this sandbox:',
|
|
90
|
-
command: `${CLI_NAME} sandbox ${sandboxId} attach`,
|
|
91
|
-
}),
|
|
92
|
-
|
|
93
|
-
afterList: () => ({
|
|
94
|
-
message: 'To connect to a sandbox:',
|
|
95
|
-
command: `${CLI_NAME} sandbox <sandbox-id> attach`,
|
|
96
|
-
}),
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
module.exports = {
|
|
100
|
-
formatTip,
|
|
101
|
-
formatNextStep,
|
|
102
|
-
printTip,
|
|
103
|
-
printNextStep,
|
|
104
|
-
printTipsBox,
|
|
105
|
-
TIPS,
|
|
106
|
-
};
|