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 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.0');
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 & start the agent as a background service (starts on boot)')
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
@@ -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
- console.log(chalk.gray(' Run "nstantpage start" to connect a project'));
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,4 @@
1
+ /**
2
+ * Logout command — clear stored credentials
3
+ */
4
+ export declare function logoutCommand(): Promise<void>;
@@ -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>;
@@ -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');
@@ -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.0';
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
- let projectId = options.projectId || conf.get('projectId');
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)
@@ -502,7 +502,7 @@ export class LocalServer {
502
502
  connected: true,
503
503
  projectId: this.options.projectId,
504
504
  agent: {
505
- version: '0.5.0',
505
+ version: '0.5.1',
506
506
  hostname: os.hostname(),
507
507
  platform: `${os.platform()} ${os.arch()}`,
508
508
  },
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.0',
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nstantpage-agent",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Local development agent for nstantpage.com — run your projects locally, preview in the cloud. Replaces cloud containers for faster builds.",
5
5
  "type": "module",
6
6
  "bin": {