ninja-terminals 2.3.3 → 2.3.5
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 +1 -92
- package/package.json +1 -1
- package/public/app.js +4 -42
- 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:
|
|
@@ -278,7 +187,7 @@ Next: Restart Claude Code, then tell Claude:
|
|
|
278
187
|
if (!hasFlag('--setup')) {
|
|
279
188
|
|
|
280
189
|
const port = parseInt(getArg('--port', '3300'), 10);
|
|
281
|
-
const terminals = parseInt(getArg('--terminals', '
|
|
190
|
+
const terminals = parseInt(getArg('--terminals', '4'), 10); // Full access
|
|
282
191
|
const cwd = getArg('--cwd', process.cwd());
|
|
283
192
|
const token = getArg('--token', null);
|
|
284
193
|
const offline = hasFlag('--offline');
|
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -1402,48 +1402,10 @@ function setupLearnings() {
|
|
|
1402
1402
|
// ── Initialize ───────────────────────────────────────────────
|
|
1403
1403
|
|
|
1404
1404
|
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
|
-
}
|
|
1405
|
+
// Auth disabled — app is free, start immediately
|
|
1406
|
+
hideAuthOverlay();
|
|
1407
|
+
startApp();
|
|
1408
|
+
sessionReadyResolve();
|
|
1447
1409
|
}
|
|
1448
1410
|
|
|
1449
1411
|
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);
|