nstantpage-agent 0.5.23 → 0.5.25
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/agentSync.js +3 -3
- package/dist/commands/start.js +10 -4
- package/dist/localServer.d.ts +5 -0
- package/dist/localServer.js +10 -0
- package/dist/tunnel.d.ts +10 -4
- package/dist/tunnel.js +93 -7
- package/package.json +1 -1
package/dist/agentSync.js
CHANGED
|
@@ -148,11 +148,11 @@ export class AgentSync {
|
|
|
148
148
|
}
|
|
149
149
|
// ─── Disk Dirty State ─────────────────────────────────────
|
|
150
150
|
markDiskDirty() {
|
|
151
|
-
const wasDirty = this.diskDirty;
|
|
152
151
|
this.diskDirty = true;
|
|
153
152
|
this.diskChecksumCache = null; // Force recompute
|
|
154
|
-
//
|
|
155
|
-
|
|
153
|
+
// Notify on every change — the debounce timer in the file watcher
|
|
154
|
+
// already batches rapid changes, so this fires at most once per debounce window.
|
|
155
|
+
if (this.onSyncDirty) {
|
|
156
156
|
this.onSyncDirty(this.projectId);
|
|
157
157
|
}
|
|
158
158
|
}
|
package/dist/commands/start.js
CHANGED
|
@@ -26,7 +26,7 @@ import { LocalServer } from '../localServer.js';
|
|
|
26
26
|
import { PackageInstaller } from '../packageInstaller.js';
|
|
27
27
|
import { probeLocalPostgres, ensureLocalProjectDb, closeAdminPool, writeDatabaseUrlToEnv } from '../projectDb.js';
|
|
28
28
|
import { StatusServer } from '../statusServer.js';
|
|
29
|
-
const VERSION = '0.5.
|
|
29
|
+
const VERSION = '0.5.24';
|
|
30
30
|
/**
|
|
31
31
|
* Resolve the backend API base URL.
|
|
32
32
|
* - If --backend is passed, use it
|
|
@@ -82,7 +82,7 @@ async function fetchProjectFiles(backendUrl, projectId, projectDir, token) {
|
|
|
82
82
|
: null;
|
|
83
83
|
if (existingVersion === String(data.versionId)) {
|
|
84
84
|
console.log(chalk.gray(` Files up-to-date (version ${data.version})`));
|
|
85
|
-
return { fileCount: data.files.length, isNew: false };
|
|
85
|
+
return { fileCount: data.files.length, isNew: false, versionId: String(data.versionId) };
|
|
86
86
|
}
|
|
87
87
|
// Write all files to disk in parallel (batch I/O for speed)
|
|
88
88
|
// First, collect unique directories to create
|
|
@@ -111,7 +111,7 @@ async function fetchProjectFiles(backendUrl, projectId, projectDir, token) {
|
|
|
111
111
|
// Write version marker
|
|
112
112
|
fs.writeFileSync(versionFile, String(data.versionId), 'utf-8');
|
|
113
113
|
console.log(chalk.green(` ✓ ${written} files downloaded (version ${data.version})`));
|
|
114
|
-
return { fileCount: written, isNew: true };
|
|
114
|
+
return { fileCount: written, isNew: true, versionId: String(data.versionId) };
|
|
115
115
|
}
|
|
116
116
|
/**
|
|
117
117
|
* Kill any process listening on the given port.
|
|
@@ -242,8 +242,10 @@ export async function startCommand(directory, options) {
|
|
|
242
242
|
console.log(chalk.gray(` Backend: ${backendUrl}\n`));
|
|
243
243
|
// 1. Fetch project files from the backend
|
|
244
244
|
const installer = new PackageInstaller({ projectDir });
|
|
245
|
+
let fetchedVersionId;
|
|
245
246
|
try {
|
|
246
|
-
const { fileCount, isNew } = await fetchProjectFiles(backendUrl, projectId, projectDir, token);
|
|
247
|
+
const { fileCount, isNew, versionId } = await fetchProjectFiles(backendUrl, projectId, projectDir, token);
|
|
248
|
+
fetchedVersionId = versionId;
|
|
247
249
|
// 2. Install dependencies if needed (verifies actual packages, not just folder)
|
|
248
250
|
if (!installer.areDependenciesInstalled()) {
|
|
249
251
|
if (fs.existsSync(path.join(projectDir, 'package.json'))) {
|
|
@@ -424,6 +426,10 @@ export async function startCommand(directory, options) {
|
|
|
424
426
|
console.log(chalk.gray(' Starting local API server...'));
|
|
425
427
|
await localServer.start();
|
|
426
428
|
console.log(chalk.green(` ✓ API server on port ${apiPort}`));
|
|
429
|
+
// Set sync baseline so file watcher knows what "clean" looks like
|
|
430
|
+
if (fetchedVersionId) {
|
|
431
|
+
localServer.markSynced(fetchedVersionId);
|
|
432
|
+
}
|
|
427
433
|
// Start dev server unless --no-dev flag
|
|
428
434
|
if (!options.noDev) {
|
|
429
435
|
if (installer.areDependenciesInstalled()) {
|
package/dist/localServer.d.ts
CHANGED
|
@@ -81,6 +81,11 @@ export declare class LocalServer {
|
|
|
81
81
|
*/
|
|
82
82
|
private ensureDatabase;
|
|
83
83
|
getDevServer(): DevServer;
|
|
84
|
+
/**
|
|
85
|
+
* Set initial sync baseline after fetching project files.
|
|
86
|
+
* This tells agentSync what "clean" looks like so it can detect local edits.
|
|
87
|
+
*/
|
|
88
|
+
markSynced(versionId: string): void;
|
|
84
89
|
getApiPort(): number;
|
|
85
90
|
getDevPort(): number;
|
|
86
91
|
start(): Promise<void>;
|
package/dist/localServer.js
CHANGED
|
@@ -162,6 +162,16 @@ export class LocalServer {
|
|
|
162
162
|
getDevServer() {
|
|
163
163
|
return this.devServer;
|
|
164
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Set initial sync baseline after fetching project files.
|
|
167
|
+
* This tells agentSync what "clean" looks like so it can detect local edits.
|
|
168
|
+
*/
|
|
169
|
+
markSynced(versionId) {
|
|
170
|
+
if (this.agentSync) {
|
|
171
|
+
this.agentSync.markSynced(versionId);
|
|
172
|
+
console.log(` [LocalServer] Sync baseline set (version ${versionId})`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
165
175
|
getApiPort() {
|
|
166
176
|
return this.options.apiPort;
|
|
167
177
|
}
|
package/dist/tunnel.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export declare class TunnelClient {
|
|
|
42
42
|
private requestsForwarded;
|
|
43
43
|
private connectedAt;
|
|
44
44
|
private lastPongReceived;
|
|
45
|
-
/** Active WebSocket channels (terminal WS relay through tunnel) */
|
|
45
|
+
/** Active WebSocket channels (terminal WS + HMR WS relay through tunnel) */
|
|
46
46
|
private wsChannels;
|
|
47
47
|
/** Listeners for status changes (used by tray app) */
|
|
48
48
|
private statusListeners;
|
|
@@ -87,14 +87,20 @@ export declare class TunnelClient {
|
|
|
87
87
|
*/
|
|
88
88
|
private handleWsOpen;
|
|
89
89
|
/**
|
|
90
|
-
* Handle ws-data: frontend sent a message through
|
|
91
|
-
*
|
|
90
|
+
* Handle ws-data: frontend sent a message through a WebSocket channel.
|
|
91
|
+
* Routes to either terminal session or dev-server WebSocket.
|
|
92
92
|
*/
|
|
93
93
|
private handleWsData;
|
|
94
94
|
/**
|
|
95
|
-
* Handle ws-close: frontend disconnected the terminal WebSocket.
|
|
95
|
+
* Handle ws-close: frontend disconnected the terminal WebSocket or HMR WebSocket.
|
|
96
96
|
*/
|
|
97
97
|
private handleWsClose;
|
|
98
|
+
/**
|
|
99
|
+
* Handle ws-open for dev-server target: open a WebSocket to the local
|
|
100
|
+
* Vite dev server and relay data bidirectionally through the tunnel.
|
|
101
|
+
* This enables Vite HMR to work through the cloud preview URL.
|
|
102
|
+
*/
|
|
103
|
+
private handleWsOpenDevServer;
|
|
98
104
|
/**
|
|
99
105
|
* Handle start-project: gateway wants this agent to start serving a new project.
|
|
100
106
|
* Called when user clicks "Connect" in the web editor's Cloud panel.
|
package/dist/tunnel.js
CHANGED
|
@@ -32,7 +32,7 @@ export class TunnelClient {
|
|
|
32
32
|
requestsForwarded = 0;
|
|
33
33
|
connectedAt = 0;
|
|
34
34
|
lastPongReceived = 0;
|
|
35
|
-
/** Active WebSocket channels (terminal WS relay through tunnel) */
|
|
35
|
+
/** Active WebSocket channels (terminal WS + HMR WS relay through tunnel) */
|
|
36
36
|
wsChannels = new Map();
|
|
37
37
|
/** Listeners for status changes (used by tray app) */
|
|
38
38
|
statusListeners = new Set();
|
|
@@ -267,13 +267,31 @@ export class TunnelClient {
|
|
|
267
267
|
});
|
|
268
268
|
}
|
|
269
269
|
/**
|
|
270
|
-
* Handle ws-data: frontend sent a message through
|
|
271
|
-
*
|
|
270
|
+
* Handle ws-data: frontend sent a message through a WebSocket channel.
|
|
271
|
+
* Routes to either terminal session or dev-server WebSocket.
|
|
272
272
|
*/
|
|
273
|
-
handleWsData(wsId, data) {
|
|
273
|
+
handleWsData(wsId, data, binary) {
|
|
274
274
|
const channel = this.wsChannels.get(wsId);
|
|
275
275
|
if (!channel)
|
|
276
276
|
return;
|
|
277
|
+
// If this channel has a dev-server WebSocket, forward data directly
|
|
278
|
+
if (channel.devServerWs) {
|
|
279
|
+
try {
|
|
280
|
+
if (channel.devServerWs.readyState === WebSocket.OPEN) {
|
|
281
|
+
if (binary) {
|
|
282
|
+
channel.devServerWs.send(Buffer.from(data, 'base64'));
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
channel.devServerWs.send(data);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
console.warn(` [Tunnel] Failed to forward HMR data to dev server:`, err.message);
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
// Terminal session handling
|
|
277
295
|
try {
|
|
278
296
|
const msg = JSON.parse(data);
|
|
279
297
|
const session = getTerminalSession(channel.sessionId);
|
|
@@ -298,16 +316,79 @@ export class TunnelClient {
|
|
|
298
316
|
}
|
|
299
317
|
}
|
|
300
318
|
/**
|
|
301
|
-
* Handle ws-close: frontend disconnected the terminal WebSocket.
|
|
319
|
+
* Handle ws-close: frontend disconnected the terminal WebSocket or HMR WebSocket.
|
|
302
320
|
*/
|
|
303
321
|
handleWsClose(wsId) {
|
|
304
322
|
const channel = this.wsChannels.get(wsId);
|
|
305
323
|
if (channel) {
|
|
306
324
|
if (channel.cleanup)
|
|
307
325
|
channel.cleanup();
|
|
326
|
+
// Close dev-server WebSocket if this was an HMR relay
|
|
327
|
+
if (channel.devServerWs) {
|
|
328
|
+
try {
|
|
329
|
+
channel.devServerWs.close();
|
|
330
|
+
}
|
|
331
|
+
catch { }
|
|
332
|
+
}
|
|
308
333
|
this.wsChannels.delete(wsId);
|
|
309
334
|
}
|
|
310
335
|
}
|
|
336
|
+
// ─── Dev Server WebSocket Relay (Vite HMR through tunnel) ──
|
|
337
|
+
/**
|
|
338
|
+
* Handle ws-open for dev-server target: open a WebSocket to the local
|
|
339
|
+
* Vite dev server and relay data bidirectionally through the tunnel.
|
|
340
|
+
* This enables Vite HMR to work through the cloud preview URL.
|
|
341
|
+
*/
|
|
342
|
+
handleWsOpenDevServer(wsId, url, protocol) {
|
|
343
|
+
const devPort = this.options.devPort;
|
|
344
|
+
const wsUrl = `ws://127.0.0.1:${devPort}${url}`;
|
|
345
|
+
console.log(` [Tunnel] Opening HMR WS relay: ${wsId} → ${wsUrl}`);
|
|
346
|
+
const wsOptions = {};
|
|
347
|
+
const protocols = protocol ? protocol.split(',').map(p => p.trim()) : undefined;
|
|
348
|
+
const devWs = new WebSocket(wsUrl, protocols, wsOptions);
|
|
349
|
+
devWs.on('open', () => {
|
|
350
|
+
console.log(` [Tunnel] HMR WS connected: ${wsId} → ${wsUrl}`);
|
|
351
|
+
});
|
|
352
|
+
devWs.on('message', (data, isBinary) => {
|
|
353
|
+
// Relay dev server messages back to browser through tunnel
|
|
354
|
+
if (isBinary) {
|
|
355
|
+
const buf = data instanceof Buffer ? data : Buffer.from(data);
|
|
356
|
+
this.send({
|
|
357
|
+
type: 'ws-data',
|
|
358
|
+
wsId,
|
|
359
|
+
data: buf.toString('base64'),
|
|
360
|
+
binary: true,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
this.send({
|
|
365
|
+
type: 'ws-data',
|
|
366
|
+
wsId,
|
|
367
|
+
data: data.toString(),
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
devWs.on('close', () => {
|
|
372
|
+
console.log(` [Tunnel] HMR WS closed: ${wsId}`);
|
|
373
|
+
this.send({ type: 'ws-close', wsId });
|
|
374
|
+
this.wsChannels.delete(wsId);
|
|
375
|
+
});
|
|
376
|
+
devWs.on('error', (err) => {
|
|
377
|
+
console.warn(` [Tunnel] HMR WS error: ${wsId}:`, err.message);
|
|
378
|
+
this.send({ type: 'ws-close', wsId });
|
|
379
|
+
this.wsChannels.delete(wsId);
|
|
380
|
+
});
|
|
381
|
+
this.wsChannels.set(wsId, {
|
|
382
|
+
sessionId: `hmr-${wsId}`,
|
|
383
|
+
cleanup: () => {
|
|
384
|
+
try {
|
|
385
|
+
devWs.close();
|
|
386
|
+
}
|
|
387
|
+
catch { }
|
|
388
|
+
},
|
|
389
|
+
devServerWs: devWs,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
311
392
|
/**
|
|
312
393
|
* Handle start-project: gateway wants this agent to start serving a new project.
|
|
313
394
|
* Called when user clicks "Connect" in the web editor's Cloud panel.
|
|
@@ -369,10 +450,15 @@ export class TunnelClient {
|
|
|
369
450
|
this.handleHttpRequest(msg);
|
|
370
451
|
break;
|
|
371
452
|
case 'ws-open':
|
|
372
|
-
|
|
453
|
+
if (msg.target === 'dev-server') {
|
|
454
|
+
this.handleWsOpenDevServer(msg.wsId, msg.url || '/', msg.protocol);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
this.handleWsOpen(msg.wsId, msg.sessionId, msg.projectId);
|
|
458
|
+
}
|
|
373
459
|
break;
|
|
374
460
|
case 'ws-data':
|
|
375
|
-
this.handleWsData(msg.wsId, msg.data);
|
|
461
|
+
this.handleWsData(msg.wsId, msg.data, msg.binary);
|
|
376
462
|
break;
|
|
377
463
|
case 'ws-close':
|
|
378
464
|
this.handleWsClose(msg.wsId);
|
package/package.json
CHANGED