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