nstantpage-agent 0.3.2 → 0.3.4
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/start.js +53 -0
- package/dist/tunnel.d.ts +2 -0
- package/dist/tunnel.js +11 -2
- package/package.json +1 -1
package/dist/commands/start.js
CHANGED
|
@@ -19,6 +19,7 @@ import chalk from 'chalk';
|
|
|
19
19
|
import path from 'path';
|
|
20
20
|
import fs from 'fs';
|
|
21
21
|
import os from 'os';
|
|
22
|
+
import { execSync } from 'child_process';
|
|
22
23
|
import { getConfig } from '../config.js';
|
|
23
24
|
import { TunnelClient } from '../tunnel.js';
|
|
24
25
|
import { LocalServer } from '../localServer.js';
|
|
@@ -97,6 +98,54 @@ async function fetchProjectFiles(backendUrl, projectId, projectDir, token) {
|
|
|
97
98
|
console.log(chalk.green(` ✓ ${written} files downloaded (version ${data.version})`));
|
|
98
99
|
return { fileCount: written, isNew: true };
|
|
99
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Kill any process listening on the given port.
|
|
103
|
+
*/
|
|
104
|
+
function killPort(port) {
|
|
105
|
+
try {
|
|
106
|
+
// Works on macOS and Linux
|
|
107
|
+
const pids = execSync(`lsof -ti :${port} 2>/dev/null`, { encoding: 'utf-8' }).trim();
|
|
108
|
+
if (pids) {
|
|
109
|
+
for (const pid of pids.split('\n')) {
|
|
110
|
+
const p = parseInt(pid.trim(), 10);
|
|
111
|
+
if (p && p !== process.pid) {
|
|
112
|
+
try {
|
|
113
|
+
process.kill(p, 'SIGTERM');
|
|
114
|
+
}
|
|
115
|
+
catch { }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// lsof not available or no process found — that's fine
|
|
122
|
+
try {
|
|
123
|
+
// Fallback for systems without lsof
|
|
124
|
+
execSync(`fuser -k ${port}/tcp 2>/dev/null`, { encoding: 'utf-8' });
|
|
125
|
+
}
|
|
126
|
+
catch { }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Clean up any previous agent instance (kill old PID + free ports).
|
|
131
|
+
*/
|
|
132
|
+
function cleanupPreviousAgent(conf, apiPort, devPort) {
|
|
133
|
+
// 1. Try to kill the previously stored agent PID
|
|
134
|
+
const oldPid = conf.get('agentPid');
|
|
135
|
+
if (oldPid && oldPid !== process.pid) {
|
|
136
|
+
try {
|
|
137
|
+
process.kill(oldPid, 'SIGTERM');
|
|
138
|
+
console.log(chalk.gray(` Stopped previous agent (PID ${oldPid})`));
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Already dead
|
|
142
|
+
}
|
|
143
|
+
conf.delete('agentPid');
|
|
144
|
+
}
|
|
145
|
+
// 2. Free the ports in case orphaned processes are still holding them
|
|
146
|
+
killPort(apiPort);
|
|
147
|
+
killPort(devPort);
|
|
148
|
+
}
|
|
100
149
|
export async function startCommand(directory, options) {
|
|
101
150
|
const conf = getConfig();
|
|
102
151
|
// If --token was passed, persist it
|
|
@@ -134,6 +183,10 @@ export async function startCommand(directory, options) {
|
|
|
134
183
|
// Save project ID
|
|
135
184
|
conf.set('projectId', projectId);
|
|
136
185
|
const backendUrl = resolveBackendUrl(options);
|
|
186
|
+
// Kill any leftover agent / free ports from a previous run
|
|
187
|
+
cleanupPreviousAgent(conf, apiPort, devPort);
|
|
188
|
+
// Small delay to let ports release
|
|
189
|
+
await new Promise(r => setTimeout(r, 300));
|
|
137
190
|
console.log(chalk.blue(`\n🚀 nstantpage agent v${VERSION}\n`));
|
|
138
191
|
console.log(chalk.gray(` Project ID: ${projectId}`));
|
|
139
192
|
console.log(chalk.gray(` Directory: ${projectDir}`));
|
package/dist/tunnel.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export declare class TunnelClient {
|
|
|
45
45
|
/**
|
|
46
46
|
* Start reconnecting in the background (non-blocking).
|
|
47
47
|
* Used when initial connect() fails so the dev server can keep running.
|
|
48
|
+
* The close handler from the failed connect() already schedules a reconnect,
|
|
49
|
+
* so we just ensure shouldReconnect is true and don't duplicate.
|
|
48
50
|
*/
|
|
49
51
|
startBackgroundReconnect(): void;
|
|
50
52
|
disconnect(): void;
|
package/dist/tunnel.js
CHANGED
|
@@ -106,11 +106,17 @@ export class TunnelClient {
|
|
|
106
106
|
/**
|
|
107
107
|
* Start reconnecting in the background (non-blocking).
|
|
108
108
|
* Used when initial connect() fails so the dev server can keep running.
|
|
109
|
+
* The close handler from the failed connect() already schedules a reconnect,
|
|
110
|
+
* so we just ensure shouldReconnect is true and don't duplicate.
|
|
109
111
|
*/
|
|
110
112
|
startBackgroundReconnect() {
|
|
111
113
|
this.shouldReconnect = true;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
// Only kick off a reconnect if there isn't one already pending
|
|
115
|
+
// (the close handler from the failed connect() already scheduled one)
|
|
116
|
+
if (!this.reconnectTimer) {
|
|
117
|
+
this.reconnectAttempts = 0;
|
|
118
|
+
this.scheduleReconnect();
|
|
119
|
+
}
|
|
114
120
|
}
|
|
115
121
|
disconnect() {
|
|
116
122
|
this.shouldReconnect = false;
|
|
@@ -235,6 +241,8 @@ export class TunnelClient {
|
|
|
235
241
|
scheduleReconnect() {
|
|
236
242
|
if (!this.shouldReconnect)
|
|
237
243
|
return;
|
|
244
|
+
if (this.reconnectTimer)
|
|
245
|
+
return; // Already have a pending reconnect
|
|
238
246
|
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
239
247
|
console.error(' [Tunnel] Max reconnect attempts reached. Giving up.');
|
|
240
248
|
return;
|
|
@@ -243,6 +251,7 @@ export class TunnelClient {
|
|
|
243
251
|
this.reconnectAttempts++;
|
|
244
252
|
console.log(` [Tunnel] Reconnecting in ${delay / 1000}s (attempt ${this.reconnectAttempts})...`);
|
|
245
253
|
this.reconnectTimer = setTimeout(async () => {
|
|
254
|
+
this.reconnectTimer = null; // Clear so next scheduleReconnect can fire
|
|
246
255
|
try {
|
|
247
256
|
await this.connect();
|
|
248
257
|
console.log(' [Tunnel] Reconnected!');
|
package/package.json
CHANGED