agentuity-vscode 0.0.86

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.
@@ -0,0 +1,480 @@
1
+ import * as vscode from 'vscode';
2
+ import { getCliClient, type VectorSearchResult, type StreamInfo } from '../../core/cliClient';
3
+ import { getAuthStatus } from '../../core/auth';
4
+ import { hasProject } from '../../core/project';
5
+
6
+ export type DataItemType =
7
+ | 'category'
8
+ | 'namespace'
9
+ | 'key'
10
+ | 'message'
11
+ | 'database'
12
+ | 'vectorSearchGroup'
13
+ | 'vectorResult'
14
+ | 'storageBucket'
15
+ | 'storageFile'
16
+ | 'streamItem';
17
+
18
+ export type DataCategory = 'kv' | 'db' | 'vector' | 'storage' | 'stream';
19
+
20
+ export interface VectorSearchGroup {
21
+ id: string;
22
+ label: string;
23
+ namespace: string;
24
+ query: string;
25
+ results: VectorSearchResult[];
26
+ }
27
+
28
+ export class DataTreeItem extends vscode.TreeItem {
29
+ constructor(
30
+ public readonly label: string,
31
+ public readonly collapsibleState: vscode.TreeItemCollapsibleState,
32
+ public readonly itemType: DataItemType,
33
+ public readonly category?: DataCategory,
34
+ public readonly parentName?: string,
35
+ public readonly vectorResult?: VectorSearchResult,
36
+ public readonly streamInfo?: StreamInfo
37
+ ) {
38
+ super(label, collapsibleState);
39
+ this.setIcon();
40
+ // Set contextValue - special case for Vectors category to enable inline search button
41
+ if (itemType === 'category' && category === 'vector') {
42
+ this.contextValue = 'category-vector';
43
+ } else {
44
+ this.contextValue = itemType;
45
+ }
46
+ }
47
+
48
+ private setIcon(): void {
49
+ switch (this.itemType) {
50
+ case 'category':
51
+ if (this.label === 'Key-Value') {
52
+ this.iconPath = new vscode.ThemeIcon('database');
53
+ } else if (this.label === 'Databases') {
54
+ this.iconPath = new vscode.ThemeIcon('server');
55
+ } else if (this.label === 'Vectors') {
56
+ this.iconPath = new vscode.ThemeIcon('symbol-numeric');
57
+ } else if (this.label === 'Storage') {
58
+ this.iconPath = new vscode.ThemeIcon('cloud');
59
+ } else if (this.label === 'Streams') {
60
+ this.iconPath = new vscode.ThemeIcon('pulse');
61
+ }
62
+ break;
63
+ case 'namespace':
64
+ this.iconPath = new vscode.ThemeIcon('folder');
65
+ break;
66
+ case 'key':
67
+ this.iconPath = new vscode.ThemeIcon('symbol-key');
68
+ break;
69
+ case 'database':
70
+ this.iconPath = new vscode.ThemeIcon('database');
71
+ break;
72
+ case 'vectorSearchGroup':
73
+ this.iconPath = new vscode.ThemeIcon('search');
74
+ break;
75
+ case 'vectorResult':
76
+ this.iconPath = new vscode.ThemeIcon('file-text');
77
+ break;
78
+ case 'storageBucket':
79
+ this.iconPath = new vscode.ThemeIcon('package');
80
+ break;
81
+ case 'storageFile':
82
+ this.iconPath = new vscode.ThemeIcon('file');
83
+ break;
84
+ case 'streamItem':
85
+ this.iconPath = new vscode.ThemeIcon('broadcast');
86
+ break;
87
+ case 'message':
88
+ this.iconPath = new vscode.ThemeIcon('info');
89
+ break;
90
+ }
91
+ }
92
+ }
93
+
94
+ export class DataTreeDataProvider implements vscode.TreeDataProvider<DataTreeItem> {
95
+ private _onDidChangeTreeData = new vscode.EventEmitter<DataTreeItem | undefined | null | void>();
96
+ readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
97
+
98
+ private vectorSearchGroups: VectorSearchGroup[] = [];
99
+
100
+ refresh(): void {
101
+ this._onDidChangeTreeData.fire();
102
+ }
103
+
104
+ addVectorSearchGroup(group: VectorSearchGroup): void {
105
+ this.vectorSearchGroups.unshift(group);
106
+ this.refresh();
107
+ }
108
+
109
+ clearVectorSearchGroups(): void {
110
+ this.vectorSearchGroups = [];
111
+ this.refresh();
112
+ }
113
+
114
+ getTreeItem(element: DataTreeItem): vscode.TreeItem {
115
+ return element;
116
+ }
117
+
118
+ async getChildren(element?: DataTreeItem): Promise<DataTreeItem[]> {
119
+ const authStatus = getAuthStatus();
120
+ if (authStatus.state === 'unknown') {
121
+ return [
122
+ new DataTreeItem('Checking auth...', vscode.TreeItemCollapsibleState.None, 'message'),
123
+ ];
124
+ }
125
+
126
+ if (authStatus.state === 'cli-missing') {
127
+ return [
128
+ new DataTreeItem('CLI not installed', vscode.TreeItemCollapsibleState.None, 'message'),
129
+ ];
130
+ }
131
+
132
+ if (authStatus.state === 'unauthenticated') {
133
+ return [
134
+ new DataTreeItem('Not logged in', vscode.TreeItemCollapsibleState.None, 'message'),
135
+ ];
136
+ }
137
+
138
+ if (!hasProject()) {
139
+ return [
140
+ new DataTreeItem(
141
+ 'No project detected',
142
+ vscode.TreeItemCollapsibleState.None,
143
+ 'message'
144
+ ),
145
+ ];
146
+ }
147
+
148
+ if (!element) {
149
+ return [
150
+ new DataTreeItem(
151
+ 'Key-Value',
152
+ vscode.TreeItemCollapsibleState.Collapsed,
153
+ 'category',
154
+ 'kv'
155
+ ),
156
+ new DataTreeItem(
157
+ 'Databases',
158
+ vscode.TreeItemCollapsibleState.Collapsed,
159
+ 'category',
160
+ 'db'
161
+ ),
162
+ new DataTreeItem(
163
+ 'Vectors',
164
+ vscode.TreeItemCollapsibleState.Collapsed,
165
+ 'category',
166
+ 'vector'
167
+ ),
168
+ new DataTreeItem(
169
+ 'Storage',
170
+ vscode.TreeItemCollapsibleState.Collapsed,
171
+ 'category',
172
+ 'storage'
173
+ ),
174
+ new DataTreeItem(
175
+ 'Streams',
176
+ vscode.TreeItemCollapsibleState.Collapsed,
177
+ 'category',
178
+ 'stream'
179
+ ),
180
+ ];
181
+ }
182
+
183
+ if (element.itemType === 'category') {
184
+ return this.loadCategoryChildren(element);
185
+ }
186
+
187
+ if (element.itemType === 'namespace' && element.category === 'kv') {
188
+ return this.loadKvKeys(element.label);
189
+ }
190
+
191
+ if (element.itemType === 'vectorSearchGroup' && element.category === 'vector') {
192
+ return this.loadVectorSearchResults(element.parentName!);
193
+ }
194
+
195
+ if (element.itemType === 'storageBucket' && element.category === 'storage') {
196
+ return this.loadStorageFiles(element.label);
197
+ }
198
+
199
+ return [];
200
+ }
201
+
202
+ private getErrorMessage(error: string | undefined, category: string): string {
203
+ const errorLower = (error || '').toLowerCase();
204
+ if (
205
+ errorLower.includes('no deployment') ||
206
+ errorLower.includes('not deployed') ||
207
+ errorLower.includes('deployment not found') ||
208
+ errorLower.includes('requires deployment') ||
209
+ errorLower.includes('project not found')
210
+ ) {
211
+ return `Deploy first to see ${category}`;
212
+ }
213
+ return error || `Failed to load ${category}`;
214
+ }
215
+
216
+ private async loadCategoryChildren(element: DataTreeItem): Promise<DataTreeItem[]> {
217
+ const cli = getCliClient();
218
+
219
+ try {
220
+ if (element.category === 'kv') {
221
+ const result = await cli.listKvNamespaces();
222
+ if (result.success && Array.isArray(result.data)) {
223
+ if (result.data.length === 0) {
224
+ return [
225
+ new DataTreeItem(
226
+ 'No namespaces',
227
+ vscode.TreeItemCollapsibleState.None,
228
+ 'message'
229
+ ),
230
+ ];
231
+ }
232
+ return result.data.map((ns) => {
233
+ return new DataTreeItem(
234
+ ns,
235
+ vscode.TreeItemCollapsibleState.Collapsed,
236
+ 'namespace',
237
+ 'kv'
238
+ );
239
+ });
240
+ }
241
+ return [
242
+ new DataTreeItem(
243
+ this.getErrorMessage(result.error, 'namespaces'),
244
+ vscode.TreeItemCollapsibleState.None,
245
+ 'message'
246
+ ),
247
+ ];
248
+ } else if (element.category === 'db') {
249
+ const result = await cli.listDatabases();
250
+ if (result.success && result.data?.databases) {
251
+ if (result.data.databases.length === 0) {
252
+ return [
253
+ new DataTreeItem(
254
+ 'No databases',
255
+ vscode.TreeItemCollapsibleState.None,
256
+ 'message'
257
+ ),
258
+ ];
259
+ }
260
+ return result.data.databases.map((db) => {
261
+ const item = new DataTreeItem(
262
+ db.name,
263
+ vscode.TreeItemCollapsibleState.None,
264
+ 'database',
265
+ 'db'
266
+ );
267
+ item.tooltip = db.url;
268
+ return item;
269
+ });
270
+ }
271
+ return [
272
+ new DataTreeItem(
273
+ this.getErrorMessage(result.error, 'databases'),
274
+ vscode.TreeItemCollapsibleState.None,
275
+ 'message'
276
+ ),
277
+ ];
278
+ } else if (element.category === 'vector') {
279
+ if (this.vectorSearchGroups.length === 0) {
280
+ const item = new DataTreeItem(
281
+ 'Use "Search Vectors..." to search',
282
+ vscode.TreeItemCollapsibleState.None,
283
+ 'message'
284
+ );
285
+ item.tooltip = 'Right-click on Vectors or use the command palette';
286
+ return [item];
287
+ }
288
+ return this.vectorSearchGroups.map((group) => {
289
+ const item = new DataTreeItem(
290
+ group.label,
291
+ vscode.TreeItemCollapsibleState.Collapsed,
292
+ 'vectorSearchGroup',
293
+ 'vector',
294
+ group.id
295
+ );
296
+ item.description = `${group.results.length} results`;
297
+ return item;
298
+ });
299
+ } else if (element.category === 'storage') {
300
+ const result = await cli.listStorageBuckets();
301
+ if (result.success && result.data?.buckets) {
302
+ if (result.data.buckets.length === 0) {
303
+ return [
304
+ new DataTreeItem(
305
+ 'No buckets',
306
+ vscode.TreeItemCollapsibleState.None,
307
+ 'message'
308
+ ),
309
+ ];
310
+ }
311
+ return result.data.buckets.map((bucket) => {
312
+ const item = new DataTreeItem(
313
+ bucket.bucket_name,
314
+ vscode.TreeItemCollapsibleState.Collapsed,
315
+ 'storageBucket',
316
+ 'storage'
317
+ );
318
+ if (bucket.region) {
319
+ item.description = bucket.region;
320
+ }
321
+ return item;
322
+ });
323
+ }
324
+ return [
325
+ new DataTreeItem(
326
+ this.getErrorMessage(result.error, 'storage'),
327
+ vscode.TreeItemCollapsibleState.None,
328
+ 'message'
329
+ ),
330
+ ];
331
+ } else if (element.category === 'stream') {
332
+ const result = await cli.listStreams();
333
+ if (result.success && result.data?.streams) {
334
+ if (result.data.streams.length === 0) {
335
+ return [
336
+ new DataTreeItem(
337
+ 'No streams',
338
+ vscode.TreeItemCollapsibleState.None,
339
+ 'message'
340
+ ),
341
+ ];
342
+ }
343
+ return result.data.streams.map((stream) => {
344
+ const item = new DataTreeItem(
345
+ stream.name || stream.id,
346
+ vscode.TreeItemCollapsibleState.None,
347
+ 'streamItem',
348
+ 'stream',
349
+ undefined,
350
+ undefined,
351
+ stream
352
+ );
353
+ item.description = this.formatFileSize(stream.sizeBytes);
354
+ item.tooltip = `ID: ${stream.id}\nURL: ${stream.url}`;
355
+ return item;
356
+ });
357
+ }
358
+ return [
359
+ new DataTreeItem(
360
+ this.getErrorMessage(result.error, 'streams'),
361
+ vscode.TreeItemCollapsibleState.None,
362
+ 'message'
363
+ ),
364
+ ];
365
+ }
366
+ } catch (err) {
367
+ const message = err instanceof Error ? err.message : 'Failed to load';
368
+ return [new DataTreeItem(message, vscode.TreeItemCollapsibleState.None, 'message')];
369
+ }
370
+
371
+ return [new DataTreeItem('Failed to load', vscode.TreeItemCollapsibleState.None, 'message')];
372
+ }
373
+
374
+ private loadVectorSearchResults(groupId: string): DataTreeItem[] {
375
+ const group = this.vectorSearchGroups.find((g) => g.id === groupId);
376
+ if (!group) {
377
+ return [];
378
+ }
379
+
380
+ if (group.results.length === 0) {
381
+ return [
382
+ new DataTreeItem('No results', vscode.TreeItemCollapsibleState.None, 'message'),
383
+ ];
384
+ }
385
+
386
+ return group.results.map((result) => {
387
+ const item = new DataTreeItem(
388
+ result.key,
389
+ vscode.TreeItemCollapsibleState.None,
390
+ 'vectorResult',
391
+ 'vector',
392
+ group.namespace,
393
+ result
394
+ );
395
+ item.description = `${(result.similarity * 100).toFixed(1)}%`;
396
+ item.tooltip = `Similarity: ${(result.similarity * 100).toFixed(2)}%\nID: ${result.id}`;
397
+ return item;
398
+ });
399
+ }
400
+
401
+ private async loadKvKeys(namespace: string): Promise<DataTreeItem[]> {
402
+ const cli = getCliClient();
403
+
404
+ try {
405
+ const result = await cli.listKvKeys(namespace);
406
+ if (result.success && result.data?.keys) {
407
+ if (result.data.keys.length === 0) {
408
+ return [
409
+ new DataTreeItem('No keys', vscode.TreeItemCollapsibleState.None, 'message'),
410
+ ];
411
+ }
412
+ return result.data.keys.map(
413
+ (key) =>
414
+ new DataTreeItem(
415
+ key,
416
+ vscode.TreeItemCollapsibleState.None,
417
+ 'key',
418
+ 'kv',
419
+ namespace
420
+ )
421
+ );
422
+ }
423
+ } catch (err) {
424
+ const message = err instanceof Error ? err.message : 'Failed to load keys';
425
+ return [new DataTreeItem(message, vscode.TreeItemCollapsibleState.None, 'message')];
426
+ }
427
+
428
+ return [
429
+ new DataTreeItem('Failed to load keys', vscode.TreeItemCollapsibleState.None, 'message'),
430
+ ];
431
+ }
432
+
433
+ private async loadStorageFiles(bucket: string): Promise<DataTreeItem[]> {
434
+ const cli = getCliClient();
435
+
436
+ try {
437
+ const result = await cli.listStorageFiles(bucket);
438
+ if (result.success && result.data?.files) {
439
+ if (result.data.files.length === 0) {
440
+ return [
441
+ new DataTreeItem('No files', vscode.TreeItemCollapsibleState.None, 'message'),
442
+ ];
443
+ }
444
+ return result.data.files.map((file) => {
445
+ const item = new DataTreeItem(
446
+ file.key,
447
+ vscode.TreeItemCollapsibleState.None,
448
+ 'storageFile',
449
+ 'storage',
450
+ bucket
451
+ );
452
+ item.description = this.formatFileSize(file.size);
453
+ return item;
454
+ });
455
+ }
456
+ } catch (err) {
457
+ const message = err instanceof Error ? err.message : 'Failed to load files';
458
+ return [new DataTreeItem(message, vscode.TreeItemCollapsibleState.None, 'message')];
459
+ }
460
+
461
+ return [
462
+ new DataTreeItem(
463
+ 'Failed to load files',
464
+ vscode.TreeItemCollapsibleState.None,
465
+ 'message'
466
+ ),
467
+ ];
468
+ }
469
+
470
+ private formatFileSize(bytes: number): string {
471
+ if (bytes < 1024) return `${bytes} B`;
472
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
473
+ if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
474
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
475
+ }
476
+
477
+ dispose(): void {
478
+ this._onDidChangeTreeData.dispose();
479
+ }
480
+ }