agentuity-vscode 0.1.6 → 0.1.7
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/package.json +475 -2
- package/src/core/cliClient.ts +477 -0
- package/src/core/index.ts +1 -0
- package/src/core/readonlyDocument.ts +11 -0
- package/src/core/sandboxManager.ts +483 -0
- package/src/extension.ts +14 -0
- package/src/features/chat/agentTools.ts +254 -1
- package/src/features/sandboxExplorer/index.ts +1375 -0
- package/src/features/sandboxExplorer/sandboxTreeData.ts +803 -0
- package/src/features/sandboxExplorer/statusBar.ts +383 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import * as vscode from 'vscode';
|
|
2
|
+
import { getSandboxManager, formatBytes } from '../../core/sandboxManager';
|
|
3
|
+
|
|
4
|
+
let statusBarItem: vscode.StatusBarItem | undefined;
|
|
5
|
+
let syncStatusItem: vscode.StatusBarItem | undefined;
|
|
6
|
+
let hideTimeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
7
|
+
|
|
8
|
+
export function createSandboxStatusBar(context: vscode.ExtensionContext): void {
|
|
9
|
+
// Main sandbox status bar item
|
|
10
|
+
statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 50);
|
|
11
|
+
statusBarItem.command = 'agentuity.sandbox.showQuickPick';
|
|
12
|
+
context.subscriptions.push(statusBarItem);
|
|
13
|
+
|
|
14
|
+
// Sync status bar item (shown during sync operations)
|
|
15
|
+
syncStatusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 49);
|
|
16
|
+
syncStatusItem.hide();
|
|
17
|
+
context.subscriptions.push(syncStatusItem);
|
|
18
|
+
|
|
19
|
+
// Register quick pick command
|
|
20
|
+
context.subscriptions.push(
|
|
21
|
+
vscode.commands.registerCommand('agentuity.sandbox.showQuickPick', showSandboxQuickPick)
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
// Update status bar when sandbox manager changes
|
|
25
|
+
updateStatusBar();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function updateStatusBar(): void {
|
|
29
|
+
if (!statusBarItem) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const manager = getSandboxManager();
|
|
34
|
+
const linked = manager.getLinkedSandboxes();
|
|
35
|
+
|
|
36
|
+
if (linked.length === 0) {
|
|
37
|
+
statusBarItem.text = '$(vm) No Sandbox';
|
|
38
|
+
statusBarItem.tooltip = 'Click to link a sandbox';
|
|
39
|
+
statusBarItem.backgroundColor = undefined;
|
|
40
|
+
} else if (linked.length === 1) {
|
|
41
|
+
const sandbox = linked[0];
|
|
42
|
+
const name = sandbox.name || sandbox.sandboxId.slice(0, 8);
|
|
43
|
+
statusBarItem.text = `$(vm) ${name}`;
|
|
44
|
+
statusBarItem.tooltip = `Sandbox: ${sandbox.sandboxId}\nLinked: ${new Date(sandbox.linkedAt).toLocaleDateString()}\nLast sync: ${sandbox.lastSyncedAt ? new Date(sandbox.lastSyncedAt).toLocaleString() : 'Never'}\n\nClick for sandbox options`;
|
|
45
|
+
statusBarItem.backgroundColor = undefined;
|
|
46
|
+
} else {
|
|
47
|
+
statusBarItem.text = `$(vm) ${linked.length} Sandboxes`;
|
|
48
|
+
statusBarItem.tooltip = `${linked.length} sandboxes linked\n\nClick for sandbox options`;
|
|
49
|
+
statusBarItem.backgroundColor = undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
statusBarItem.show();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function showSyncProgress(message: string): void {
|
|
56
|
+
if (!syncStatusItem) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
syncStatusItem.text = `$(sync~spin) ${message}`;
|
|
61
|
+
syncStatusItem.tooltip = message;
|
|
62
|
+
syncStatusItem.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground');
|
|
63
|
+
syncStatusItem.show();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function hideSyncProgress(): void {
|
|
67
|
+
if (syncStatusItem) {
|
|
68
|
+
syncStatusItem.hide();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function showSyncSuccess(filesUploaded: number, bytesTransferred: number): void {
|
|
73
|
+
if (!syncStatusItem) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const sizeStr = formatBytes(bytesTransferred);
|
|
78
|
+
syncStatusItem.text = `$(check) Synced ${filesUploaded} files (${sizeStr})`;
|
|
79
|
+
syncStatusItem.tooltip = `Successfully synced ${filesUploaded} files (${sizeStr})`;
|
|
80
|
+
syncStatusItem.backgroundColor = undefined;
|
|
81
|
+
syncStatusItem.show();
|
|
82
|
+
|
|
83
|
+
// Hide after 3 seconds
|
|
84
|
+
clearHideTimeout();
|
|
85
|
+
hideTimeoutId = setTimeout(() => {
|
|
86
|
+
hideSyncProgress();
|
|
87
|
+
}, 3000);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function showSyncError(error: string): void {
|
|
91
|
+
if (!syncStatusItem) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
syncStatusItem.text = '$(error) Sync failed';
|
|
96
|
+
syncStatusItem.tooltip = `Sync failed: ${error}`;
|
|
97
|
+
syncStatusItem.backgroundColor = new vscode.ThemeColor('statusBarItem.errorBackground');
|
|
98
|
+
syncStatusItem.show();
|
|
99
|
+
|
|
100
|
+
// Hide after 5 seconds
|
|
101
|
+
clearHideTimeout();
|
|
102
|
+
hideTimeoutId = setTimeout(() => {
|
|
103
|
+
hideSyncProgress();
|
|
104
|
+
}, 5000);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function showSandboxQuickPick(): Promise<void> {
|
|
108
|
+
const manager = getSandboxManager();
|
|
109
|
+
const linked = manager.getLinkedSandboxes();
|
|
110
|
+
|
|
111
|
+
interface SandboxQuickPickItem extends vscode.QuickPickItem {
|
|
112
|
+
action: string;
|
|
113
|
+
sandboxId?: string;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const items: SandboxQuickPickItem[] = [];
|
|
117
|
+
|
|
118
|
+
// Add linked sandboxes with actions
|
|
119
|
+
if (linked.length > 0) {
|
|
120
|
+
items.push({
|
|
121
|
+
label: 'Linked Sandboxes',
|
|
122
|
+
kind: vscode.QuickPickItemKind.Separator,
|
|
123
|
+
action: '',
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
for (const sandbox of linked) {
|
|
127
|
+
const name = sandbox.name || sandbox.sandboxId.slice(0, 8);
|
|
128
|
+
items.push({
|
|
129
|
+
label: `$(vm) ${name}`,
|
|
130
|
+
description: sandbox.sandboxId,
|
|
131
|
+
detail: sandbox.lastSyncedAt
|
|
132
|
+
? `Last synced: ${new Date(sandbox.lastSyncedAt).toLocaleString()}`
|
|
133
|
+
: 'Never synced',
|
|
134
|
+
action: 'select',
|
|
135
|
+
sandboxId: sandbox.sandboxId,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
items.push({
|
|
140
|
+
label: 'Actions',
|
|
141
|
+
kind: vscode.QuickPickItemKind.Separator,
|
|
142
|
+
action: '',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Add actions
|
|
147
|
+
items.push({
|
|
148
|
+
label: '$(add) Create New Sandbox',
|
|
149
|
+
description: 'Create a new sandbox environment',
|
|
150
|
+
action: 'create',
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
items.push({
|
|
154
|
+
label: '$(link) Link Existing Sandbox',
|
|
155
|
+
description: 'Link an existing sandbox to this workspace',
|
|
156
|
+
action: 'link',
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
if (linked.length > 0) {
|
|
160
|
+
items.push({
|
|
161
|
+
label: '$(sync) Sync to Sandbox',
|
|
162
|
+
description: 'Sync workspace files to a linked sandbox',
|
|
163
|
+
action: 'sync',
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
items.push({
|
|
167
|
+
label: '$(terminal) Execute in Sandbox',
|
|
168
|
+
description: 'Run a command in a sandbox',
|
|
169
|
+
action: 'exec',
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
items.push({
|
|
173
|
+
label: '$(eye) View in Explorer',
|
|
174
|
+
description: 'Open the Sandbox Explorer panel',
|
|
175
|
+
action: 'explorer',
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const selected = await vscode.window.showQuickPick(items, {
|
|
180
|
+
placeHolder: 'Select a sandbox or action',
|
|
181
|
+
title: 'Agentuity Sandboxes',
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
if (!selected) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
switch (selected.action) {
|
|
189
|
+
case 'select':
|
|
190
|
+
if (selected.sandboxId) {
|
|
191
|
+
await showSandboxActions(selected.sandboxId);
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
case 'create':
|
|
195
|
+
await vscode.commands.executeCommand('agentuity.sandbox.create');
|
|
196
|
+
break;
|
|
197
|
+
case 'link':
|
|
198
|
+
await vscode.commands.executeCommand('agentuity.sandbox.link');
|
|
199
|
+
break;
|
|
200
|
+
case 'sync':
|
|
201
|
+
await promptAndSync();
|
|
202
|
+
break;
|
|
203
|
+
case 'exec':
|
|
204
|
+
await promptAndExec();
|
|
205
|
+
break;
|
|
206
|
+
case 'explorer':
|
|
207
|
+
await vscode.commands.executeCommand('agentuity.sandboxes.focus');
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function showSandboxActions(sandboxId: string): Promise<void> {
|
|
213
|
+
const manager = getSandboxManager();
|
|
214
|
+
const linked = manager.getLinkedSandboxes();
|
|
215
|
+
const sandbox = linked.find((s) => s.sandboxId === sandboxId);
|
|
216
|
+
|
|
217
|
+
if (!sandbox) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const name = sandbox.name || sandbox.sandboxId.slice(0, 8);
|
|
222
|
+
|
|
223
|
+
interface ActionQuickPickItem extends vscode.QuickPickItem {
|
|
224
|
+
action: string;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const items: ActionQuickPickItem[] = [
|
|
228
|
+
{
|
|
229
|
+
label: '$(sync) Sync Files',
|
|
230
|
+
description: 'Sync workspace files to this sandbox',
|
|
231
|
+
action: 'sync',
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
label: '$(terminal) Execute Command',
|
|
235
|
+
description: 'Run a command in this sandbox',
|
|
236
|
+
action: 'exec',
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
label: '$(save) Create Snapshot',
|
|
240
|
+
description: 'Save current sandbox state',
|
|
241
|
+
action: 'snapshot',
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
label: '$(folder-opened) Browse Files',
|
|
245
|
+
description: 'View files in this sandbox',
|
|
246
|
+
action: 'browse',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
label: '$(link-external) Unlink',
|
|
250
|
+
description: 'Remove this sandbox from workspace',
|
|
251
|
+
action: 'unlink',
|
|
252
|
+
},
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
const selected = await vscode.window.showQuickPick(items, {
|
|
256
|
+
placeHolder: `Actions for ${name}`,
|
|
257
|
+
title: `Sandbox: ${name}`,
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
if (!selected) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
switch (selected.action) {
|
|
265
|
+
case 'sync':
|
|
266
|
+
await vscode.commands.executeCommand('agentuity.sandbox.sync', { sandboxId });
|
|
267
|
+
break;
|
|
268
|
+
case 'exec':
|
|
269
|
+
await promptAndExecForSandbox(sandboxId);
|
|
270
|
+
break;
|
|
271
|
+
case 'snapshot':
|
|
272
|
+
await vscode.commands.executeCommand('agentuity.sandbox.snapshot.create', { sandboxId });
|
|
273
|
+
break;
|
|
274
|
+
case 'browse':
|
|
275
|
+
await vscode.commands.executeCommand('agentuity.sandboxes.focus');
|
|
276
|
+
break;
|
|
277
|
+
case 'unlink':
|
|
278
|
+
await vscode.commands.executeCommand('agentuity.sandbox.unlink', { sandboxId });
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function promptAndSync(): Promise<void> {
|
|
284
|
+
const manager = getSandboxManager();
|
|
285
|
+
const linked = manager.getLinkedSandboxes();
|
|
286
|
+
|
|
287
|
+
if (linked.length === 0) {
|
|
288
|
+
vscode.window.showWarningMessage('No sandboxes linked. Link a sandbox first.');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
let sandboxId: string;
|
|
293
|
+
|
|
294
|
+
if (linked.length === 1) {
|
|
295
|
+
sandboxId = linked[0].sandboxId;
|
|
296
|
+
} else {
|
|
297
|
+
// Let user pick which sandbox to sync to
|
|
298
|
+
const items = linked.map((s) => ({
|
|
299
|
+
label: s.name || s.sandboxId.slice(0, 8),
|
|
300
|
+
description: s.sandboxId,
|
|
301
|
+
sandboxId: s.sandboxId,
|
|
302
|
+
}));
|
|
303
|
+
|
|
304
|
+
const selected = await vscode.window.showQuickPick(items, {
|
|
305
|
+
placeHolder: 'Select sandbox to sync to',
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
if (!selected) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
sandboxId = selected.sandboxId;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
await vscode.commands.executeCommand('agentuity.sandbox.sync', { sandboxId });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async function promptAndExec(): Promise<void> {
|
|
319
|
+
const manager = getSandboxManager();
|
|
320
|
+
const linked = manager.getLinkedSandboxes();
|
|
321
|
+
|
|
322
|
+
if (linked.length === 0) {
|
|
323
|
+
vscode.window.showWarningMessage('No sandboxes linked. Link a sandbox first.');
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
let sandboxId: string;
|
|
328
|
+
|
|
329
|
+
if (linked.length === 1) {
|
|
330
|
+
sandboxId = linked[0].sandboxId;
|
|
331
|
+
} else {
|
|
332
|
+
const items = linked.map((s) => ({
|
|
333
|
+
label: s.name || s.sandboxId.slice(0, 8),
|
|
334
|
+
description: s.sandboxId,
|
|
335
|
+
sandboxId: s.sandboxId,
|
|
336
|
+
}));
|
|
337
|
+
|
|
338
|
+
const selected = await vscode.window.showQuickPick(items, {
|
|
339
|
+
placeHolder: 'Select sandbox to execute in',
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
if (!selected) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
sandboxId = selected.sandboxId;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
await promptAndExecForSandbox(sandboxId);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async function promptAndExecForSandbox(sandboxId: string): Promise<void> {
|
|
353
|
+
const command = await vscode.window.showInputBox({
|
|
354
|
+
prompt: 'Enter command to execute',
|
|
355
|
+
placeHolder: 'npm test',
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
if (!command) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Use the exec command which opens in terminal
|
|
363
|
+
await vscode.commands.executeCommand('agentuity.sandbox.exec', { sandboxId, command });
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function clearHideTimeout(): void {
|
|
367
|
+
if (hideTimeoutId) {
|
|
368
|
+
clearTimeout(hideTimeoutId);
|
|
369
|
+
hideTimeoutId = undefined;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export function disposeSandboxStatusBar(): void {
|
|
374
|
+
clearHideTimeout();
|
|
375
|
+
if (statusBarItem) {
|
|
376
|
+
statusBarItem.dispose();
|
|
377
|
+
statusBarItem = undefined;
|
|
378
|
+
}
|
|
379
|
+
if (syncStatusItem) {
|
|
380
|
+
syncStatusItem.dispose();
|
|
381
|
+
syncStatusItem = undefined;
|
|
382
|
+
}
|
|
383
|
+
}
|