@allternit/gizzi-code 1.0.0 → 1.0.1

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 (158) hide show
  1. package/bin/gizzi +4 -0
  2. package/bin/gizzi-ink +5 -0
  3. package/package.json +137 -31
  4. package/src/Tool.ts +26 -0
  5. package/src/bootstrap/state.ts +165 -322
  6. package/src/commands.ts +209 -0
  7. package/src/context/QueuedMessageContext.tsx +63 -0
  8. package/src/context/fpsMetrics.tsx +30 -0
  9. package/src/context/mailbox.tsx +38 -0
  10. package/src/context/modalContext.tsx +58 -0
  11. package/src/context/notifications.tsx +240 -0
  12. package/src/context/overlayContext.tsx +151 -0
  13. package/src/context/promptOverlayContext.tsx +125 -0
  14. package/src/context/stats.tsx +220 -0
  15. package/src/context/voice.tsx +88 -0
  16. package/src/hooks/useCanUseTool.ts +11 -0
  17. package/src/hooks/useExitOnCtrlCD.ts +40 -187
  18. package/src/hooks/useExitOnCtrlCDWithKeybindings.ts +68 -0
  19. package/src/hooks/useKeybinding.ts +68 -209
  20. package/src/hooks/useTerminalSize.ts +31 -99
  21. package/src/hooks.ts +37 -0
  22. package/src/ink.ts +204 -0
  23. package/src/main.ts +7 -0
  24. package/src/types/command.ts +216 -0
  25. package/src/types/connectorText.ts +39 -0
  26. package/src/types/fileSuggestion.ts +15 -0
  27. package/src/types/generated/events_mono/claude_code/v1/claude_code_internal_event.ts +865 -0
  28. package/src/types/generated/events_mono/common/v1/auth.ts +100 -0
  29. package/src/types/generated/events_mono/growthbook/v1/growthbook_experiment_event.ts +223 -0
  30. package/src/types/generated/google/protobuf/timestamp.ts +187 -0
  31. package/src/types/hooks.ts +319 -0
  32. package/src/types/ids.ts +44 -0
  33. package/src/types/logs.ts +331 -0
  34. package/src/types/merged/index.ts +5 -0
  35. package/src/types/merged/message.ts +552 -0
  36. package/src/types/message.ts +927 -186
  37. package/src/types/messageQueueTypes.ts +21 -0
  38. package/src/types/notebook.ts +72 -0
  39. package/src/types/permissions.ts +447 -0
  40. package/src/types/plugin.ts +363 -0
  41. package/src/types/statusLine.ts +15 -0
  42. package/src/types/textInputTypes.ts +387 -0
  43. package/src/types/tools.ts +149 -0
  44. package/src/types/utils.ts +20 -0
  45. package/src/utils/advisor.ts +12 -0
  46. package/src/utils/api.ts +29 -0
  47. package/src/utils/array.ts +7 -0
  48. package/src/utils/attachments.ts +60 -0
  49. package/src/utils/auth.ts +3 -0
  50. package/src/utils/claudeInChrome/chromeNativeHost.ts +15 -0
  51. package/src/utils/claudeInChrome/common.ts +12 -0
  52. package/src/utils/claudeInChrome/mcpServer.ts +15 -0
  53. package/src/utils/computerUse/common.ts +19 -0
  54. package/src/utils/computerUse/mcpServer.ts +15 -0
  55. package/src/utils/config.ts +35 -0
  56. package/src/utils/context.ts +19 -0
  57. package/src/utils/cwd.ts +3 -0
  58. package/src/utils/debug.ts +3 -0
  59. package/src/utils/earlyInput.ts +7 -0
  60. package/src/utils/effort.ts +13 -0
  61. package/src/utils/embeddedTools.ts +12 -0
  62. package/src/utils/envUtils.ts +3 -0
  63. package/src/utils/errors.ts +3 -0
  64. package/src/utils/execFileNoThrow.ts +24 -0
  65. package/src/utils/feature-flags.ts +112 -0
  66. package/src/utils/file.ts +3 -0
  67. package/src/utils/fileHistory.ts +17 -0
  68. package/src/utils/fingerprint.ts +7 -0
  69. package/src/utils/format.ts +75 -305
  70. package/src/utils/frontmatterParser.ts +33 -0
  71. package/src/utils/fsOperations.ts +31 -0
  72. package/src/utils/fullscreen.ts +40 -350
  73. package/src/utils/git.ts +15 -0
  74. package/src/utils/imageResizer.ts +30 -0
  75. package/src/utils/lazySchema.ts +9 -0
  76. package/src/utils/log.ts +3 -0
  77. package/src/utils/markdownConfigLoader.ts +12 -0
  78. package/src/utils/messages.ts +13 -0
  79. package/src/utils/migration.ts +137 -0
  80. package/src/utils/model/providers.ts +15 -0
  81. package/src/utils/path.ts +3 -0
  82. package/src/utils/permissions/PermissionResult.ts +14 -0
  83. package/src/utils/permissions/autoModeState.ts +12 -0
  84. package/src/utils/permissions/filesystem.ts +12 -0
  85. package/src/utils/plugins/loadPluginOutputStyles.ts +12 -0
  86. package/src/utils/plugins/schemas.ts +25 -0
  87. package/src/utils/process.ts +16 -0
  88. package/src/utils/sandbox/sandbox-adapter.ts +19 -0
  89. package/src/utils/sessionStorage.ts +238 -472
  90. package/src/utils/settings/constants.ts +10 -0
  91. package/src/utils/settings/settings.ts +19 -0
  92. package/src/utils/settings/types.ts +18 -0
  93. package/src/utils/shell/shellToolUtils.ts +9 -0
  94. package/src/utils/sinks.ts +11 -0
  95. package/src/utils/sleep.ts +7 -0
  96. package/src/utils/sliceAnsi.ts +7 -0
  97. package/src/utils/slowOperations.ts +3 -0
  98. package/src/utils/startupProfiler.ts +11 -0
  99. package/src/utils/stringUtils.ts +106 -0
  100. package/src/utils/systemPromptType.ts +9 -0
  101. package/src/utils/teleport/api.ts +15 -0
  102. package/src/utils/textHighlighting.ts +16 -0
  103. package/src/utils/theme.ts +17 -0
  104. package/src/utils/thinking.ts +58 -0
  105. package/src/utils/tokens.ts +11 -0
  106. package/src/utils/toolResultStorage.ts +17 -0
  107. package/src/utils/worktreeModeEnabled.ts +7 -0
  108. package/WORKSPACE_INTEGRATION.md +0 -290
  109. package/chat-sessions/sessions-index.json +0 -98
  110. package/package.json.original +0 -31
  111. package/package.json.publish +0 -47
  112. package/src/bootstrap/state.d.ts +0 -169
  113. package/src/bootstrap/state.js +0 -261
  114. package/src/context/pack-builder.d.ts +0 -24
  115. package/src/context/pack-builder.js +0 -281
  116. package/src/hooks/useExitOnCtrlCD.d.ts +0 -86
  117. package/src/hooks/useExitOnCtrlCD.js +0 -130
  118. package/src/hooks/useKeybinding.d.ts +0 -89
  119. package/src/hooks/useKeybinding.js +0 -163
  120. package/src/hooks/useTerminalSize.d.ts +0 -41
  121. package/src/hooks/useTerminalSize.js +0 -90
  122. package/src/index.d.ts +0 -23
  123. package/src/index.js +0 -24
  124. package/src/runtime/brand/brand.d.ts +0 -488
  125. package/src/runtime/brand/brand.js +0 -464
  126. package/src/runtime/brand/brand.ts +0 -667
  127. package/src/runtime/services/tokenEstimation.d.ts +0 -187
  128. package/src/runtime/services/tokenEstimation.js +0 -423
  129. package/src/runtime/services/tokenEstimation.ts +0 -547
  130. package/src/runtime/tools/Tool.d.ts +0 -239
  131. package/src/runtime/tools/Tool.js +0 -285
  132. package/src/runtime/tools/Tool.ts +0 -524
  133. package/src/scheduler/TaskScheduler.d.ts +0 -98
  134. package/src/scheduler/TaskScheduler.js +0 -201
  135. package/src/scheduler/TaskScheduler.ts +0 -259
  136. package/src/state/AppStateStore.d.ts +0 -347
  137. package/src/state/AppStateStore.js +0 -439
  138. package/src/state/AppStateStore.ts +0 -658
  139. package/src/state/WorkspaceStateStore.d.ts +0 -43
  140. package/src/state/WorkspaceStateStore.js +0 -224
  141. package/src/state/WorkspaceStateStore.ts +0 -297
  142. package/src/types/message.d.ts +0 -190
  143. package/src/types/message.js +0 -77
  144. package/src/utils/format.d.ts +0 -126
  145. package/src/utils/format.js +0 -230
  146. package/src/utils/fullscreen.d.ts +0 -161
  147. package/src/utils/fullscreen.js +0 -277
  148. package/src/utils/sessionStorage.d.ts +0 -210
  149. package/src/utils/sessionStorage.js +0 -435
  150. package/src/watcher/WorkspaceWatcher.d.ts +0 -70
  151. package/src/watcher/WorkspaceWatcher.js +0 -191
  152. package/src/watcher/WorkspaceWatcher.ts +0 -251
  153. package/src/workspace/loader.d.ts +0 -30
  154. package/src/workspace/loader.js +0 -117
  155. package/src/workspace/types.d.ts +0 -91
  156. package/src/workspace/types.js +0 -7
  157. package/threads/thread_1775361175022.json +0 -9
  158. package/tsconfig.json +0 -14
