vibecodingmachine-cli 2026.3.9-850 → 2026.3.10-1547
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 +85 -85
- package/bin/commands/agent-commands.js +295 -28
- package/bin/vibecodingmachine.js +0 -0
- package/package.json +2 -2
- package/scripts/postinstall.js +161 -161
- package/src/commands/auth.js +100 -100
- package/src/commands/auto-execution.js +120 -32
- package/src/commands/auto-requirement-management.js +9 -9
- package/src/commands/auto-status-helpers.js +6 -12
- package/src/commands/computers.js +318 -318
- package/src/commands/feature.js +123 -123
- package/src/commands/locale.js +72 -72
- package/src/commands/repo.js +163 -163
- package/src/commands/setup.js +93 -93
- package/src/commands/sync.js +287 -287
- package/src/index.js +5 -5
- package/src/utils/agent-selector.js +50 -50
- package/src/utils/asset-cleanup.js +60 -60
- package/src/utils/auth.js +6 -0
- package/src/utils/auto-mode-ansi-ui.js +237 -237
- package/src/utils/auto-mode-simple-ui.js +141 -141
- package/src/utils/copy-with-progress.js +167 -167
- package/src/utils/download-with-progress.js +84 -84
- package/src/utils/keyboard-handler.js +153 -153
- package/src/utils/kiro-installer.js +178 -178
- package/src/utils/logger.js +4 -4
- package/src/utils/persistent-header.js +114 -114
- package/src/utils/prompt-helper.js +63 -63
- package/src/utils/provider-checker/agent-runner.js +110 -31
- package/src/utils/provider-checker/ide-manager.js +37 -8
- package/src/utils/provider-checker/provider-validator.js +50 -0
- package/src/utils/provider-checker/requirements-manager.js +21 -6
- package/src/utils/status-card.js +121 -121
- package/src/utils/stdout-interceptor.js +127 -127
- package/src/utils/trui-main-handlers.js +41 -8
- package/src/utils/trui-main-menu.js +10 -3
- package/src/utils/trui-nav-agents.js +23 -33
- package/src/utils/trui-navigation.js +2 -2
- package/src/utils/user-tracking.js +299 -299
|
@@ -1,318 +1,318 @@
|
|
|
1
|
-
const chalk = require('chalk');
|
|
2
|
-
const Table = require('cli-table3');
|
|
3
|
-
const SyncEngine = require('vibecodingmachine-core/src/sync/sync-engine');
|
|
4
|
-
const { t } = require('vibecodingmachine-core');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Display all registered computers with their status
|
|
8
|
-
*/
|
|
9
|
-
async function listComputers(options = {}) {
|
|
10
|
-
const syncEngine = new SyncEngine();
|
|
11
|
-
|
|
12
|
-
try {
|
|
13
|
-
await syncEngine.initialize();
|
|
14
|
-
|
|
15
|
-
// Fetch all computers from DynamoDB
|
|
16
|
-
const computers = await fetchAllComputers(syncEngine);
|
|
17
|
-
|
|
18
|
-
if (computers.length === 0) {
|
|
19
|
-
console.log(chalk.yellow(`\n${t('computers.no.computers')}\n`));
|
|
20
|
-
console.log(chalk.gray(`${t('computers.will.appear')}\n`));
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Filter by focus area if specified
|
|
25
|
-
let filteredComputers = computers;
|
|
26
|
-
if (options.focus) {
|
|
27
|
-
filteredComputers = computers.filter(c =>
|
|
28
|
-
c.focusArea && c.focusArea.toLowerCase().includes(options.focus.toLowerCase())
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Filter by status if specified
|
|
33
|
-
if (options.status) {
|
|
34
|
-
filteredComputers = filteredComputers.filter(c =>
|
|
35
|
-
c.status && c.status.toLowerCase() === options.status.toLowerCase()
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Create table
|
|
40
|
-
const table = new Table({
|
|
41
|
-
head: [
|
|
42
|
-
chalk.cyan(t('computers.table.computer')),
|
|
43
|
-
chalk.cyan(t('computers.table.focus')),
|
|
44
|
-
chalk.cyan(t('computers.table.status')),
|
|
45
|
-
chalk.cyan(t('computers.table.requirement')),
|
|
46
|
-
chalk.cyan(t('computers.table.progress')),
|
|
47
|
-
chalk.cyan(t('computers.table.last.sync'))
|
|
48
|
-
],
|
|
49
|
-
colWidths: [25, 30, 12, 35, 10, 15]
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Add rows
|
|
53
|
-
for (const computer of filteredComputers) {
|
|
54
|
-
const statusIcon = getStatusIcon(computer.status);
|
|
55
|
-
const statusKey = `computers.status.${computer.status || 'unknown'}`;
|
|
56
|
-
const statusText = `${statusIcon} ${t(statusKey)}`;
|
|
57
|
-
const progress = computer.progress ? `${computer.progress}%` : '-';
|
|
58
|
-
const lastSync = computer.lastSyncTime
|
|
59
|
-
? formatTimeSince(computer.lastSyncTime)
|
|
60
|
-
: t('computers.never');
|
|
61
|
-
|
|
62
|
-
table.push([
|
|
63
|
-
computer.hostname || computer.computerId,
|
|
64
|
-
computer.focusArea || '-',
|
|
65
|
-
statusText,
|
|
66
|
-
truncate(computer.currentRequirement || '-', 33),
|
|
67
|
-
progress,
|
|
68
|
-
lastSync
|
|
69
|
-
]);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
console.log('\n' + table.toString() + '\n');
|
|
73
|
-
|
|
74
|
-
// Show summary
|
|
75
|
-
const activeCount = computers.filter(c => c.status === 'active').length;
|
|
76
|
-
const idleCount = computers.filter(c => c.status === 'idle').length;
|
|
77
|
-
const errorCount = computers.filter(c => c.status === 'error').length;
|
|
78
|
-
|
|
79
|
-
console.log(chalk.gray(t('computers.total', { count: computers.length })));
|
|
80
|
-
console.log(chalk.green(` ${t('computers.active', { count: activeCount })}`));
|
|
81
|
-
console.log(chalk.yellow(` ${t('computers.idle', { count: idleCount })}`));
|
|
82
|
-
if (errorCount > 0) {
|
|
83
|
-
console.log(chalk.red(` ${t('computers.error', { count: errorCount })}`));
|
|
84
|
-
}
|
|
85
|
-
console.log('');
|
|
86
|
-
|
|
87
|
-
} catch (error) {
|
|
88
|
-
console.error(chalk.red(`\n✗ ${t('computers.fetch.failed')}`), error.message);
|
|
89
|
-
await errorReporter.reportError(error, {
|
|
90
|
-
command: 'listComputers',
|
|
91
|
-
focusArea,
|
|
92
|
-
status
|
|
93
|
-
});
|
|
94
|
-
throw error;
|
|
95
|
-
} finally {
|
|
96
|
-
syncEngine.stop();
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Show detailed status of a specific computer
|
|
102
|
-
*/
|
|
103
|
-
async function showComputerStatus(computerId) {
|
|
104
|
-
const syncEngine = new SyncEngine();
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
await syncEngine.initialize();
|
|
108
|
-
|
|
109
|
-
const computer = await fetchComputer(syncEngine, computerId);
|
|
110
|
-
|
|
111
|
-
if (!computer) {
|
|
112
|
-
console.log(chalk.red(`\n✗ ${t('computers.not.found', { id: computerId })}\n`));
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Display computer details
|
|
117
|
-
console.log('\n' + chalk.bold.cyan(t('computers.details.title')));
|
|
118
|
-
console.log(chalk.gray('─'.repeat(60)));
|
|
119
|
-
console.log(chalk.white(t('computers.details.hostname').padEnd(17)) + (computer.hostname || computer.computerId));
|
|
120
|
-
console.log(chalk.white(t('computers.details.id').padEnd(17)) + computer.computerId);
|
|
121
|
-
console.log(chalk.white(t('computers.details.focus').padEnd(17)) + (computer.focusArea || '-'));
|
|
122
|
-
console.log(chalk.white(t('computers.details.os').padEnd(17)) + (computer.os || '-'));
|
|
123
|
-
const statusKey = `computers.status.${computer.status || 'unknown'}`;
|
|
124
|
-
console.log(chalk.white(t('computers.details.status').padEnd(17)) + getStatusIcon(computer.status) + ' ' + t(statusKey));
|
|
125
|
-
console.log(chalk.white(t('computers.details.last.sync').padEnd(17)) + (computer.lastSyncTime ? formatTimeSince(computer.lastSyncTime) : t('computers.never')));
|
|
126
|
-
console.log('');
|
|
127
|
-
|
|
128
|
-
// Current work
|
|
129
|
-
console.log(chalk.bold.cyan(t('computers.work.title')));
|
|
130
|
-
console.log(chalk.gray('─'.repeat(60)));
|
|
131
|
-
console.log(chalk.white(t('computers.work.requirement').padEnd(17)) + (computer.currentRequirement || t('computers.work.none')));
|
|
132
|
-
console.log(chalk.white(t('computers.work.progress').padEnd(17)) + (computer.progress ? `${computer.progress}%` : '-'));
|
|
133
|
-
console.log(chalk.white(t('computers.work.time.active').padEnd(17)) + (computer.timeActive || '-'));
|
|
134
|
-
console.log(chalk.white(t('computers.work.ide').padEnd(17)) + (computer.ide || '-'));
|
|
135
|
-
console.log('');
|
|
136
|
-
|
|
137
|
-
// Statistics
|
|
138
|
-
if (computer.stats) {
|
|
139
|
-
console.log(chalk.bold.cyan(t('computers.stats.title')));
|
|
140
|
-
console.log(chalk.gray('─'.repeat(60)));
|
|
141
|
-
console.log(chalk.white(t('computers.stats.total').padEnd(21)) + (computer.stats.total || 0));
|
|
142
|
-
console.log(chalk.white(t('computers.stats.completed').padEnd(21)) + chalk.green(computer.stats.completed || 0));
|
|
143
|
-
console.log(chalk.white(t('computers.stats.in.progress').padEnd(21)) + chalk.yellow(computer.stats.inProgress || 0));
|
|
144
|
-
console.log(chalk.white(t('computers.stats.to.verify').padEnd(21)) + chalk.blue(computer.stats.toVerify || 0));
|
|
145
|
-
console.log(chalk.white(t('computers.stats.completion.rate').padEnd(21)) + (computer.stats.completionRate || '0%'));
|
|
146
|
-
console.log('');
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
} catch (error) {
|
|
150
|
-
console.error(chalk.red(`\n✗ ${t('computers.status.fetch.failed')}`), error.message);
|
|
151
|
-
throw error;
|
|
152
|
-
} finally {
|
|
153
|
-
syncEngine.stop();
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Register current computer with focus area
|
|
159
|
-
*/
|
|
160
|
-
async function registerComputer(focusArea) {
|
|
161
|
-
const syncEngine = new SyncEngine();
|
|
162
|
-
|
|
163
|
-
try {
|
|
164
|
-
await syncEngine.initialize();
|
|
165
|
-
|
|
166
|
-
const os = require('os');
|
|
167
|
-
const computerData = {
|
|
168
|
-
computerId: syncEngine.computerId,
|
|
169
|
-
hostname: os.hostname(),
|
|
170
|
-
focusArea: focusArea,
|
|
171
|
-
os: `${os.platform()} ${os.release()}`,
|
|
172
|
-
registeredAt: Date.now(),
|
|
173
|
-
lastSyncTime: Date.now(),
|
|
174
|
-
status: 'idle'
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
await saveComputer(syncEngine, computerData);
|
|
178
|
-
|
|
179
|
-
console.log(chalk.green(`\n✓ ${t('computers.register.success')}\n`));
|
|
180
|
-
console.log(chalk.white(t('computers.register.id').padEnd(14)) + computerData.computerId);
|
|
181
|
-
console.log(chalk.white(t('computers.register.hostname').padEnd(14)) + computerData.hostname);
|
|
182
|
-
console.log(chalk.white(t('computers.register.focus').padEnd(14)) + computerData.focusArea);
|
|
183
|
-
console.log('');
|
|
184
|
-
|
|
185
|
-
} catch (error) {
|
|
186
|
-
console.error(chalk.red(`\n✗ ${t('computers.register.failed')}`), error.message);
|
|
187
|
-
await errorReporter.reportError(error, {
|
|
188
|
-
command: 'registerComputer',
|
|
189
|
-
focusArea
|
|
190
|
-
});
|
|
191
|
-
throw error;
|
|
192
|
-
} finally {
|
|
193
|
-
syncEngine.stop();
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Update focus area for current computer
|
|
199
|
-
*/
|
|
200
|
-
async function updateFocus(newFocusArea) {
|
|
201
|
-
const syncEngine = new SyncEngine();
|
|
202
|
-
|
|
203
|
-
try {
|
|
204
|
-
await syncEngine.initialize();
|
|
205
|
-
|
|
206
|
-
const computer = await fetchComputer(syncEngine, syncEngine.computerId);
|
|
207
|
-
|
|
208
|
-
if (!computer) {
|
|
209
|
-
console.log(chalk.yellow(`\n⚠ ${t('computers.not.registered')}\n`));
|
|
210
|
-
await registerComputer(newFocusArea);
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
computer.focusArea = newFocusArea;
|
|
215
|
-
computer.lastSyncTime = Date.now();
|
|
216
|
-
|
|
217
|
-
await saveComputer(syncEngine, computer);
|
|
218
|
-
|
|
219
|
-
console.log(chalk.green(`\n✓ ${t('computers.focus.updated')}\n`));
|
|
220
|
-
console.log(chalk.white(t('computers.focus.computer').padEnd(14)) + computer.hostname);
|
|
221
|
-
console.log(chalk.white(t('computers.focus.new').padEnd(14)) + newFocusArea);
|
|
222
|
-
console.log('');
|
|
223
|
-
|
|
224
|
-
} catch (error) {
|
|
225
|
-
console.error(chalk.red(`\n✗ ${t('computers.focus.update.failed')}`), error.message);
|
|
226
|
-
throw error;
|
|
227
|
-
} finally {
|
|
228
|
-
syncEngine.stop();
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Helper functions
|
|
233
|
-
|
|
234
|
-
function getStatusIcon(status) {
|
|
235
|
-
switch (status) {
|
|
236
|
-
case 'active': return chalk.green('●');
|
|
237
|
-
case 'idle': return chalk.yellow('●');
|
|
238
|
-
case 'error': return chalk.red('●');
|
|
239
|
-
case 'offline': return chalk.gray('●');
|
|
240
|
-
default: return chalk.gray('○');
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function formatTimeSince(timestamp) {
|
|
245
|
-
const now = Date.now();
|
|
246
|
-
const diff = now - timestamp;
|
|
247
|
-
|
|
248
|
-
const seconds = Math.floor(diff / 1000);
|
|
249
|
-
const minutes = Math.floor(seconds / 60);
|
|
250
|
-
const hours = Math.floor(minutes / 60);
|
|
251
|
-
const days = Math.floor(hours / 24);
|
|
252
|
-
|
|
253
|
-
if (days > 0) return t('computers.time.days.ago', { count: days });
|
|
254
|
-
if (hours > 0) return t('computers.time.hours.ago', { count: hours });
|
|
255
|
-
if (minutes > 0) return t('computers.time.minutes.ago', { count: minutes });
|
|
256
|
-
return t('computers.time.seconds.ago', { count: seconds });
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function truncate(str, maxLength) {
|
|
260
|
-
if (str.length <= maxLength) return str;
|
|
261
|
-
return str.substring(0, maxLength - 3) + '...';
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
async function fetchAllComputers(syncEngine) {
|
|
265
|
-
const { ScanCommand } = require('@aws-sdk/lib-dynamodb');
|
|
266
|
-
|
|
267
|
-
const tableName = 'vibecodingmachine-computers';
|
|
268
|
-
|
|
269
|
-
try {
|
|
270
|
-
const command = new ScanCommand({
|
|
271
|
-
TableName: tableName
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
const response = await syncEngine.dynamoClient.send(command);
|
|
275
|
-
return response.Items || [];
|
|
276
|
-
} catch (error) {
|
|
277
|
-
// Table might not exist yet
|
|
278
|
-
return [];
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
async function fetchComputer(syncEngine, computerId) {
|
|
283
|
-
const { GetCommand } = require('@aws-sdk/lib-dynamodb');
|
|
284
|
-
|
|
285
|
-
const tableName = 'vibecodingmachine-computers';
|
|
286
|
-
|
|
287
|
-
try {
|
|
288
|
-
const command = new GetCommand({
|
|
289
|
-
TableName: tableName,
|
|
290
|
-
Key: { computerId }
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
const response = await syncEngine.dynamoClient.send(command);
|
|
294
|
-
return response.Item;
|
|
295
|
-
} catch (error) {
|
|
296
|
-
return null;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
async function saveComputer(syncEngine, computerData) {
|
|
301
|
-
const { PutCommand } = require('@aws-sdk/lib-dynamodb');
|
|
302
|
-
|
|
303
|
-
const tableName = 'vibecodingmachine-computers';
|
|
304
|
-
|
|
305
|
-
const command = new PutCommand({
|
|
306
|
-
TableName: tableName,
|
|
307
|
-
Item: computerData
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
await syncEngine.dynamoClient.send(command);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
module.exports = {
|
|
314
|
-
listComputers,
|
|
315
|
-
showComputerStatus,
|
|
316
|
-
registerComputer,
|
|
317
|
-
updateFocus
|
|
318
|
-
};
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const Table = require('cli-table3');
|
|
3
|
+
const SyncEngine = require('vibecodingmachine-core/src/sync/sync-engine');
|
|
4
|
+
const { t } = require('vibecodingmachine-core');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Display all registered computers with their status
|
|
8
|
+
*/
|
|
9
|
+
async function listComputers(options = {}) {
|
|
10
|
+
const syncEngine = new SyncEngine();
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
await syncEngine.initialize();
|
|
14
|
+
|
|
15
|
+
// Fetch all computers from DynamoDB
|
|
16
|
+
const computers = await fetchAllComputers(syncEngine);
|
|
17
|
+
|
|
18
|
+
if (computers.length === 0) {
|
|
19
|
+
console.log(chalk.yellow(`\n${t('computers.no.computers')}\n`));
|
|
20
|
+
console.log(chalk.gray(`${t('computers.will.appear')}\n`));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Filter by focus area if specified
|
|
25
|
+
let filteredComputers = computers;
|
|
26
|
+
if (options.focus) {
|
|
27
|
+
filteredComputers = computers.filter(c =>
|
|
28
|
+
c.focusArea && c.focusArea.toLowerCase().includes(options.focus.toLowerCase())
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Filter by status if specified
|
|
33
|
+
if (options.status) {
|
|
34
|
+
filteredComputers = filteredComputers.filter(c =>
|
|
35
|
+
c.status && c.status.toLowerCase() === options.status.toLowerCase()
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Create table
|
|
40
|
+
const table = new Table({
|
|
41
|
+
head: [
|
|
42
|
+
chalk.cyan(t('computers.table.computer')),
|
|
43
|
+
chalk.cyan(t('computers.table.focus')),
|
|
44
|
+
chalk.cyan(t('computers.table.status')),
|
|
45
|
+
chalk.cyan(t('computers.table.requirement')),
|
|
46
|
+
chalk.cyan(t('computers.table.progress')),
|
|
47
|
+
chalk.cyan(t('computers.table.last.sync'))
|
|
48
|
+
],
|
|
49
|
+
colWidths: [25, 30, 12, 35, 10, 15]
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Add rows
|
|
53
|
+
for (const computer of filteredComputers) {
|
|
54
|
+
const statusIcon = getStatusIcon(computer.status);
|
|
55
|
+
const statusKey = `computers.status.${computer.status || 'unknown'}`;
|
|
56
|
+
const statusText = `${statusIcon} ${t(statusKey)}`;
|
|
57
|
+
const progress = computer.progress ? `${computer.progress}%` : '-';
|
|
58
|
+
const lastSync = computer.lastSyncTime
|
|
59
|
+
? formatTimeSince(computer.lastSyncTime)
|
|
60
|
+
: t('computers.never');
|
|
61
|
+
|
|
62
|
+
table.push([
|
|
63
|
+
computer.hostname || computer.computerId,
|
|
64
|
+
computer.focusArea || '-',
|
|
65
|
+
statusText,
|
|
66
|
+
truncate(computer.currentRequirement || '-', 33),
|
|
67
|
+
progress,
|
|
68
|
+
lastSync
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log('\n' + table.toString() + '\n');
|
|
73
|
+
|
|
74
|
+
// Show summary
|
|
75
|
+
const activeCount = computers.filter(c => c.status === 'active').length;
|
|
76
|
+
const idleCount = computers.filter(c => c.status === 'idle').length;
|
|
77
|
+
const errorCount = computers.filter(c => c.status === 'error').length;
|
|
78
|
+
|
|
79
|
+
console.log(chalk.gray(t('computers.total', { count: computers.length })));
|
|
80
|
+
console.log(chalk.green(` ${t('computers.active', { count: activeCount })}`));
|
|
81
|
+
console.log(chalk.yellow(` ${t('computers.idle', { count: idleCount })}`));
|
|
82
|
+
if (errorCount > 0) {
|
|
83
|
+
console.log(chalk.red(` ${t('computers.error', { count: errorCount })}`));
|
|
84
|
+
}
|
|
85
|
+
console.log('');
|
|
86
|
+
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error(chalk.red(`\n✗ ${t('computers.fetch.failed')}`), error.message);
|
|
89
|
+
await errorReporter.reportError(error, {
|
|
90
|
+
command: 'listComputers',
|
|
91
|
+
focusArea,
|
|
92
|
+
status
|
|
93
|
+
});
|
|
94
|
+
throw error;
|
|
95
|
+
} finally {
|
|
96
|
+
syncEngine.stop();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Show detailed status of a specific computer
|
|
102
|
+
*/
|
|
103
|
+
async function showComputerStatus(computerId) {
|
|
104
|
+
const syncEngine = new SyncEngine();
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
await syncEngine.initialize();
|
|
108
|
+
|
|
109
|
+
const computer = await fetchComputer(syncEngine, computerId);
|
|
110
|
+
|
|
111
|
+
if (!computer) {
|
|
112
|
+
console.log(chalk.red(`\n✗ ${t('computers.not.found', { id: computerId })}\n`));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Display computer details
|
|
117
|
+
console.log('\n' + chalk.bold.cyan(t('computers.details.title')));
|
|
118
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
119
|
+
console.log(chalk.white(t('computers.details.hostname').padEnd(17)) + (computer.hostname || computer.computerId));
|
|
120
|
+
console.log(chalk.white(t('computers.details.id').padEnd(17)) + computer.computerId);
|
|
121
|
+
console.log(chalk.white(t('computers.details.focus').padEnd(17)) + (computer.focusArea || '-'));
|
|
122
|
+
console.log(chalk.white(t('computers.details.os').padEnd(17)) + (computer.os || '-'));
|
|
123
|
+
const statusKey = `computers.status.${computer.status || 'unknown'}`;
|
|
124
|
+
console.log(chalk.white(t('computers.details.status').padEnd(17)) + getStatusIcon(computer.status) + ' ' + t(statusKey));
|
|
125
|
+
console.log(chalk.white(t('computers.details.last.sync').padEnd(17)) + (computer.lastSyncTime ? formatTimeSince(computer.lastSyncTime) : t('computers.never')));
|
|
126
|
+
console.log('');
|
|
127
|
+
|
|
128
|
+
// Current work
|
|
129
|
+
console.log(chalk.bold.cyan(t('computers.work.title')));
|
|
130
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
131
|
+
console.log(chalk.white(t('computers.work.requirement').padEnd(17)) + (computer.currentRequirement || t('computers.work.none')));
|
|
132
|
+
console.log(chalk.white(t('computers.work.progress').padEnd(17)) + (computer.progress ? `${computer.progress}%` : '-'));
|
|
133
|
+
console.log(chalk.white(t('computers.work.time.active').padEnd(17)) + (computer.timeActive || '-'));
|
|
134
|
+
console.log(chalk.white(t('computers.work.ide').padEnd(17)) + (computer.ide || '-'));
|
|
135
|
+
console.log('');
|
|
136
|
+
|
|
137
|
+
// Statistics
|
|
138
|
+
if (computer.stats) {
|
|
139
|
+
console.log(chalk.bold.cyan(t('computers.stats.title')));
|
|
140
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
141
|
+
console.log(chalk.white(t('computers.stats.total').padEnd(21)) + (computer.stats.total || 0));
|
|
142
|
+
console.log(chalk.white(t('computers.stats.completed').padEnd(21)) + chalk.green(computer.stats.completed || 0));
|
|
143
|
+
console.log(chalk.white(t('computers.stats.in.progress').padEnd(21)) + chalk.yellow(computer.stats.inProgress || 0));
|
|
144
|
+
console.log(chalk.white(t('computers.stats.to.verify').padEnd(21)) + chalk.blue(computer.stats.toVerify || 0));
|
|
145
|
+
console.log(chalk.white(t('computers.stats.completion.rate').padEnd(21)) + (computer.stats.completionRate || '0%'));
|
|
146
|
+
console.log('');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error(chalk.red(`\n✗ ${t('computers.status.fetch.failed')}`), error.message);
|
|
151
|
+
throw error;
|
|
152
|
+
} finally {
|
|
153
|
+
syncEngine.stop();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Register current computer with focus area
|
|
159
|
+
*/
|
|
160
|
+
async function registerComputer(focusArea) {
|
|
161
|
+
const syncEngine = new SyncEngine();
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
await syncEngine.initialize();
|
|
165
|
+
|
|
166
|
+
const os = require('os');
|
|
167
|
+
const computerData = {
|
|
168
|
+
computerId: syncEngine.computerId,
|
|
169
|
+
hostname: os.hostname(),
|
|
170
|
+
focusArea: focusArea,
|
|
171
|
+
os: `${os.platform()} ${os.release()}`,
|
|
172
|
+
registeredAt: Date.now(),
|
|
173
|
+
lastSyncTime: Date.now(),
|
|
174
|
+
status: 'idle'
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
await saveComputer(syncEngine, computerData);
|
|
178
|
+
|
|
179
|
+
console.log(chalk.green(`\n✓ ${t('computers.register.success')}\n`));
|
|
180
|
+
console.log(chalk.white(t('computers.register.id').padEnd(14)) + computerData.computerId);
|
|
181
|
+
console.log(chalk.white(t('computers.register.hostname').padEnd(14)) + computerData.hostname);
|
|
182
|
+
console.log(chalk.white(t('computers.register.focus').padEnd(14)) + computerData.focusArea);
|
|
183
|
+
console.log('');
|
|
184
|
+
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.error(chalk.red(`\n✗ ${t('computers.register.failed')}`), error.message);
|
|
187
|
+
await errorReporter.reportError(error, {
|
|
188
|
+
command: 'registerComputer',
|
|
189
|
+
focusArea
|
|
190
|
+
});
|
|
191
|
+
throw error;
|
|
192
|
+
} finally {
|
|
193
|
+
syncEngine.stop();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Update focus area for current computer
|
|
199
|
+
*/
|
|
200
|
+
async function updateFocus(newFocusArea) {
|
|
201
|
+
const syncEngine = new SyncEngine();
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
await syncEngine.initialize();
|
|
205
|
+
|
|
206
|
+
const computer = await fetchComputer(syncEngine, syncEngine.computerId);
|
|
207
|
+
|
|
208
|
+
if (!computer) {
|
|
209
|
+
console.log(chalk.yellow(`\n⚠ ${t('computers.not.registered')}\n`));
|
|
210
|
+
await registerComputer(newFocusArea);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
computer.focusArea = newFocusArea;
|
|
215
|
+
computer.lastSyncTime = Date.now();
|
|
216
|
+
|
|
217
|
+
await saveComputer(syncEngine, computer);
|
|
218
|
+
|
|
219
|
+
console.log(chalk.green(`\n✓ ${t('computers.focus.updated')}\n`));
|
|
220
|
+
console.log(chalk.white(t('computers.focus.computer').padEnd(14)) + computer.hostname);
|
|
221
|
+
console.log(chalk.white(t('computers.focus.new').padEnd(14)) + newFocusArea);
|
|
222
|
+
console.log('');
|
|
223
|
+
|
|
224
|
+
} catch (error) {
|
|
225
|
+
console.error(chalk.red(`\n✗ ${t('computers.focus.update.failed')}`), error.message);
|
|
226
|
+
throw error;
|
|
227
|
+
} finally {
|
|
228
|
+
syncEngine.stop();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Helper functions
|
|
233
|
+
|
|
234
|
+
function getStatusIcon(status) {
|
|
235
|
+
switch (status) {
|
|
236
|
+
case 'active': return chalk.green('●');
|
|
237
|
+
case 'idle': return chalk.yellow('●');
|
|
238
|
+
case 'error': return chalk.red('●');
|
|
239
|
+
case 'offline': return chalk.gray('●');
|
|
240
|
+
default: return chalk.gray('○');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function formatTimeSince(timestamp) {
|
|
245
|
+
const now = Date.now();
|
|
246
|
+
const diff = now - timestamp;
|
|
247
|
+
|
|
248
|
+
const seconds = Math.floor(diff / 1000);
|
|
249
|
+
const minutes = Math.floor(seconds / 60);
|
|
250
|
+
const hours = Math.floor(minutes / 60);
|
|
251
|
+
const days = Math.floor(hours / 24);
|
|
252
|
+
|
|
253
|
+
if (days > 0) return t('computers.time.days.ago', { count: days });
|
|
254
|
+
if (hours > 0) return t('computers.time.hours.ago', { count: hours });
|
|
255
|
+
if (minutes > 0) return t('computers.time.minutes.ago', { count: minutes });
|
|
256
|
+
return t('computers.time.seconds.ago', { count: seconds });
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function truncate(str, maxLength) {
|
|
260
|
+
if (str.length <= maxLength) return str;
|
|
261
|
+
return str.substring(0, maxLength - 3) + '...';
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function fetchAllComputers(syncEngine) {
|
|
265
|
+
const { ScanCommand } = require('@aws-sdk/lib-dynamodb');
|
|
266
|
+
|
|
267
|
+
const tableName = 'vibecodingmachine-computers';
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
const command = new ScanCommand({
|
|
271
|
+
TableName: tableName
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const response = await syncEngine.dynamoClient.send(command);
|
|
275
|
+
return response.Items || [];
|
|
276
|
+
} catch (error) {
|
|
277
|
+
// Table might not exist yet
|
|
278
|
+
return [];
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function fetchComputer(syncEngine, computerId) {
|
|
283
|
+
const { GetCommand } = require('@aws-sdk/lib-dynamodb');
|
|
284
|
+
|
|
285
|
+
const tableName = 'vibecodingmachine-computers';
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
const command = new GetCommand({
|
|
289
|
+
TableName: tableName,
|
|
290
|
+
Key: { computerId }
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const response = await syncEngine.dynamoClient.send(command);
|
|
294
|
+
return response.Item;
|
|
295
|
+
} catch (error) {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async function saveComputer(syncEngine, computerData) {
|
|
301
|
+
const { PutCommand } = require('@aws-sdk/lib-dynamodb');
|
|
302
|
+
|
|
303
|
+
const tableName = 'vibecodingmachine-computers';
|
|
304
|
+
|
|
305
|
+
const command = new PutCommand({
|
|
306
|
+
TableName: tableName,
|
|
307
|
+
Item: computerData
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
await syncEngine.dynamoClient.send(command);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
module.exports = {
|
|
314
|
+
listComputers,
|
|
315
|
+
showComputerStatus,
|
|
316
|
+
registerComputer,
|
|
317
|
+
updateFocus
|
|
318
|
+
};
|