spck 0.3.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 (155) hide show
  1. package/.oxlintrc.json +49 -0
  2. package/LICENSE +21 -0
  3. package/README.md +631 -0
  4. package/bin/cli.js +20 -0
  5. package/bin/validate-cwd.js +41 -0
  6. package/dist/config/__tests__/config.test.d.ts +2 -0
  7. package/dist/config/__tests__/config.test.js +262 -0
  8. package/dist/config/__tests__/credentials.test.d.ts +2 -0
  9. package/dist/config/__tests__/credentials.test.js +360 -0
  10. package/dist/config/config.d.ts +33 -0
  11. package/dist/config/config.js +185 -0
  12. package/dist/config/credentials.d.ts +75 -0
  13. package/dist/config/credentials.js +259 -0
  14. package/dist/config/server-selection.d.ts +40 -0
  15. package/dist/config/server-selection.js +130 -0
  16. package/dist/connection/__tests__/firebase-auth.test.d.ts +2 -0
  17. package/dist/connection/__tests__/firebase-auth.test.js +96 -0
  18. package/dist/connection/__tests__/hmac.test.d.ts +2 -0
  19. package/dist/connection/__tests__/hmac.test.js +372 -0
  20. package/dist/connection/auth.d.ts +13 -0
  21. package/dist/connection/auth.js +91 -0
  22. package/dist/connection/firebase-auth.d.ts +40 -0
  23. package/dist/connection/firebase-auth.js +429 -0
  24. package/dist/connection/hmac.d.ts +24 -0
  25. package/dist/connection/hmac.js +109 -0
  26. package/dist/i18n/index.d.ts +25 -0
  27. package/dist/i18n/index.js +101 -0
  28. package/dist/i18n/locales/en.json +313 -0
  29. package/dist/i18n/locales/es.json +302 -0
  30. package/dist/i18n/locales/fr.json +302 -0
  31. package/dist/i18n/locales/id.json +302 -0
  32. package/dist/i18n/locales/ja.json +302 -0
  33. package/dist/i18n/locales/ko.json +302 -0
  34. package/dist/i18n/locales/locales/en.json +309 -0
  35. package/dist/i18n/locales/locales/es.json +302 -0
  36. package/dist/i18n/locales/locales/fr.json +302 -0
  37. package/dist/i18n/locales/locales/id.json +302 -0
  38. package/dist/i18n/locales/locales/ja.json +302 -0
  39. package/dist/i18n/locales/locales/ko.json +302 -0
  40. package/dist/i18n/locales/locales/pt.json +302 -0
  41. package/dist/i18n/locales/locales/zh-Hans.json +302 -0
  42. package/dist/i18n/locales/pt.json +302 -0
  43. package/dist/i18n/locales/zh-Hans.json +302 -0
  44. package/dist/index.d.ts +25 -0
  45. package/dist/index.js +493 -0
  46. package/dist/proxy/ProxyClient.d.ts +125 -0
  47. package/dist/proxy/ProxyClient.js +781 -0
  48. package/dist/proxy/ProxySocketWrapper.d.ts +43 -0
  49. package/dist/proxy/ProxySocketWrapper.js +98 -0
  50. package/dist/proxy/__tests__/ProxyClient.test.d.ts +2 -0
  51. package/dist/proxy/__tests__/ProxyClient.test.js +445 -0
  52. package/dist/proxy/__tests__/ProxySocketWrapper.test.d.ts +2 -0
  53. package/dist/proxy/__tests__/ProxySocketWrapper.test.js +190 -0
  54. package/dist/proxy/__tests__/handshake-validation.test.d.ts +2 -0
  55. package/dist/proxy/__tests__/handshake-validation.test.js +282 -0
  56. package/dist/proxy/__tests__/token-refresh-race.test.d.ts +14 -0
  57. package/dist/proxy/__tests__/token-refresh-race.test.js +173 -0
  58. package/dist/proxy/chunking.d.ts +53 -0
  59. package/dist/proxy/chunking.js +127 -0
  60. package/dist/proxy/handshake-validation.d.ts +21 -0
  61. package/dist/proxy/handshake-validation.js +49 -0
  62. package/dist/rpc/__tests__/router.test.d.ts +2 -0
  63. package/dist/rpc/__tests__/router.test.js +262 -0
  64. package/dist/rpc/router.d.ts +37 -0
  65. package/dist/rpc/router.js +132 -0
  66. package/dist/services/BrowserProxyService.d.ts +13 -0
  67. package/dist/services/BrowserProxyService.js +139 -0
  68. package/dist/services/FilesystemService.d.ts +99 -0
  69. package/dist/services/FilesystemService.js +742 -0
  70. package/dist/services/GitService.d.ts +243 -0
  71. package/dist/services/GitService.js +1439 -0
  72. package/dist/services/SearchService.d.ts +93 -0
  73. package/dist/services/SearchService.js +670 -0
  74. package/dist/services/TerminalService.d.ts +62 -0
  75. package/dist/services/TerminalService.js +337 -0
  76. package/dist/services/__tests__/BrowserProxyService.test.d.ts +2 -0
  77. package/dist/services/__tests__/BrowserProxyService.test.js +145 -0
  78. package/dist/services/__tests__/FilesystemService.test.d.ts +2 -0
  79. package/dist/services/__tests__/FilesystemService.test.js +609 -0
  80. package/dist/services/__tests__/GitService.test.d.ts +2 -0
  81. package/dist/services/__tests__/GitService.test.js +953 -0
  82. package/dist/services/__tests__/SearchService.test.d.ts +2 -0
  83. package/dist/services/__tests__/SearchService.test.js +384 -0
  84. package/dist/services/__tests__/TerminalService.test.d.ts +2 -0
  85. package/dist/services/__tests__/TerminalService.test.js +513 -0
  86. package/dist/setup/wizard.d.ts +10 -0
  87. package/dist/setup/wizard.js +172 -0
  88. package/dist/types.d.ts +196 -0
  89. package/dist/types.js +44 -0
  90. package/dist/utils/__tests__/gitignore.test.d.ts +2 -0
  91. package/dist/utils/__tests__/gitignore.test.js +127 -0
  92. package/dist/utils/gitignore.d.ts +24 -0
  93. package/dist/utils/gitignore.js +77 -0
  94. package/dist/utils/logger.d.ts +96 -0
  95. package/dist/utils/logger.js +456 -0
  96. package/dist/utils/project-dir.d.ts +51 -0
  97. package/dist/utils/project-dir.js +191 -0
  98. package/dist/utils/ripgrep.d.ts +34 -0
  99. package/dist/utils/ripgrep.js +148 -0
  100. package/dist/utils/tool-detection.d.ts +17 -0
  101. package/dist/utils/tool-detection.js +126 -0
  102. package/dist/watcher/FileWatcher.d.ts +10 -0
  103. package/dist/watcher/FileWatcher.js +42 -0
  104. package/package.json +70 -0
  105. package/src/config/__tests__/config.test.ts +318 -0
  106. package/src/config/__tests__/credentials.test.ts +494 -0
  107. package/src/config/config.ts +206 -0
  108. package/src/config/credentials.ts +302 -0
  109. package/src/config/server-selection.ts +150 -0
  110. package/src/connection/__tests__/firebase-auth.test.ts +121 -0
  111. package/src/connection/__tests__/hmac.test.ts +509 -0
  112. package/src/connection/auth.ts +140 -0
  113. package/src/connection/firebase-auth.ts +504 -0
  114. package/src/connection/hmac.ts +139 -0
  115. package/src/i18n/index.ts +119 -0
  116. package/src/i18n/locales/en.json +313 -0
  117. package/src/i18n/locales/es.json +302 -0
  118. package/src/i18n/locales/fr.json +302 -0
  119. package/src/i18n/locales/id.json +302 -0
  120. package/src/i18n/locales/ja.json +302 -0
  121. package/src/i18n/locales/ko.json +302 -0
  122. package/src/i18n/locales/pt.json +302 -0
  123. package/src/i18n/locales/zh-Hans.json +302 -0
  124. package/src/index.ts +542 -0
  125. package/src/proxy/ProxyClient.ts +968 -0
  126. package/src/proxy/ProxySocketWrapper.ts +113 -0
  127. package/src/proxy/__tests__/ProxyClient.test.ts +575 -0
  128. package/src/proxy/__tests__/ProxySocketWrapper.test.ts +251 -0
  129. package/src/proxy/__tests__/handshake-validation.test.ts +367 -0
  130. package/src/proxy/chunking.ts +162 -0
  131. package/src/proxy/handshake-validation.ts +64 -0
  132. package/src/rpc/__tests__/router.test.ts +400 -0
  133. package/src/rpc/router.ts +183 -0
  134. package/src/services/BrowserProxyService.ts +179 -0
  135. package/src/services/FilesystemService.ts +841 -0
  136. package/src/services/GitService.ts +1639 -0
  137. package/src/services/SearchService.ts +809 -0
  138. package/src/services/TerminalService.ts +413 -0
  139. package/src/services/__tests__/BrowserProxyService.test.ts +155 -0
  140. package/src/services/__tests__/FilesystemService.test.ts +1002 -0
  141. package/src/services/__tests__/GitService.test.ts +1552 -0
  142. package/src/services/__tests__/SearchService.test.ts +484 -0
  143. package/src/services/__tests__/TerminalService.test.ts +702 -0
  144. package/src/setup/wizard.ts +242 -0
  145. package/src/types/fossil-delta.d.ts +4 -0
  146. package/src/types.ts +287 -0
  147. package/src/utils/__tests__/gitignore.test.ts +174 -0
  148. package/src/utils/gitignore.ts +91 -0
  149. package/src/utils/logger.ts +578 -0
  150. package/src/utils/project-dir.ts +218 -0
  151. package/src/utils/ripgrep.ts +180 -0
  152. package/src/utils/tool-detection.ts +141 -0
  153. package/src/watcher/FileWatcher.ts +53 -0
  154. package/tsconfig.json +24 -0
  155. package/vitest.config.ts +19 -0
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Simple, readable logging utility for network operations
3
+ */
4
+ /**
5
+ * Log a filesystem read operation
6
+ */
7
+ export declare function logFsRead(method: string, params: {
8
+ path?: string;
9
+ src?: string;
10
+ target?: string;
11
+ oldpath?: string;
12
+ [key: string]: any;
13
+ }, uid: string, success: boolean, error?: any, metadata?: Record<string, any>): void;
14
+ /**
15
+ * Log a filesystem write operation
16
+ */
17
+ export declare function logFsWrite(method: string, params: {
18
+ path?: string;
19
+ src?: string;
20
+ target?: string;
21
+ oldpath?: string;
22
+ [key: string]: any;
23
+ }, uid: string, success: boolean, error?: any, metadata?: Record<string, any>): void;
24
+ /**
25
+ * Log a git read operation
26
+ */
27
+ export declare function logGitRead(method: string, params: {
28
+ dir?: string;
29
+ [key: string]: any;
30
+ }, uid: string, success: boolean, error?: any, metadata?: Record<string, any>): void;
31
+ /**
32
+ * Log a git write operation
33
+ */
34
+ export declare function logGitWrite(method: string, params: {
35
+ dir?: string;
36
+ message?: string;
37
+ filepaths?: string[];
38
+ ref?: string;
39
+ [key: string]: any;
40
+ }, uid: string, success: boolean, error?: any, metadata?: Record<string, any>): void;
41
+ /**
42
+ * Log a terminal read operation
43
+ */
44
+ export declare function logTerminalRead(method: string, params: {
45
+ terminalId?: string;
46
+ [key: string]: any;
47
+ }, uid: string, success: boolean, error?: any, metadata?: Record<string, any>): void;
48
+ /**
49
+ * Log a terminal write operation
50
+ */
51
+ export declare function logTerminalWrite(method: string, params: {
52
+ terminalId?: string;
53
+ data?: string;
54
+ cols?: number;
55
+ rows?: number;
56
+ [key: string]: any;
57
+ }, uid: string, success: boolean, error?: any, metadata?: Record<string, any>): void;
58
+ /**
59
+ * Log a search operation
60
+ */
61
+ export declare function logSearchRead(method: string, params: {
62
+ path?: string;
63
+ searchTerm?: string;
64
+ [key: string]: any;
65
+ }, uid: string, success: boolean, error?: any, metadata?: Record<string, any>): void;
66
+ /**
67
+ * Log an authentication event
68
+ */
69
+ export declare function logAuth(event: string, details: Record<string, any>, level?: 'info' | 'warn' | 'error'): void;
70
+ /**
71
+ * Log connection events (client connecting, authenticated, disconnected)
72
+ */
73
+ export declare function logConnection(event: 'connecting' | 'authenticated' | 'auth_failed' | 'disconnected' | 'ready', deviceId?: string, metadata?: Record<string, any>): void;
74
+ /**
75
+ * Log a browser proxy request
76
+ */
77
+ export declare function logBrowserProxy(params: {
78
+ url?: string;
79
+ method?: string;
80
+ requestId?: string;
81
+ [key: string]: any;
82
+ }, uid: string, success: boolean, error?: any, metadata?: Record<string, any>): void;
83
+ declare const _default: {
84
+ logFsRead: typeof logFsRead;
85
+ logFsWrite: typeof logFsWrite;
86
+ logGitRead: typeof logGitRead;
87
+ logGitWrite: typeof logGitWrite;
88
+ logTerminalRead: typeof logTerminalRead;
89
+ logTerminalWrite: typeof logTerminalWrite;
90
+ logSearchRead: typeof logSearchRead;
91
+ logBrowserProxy: typeof logBrowserProxy;
92
+ logAuth: typeof logAuth;
93
+ logConnection: typeof logConnection;
94
+ };
95
+ export default _default;
96
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1,456 @@
1
+ /**
2
+ * Simple, readable logging utility for network operations
3
+ */
4
+ import chalk from 'chalk';
5
+ import * as path from 'path';
6
+ import * as fs from 'fs';
7
+ const LOG_RETENTION_DAYS = 30;
8
+ let logDirInitialized = false;
9
+ let cleanupScheduled = false;
10
+ /**
11
+ * Get log directory path (lazy - doesn't create it)
12
+ */
13
+ function getLogDir() {
14
+ return path.join(process.cwd(), '.spck-editor', 'logs');
15
+ }
16
+ /**
17
+ * Ensure log directory exists (lazy initialization)
18
+ * Called only when actually writing logs
19
+ */
20
+ function ensureLogDirectory() {
21
+ if (logDirInitialized) {
22
+ return;
23
+ }
24
+ try {
25
+ const logDir = getLogDir();
26
+ // Check if .spck-editor exists and is accessible
27
+ const spckEditorDir = path.join(process.cwd(), '.spck-editor');
28
+ if (!fs.existsSync(spckEditorDir)) {
29
+ // .spck-editor directory not set up yet - skip logging to file
30
+ return;
31
+ }
32
+ // Create logs subdirectory if needed
33
+ if (!fs.existsSync(logDir)) {
34
+ fs.mkdirSync(logDir, { recursive: true });
35
+ }
36
+ logDirInitialized = true;
37
+ // Schedule cleanup only once, after first successful initialization
38
+ if (!cleanupScheduled) {
39
+ cleanupScheduled = true;
40
+ // Run cleanup after a short delay (not immediately on import)
41
+ setTimeout(() => {
42
+ cleanOldLogs();
43
+ setInterval(cleanOldLogs, 24 * 60 * 60 * 1000).unref();
44
+ }, 1000).unref();
45
+ }
46
+ }
47
+ catch (error) {
48
+ // Silently fail if we can't create log directory
49
+ // Logging will just go to console only
50
+ }
51
+ }
52
+ /**
53
+ * Get current log file path with date
54
+ */
55
+ function getCurrentLogFile() {
56
+ const date = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
57
+ return path.join(getLogDir(), `spck-cli-${date}.log`);
58
+ }
59
+ /**
60
+ * Clean up old log files (retention policy)
61
+ */
62
+ function cleanOldLogs() {
63
+ try {
64
+ const logDir = getLogDir();
65
+ if (!fs.existsSync(logDir)) {
66
+ return;
67
+ }
68
+ const files = fs.readdirSync(logDir);
69
+ const now = Date.now();
70
+ const retentionMs = LOG_RETENTION_DAYS * 24 * 60 * 60 * 1000;
71
+ for (const file of files) {
72
+ if (file.startsWith('spck-cli-') && file.endsWith('.log')) {
73
+ const filePath = path.join(logDir, file);
74
+ const stats = fs.statSync(filePath);
75
+ if (now - stats.mtimeMs > retentionMs) {
76
+ fs.unlinkSync(filePath);
77
+ console.log(chalk.gray(`[Logger] Deleted old log file: ${file}`));
78
+ }
79
+ }
80
+ }
81
+ }
82
+ catch (error) {
83
+ // Silently fail cleanup errors
84
+ }
85
+ }
86
+ /**
87
+ * Format timestamp for display (compact format for files)
88
+ * Format: MM-DD HH:MM:SS
89
+ */
90
+ function formatTime() {
91
+ const now = new Date();
92
+ const month = String(now.getMonth() + 1).padStart(2, '0');
93
+ const day = String(now.getDate()).padStart(2, '0');
94
+ const hours = String(now.getHours()).padStart(2, '0');
95
+ const minutes = String(now.getMinutes()).padStart(2, '0');
96
+ const seconds = String(now.getSeconds()).padStart(2, '0');
97
+ return `${month}-${day} ${hours}:${minutes}:${seconds}`;
98
+ }
99
+ /**
100
+ * Format timestamp for terminal display (compact format)
101
+ */
102
+ function formatTimeCompact() {
103
+ const now = new Date();
104
+ // Format: HH:MM:SS
105
+ return now.toTimeString().substring(0, 8);
106
+ }
107
+ /**
108
+ * Format UID for display (truncate if needed)
109
+ */
110
+ function formatUid(uid, maxLen = 12) {
111
+ if (uid.length <= maxLen)
112
+ return uid;
113
+ return uid.substring(0, maxLen - 3) + '...';
114
+ }
115
+ /**
116
+ * Write log entry to file
117
+ */
118
+ function writeToFile(message) {
119
+ try {
120
+ // Lazy initialization - only create log directory when actually writing
121
+ ensureLogDirectory();
122
+ if (!logDirInitialized) {
123
+ // Log directory couldn't be initialized, skip file logging
124
+ return;
125
+ }
126
+ const logFile = getCurrentLogFile();
127
+ const timestamp = formatTime();
128
+ fs.appendFileSync(logFile, `[${timestamp}] ${message}\n`);
129
+ }
130
+ catch (error) {
131
+ // Silently fail file writes to not disrupt service
132
+ }
133
+ }
134
+ /**
135
+ * Format path for display (truncate if too long)
136
+ */
137
+ function formatPath(p, maxLen = 50) {
138
+ if (!p)
139
+ return '';
140
+ if (p.length <= maxLen)
141
+ return p;
142
+ return '...' + p.substring(p.length - maxLen + 3);
143
+ }
144
+ /**
145
+ * Format byte count as human-readable size string
146
+ */
147
+ function formatBytes(bytes) {
148
+ if (bytes < 1024)
149
+ return `${bytes} B`;
150
+ if (bytes < 1024 * 1024)
151
+ return `${(bytes / 1024).toFixed(1)} KB`;
152
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
153
+ }
154
+ // Debounce state for browser proxy console output
155
+ const BROWSER_PROXY_DEBOUNCE_MS = 1000;
156
+ let _browserDebounceTimer = null;
157
+ let _browserSuccessCount = 0;
158
+ let _browserTotalBytes = 0;
159
+ let _browserLastUid = '';
160
+ function flushBrowserProxyLog() {
161
+ if (_browserSuccessCount === 0)
162
+ return;
163
+ const timestamp = chalk.gray(formatTimeCompact());
164
+ const uidStr = chalk.gray(formatUid(_browserLastUid));
165
+ const sizeStr = chalk.gray(formatBytes(_browserTotalBytes));
166
+ const countStr = chalk.white(String(_browserSuccessCount));
167
+ console.log(`${timestamp} ${uidStr} ${chalk.green('✓')} ${chalk.blueBright('BROWSER')} ${countStr} files fetched (${sizeStr})`);
168
+ _browserSuccessCount = 0;
169
+ _browserTotalBytes = 0;
170
+ _browserLastUid = '';
171
+ _browserDebounceTimer = null;
172
+ }
173
+ /**
174
+ * Log a filesystem read operation
175
+ */
176
+ export function logFsRead(method, params, uid, success, error, metadata) {
177
+ const filepath = params.path || params.src || params.oldpath;
178
+ const displayPath = formatPath(filepath);
179
+ const metaStr = metadata ? ` ${chalk.gray(JSON.stringify(metadata))}` : '';
180
+ const timestamp = chalk.gray(formatTimeCompact());
181
+ const uidStr = chalk.gray(formatUid(uid));
182
+ if (success) {
183
+ const msg = `${timestamp} ${uidStr} ${chalk.green('✓')} ${chalk.cyan('FS')} ${chalk.white(method.padEnd(12))} ${chalk.gray(displayPath)}${metaStr}`;
184
+ console.log(msg);
185
+ writeToFile(`[INFO] FS READ ${method} ${filepath} uid=${uid} success=true${metaStr}`);
186
+ }
187
+ else {
188
+ const errMsg = error?.message || String(error);
189
+ const msg = `${timestamp} ${uidStr} ${chalk.red('✗')} ${chalk.cyan('FS')} ${chalk.white(method.padEnd(12))} ${chalk.gray(displayPath)} ${chalk.red(errMsg)}`;
190
+ console.log(msg);
191
+ writeToFile(`[ERROR] FS READ ${method} ${filepath} uid=${uid} success=false error="${errMsg}"`);
192
+ }
193
+ }
194
+ /**
195
+ * Log a filesystem write operation
196
+ */
197
+ export function logFsWrite(method, params, uid, success, error, metadata) {
198
+ const filepath = params.path || params.src || params.target || params.oldpath;
199
+ const displayPath = formatPath(filepath);
200
+ const srcTarget = params.src && params.target
201
+ ? `${formatPath(params.src, 25)} → ${formatPath(params.target, 25)}`
202
+ : displayPath;
203
+ const metaStr = metadata ? ` ${chalk.gray(JSON.stringify(metadata))}` : '';
204
+ const timestamp = chalk.gray(formatTimeCompact());
205
+ const uidStr = chalk.gray(formatUid(uid));
206
+ if (success) {
207
+ const msg = `${timestamp} ${uidStr} ${chalk.green('✓')} ${chalk.yellow('FS')} ${chalk.white(method.padEnd(12))} ${chalk.gray(srcTarget)}${metaStr}`;
208
+ console.log(msg);
209
+ writeToFile(`[INFO] FS WRITE ${method} ${filepath} uid=${uid} success=true${metaStr}`);
210
+ }
211
+ else {
212
+ const errMsg = error?.message || String(error);
213
+ const msg = `${timestamp} ${uidStr} ${chalk.red('✗')} ${chalk.yellow('FS')} ${chalk.white(method.padEnd(12))} ${chalk.gray(srcTarget)} ${chalk.red(errMsg)}`;
214
+ console.log(msg);
215
+ writeToFile(`[ERROR] FS WRITE ${method} ${filepath} uid=${uid} success=false error="${errMsg}"`);
216
+ }
217
+ }
218
+ /**
219
+ * Log a git read operation
220
+ */
221
+ export function logGitRead(method, params, uid, success, error, metadata) {
222
+ const dir = formatPath(params.dir);
223
+ const metaStr = metadata ? ` ${chalk.gray(JSON.stringify(metadata))}` : '';
224
+ const timestamp = chalk.gray(formatTimeCompact());
225
+ const uidStr = chalk.gray(formatUid(uid));
226
+ if (success) {
227
+ const msg = `${timestamp} ${uidStr} ${chalk.green('✓')} ${chalk.magenta('GIT')} ${chalk.white(method.padEnd(12))} ${chalk.gray(dir)}${metaStr}`;
228
+ console.log(msg);
229
+ writeToFile(`[INFO] GIT READ ${method} dir=${params.dir} uid=${uid} success=true${metaStr}`);
230
+ }
231
+ else {
232
+ const errMsg = error?.message || String(error);
233
+ const msg = `${timestamp} ${uidStr} ${chalk.red('✗')} ${chalk.magenta('GIT')} ${chalk.white(method.padEnd(12))} ${chalk.gray(dir)} ${chalk.red(errMsg)}`;
234
+ console.log(msg);
235
+ writeToFile(`[ERROR] GIT READ ${method} dir=${params.dir} uid=${uid} success=false error="${errMsg}"`);
236
+ }
237
+ }
238
+ /**
239
+ * Log a git write operation
240
+ */
241
+ export function logGitWrite(method, params, uid, success, error, metadata) {
242
+ const dir = formatPath(params.dir);
243
+ const details = [];
244
+ if (params.message)
245
+ details.push(`msg="${params.message.substring(0, 30)}${params.message.length > 30 ? '...' : ''}"`);
246
+ if (params.filepaths?.length)
247
+ details.push(`files=${params.filepaths.length}`);
248
+ if (params.ref)
249
+ details.push(`ref=${params.ref}`);
250
+ const detailStr = details.length ? ` ${chalk.gray(details.join(' '))}` : '';
251
+ const metaStr = metadata ? ` ${chalk.gray(JSON.stringify(metadata))}` : '';
252
+ const timestamp = chalk.gray(formatTimeCompact());
253
+ const uidStr = chalk.gray(formatUid(uid));
254
+ if (success) {
255
+ const msg = `${timestamp} ${uidStr} ${chalk.green('✓')} ${chalk.yellow('GIT')} ${chalk.white(method.padEnd(12))} ${chalk.gray(dir)}${detailStr}${metaStr}`;
256
+ console.log(msg);
257
+ writeToFile(`[INFO] GIT WRITE ${method} dir=${params.dir} uid=${uid} success=true${detailStr}${metaStr}`);
258
+ }
259
+ else {
260
+ const errMsg = error?.message || String(error);
261
+ const msg = `${timestamp} ${uidStr} ${chalk.red('✗')} ${chalk.yellow('GIT')} ${chalk.white(method.padEnd(12))} ${chalk.gray(dir)} ${chalk.red(errMsg)}`;
262
+ console.log(msg);
263
+ writeToFile(`[ERROR] GIT WRITE ${method} dir=${params.dir} uid=${uid} success=false error="${errMsg}"`);
264
+ }
265
+ }
266
+ /**
267
+ * Log a terminal read operation
268
+ */
269
+ export function logTerminalRead(method, params, uid, success, error, metadata) {
270
+ const termId = params.terminalId || 'all';
271
+ const metaStr = metadata ? ` ${chalk.gray(JSON.stringify(metadata))}` : '';
272
+ const timestamp = chalk.gray(formatTimeCompact());
273
+ const uidStr = chalk.gray(formatUid(uid));
274
+ if (success) {
275
+ const msg = `${timestamp} ${uidStr} ${chalk.green('✓')} ${chalk.blue('TERM')} ${chalk.white(method.padEnd(12))} ${chalk.gray(termId)}${metaStr}`;
276
+ console.log(msg);
277
+ writeToFile(`[INFO] TERMINAL READ ${method} terminalId=${termId} uid=${uid} success=true${metaStr}`);
278
+ }
279
+ else {
280
+ const errMsg = error?.message || String(error);
281
+ const msg = `${timestamp} ${uidStr} ${chalk.red('✗')} ${chalk.blue('TERM')} ${chalk.white(method.padEnd(12))} ${chalk.gray(termId)} ${chalk.red(errMsg)}`;
282
+ console.log(msg);
283
+ writeToFile(`[ERROR] TERMINAL READ ${method} terminalId=${termId} uid=${uid} success=false error="${errMsg}"`);
284
+ }
285
+ }
286
+ /**
287
+ * Log a terminal write operation
288
+ */
289
+ export function logTerminalWrite(method, params, uid, success, error, metadata) {
290
+ const termId = params.terminalId || metadata?.terminalId || 'new';
291
+ const details = [];
292
+ if (params.cols && params.rows)
293
+ details.push(`${params.cols}x${params.rows}`);
294
+ if (params.data)
295
+ details.push(`${params.data.length}b`);
296
+ const detailStr = details.length ? ` ${chalk.gray(details.join(' '))}` : '';
297
+ const metaStr = metadata && !metadata.terminalId ? ` ${chalk.gray(JSON.stringify(metadata))}` : '';
298
+ const timestamp = chalk.gray(formatTimeCompact());
299
+ const uidStr = chalk.gray(formatUid(uid));
300
+ if (success) {
301
+ const msg = `${timestamp} ${uidStr} ${chalk.green('✓')} ${chalk.yellow('TERM')} ${chalk.white(method.padEnd(12))} ${chalk.gray(termId)}${detailStr}${metaStr}`;
302
+ console.log(msg);
303
+ writeToFile(`[INFO] TERMINAL WRITE ${method} terminalId=${termId} uid=${uid} success=true${detailStr}${metaStr}`);
304
+ }
305
+ else {
306
+ const errMsg = error?.message || String(error);
307
+ const msg = `${timestamp} ${uidStr} ${chalk.red('✗')} ${chalk.yellow('TERM')} ${chalk.white(method.padEnd(12))} ${chalk.gray(termId)} ${chalk.red(errMsg)}`;
308
+ console.log(msg);
309
+ writeToFile(`[ERROR] TERMINAL WRITE ${method} terminalId=${termId} uid=${uid} success=false error="${errMsg}"`);
310
+ }
311
+ }
312
+ /**
313
+ * Log a search operation
314
+ */
315
+ export function logSearchRead(method, params, uid, success, error, metadata) {
316
+ const filepath = formatPath(params.path);
317
+ const searchTerm = params.searchTerm ? `"${params.searchTerm.substring(0, 30)}${params.searchTerm.length > 30 ? '...' : ''}'"` : '';
318
+ const details = [];
319
+ if (searchTerm)
320
+ details.push(searchTerm);
321
+ if (metadata?.matches !== undefined)
322
+ details.push(`matches=${metadata.matches}`);
323
+ if (metadata?.method)
324
+ details.push(metadata.method);
325
+ const detailStr = details.length ? ` ${chalk.gray(details.join(' '))}` : '';
326
+ const metaStr = metadata && !metadata.matches && !metadata.method ? ` ${chalk.gray(JSON.stringify(metadata))}` : '';
327
+ const timestamp = chalk.gray(formatTimeCompact());
328
+ const uidStr = chalk.gray(formatUid(uid));
329
+ if (success) {
330
+ const msg = `${timestamp} ${uidStr} ${chalk.green('✓')} ${chalk.green('SEARCH')} ${chalk.white(method.padEnd(12))} ${chalk.gray(filepath)}${detailStr}${metaStr}`;
331
+ console.log(msg);
332
+ writeToFile(`[INFO] SEARCH ${method} ${params.path} searchTerm="${params.searchTerm}" uid=${uid} success=true${detailStr}${metaStr}`);
333
+ }
334
+ else {
335
+ const errMsg = error?.message || String(error);
336
+ const msg = `${timestamp} ${uidStr} ${chalk.red('✗')} ${chalk.green('SEARCH')} ${chalk.white(method.padEnd(12))} ${chalk.gray(filepath)} ${chalk.red(errMsg)}`;
337
+ console.log(msg);
338
+ writeToFile(`[ERROR] SEARCH ${method} ${params.path} searchTerm="${params.searchTerm}" uid=${uid} success=false error="${errMsg}"`);
339
+ }
340
+ }
341
+ /**
342
+ * Log an authentication event
343
+ */
344
+ export function logAuth(event, details, level = 'info') {
345
+ const timestamp = chalk.gray(formatTimeCompact());
346
+ const deviceId = details.deviceId ? chalk.gray(formatUid(details.deviceId)) : '';
347
+ const userId = details.userId ? chalk.gray(`user=${details.userId}`) : '';
348
+ const metaStr = Object.entries(details)
349
+ .filter(([key]) => key !== 'deviceId' && key !== 'userId')
350
+ .map(([key, val]) => `${key}=${val}`)
351
+ .join(' ');
352
+ let symbol;
353
+ let color;
354
+ let logLevel;
355
+ if (level === 'error') {
356
+ symbol = chalk.red('✗');
357
+ color = chalk.red;
358
+ logLevel = 'ERROR';
359
+ }
360
+ else if (level === 'warn') {
361
+ symbol = chalk.yellow('⚠');
362
+ color = chalk.yellow;
363
+ logLevel = 'WARN';
364
+ }
365
+ else {
366
+ symbol = chalk.green('✓');
367
+ color = chalk.green;
368
+ logLevel = 'INFO';
369
+ }
370
+ const msg = `${timestamp} ${deviceId} ${userId} ${symbol} ${color('AUTH')} ${chalk.white(event.padEnd(20))} ${chalk.gray(metaStr)}`;
371
+ console.log(msg);
372
+ writeToFile(`[${logLevel}] AUTH ${event} ${metaStr}`);
373
+ }
374
+ /**
375
+ * Log connection events (client connecting, authenticated, disconnected)
376
+ */
377
+ export function logConnection(event, deviceId, metadata) {
378
+ const timestamp = chalk.gray(formatTimeCompact());
379
+ const deviceStr = deviceId ? chalk.gray(formatUid(deviceId)) : chalk.gray('...');
380
+ const metaStr = metadata ? ` ${chalk.gray(JSON.stringify(metadata))}` : '';
381
+ let symbol;
382
+ let color;
383
+ let logLevel;
384
+ switch (event) {
385
+ case 'connecting':
386
+ symbol = '🔌';
387
+ color = chalk.blue;
388
+ logLevel = 'INFO';
389
+ break;
390
+ case 'authenticated':
391
+ symbol = chalk.green('✓');
392
+ color = chalk.green;
393
+ logLevel = 'INFO';
394
+ break;
395
+ case 'auth_failed':
396
+ symbol = chalk.red('✗');
397
+ color = chalk.red;
398
+ logLevel = 'ERROR';
399
+ break;
400
+ case 'disconnected':
401
+ symbol = '🔌';
402
+ color = chalk.gray;
403
+ logLevel = 'INFO';
404
+ break;
405
+ case 'ready':
406
+ symbol = '🎉';
407
+ color = chalk.green;
408
+ logLevel = 'INFO';
409
+ break;
410
+ default:
411
+ symbol = 'ℹ';
412
+ color = chalk.gray;
413
+ logLevel = 'INFO';
414
+ }
415
+ const msg = `${timestamp} ${deviceStr} ${symbol} ${color('CONN')} ${chalk.white(event.padEnd(15))}${metaStr}`;
416
+ console.log(msg);
417
+ writeToFile(`[${logLevel}] CONN ${event} deviceId=${deviceId || 'unknown'}${metaStr}`);
418
+ }
419
+ /**
420
+ * Log a browser proxy request
421
+ */
422
+ export function logBrowserProxy(params, uid, success, error, metadata) {
423
+ const httpMethod = (params.method || 'GET').toUpperCase();
424
+ if (success) {
425
+ writeToFile(`[INFO] BROWSER PROXY ${httpMethod} ${params.url} uid=${uid} success=true status=${metadata?.status}`);
426
+ // Accumulate and debounce console output into a single summary line
427
+ _browserSuccessCount++;
428
+ _browserTotalBytes += metadata?.size ?? 0;
429
+ _browserLastUid = uid;
430
+ if (_browserDebounceTimer)
431
+ clearTimeout(_browserDebounceTimer);
432
+ _browserDebounceTimer = setTimeout(flushBrowserProxyLog, BROWSER_PROXY_DEBOUNCE_MS);
433
+ }
434
+ else {
435
+ const displayUrl = formatPath(params.url, 60);
436
+ const timestamp = chalk.gray(formatTimeCompact());
437
+ const uidStr = chalk.gray(formatUid(uid));
438
+ const errMsg = error?.message || String(error);
439
+ const msg = `${timestamp} ${uidStr} ${chalk.red('✗')} ${chalk.blueBright('BROWSER')} ${chalk.white(httpMethod.padEnd(7))} ${chalk.gray(displayUrl)} ${chalk.red(errMsg)}`;
440
+ console.log(msg);
441
+ writeToFile(`[ERROR] BROWSER PROXY ${httpMethod} ${params.url} uid=${uid} success=false error="${errMsg}"`);
442
+ }
443
+ }
444
+ export default {
445
+ logFsRead,
446
+ logFsWrite,
447
+ logGitRead,
448
+ logGitWrite,
449
+ logTerminalRead,
450
+ logTerminalWrite,
451
+ logSearchRead,
452
+ logBrowserProxy,
453
+ logAuth,
454
+ logConnection,
455
+ };
456
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Project directory management with symlink support
3
+ * .spck-editor/ is a regular directory with .spck-editor/config symlinked to ~/.spck-editor/projects/{id}
4
+ * This prevents accidental git commits of secrets while avoiding cross-device link errors
5
+ */
6
+ /**
7
+ * Generate a consistent project ID from the project root path
8
+ */
9
+ export declare function generateProjectId(projectRoot: string): string;
10
+ /**
11
+ * Get the home directory location for a project's data
12
+ */
13
+ export declare function getProjectDataPath(projectRoot: string): string;
14
+ /**
15
+ * Get the .spck-editor directory path in the project
16
+ */
17
+ export declare function getProjectDirPath(projectRoot: string): string;
18
+ /**
19
+ * Get the config symlink path (.spck-editor/config)
20
+ */
21
+ export declare function getConfigSymlinkPath(projectRoot: string): string;
22
+ /**
23
+ * @deprecated Use getConfigSymlinkPath() instead
24
+ * Legacy compatibility - returns config symlink path
25
+ */
26
+ export declare function getProjectSymlinkPath(projectRoot: string): string;
27
+ /**
28
+ * Check if the project directory exists and is properly set up
29
+ */
30
+ export declare function isProjectDirSetup(projectRoot: string): boolean;
31
+ /**
32
+ * Setup the project directory with config symlink
33
+ * Creates ~/.spck-editor/projects/{project_id}/ and symlinks .spck-editor/config to it
34
+ */
35
+ export declare function setupProjectDir(projectRoot: string): void;
36
+ /**
37
+ * Ensure project directory is set up, creating if needed
38
+ */
39
+ export declare function ensureProjectDir(projectRoot: string): void;
40
+ /**
41
+ * Get the absolute path to a file within the project directory
42
+ * Files go in .spck-editor/{filename} (local) or .spck-editor/config/{filename} (symlinked)
43
+ * Config files (like connection-settings.json) go in the config subdirectory
44
+ */
45
+ export declare function getProjectFilePath(projectRoot: string, filename: string): string;
46
+ /**
47
+ * Remove project directory and all associated data
48
+ * WARNING: This deletes the data directory in home and the local .spck-editor directory
49
+ */
50
+ export declare function removeProjectDir(projectRoot: string): void;
51
+ //# sourceMappingURL=project-dir.d.ts.map