@allternit/gizzi-code 1.0.0

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 (64) hide show
  1. package/WORKSPACE_INTEGRATION.md +290 -0
  2. package/chat-sessions/sessions-index.json +98 -0
  3. package/package.json +43 -0
  4. package/package.json.original +31 -0
  5. package/package.json.publish +47 -0
  6. package/src/bootstrap/state.d.ts +169 -0
  7. package/src/bootstrap/state.js +261 -0
  8. package/src/bootstrap/state.ts +371 -0
  9. package/src/context/pack-builder.d.ts +24 -0
  10. package/src/context/pack-builder.js +281 -0
  11. package/src/context/pack-builder.ts +334 -0
  12. package/src/hooks/useExitOnCtrlCD.d.ts +86 -0
  13. package/src/hooks/useExitOnCtrlCD.js +130 -0
  14. package/src/hooks/useExitOnCtrlCD.ts +202 -0
  15. package/src/hooks/useKeybinding.d.ts +89 -0
  16. package/src/hooks/useKeybinding.js +163 -0
  17. package/src/hooks/useKeybinding.ts +226 -0
  18. package/src/hooks/useTerminalSize.d.ts +41 -0
  19. package/src/hooks/useTerminalSize.js +90 -0
  20. package/src/hooks/useTerminalSize.ts +117 -0
  21. package/src/index.d.ts +23 -0
  22. package/src/index.js +24 -0
  23. package/src/index.ts +132 -0
  24. package/src/runtime/brand/brand.d.ts +488 -0
  25. package/src/runtime/brand/brand.js +464 -0
  26. package/src/runtime/brand/brand.ts +667 -0
  27. package/src/runtime/services/tokenEstimation.d.ts +187 -0
  28. package/src/runtime/services/tokenEstimation.js +423 -0
  29. package/src/runtime/services/tokenEstimation.ts +547 -0
  30. package/src/runtime/tools/Tool.d.ts +239 -0
  31. package/src/runtime/tools/Tool.js +285 -0
  32. package/src/runtime/tools/Tool.ts +524 -0
  33. package/src/scheduler/TaskScheduler.d.ts +98 -0
  34. package/src/scheduler/TaskScheduler.js +201 -0
  35. package/src/scheduler/TaskScheduler.ts +259 -0
  36. package/src/state/AppStateStore.d.ts +347 -0
  37. package/src/state/AppStateStore.js +439 -0
  38. package/src/state/AppStateStore.ts +658 -0
  39. package/src/state/WorkspaceStateStore.d.ts +43 -0
  40. package/src/state/WorkspaceStateStore.js +224 -0
  41. package/src/state/WorkspaceStateStore.ts +297 -0
  42. package/src/types/message.d.ts +190 -0
  43. package/src/types/message.js +77 -0
  44. package/src/types/message.ts +277 -0
  45. package/src/utils/format.d.ts +126 -0
  46. package/src/utils/format.js +230 -0
  47. package/src/utils/format.ts +336 -0
  48. package/src/utils/fullscreen.d.ts +161 -0
  49. package/src/utils/fullscreen.js +277 -0
  50. package/src/utils/fullscreen.ts +382 -0
  51. package/src/utils/sessionStorage.d.ts +210 -0
  52. package/src/utils/sessionStorage.js +435 -0
  53. package/src/utils/sessionStorage.ts +559 -0
  54. package/src/watcher/WorkspaceWatcher.d.ts +70 -0
  55. package/src/watcher/WorkspaceWatcher.js +191 -0
  56. package/src/watcher/WorkspaceWatcher.ts +251 -0
  57. package/src/workspace/loader.d.ts +30 -0
  58. package/src/workspace/loader.js +117 -0
  59. package/src/workspace/loader.ts +171 -0
  60. package/src/workspace/types.d.ts +91 -0
  61. package/src/workspace/types.js +7 -0
  62. package/src/workspace/types.ts +122 -0
  63. package/threads/thread_1775361175022.json +9 -0
  64. package/tsconfig.json +14 -0
