nstantpage-agent 0.5.30 → 0.5.32

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.
@@ -49,16 +49,30 @@ export async function loginCommand(options = {}) {
49
49
  // Check if already logged in (unless --force)
50
50
  const existingToken = conf.get('token');
51
51
  if (existingToken && !options.force) {
52
- const email = conf.get('email') || getEmailFromToken(existingToken);
53
- const storedGateway = conf.get('gatewayUrl') || 'wss://webprev.live';
54
- const isStoredLocal = /^wss?:\/\/(localhost|127\.0\.0\.1)/.test(storedGateway);
55
- console.log(chalk.green('✓ Already authenticated'));
56
- if (email)
57
- console.log(chalk.gray(` Account: ${email}`));
58
- console.log(chalk.gray(` Server: ${isStoredLocal ? 'localhost (dev)' : 'nstantpage.com'}`));
59
- console.log(chalk.gray(' Run "nstantpage login --force" to re-authenticate'));
60
- console.log(chalk.gray(' Run "nstantpage logout" to sign out.'));
61
- return;
52
+ // Check if existing token is expired
53
+ const payload = decodeJwtPayload(existingToken);
54
+ const isExpired = payload?.exp && payload.exp < Date.now() / 1000;
55
+ if (!isExpired) {
56
+ const email = conf.get('email') || getEmailFromToken(existingToken);
57
+ const storedGateway = conf.get('gatewayUrl') || 'wss://webprev.live';
58
+ const isStoredLocal = /^wss?:\/\/(localhost|127\.0\.0\.1)/.test(storedGateway);
59
+ console.log(chalk.green(' Already authenticated'));
60
+ if (email)
61
+ console.log(chalk.gray(` Account: ${email}`));
62
+ console.log(chalk.gray(` Server: ${isStoredLocal ? 'localhost (dev)' : 'nstantpage.com'}`));
63
+ console.log(chalk.gray(' Run "nstantpage login --force" to re-authenticate'));
64
+ console.log(chalk.gray(' Run "nstantpage logout" to sign out.'));
65
+ return;
66
+ }
67
+ // Token expired — clear it and proceed with fresh login
68
+ console.log(chalk.yellow('⚠ Your token has expired. Starting fresh login...\n'));
69
+ conf.delete('token');
70
+ conf.delete('email');
71
+ }
72
+ // If --force, clear old token so we start fresh
73
+ if (options.force && existingToken) {
74
+ conf.delete('token');
75
+ conf.delete('email');
62
76
  }
63
77
  const frontendUrl = resolveFrontendUrl(options.gateway);
64
78
  const isLocal = frontendUrl.includes('localhost');
@@ -79,6 +93,35 @@ export async function loginCommand(options = {}) {
79
93
  const url = new URL(req.url, `http://localhost:${callbackPort}`);
80
94
  const token = url.searchParams.get('token');
81
95
  if (token) {
96
+ // Validate token is not expired before accepting it
97
+ const payload = decodeJwtPayload(token);
98
+ if (payload?.exp && payload.exp < Date.now() / 1000) {
99
+ // Token is expired — clear browser storage and redirect to sign-in
100
+ // After signing in, the browser will get a fresh token and revisit /auth/agent
101
+ const agentCallbackUrl = `http://localhost:${callbackPort}/callback`;
102
+ res.writeHead(200, { 'Content-Type': 'text/html' });
103
+ res.end(`
104
+ <html>
105
+ <body style="font-family: sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #0f172a; color: #f8fafc;">
106
+ <div style="text-align: center;">
107
+ <h1 style="color: #eab308;">⟳ Session expired — signing you in...</h1>
108
+ <p style="color: #94a3b8;">Redirecting to nstantpage.com...</p>
109
+ </div>
110
+ <script>
111
+ // Clear the expired token from browser localStorage
112
+ localStorage.removeItem('auth_token');
113
+ localStorage.removeItem('auth_user');
114
+ // Store the agent callback URL for after sign-in
115
+ sessionStorage.setItem('agentCallbackUrl', '${agentCallbackUrl}');
116
+ // Redirect to sign-in, which will redirect back to /auth/agent after login
117
+ window.location.href = '${frontendUrl}/sign-in?returnUrl=/auth/agent';
118
+ </script>
119
+ </body>
120
+ </html>
121
+ `);
122
+ // Don't close server — wait for a valid token after re-login
123
+ return;
124
+ }
82
125
  res.writeHead(200, { 'Content-Type': 'text/html' });
83
126
  res.end(`
84
127
  <html>
@@ -194,6 +194,21 @@ export async function startCommand(directory, options) {
194
194
  if (!token && isLocalGateway) {
195
195
  token = 'local-dev';
196
196
  }
197
+ // Check token expiry before connecting
198
+ if (token && token !== 'local-dev') {
199
+ try {
200
+ const parts = token.split('.');
201
+ if (parts.length === 3) {
202
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString('utf-8'));
203
+ if (payload.exp && payload.exp < Date.now() / 1000) {
204
+ console.log(chalk.red('✗ Your authentication token has expired.'));
205
+ console.log(chalk.gray(' Run "nstantpage login" to re-authenticate.'));
206
+ process.exit(1);
207
+ }
208
+ }
209
+ }
210
+ catch { }
211
+ }
197
212
  // Determine project ID (optional — without it, agent enters standby mode)
198
213
  // Only use explicitly passed --project-id, never fall back to stored value
199
214
  let projectId = options.projectId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nstantpage-agent",
3
- "version": "0.5.30",
3
+ "version": "0.5.32",
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": {