rl-rockcli 0.0.10 → 0.0.12

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 (2) hide show
  1. package/commands/attach.js +186 -0
  2. package/package.json +5 -2
@@ -0,0 +1,186 @@
1
+ 'use strict';
2
+
3
+ const logger = require('../utils/logger');
4
+ const { resolveSandboxId } = require('./attach/sandbox-id-resolver');
5
+
6
+ /**
7
+ * Prompt user for sandbox ID using terminal-kit interactive UI
8
+ * @returns {Promise<string|null>} The sandbox ID entered by user, or null if cancelled
9
+ */
10
+ async function promptForSandboxId() {
11
+ const term = require('terminal-kit').terminal;
12
+
13
+ // Clear screen and show welcome
14
+ term.clear();
15
+ term.cyan('┌─────────────────────────────────────────────────────────────┐\n');
16
+ term.cyan('│ │\n');
17
+ term.cyan('│ ');
18
+ term.bold.white('🪨 ROCK CLI - Sandbox Attach');
19
+ term.cyan(' │\n');
20
+ term.cyan('│ │\n');
21
+ term.cyan('└─────────────────────────────────────────────────────────────┘\n');
22
+
23
+ // Check for recent sandboxes from history
24
+ const fs = require('fs');
25
+ const path = require('path');
26
+ const os = require('os');
27
+ const historyDir = path.join(os.homedir(), '.rock', 'history');
28
+ let recentSandboxes = [];
29
+
30
+ if (fs.existsSync(historyDir)) {
31
+ try {
32
+ const dirs = fs.readdirSync(historyDir)
33
+ .filter(d => fs.statSync(path.join(historyDir, d)).isDirectory() && d !== 'current')
34
+ .map(d => ({
35
+ name: d,
36
+ mtime: fs.statSync(path.join(historyDir, d)).mtimeMs,
37
+ }))
38
+ .sort((a, b) => b.mtime - a.mtime) // Sort by most recent first
39
+ .slice(0, 5)
40
+ .map(d => d.name);
41
+ recentSandboxes = dirs;
42
+ } catch (e) {
43
+ // Ignore errors reading history
44
+ }
45
+ }
46
+
47
+ // Show recent sandboxes if any
48
+ if (recentSandboxes.length > 0) {
49
+ term.gray(' 最近使用的 Sandbox:\n');
50
+ recentSandboxes.forEach((id, index) => {
51
+ term.gray(` ${index + 1}. `);
52
+ term.white(id);
53
+ term('\n');
54
+ });
55
+ term.gray(' 输入序号选择,或直接输入新的 Sandbox ID\n\n');
56
+ }
57
+
58
+ // Prompt for input
59
+ term.bold(' Sandbox ID: ');
60
+
61
+ return new Promise((resolve) => {
62
+ let resolved = false;
63
+ function finish(val) {
64
+ if (resolved) return;
65
+ resolved = true;
66
+ if (typeof term.removeListener === 'function') {
67
+ term.removeListener('key', onKey);
68
+ } else if (typeof term.off === 'function') {
69
+ term.off('key', onKey);
70
+ }
71
+ resolve(val);
72
+ }
73
+
74
+ function onKey(name) {
75
+ if (name === 'CTRL_C') {
76
+ term('\n\n');
77
+ term.gray(' 已取消\n');
78
+ finish(null);
79
+ }
80
+ }
81
+
82
+ term.on('key', onKey);
83
+
84
+ term.inputField({
85
+ cancelable: true,
86
+ history: recentSandboxes,
87
+ autoComplete: recentSandboxes,
88
+ autoCompleteHint: true,
89
+ autoCompleteMenu: recentSandboxes.length > 0,
90
+ }, (error, input) => {
91
+ term('\n');
92
+
93
+ if (error || input === undefined) {
94
+ term.gray(' 已取消\n');
95
+ finish(null);
96
+ return;
97
+ }
98
+
99
+ const trimmed = input.trim();
100
+
101
+ // Check if user entered a number to select from recent
102
+ if (recentSandboxes.length > 0 && /^[1-5]$/.test(trimmed)) {
103
+ const index = parseInt(trimmed) - 1;
104
+ if (index < recentSandboxes.length) {
105
+ finish(recentSandboxes[index]);
106
+ return;
107
+ }
108
+ }
109
+
110
+ finish(trimmed);
111
+ });
112
+ });
113
+ }
114
+
115
+ module.exports = {
116
+ command: 'attach [sandbox-id]',
117
+ describe: 'Connect to a sandbox in interactive REPL mode',
118
+ builder: (yargs) => {
119
+ return yargs
120
+ .positional('sandbox-id', {
121
+ describe: 'The sandbox ID to connect to (will prompt if not provided)',
122
+ type: 'string',
123
+ })
124
+ .option('verbose', {
125
+ alias: 'v',
126
+ type: 'count',
127
+ description: 'Verbosity level',
128
+ })
129
+ .option('api-key', {
130
+ describe: 'API key for authentication',
131
+ type: 'string',
132
+ })
133
+ .option('cluster', {
134
+ describe: 'Target cluster',
135
+ type: 'string',
136
+ })
137
+ .option('base-url', {
138
+ describe: 'Base URL for the API',
139
+ type: 'string',
140
+ })
141
+ .option('session', {
142
+ alias: 's',
143
+ describe: 'Resume a specific session by ID (preserves env vars, cwd, etc.)',
144
+ type: 'string',
145
+ })
146
+ .option('ui', {
147
+ describe: 'Attach UI mode: ink|basic|opentui (default: opentui; env: ROCK_ATTACH_UI)',
148
+ type: 'string',
149
+ choices: ['ink', 'basic', 'opentui'],
150
+ });
151
+ },
152
+ handler: async (argv) => {
153
+ const logger = require('../utils/logger');
154
+ logger.debug(`attach handler received argv.session=${argv.session}, argv.s=${argv.s}`);
155
+ logger.debug(`Full argv keys: ${Object.keys(argv).join(', ')}`);
156
+
157
+ let sandboxId = argv.sandboxId;
158
+
159
+ // If sandbox-id not provided, prompt interactively with terminal-kit UI
160
+ if (!sandboxId) {
161
+ sandboxId = await promptForSandboxId();
162
+ if (!sandboxId) {
163
+ process.exitCode = 0;
164
+ return;
165
+ }
166
+ }
167
+
168
+ // Resolve abbreviated sandbox ID to full ID
169
+ try {
170
+ sandboxId = await resolveSandboxId(sandboxId);
171
+ } catch (error) {
172
+ logger.error(error.message);
173
+ process.exitCode = 1;
174
+ return;
175
+ }
176
+
177
+ const { SandboxREPL } = require('./attach/repl');
178
+ const repl = new SandboxREPL(sandboxId, argv);
179
+ try {
180
+ await repl.start();
181
+ } catch (e) {
182
+ logger.error(e && e.message ? e.message : String(e));
183
+ process.exitCode = e && typeof e.exitCode === 'number' ? e.exitCode : 1;
184
+ }
185
+ },
186
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rl-rockcli",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "Open-source ROCK CLI - Sandbox and Log management tool",
5
5
  "bin": {
6
6
  "rockcli": "./index.js"
@@ -40,7 +40,10 @@
40
40
  "js-yaml": "^4.1.1",
41
41
  "chalk": "^4.1.2",
42
42
  "terminal-kit": "^3.0.1",
43
- "rl-rock": "^1.2.4"
43
+ "rl-rock": "^1.2.4",
44
+ "@opentui/solid": "^0.1.0",
45
+ "ink": "^4.4.1",
46
+ "react": "^18.2.0"
44
47
  },
45
48
  "publishConfig": {
46
49
  "registry": "https://registry.npmjs.org",