ninja-terminals 2.3.4 → 2.3.6
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/cli.js +0 -91
- package/package.json +1 -1
- package/public/app.js +6 -45
- package/public/index.html +2 -2
- package/server.js +3 -25
package/cli.js
CHANGED
|
@@ -175,97 +175,6 @@ async function runSetup() {
|
|
|
175
175
|
|
|
176
176
|
console.log(`
|
|
177
177
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
178
|
-
✅ MCP configured!
|
|
179
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
180
|
-
`);
|
|
181
|
-
|
|
182
|
-
// Now prompt for login
|
|
183
|
-
const readline = require('readline');
|
|
184
|
-
const https = require('https');
|
|
185
|
-
const { execSync } = require('child_process');
|
|
186
|
-
const { writeAuthToken } = require('./lib/runtime-session');
|
|
187
|
-
|
|
188
|
-
const BACKEND_URL = process.env.NINJA_BACKEND_URL || 'https://emtchat-backend.onrender.com';
|
|
189
|
-
|
|
190
|
-
function prompt(question) {
|
|
191
|
-
return new Promise((resolve) => {
|
|
192
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
193
|
-
rl.question(question, (answer) => { rl.close(); resolve(answer); });
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function promptPassword(question) {
|
|
198
|
-
return new Promise((resolve) => {
|
|
199
|
-
try { execSync('stty -echo', { stdio: 'pipe' }); } catch {}
|
|
200
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
201
|
-
process.stdout.write(question);
|
|
202
|
-
rl.question('', (answer) => {
|
|
203
|
-
try { execSync('stty echo', { stdio: 'pipe' }); } catch {}
|
|
204
|
-
console.log();
|
|
205
|
-
rl.close();
|
|
206
|
-
resolve(answer);
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function postJson(url, body) {
|
|
212
|
-
return new Promise((resolve, reject) => {
|
|
213
|
-
const parsed = new URL(url);
|
|
214
|
-
const payload = JSON.stringify(body);
|
|
215
|
-
const req = https.request({
|
|
216
|
-
hostname: parsed.hostname,
|
|
217
|
-
port: parsed.port || 443,
|
|
218
|
-
path: parsed.pathname,
|
|
219
|
-
method: 'POST',
|
|
220
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
|
|
221
|
-
}, (res) => {
|
|
222
|
-
let data = '';
|
|
223
|
-
res.on('data', chunk => { data += chunk; });
|
|
224
|
-
res.on('end', () => {
|
|
225
|
-
try { resolve({ status: res.statusCode, body: JSON.parse(data) }); }
|
|
226
|
-
catch { resolve({ status: res.statusCode, body: data }); }
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
req.on('error', reject);
|
|
230
|
-
req.write(payload);
|
|
231
|
-
req.end();
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
async function doLogin() {
|
|
236
|
-
console.log('Login to your Ninja Terminals account:\n');
|
|
237
|
-
const username = await prompt('Username or email: ');
|
|
238
|
-
const password = await promptPassword('Password: ');
|
|
239
|
-
|
|
240
|
-
if (!username || !password) {
|
|
241
|
-
console.log('\n⚠️ Skipped login. Run `npx ninja-login` later to authenticate.');
|
|
242
|
-
return false;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
console.log('Authenticating...');
|
|
246
|
-
try {
|
|
247
|
-
const res = await postJson(`${BACKEND_URL}/api/auth/login`, { username, password });
|
|
248
|
-
if (res.status === 200 && res.body.token) {
|
|
249
|
-
writeAuthToken(res.body.token);
|
|
250
|
-
console.log(`\n✅ Logged in as ${username}`);
|
|
251
|
-
console.log(` Token saved to ~/.ninja/token`);
|
|
252
|
-
return true;
|
|
253
|
-
} else {
|
|
254
|
-
console.log(`\n❌ Login failed: ${res.body.message || res.body.error || 'Unknown error'}`);
|
|
255
|
-
console.log(' Run `npx ninja-login` to try again.');
|
|
256
|
-
return false;
|
|
257
|
-
}
|
|
258
|
-
} catch (err) {
|
|
259
|
-
console.log(`\n❌ Connection failed: ${err.message}`);
|
|
260
|
-
console.log(' Run `npx ninja-login` to try again.');
|
|
261
|
-
return false;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
await doLogin();
|
|
266
|
-
|
|
267
|
-
console.log(`
|
|
268
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
269
178
|
✨ Setup complete!
|
|
270
179
|
|
|
271
180
|
Next: Restart Claude Code, then tell Claude:
|
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -193,9 +193,8 @@ const auth = {
|
|
|
193
193
|
// ── Auth UI ──────────────────────────────────────────────────
|
|
194
194
|
|
|
195
195
|
function showAuthOverlay() {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
document.getElementById('login-email').focus();
|
|
196
|
+
// Auth disabled - app is free, never show auth overlay
|
|
197
|
+
return;
|
|
199
198
|
}
|
|
200
199
|
|
|
201
200
|
function hideAuthOverlay() {
|
|
@@ -1402,48 +1401,10 @@ function setupLearnings() {
|
|
|
1402
1401
|
// ── Initialize ───────────────────────────────────────────────
|
|
1403
1402
|
|
|
1404
1403
|
async function init() {
|
|
1405
|
-
//
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
if (auth.init()) {
|
|
1410
|
-
// Valid local token — hide overlay immediately, start app
|
|
1411
|
-
hideAuthOverlay();
|
|
1412
|
-
startApp();
|
|
1413
|
-
|
|
1414
|
-
// Validate tier in background (network call to backend)
|
|
1415
|
-
auth.validateTier()
|
|
1416
|
-
.then(result => {
|
|
1417
|
-
if (result?.needsLogin) {
|
|
1418
|
-
// Token was rejected by backend — need fresh login
|
|
1419
|
-
showAuthOverlay();
|
|
1420
|
-
}
|
|
1421
|
-
})
|
|
1422
|
-
.catch(err => {
|
|
1423
|
-
console.warn('Tier validation failed:', err);
|
|
1424
|
-
// Network error — continue with cached token
|
|
1425
|
-
})
|
|
1426
|
-
.finally(() => {
|
|
1427
|
-
sessionReadyResolve();
|
|
1428
|
-
});
|
|
1429
|
-
} else {
|
|
1430
|
-
// No valid local token — try bootstrap from saved server token
|
|
1431
|
-
const bootstrapped = await auth.tryBootstrap();
|
|
1432
|
-
if (bootstrapped) {
|
|
1433
|
-
hideAuthOverlay();
|
|
1434
|
-
startApp();
|
|
1435
|
-
auth.validateTier()
|
|
1436
|
-
.then(result => {
|
|
1437
|
-
if (result?.needsLogin) showAuthOverlay();
|
|
1438
|
-
})
|
|
1439
|
-
.catch(err => console.warn('Tier validation failed:', err))
|
|
1440
|
-
.finally(() => sessionReadyResolve());
|
|
1441
|
-
} else {
|
|
1442
|
-
// No saved token — show login
|
|
1443
|
-
showAuthOverlay();
|
|
1444
|
-
sessionReadyResolve();
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1404
|
+
// Auth disabled — app is free, start immediately
|
|
1405
|
+
hideAuthOverlay();
|
|
1406
|
+
startApp();
|
|
1407
|
+
sessionReadyResolve();
|
|
1447
1408
|
}
|
|
1448
1409
|
|
|
1449
1410
|
init();
|
package/public/index.html
CHANGED
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
</div>
|
|
27
27
|
</div>
|
|
28
28
|
|
|
29
|
-
<!-- Auth Overlay -->
|
|
30
|
-
<div id="auth-overlay">
|
|
29
|
+
<!-- Auth Overlay (disabled - app is free) -->
|
|
30
|
+
<div id="auth-overlay" style="display:none;">
|
|
31
31
|
<div class="auth-card">
|
|
32
32
|
<div class="auth-stripes">
|
|
33
33
|
<div class="stripe s1"></div>
|
package/server.js
CHANGED
|
@@ -88,17 +88,8 @@ const terminals = new Map();
|
|
|
88
88
|
const sessionCache = new Map(); // token -> { tier, terminalsMax, features, validatedAt }
|
|
89
89
|
let activeSession = null; // { token, tier, terminalsMax, features, terminalIds: [] }
|
|
90
90
|
|
|
91
|
-
// Auth
|
|
92
|
-
const
|
|
93
|
-
const requireAuth = (req, res, next) => {
|
|
94
|
-
// Skip auth for these paths
|
|
95
|
-
const skipPaths = ['/', '/health', '/api/events'];
|
|
96
|
-
if (skipPaths.includes(req.path)) {
|
|
97
|
-
return next();
|
|
98
|
-
}
|
|
99
|
-
// Apply auth
|
|
100
|
-
return authMiddleware(req, res, next);
|
|
101
|
-
};
|
|
91
|
+
// Auth disabled — app is free, no login required
|
|
92
|
+
const requireAuth = (req, res, next) => next();
|
|
102
93
|
|
|
103
94
|
// Loopback check for local-only endpoints (e.g., auth bootstrap)
|
|
104
95
|
function isLoopbackRequest(req) {
|
|
@@ -412,20 +403,7 @@ server.on('upgrade', async (req, socket, head) => {
|
|
|
412
403
|
return;
|
|
413
404
|
}
|
|
414
405
|
|
|
415
|
-
//
|
|
416
|
-
const token = urlParts.searchParams.get('token');
|
|
417
|
-
if (!token) {
|
|
418
|
-
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
419
|
-
socket.destroy();
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
const validation = await validateWebSocketToken(token, sessionCache);
|
|
424
|
-
if (!validation.valid) {
|
|
425
|
-
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
426
|
-
socket.destroy();
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
406
|
+
// Auth disabled — no token validation needed
|
|
429
407
|
|
|
430
408
|
const id = parseInt(match[1], 10);
|
|
431
409
|
const terminal = terminals.get(id);
|