nstantpage-agent 0.5.3 → 0.5.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/cli.js +1 -1
- package/dist/commands/start.js +45 -29
- package/dist/localServer.js +1 -1
- package/dist/packageInstaller.d.ts +8 -0
- package/dist/packageInstaller.js +46 -9
- 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.4');
|
|
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.4';
|
|
28
28
|
/**
|
|
29
29
|
* Resolve the backend API base URL.
|
|
30
30
|
* - If --backend is passed, use it
|
|
@@ -226,20 +226,20 @@ export async function startCommand(directory, options) {
|
|
|
226
226
|
console.log(chalk.gray(` Gateway: ${options.gateway}`));
|
|
227
227
|
console.log(chalk.gray(` Backend: ${backendUrl}\n`));
|
|
228
228
|
// 1. Fetch project files from the backend
|
|
229
|
+
const installer = new PackageInstaller({ projectDir });
|
|
229
230
|
try {
|
|
230
231
|
const { fileCount, isNew } = await fetchProjectFiles(backendUrl, projectId, projectDir, token);
|
|
231
|
-
// 2. Install dependencies if
|
|
232
|
-
|
|
233
|
-
if (isNew || !hasNodeModules) {
|
|
232
|
+
// 2. Install dependencies if needed (verifies actual packages, not just folder)
|
|
233
|
+
if (!installer.areDependenciesInstalled()) {
|
|
234
234
|
if (fs.existsSync(path.join(projectDir, 'package.json'))) {
|
|
235
235
|
console.log(chalk.gray(' Installing dependencies...'));
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (result.success) {
|
|
236
|
+
try {
|
|
237
|
+
await installer.ensureDependencies();
|
|
239
238
|
console.log(chalk.green(` ✓ Dependencies installed`));
|
|
240
239
|
}
|
|
241
|
-
|
|
242
|
-
console.log(chalk.yellow(` ⚠
|
|
240
|
+
catch (err) {
|
|
241
|
+
console.log(chalk.yellow(` ⚠ Install failed: ${err.message?.slice(0, 200)}`));
|
|
242
|
+
console.log(chalk.gray(` Will retry when dev server starts`));
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
245
|
}
|
|
@@ -376,15 +376,21 @@ export async function startCommand(directory, options) {
|
|
|
376
376
|
console.log(chalk.green(` ✓ API server on port ${apiPort}`));
|
|
377
377
|
// Start dev server unless --no-dev flag
|
|
378
378
|
if (!options.noDev) {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
379
|
+
if (installer.areDependenciesInstalled()) {
|
|
380
|
+
console.log(chalk.gray(' Starting dev server...'));
|
|
381
|
+
try {
|
|
382
|
+
const devServer = localServer.getDevServer();
|
|
383
|
+
await devServer.start();
|
|
384
|
+
console.log(chalk.green(` ✓ Dev server on port ${devPort}`));
|
|
385
|
+
}
|
|
386
|
+
catch (err) {
|
|
387
|
+
console.log(chalk.yellow(` ⚠ Dev server failed to start: ${err.message}`));
|
|
388
|
+
console.log(chalk.gray(' You can start it later from the editor'));
|
|
389
|
+
}
|
|
384
390
|
}
|
|
385
|
-
|
|
386
|
-
console.log(chalk.yellow(` ⚠
|
|
387
|
-
console.log(chalk.gray('
|
|
391
|
+
else {
|
|
392
|
+
console.log(chalk.yellow(` ⚠ Skipping dev server — dependencies not fully installed`));
|
|
393
|
+
console.log(chalk.gray(' Dev server will start when browser opens the project'));
|
|
388
394
|
}
|
|
389
395
|
}
|
|
390
396
|
else {
|
|
@@ -557,14 +563,18 @@ async function startAdditionalProject(projectId, opts) {
|
|
|
557
563
|
catch (err) {
|
|
558
564
|
console.log(chalk.yellow(` ⚠ Could not fetch files: ${err.message}`));
|
|
559
565
|
}
|
|
560
|
-
// Install dependencies
|
|
561
|
-
const
|
|
562
|
-
if (!
|
|
566
|
+
// Install dependencies (must complete before dev server can start)
|
|
567
|
+
const installer = new PackageInstaller({ projectDir });
|
|
568
|
+
if (!installer.areDependenciesInstalled() && fs.existsSync(path.join(projectDir, 'package.json'))) {
|
|
563
569
|
console.log(chalk.gray(` Installing dependencies...`));
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
if (result.success)
|
|
570
|
+
try {
|
|
571
|
+
await installer.ensureDependencies();
|
|
567
572
|
console.log(chalk.green(` ✓ Dependencies installed`));
|
|
573
|
+
}
|
|
574
|
+
catch (err) {
|
|
575
|
+
console.log(chalk.red(` ✗ Install failed: ${err.message}`));
|
|
576
|
+
console.log(chalk.yellow(` ⚠ Dev server may fail — retrying install on first request`));
|
|
577
|
+
}
|
|
568
578
|
}
|
|
569
579
|
// Start local server
|
|
570
580
|
const localServer = new LocalServer({
|
|
@@ -573,14 +583,20 @@ async function startAdditionalProject(projectId, opts) {
|
|
|
573
583
|
});
|
|
574
584
|
await localServer.start();
|
|
575
585
|
console.log(chalk.green(` ✓ API server on port ${allocated.apiPort}`));
|
|
576
|
-
// Start dev server
|
|
586
|
+
// Start dev server (only if dependencies are installed)
|
|
577
587
|
if (!opts.noDev) {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
588
|
+
if (installer.areDependenciesInstalled()) {
|
|
589
|
+
try {
|
|
590
|
+
await localServer.getDevServer().start();
|
|
591
|
+
console.log(chalk.green(` ✓ Dev server on port ${allocated.devPort}`));
|
|
592
|
+
}
|
|
593
|
+
catch (err) {
|
|
594
|
+
console.log(chalk.yellow(` ⚠ Dev server: ${err.message}`));
|
|
595
|
+
}
|
|
581
596
|
}
|
|
582
|
-
|
|
583
|
-
console.log(chalk.yellow(` ⚠
|
|
597
|
+
else {
|
|
598
|
+
console.log(chalk.yellow(` ⚠ Skipping dev server — dependencies not installed`));
|
|
599
|
+
console.log(chalk.gray(` Dev server will start when browser opens the project`));
|
|
584
600
|
}
|
|
585
601
|
}
|
|
586
602
|
// Connect project tunnel
|
package/dist/localServer.js
CHANGED
|
@@ -7,6 +7,8 @@ export interface PackageInstallerOptions {
|
|
|
7
7
|
}
|
|
8
8
|
export declare class PackageInstaller {
|
|
9
9
|
private projectDir;
|
|
10
|
+
/** Serializes concurrent install calls */
|
|
11
|
+
private installLock;
|
|
10
12
|
constructor(options: PackageInstallerOptions);
|
|
11
13
|
/**
|
|
12
14
|
* Install packages into the project.
|
|
@@ -18,8 +20,14 @@ export declare class PackageInstaller {
|
|
|
18
20
|
}>;
|
|
19
21
|
/**
|
|
20
22
|
* Ensure all project dependencies are installed.
|
|
23
|
+
* Uses a lock to prevent concurrent installs.
|
|
21
24
|
*/
|
|
22
25
|
ensureDependencies(): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Check if dependencies are actually installed (not just that node_modules/ exists).
|
|
28
|
+
* Verifies that at least one key dependency from package.json is present.
|
|
29
|
+
*/
|
|
30
|
+
areDependenciesInstalled(): boolean;
|
|
23
31
|
/**
|
|
24
32
|
* Detect which package manager is used (pnpm, yarn, npm).
|
|
25
33
|
*/
|
package/dist/packageInstaller.js
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { spawn } from 'child_process';
|
|
6
6
|
import path from 'path';
|
|
7
|
-
import { existsSync } from 'fs';
|
|
7
|
+
import fs, { existsSync } from 'fs';
|
|
8
8
|
export class PackageInstaller {
|
|
9
9
|
projectDir;
|
|
10
|
+
/** Serializes concurrent install calls */
|
|
11
|
+
installLock = Promise.resolve();
|
|
10
12
|
constructor(options) {
|
|
11
13
|
this.projectDir = options.projectDir;
|
|
12
14
|
}
|
|
@@ -18,7 +20,7 @@ export class PackageInstaller {
|
|
|
18
20
|
const args = this.buildInstallArgs(pm, packages, dev);
|
|
19
21
|
console.log(` [Installer] ${pm} ${args.join(' ')}`);
|
|
20
22
|
try {
|
|
21
|
-
const timeout = packages.length === 0 ?
|
|
23
|
+
const timeout = packages.length === 0 ? 300_000 : 120_000;
|
|
22
24
|
const output = await this.runCommand(pm, args, timeout);
|
|
23
25
|
return {
|
|
24
26
|
success: true,
|
|
@@ -36,16 +38,51 @@ export class PackageInstaller {
|
|
|
36
38
|
}
|
|
37
39
|
/**
|
|
38
40
|
* Ensure all project dependencies are installed.
|
|
41
|
+
* Uses a lock to prevent concurrent installs.
|
|
39
42
|
*/
|
|
40
43
|
async ensureDependencies() {
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
// Serialize with any in-flight install
|
|
45
|
+
await this.installLock;
|
|
46
|
+
if (this.areDependenciesInstalled())
|
|
43
47
|
return;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
// Acquire lock for this install
|
|
49
|
+
let releaseLock;
|
|
50
|
+
this.installLock = new Promise(resolve => { releaseLock = resolve; });
|
|
51
|
+
try {
|
|
52
|
+
console.log(` [Installer] Installing project dependencies...`);
|
|
53
|
+
const pm = this.detectPackageManager();
|
|
54
|
+
const args = pm === 'pnpm' ? ['install'] : ['install'];
|
|
55
|
+
await this.runCommand(pm, args, 300_000);
|
|
56
|
+
console.log(` [Installer] Dependencies installed`);
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
releaseLock();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check if dependencies are actually installed (not just that node_modules/ exists).
|
|
64
|
+
* Verifies that at least one key dependency from package.json is present.
|
|
65
|
+
*/
|
|
66
|
+
areDependenciesInstalled() {
|
|
67
|
+
const nodeModules = path.join(this.projectDir, 'node_modules');
|
|
68
|
+
if (!existsSync(nodeModules))
|
|
69
|
+
return false;
|
|
70
|
+
// Check that at least one real dependency is installed
|
|
71
|
+
try {
|
|
72
|
+
const pkgPath = path.join(this.projectDir, 'package.json');
|
|
73
|
+
if (!existsSync(pkgPath))
|
|
74
|
+
return true; // no package.json → nothing to install
|
|
75
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
76
|
+
const deps = Object.keys(pkg.dependencies || {});
|
|
77
|
+
if (deps.length === 0)
|
|
78
|
+
return true; // no deps → nothing needed
|
|
79
|
+
// Check if the first listed dependency has its folder in node_modules
|
|
80
|
+
const firstDep = deps[0];
|
|
81
|
+
return existsSync(path.join(nodeModules, firstDep));
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return existsSync(nodeModules);
|
|
85
|
+
}
|
|
49
86
|
}
|
|
50
87
|
/**
|
|
51
88
|
* Detect which package manager is used (pnpm, yarn, npm).
|
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.4',
|
|
67
67
|
hostname: os.hostname(),
|
|
68
68
|
platform: `${os.platform()} ${os.arch()}`,
|
|
69
69
|
deviceId: getDeviceId(),
|
package/package.json
CHANGED