@@ -0,0 +1,559 @@
1
+ /**
2
+ * @fileoverview Session storage utilities for gizzi-code integration
3
+ * @module utils/sessionStorage
4
+ *
5
+ * Provides session persistence, storage management, and user type detection
6
+ * with safe fallbacks for environments without storage access.
7
+ */
8
+
9
+ import type { UserSession } from '../state/AppStateStore';
10
+ import type { TypedMessage } from '../types/message';
11
+
12
+ /**
13
+ * Storage keys used throughout the application
14
+ * @constant {Record<string, string>}
15
+ */
16
+ export const STORAGE_KEYS = {
17
+ SESSION: 'gizzi_session',
18
+ USER_TYPE: 'gizzi_user_type',
19
+ MESSAGES: 'gizzi_messages',
20
+ PREFERENCES: 'gizzi_preferences',
21
+ LAST_ACTIVITY: 'gizzi_last_activity',
22
+ SESSION_METADATA: 'gizzi_session_metadata',
23
+ } as const;
24
+
25
+ /**
26
+ * User type categories
27
+ * @typedef {'anonymous' | 'free' | 'pro' | 'enterprise' | 'admin'} UserType
28
+ */
29
+ export type UserType = 'anonymous' | 'free' | 'pro' | 'enterprise' | 'admin';
30
+
31
+ /**
32
+ * Valid user types for runtime validation
33
+ * @constant {readonly UserType[]}
34
+ */
35
+ export const VALID_USER_TYPES: readonly UserType[] = [
36
+ 'anonymous',
37
+ 'free',
38
+ 'pro',
39
+ 'enterprise',
40
+ 'admin',
41
+ ] as const;
42
+
43
+ /**
44
+ * Session metadata stored alongside the session
45
+ * @interface SessionMetadata
46
+ */
47
+ export interface SessionMetadata {
48
+ /** When the session was created */
49
+ readonly createdAt: string;
50
+
51
+ /** Last activity timestamp (ISO string) */
52
+ readonly lastActivity: string;
53
+
54
+ /** Number of messages in this session */
55
+ readonly messageCount: number;
56
+
57
+ /** Session version for migration purposes */
58
+ readonly version: number;
59
+
60
+ /** Device/browser information */
61
+ readonly deviceInfo?: {
62
+ readonly userAgent: string;
63
+ readonly platform: string;
64
+ readonly language: string;
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Complete session data structure
70
+ * @interface SessionData
71
+ */
72
+ export interface SessionData {
73
+ /** User session information */
74
+ readonly session: UserSession;
75
+
76
+ /** Session metadata */
77
+ readonly metadata: SessionMetadata;
78
+
79
+ /** Persisted messages */
80
+ readonly messages: readonly TypedMessage[];
81
+
82
+ /** User preferences */
83
+ readonly preferences: Readonly<Record<string, unknown>>;
84
+ }
85
+
86
+ /**
87
+ * Storage operation result
88
+ * @interface StorageResult
89
+ */
90
+ export interface StorageResult<T> {
91
+ /** Whether the operation succeeded */
92
+ readonly success: boolean;
93
+
94
+ /** The data if successful, null otherwise */
95
+ readonly data: T | null;
96
+
97
+ /** Error message if failed */
98
+ readonly error: string | null;
99
+ }
100
+
101
+ /**
102
+ * Checks if localStorage/sessionStorage is available
103
+ * @returns {boolean} True if storage APIs are available
104
+ */
105
+ function isStorageAvailable(): boolean {
106
+ try {
107
+ if (typeof window === 'undefined') {
108
+ return false;
109
+ }
110
+ const testKey = '__gizzi_storage_test__';
111
+ window.sessionStorage.setItem(testKey, 'test');
112
+ window.sessionStorage.removeItem(testKey);
113
+ return true;
114
+ } catch {
115
+ return false;
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Safely parses JSON with error handling
121
+ *
122
+ * @template T
123
+ * @param {string} json - JSON string to parse
124
+ * @param {T} [defaultValue] - Default value if parsing fails
125
+ * @returns {T | null} Parsed value or default/null on error
126
+ */
127
+ function safeJsonParse<T>(json: string, defaultValue?: T): T | null {
128
+ try {
129
+ return JSON.parse(json) as T;
130
+ } catch {
131
+ return defaultValue ?? null;
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Safely stringifies JSON with error handling
137
+ *
138
+ * @param {unknown} value - Value to stringify
139
+ * @returns {string | null} JSON string or null on error
140
+ */
141
+ function safeJsonStringify(value: unknown): string | null {
142
+ try {
143
+ return JSON.stringify(value);
144
+ } catch {
145
+ return null;
146
+ }
147
+ }
148
+
149
+ /**
150
+ * In-memory fallback storage when sessionStorage is unavailable
151
+ * @constant {Map<string, string>}
152
+ */
153
+ const memoryStorage: Map<string, string> = new Map();
154
+
155
+ /**
156
+ * Gets an item from storage (sessionStorage or memory fallback)
157
+ *
158
+ * @param {string} key - Storage key
159
+ * @returns {string | null} The stored value or null
160
+ */
161
+ function getStorageItem(key: string): string | null {
162
+ if (isStorageAvailable()) {
163
+ return window.sessionStorage.getItem(key);
164
+ }
165
+ return memoryStorage.get(key) ?? null;
166
+ }
167
+
168
+ /**
169
+ * Sets an item in storage (sessionStorage or memory fallback)
170
+ *
171
+ * @param {string} key - Storage key
172
+ * @param {string} value - Value to store
173
+ * @returns {boolean} True if successful
174
+ */
175
+ function setStorageItem(key: string, value: string): boolean {
176
+ try {
177
+ if (isStorageAvailable()) {
178
+ window.sessionStorage.setItem(key, value);
179
+ } else {
180
+ memoryStorage.set(key, value);
181
+ }
182
+ return true;
183
+ } catch (error) {
184
+ console.error(`Failed to set storage item '${key}':`, error);
185
+ return false;
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Removes an item from storage
191
+ *
192
+ * @param {string} key - Storage key to remove
193
+ * @returns {boolean} True if successful
194
+ */
195
+ function removeStorageItem(key: string): boolean {
196
+ try {
197
+ if (isStorageAvailable()) {
198
+ window.sessionStorage.removeItem(key);
199
+ } else {
200
+ memoryStorage.delete(key);
201
+ }
202
+ return true;
203
+ } catch (error) {
204
+ console.error(`Failed to remove storage item '${key}':`, error);
205
+ return false;
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Saves the current session to storage
211
+ *
212
+ * @param {UserSession} session - The session to save
213
+ * @param {Object} [options] - Save options
214
+ * @param {TypedMessage[]} [options.messages] - Messages to persist
215
+ * @param {Record<string, unknown>} [options.preferences] - Preferences to persist
216
+ * @returns {StorageResult<void>} Result of the save operation
217
+ * @example
218
+ * ```typescript
219
+ * const result = saveSession(session, { messages, preferences });
220
+ * if (!result.success) {
221
+ * console.error('Failed to save session:', result.error);
222
+ * }
223
+ * ```
224
+ */
225
+ export function saveSession(
226
+ session: UserSession,
227
+ options: {
228
+ messages?: TypedMessage[];
229
+ preferences?: Record<string, unknown>;
230
+ } = {}
231
+ ): StorageResult<void> {
232
+ if (!session) {
233
+ return { success: false, data: null, error: 'Session is required' };
234
+ }
235
+
236
+ const existingMetadata = loadSessionMetadata();
237
+ const metadata: SessionMetadata = {
238
+ createdAt: existingMetadata?.createdAt ?? new Date().toISOString(),
239
+ lastActivity: new Date().toISOString(),
240
+ messageCount: options.messages?.length ?? 0,
241
+ version: 1,
242
+ deviceInfo: isStorageAvailable()
243
+ ? {
244
+ userAgent: navigator.userAgent,
245
+ platform: navigator.platform,
246
+ language: navigator.language,
247
+ }
248
+ : undefined,
249
+ };
250
+
251
+ const sessionData: SessionData = {
252
+ session,
253
+ metadata,
254
+ messages: options.messages ?? [],
255
+ preferences: options.preferences ?? {},
256
+ };
257
+
258
+ const serialized = safeJsonStringify(sessionData);
259
+ if (!serialized) {
260
+ return { success: false, data: null, error: 'Failed to serialize session data' };
261
+ }
262
+
263
+ if (!setStorageItem(STORAGE_KEYS.SESSION, serialized)) {
264
+ return { success: false, data: null, error: 'Failed to write to storage' };
265
+ }
266
+
267
+ // Also save user type separately for quick access
268
+ setStorageItem(STORAGE_KEYS.USER_TYPE, session.userType);
269
+ setStorageItem(STORAGE_KEYS.LAST_ACTIVITY, metadata.lastActivity);
270
+
271
+ return { success: true, data: undefined, error: null };
272
+ }
273
+
274
+ /**
275
+ * Loads the session from storage
276
+ *
277
+ * @returns {StorageResult<SessionData>} The loaded session data or error
278
+ * @example
279
+ * ```typescript
280
+ * const result = loadSession();
281
+ * if (result.success && result.data) {
282
+ * const { session, messages } = result.data;
283
+ * // Restore session...
284
+ * }
285
+ * ```
286
+ */
287
+ export function loadSession(): StorageResult<SessionData> {
288
+ const serialized = getStorageItem(STORAGE_KEYS.SESSION);
289
+
290
+ if (!serialized) {
291
+ return { success: false, data: null, error: 'No session found in storage' };
292
+ }
293
+
294
+ const data = safeJsonParse<SessionData>(serialized);
295
+
296
+ if (!data) {
297
+ return { success: false, data: null, error: 'Failed to parse session data' };
298
+ }
299
+
300
+ // Validate session has not expired
301
+ if (data.session.expiresAt) {
302
+ const expiresAt = new Date(data.session.expiresAt);
303
+ if (expiresAt < new Date()) {
304
+ clearSession();
305
+ return { success: false, data: null, error: 'Session has expired' };
306
+ }
307
+ }
308
+
309
+ // Update last activity
310
+ const updatedMetadata: SessionMetadata = {
311
+ ...data.metadata,
312
+ lastActivity: new Date().toISOString(),
313
+ };
314
+
315
+ // Silently update last activity in storage
316
+ const updatedData: SessionData = { ...data, metadata: updatedMetadata };
317
+ const serializedUpdated = safeJsonStringify(updatedData);
318
+ if (serializedUpdated) {
319
+ setStorageItem(STORAGE_KEYS.SESSION, serializedUpdated);
320
+ }
321
+
322
+ return { success: true, data: updatedData, error: null };
323
+ }
324
+
325
+ /**
326
+ * Clears all session data from storage
327
+ *
328
+ * @returns {boolean} True if successful
329
+ * @example
330
+ * ```typescript
331
+ * clearSession(); // Logs out the user
332
+ * ```
333
+ */
334
+ export function clearSession(): boolean {
335
+ const keysToRemove = [
336
+ STORAGE_KEYS.SESSION,
337
+ STORAGE_KEYS.USER_TYPE,
338
+ STORAGE_KEYS.MESSAGES,
339
+ STORAGE_KEYS.PREFERENCES,
340
+ STORAGE_KEYS.LAST_ACTIVITY,
341
+ STORAGE_KEYS.SESSION_METADATA,
342
+ ];
343
+
344
+ let allSuccess = true;
345
+ for (const key of keysToRemove) {
346
+ if (!removeStorageItem(key)) {
347
+ allSuccess = false;
348
+ }
349
+ }
350
+
351
+ // Also clear memory storage
352
+ memoryStorage.clear();
353
+
354
+ return allSuccess;
355
+ }
356
+
357
+ /**
358
+ * Gets the user type from storage
359
+ * Returns 'anonymous' if no user type is stored or on error
360
+ *
361
+ * @returns {UserType} The stored user type or 'anonymous'
362
+ * @example
363
+ * ```typescript
364
+ * const userType = getUserType();
365
+ * if (userType === 'pro') {
366
+ * // Show pro features
367
+ * }
368
+ * ```
369
+ */
370
+ export function getUserType(): UserType {
371
+ const stored = getStorageItem(STORAGE_KEYS.USER_TYPE);
372
+
373
+ if (stored && VALID_USER_TYPES.includes(stored as UserType)) {
374
+ return stored as UserType;
375
+ }
376
+
377
+ // Try to infer from session
378
+ const sessionResult = loadSession();
379
+ if (sessionResult.success && sessionResult.data) {
380
+ const sessionType = sessionResult.data.session.userType;
381
+ if (VALID_USER_TYPES.includes(sessionType as UserType)) {
382
+ return sessionType as UserType;
383
+ }
384
+ }
385
+
386
+ return 'anonymous';
387
+ }
388
+
389
+ /**
390
+ * Loads session metadata without loading the full session
391
+ *
392
+ * @returns {SessionMetadata | null} The metadata or null if not found
393
+ */
394
+ function loadSessionMetadata(): SessionMetadata | null {
395
+ const sessionResult = loadSession();
396
+ if (sessionResult.success && sessionResult.data) {
397
+ return sessionResult.data.metadata;
398
+ }
399
+ return null;
400
+ }
401
+
402
+ /**
403
+ * Saves messages to session storage
404
+ *
405
+ * @param {TypedMessage[]} messages - Messages to save
406
+ * @returns {StorageResult<void>} Result of the operation
407
+ */
408
+ export function saveMessages(messages: TypedMessage[]): StorageResult<void> {
409
+ const serialized = safeJsonStringify(messages);
410
+
411
+ if (!serialized) {
412
+ return { success: false, data: null, error: 'Failed to serialize messages' };
413
+ }
414
+
415
+ if (!setStorageItem(STORAGE_KEYS.MESSAGES, serialized)) {
416
+ return { success: false, data: null, error: 'Failed to write messages to storage' };
417
+ }
418
+
419
+ // Update message count in session metadata
420
+ const sessionResult = loadSession();
421
+ if (sessionResult.success && sessionResult.data) {
422
+ const { session, metadata, preferences } = sessionResult.data;
423
+ saveSession(session, {
424
+ messages,
425
+ preferences,
426
+ });
427
+ }
428
+
429
+ return { success: true, data: undefined, error: null };
430
+ }
431
+
432
+ /**
433
+ * Loads messages from session storage
434
+ *
435
+ * @returns {StorageResult<TypedMessage[]>} The loaded messages or error
436
+ */
437
+ export function loadMessages(): StorageResult<TypedMessage[]> {
438
+ const serialized = getStorageItem(STORAGE_KEYS.MESSAGES);
439
+
440
+ if (!serialized) {
441
+ return { success: false, data: null, error: 'No messages found in storage' };
442
+ }
443
+
444
+ const messages = safeJsonParse<TypedMessage[]>(serialized, []);
445
+
446
+ if (!messages) {
447
+ return { success: false, data: null, error: 'Failed to parse messages' };
448
+ }
449
+
450
+ return { success: true, data: messages, error: null };
451
+ }
452
+
453
+ /**
454
+ * Saves user preferences to storage
455
+ *
456
+ * @param {Record<string, unknown>} preferences - Preferences to save
457
+ * @returns {StorageResult<void>} Result of the operation
458
+ */
459
+ export function savePreferences(preferences: Record<string, unknown>): StorageResult<void> {
460
+ const serialized = safeJsonStringify(preferences);
461
+
462
+ if (!serialized) {
463
+ return { success: false, data: null, error: 'Failed to serialize preferences' };
464
+ }
465
+
466
+ if (!setStorageItem(STORAGE_KEYS.PREFERENCES, serialized)) {
467
+ return { success: false, data: null, error: 'Failed to write preferences to storage' };
468
+ }
469
+
470
+ return { success: true, data: undefined, error: null };
471
+ }
472
+
473
+ /**
474
+ * Loads user preferences from storage
475
+ *
476
+ * @returns {StorageResult<Record<string, unknown>>} The loaded preferences or error
477
+ */
478
+ export function loadPreferences(): StorageResult<Record<string, unknown>> {
479
+ const serialized = getStorageItem(STORAGE_KEYS.PREFERENCES);
480
+
481
+ if (!serialized) {
482
+ return { success: false, data: null, error: 'No preferences found in storage' };
483
+ }
484
+
485
+ const preferences = safeJsonParse<Record<string, unknown>>(serialized, {});
486
+
487
+ if (!preferences) {
488
+ return { success: false, data: null, error: 'Failed to parse preferences' };
489
+ }
490
+
491
+ return { success: true, data: preferences, error: null };
492
+ }
493
+
494
+ /**
495
+ * Gets the timestamp of the last user activity
496
+ *
497
+ * @returns {Date | null} The last activity date or null if not found
498
+ */
499
+ export function getLastActivity(): Date | null {
500
+ const stored = getStorageItem(STORAGE_KEYS.LAST_ACTIVITY);
501
+
502
+ if (!stored) {
503
+ return null;
504
+ }
505
+
506
+ try {
507
+ const date = new Date(stored);
508
+ return isNaN(date.getTime()) ? null : date;
509
+ } catch {
510
+ return null;
511
+ }
512
+ }
513
+
514
+ /**
515
+ * Updates the last activity timestamp to now
516
+ *
517
+ * @returns {boolean} True if successful
518
+ */
519
+ export function updateLastActivity(): boolean {
520
+ return setStorageItem(STORAGE_KEYS.LAST_ACTIVITY, new Date().toISOString());
521
+ }
522
+
523
+ /**
524
+ * Checks if there is an active session in storage
525
+ *
526
+ * @returns {boolean} True if a valid session exists
527
+ */
528
+ export function hasActiveSession(): boolean {
529
+ const result = loadSession();
530
+ return result.success;
531
+ }
532
+
533
+ /**
534
+ * Type guard to check if a value is a valid UserType
535
+ *
536
+ * @param {unknown} value - The value to check
537
+ * @returns {boolean} True if the value is a valid UserType
538
+ */
539
+ export function isUserType(value: unknown): value is UserType {
540
+ return typeof value === 'string' && VALID_USER_TYPES.includes(value as UserType);
541
+ }
542
+
543
+ // Default export
544
+ export default {
545
+ STORAGE_KEYS,
546
+ VALID_USER_TYPES,
547
+ saveSession,
548
+ loadSession,
549
+ clearSession,
550
+ getUserType,
551
+ saveMessages,
552
+ loadMessages,
553
+ savePreferences,
554
+ loadPreferences,
555
+ getLastActivity,
556
+ updateLastActivity,
557
+ hasActiveSession,
558
+ isUserType,
559
+ };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Gizzi Workspace File Watcher
3
+ *
4
+ * Monitors agent workspace files for changes and triggers context refresh.
5
+ */
6
+ import type { FileSystem } from '../workspace/loader';
7
+ export interface FileChange {
8
+ path: string;
9
+ type: 'created' | 'modified' | 'deleted';
10
+ previousModTime?: Date;
11
+ currentModTime?: Date;
12
+ }
13
+ export interface WatcherOptions {
14
+ pollIntervalMs: number;
15
+ onChange: (changes: FileChange[]) => void;
16
+ onError?: (error: Error) => void;
17
+ }
18
+ /**
19
+ * Workspace File Watcher for Gizzi
20
+ */
21
+ export declare class WorkspaceWatcher {
22
+ private agentId;
23
+ private basePath;
24
+ private fs;
25
+ private options;
26
+ private intervalId;
27
+ private watchedFiles;
28
+ private isWatching;
29
+ constructor(agentId: string, basePath: string, fs: FileSystem, options?: Partial<WatcherOptions>);
30
+ /**
31
+ * Start watching for changes
32
+ */
33
+ start(): Promise<void>;
34
+ /**
35
+ * Stop watching
36
+ */
37
+ stop(): void;
38
+ /**
39
+ * Get current watch status
40
+ */
41
+ getStatus(): {
42
+ isWatching: boolean;
43
+ filesWatched: number;
44
+ };
45
+ /**
46
+ * Force a manual refresh
47
+ */
48
+ refresh(): Promise<FileChange[]>;
49
+ /**
50
+ * Scan workspace for files
51
+ */
52
+ private scanWorkspace;
53
+ /**
54
+ * Check for changes
55
+ */
56
+ private checkForChanges;
57
+ }
58
+ /**
59
+ * Get or create a watcher for an agent
60
+ */
61
+ export declare function getWorkspaceWatcher(agentId: string, basePath: string, fs: FileSystem, options?: Partial<WatcherOptions>): WorkspaceWatcher;
62
+ /**
63
+ * Stop all watchers
64
+ */
65
+ export declare function stopAllWatchers(): void;
66
+ export declare const workspaceWatcher: {
67
+ watchers: Map<string, WorkspaceWatcher>;
68
+ getWatcher: typeof getWorkspaceWatcher;
69
+ stopAll: typeof stopAllWatchers;
70
+ };