terminal-smart-cli 0.47.0 → 0.49.0

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.
Files changed (2) hide show
  1. package/bin/ts.js +30 -10
  2. package/package.json +1 -1
package/bin/ts.js CHANGED
@@ -1360,17 +1360,26 @@ async function cloudCmd(args) {
1360
1360
  const r = await call('status');
1361
1361
  if (!r.exists) { console.log(ui.infoLine(en ? 'No cloud box yet. Run: ts cloud up' : 'Nenhuma caixa ainda. Rode: ts cloud up')); return; }
1362
1362
  if (sub === 'url') { console.log(r.url); return; }
1363
- console.log('\n' + ui.box([
1364
- C.dim('status: ') + (r.running ? C.ok(en ? 'running' : 'no ar') : C.err(en ? 'stopped' : 'parada')),
1363
+ const lines = [
1364
+ C.dim('status: ') + (r.running ? C.ok(en ? 'running' : 'no ar') : C.dim(en ? 'suspended (idle) — wakes on next use' : 'suspensa (ociosa) — acorda no próximo uso')),
1365
1365
  C.dim('URL: ') + C.bold(r.url),
1366
1366
  C.dim(en ? 'port: ' : 'porta: ') + r.port,
1367
- ], { title: 'ts cloud' }) + '\n');
1367
+ ];
1368
+ if (r.running && typeof r.idleMin === 'number') lines.push(C.dim(en ? 'idle: ' : 'ociosa há: ') + r.idleMin + ' min');
1369
+ console.log('\n' + ui.box(lines, { title: 'ts cloud' }) + '\n');
1368
1370
  return;
1369
1371
  }
1370
1372
 
1371
1373
  if (sub === 'exec' || sub === 'sh' || sub === 'run') {
1372
1374
  const cmd = rest.join(' ').trim();
1373
1375
  if (!cmd) { console.log(ui.errLine(en ? 'usage: ts cloud exec "<command>"' : 'uso: ts cloud exec "<comando>"')); return; }
1376
+ // Saída AO VIVO via SSE quando é terminal interativo; fallback pro modo bloqueante.
1377
+ if (process.stdout.isTTY) {
1378
+ try {
1379
+ await sse('/api/cloud/exec-stream', { token, body: { cmd, timeoutMs: 300000 }, timeoutMs: 310000, onEvent: (e) => { if (e.chunk) process.stdout.write(e.chunk); } });
1380
+ return;
1381
+ } catch (e) { if (e.code === 'auth') { console.log(ui.errLine(T.need_login)); return; } }
1382
+ }
1374
1383
  const r = await call('exec', { method: 'POST', body: { cmd }, timeoutMs: 300000 });
1375
1384
  if (r.out) process.stdout.write(r.out);
1376
1385
  if (!r.ok && r.error) console.log(ui.errLine(r.error));
@@ -1384,13 +1393,24 @@ async function cloudCmd(args) {
1384
1393
  if (!st.exists) { console.log(C.dim(en ? 'creating box…' : 'criando caixa…')); const up = await call('up', { method: 'POST', body: {}, timeoutMs: 120000 }); if (!up.ok) { console.log(ui.errLine(up.error || 'falha')); if (up.code === 'plan_required') console.log(C.dim('terminalsmart.com.br/planos')); return; } }
1385
1394
  console.log(C.dim(en ? 'running ts meta inside the cloud box (may take a while)…' : 'rodando ts meta dentro da caixa (pode demorar)…'));
1386
1395
  const safe = goal.replace(/(["\\$`])/g, '\\$1');
1387
- // build com ts meta e, ao terminar, EXPÕE o app na porta 3000 (mapeada pro subdomínio):
1388
- // npm start se houver, senão serve estático. Assim a caixa fica no ar de verdade.
1389
- const expose = 'pkill -f "http.server 3000" 2>/dev/null; pkill -f "PORT=3000" 2>/dev/null; ' +
1390
- 'if [ -f package.json ] && grep -q \'"start"\' package.json; then (PORT=3000 nohup npm start >/tmp/app.log 2>&1 &); ' +
1391
- 'elif [ -f index.html ] || ls *.html >/dev/null 2>&1; then (nohup python3 -m http.server 3000 >/tmp/app.log 2>&1 &); fi; sleep 2; echo "[ts cloud] app exposto na porta 3000"';
1392
- const r = await call('exec', { method: 'POST', body: { cmd: 'cd /workspace && ts meta "' + safe + '" --yes 2>&1 | tail -50; ' + expose }, timeoutMs: 1800000 });
1393
- if (r.out) process.stdout.write(r.out);
1396
+ const cmdMeta = 'cd /workspace && ts meta "' + safe + '" --yes 2>&1';
1397
+ // Build AO VIVO via SSE quando é terminal interativo; fallback bloqueante com tail.
1398
+ let streamed = false;
1399
+ if (process.stdout.isTTY) {
1400
+ try {
1401
+ await sse('/api/cloud/exec-stream', { token, body: { cmd: cmdMeta, timeoutMs: 1800000 }, timeoutMs: 1860000, onEvent: (e) => { if (e.chunk) process.stdout.write(e.chunk); } });
1402
+ streamed = true;
1403
+ } catch (e) { if (e.code === 'auth') { console.log(ui.errLine(T.need_login)); return; } }
1404
+ }
1405
+ if (!streamed) {
1406
+ const r = await call('exec', { method: 'POST', body: { cmd: cmdMeta + ' | tail -50' }, timeoutMs: 1800000 });
1407
+ if (r.out) process.stdout.write(r.out);
1408
+ }
1409
+ // EXPÕE o app na porta 3000 de forma PERSISTENTE (o startCmd fica salvo e é
1410
+ // re-executado no resume, então sobrevive ao idle-suspend). npm start senão estático.
1411
+ const startCmd = 'if [ -f package.json ] && grep -q \'"start"\' package.json; then PORT=3000 npm start; elif [ -f index.html ] || ls *.html >/dev/null 2>&1; then python3 -m http.server 3000; fi';
1412
+ await call('expose', { method: 'POST', body: { cmd: startCmd } });
1413
+ console.log(C.dim(en ? '[ts cloud] app exposed on :3000 (survives idle-suspend)' : '[ts cloud] app exposto na :3000 (sobrevive ao idle-suspend)'));
1394
1414
  const u = await call('status'); if (u && u.url) console.log('\n' + ui.okLine((en ? 'Live at: ' : 'No ar em: ') + u.url));
1395
1415
  return;
1396
1416
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminal-smart-cli",
3
- "version": "0.47.0",
3
+ "version": "0.49.0",
4
4
  "description": "Terminal Smart no seu terminal — pergunte, analise logs por pipe e orquestre agentes de IA. Comando: ts",
5
5
  "bin": {
6
6
  "ts": "bin/ts.js"