nstantpage-agent 0.5.0 → 0.5.1
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 +17 -3
- package/dist/commands/login.js +38 -1
- package/dist/commands/logout.d.ts +4 -0
- package/dist/commands/logout.js +20 -0
- package/dist/commands/service.d.ts +13 -0
- package/dist/commands/service.js +70 -0
- package/dist/commands/start.js +3 -2
- package/dist/config.js +1 -0
- package/dist/localServer.js +1 -1
- package/dist/tunnel.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -17,20 +17,25 @@
|
|
|
17
17
|
import { Command } from 'commander';
|
|
18
18
|
import chalk from 'chalk';
|
|
19
19
|
import { loginCommand } from './commands/login.js';
|
|
20
|
+
import { logoutCommand } from './commands/logout.js';
|
|
20
21
|
import { startCommand } from './commands/start.js';
|
|
21
22
|
import { statusCommand } from './commands/status.js';
|
|
22
|
-
import { serviceInstallCommand, serviceUninstallCommand, serviceStatusCommand } from './commands/service.js';
|
|
23
|
+
import { serviceInstallCommand, serviceUninstallCommand, serviceStatusCommand, serviceStartCommand, serviceStopCommand } from './commands/service.js';
|
|
23
24
|
const program = new Command();
|
|
24
25
|
program
|
|
25
26
|
.name('nstantpage')
|
|
26
27
|
.description('Local development agent for nstantpage.com — run projects on your machine, preview in the cloud')
|
|
27
|
-
.version('0.5.
|
|
28
|
+
.version('0.5.1');
|
|
28
29
|
program
|
|
29
30
|
.command('login')
|
|
30
31
|
.description('Authenticate with nstantpage.com')
|
|
31
32
|
.option('--gateway <url>', 'Gateway URL (auto-detects local vs production)')
|
|
32
33
|
.option('--force', 'Re-authenticate even if already logged in')
|
|
33
34
|
.action(loginCommand);
|
|
35
|
+
program
|
|
36
|
+
.command('logout')
|
|
37
|
+
.description('Sign out and clear stored credentials')
|
|
38
|
+
.action(logoutCommand);
|
|
34
39
|
program
|
|
35
40
|
.command('start')
|
|
36
41
|
.description('Start the agent for a project (replaces cloud containers)')
|
|
@@ -75,9 +80,18 @@ program
|
|
|
75
80
|
const service = program
|
|
76
81
|
.command('service')
|
|
77
82
|
.description('Manage the background service (keeps your machine connected to nstantpage.com)');
|
|
83
|
+
service
|
|
84
|
+
.command('start')
|
|
85
|
+
.description('Start the agent in standby mode (foreground — keeps your machine online)')
|
|
86
|
+
.option('--gateway <url>', 'Gateway URL', 'wss://webprev.live')
|
|
87
|
+
.action(serviceStartCommand);
|
|
88
|
+
service
|
|
89
|
+
.command('stop')
|
|
90
|
+
.description('Stop the running agent')
|
|
91
|
+
.action(serviceStopCommand);
|
|
78
92
|
service
|
|
79
93
|
.command('install')
|
|
80
|
-
.description('Install
|
|
94
|
+
.description('Install as a background service (auto-starts on boot)')
|
|
81
95
|
.option('--gateway <url>', 'Gateway URL', 'wss://webprev.live')
|
|
82
96
|
.action(serviceInstallCommand);
|
|
83
97
|
service
|
package/dist/commands/login.js
CHANGED
|
@@ -16,13 +16,45 @@ function resolveFrontendUrl(gateway) {
|
|
|
16
16
|
}
|
|
17
17
|
return 'https://nstantpage.com';
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Decode a JWT token payload (without verification — just reading claims).
|
|
21
|
+
*/
|
|
22
|
+
function decodeJwtPayload(token) {
|
|
23
|
+
try {
|
|
24
|
+
const parts = token.split('.');
|
|
25
|
+
if (parts.length !== 3)
|
|
26
|
+
return null;
|
|
27
|
+
const payload = Buffer.from(parts[1], 'base64').toString('utf-8');
|
|
28
|
+
return JSON.parse(payload);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Extract email from JWT claims.
|
|
36
|
+
* .NET uses long claim type URIs by default.
|
|
37
|
+
*/
|
|
38
|
+
function getEmailFromToken(token) {
|
|
39
|
+
const payload = decodeJwtPayload(token);
|
|
40
|
+
if (!payload)
|
|
41
|
+
return null;
|
|
42
|
+
return payload['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']
|
|
43
|
+
|| payload['email']
|
|
44
|
+
|| payload['sub']
|
|
45
|
+
|| null;
|
|
46
|
+
}
|
|
19
47
|
export async function loginCommand(options = {}) {
|
|
20
48
|
const conf = getConfig();
|
|
21
49
|
// Check if already logged in (unless --force)
|
|
22
50
|
const existingToken = conf.get('token');
|
|
23
51
|
if (existingToken && !options.force) {
|
|
52
|
+
const email = conf.get('email') || getEmailFromToken(existingToken);
|
|
24
53
|
console.log(chalk.green('✓ Already authenticated'));
|
|
54
|
+
if (email)
|
|
55
|
+
console.log(chalk.gray(` Account: ${email}`));
|
|
25
56
|
console.log(chalk.gray(' Run "nstantpage login --force" to re-authenticate'));
|
|
57
|
+
console.log(chalk.gray(' Run "nstantpage logout" to sign out.'));
|
|
26
58
|
return;
|
|
27
59
|
}
|
|
28
60
|
const frontendUrl = resolveFrontendUrl(options.gateway);
|
|
@@ -78,7 +110,12 @@ export async function loginCommand(options = {}) {
|
|
|
78
110
|
}, 5 * 60 * 1000);
|
|
79
111
|
});
|
|
80
112
|
conf.set('token', token);
|
|
113
|
+
const email = getEmailFromToken(token);
|
|
114
|
+
if (email)
|
|
115
|
+
conf.set('email', email);
|
|
81
116
|
console.log(chalk.green('\n✓ Successfully authenticated!'));
|
|
82
|
-
|
|
117
|
+
if (email)
|
|
118
|
+
console.log(chalk.gray(` Account: ${email}`));
|
|
119
|
+
console.log(chalk.gray(' Run "nstantpage start" to connect your machine'));
|
|
83
120
|
}
|
|
84
121
|
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logout command — clear stored credentials
|
|
3
|
+
*/
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { getConfig } from '../config.js';
|
|
6
|
+
export async function logoutCommand() {
|
|
7
|
+
const conf = getConfig();
|
|
8
|
+
const email = conf.get('email');
|
|
9
|
+
conf.delete('token');
|
|
10
|
+
conf.delete('email');
|
|
11
|
+
conf.delete('projectId');
|
|
12
|
+
if (email) {
|
|
13
|
+
console.log(chalk.green(`✓ Logged out (was: ${email})`));
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
console.log(chalk.green('✓ Logged out'));
|
|
17
|
+
}
|
|
18
|
+
console.log(chalk.gray(' Run "nstantpage login" to authenticate again.'));
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=logout.js.map
|
|
@@ -18,6 +18,19 @@
|
|
|
18
18
|
interface ServiceInstallOptions {
|
|
19
19
|
gateway?: string;
|
|
20
20
|
}
|
|
21
|
+
interface ServiceStartOptions {
|
|
22
|
+
gateway?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* `nstantpage service start` — run agent in standby mode (foreground).
|
|
26
|
+
* This keeps your machine online and visible from the web UI.
|
|
27
|
+
* Projects are started via the web when you click "Connect".
|
|
28
|
+
*/
|
|
29
|
+
export declare function serviceStartCommand(options?: ServiceStartOptions): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* `nstantpage service stop` — stop any running agent (PID-based or OS service).
|
|
32
|
+
*/
|
|
33
|
+
export declare function serviceStopCommand(): Promise<void>;
|
|
21
34
|
export declare function serviceInstallCommand(options?: ServiceInstallOptions): Promise<void>;
|
|
22
35
|
export declare function serviceUninstallCommand(): Promise<void>;
|
|
23
36
|
export declare function serviceStatusCommand(): Promise<void>;
|
package/dist/commands/service.js
CHANGED
|
@@ -21,8 +21,78 @@ import path from 'path';
|
|
|
21
21
|
import os from 'os';
|
|
22
22
|
import { execSync } from 'child_process';
|
|
23
23
|
import { getConfig } from '../config.js';
|
|
24
|
+
import { startCommand } from './start.js';
|
|
24
25
|
const PLIST_LABEL = 'com.nstantpage.agent';
|
|
25
26
|
const SYSTEMD_SERVICE = 'nstantpage-agent';
|
|
27
|
+
/**
|
|
28
|
+
* `nstantpage service start` — run agent in standby mode (foreground).
|
|
29
|
+
* This keeps your machine online and visible from the web UI.
|
|
30
|
+
* Projects are started via the web when you click "Connect".
|
|
31
|
+
*/
|
|
32
|
+
export async function serviceStartCommand(options = {}) {
|
|
33
|
+
const conf = getConfig();
|
|
34
|
+
const token = conf.get('token');
|
|
35
|
+
if (!token) {
|
|
36
|
+
console.log(chalk.red('✗ Not authenticated. Run "nstantpage login" first.'));
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
const email = conf.get('email');
|
|
40
|
+
if (email) {
|
|
41
|
+
console.log(chalk.gray(` Account: ${email}`));
|
|
42
|
+
}
|
|
43
|
+
// Delegate to start command with no project-id → enters standby mode
|
|
44
|
+
const gateway = options.gateway || 'wss://webprev.live';
|
|
45
|
+
await startCommand('.', {
|
|
46
|
+
port: '3000',
|
|
47
|
+
apiPort: '18924',
|
|
48
|
+
gateway,
|
|
49
|
+
token,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* `nstantpage service stop` — stop any running agent (PID-based or OS service).
|
|
54
|
+
*/
|
|
55
|
+
export async function serviceStopCommand() {
|
|
56
|
+
const platform = os.platform();
|
|
57
|
+
let stopped = false;
|
|
58
|
+
// Try stopping OS service first
|
|
59
|
+
if (platform === 'darwin') {
|
|
60
|
+
const plistPath = path.join(os.homedir(), 'Library', 'LaunchAgents', `${PLIST_LABEL}.plist`);
|
|
61
|
+
if (fs.existsSync(plistPath)) {
|
|
62
|
+
try {
|
|
63
|
+
execSync(`launchctl unload "${plistPath}" 2>/dev/null`, { encoding: 'utf-8' });
|
|
64
|
+
console.log(chalk.green(' ✓ Background service stopped'));
|
|
65
|
+
stopped = true;
|
|
66
|
+
}
|
|
67
|
+
catch { }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (platform === 'linux') {
|
|
71
|
+
try {
|
|
72
|
+
execSync(`systemctl --user stop ${SYSTEMD_SERVICE} 2>/dev/null`, { encoding: 'utf-8' });
|
|
73
|
+
console.log(chalk.green(' ✓ Background service stopped'));
|
|
74
|
+
stopped = true;
|
|
75
|
+
}
|
|
76
|
+
catch { }
|
|
77
|
+
}
|
|
78
|
+
// Also try PID-based stop from global config
|
|
79
|
+
const conf = getConfig();
|
|
80
|
+
const pid = conf.get('agentPid');
|
|
81
|
+
if (pid) {
|
|
82
|
+
try {
|
|
83
|
+
process.kill(pid, 'SIGTERM');
|
|
84
|
+
conf.delete('agentPid');
|
|
85
|
+
console.log(chalk.green(' ✓ Agent process stopped'));
|
|
86
|
+
stopped = true;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
conf.delete('agentPid');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!stopped) {
|
|
93
|
+
console.log(chalk.yellow(' No running agent found.'));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
26
96
|
export async function serviceInstallCommand(options = {}) {
|
|
27
97
|
const conf = getConfig();
|
|
28
98
|
const token = conf.get('token');
|
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.1';
|
|
28
28
|
/**
|
|
29
29
|
* Resolve the backend API base URL.
|
|
30
30
|
* - If --backend is passed, use it
|
|
@@ -176,7 +176,8 @@ export async function startCommand(directory, options) {
|
|
|
176
176
|
token = 'local-dev';
|
|
177
177
|
}
|
|
178
178
|
// Determine project ID (optional — without it, agent enters standby mode)
|
|
179
|
-
|
|
179
|
+
// Only use explicitly passed --project-id, never fall back to stored value
|
|
180
|
+
let projectId = options.projectId;
|
|
180
181
|
const backendUrl = resolveBackendUrl(options);
|
|
181
182
|
const deviceId = getDeviceId();
|
|
182
183
|
if (!projectId) {
|
package/dist/config.js
CHANGED
|
@@ -20,6 +20,7 @@ export function getConfig() {
|
|
|
20
20
|
projectName: 'nstantpage-agent',
|
|
21
21
|
schema: {
|
|
22
22
|
token: { type: 'string', default: '' },
|
|
23
|
+
email: { type: 'string', default: '' },
|
|
23
24
|
gatewayUrl: { type: 'string', default: 'wss://webprev.live' },
|
|
24
25
|
projectId: { type: 'string', default: '' },
|
|
25
26
|
// Legacy fields (kept for backward compat, but per-project config is preferred)
|
package/dist/localServer.js
CHANGED
package/dist/tunnel.js
CHANGED
|
@@ -62,7 +62,7 @@ export class TunnelClient {
|
|
|
62
62
|
// Send enhanced agent info with capabilities and deviceId
|
|
63
63
|
this.send({
|
|
64
64
|
type: 'agent-info',
|
|
65
|
-
version: '0.5.
|
|
65
|
+
version: '0.5.1',
|
|
66
66
|
hostname: os.hostname(),
|
|
67
67
|
platform: `${os.platform()} ${os.arch()}`,
|
|
68
68
|
deviceId: getDeviceId(),
|
package/package.json
CHANGED