terminal-smart-cli 0.97.3 → 0.97.4

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/bin/ts.js CHANGED
@@ -2658,12 +2658,38 @@ async function cloudCmd(args) {
2658
2658
  const sub = String(args[0] || 'status').toLowerCase();
2659
2659
  const rest = args.slice(1).filter(a => !a.startsWith('-'));
2660
2660
  const call = (path, opts = {}) => api('/api/cloud/' + path, { token, ...opts });
2661
+ const cloudSlug = require('../lib/cloud-slug');
2662
+ const chooseSlug = async (provided) => {
2663
+ let value = String(provided || '').trim();
2664
+ if (!value && process.stdin.isTTY && !JSON_OUT && !YES) {
2665
+ value = String(await ui.ask(en
2666
+ ? ' Choose the public name (<name>.tsbox1.com): '
2667
+ : ' Escolha o endereço público (<nome>.tsbox1.com): ')).trim();
2668
+ }
2669
+ const checked = cloudSlug.validate(value);
2670
+ if (!checked.ok) {
2671
+ if (JSON_OUT) console.log(JSON.stringify(checked));
2672
+ else console.log(ui.errLine(checked.error));
2673
+ return null;
2674
+ }
2675
+ return checked.slug;
2676
+ };
2661
2677
 
2662
2678
  if (sub === 'up' || sub === 'start' || sub === 'ligar') {
2679
+ let requestedSlug = rest[0] || '';
2680
+ const current = await call('status');
2681
+ if (requestedSlug) {
2682
+ requestedSlug = await chooseSlug(requestedSlug);
2683
+ if (!requestedSlug) return;
2684
+ } else if (!current.exists || current.needsSlug) {
2685
+ requestedSlug = await chooseSlug('');
2686
+ if (!requestedSlug) return;
2687
+ }
2663
2688
  console.log(C.dim(en ? 'provisioning your cloud box…' : 'provisionando sua caixa na nuvem…'));
2664
2689
  try {
2665
- const r = await call('up', { method: 'POST', body: {}, timeoutMs: 120000 });
2690
+ const r = await call('up', { method: 'POST', body: requestedSlug ? { slug: requestedSlug } : {}, timeoutMs: 120000 });
2666
2691
  if (!r.ok) { console.log(ui.errLine(r.error || 'falha')); if (r.code === 'plan_required') console.log(C.dim(en ? 'Get Ultra at terminalsmart.com.br/planos' : 'Assine o Ultra em terminalsmart.com.br/planos')); return; }
2692
+ if (JSON_OUT) { console.log(JSON.stringify(r)); return; }
2667
2693
  console.log('\n' + ui.box([
2668
2694
  C.ok(en ? 'Cloud box is up!' : 'Caixa na nuvem no ar!'), '',
2669
2695
  C.dim('URL: ') + C.bold(r.url),
@@ -2677,6 +2703,15 @@ async function cloudCmd(args) {
2677
2703
  if (sub === 'status' || sub === 'url') {
2678
2704
  const r = await call('status');
2679
2705
  if (!r.exists) { console.log(ui.infoLine(en ? 'No cloud box yet. Run: ts cloud up' : 'Nenhuma caixa ainda. Rode: ts cloud up')); return; }
2706
+ if (JSON_OUT) { console.log(JSON.stringify(r)); return; }
2707
+ if (r.needsSlug || !r.url) {
2708
+ console.log('\n' + ui.box([
2709
+ C.warn(en ? 'Public name not chosen yet.' : 'Endereço público ainda não escolhido.'),
2710
+ C.dim(en ? 'Choose one: ' : 'Escolha um nome: ') + C.cyan('ts cloud nome <nome>'),
2711
+ C.dim(en ? 'Example: ' : 'Exemplo: ') + C.cyan('ts cloud nome minhaempresa'),
2712
+ ], { title: 'ts cloud' }) + '\n');
2713
+ return;
2714
+ }
2680
2715
  if (sub === 'url') { console.log(r.url); return; }
2681
2716
  const lines = [
2682
2717
  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')),
@@ -2688,6 +2723,19 @@ async function cloudCmd(args) {
2688
2723
  return;
2689
2724
  }
2690
2725
 
2726
+ if (sub === 'nome' || sub === 'name' || sub === 'slug' || sub === 'renomear' || sub === 'rename') {
2727
+ const slug = await chooseSlug(rest[0]);
2728
+ if (!slug) return;
2729
+ const r = await call('slug', { method: 'POST', body: { slug } });
2730
+ if (JSON_OUT) { console.log(JSON.stringify(r)); return; }
2731
+ if (!r.ok) { console.log(ui.errLine(r.error || 'falha')); return; }
2732
+ console.log('\n' + ui.box([
2733
+ C.ok(en ? 'Public address saved!' : 'Endereço público salvo!'),
2734
+ C.dim('URL: ') + C.bold(r.url),
2735
+ ], { title: 'ts cloud' }) + '\n');
2736
+ return;
2737
+ }
2738
+
2691
2739
  if (sub === 'exec' || sub === 'sh' || sub === 'run') {
2692
2740
  const cmd = rest.join(' ').trim();
2693
2741
  if (!cmd) { console.log(ui.errLine(en ? 'usage: ts cloud exec "<command>"' : 'uso: ts cloud exec "<comando>"')); return; }
@@ -2708,7 +2756,13 @@ async function cloudCmd(args) {
2708
2756
  const goal = rest.join(' ').trim();
2709
2757
  if (!goal) { console.log(ui.errLine(en ? 'usage: ts cloud meta "<goal>"' : 'uso: ts cloud meta "<objetivo>"')); return; }
2710
2758
  let st = await call('status');
2711
- 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; } }
2759
+ if (!st.exists || st.needsSlug) {
2760
+ const slug = await chooseSlug('');
2761
+ if (!slug) return;
2762
+ console.log(C.dim(en ? 'creating box…' : 'criando caixa…'));
2763
+ const up = await call('up', { method: 'POST', body: { slug }, timeoutMs: 120000 });
2764
+ if (!up.ok) { console.log(ui.errLine(up.error || 'falha')); if (up.code === 'plan_required') console.log(C.dim('terminalsmart.com.br/planos')); return; }
2765
+ }
2712
2766
  console.log(C.dim(en ? 'running ts meta inside the cloud box (may take a while)…' : 'rodando ts meta dentro da caixa (pode demorar)…'));
2713
2767
  const safe = goal.replace(/(["\\$`])/g, '\\$1');
2714
2768
  const cmdMeta = 'cd /workspace && ts meta "' + safe + '" --yes 2>&1';
@@ -2742,7 +2796,8 @@ async function cloudCmd(args) {
2742
2796
 
2743
2797
  console.log('\n' + ui.box([
2744
2798
  C.bold('ts cloud') + C.dim(en ? ' — your dev box in the cloud' : ' — sua caixa de dev na nuvem'), '',
2745
- C.dim('up') + ' ' + (en ? 'create/start your box + public URL' : 'cria/liga sua caixa + URL pública'),
2799
+ C.dim('up [nome]') + ' ' + (en ? 'create/start your box with a chosen URL' : 'cria/liga sua caixa com endereço escolhido'),
2800
+ C.dim('nome <nome>') + ' ' + (en ? 'choose or change <name>.tsbox1.com' : 'escolhe ou altera <nome>.tsbox1.com'),
2746
2801
  C.dim('meta "…"') + ' ' + (en ? 'the agent builds your app in the cloud' : 'o agente constrói seu app na nuvem'),
2747
2802
  C.dim('exec "…"') + ' ' + (en ? 'run a shell command in the box' : 'roda um comando na caixa'),
2748
2803
  C.dim('url') + ' ' + (en ? 'print the public URL' : 'mostra a URL pública'),
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ const RESERVED = new Set([
4
+ 'www', 'api', 'app', 'admin', 'mail', 'email', 'ftp', 'smtp', 'imap',
5
+ 'pop', 'ns1', 'ns2', 'cdn', 'assets', 'static', 'status', 'suporte',
6
+ 'support', 'help', 'billing', 'checkout', 'teste', 'test', 'terminalsmart',
7
+ 'terminal-smart', 'cloudflare', 'cloud', 'box', 'site', 'ts',
8
+ ]);
9
+
10
+ function normalize(value) {
11
+ return String(value || '')
12
+ .normalize('NFD')
13
+ .replace(/[\u0300-\u036f]/g, '')
14
+ .toLowerCase()
15
+ .trim()
16
+ .replace(/[^a-z0-9]+/g, '-')
17
+ .replace(/^-+|-+$/g, '')
18
+ .replace(/-+/g, '-');
19
+ }
20
+
21
+ function validate(value) {
22
+ const slug = normalize(value);
23
+ if (!slug || slug.length < 3 || slug.length > 40) return { ok: false, code: 'invalid_slug', error: 'O nome deve ter entre 3 e 40 caracteres.' };
24
+ if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(slug)) return { ok: false, code: 'invalid_slug', error: 'Use letras, números e hífens.' };
25
+ if (RESERVED.has(slug)) return { ok: false, code: 'reserved_slug', error: 'Esse nome é reservado pelo Terminal Smart.' };
26
+ return { ok: true, slug };
27
+ }
28
+
29
+ module.exports = { RESERVED, normalize, validate };
30
+
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "terminal-smart-cli",
3
- "version": "0.97.3",
3
+ "version": "0.97.4",
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"
7
7
  },
8
8
  "scripts": {
9
- "test": "node test/core.test.js && node test/intelligence-core.test.js && node test/eval-model.test.js && node test/project-cache.test.js && node test/memory-bus.test.js && node test/capabilities.test.js && node test/mcp-e2e.test.js && node test/erros.test.js && node test/capability-pack.test.js && node test/byok.test.js && node test/conhecimento.test.js && node test/policy.test.js && node test/temas.test.js && node test/skill-index.test.js"
9
+ "test": "node test/core.test.js && node test/intelligence-core.test.js && node test/cloud-slug.test.js && node test/eval-model.test.js && node test/project-cache.test.js && node test/memory-bus.test.js && node test/capabilities.test.js && node test/mcp-e2e.test.js && node test/erros.test.js && node test/capability-pack.test.js && node test/byok.test.js && node test/conhecimento.test.js && node test/policy.test.js && node test/temas.test.js && node test/skill-index.test.js"
10
10
  },
11
11
  "files": [
12
12
  "bin",