@@ -1,559 +1,325 @@
1
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.
2
+ * Session Storage
3
+ * Production-quality session management with filesystem persistence
7
4
  */
8
5
 
9
- import type { UserSession } from '../state/AppStateStore';
10
- import type { TypedMessage } from '../types/message';
6
+ import { writeFile, readFile, mkdir, access, constants } from 'fs/promises'
7
+ import { join } from 'path'
8
+ import { homedir } from 'os'
9
+ import { log } from '../runtime/util/log.js'
11
10
 
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;
11
+ export type UserType = 'anonymous' | 'authenticated' | 'admin' | 'ant' | 'beta'
56
12
 
57
- /** Session version for migration purposes */
58
- readonly version: number;
13
+ export interface SessionData {
14
+ sessionId: string
15
+ userId?: string
16
+ userType: UserType
17
+ email?: string
18
+ accessToken?: string
19
+ refreshToken?: string
20
+ createdAt: number
21
+ lastActivity: number
22
+ expiresAt?: number
23
+ metadata?: Record<string, unknown>
24
+ }
59
25
 
60
- /** Device/browser information */
61
- readonly deviceInfo?: {
62
- readonly userAgent: string;
63
- readonly platform: string;
64
- readonly language: string;
65
- };
26
+ export interface UserPreferences {
27
+ theme?: 'dark' | 'light' | 'system'
28
+ fontSize?: number
29
+ autoSave?: boolean
30
+ notifications?: boolean
31
+ telemetry?: boolean
32
+ [key: string]: unknown
66
33
  }
