nstantpage-agent 0.5.7 ā 0.5.9
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/cli.js +1 -1
- package/dist/commands/start.js +90 -61
- package/dist/devServer.js +4 -3
- package/dist/localServer.js +3 -2
- package/dist/packageInstaller.d.ts +15 -0
- package/dist/packageInstaller.js +77 -2
- package/dist/tunnel.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -25,7 +25,7 @@ const program = new Command();
|
|
|
25
25
|
program
|
|
26
26
|
.name('nstantpage')
|
|
27
27
|
.description('Local development agent for nstantpage.com ā run projects on your machine, preview in the cloud')
|
|
28
|
-
.version('0.5.
|
|
28
|
+
.version('0.5.8');
|
|
29
29
|
program
|
|
30
30
|
.command('login')
|
|
31
31
|
.description('Authenticate with nstantpage.com')
|
package/dist/commands/start.js
CHANGED
|
@@ -24,7 +24,7 @@ import { getConfig, getProjectConfig, setProjectConfig, clearProjectConfig, getD
|
|
|
24
24
|
import { TunnelClient } from '../tunnel.js';
|
|
25
25
|
import { LocalServer } from '../localServer.js';
|
|
26
26
|
import { PackageInstaller } from '../packageInstaller.js';
|
|
27
|
-
const VERSION = '0.5.
|
|
27
|
+
const VERSION = '0.5.8';
|
|
28
28
|
/**
|
|
29
29
|
* Resolve the backend API base URL.
|
|
30
30
|
* - If --backend is passed, use it
|
|
@@ -82,16 +82,29 @@ async function fetchProjectFiles(backendUrl, projectId, projectDir, token) {
|
|
|
82
82
|
console.log(chalk.gray(` Files up-to-date (version ${data.version})`));
|
|
83
83
|
return { fileCount: data.files.length, isNew: false };
|
|
84
84
|
}
|
|
85
|
-
// Write all files to disk
|
|
86
|
-
|
|
85
|
+
// Write all files to disk in parallel (batch I/O for speed)
|
|
86
|
+
// First, collect unique directories to create
|
|
87
|
+
const dirsToCreate = new Set();
|
|
87
88
|
for (const file of data.files) {
|
|
88
89
|
const filePath = path.join(projectDir, file.path);
|
|
89
|
-
|
|
90
|
+
dirsToCreate.add(path.dirname(filePath));
|
|
91
|
+
}
|
|
92
|
+
// Create all directories first (sync is fine, there are few unique dirs)
|
|
93
|
+
for (const dir of dirsToCreate) {
|
|
90
94
|
if (!fs.existsSync(dir)) {
|
|
91
95
|
fs.mkdirSync(dir, { recursive: true });
|
|
92
96
|
}
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
}
|
|
98
|
+
// Write files in parallel batches of 50
|
|
99
|
+
const BATCH_SIZE = 50;
|
|
100
|
+
let written = 0;
|
|
101
|
+
for (let i = 0; i < data.files.length; i += BATCH_SIZE) {
|
|
102
|
+
const batch = data.files.slice(i, i + BATCH_SIZE);
|
|
103
|
+
await Promise.all(batch.map(async (file) => {
|
|
104
|
+
const filePath = path.join(projectDir, file.path);
|
|
105
|
+
await fs.promises.writeFile(filePath, file.content, 'utf-8');
|
|
106
|
+
}));
|
|
107
|
+
written += batch.length;
|
|
95
108
|
}
|
|
96
109
|
// Write version marker
|
|
97
110
|
fs.writeFileSync(versionFile, String(data.versionId), 'utf-8');
|
|
@@ -215,8 +228,8 @@ export async function startCommand(directory, options) {
|
|
|
215
228
|
conf.set('projectId', projectId);
|
|
216
229
|
// Kill any leftover agent for THIS PROJECT only (not other projects)
|
|
217
230
|
cleanupPreviousAgent(projectId, apiPort, devPort);
|
|
218
|
-
//
|
|
219
|
-
await new Promise(r => setTimeout(r,
|
|
231
|
+
// Brief pause for OS to release ports (50ms is sufficient on macOS/Linux)
|
|
232
|
+
await new Promise(r => setTimeout(r, 50));
|
|
220
233
|
console.log(chalk.blue(`\nš nstantpage agent v${VERSION}\n`));
|
|
221
234
|
console.log(chalk.gray(` Project ID: ${projectId}`));
|
|
222
235
|
console.log(chalk.gray(` Device ID: ${deviceId.slice(0, 12)}...`));
|
|
@@ -558,57 +571,91 @@ async function startStandbyMode(token, options, backendUrl, deviceId) {
|
|
|
558
571
|
*/
|
|
559
572
|
async function startAdditionalProject(projectId, opts) {
|
|
560
573
|
const progress = opts.onProgress || (() => { });
|
|
574
|
+
const timings = {};
|
|
575
|
+
const t0 = Date.now();
|
|
561
576
|
console.log(chalk.blue(`\n š¦ Starting project ${projectId}...`));
|
|
562
|
-
progress('fetching-files', 'Fetching project files...');
|
|
563
577
|
try {
|
|
564
578
|
const allocated = allocatePortsForProject(projectId);
|
|
565
579
|
const projectDir = resolveProjectDir('.', projectId);
|
|
566
580
|
if (!fs.existsSync(projectDir))
|
|
567
581
|
fs.mkdirSync(projectDir, { recursive: true });
|
|
568
|
-
//
|
|
582
|
+
// Ensure ports are clear for this project before starting
|
|
583
|
+
cleanupPreviousAgent(projectId, allocated.apiPort, allocated.devPort);
|
|
584
|
+
await new Promise(r => setTimeout(r, 50));
|
|
585
|
+
// āā Phase 1: API server + file fetch + tunnel connect (all parallel) āā
|
|
586
|
+
progress('fetching-files', 'Fetching project files...');
|
|
587
|
+
const localServer = new LocalServer({
|
|
588
|
+
projectDir, projectId,
|
|
589
|
+
apiPort: allocated.apiPort, devPort: allocated.devPort,
|
|
590
|
+
});
|
|
591
|
+
const tunnel = new TunnelClient({
|
|
592
|
+
gatewayUrl: opts.gatewayUrl,
|
|
593
|
+
token: opts.token,
|
|
594
|
+
projectId,
|
|
595
|
+
apiPort: allocated.apiPort,
|
|
596
|
+
devPort: allocated.devPort,
|
|
597
|
+
});
|
|
598
|
+
// Launch API server, tunnel, and file fetch all in parallel
|
|
599
|
+
const apiServerPromise = localServer.start().then(() => {
|
|
600
|
+
timings['api-server'] = Date.now() - t0;
|
|
601
|
+
console.log(chalk.green(` ā API server on port ${allocated.apiPort} (${timings['api-server']}ms)`));
|
|
602
|
+
});
|
|
603
|
+
const tunnelPromise = (async () => {
|
|
604
|
+
try {
|
|
605
|
+
await tunnel.connect();
|
|
606
|
+
timings['tunnel'] = Date.now() - t0;
|
|
607
|
+
console.log(chalk.green(` ā Tunnel connected for project ${projectId} (${timings['tunnel']}ms)`));
|
|
608
|
+
}
|
|
609
|
+
catch (err) {
|
|
610
|
+
console.log(chalk.yellow(` ā Tunnel: ${err.message}`));
|
|
611
|
+
tunnel.startBackgroundReconnect();
|
|
612
|
+
}
|
|
613
|
+
})();
|
|
614
|
+
const fileStart = Date.now();
|
|
569
615
|
try {
|
|
570
616
|
await fetchProjectFiles(opts.backendUrl, projectId, projectDir, opts.token);
|
|
571
|
-
|
|
617
|
+
timings['fetch-files'] = Date.now() - fileStart;
|
|
618
|
+
progress('fetching-files', `Files downloaded (${timings['fetch-files']}ms)`);
|
|
572
619
|
}
|
|
573
620
|
catch (err) {
|
|
574
621
|
console.log(chalk.yellow(` ā Could not fetch files: ${err.message}`));
|
|
575
622
|
progress('fetching-files', `Warning: ${err.message}`);
|
|
576
623
|
}
|
|
577
|
-
//
|
|
624
|
+
// Wait for API server before proceeding (tunnel can keep connecting in background)
|
|
625
|
+
await apiServerPromise;
|
|
626
|
+
progress('starting-server', `API server ready`);
|
|
627
|
+
// āā Phase 2: Install deps (if needed) āāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
578
628
|
const installer = new PackageInstaller({ projectDir });
|
|
579
|
-
|
|
629
|
+
const needsInstall = !installer.areDependenciesInstalled() && fs.existsSync(path.join(projectDir, 'package.json'));
|
|
630
|
+
if (needsInstall) {
|
|
631
|
+
const installStart = Date.now();
|
|
580
632
|
console.log(chalk.gray(` Installing dependencies...`));
|
|
581
633
|
progress('installing-deps', 'Installing npm dependencies...');
|
|
582
634
|
try {
|
|
583
635
|
await installer.ensureDependencies();
|
|
584
|
-
|
|
585
|
-
|
|
636
|
+
timings['install'] = Date.now() - installStart;
|
|
637
|
+
console.log(chalk.green(` ā Dependencies installed (${timings['install']}ms)`));
|
|
638
|
+
progress('installing-deps', `Dependencies installed (${timings['install']}ms)`);
|
|
586
639
|
}
|
|
587
640
|
catch (err) {
|
|
641
|
+
timings['install'] = Date.now() - installStart;
|
|
588
642
|
console.log(chalk.red(` ā Install failed: ${err.message}`));
|
|
589
643
|
console.log(chalk.yellow(` ā Dev server may fail ā retrying install on first request`));
|
|
590
644
|
progress('installing-deps', `Install failed: ${err.message}`);
|
|
591
645
|
}
|
|
592
646
|
}
|
|
593
647
|
else {
|
|
594
|
-
progress('installing-deps', 'Dependencies already installed');
|
|
648
|
+
progress('installing-deps', 'Dependencies already installed (cached)');
|
|
595
649
|
}
|
|
596
|
-
// Start
|
|
597
|
-
progress('starting-server', 'Starting API server...');
|
|
598
|
-
const localServer = new LocalServer({
|
|
599
|
-
projectDir, projectId,
|
|
600
|
-
apiPort: allocated.apiPort, devPort: allocated.devPort,
|
|
601
|
-
});
|
|
602
|
-
await localServer.start();
|
|
603
|
-
console.log(chalk.green(` ā API server on port ${allocated.apiPort}`));
|
|
604
|
-
progress('starting-server', `API server on port ${allocated.apiPort}`);
|
|
605
|
-
// Start dev server (only if dependencies are installed)
|
|
650
|
+
// āā Phase 3: Start dev server āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
606
651
|
if (!opts.noDev) {
|
|
607
652
|
if (installer.areDependenciesInstalled()) {
|
|
608
653
|
progress('starting-dev', 'Starting dev server...');
|
|
609
654
|
try {
|
|
655
|
+
const devStart = Date.now();
|
|
610
656
|
await localServer.getDevServer().start();
|
|
611
|
-
|
|
657
|
+
timings['dev-server'] = Date.now() - devStart;
|
|
658
|
+
console.log(chalk.green(` ā Dev server on port ${allocated.devPort} (${timings['dev-server']}ms)`));
|
|
612
659
|
progress('starting-dev', `Dev server on port ${allocated.devPort}`);
|
|
613
660
|
}
|
|
614
661
|
catch (err) {
|
|
@@ -622,40 +669,22 @@ async function startAdditionalProject(projectId, opts) {
|
|
|
622
669
|
progress('starting-dev', 'Skipped ā dependencies not installed');
|
|
623
670
|
}
|
|
624
671
|
}
|
|
625
|
-
//
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
tunnel.startBackgroundReconnect();
|
|
642
|
-
progress('connecting', `Warning: ${err.message} ā reconnecting`);
|
|
643
|
-
}
|
|
644
|
-
// Register project with backend
|
|
645
|
-
try {
|
|
646
|
-
await fetch(`${opts.backendUrl}/api/agent/register`, {
|
|
647
|
-
method: 'POST',
|
|
648
|
-
headers: { 'Authorization': `Bearer ${opts.token}`, 'Content-Type': 'application/json' },
|
|
649
|
-
body: JSON.stringify({
|
|
650
|
-
deviceId: opts.deviceId, name: os.hostname(), hostname: os.hostname(),
|
|
651
|
-
platform: `${os.platform()} ${os.arch()}`, agentVersion: VERSION,
|
|
652
|
-
projectId, capabilities: ['file-sync', 'type-check', 'install', 'terminal', 'dev-server'],
|
|
653
|
-
}),
|
|
654
|
-
});
|
|
655
|
-
}
|
|
656
|
-
catch { }
|
|
657
|
-
console.log(chalk.green(` ā Project ${projectId} is live!\n`));
|
|
658
|
-
progress('ready', 'Project is live!');
|
|
672
|
+
// Ensure tunnel is connected before declaring ready
|
|
673
|
+
await tunnelPromise;
|
|
674
|
+
// Register project with backend (non-blocking)
|
|
675
|
+
fetch(`${opts.backendUrl}/api/agent/register`, {
|
|
676
|
+
method: 'POST',
|
|
677
|
+
headers: { 'Authorization': `Bearer ${opts.token}`, 'Content-Type': 'application/json' },
|
|
678
|
+
body: JSON.stringify({
|
|
679
|
+
deviceId: opts.deviceId, name: os.hostname(), hostname: os.hostname(),
|
|
680
|
+
platform: `${os.platform()} ${os.arch()}`, agentVersion: VERSION,
|
|
681
|
+
projectId, capabilities: ['file-sync', 'type-check', 'install', 'terminal', 'dev-server'],
|
|
682
|
+
}),
|
|
683
|
+
}).catch(() => { });
|
|
684
|
+
const totalTime = Date.now() - t0;
|
|
685
|
+
console.log(chalk.green(` ā Project ${projectId} is live! (total: ${totalTime}ms)\n`));
|
|
686
|
+
console.log(chalk.gray(` Timings: ${JSON.stringify(timings)}`));
|
|
687
|
+
progress('ready', `Project is live! (${totalTime}ms)`);
|
|
659
688
|
return { localServer, tunnel };
|
|
660
689
|
}
|
|
661
690
|
catch (err) {
|
package/dist/devServer.js
CHANGED
|
@@ -294,14 +294,15 @@ export class DevServer {
|
|
|
294
294
|
resolve();
|
|
295
295
|
});
|
|
296
296
|
req.on('error', () => {
|
|
297
|
-
setTimeout(check,
|
|
297
|
+
setTimeout(check, 300);
|
|
298
298
|
});
|
|
299
299
|
req.setTimeout(2000, () => {
|
|
300
300
|
req.destroy();
|
|
301
|
-
setTimeout(check,
|
|
301
|
+
setTimeout(check, 300);
|
|
302
302
|
});
|
|
303
303
|
};
|
|
304
|
-
|
|
304
|
+
// Start checking after a short delay (Vite can be ready in <500ms)
|
|
305
|
+
setTimeout(check, 300);
|
|
305
306
|
});
|
|
306
307
|
}
|
|
307
308
|
}
|
package/dist/localServer.js
CHANGED
|
@@ -203,7 +203,8 @@ export class LocalServer {
|
|
|
203
203
|
'/live/normalize': this.handleNormalize,
|
|
204
204
|
'/live/normalize-batch': this.handleNormalizeBatch,
|
|
205
205
|
'/live/invalidate': this.handleInvalidate,
|
|
206
|
-
'/live/refetch': this.handleRefetch,
|
|
206
|
+
'/live/refetch': this.handleRefetch, // Legacy ā no-op in agent mode
|
|
207
|
+
'/live/reset': this.handleRefetch, // New push-based reset ā also no-op in agent mode
|
|
207
208
|
'/live/grace-period': this.handleGracePeriod,
|
|
208
209
|
'/live/stats': this.handleStats,
|
|
209
210
|
'/live/usage': this.handleUsage,
|
|
@@ -591,7 +592,7 @@ export class LocalServer {
|
|
|
591
592
|
connected: true,
|
|
592
593
|
projectId: this.options.projectId,
|
|
593
594
|
agent: {
|
|
594
|
-
version: '0.5.
|
|
595
|
+
version: '0.5.8',
|
|
595
596
|
hostname: os.hostname(),
|
|
596
597
|
platform: `${os.platform()} ${os.arch()}`,
|
|
597
598
|
},
|
|
@@ -21,8 +21,23 @@ export declare class PackageInstaller {
|
|
|
21
21
|
/**
|
|
22
22
|
* Ensure all project dependencies are installed.
|
|
23
23
|
* Uses a lock to prevent concurrent installs.
|
|
24
|
+
* Skips install entirely if package.json + lockfile haven't changed (hash cache).
|
|
24
25
|
*/
|
|
25
26
|
ensureDependencies(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Build the fastest possible install command:
|
|
29
|
+
* - npm ci (clean install from lockfile) if package-lock.json exists
|
|
30
|
+
* - npm install --prefer-offline otherwise
|
|
31
|
+
* - pnpm install --frozen-lockfile if pnpm
|
|
32
|
+
*/
|
|
33
|
+
private buildFastInstallArgs;
|
|
34
|
+
/**
|
|
35
|
+
* Compute a hash of package.json + lockfile contents.
|
|
36
|
+
* Used to skip npm install when nothing has changed.
|
|
37
|
+
*/
|
|
38
|
+
private computeDepsHash;
|
|
39
|
+
private isDepsHashMatch;
|
|
40
|
+
private writeDepsHash;
|
|
26
41
|
/**
|
|
27
42
|
* Check if dependencies are actually installed (not just that node_modules/ exists).
|
|
28
43
|
* Verifies that at least one key dependency from package.json is present.
|
package/dist/packageInstaller.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { spawn } from 'child_process';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import fs, { existsSync } from 'fs';
|
|
8
|
+
import crypto from 'crypto';
|
|
8
9
|
export class PackageInstaller {
|
|
9
10
|
projectDir;
|
|
10
11
|
/** Serializes concurrent install calls */
|
|
@@ -39,26 +40,100 @@ export class PackageInstaller {
|
|
|
39
40
|
/**
|
|
40
41
|
* Ensure all project dependencies are installed.
|
|
41
42
|
* Uses a lock to prevent concurrent installs.
|
|
43
|
+
* Skips install entirely if package.json + lockfile haven't changed (hash cache).
|
|
42
44
|
*/
|
|
43
45
|
async ensureDependencies() {
|
|
44
46
|
// Serialize with any in-flight install
|
|
45
47
|
await this.installLock;
|
|
46
|
-
if
|
|
48
|
+
// Fast path: check if deps are cached by hash
|
|
49
|
+
if (this.isDepsHashMatch()) {
|
|
50
|
+
console.log(` [Installer] Dependencies up-to-date (hash match) ā skipping install`);
|
|
47
51
|
return;
|
|
52
|
+
}
|
|
53
|
+
if (this.areDependenciesInstalled()) {
|
|
54
|
+
// node_modules exists and looks valid ā write hash and skip
|
|
55
|
+
this.writeDepsHash();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
48
58
|
// Acquire lock for this install
|
|
49
59
|
let releaseLock;
|
|
50
60
|
this.installLock = new Promise(resolve => { releaseLock = resolve; });
|
|
51
61
|
try {
|
|
52
62
|
console.log(` [Installer] Installing project dependencies...`);
|
|
53
63
|
const pm = this.detectPackageManager();
|
|
54
|
-
const args = pm
|
|
64
|
+
const args = this.buildFastInstallArgs(pm);
|
|
55
65
|
await this.runCommand(pm, args, 300_000);
|
|
66
|
+
this.writeDepsHash();
|
|
56
67
|
console.log(` [Installer] Dependencies installed`);
|
|
57
68
|
}
|
|
58
69
|
finally {
|
|
59
70
|
releaseLock();
|
|
60
71
|
}
|
|
61
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Build the fastest possible install command:
|
|
75
|
+
* - npm ci (clean install from lockfile) if package-lock.json exists
|
|
76
|
+
* - npm install --prefer-offline otherwise
|
|
77
|
+
* - pnpm install --frozen-lockfile if pnpm
|
|
78
|
+
*/
|
|
79
|
+
buildFastInstallArgs(pm) {
|
|
80
|
+
switch (pm) {
|
|
81
|
+
case 'pnpm': {
|
|
82
|
+
const hasLock = existsSync(path.join(this.projectDir, 'pnpm-lock.yaml'));
|
|
83
|
+
return hasLock ? ['install', '--frozen-lockfile', '--prefer-offline'] : ['install'];
|
|
84
|
+
}
|
|
85
|
+
case 'yarn': {
|
|
86
|
+
const hasLock = existsSync(path.join(this.projectDir, 'yarn.lock'));
|
|
87
|
+
return hasLock ? ['install', '--frozen-lockfile', '--prefer-offline'] : ['install'];
|
|
88
|
+
}
|
|
89
|
+
default: {
|
|
90
|
+
// npm: prefer ci (much faster) if lockfile exists
|
|
91
|
+
const hasLock = existsSync(path.join(this.projectDir, 'package-lock.json'));
|
|
92
|
+
return hasLock ? ['ci', '--prefer-offline'] : ['install', '--prefer-offline'];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Compute a hash of package.json + lockfile contents.
|
|
98
|
+
* Used to skip npm install when nothing has changed.
|
|
99
|
+
*/
|
|
100
|
+
computeDepsHash() {
|
|
101
|
+
const hash = crypto.createHash('sha256');
|
|
102
|
+
const pkgPath = path.join(this.projectDir, 'package.json');
|
|
103
|
+
if (existsSync(pkgPath))
|
|
104
|
+
hash.update(fs.readFileSync(pkgPath));
|
|
105
|
+
// Include lockfile if present
|
|
106
|
+
for (const lockFile of ['package-lock.json', 'pnpm-lock.yaml', 'yarn.lock']) {
|
|
107
|
+
const lockPath = path.join(this.projectDir, lockFile);
|
|
108
|
+
if (existsSync(lockPath)) {
|
|
109
|
+
hash.update(fs.readFileSync(lockPath));
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return hash.digest('hex').slice(0, 16);
|
|
114
|
+
}
|
|
115
|
+
isDepsHashMatch() {
|
|
116
|
+
const hashFile = path.join(this.projectDir, 'node_modules', '.nstantpage-deps-hash');
|
|
117
|
+
if (!existsSync(hashFile))
|
|
118
|
+
return false;
|
|
119
|
+
try {
|
|
120
|
+
const stored = fs.readFileSync(hashFile, 'utf-8').trim();
|
|
121
|
+
return stored === this.computeDepsHash();
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
writeDepsHash() {
|
|
128
|
+
const hashFile = path.join(this.projectDir, 'node_modules', '.nstantpage-deps-hash');
|
|
129
|
+
try {
|
|
130
|
+
const dir = path.dirname(hashFile);
|
|
131
|
+
if (!existsSync(dir))
|
|
132
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
133
|
+
fs.writeFileSync(hashFile, this.computeDepsHash(), 'utf-8');
|
|
134
|
+
}
|
|
135
|
+
catch { }
|
|
136
|
+
}
|
|
62
137
|
/**
|
|
63
138
|
* Check if dependencies are actually installed (not just that node_modules/ exists).
|
|
64
139
|
* Verifies that at least one key dependency from package.json is present.
|
package/dist/tunnel.js
CHANGED
|
@@ -63,7 +63,7 @@ export class TunnelClient {
|
|
|
63
63
|
// Send enhanced agent info with capabilities and deviceId
|
|
64
64
|
this.send({
|
|
65
65
|
type: 'agent-info',
|
|
66
|
-
version: '0.5.
|
|
66
|
+
version: '0.5.8',
|
|
67
67
|
hostname: os.hostname(),
|
|
68
68
|
platform: `${os.platform()} ${os.arch()}`,
|
|
69
69
|
deviceId: getDeviceId(),
|
package/package.json
CHANGED