undoai 0.1.0-beta.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/README.md +243 -0
- package/dist/cli/commands/restore.d.ts +11 -0
- package/dist/cli/commands/restore.d.ts.map +1 -0
- package/dist/cli/commands/restore.js +188 -0
- package/dist/cli/commands/restore.js.map +1 -0
- package/dist/cli/commands/status.d.ts +5 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +46 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +5 -0
- package/dist/cli/commands/stop.d.ts.map +1 -0
- package/dist/cli/commands/stop.js +21 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/commands/watch.d.ts +5 -0
- package/dist/cli/commands/watch.d.ts.map +1 -0
- package/dist/cli/commands/watch.js +138 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +45 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/daemon.d.ts +35 -0
- package/dist/core/daemon.d.ts.map +1 -0
- package/dist/core/daemon.js +94 -0
- package/dist/core/daemon.js.map +1 -0
- package/dist/core/snapshot.d.ts +39 -0
- package/dist/core/snapshot.d.ts.map +1 -0
- package/dist/core/snapshot.js +119 -0
- package/dist/core/snapshot.js.map +1 -0
- package/dist/core/storage.d.ts +92 -0
- package/dist/core/storage.d.ts.map +1 -0
- package/dist/core/storage.js +198 -0
- package/dist/core/storage.js.map +1 -0
- package/dist/example.d.ts +2 -0
- package/dist/example.d.ts.map +1 -0
- package/dist/example.js +30 -0
- package/dist/example.js.map +1 -0
- package/dist/utils/logger.d.ts +42 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +61 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/watcher.d.ts +55 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +122 -0
- package/dist/watcher.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import zlib from 'zlib';
|
|
5
|
+
/**
|
|
6
|
+
* Storage paths configuration
|
|
7
|
+
*/
|
|
8
|
+
export const STORAGE_PATHS = {
|
|
9
|
+
root: path.join(homedir(), '.undoai'),
|
|
10
|
+
snapshots: path.join(homedir(), '.undoai', 'snapshots'),
|
|
11
|
+
daemonPid: path.join(homedir(), '.undoai', 'daemon.pid'),
|
|
12
|
+
config: path.join(homedir(), '.undoai', 'config.json'),
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Storage manager for undoai snapshots
|
|
16
|
+
*/
|
|
17
|
+
export class Storage {
|
|
18
|
+
/**
|
|
19
|
+
* Initialize storage directory structure
|
|
20
|
+
*/
|
|
21
|
+
static init() {
|
|
22
|
+
// Create root directory
|
|
23
|
+
if (!fs.existsSync(STORAGE_PATHS.root)) {
|
|
24
|
+
fs.mkdirSync(STORAGE_PATHS.root, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
// Create snapshots directory
|
|
27
|
+
if (!fs.existsSync(STORAGE_PATHS.snapshots)) {
|
|
28
|
+
fs.mkdirSync(STORAGE_PATHS.snapshots, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Check if storage is initialized
|
|
33
|
+
*/
|
|
34
|
+
static isInitialized() {
|
|
35
|
+
return fs.existsSync(STORAGE_PATHS.root) && fs.existsSync(STORAGE_PATHS.snapshots);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Check if a file exists
|
|
39
|
+
*/
|
|
40
|
+
static fileExists(filePath) {
|
|
41
|
+
try {
|
|
42
|
+
return fs.existsSync(filePath) && fs.statSync(filePath).isFile();
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get all snapshot IDs (sorted by timestamp, newest first)
|
|
50
|
+
*/
|
|
51
|
+
static getSnapshotIds() {
|
|
52
|
+
if (!fs.existsSync(STORAGE_PATHS.snapshots)) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const entries = fs.readdirSync(STORAGE_PATHS.snapshots, { withFileTypes: true });
|
|
56
|
+
const snapshotIds = entries
|
|
57
|
+
.filter((entry) => entry.isDirectory())
|
|
58
|
+
.map((entry) => entry.name)
|
|
59
|
+
.sort((a, b) => parseInt(b) - parseInt(a)); // Newest first
|
|
60
|
+
return snapshotIds;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get snapshot metadata
|
|
64
|
+
*/
|
|
65
|
+
static getSnapshotMetadata(snapshotId) {
|
|
66
|
+
const metadataPath = path.join(STORAGE_PATHS.snapshots, snapshotId, 'metadata.json');
|
|
67
|
+
if (!fs.existsSync(metadataPath)) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const content = fs.readFileSync(metadataPath, 'utf-8');
|
|
72
|
+
return JSON.parse(content);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error(`Failed to read metadata for snapshot ${snapshotId}:`, error);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get snapshot directory path
|
|
81
|
+
*/
|
|
82
|
+
static getSnapshotDir(snapshotId) {
|
|
83
|
+
return path.join(STORAGE_PATHS.snapshots, snapshotId);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get snapshot files directory path
|
|
87
|
+
*/
|
|
88
|
+
static getSnapshotFilesDir(snapshotId) {
|
|
89
|
+
return path.join(STORAGE_PATHS.snapshots, snapshotId, 'files');
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create a new snapshot directory
|
|
93
|
+
*/
|
|
94
|
+
static createSnapshotDir(snapshotId) {
|
|
95
|
+
const snapshotDir = this.getSnapshotDir(snapshotId);
|
|
96
|
+
const filesDir = this.getSnapshotFilesDir(snapshotId);
|
|
97
|
+
fs.mkdirSync(snapshotDir, { recursive: true });
|
|
98
|
+
fs.mkdirSync(filesDir, { recursive: true });
|
|
99
|
+
return snapshotDir;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Save snapshot metadata
|
|
103
|
+
*/
|
|
104
|
+
static saveMetadata(snapshotId, metadata) {
|
|
105
|
+
const metadataPath = path.join(STORAGE_PATHS.snapshots, snapshotId, 'metadata.json');
|
|
106
|
+
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), 'utf-8');
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Convert file path to safe filename (replace / with __)
|
|
110
|
+
* Example: /home/user/project/src/auth.ts -> src__auth.ts
|
|
111
|
+
*/
|
|
112
|
+
static pathToSafeFilename(filePath, projectRoot) {
|
|
113
|
+
const relativePath = path.relative(projectRoot, filePath);
|
|
114
|
+
return relativePath.replace(/\//g, '__').replace(/\\/g, '__');
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Convert safe filename back to relative path
|
|
118
|
+
* Example: src__auth.ts -> src/auth.ts
|
|
119
|
+
*/
|
|
120
|
+
static safeFilenameToPath(safeFilename) {
|
|
121
|
+
return safeFilename.replace(/__/g, path.sep);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Copy file to snapshot with compression
|
|
125
|
+
*/
|
|
126
|
+
static copyFileToSnapshot(sourceFile, snapshotId, projectRoot) {
|
|
127
|
+
const safeFilename = this.pathToSafeFilename(sourceFile, projectRoot);
|
|
128
|
+
const destPath = path.join(this.getSnapshotFilesDir(snapshotId), safeFilename + '.gz');
|
|
129
|
+
// Read, compress, and save file
|
|
130
|
+
const fileContent = fs.readFileSync(sourceFile);
|
|
131
|
+
const compressed = zlib.gzipSync(fileContent);
|
|
132
|
+
fs.writeFileSync(destPath, compressed);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Restore file from snapshot with decompression
|
|
136
|
+
*/
|
|
137
|
+
static restoreFileFromSnapshot(originalPath, snapshotId, projectRoot) {
|
|
138
|
+
const safeFilename = this.pathToSafeFilename(originalPath, projectRoot);
|
|
139
|
+
const sourcePath = path.join(this.getSnapshotFilesDir(snapshotId), safeFilename + '.gz');
|
|
140
|
+
if (!fs.existsSync(sourcePath)) {
|
|
141
|
+
throw new Error(`Snapshot file not found: ${safeFilename}.gz`);
|
|
142
|
+
}
|
|
143
|
+
// Ensure target directory exists
|
|
144
|
+
const targetDir = path.dirname(originalPath);
|
|
145
|
+
if (!fs.existsSync(targetDir)) {
|
|
146
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
147
|
+
}
|
|
148
|
+
// Decompress and restore file
|
|
149
|
+
const compressed = fs.readFileSync(sourcePath);
|
|
150
|
+
const decompressed = zlib.gunzipSync(compressed);
|
|
151
|
+
fs.writeFileSync(originalPath, decompressed);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Delete a snapshot
|
|
155
|
+
*/
|
|
156
|
+
static deleteSnapshot(snapshotId) {
|
|
157
|
+
const snapshotDir = this.getSnapshotDir(snapshotId);
|
|
158
|
+
if (fs.existsSync(snapshotDir)) {
|
|
159
|
+
fs.rmSync(snapshotDir, { recursive: true, force: true });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get total storage size in bytes
|
|
164
|
+
*/
|
|
165
|
+
static getStorageSize() {
|
|
166
|
+
if (!fs.existsSync(STORAGE_PATHS.snapshots)) {
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
let totalSize = 0;
|
|
170
|
+
const calculateDirSize = (dirPath) => {
|
|
171
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
172
|
+
for (const entry of entries) {
|
|
173
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
174
|
+
if (entry.isDirectory()) {
|
|
175
|
+
calculateDirSize(fullPath);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
const stats = fs.statSync(fullPath);
|
|
179
|
+
totalSize += stats.size;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
calculateDirSize(STORAGE_PATHS.snapshots);
|
|
184
|
+
return totalSize;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Format bytes to human readable string
|
|
188
|
+
*/
|
|
189
|
+
static formatBytes(bytes) {
|
|
190
|
+
if (bytes === 0)
|
|
191
|
+
return '0 Bytes';
|
|
192
|
+
const k = 1024;
|
|
193
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
194
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
195
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/core/storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example.d.ts","sourceRoot":"","sources":["../src/example.ts"],"names":[],"mappings":""}
|
package/dist/example.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { FileWatcher } from './watcher.js';
|
|
2
|
+
/**
|
|
3
|
+
* Example usage of the FileWatcher
|
|
4
|
+
*/
|
|
5
|
+
function main() {
|
|
6
|
+
// Initialize the watcher
|
|
7
|
+
const watcher = new FileWatcher({
|
|
8
|
+
watchPath: process.cwd(), // Watch current directory
|
|
9
|
+
onBurstChange: (changedFiles) => {
|
|
10
|
+
console.log(`\n🔥 Burst detected! ${changedFiles.size} files changed:`);
|
|
11
|
+
changedFiles.forEach((file) => {
|
|
12
|
+
console.log(` - ${file}`);
|
|
13
|
+
});
|
|
14
|
+
},
|
|
15
|
+
burstThreshold: 5, // Trigger when ≥5 files change
|
|
16
|
+
debounceDelay: 2000, // After 2000ms of no changes
|
|
17
|
+
});
|
|
18
|
+
// Start watching
|
|
19
|
+
console.log('👀 Watching for file changes...');
|
|
20
|
+
console.log('💡 Make changes to ≥5 files, then wait 2 seconds\n');
|
|
21
|
+
watcher.start();
|
|
22
|
+
// Handle graceful shutdown
|
|
23
|
+
process.on('SIGINT', async () => {
|
|
24
|
+
console.log('\n\n👋 Stopping watcher...');
|
|
25
|
+
await watcher.stop();
|
|
26
|
+
process.exit(0);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
main();
|
|
30
|
+
//# sourceMappingURL=example.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example.js","sourceRoot":"","sources":["../src/example.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C;;GAEG;AACH,SAAS,IAAI;IACT,yBAAyB;IACzB,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC;QAC5B,SAAS,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,0BAA0B;QACpD,aAAa,EAAE,CAAC,YAAY,EAAE,EAAE;YAC5B,OAAO,CAAC,GAAG,CAAC,wBAAwB,YAAY,CAAC,IAAI,iBAAiB,CAAC,CAAC;YACxE,YAAY,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;gBAC1B,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;YAC/B,CAAC,CAAC,CAAC;QACP,CAAC;QACD,cAAc,EAAE,CAAC,EAAK,+BAA+B;QACrD,aAAa,EAAE,IAAI,EAAG,6BAA6B;KACtD,CAAC,CAAC;IAEH,iBAAiB;IACjB,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;IAClE,OAAO,CAAC,KAAK,EAAE,CAAC;IAEhB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QAC5B,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAC1C,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;AACP,CAAC;AAED,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger utility with colored output
|
|
3
|
+
*/
|
|
4
|
+
export declare class Logger {
|
|
5
|
+
/**
|
|
6
|
+
* Success message (green with checkmark)
|
|
7
|
+
*/
|
|
8
|
+
static success(message: string): void;
|
|
9
|
+
/**
|
|
10
|
+
* Error message (red with cross)
|
|
11
|
+
*/
|
|
12
|
+
static error(message: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* Warning message (yellow with warning icon)
|
|
15
|
+
*/
|
|
16
|
+
static warn(message: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* Info message (blue with info icon)
|
|
19
|
+
*/
|
|
20
|
+
static info(message: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Snapshot message (camera icon)
|
|
23
|
+
*/
|
|
24
|
+
static snapshot(message: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Watch message (eyes icon)
|
|
27
|
+
*/
|
|
28
|
+
static watch(message: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Plain message (no icon or color)
|
|
31
|
+
*/
|
|
32
|
+
static plain(message: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Dim/subtle message
|
|
35
|
+
*/
|
|
36
|
+
static dim(message: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Bold message
|
|
39
|
+
*/
|
|
40
|
+
static bold(message: string): void;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,MAAM;IACf;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIrC;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAInC;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIlC;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIlC;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAItC;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAInC;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAInC;;OAEG;IACH,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIjC;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAGrC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
/**
|
|
3
|
+
* Logger utility with colored output
|
|
4
|
+
*/
|
|
5
|
+
export class Logger {
|
|
6
|
+
/**
|
|
7
|
+
* Success message (green with checkmark)
|
|
8
|
+
*/
|
|
9
|
+
static success(message) {
|
|
10
|
+
console.log(chalk.green('✅ ' + message));
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Error message (red with cross)
|
|
14
|
+
*/
|
|
15
|
+
static error(message) {
|
|
16
|
+
console.log(chalk.red('❌ ' + message));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Warning message (yellow with warning icon)
|
|
20
|
+
*/
|
|
21
|
+
static warn(message) {
|
|
22
|
+
console.log(chalk.yellow('⚠️ ' + message));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Info message (blue with info icon)
|
|
26
|
+
*/
|
|
27
|
+
static info(message) {
|
|
28
|
+
console.log(chalk.blue('ℹ️ ' + message));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Snapshot message (camera icon)
|
|
32
|
+
*/
|
|
33
|
+
static snapshot(message) {
|
|
34
|
+
console.log(chalk.cyan('📸 ' + message));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Watch message (eyes icon)
|
|
38
|
+
*/
|
|
39
|
+
static watch(message) {
|
|
40
|
+
console.log(chalk.magenta('👀 ' + message));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Plain message (no icon or color)
|
|
44
|
+
*/
|
|
45
|
+
static plain(message) {
|
|
46
|
+
console.log(message);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Dim/subtle message
|
|
50
|
+
*/
|
|
51
|
+
static dim(message) {
|
|
52
|
+
console.log(chalk.dim(message));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Bold message
|
|
56
|
+
*/
|
|
57
|
+
static bold(message) {
|
|
58
|
+
console.log(chalk.bold(message));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;GAEG;AACH,MAAM,OAAO,MAAM;IACf;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,OAAe;QAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,OAAe;QACxB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,OAAe;QACvB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,OAAe;QACvB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,OAAe;QAC3B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,OAAe;QACxB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,OAAe;QACxB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,GAAG,CAAC,OAAe;QACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,OAAe;QACvB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACrC,CAAC;CACJ"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for the file watcher
|
|
3
|
+
*/
|
|
4
|
+
export interface WatcherOptions {
|
|
5
|
+
/** Directory to watch for file changes */
|
|
6
|
+
watchPath: string;
|
|
7
|
+
/** Callback triggered when burst threshold is met */
|
|
8
|
+
onBurstChange: (changedFiles: Set<string>) => void;
|
|
9
|
+
/** Minimum number of files that must change to trigger burst (default: 5) */
|
|
10
|
+
burstThreshold?: number;
|
|
11
|
+
/** Debounce delay in milliseconds (default: 2000ms) */
|
|
12
|
+
debounceDelay?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* File watcher that monitors directory changes and triggers callbacks
|
|
16
|
+
* when a burst of changes occurs (≥5 files, no changes for ≥2000ms)
|
|
17
|
+
*/
|
|
18
|
+
export declare class FileWatcher {
|
|
19
|
+
private watcher;
|
|
20
|
+
private changedFiles;
|
|
21
|
+
private debounceTimer;
|
|
22
|
+
private readonly watchPath;
|
|
23
|
+
private readonly onBurstChange;
|
|
24
|
+
private readonly burstThreshold;
|
|
25
|
+
private readonly debounceDelay;
|
|
26
|
+
constructor(options: WatcherOptions);
|
|
27
|
+
/**
|
|
28
|
+
* Start watching the directory for file changes
|
|
29
|
+
*/
|
|
30
|
+
start(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Handle a file change event
|
|
33
|
+
* Debounce Logic:
|
|
34
|
+
* 1. Add the file path to the Set (automatically prevents duplicates)
|
|
35
|
+
* 2. Clear any existing debounce timer
|
|
36
|
+
* 3. Start a new timer
|
|
37
|
+
* 4. If timer expires (no new changes for debounceDelay ms):
|
|
38
|
+
* - Check if we have ≥ burstThreshold unique files
|
|
39
|
+
* - If yes, trigger the callback and reset the buffer
|
|
40
|
+
*/
|
|
41
|
+
private handleFileChange;
|
|
42
|
+
/**
|
|
43
|
+
* Stop watching and clean up resources
|
|
44
|
+
*/
|
|
45
|
+
stop(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Get the current count of changed files in the buffer
|
|
48
|
+
*/
|
|
49
|
+
getChangedFilesCount(): number;
|
|
50
|
+
/**
|
|
51
|
+
* Manually clear the changed files buffer
|
|
52
|
+
*/
|
|
53
|
+
clearBuffer(): void;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,aAAa,EAAE,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACnD,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uDAAuD;IACvD,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;GAGG;AACH,qBAAa,WAAW;IACpB,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,aAAa,CAA+B;IAEpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsC;IACpE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,OAAO,EAAE,cAAc;IAOnC;;OAEG;IACI,KAAK,IAAI,IAAI;IA4CpB;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;IAkCxB;;OAEG;IACU,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBlC;;OAEG;IACI,oBAAoB,IAAI,MAAM;IAIrC;;OAEG;IACI,WAAW,IAAI,IAAI;CAG7B"}
|
package/dist/watcher.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import chokidar from 'chokidar';
|
|
2
|
+
/**
|
|
3
|
+
* File watcher that monitors directory changes and triggers callbacks
|
|
4
|
+
* when a burst of changes occurs (≥5 files, no changes for ≥2000ms)
|
|
5
|
+
*/
|
|
6
|
+
export class FileWatcher {
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.watcher = null;
|
|
9
|
+
this.changedFiles = new Set();
|
|
10
|
+
this.debounceTimer = null;
|
|
11
|
+
this.watchPath = options.watchPath;
|
|
12
|
+
this.onBurstChange = options.onBurstChange;
|
|
13
|
+
this.burstThreshold = options.burstThreshold ?? 5;
|
|
14
|
+
this.debounceDelay = options.debounceDelay ?? 2000;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Start watching the directory for file changes
|
|
18
|
+
*/
|
|
19
|
+
start() {
|
|
20
|
+
this.watcher = chokidar.watch(this.watchPath, {
|
|
21
|
+
// Watch configuration
|
|
22
|
+
persistent: true,
|
|
23
|
+
ignoreInitial: true, // Don't trigger events for existing files on startup
|
|
24
|
+
// Ignored paths - use glob patterns
|
|
25
|
+
ignored: [
|
|
26
|
+
'**/node_modules/**',
|
|
27
|
+
'**/.git/**',
|
|
28
|
+
'**/dist/**',
|
|
29
|
+
'**/build/**',
|
|
30
|
+
'**/_tmp_*', // Temp files
|
|
31
|
+
'**/*.tmp', // Temp extensions
|
|
32
|
+
'**/pnpm-lock.yaml.*', // pnpm temp lock files
|
|
33
|
+
'**/package.json.*', // npm/pnpm temp package files
|
|
34
|
+
],
|
|
35
|
+
});
|
|
36
|
+
// Handle file addition
|
|
37
|
+
this.watcher.on('add', (filePath) => {
|
|
38
|
+
this.handleFileChange(filePath, 'add');
|
|
39
|
+
});
|
|
40
|
+
// Handle file modification
|
|
41
|
+
this.watcher.on('change', (filePath) => {
|
|
42
|
+
this.handleFileChange(filePath, 'change');
|
|
43
|
+
});
|
|
44
|
+
// Handle file deletion
|
|
45
|
+
this.watcher.on('unlink', (filePath) => {
|
|
46
|
+
this.handleFileChange(filePath, 'unlink');
|
|
47
|
+
});
|
|
48
|
+
// Handle file rename - chokidar treats this as unlink (old) + add (new)
|
|
49
|
+
// We handle both events separately, so no special handling needed
|
|
50
|
+
// The rename effectively becomes two separate events in our changedFiles Set
|
|
51
|
+
// Error handling
|
|
52
|
+
this.watcher.on('error', (error) => {
|
|
53
|
+
console.error('Watcher error:', error);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Handle a file change event
|
|
58
|
+
* Debounce Logic:
|
|
59
|
+
* 1. Add the file path to the Set (automatically prevents duplicates)
|
|
60
|
+
* 2. Clear any existing debounce timer
|
|
61
|
+
* 3. Start a new timer
|
|
62
|
+
* 4. If timer expires (no new changes for debounceDelay ms):
|
|
63
|
+
* - Check if we have ≥ burstThreshold unique files
|
|
64
|
+
* - If yes, trigger the callback and reset the buffer
|
|
65
|
+
*/
|
|
66
|
+
handleFileChange(filePath, eventType) {
|
|
67
|
+
// Debug: log individual file changes
|
|
68
|
+
console.log(` 📝 [${eventType}] ${filePath}`);
|
|
69
|
+
// Add file to the set of changed files (Set automatically prevents duplicates)
|
|
70
|
+
this.changedFiles.add(filePath);
|
|
71
|
+
// Clear the existing debounce timer if any
|
|
72
|
+
if (this.debounceTimer) {
|
|
73
|
+
clearTimeout(this.debounceTimer);
|
|
74
|
+
}
|
|
75
|
+
// Start a new debounce timer
|
|
76
|
+
this.debounceTimer = setTimeout(() => {
|
|
77
|
+
// Timer expired - no new changes for debounceDelay ms
|
|
78
|
+
// ALWAYS trigger callback if there are ANY changes
|
|
79
|
+
// Let the callback (smart detection in watch.ts) decide whether to snapshot
|
|
80
|
+
if (this.changedFiles.size > 0) {
|
|
81
|
+
// Create a copy of the changed files set for the callback
|
|
82
|
+
const filesSnapshot = new Set(this.changedFiles);
|
|
83
|
+
// Clear the buffer before triggering callback
|
|
84
|
+
this.changedFiles.clear();
|
|
85
|
+
// Trigger callback - smart detection will decide if snapshot needed
|
|
86
|
+
this.onBurstChange(filesSnapshot);
|
|
87
|
+
}
|
|
88
|
+
// Clear the timer reference
|
|
89
|
+
this.debounceTimer = null;
|
|
90
|
+
}, this.debounceDelay);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Stop watching and clean up resources
|
|
94
|
+
*/
|
|
95
|
+
async stop() {
|
|
96
|
+
// Clear any pending debounce timer
|
|
97
|
+
if (this.debounceTimer) {
|
|
98
|
+
clearTimeout(this.debounceTimer);
|
|
99
|
+
this.debounceTimer = null;
|
|
100
|
+
}
|
|
101
|
+
// Close the watcher
|
|
102
|
+
if (this.watcher) {
|
|
103
|
+
await this.watcher.close();
|
|
104
|
+
this.watcher = null;
|
|
105
|
+
}
|
|
106
|
+
// Clear the changed files buffer
|
|
107
|
+
this.changedFiles.clear();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get the current count of changed files in the buffer
|
|
111
|
+
*/
|
|
112
|
+
getChangedFilesCount() {
|
|
113
|
+
return this.changedFiles.size;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Manually clear the changed files buffer
|
|
117
|
+
*/
|
|
118
|
+
clearBuffer() {
|
|
119
|
+
this.changedFiles.clear();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "undoai",
|
|
3
|
+
"version": "0.1.0-beta.1",
|
|
4
|
+
"description": "Free, local undo button for AI coding - automatic snapshots and instant restore",
|
|
5
|
+
"main": "dist/cli/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"undoai": "./dist/cli/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"start": "node dist/cli/index.js",
|
|
19
|
+
"test": "node dist/cli/index.js",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"cli",
|
|
24
|
+
"undo",
|
|
25
|
+
"ai",
|
|
26
|
+
"snapshot",
|
|
27
|
+
"restore",
|
|
28
|
+
"mrq",
|
|
29
|
+
"cursor",
|
|
30
|
+
"vibe-coding",
|
|
31
|
+
"ai-safety",
|
|
32
|
+
"development-tools"
|
|
33
|
+
],
|
|
34
|
+
"author": "",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/yourusername/undo-ai.git"
|
|
39
|
+
},
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/yourusername/undo-ai/issues"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/yourusername/undo-ai#readme",
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18.0.0"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"chalk": "^5.6.2",
|
|
49
|
+
"chokidar": "^3.5.3",
|
|
50
|
+
"commander": "^14.0.2",
|
|
51
|
+
"minimatch": "^10.1.1"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/inquirer": "^9.0.9",
|
|
55
|
+
"@types/node": "^20.10.0",
|
|
56
|
+
"inquirer": "^9.3.8",
|
|
57
|
+
"typescript": "^5.3.0"
|
|
58
|
+
}
|
|
59
|
+
}
|