67
34
 
68
- /**
69
- * Complete session data structure
70
- * @interface SessionData
71
- */
72
- export interface SessionData {
73
- /** User session information */
74
- readonly session: UserSession;
35
+ export interface Message {
36
+ id: string
37
+ role: 'user' | 'assistant' | 'system'
38
+ content: string
39
+ timestamp: number
40
+ attachments?: Attachment[]
41
+ }
75
42
 
76
- /** Session metadata */
77
- readonly metadata: SessionMetadata;
43
+ export interface Attachment {
44
+ id: string
45
+ filename: string
46
+ contentType: string
47
+ size: number
48
+ path: string
49
+ }
78
50
 
79
- /** Persisted messages */
80
- readonly messages: readonly TypedMessage[];
51
+ // In-memory cache
52
+ const memoryStorage = new Map<string, unknown>()
53
+ let currentSession: SessionData | null = null
54
+ let preferencesCache: UserPreferences | null = null
81
55
 
82
- /** User preferences */
83
- readonly preferences: Readonly<Record<string, unknown>>;
56
+ // Config directory
57
+ function getConfigDir(): string {
58
+ return join(homedir(), '.config', 'gizzi')
84
59
  }
85
60
 
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;
61
+ function getSessionFilePath(): string {
62
+ return join(getConfigDir(), 'session.json')
63
+ }
96
64
 
97
- /** Error message if failed */
98
- readonly error: string | null;
65
+ function getPreferencesFilePath(): string {
66
+ return join(getConfigDir(), 'preferences.json')
99
67
  }
100
68
 
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
- }
69
+ function getMessagesFilePath(): string {
70
+ return join(getConfigDir(), 'messages.json')
117
71
  }
