nstantpage-agent 0.8.12 → 0.8.14
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 +83 -65
- package/dist/tunnel.d.ts +1 -0
- package/dist/tunnel.js +4 -4
- package/package.json +1 -1
package/dist/commands/start.js
CHANGED
|
@@ -572,18 +572,21 @@ async function startStandbyMode(token, options, backendUrl, deviceId) {
|
|
|
572
572
|
devPort: 0,
|
|
573
573
|
onStartProject: async (pid, opts) => {
|
|
574
574
|
const isClean = opts?.clean === true;
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
575
|
+
const isLocalRepo = !!opts?.localFolderPath;
|
|
576
|
+
// If clean reset or LocalRepo re-start requested and project is already running, stop it first
|
|
577
|
+
if ((isClean || isLocalRepo) && activeProjects.has(pid)) {
|
|
578
|
+
console.log(chalk.yellow(` Stopping existing project ${pid} for ${isClean ? 'clean reset' : 'LocalRepo restart'}...`));
|
|
578
579
|
const existing = activeProjects.get(pid);
|
|
579
580
|
existing.tunnel.disconnect();
|
|
580
581
|
await existing.localServer.stop();
|
|
581
582
|
activeProjects.delete(pid);
|
|
582
|
-
// Wipe the project directory
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
583
|
+
// Wipe the project directory (only for clean non-LocalRepo)
|
|
584
|
+
if (isClean && !isLocalRepo) {
|
|
585
|
+
const projectDir = resolveProjectDir('.', pid);
|
|
586
|
+
if (fs.existsSync(projectDir)) {
|
|
587
|
+
console.log(chalk.gray(` Wiping project directory: ${projectDir}`));
|
|
588
|
+
fs.rmSync(projectDir, { recursive: true, force: true });
|
|
589
|
+
}
|
|
587
590
|
}
|
|
588
591
|
}
|
|
589
592
|
else if (activeProjects.has(pid)) {
|
|
@@ -592,6 +595,7 @@ async function startStandbyMode(token, options, backendUrl, deviceId) {
|
|
|
592
595
|
}
|
|
593
596
|
const result = await startAdditionalProject(pid, {
|
|
594
597
|
token, backendUrl, gatewayUrl: options.gateway, deviceId, noDev: options.noDev,
|
|
598
|
+
localFolderPath: opts?.localFolderPath,
|
|
595
599
|
onProgress: (phase, message) => {
|
|
596
600
|
standbyTunnel.sendSetupProgress(pid, phase, message);
|
|
597
601
|
},
|
|
@@ -678,6 +682,7 @@ async function startStandbyMode(token, options, backendUrl, deviceId) {
|
|
|
678
682
|
}
|
|
679
683
|
const result = await startAdditionalProject(projectId, {
|
|
680
684
|
token, backendUrl, gatewayUrl: options.gateway, deviceId, noDev: options.noDev,
|
|
685
|
+
localFolderPath: opts?.localFolderPath,
|
|
681
686
|
onProgress: (phase, message) => {
|
|
682
687
|
standbyTunnel.sendSetupProgress(projectId, phase, message);
|
|
683
688
|
},
|
|
@@ -736,29 +741,37 @@ async function startAdditionalProject(projectId, opts) {
|
|
|
736
741
|
const progress = opts.onProgress || (() => { });
|
|
737
742
|
const timings = {};
|
|
738
743
|
const t0 = Date.now();
|
|
739
|
-
|
|
744
|
+
const isLocalRepo = !!opts.localFolderPath;
|
|
745
|
+
console.log(chalk.blue(`\n 📦 Starting project ${projectId}${isLocalRepo ? ' (LocalRepo)' : ''}...`));
|
|
740
746
|
try {
|
|
741
747
|
const allocated = allocatePortsForProject(projectId);
|
|
742
|
-
const projectDir = resolveProjectDir('.', projectId);
|
|
748
|
+
const projectDir = isLocalRepo ? opts.localFolderPath : resolveProjectDir('.', projectId);
|
|
743
749
|
if (!fs.existsSync(projectDir))
|
|
744
750
|
fs.mkdirSync(projectDir, { recursive: true });
|
|
745
751
|
// Ensure ports are clear for this project before starting
|
|
746
752
|
cleanupPreviousAgent(projectId, allocated.apiPort, allocated.devPort);
|
|
747
753
|
await new Promise(r => setTimeout(r, 50));
|
|
748
754
|
// ── Phase 1: API server + file fetch + tunnel connect (all parallel) ──
|
|
749
|
-
|
|
750
|
-
|
|
755
|
+
if (!isLocalRepo) {
|
|
756
|
+
progress('fetching-files', 'Fetching project files...');
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
progress('starting-server', 'Starting local server...');
|
|
760
|
+
}
|
|
761
|
+
// Provision database if PostgreSQL is available (skip for LocalRepo)
|
|
751
762
|
let databaseUrl = null;
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
763
|
+
if (!isLocalRepo) {
|
|
764
|
+
try {
|
|
765
|
+
const hasPg = await probeLocalPostgres();
|
|
766
|
+
if (hasPg) {
|
|
767
|
+
databaseUrl = await ensureLocalProjectDb(projectId);
|
|
768
|
+
if (databaseUrl) {
|
|
769
|
+
console.log(chalk.green(` ✓ Database ready: project_${projectId}`));
|
|
770
|
+
}
|
|
758
771
|
}
|
|
759
772
|
}
|
|
773
|
+
catch { }
|
|
760
774
|
}
|
|
761
|
-
catch { }
|
|
762
775
|
const serverEnv = {};
|
|
763
776
|
if (databaseUrl)
|
|
764
777
|
serverEnv['DATABASE_URL'] = databaseUrl;
|
|
@@ -799,17 +812,20 @@ async function startAdditionalProject(projectId, opts) {
|
|
|
799
812
|
tunnel.startBackgroundReconnect();
|
|
800
813
|
}
|
|
801
814
|
})();
|
|
815
|
+
// Fetch project files from backend (skip for LocalRepo — files are on disk)
|
|
802
816
|
const fileStart = Date.now();
|
|
803
817
|
let fetchedVersionId;
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
818
|
+
if (!isLocalRepo) {
|
|
819
|
+
try {
|
|
820
|
+
const { versionId } = await fetchProjectFiles(opts.backendUrl, projectId, projectDir, opts.token);
|
|
821
|
+
fetchedVersionId = versionId;
|
|
822
|
+
timings['fetch-files'] = Date.now() - fileStart;
|
|
823
|
+
progress('fetching-files', `Files downloaded (${timings['fetch-files']}ms)`);
|
|
824
|
+
}
|
|
825
|
+
catch (err) {
|
|
826
|
+
console.log(chalk.yellow(` ⚠ Could not fetch files: ${err.message}`));
|
|
827
|
+
progress('fetching-files', `Warning: ${err.message}`);
|
|
828
|
+
}
|
|
813
829
|
}
|
|
814
830
|
// Write DATABASE_URL to .env AFTER file fetch (so it doesn't get overwritten)
|
|
815
831
|
if (databaseUrl) {
|
|
@@ -822,49 +838,51 @@ async function startAdditionalProject(projectId, opts) {
|
|
|
822
838
|
if (fetchedVersionId) {
|
|
823
839
|
localServer.markSynced(fetchedVersionId);
|
|
824
840
|
}
|
|
825
|
-
// ── Phase 2: Install deps (if needed)
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
await installer.ensureDependencies();
|
|
834
|
-
timings['install'] = Date.now() - installStart;
|
|
835
|
-
console.log(chalk.green(` ✓ Dependencies installed (${timings['install']}ms)`));
|
|
836
|
-
progress('installing-deps', `Dependencies installed (${timings['install']}ms)`);
|
|
837
|
-
}
|
|
838
|
-
catch (err) {
|
|
839
|
-
timings['install'] = Date.now() - installStart;
|
|
840
|
-
console.log(chalk.red(` ✗ Install failed: ${err.message}`));
|
|
841
|
-
console.log(chalk.yellow(` ⚠ Dev server may fail — retrying install on first request`));
|
|
842
|
-
progress('installing-deps', `Install failed: ${err.message}`);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
else {
|
|
846
|
-
progress('installing-deps', 'Dependencies already installed (cached)');
|
|
847
|
-
}
|
|
848
|
-
// ── Phase 3: Start dev server ────────────────────────────────────
|
|
849
|
-
if (!opts.noDev) {
|
|
850
|
-
if (installer.areDependenciesInstalled()) {
|
|
851
|
-
progress('starting-dev', 'Starting dev server...');
|
|
841
|
+
// ── Phase 2: Install deps (if needed) — skip for LocalRepo ──
|
|
842
|
+
if (!isLocalRepo) {
|
|
843
|
+
const installer = new PackageInstaller({ projectDir });
|
|
844
|
+
const needsInstall = !installer.areDependenciesInstalled() && fs.existsSync(path.join(projectDir, 'package.json'));
|
|
845
|
+
if (needsInstall) {
|
|
846
|
+
const installStart = Date.now();
|
|
847
|
+
console.log(chalk.gray(` Installing dependencies...`));
|
|
848
|
+
progress('installing-deps', 'Installing npm dependencies...');
|
|
852
849
|
try {
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
timings['
|
|
856
|
-
|
|
857
|
-
progress('starting-dev', `Dev server on port ${allocated.devPort}`);
|
|
850
|
+
await installer.ensureDependencies();
|
|
851
|
+
timings['install'] = Date.now() - installStart;
|
|
852
|
+
console.log(chalk.green(` ✓ Dependencies installed (${timings['install']}ms)`));
|
|
853
|
+
progress('installing-deps', `Dependencies installed (${timings['install']}ms)`);
|
|
858
854
|
}
|
|
859
855
|
catch (err) {
|
|
860
|
-
|
|
861
|
-
|
|
856
|
+
timings['install'] = Date.now() - installStart;
|
|
857
|
+
console.log(chalk.red(` ✗ Install failed: ${err.message}`));
|
|
858
|
+
console.log(chalk.yellow(` ⚠ Dev server may fail — retrying install on first request`));
|
|
859
|
+
progress('installing-deps', `Install failed: ${err.message}`);
|
|
862
860
|
}
|
|
863
861
|
}
|
|
864
862
|
else {
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
863
|
+
progress('installing-deps', 'Dependencies already installed (cached)');
|
|
864
|
+
}
|
|
865
|
+
// ── Phase 3: Start dev server — skip for LocalRepo ──
|
|
866
|
+
if (!opts.noDev) {
|
|
867
|
+
if (installer.areDependenciesInstalled()) {
|
|
868
|
+
progress('starting-dev', 'Starting dev server...');
|
|
869
|
+
try {
|
|
870
|
+
const devStart = Date.now();
|
|
871
|
+
await localServer.getDevServer().start();
|
|
872
|
+
timings['dev-server'] = Date.now() - devStart;
|
|
873
|
+
console.log(chalk.green(` ✓ Dev server on port ${allocated.devPort} (${timings['dev-server']}ms)`));
|
|
874
|
+
progress('starting-dev', `Dev server on port ${allocated.devPort}`);
|
|
875
|
+
}
|
|
876
|
+
catch (err) {
|
|
877
|
+
console.log(chalk.yellow(` ⚠ Dev server: ${err.message}`));
|
|
878
|
+
progress('starting-dev', `Warning: ${err.message}`);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
else {
|
|
882
|
+
console.log(chalk.yellow(` ⚠ Skipping dev server — dependencies not installed`));
|
|
883
|
+
console.log(chalk.gray(` Dev server will start when browser opens the project`));
|
|
884
|
+
progress('starting-dev', 'Skipped — dependencies not installed');
|
|
885
|
+
}
|
|
868
886
|
}
|
|
869
887
|
}
|
|
870
888
|
// Ensure tunnel is connected before declaring ready
|
package/dist/tunnel.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ interface TunnelClientOptions {
|
|
|
27
27
|
/** Callback when gateway requests starting a new project on this device */
|
|
28
28
|
onStartProject?: (projectId: string, options?: {
|
|
29
29
|
clean?: boolean;
|
|
30
|
+
localFolderPath?: string;
|
|
30
31
|
}) => Promise<void>;
|
|
31
32
|
/** Callback for setup progress — lets the standby tunnel relay phases to gateway */
|
|
32
33
|
onSetupProgress?: (projectId: string, phase: string, message: string) => void;
|
package/dist/tunnel.js
CHANGED
|
@@ -403,8 +403,8 @@ export class TunnelClient {
|
|
|
403
403
|
* Handle start-project: gateway wants this agent to start serving a new project.
|
|
404
404
|
* Called when user clicks "Connect" in the web editor's Cloud panel.
|
|
405
405
|
*/
|
|
406
|
-
async handleStartProject(projectId, clean) {
|
|
407
|
-
console.log(` [Tunnel] Received start-project command for ${projectId}${clean ? ' (clean)' : ''}`);
|
|
406
|
+
async handleStartProject(projectId, clean, localFolderPath) {
|
|
407
|
+
console.log(` [Tunnel] Received start-project command for ${projectId}${clean ? ' (clean)' : ''}${localFolderPath ? ` (local: ${localFolderPath})` : ''}`);
|
|
408
408
|
if (!projectId) {
|
|
409
409
|
this.send({ type: 'start-project-result', success: false, error: 'Missing projectId' });
|
|
410
410
|
return;
|
|
@@ -413,7 +413,7 @@ export class TunnelClient {
|
|
|
413
413
|
try {
|
|
414
414
|
// Send initial progress
|
|
415
415
|
this.sendSetupProgress(projectId, 'starting', 'Starting project setup...');
|
|
416
|
-
await this.options.onStartProject(projectId, clean ? { clean: true } :
|
|
416
|
+
await this.options.onStartProject(projectId, { ...(clean ? { clean: true } : {}), ...(localFolderPath ? { localFolderPath } : {}) });
|
|
417
417
|
this.sendSetupProgress(projectId, 'ready', 'Project is live!');
|
|
418
418
|
this.send({ type: 'start-project-result', projectId, success: true });
|
|
419
419
|
}
|
|
@@ -474,7 +474,7 @@ export class TunnelClient {
|
|
|
474
474
|
this.handleWsClose(msg.wsId);
|
|
475
475
|
break;
|
|
476
476
|
case 'start-project':
|
|
477
|
-
this.handleStartProject(msg.projectId, msg.clean);
|
|
477
|
+
this.handleStartProject(msg.projectId, msg.clean, msg.localFolderPath);
|
|
478
478
|
break;
|
|
479
479
|
case 'force-disconnect':
|
|
480
480
|
console.log(' [Tunnel] Received force-disconnect — will not reconnect');
|
package/package.json
CHANGED