@neurcode-ai/cli 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +21 -0
- package/dist/commands/watch.js.map +1 -1
- package/dist/services/watch/CommandPoller.d.ts +67 -0
- package/dist/services/watch/CommandPoller.d.ts.map +1 -0
- package/dist/services/watch/CommandPoller.js +223 -0
- package/dist/services/watch/CommandPoller.js.map +1 -0
- package/dist/services/watch/Sentinel.d.ts +6 -0
- package/dist/services/watch/Sentinel.d.ts.map +1 -1
- package/dist/services/watch/Sentinel.js +25 -1
- package/dist/services/watch/Sentinel.js.map +1 -1
- package/dist/services/watch/Syncer.d.ts +54 -0
- package/dist/services/watch/Syncer.d.ts.map +1 -0
- package/dist/services/watch/Syncer.js +207 -0
- package/dist/services/watch/Syncer.js.map +1 -0
- package/dist/utils/restore.d.ts +14 -0
- package/dist/utils/restore.d.ts.map +1 -0
- package/dist/utils/restore.js +89 -0
- package/dist/utils/restore.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAsB,YAAY,kBAyDjC"}
|
package/dist/commands/watch.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.watchCommand = watchCommand;
|
|
4
4
|
const Sentinel_1 = require("../services/watch/Sentinel");
|
|
5
|
+
const CommandPoller_1 = require("../services/watch/CommandPoller");
|
|
5
6
|
/**
|
|
6
7
|
* Watch command - Start the Neurcode Watch service
|
|
7
8
|
*
|
|
@@ -15,13 +16,33 @@ async function watchCommand() {
|
|
|
15
16
|
// Create and initialize Sentinel
|
|
16
17
|
const sentinel = new Sentinel_1.Sentinel(projectRoot);
|
|
17
18
|
await sentinel.initialize();
|
|
19
|
+
// Create and start CommandPoller for remote commands
|
|
20
|
+
const commandPoller = new CommandPoller_1.CommandPoller(projectRoot);
|
|
21
|
+
commandPoller.start();
|
|
18
22
|
// Start watching
|
|
19
23
|
await sentinel.start();
|
|
20
24
|
console.log('✅ Neurcode Watch is running...');
|
|
25
|
+
// Check if cloud sync is configured
|
|
26
|
+
const syncer = sentinel.getSyncer();
|
|
27
|
+
if (syncer.isConfigured()) {
|
|
28
|
+
console.log('☁️ Cloud sync: ENABLED (events will be synced to dashboard.neurcode.com)');
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
console.log('📦 Cloud sync: DISABLED (local-only mode)');
|
|
32
|
+
console.log(' Run "neurcode config --key <your_api_key>" to enable cloud sync');
|
|
33
|
+
}
|
|
34
|
+
// Check if command polling is configured
|
|
35
|
+
if (commandPoller.isConfigured()) {
|
|
36
|
+
console.log('🔄 Remote commands: ENABLED (will execute revert commands from dashboard)');
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
console.log('📦 Remote commands: DISABLED (no API key configured)');
|
|
40
|
+
}
|
|
21
41
|
console.log(' Press Ctrl+C to stop\n');
|
|
22
42
|
// Handle graceful shutdown
|
|
23
43
|
const shutdown = async () => {
|
|
24
44
|
console.log('\n🛑 Shutting down...');
|
|
45
|
+
commandPoller.stop();
|
|
25
46
|
await sentinel.stop();
|
|
26
47
|
process.exit(0);
|
|
27
48
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":";;AASA,oCAyDC;AAlED,yDAAsD;AACtD,mEAAgE;AAEhE;;;;;GAKG;AACI,KAAK,UAAU,YAAY;IAChC,IAAI,CAAC;QACH,+CAA+C;QAC/C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAElC,iCAAiC;QACjC,MAAM,QAAQ,GAAG,IAAI,mBAAQ,CAAC,WAAW,CAAC,CAAC;QAC3C,MAAM,QAAQ,CAAC,UAAU,EAAE,CAAC;QAE5B,qDAAqD;QACrD,MAAM,aAAa,GAAG,IAAI,6BAAa,CAAC,WAAW,CAAC,CAAC;QACrD,aAAa,CAAC,KAAK,EAAE,CAAC;QAEtB,iBAAiB;QACjB,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC;QAEvB,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;QAE9C,oCAAoC;QACpC,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC;QACpC,IAAI,MAAM,CAAC,YAAY,EAAE,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,2EAA2E,CAAC,CAAC;QAC3F,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;YACzD,OAAO,CAAC,GAAG,CAAC,oEAAoE,CAAC,CAAC;QACpF,CAAC;QAED,yCAAyC;QACzC,IAAI,aAAa,CAAC,YAAY,EAAE,EAAE,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,2EAA2E,CAAC,CAAC;QAC3F,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;QACtE,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QAEzC,2BAA2B;QAC3B,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;YAC1B,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;YACrC,aAAa,CAAC,IAAI,EAAE,CAAC;YACrB,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC;QAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAEhC,yBAAyB;QACzB,6CAA6C;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAClE,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommandPoller - Polls for remote commands and executes them locally
|
|
3
|
+
*
|
|
4
|
+
* Polls the cloud API every 3 seconds for pending commands (like file reverts).
|
|
5
|
+
* When a command is received, executes it locally and updates the status.
|
|
6
|
+
*/
|
|
7
|
+
export interface Command {
|
|
8
|
+
id: string;
|
|
9
|
+
userId: string;
|
|
10
|
+
organizationId: string;
|
|
11
|
+
type: string;
|
|
12
|
+
payload: {
|
|
13
|
+
filePath: string;
|
|
14
|
+
blobHash: string;
|
|
15
|
+
};
|
|
16
|
+
status: string;
|
|
17
|
+
createdAt: string;
|
|
18
|
+
updatedAt: string;
|
|
19
|
+
}
|
|
20
|
+
export interface PollResponse {
|
|
21
|
+
command: Command | null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* CommandPoller - Handles polling and execution of remote commands
|
|
25
|
+
*/
|
|
26
|
+
export declare class CommandPoller {
|
|
27
|
+
private apiUrl;
|
|
28
|
+
private apiKey;
|
|
29
|
+
private projectRoot;
|
|
30
|
+
private pollInterval;
|
|
31
|
+
private readonly pollIntervalMs;
|
|
32
|
+
private isRunning;
|
|
33
|
+
constructor(projectRoot: string);
|
|
34
|
+
/**
|
|
35
|
+
* Start polling for commands
|
|
36
|
+
*/
|
|
37
|
+
start(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Stop polling for commands
|
|
40
|
+
*/
|
|
41
|
+
stop(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Poll for pending commands and execute them
|
|
44
|
+
*/
|
|
45
|
+
private poll;
|
|
46
|
+
/**
|
|
47
|
+
* Execute a command locally
|
|
48
|
+
*/
|
|
49
|
+
private executeCommand;
|
|
50
|
+
/**
|
|
51
|
+
* Execute a FILE_REVERT command
|
|
52
|
+
*/
|
|
53
|
+
private executeFileRevert;
|
|
54
|
+
/**
|
|
55
|
+
* Update command status on the server
|
|
56
|
+
*/
|
|
57
|
+
private updateCommandStatus;
|
|
58
|
+
/**
|
|
59
|
+
* Check if poller is configured (has API key)
|
|
60
|
+
*/
|
|
61
|
+
isConfigured(): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Reload API key from config (useful if user logs in after watch starts)
|
|
64
|
+
*/
|
|
65
|
+
reloadConfig(): void;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=CommandPoller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommandPoller.d.ts","sourceRoot":"","sources":["../../../src/services/watch/CommandPoller.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACzB;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,SAAS,CAAkB;gBAEvB,WAAW,EAAE,MAAM;IAO/B;;OAEG;IACH,KAAK,IAAI,IAAI;IAsBb;;OAEG;IACH,IAAI,IAAI,IAAI;IASZ;;OAEG;YACW,IAAI;IAyClB;;OAEG;YACW,cAAc;IAe5B;;OAEG;YACW,iBAAiB;IA0B/B;;OAEG;YACW,mBAAmB;IA+BjC;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,YAAY,IAAI,IAAI;CAUrB"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CommandPoller - Polls for remote commands and executes them locally
|
|
4
|
+
*
|
|
5
|
+
* Polls the cloud API every 3 seconds for pending commands (like file reverts).
|
|
6
|
+
* When a command is received, executes it locally and updates the status.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.CommandPoller = void 0;
|
|
43
|
+
const config_1 = require("../../config");
|
|
44
|
+
const restore_1 = require("../../utils/restore");
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const fs_1 = require("fs");
|
|
47
|
+
/**
|
|
48
|
+
* CommandPoller - Handles polling and execution of remote commands
|
|
49
|
+
*/
|
|
50
|
+
class CommandPoller {
|
|
51
|
+
apiUrl;
|
|
52
|
+
apiKey;
|
|
53
|
+
projectRoot;
|
|
54
|
+
pollInterval = null;
|
|
55
|
+
pollIntervalMs = 3000; // Poll every 3 seconds
|
|
56
|
+
isRunning = false;
|
|
57
|
+
constructor(projectRoot) {
|
|
58
|
+
this.projectRoot = projectRoot;
|
|
59
|
+
const config = (0, config_1.loadConfig)();
|
|
60
|
+
this.apiUrl = config.apiUrl || config_1.DEFAULT_API_URL;
|
|
61
|
+
this.apiKey = (0, config_1.getApiKey)();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Start polling for commands
|
|
65
|
+
*/
|
|
66
|
+
start() {
|
|
67
|
+
if (this.isRunning) {
|
|
68
|
+
console.warn('⚠️ CommandPoller is already running');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
// If no API key, skip silently (local-only mode)
|
|
72
|
+
if (!this.apiKey) {
|
|
73
|
+
console.log('📦 Command polling: DISABLED (no API key configured)');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this.isRunning = true;
|
|
77
|
+
console.log('🔄 Command polling: ENABLED (checking every 3s)');
|
|
78
|
+
// Start polling immediately, then every 3 seconds
|
|
79
|
+
this.poll();
|
|
80
|
+
this.pollInterval = setInterval(() => {
|
|
81
|
+
this.poll();
|
|
82
|
+
}, this.pollIntervalMs);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Stop polling for commands
|
|
86
|
+
*/
|
|
87
|
+
stop() {
|
|
88
|
+
if (this.pollInterval) {
|
|
89
|
+
clearInterval(this.pollInterval);
|
|
90
|
+
this.pollInterval = null;
|
|
91
|
+
}
|
|
92
|
+
this.isRunning = false;
|
|
93
|
+
console.log('🛑 Command polling stopped');
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Poll for pending commands and execute them
|
|
97
|
+
*/
|
|
98
|
+
async poll() {
|
|
99
|
+
if (!this.apiKey) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
// Poll for pending commands
|
|
104
|
+
const response = await fetch(`${this.apiUrl}/api/v1/commands/poll`, {
|
|
105
|
+
method: 'GET',
|
|
106
|
+
headers: {
|
|
107
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
if (!response.ok) {
|
|
111
|
+
if (response.status === 401 || response.status === 403) {
|
|
112
|
+
// API key invalid, stop polling
|
|
113
|
+
console.warn('⚠️ Command polling: API key invalid, stopping');
|
|
114
|
+
this.stop();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// For other errors, log but continue polling
|
|
118
|
+
console.warn(`⚠️ Command poll failed: ${response.status}`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const data = await response.json();
|
|
122
|
+
if (!data.command) {
|
|
123
|
+
// No pending commands
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Execute the command
|
|
127
|
+
await this.executeCommand(data.command);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
// Log error but continue polling (network issues, etc.)
|
|
131
|
+
console.warn(`⚠️ Command poll error: ${error.message}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Execute a command locally
|
|
136
|
+
*/
|
|
137
|
+
async executeCommand(command) {
|
|
138
|
+
console.log(`📥 Received command: ${command.type} (${command.id.substring(0, 8)}...)`);
|
|
139
|
+
try {
|
|
140
|
+
if (command.type === 'FILE_REVERT') {
|
|
141
|
+
await this.executeFileRevert(command);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
throw new Error(`Unknown command type: ${command.type}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
console.error(`❌ Command execution failed: ${error.message}`);
|
|
149
|
+
await this.updateCommandStatus(command.id, 'FAILED', error.message);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Execute a FILE_REVERT command
|
|
154
|
+
*/
|
|
155
|
+
async executeFileRevert(command) {
|
|
156
|
+
const { filePath, blobHash } = command.payload;
|
|
157
|
+
if (!filePath || !blobHash) {
|
|
158
|
+
throw new Error('Missing filePath or blobHash in command payload');
|
|
159
|
+
}
|
|
160
|
+
// Check if blob exists locally
|
|
161
|
+
const blobPath = path.join(this.projectRoot, '.neurcode', 'blobs', blobHash);
|
|
162
|
+
try {
|
|
163
|
+
await fs_1.promises.access(blobPath);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
throw new Error(`Blob not found locally: ${blobHash}. Make sure neurcode watch is running.`);
|
|
167
|
+
}
|
|
168
|
+
// Restore the file using existing restoreFile utility
|
|
169
|
+
// Note: restoreFile expects hash and targetPath, and handles decompression
|
|
170
|
+
await (0, restore_1.restoreFile)(blobHash, filePath, this.projectRoot);
|
|
171
|
+
console.log(`✅ File reverted: ${filePath} (${blobHash.substring(0, 8)}...)`);
|
|
172
|
+
// Update command status to COMPLETED
|
|
173
|
+
await this.updateCommandStatus(command.id, 'COMPLETED');
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Update command status on the server
|
|
177
|
+
*/
|
|
178
|
+
async updateCommandStatus(commandId, status, errorMessage) {
|
|
179
|
+
if (!this.apiKey) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
const response = await fetch(`${this.apiUrl}/api/v1/commands/${commandId}/status`, {
|
|
184
|
+
method: 'POST',
|
|
185
|
+
headers: {
|
|
186
|
+
'Content-Type': 'application/json',
|
|
187
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
188
|
+
},
|
|
189
|
+
body: JSON.stringify({
|
|
190
|
+
status,
|
|
191
|
+
errorMessage,
|
|
192
|
+
}),
|
|
193
|
+
});
|
|
194
|
+
if (!response.ok) {
|
|
195
|
+
const errorText = await response.text();
|
|
196
|
+
console.warn(`⚠️ Failed to update command status: ${response.status} ${errorText}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
console.warn(`⚠️ Failed to update command status: ${error.message}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Check if poller is configured (has API key)
|
|
205
|
+
*/
|
|
206
|
+
isConfigured() {
|
|
207
|
+
return this.apiKey !== null;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Reload API key from config (useful if user logs in after watch starts)
|
|
211
|
+
*/
|
|
212
|
+
reloadConfig() {
|
|
213
|
+
const config = (0, config_1.loadConfig)();
|
|
214
|
+
this.apiUrl = config.apiUrl || config_1.DEFAULT_API_URL;
|
|
215
|
+
this.apiKey = (0, config_1.getApiKey)();
|
|
216
|
+
// Restart polling if we now have an API key and weren't running before
|
|
217
|
+
if (this.apiKey && !this.isRunning) {
|
|
218
|
+
this.start();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
exports.CommandPoller = CommandPoller;
|
|
223
|
+
//# sourceMappingURL=CommandPoller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommandPoller.js","sourceRoot":"","sources":["../../../src/services/watch/CommandPoller.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Syncer } from './Syncer';
|
|
1
2
|
/**
|
|
2
3
|
* Sentinel - File system watcher that records file changes
|
|
3
4
|
*
|
|
@@ -8,6 +9,7 @@ export declare class Sentinel {
|
|
|
8
9
|
private watcher;
|
|
9
10
|
private blobStore;
|
|
10
11
|
private journal;
|
|
12
|
+
private syncer;
|
|
11
13
|
private sessionId;
|
|
12
14
|
private projectRoot;
|
|
13
15
|
private debounceTimer;
|
|
@@ -38,5 +40,9 @@ export declare class Sentinel {
|
|
|
38
40
|
* Get the current session ID
|
|
39
41
|
*/
|
|
40
42
|
getSessionId(): string;
|
|
43
|
+
/**
|
|
44
|
+
* Get the syncer instance (for checking sync status)
|
|
45
|
+
*/
|
|
46
|
+
getSyncer(): Syncer;
|
|
41
47
|
}
|
|
42
48
|
//# sourceMappingURL=Sentinel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sentinel.d.ts","sourceRoot":"","sources":["../../../src/services/watch/Sentinel.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Sentinel.d.ts","sourceRoot":"","sources":["../../../src/services/watch/Sentinel.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC;;;;;GAKG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,cAAc,CAAuD;IAC7E,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAO;gBAEtB,WAAW,EAAE,MAAM;IAQ/B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC5B;;OAEG;IACH,OAAO,CAAC,YAAY;IAkBpB;;OAEG;YACW,qBAAqB;IA4CnC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB3B;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,SAAS,IAAI,MAAM;CAGpB"}
|
|
@@ -39,6 +39,7 @@ const path = __importStar(require("path"));
|
|
|
39
39
|
const fs_1 = require("fs");
|
|
40
40
|
const BlobStore_1 = require("./BlobStore");
|
|
41
41
|
const Journal_1 = require("./Journal");
|
|
42
|
+
const Syncer_1 = require("./Syncer");
|
|
42
43
|
/**
|
|
43
44
|
* Sentinel - File system watcher that records file changes
|
|
44
45
|
*
|
|
@@ -49,6 +50,7 @@ class Sentinel {
|
|
|
49
50
|
watcher = null;
|
|
50
51
|
blobStore;
|
|
51
52
|
journal;
|
|
53
|
+
syncer;
|
|
52
54
|
sessionId;
|
|
53
55
|
projectRoot;
|
|
54
56
|
debounceTimer = null;
|
|
@@ -58,6 +60,7 @@ class Sentinel {
|
|
|
58
60
|
this.projectRoot = projectRoot;
|
|
59
61
|
this.blobStore = new BlobStore_1.BlobStore(projectRoot);
|
|
60
62
|
this.journal = new Journal_1.Journal(projectRoot);
|
|
63
|
+
this.syncer = new Syncer_1.Syncer(projectRoot);
|
|
61
64
|
this.sessionId = this.journal.createSession();
|
|
62
65
|
}
|
|
63
66
|
/**
|
|
@@ -141,8 +144,15 @@ class Sentinel {
|
|
|
141
144
|
const content = await fs_1.promises.readFile(fullPath, 'utf-8');
|
|
142
145
|
// Store content in blob store
|
|
143
146
|
const hash = await this.blobStore.store(content);
|
|
144
|
-
// Record event in journal
|
|
147
|
+
// Record event in journal (local SQLite)
|
|
145
148
|
this.journal.recordEvent(this.sessionId, filePath, hash);
|
|
149
|
+
// Queue event for cloud sync (non-blocking, fire-and-forget)
|
|
150
|
+
this.syncer.queueEvent({
|
|
151
|
+
sessionId: this.sessionId,
|
|
152
|
+
filePath,
|
|
153
|
+
hash,
|
|
154
|
+
timestamp: Date.now(),
|
|
155
|
+
});
|
|
146
156
|
console.log(`📝 Recorded: ${filePath} (${hash.substring(0, 8)}...)`);
|
|
147
157
|
}
|
|
148
158
|
catch (error) {
|
|
@@ -165,6 +175,14 @@ class Sentinel {
|
|
|
165
175
|
await this.watcher.close();
|
|
166
176
|
this.watcher = null;
|
|
167
177
|
}
|
|
178
|
+
// Flush any pending syncs before closing
|
|
179
|
+
if (this.syncer.isConfigured()) {
|
|
180
|
+
console.log('☁️ Flushing pending cloud syncs...');
|
|
181
|
+
const result = await this.syncer.flush();
|
|
182
|
+
if (result.success && result.synced > 0) {
|
|
183
|
+
console.log(`✅ Synced ${result.synced} events to cloud`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
168
186
|
this.journal.close();
|
|
169
187
|
console.log('🛑 Watch stopped');
|
|
170
188
|
}
|
|
@@ -174,6 +192,12 @@ class Sentinel {
|
|
|
174
192
|
getSessionId() {
|
|
175
193
|
return this.sessionId;
|
|
176
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Get the syncer instance (for checking sync status)
|
|
197
|
+
*/
|
|
198
|
+
getSyncer() {
|
|
199
|
+
return this.syncer;
|
|
200
|
+
}
|
|
177
201
|
}
|
|
178
202
|
exports.Sentinel = Sentinel;
|
|
179
203
|
//# sourceMappingURL=Sentinel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sentinel.js","sourceRoot":"","sources":["../../../src/services/watch/Sentinel.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,mDAAqC;AACrC,2CAA6B;AAC7B,2BAAoC;AACpC,2CAAwC;AACxC,uCAAoC;
|
|
1
|
+
{"version":3,"file":"Sentinel.js","sourceRoot":"","sources":["../../../src/services/watch/Sentinel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Syncer - Cloud sync service for Time Machine events
|
|
3
|
+
*
|
|
4
|
+
* Pushes file change events to the cloud API in a fire-and-forget manner.
|
|
5
|
+
* If API key is not configured, operates in local-only mode (silent failure).
|
|
6
|
+
*/
|
|
7
|
+
export interface SyncEvent {
|
|
8
|
+
sessionId: string;
|
|
9
|
+
filePath: string;
|
|
10
|
+
hash: string;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
}
|
|
13
|
+
export interface SyncResult {
|
|
14
|
+
success: boolean;
|
|
15
|
+
synced: number;
|
|
16
|
+
skipped: number;
|
|
17
|
+
error?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Syncer - Handles cloud sync of history events
|
|
21
|
+
*/
|
|
22
|
+
export declare class Syncer {
|
|
23
|
+
private apiUrl;
|
|
24
|
+
private apiKey;
|
|
25
|
+
private projectRoot;
|
|
26
|
+
private syncQueue;
|
|
27
|
+
private syncTimer;
|
|
28
|
+
private readonly batchSize;
|
|
29
|
+
private readonly debounceMs;
|
|
30
|
+
constructor(projectRoot: string);
|
|
31
|
+
/**
|
|
32
|
+
* Queue an event for sync (non-blocking)
|
|
33
|
+
* @param event - The event to sync
|
|
34
|
+
*/
|
|
35
|
+
queueEvent(event: SyncEvent): void;
|
|
36
|
+
/**
|
|
37
|
+
* Sync a batch of events to the cloud
|
|
38
|
+
* @returns Sync result
|
|
39
|
+
*/
|
|
40
|
+
private syncBatch;
|
|
41
|
+
/**
|
|
42
|
+
* Force sync all pending events (useful for shutdown)
|
|
43
|
+
*/
|
|
44
|
+
flush(): Promise<SyncResult>;
|
|
45
|
+
/**
|
|
46
|
+
* Check if syncer is configured (has API key)
|
|
47
|
+
*/
|
|
48
|
+
isConfigured(): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Reload API key from config (useful if user logs in after watch starts)
|
|
51
|
+
*/
|
|
52
|
+
reloadConfig(): void;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=Syncer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Syncer.d.ts","sourceRoot":"","sources":["../../../src/services/watch/Syncer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAM;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;gBAEvB,WAAW,EAAE,MAAM;IAO/B;;;OAGG;IACH,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAuBlC;;;OAGG;YACW,SAAS;IAuFvB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAyBlC;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,YAAY,IAAI,IAAI;CAKrB"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Syncer - Cloud sync service for Time Machine events
|
|
4
|
+
*
|
|
5
|
+
* Pushes file change events to the cloud API in a fire-and-forget manner.
|
|
6
|
+
* If API key is not configured, operates in local-only mode (silent failure).
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.Syncer = void 0;
|
|
43
|
+
const config_1 = require("../../config");
|
|
44
|
+
const fs_1 = require("fs");
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
/**
|
|
47
|
+
* Syncer - Handles cloud sync of history events
|
|
48
|
+
*/
|
|
49
|
+
class Syncer {
|
|
50
|
+
apiUrl;
|
|
51
|
+
apiKey;
|
|
52
|
+
projectRoot;
|
|
53
|
+
syncQueue = [];
|
|
54
|
+
syncTimer = null;
|
|
55
|
+
batchSize = 10; // Sync in batches
|
|
56
|
+
debounceMs = 2000; // Wait 2 seconds before syncing
|
|
57
|
+
constructor(projectRoot) {
|
|
58
|
+
this.projectRoot = projectRoot;
|
|
59
|
+
const config = (0, config_1.loadConfig)();
|
|
60
|
+
this.apiUrl = config.apiUrl || config_1.DEFAULT_API_URL;
|
|
61
|
+
this.apiKey = (0, config_1.getApiKey)();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Queue an event for sync (non-blocking)
|
|
65
|
+
* @param event - The event to sync
|
|
66
|
+
*/
|
|
67
|
+
queueEvent(event) {
|
|
68
|
+
// If no API key, skip silently (local-only mode)
|
|
69
|
+
if (!this.apiKey) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// Add to queue
|
|
73
|
+
this.syncQueue.push(event);
|
|
74
|
+
// Clear existing timer
|
|
75
|
+
if (this.syncTimer) {
|
|
76
|
+
clearTimeout(this.syncTimer);
|
|
77
|
+
}
|
|
78
|
+
// Set new timer to sync after debounce period
|
|
79
|
+
this.syncTimer = setTimeout(() => {
|
|
80
|
+
this.syncBatch().catch((error) => {
|
|
81
|
+
// Silently handle errors - don't block the watch service
|
|
82
|
+
console.error('⚠️ Sync error (non-fatal):', error.message);
|
|
83
|
+
});
|
|
84
|
+
}, this.debounceMs);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Sync a batch of events to the cloud
|
|
88
|
+
* @returns Sync result
|
|
89
|
+
*/
|
|
90
|
+
async syncBatch() {
|
|
91
|
+
if (this.syncQueue.length === 0) {
|
|
92
|
+
return { success: true, synced: 0, skipped: 0 };
|
|
93
|
+
}
|
|
94
|
+
// If no API key, clear queue and return
|
|
95
|
+
if (!this.apiKey) {
|
|
96
|
+
this.syncQueue = [];
|
|
97
|
+
return { success: false, synced: 0, skipped: 0, error: 'No API key configured' };
|
|
98
|
+
}
|
|
99
|
+
// Take a batch from the queue
|
|
100
|
+
const batch = this.syncQueue.splice(0, this.batchSize);
|
|
101
|
+
// Prepare events (metadata only - no content)
|
|
102
|
+
const events = batch.map(event => ({
|
|
103
|
+
sessionId: event.sessionId,
|
|
104
|
+
filePath: event.filePath,
|
|
105
|
+
hash: event.hash,
|
|
106
|
+
timestamp: event.timestamp,
|
|
107
|
+
}));
|
|
108
|
+
// Read blob content for all unique hashes in the batch
|
|
109
|
+
// Send blobs separately for server-side deduplication
|
|
110
|
+
const blobs = {};
|
|
111
|
+
const uniqueHashes = new Set(batch.map(e => e.hash));
|
|
112
|
+
for (const hash of uniqueHashes) {
|
|
113
|
+
try {
|
|
114
|
+
// Try to read the blob content
|
|
115
|
+
const blobPath = path.join(this.projectRoot, '.neurcode', 'blobs', hash);
|
|
116
|
+
const blobContent = await fs_1.promises.readFile(blobPath);
|
|
117
|
+
// Convert to base64 for transmission
|
|
118
|
+
// Server will handle deduplication - we send all blobs, server checks which are new
|
|
119
|
+
blobs[hash] = blobContent.toString('base64');
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// If blob doesn't exist or can't be read, skip it
|
|
123
|
+
// The server can still store the event metadata with just the hash
|
|
124
|
+
// This allows events to be recorded even if blob read fails
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
// Make API request with separated events and blobs
|
|
129
|
+
// Format: { events: [...], blobs: { [hash]: base64Content } }
|
|
130
|
+
const response = await fetch(`${this.apiUrl}/api/v1/history/sync`, {
|
|
131
|
+
method: 'POST',
|
|
132
|
+
headers: {
|
|
133
|
+
'Content-Type': 'application/json',
|
|
134
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
135
|
+
},
|
|
136
|
+
body: JSON.stringify({
|
|
137
|
+
events,
|
|
138
|
+
blobs, // Separate blobs map for server-side deduplication
|
|
139
|
+
}),
|
|
140
|
+
});
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
const errorText = await response.text();
|
|
143
|
+
throw new Error(`API error: ${response.status} ${errorText}`);
|
|
144
|
+
}
|
|
145
|
+
const result = await response.json();
|
|
146
|
+
// If there are more events in the queue, schedule another sync
|
|
147
|
+
if (this.syncQueue.length > 0) {
|
|
148
|
+
setTimeout(() => {
|
|
149
|
+
this.syncBatch().catch((error) => {
|
|
150
|
+
console.error('⚠️ Sync error (non-fatal):', error.message);
|
|
151
|
+
});
|
|
152
|
+
}, this.debounceMs);
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
// Log error but don't throw - this is fire-and-forget
|
|
158
|
+
console.error('⚠️ Failed to sync events to cloud:', error.message);
|
|
159
|
+
return {
|
|
160
|
+
success: false,
|
|
161
|
+
synced: 0,
|
|
162
|
+
skipped: batch.length,
|
|
163
|
+
error: error.message,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Force sync all pending events (useful for shutdown)
|
|
169
|
+
*/
|
|
170
|
+
async flush() {
|
|
171
|
+
if (this.syncTimer) {
|
|
172
|
+
clearTimeout(this.syncTimer);
|
|
173
|
+
this.syncTimer = null;
|
|
174
|
+
}
|
|
175
|
+
// Sync all remaining events
|
|
176
|
+
const results = [];
|
|
177
|
+
while (this.syncQueue.length > 0) {
|
|
178
|
+
const result = await this.syncBatch();
|
|
179
|
+
results.push(result);
|
|
180
|
+
}
|
|
181
|
+
// Aggregate results
|
|
182
|
+
const totalSynced = results.reduce((sum, r) => sum + r.synced, 0);
|
|
183
|
+
const totalSkipped = results.reduce((sum, r) => sum + r.skipped, 0);
|
|
184
|
+
const allSuccess = results.every(r => r.success);
|
|
185
|
+
return {
|
|
186
|
+
success: allSuccess,
|
|
187
|
+
synced: totalSynced,
|
|
188
|
+
skipped: totalSkipped,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Check if syncer is configured (has API key)
|
|
193
|
+
*/
|
|
194
|
+
isConfigured() {
|
|
195
|
+
return this.apiKey !== null;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Reload API key from config (useful if user logs in after watch starts)
|
|
199
|
+
*/
|
|
200
|
+
reloadConfig() {
|
|
201
|
+
const config = (0, config_1.loadConfig)();
|
|
202
|
+
this.apiUrl = config.apiUrl || config_1.DEFAULT_API_URL;
|
|
203
|
+
this.apiKey = (0, config_1.getApiKey)();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
exports.Syncer = Syncer;
|
|
207
|
+
//# sourceMappingURL=Syncer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Syncer.js","sourceRoot":"","sources":["../../../src/services/watch/Syncer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Restore a file from a blob hash
|
|
3
|
+
*
|
|
4
|
+
* Reads a GZIP-compressed blob from .neurcode/blobs/<hash> and
|
|
5
|
+
* writes it to the target file path.
|
|
6
|
+
*
|
|
7
|
+
* @param hash - SHA-256 hash of the file content
|
|
8
|
+
* @param targetPath - Relative path to the file to restore (e.g., "src/components/Button.tsx")
|
|
9
|
+
* @param projectRoot - Root directory of the project
|
|
10
|
+
* @returns Promise that resolves when the file is restored
|
|
11
|
+
* @throws Error if the blob doesn't exist, path is invalid, or write fails
|
|
12
|
+
*/
|
|
13
|
+
export declare function restoreFile(hash: string, targetPath: string, projectRoot: string): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=restore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../../src/utils/restore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAyCf"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Restore a file from a blob hash
|
|
4
|
+
*
|
|
5
|
+
* Reads a GZIP-compressed blob from .neurcode/blobs/<hash> and
|
|
6
|
+
* writes it to the target file path.
|
|
7
|
+
*
|
|
8
|
+
* @param hash - SHA-256 hash of the file content
|
|
9
|
+
* @param targetPath - Relative path to the file to restore (e.g., "src/components/Button.tsx")
|
|
10
|
+
* @param projectRoot - Root directory of the project
|
|
11
|
+
* @returns Promise that resolves when the file is restored
|
|
12
|
+
* @throws Error if the blob doesn't exist, path is invalid, or write fails
|
|
13
|
+
*/
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.restoreFile = restoreFile;
|
|
49
|
+
const zlib_1 = require("zlib");
|
|
50
|
+
const fs_1 = require("fs");
|
|
51
|
+
const path = __importStar(require("path"));
|
|
52
|
+
const fsExtra = __importStar(require("fs-extra"));
|
|
53
|
+
async function restoreFile(hash, targetPath, projectRoot) {
|
|
54
|
+
// Resolve the target file path relative to project root
|
|
55
|
+
const resolvedTargetPath = path.resolve(projectRoot, targetPath);
|
|
56
|
+
// Security: Ensure the target path is within the project root
|
|
57
|
+
// This prevents path traversal attacks (e.g., ../../../etc/passwd)
|
|
58
|
+
const resolvedProjectRoot = path.resolve(projectRoot);
|
|
59
|
+
if (!resolvedTargetPath.startsWith(resolvedProjectRoot)) {
|
|
60
|
+
throw new Error(`Invalid file path: ${targetPath} - Path traversal detected`);
|
|
61
|
+
}
|
|
62
|
+
// Resolve blob path
|
|
63
|
+
const blobPath = path.join(projectRoot, '.neurcode', 'blobs', hash);
|
|
64
|
+
// Check if blob exists
|
|
65
|
+
try {
|
|
66
|
+
await fs_1.promises.access(blobPath);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
throw new Error(`Blob not found: ${hash}`);
|
|
70
|
+
}
|
|
71
|
+
// Read the compressed blob
|
|
72
|
+
const compressedData = await fs_1.promises.readFile(blobPath);
|
|
73
|
+
// Decompress using GZIP
|
|
74
|
+
let decompressedData;
|
|
75
|
+
try {
|
|
76
|
+
decompressedData = (0, zlib_1.gunzipSync)(compressedData);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
throw new Error(`Failed to decompress blob: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
80
|
+
}
|
|
81
|
+
// Convert to string (assuming UTF-8 encoding)
|
|
82
|
+
const fileContent = decompressedData.toString('utf-8');
|
|
83
|
+
// Ensure the target directory exists
|
|
84
|
+
const targetDir = path.dirname(resolvedTargetPath);
|
|
85
|
+
await fsExtra.ensureDir(targetDir);
|
|
86
|
+
// Write the file
|
|
87
|
+
await fs_1.promises.writeFile(resolvedTargetPath, fileContent, 'utf-8');
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=restore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restore.js","sourceRoot":"","sources":["../../src/utils/restore.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOH,kCA6CC;AAlDD,+BAAkC;AAClC,2BAAoC;AACpC,2CAA6B;AAC7B,kDAAoC;AAE7B,KAAK,UAAU,WAAW,CAC/B,IAAY,EACZ,UAAkB,EAClB,WAAmB;IAEnB,wDAAwD;IACxD,MAAM,kBAAkB,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IAEjE,8DAA8D;IAC9D,mEAAmE;IACnE,MAAM,mBAAmB,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACtD,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,sBAAsB,UAAU,4BAA4B,CAAC,CAAC;IAChF,CAAC;IAED,oBAAoB;IACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAEpE,uBAAuB;IACvB,IAAI,CAAC;QACH,MAAM,aAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,2BAA2B;IAC3B,MAAM,cAAc,GAAG,MAAM,aAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAEnD,wBAAwB;IACxB,IAAI,gBAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,gBAAgB,GAAG,IAAA,iBAAU,EAAC,cAAc,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;IAC5G,CAAC;IAED,8CAA8C;IAC9C,MAAM,WAAW,GAAG,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAEvD,qCAAqC;IACrC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACnD,MAAM,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAEnC,iBAAiB;IACjB,MAAM,aAAE,CAAC,SAAS,CAAC,kBAAkB,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;AAC/D,CAAC"}
|