118
72
 
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 {
73
+ async function ensureConfigDir(): Promise<void> {
74
+ const configDir = getConfigDir()
128
75
  try {
129
- return JSON.parse(json) as T;
76
+ await access(configDir, constants.F_OK)
130
77
  } catch {
131
- return defaultValue ?? null;
78
+ await mkdir(configDir, { recursive: true })
132
79
  }
133
80
  }
134
81
 
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 {
82
+ // Session Management
83
+ export async function saveSession(data: SessionData): Promise<void> {
84
+ await ensureConfigDir()
85
+
86
+ const session: SessionData = {
87
+ ...data,
88
+ lastActivity: Date.now(),
89
+ }
90
+
91
+ currentSession = session
92
+ memoryStorage.set('__session', session)
93
+
94
+ // Persist to disk
142
95
  try {
143
- return JSON.stringify(value);
144
- } catch {
145
- return null;
96
+ await writeFile(getSessionFilePath(), JSON.stringify(session, null, 2))
97
+ log('debug', 'Session persisted to disk', { sessionId: data.sessionId })
98
+ } catch (error) {
99
+ log('error', 'Failed to persist session', error)
100
+ throw new Error('Failed to save session')
146
101
  }
147
102
  }
148
103
 
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);
104
+ export async function loadSession(): Promise<SessionData | null> {
105
+ // Check memory first
106
+ if (currentSession) {
107
+ return currentSession
164
108
  }
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 {
109
+
110
+ // Try to load from disk
176
111
  try {
177
- if (isStorageAvailable()) {
178
- window.sessionStorage.setItem(key, value);
179
- } else {
180
- memoryStorage.set(key, value);
112
+ const data = await readFile(getSessionFilePath(), 'utf8')
113
+ const session: SessionData = JSON.parse(data)
114
+
115
+ // Check if session is expired
116
+ if (session.expiresAt && session.expiresAt < Date.now()) {
117
+ log('info', 'Session expired')
118
+ await clearSession()
119
+ return null
181
120
  }
182
- return true;
121
+
122
+ currentSession = session
123
+ memoryStorage.set('__session', session)
124
+
125
+ // Update last activity
126
+ session.lastActivity = Date.now()
127
+ await saveSession(session)
128
+
129
+ return session
183
130
  } catch (error) {
184
- console.error(`Failed to set storage item '${key}':`, error);
185
- return false;
131
+ // File doesn't exist or is corrupted
132
+ return null
186
133
  }
187
134
  }
188
135
 
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 {
136
+ export async function clearSession(): Promise<void> {
137
+ currentSession = null
138
+ memoryStorage.delete('__session')
139
+
196
140
  try {
197
- if (isStorageAvailable()) {
198
- window.sessionStorage.removeItem(key);
199
- } else {
200
- memoryStorage.delete(key);
201
- }
202
- return true;
141
+ await writeFile(getSessionFilePath(), '{}')
142
+ log('info', 'Session cleared')
203
143
  } catch (error) {
204
- console.error(`Failed to remove storage item '${key}':`, error);
205
- return false;
144
+ log('error', 'Failed to clear session file', error)
206
145
  }
207
146
  }
208
147
 
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 };
148
+ export function hasActiveSession(): boolean {
149
+ if (currentSession) return true
150
+ return memoryStorage.has('__session')
272
151
  }
273
152
 
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
- }
153
+ export function getSessionId(): string | undefined {
154
+ return currentSession?.sessionId
155
+ }
293
156
 
294
- const data = safeJsonParse<SessionData>(serialized);
157
+ export function getUserType(): UserType {
158
+ return currentSession?.userType || 'anonymous'
159
+ }
295
160
 
296
- if (!data) {
297
- return { success: false, data: null, error: 'Failed to parse session data' };
298
- }
161
+ export function isUserType(type: UserType): boolean {
162
+ return getUserType() === type
163
+ }
299
164
 
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
- }
165
+ export function isAuthenticated(): boolean {
166
+ return currentSession?.userType === 'authenticated' ||
167
+ currentSession?.userType === 'admin' ||
168
+ currentSession?.userType === 'ant'
169
+ }
308
170
 
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);
171
+ export async function updateLastActivity(): Promise<void> {
172
+ if (currentSession) {
173
+ currentSession.lastActivity = Date.now()
174
+ await saveSession(currentSession)
320
175
  }
176
+ }
321
177
 
322
- return { success: true, data: updatedData, error: null };
178
+ export function getLastActivity(): number | undefined {
179
+ return currentSession?.lastActivity
323
180
  }
324
181
 
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
- }
182
+ // Preferences Management
183
+ export async function savePreferences(prefs: UserPreferences): Promise<void> {
184
+ await ensureConfigDir()
185
+ preferencesCache = prefs
186
+
187
+ try {
188
+ await writeFile(getPreferencesFilePath(), JSON.stringify(prefs, null, 2))
189
+ log('debug', 'Preferences saved')
190
+ } catch (error) {
191
+ log('error', 'Failed to save preferences', error)
192
+ throw new Error('Failed to save preferences')
349
193
  }
350
-
351
- // Also clear memory storage
352
- memoryStorage.clear();
353
-
354
- return allSuccess;
355
194
  }
356
195
 
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;
196
+ export async function loadPreferences(): Promise<UserPreferences> {
197
+ if (preferencesCache) {
198
+ return preferencesCache
375
199
  }
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;
200
+
201
+ try {
202
+ const data = await readFile(getPreferencesFilePath(), 'utf8')
203
+ const prefs: UserPreferences = JSON.parse(data)
204
+ preferencesCache = prefs
205
+ return prefs
206
+ } catch {
207
+ // Return defaults
208
+ return {
209
+ theme: 'dark',
210
+ fontSize: 14,
211
+ autoSave: true,
212
+ notifications: true,
213
+ telemetry: true,
383
214
  }
384
215
  }
385
-
386
- return 'anonymous';
387
216
  }
388
217
 
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;
218
+ export async function getPreference<T>(key: string, defaultValue: T): Promise<T> {
219
+ const prefs = await loadPreferences()
220
+ return (prefs[key] as T) ?? defaultValue
400
221
  }
401
222
 
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 };
223
+ export async function setPreference<T>(key: string, value: T): Promise<void> {
224
+ const prefs = await loadPreferences()
225
+ prefs[key] = value
226
+ await savePreferences(prefs)
430
227
  }
431
228
 
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' };
229
+ // Message Persistence
230
+ export async function saveMessages(messages: Message[]): Promise<void> {
231
+ await ensureConfigDir()
232
+
233
+ try {
234
+ await writeFile(getMessagesFilePath(), JSON.stringify(messages, null, 2))
235
+ } catch (error) {
236
+ log('error', 'Failed to save messages', error)
448
237
  }
449
-
450
- return { success: true, data: messages, error: null };
451
238
  }
452
239
 
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' };
240
+ export async function loadMessages(): Promise<Message[]> {
241
+ try {
242
+ const data = await readFile(getMessagesFilePath(), 'utf8')
243
+ return JSON.parse(data)
244
+ } catch {
245
+ return []
468
246
  }
469
-
470
- return { success: true, data: undefined, error: null };
471
247
  }
472
248
 
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' };
249
+ export async function addMessage(message: Message): Promise<void> {
250
+ const messages = await loadMessages()
251
+ messages.push(message)
252
+
253
+ // Keep only last 1000 messages
254
+ if (messages.length > 1000) {
255
+ messages.splice(0, messages.length - 1000)
483
256
  }
257
+
258
+ await saveMessages(messages)
259
+ }
484
260
 
485
- const preferences = safeJsonParse<Record<string, unknown>>(serialized, {});
486
-
487
- if (!preferences) {
488
- return { success: false, data: null, error: 'Failed to parse preferences' };
261
+ // Low-level storage (for other uses)
262
+ export async function getSessionItem<T>(key: string): Promise<T | undefined> {
263
+ // Check memory first
264
+ if (memoryStorage.has(key)) {
265
+ return memoryStorage.get(key) as T
489
266
  }
490
-
491
- return { success: true, data: preferences, error: null };
267
+
268
+ // Could extend to read from separate files per key
269
+ return undefined
492
270
  }
493
271
 
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;
272
+ export async function setSessionItem<T>(key: string, value: T): Promise<void> {
273
+ memoryStorage.set(key, value)
274
+
275
+ // Persist to separate file
276
+ await ensureConfigDir()
277
+ try {
278
+ const filePath = join(getConfigDir(), `${key}.json`)
279
+ await writeFile(filePath, JSON.stringify(value, null, 2))
280
+ } catch (error) {
281
+ log('error', `Failed to persist ${key}`, error)
504
282
  }
283
+ }
505
284
 
285
+ export async function removeSessionItem(key: string): Promise<void> {
286
+ memoryStorage.delete(key)
287
+
506
288
  try {
507
- const date = new Date(stored);
508
- return isNaN(date.getTime()) ? null : date;
289
+ const filePath = join(getConfigDir(), `${key}.json`)
290
+ await writeFile(filePath, '{}')
509
291
  } catch {
510
- return null;
292
+ // Ignore
511
293
  }
512
294
  }
513
295
 
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
296
  // Default export
544
297
  export default {
545
- STORAGE_KEYS,
546
- VALID_USER_TYPES,
298
+ // Session
547
299
  saveSession,
548
300
  loadSession,
549
301
  clearSession,
302
+ hasActiveSession,
303
+ getSessionId,
550
304
  getUserType,
551
- saveMessages,
552
- loadMessages,
305
+ isUserType,
306
+ isAuthenticated,
307
+ updateLastActivity,
308
+ getLastActivity,
309
+
310
+ // Preferences
553
311
  savePreferences,
554
312
  loadPreferences,
555
- getLastActivity,
556
- updateLastActivity,
557
- hasActiveSession,
558
- isUserType,
559
- };
313
+ getPreference,
314
+ setPreference,
315
+
316
+ // Messages
317
+ saveMessages,
318
+ loadMessages,
319
+ addMessage,
320
+
321
+ // Low-level
322
+ getSessionItem,
323
+ setSessionItem,
324
+ removeSessionItem,
325
+ }