@neetru/cli 2.3.0 → 2.5.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 (48) hide show
  1. package/dist/commands/artifact-registry.d.ts +7 -0
  2. package/dist/commands/artifact-registry.js +60 -0
  3. package/dist/commands/artifact-registry.js.map +1 -0
  4. package/dist/commands/deploy.js +8 -8
  5. package/dist/commands/deployments.d.ts +5 -5
  6. package/dist/commands/deployments.js +32 -6
  7. package/dist/commands/deployments.js.map +1 -1
  8. package/dist/commands/env.d.ts +11 -0
  9. package/dist/commands/env.js +91 -0
  10. package/dist/commands/env.js.map +1 -1
  11. package/dist/commands/hosting-write.d.ts +6 -0
  12. package/dist/commands/hosting-write.js +52 -0
  13. package/dist/commands/hosting-write.js.map +1 -0
  14. package/dist/commands/products-db.d.ts +9 -9
  15. package/dist/commands/products-db.js +84 -19
  16. package/dist/commands/products-db.js.map +1 -1
  17. package/dist/commands/products.d.ts +2 -2
  18. package/dist/commands/products.js +8 -6
  19. package/dist/commands/products.js.map +1 -1
  20. package/dist/commands/schema.d.ts +25 -0
  21. package/dist/commands/schema.js +321 -0
  22. package/dist/commands/schema.js.map +1 -0
  23. package/dist/commands/servers.d.ts +5 -5
  24. package/dist/commands/servers.js +25 -17
  25. package/dist/commands/servers.js.map +1 -1
  26. package/dist/commands/status.js +3 -2
  27. package/dist/commands/status.js.map +1 -1
  28. package/dist/commands/tenants.d.ts +14 -14
  29. package/dist/commands/tenants.js +62 -30
  30. package/dist/commands/tenants.js.map +1 -1
  31. package/dist/commands/ui.d.ts +1 -0
  32. package/dist/commands/ui.js +454 -0
  33. package/dist/commands/ui.js.map +1 -0
  34. package/dist/commands/workspaces.d.ts +9 -9
  35. package/dist/commands/workspaces.js +66 -31
  36. package/dist/commands/workspaces.js.map +1 -1
  37. package/dist/index.js +164 -65
  38. package/dist/index.js.map +1 -1
  39. package/dist/lib/ai/orchestrator.js +46 -23
  40. package/dist/lib/ai/orchestrator.js.map +1 -1
  41. package/dist/lib/config-schema.d.ts +18 -18
  42. package/dist/lib/pickers.d.ts +123 -0
  43. package/dist/lib/pickers.js +336 -0
  44. package/dist/lib/pickers.js.map +1 -0
  45. package/dist/lib/schema-cache.d.ts +87 -0
  46. package/dist/lib/schema-cache.js +134 -0
  47. package/dist/lib/schema-cache.js.map +1 -0
  48. package/package.json +1 -1
@@ -0,0 +1,7 @@
1
+ import { type StepUpOptions } from '../lib/cli-write.js';
2
+ export interface ArtifactRegistryCreateOptions extends StepUpOptions {
3
+ location?: string;
4
+ format?: string;
5
+ description?: string;
6
+ }
7
+ export declare function runArtifactRegistryCreate(name: string, opts: ArtifactRegistryCreateOptions): Promise<void>;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * `neetru artifact-registry create <name>` — cria repositório no Artifact Registry.
3
+ *
4
+ * Use case típico: setup one-shot por produto SaaS antes do primeiro deploy.
5
+ * Após criado, `docker push <location>-docker.pkg.dev/neetru/<name>/<image>:<tag>`
6
+ * passa a funcionar.
7
+ *
8
+ * Endpoint: POST /api/cli/v1/artifact-registry
9
+ * body: { name, location, format?, description? }
10
+ *
11
+ * Não exige step-up MFA (criação não-destrutiva). Admin only.
12
+ */
13
+ import ora from 'ora';
14
+ import { apiRequest } from '../lib/api-client.js';
15
+ import { requireToken, handleApiError, printWriteResult, } from '../lib/cli-write.js';
16
+ import { log } from '../utils/logger.js';
17
+ export async function runArtifactRegistryCreate(name, opts) {
18
+ if (!name) {
19
+ log.error('Uso: neetru artifact-registry create <name> [--location=us-central1] [--format=DOCKER]');
20
+ process.exit(1);
21
+ }
22
+ const location = opts.location ?? 'us-central1';
23
+ const format = (opts.format ?? 'DOCKER').toUpperCase();
24
+ const token = await requireToken(opts.json);
25
+ const spinner = opts.json
26
+ ? null
27
+ : ora({
28
+ text: `Criando repositório ${name} em ${location} (${format})…`,
29
+ color: 'blue',
30
+ }).start();
31
+ const path = `/api/cli/v1/artifact-registry${opts.dryRun ? '?dryRun=true' : ''}`;
32
+ let res;
33
+ try {
34
+ res = await apiRequest(path, {
35
+ method: 'POST',
36
+ token,
37
+ body: { name, location, format, description: opts.description },
38
+ });
39
+ spinner?.stop();
40
+ }
41
+ catch (error) {
42
+ spinner?.fail('Falha ao criar repositório.');
43
+ handleApiError(error, opts.json);
44
+ }
45
+ const humanMessage = res.done
46
+ ? `Repositório ${res.name} criado em ${res.location}. Push: ${res.pushExample}`
47
+ : `Criação iniciada — operação ${res.operationName ?? '(?)'} ainda em andamento. Confirme via Console ou aguarde uns segundos antes do primeiro push.`;
48
+ printWriteResult(res.dryRun
49
+ ? res
50
+ : {
51
+ name: res.name,
52
+ location: res.location,
53
+ format: res.format,
54
+ done: res.done,
55
+ operationName: res.operationName,
56
+ dockerHost: res.dockerHost,
57
+ pushExample: res.pushExample,
58
+ }, humanMessage, opts.json);
59
+ }
60
+ //# sourceMappingURL=artifact-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-registry.js","sourceRoot":"","sources":["../../src/commands/artifact-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EACL,YAAY,EACZ,cAAc,EACd,gBAAgB,GAEjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAQzC,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,IAAY,EACZ,IAAmC;IAEnC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,GAAG,CAAC,KAAK,CAAC,wFAAwF,CAAC,CAAC;QACpG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,aAAa,CAAC;IAChD,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IAEvD,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE5C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI;QACvB,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,GAAG,CAAC;YACF,IAAI,EAAE,uBAAuB,IAAI,OAAO,QAAQ,KAAK,MAAM,IAAI;YAC/D,KAAK,EAAE,MAAM;SACd,CAAC,CAAC,KAAK,EAAE,CAAC;IAEf,MAAM,IAAI,GAAG,gCAAgC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IAEjF,IAAI,GAWH,CAAC;IACF,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE;YAC3B,MAAM,EAAE,MAAM;YACd,KAAK;YACL,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE;SAChE,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,CAAC;IAClB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAC7C,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,MAAM,YAAY,GAAG,GAAG,CAAC,IAAI;QAC3B,CAAC,CAAC,eAAe,GAAG,CAAC,IAAI,cAAc,GAAG,CAAC,QAAQ,WAAW,GAAG,CAAC,WAAW,EAAE;QAC/E,CAAC,CAAC,+BAA+B,GAAG,CAAC,aAAa,IAAI,KAAK,2FAA2F,CAAC;IAEzJ,gBAAgB,CACd,GAAG,CAAC,MAAM;QACR,CAAC,CAAC,GAAG;QACL,CAAC,CAAC;YACE,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,aAAa,EAAE,GAAG,CAAC,aAAa;YAChC,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,WAAW,EAAE,GAAG,CAAC,WAAW;SAC7B,EACL,YAAY,EACZ,IAAI,CAAC,IAAI,CACV,CAAC;AACJ,CAAC"}
@@ -2,18 +2,18 @@
2
2
  * `neetru deploy` — pipeline interativo de deploy do produto.
3
3
  *
4
4
  * Sprint 3 (P1-1): substitui o stub vazio. Fluxo:
5
- * 1. Resolve produto (lista via /api/v1/cli/catalog).
5
+ * 1. Resolve produto (lista via /api/cli/v1/catalog).
6
6
  * 2. Detecta stack (product_config + fallback local).
7
7
  * 3. Escolhe target (cloud-run | vm-existente | provisionar-nova).
8
- * 4. Para target=vm: lista servers online (/api/v1/cli/servers) +
8
+ * 4. Para target=vm: lista servers online (/api/cli/v1/servers) +
9
9
  * escolha capacity-aware.
10
10
  * 5. Domínio + porta (auto-detect ou input).
11
11
  * 6. Build (chama runBuild se artifact ausente).
12
12
  * 7. Upload artifact (Sprint 1: usa file:// local — owner roda em VM com
13
13
  * acesso ao mesmo FS, OU define `NEETRU_ARTIFACT_BUCKET` e o CLI faz
14
14
  * upload via signed URL — Sprint 2.5).
15
- * 8. POST /api/v1/cli/deploy → recebe deploymentId + statusUrl.
16
- * 9. Polling /api/v1/cli/deploy/status até estado terminal.
15
+ * 8. POST /api/cli/v1/deploy → recebe deploymentId + statusUrl.
16
+ * 9. Polling /api/cli/v1/deploy/status até estado terminal.
17
17
  *
18
18
  * NÃO bloqueia em modo `--non-interactive` quando flags suficientes vieram.
19
19
  */
@@ -113,7 +113,7 @@ async function pickProduct(opts, nonInteractive, cwd) {
113
113
  const spinner = ora({ text: 'Buscando catálogo…', color: 'blue' }).start();
114
114
  let catalog;
115
115
  try {
116
- catalog = await apiRequest('/api/v1/cli/catalog');
116
+ catalog = await apiRequest('/api/cli/v1/catalog');
117
117
  spinner.stop();
118
118
  }
119
119
  catch (err) {
@@ -172,7 +172,7 @@ async function pickServer(opts, nonInteractive) {
172
172
  }
173
173
  if (resp.servers.length === 0) {
174
174
  log.error('Nenhum servidor online encontrado.');
175
- log.dim(' Provisione um com: POST /api/v1/cli/servers/provision');
175
+ log.dim(' Provisione um com: POST /api/cli/v1/servers/provision');
176
176
  process.exit(1);
177
177
  }
178
178
  if (opts.server) {
@@ -433,7 +433,7 @@ async function pollDeploymentStatus(deploymentId) {
433
433
  while (Date.now() - start < timeoutMs) {
434
434
  let resp;
435
435
  try {
436
- resp = await apiRequest(`/api/v1/cli/deploy/status?id=${encodeURIComponent(deploymentId)}`);
436
+ resp = await apiRequest(`/api/cli/v1/deploy/status?id=${encodeURIComponent(deploymentId)}`);
437
437
  }
438
438
  catch (err) {
439
439
  // transient — segue tentando.
@@ -527,7 +527,7 @@ export async function runDeploy(opts) {
527
527
  let deploy;
528
528
  const dispatchSpinner = ora({ text: 'Enviando comando ao Core…', color: 'blue' }).start();
529
529
  try {
530
- deploy = await apiRequest('/api/v1/cli/deploy', {
530
+ deploy = await apiRequest('/api/cli/v1/deploy', {
531
531
  method: 'POST',
532
532
  body,
533
533
  });
@@ -1,10 +1,10 @@
1
1
  import { type StepUpOptions } from '../lib/cli-write.js';
2
2
  export interface DeploymentsCreateOptions {
3
- product: string;
4
- tenant: string;
5
- version: string;
6
- env: string;
7
- server: string;
3
+ product?: string;
4
+ tenant?: string;
5
+ version?: string;
6
+ env?: string;
7
+ server?: string;
8
8
  json?: boolean;
9
9
  }
10
10
  export declare function runDeploymentsCreate(opts: DeploymentsCreateOptions): Promise<void>;
@@ -11,23 +11,49 @@
11
11
  import ora from 'ora';
12
12
  import { apiRequest } from '../lib/api-client.js';
13
13
  import { requireToken, handleApiError, confirmDestructive, printWriteResult, resolveStepUp, } from '../lib/cli-write.js';
14
+ import { pickProductId, pickServer, pickTenant, promptChoice, promptText } from '../lib/pickers.js';
14
15
  import { log } from '../utils/logger.js';
15
16
  export async function runDeploymentsCreate(opts) {
16
17
  const token = await requireToken(opts.json);
18
+ const product = await pickProductId(token, {
19
+ fromFlag: opts.product,
20
+ json: opts.json,
21
+ message: 'Produto do deployment:',
22
+ });
23
+ const tenant = await pickTenant(token, {
24
+ fromFlag: opts.tenant,
25
+ product,
26
+ json: opts.json,
27
+ message: 'Tenant alvo:',
28
+ });
29
+ const version = opts.version ?? (await promptText('Versao:', { json: opts.json }));
30
+ const env = opts.env ??
31
+ (await promptChoice('Ambiente:', [
32
+ { name: 'dev', value: 'dev' },
33
+ { name: 'staging', value: 'staging' },
34
+ { name: 'prod', value: 'prod' },
35
+ ], { default: 'dev', json: opts.json }));
36
+ const server = await pickServer(token, {
37
+ fromFlag: opts.server,
38
+ status: 'online',
39
+ capacity: true,
40
+ json: opts.json,
41
+ message: 'Server alvo:',
42
+ });
17
43
  const spinner = opts.json
18
44
  ? null
19
- : ora({ text: `Criando deployment ${opts.product}@${opts.version}…`, color: 'blue' }).start();
45
+ : ora({ text: `Criando deployment ${product}@${version}...`, color: 'blue' }).start();
20
46
  let res;
21
47
  try {
22
48
  res = await apiRequest('/api/cli/v1/deployments', {
23
49
  method: 'POST',
24
50
  token,
25
51
  body: {
26
- productId: opts.product,
27
- tenantId: opts.tenant,
28
- version: opts.version,
29
- environmentId: opts.env,
30
- serverId: opts.server,
52
+ productId: product,
53
+ tenantId: tenant,
54
+ version,
55
+ environmentId: env,
56
+ serverId: server,
31
57
  },
32
58
  });
33
59
  spinner?.stop();
@@ -1 +1 @@
1
- {"version":3,"file":"deployments.js","sourceRoot":"","sources":["../../src/commands/deployments.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"deployments.js","sourceRoot":"","sources":["../../src/commands/deployments.ts"],"names":[],"mappings":"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"}
@@ -12,4 +12,15 @@ export interface EnvSwitchOptions {
12
12
  */
13
13
  export declare function applyEnvSwitch(content: string, value: EnvTarget): string;
14
14
  export declare function runEnvSwitch(opts: EnvSwitchOptions): Promise<void>;
15
+ import { type StepUpOptions } from '../lib/cli-write.js';
16
+ export interface EnvSetOptions extends StepUpOptions {
17
+ service: string;
18
+ /** Repetível: --set KEY=VALUE */
19
+ set?: string[];
20
+ /** Repetível: --secret KEY=secretName[:version] */
21
+ secret?: string[];
22
+ /** Repetível: --unset KEY */
23
+ unset?: string[];
24
+ }
25
+ export declare function runEnvSet(opts: EnvSetOptions): Promise<void>;
15
26
  export {};
@@ -53,4 +53,95 @@ export async function runEnvSwitch(opts) {
53
53
  await fs.writeFile(filePath, updated, 'utf-8');
54
54
  log.success(`NEETRU_ENV=${target} (${file}).`);
55
55
  }
56
+ // ─── neetru env set --service=X --key=Y --value=Z ──────────────────────────
57
+ //
58
+ // Mutação REMOTA: env vars de um serviço Cloud Run via control plane Core.
59
+ // Distinto do `env switch` (local .env.local). Operação merge — preserva vars
60
+ // existentes não-listadas em --unset. Suporta secretRefs apontando pra
61
+ // Secret Manager.
62
+ //
63
+ // Step-up MFA obrigatório (env vars contêm credenciais efetivas).
64
+ import ora from 'ora';
65
+ import { apiRequest } from '../lib/api-client.js';
66
+ import { requireToken, handleApiError, printWriteResult, resolveStepUp, } from '../lib/cli-write.js';
67
+ function parseKeyValue(pair) {
68
+ const idx = pair.indexOf('=');
69
+ if (idx < 1) {
70
+ throw new Error(`Formato inválido (esperado KEY=VALUE): ${pair}`);
71
+ }
72
+ const k = pair.slice(0, idx);
73
+ const v = pair.slice(idx + 1);
74
+ if (!k)
75
+ throw new Error(`KEY vazia em: ${pair}`);
76
+ return [k, v];
77
+ }
78
+ function parseSecretRef(pair) {
79
+ const [k, ref] = parseKeyValue(pair);
80
+ // ref pode ser "secretName" ou "secretName:version"
81
+ const colonIdx = ref.lastIndexOf(':');
82
+ if (colonIdx > 0) {
83
+ return [k, { secret: ref.slice(0, colonIdx), version: ref.slice(colonIdx + 1) }];
84
+ }
85
+ return [k, { secret: ref, version: 'latest' }];
86
+ }
87
+ export async function runEnvSet(opts) {
88
+ if (!opts.service) {
89
+ log.error('Uso: neetru env set --service=X [--set KEY=VAL] [--secret KEY=secretName[:version]] [--unset KEY]');
90
+ process.exit(1);
91
+ }
92
+ const envVars = {};
93
+ const secretRefs = {};
94
+ const unset = opts.unset ?? [];
95
+ try {
96
+ for (const pair of opts.set ?? []) {
97
+ const [k, v] = parseKeyValue(pair);
98
+ envVars[k] = v;
99
+ }
100
+ for (const pair of opts.secret ?? []) {
101
+ const [k, ref] = parseSecretRef(pair);
102
+ secretRefs[k] = ref;
103
+ }
104
+ }
105
+ catch (err) {
106
+ log.error(err instanceof Error ? err.message : String(err));
107
+ process.exit(1);
108
+ }
109
+ if (Object.keys(envVars).length === 0 &&
110
+ Object.keys(secretRefs).length === 0 &&
111
+ unset.length === 0) {
112
+ log.error('Nada para alterar — passe --set, --secret ou --unset.');
113
+ process.exit(1);
114
+ }
115
+ const token = await requireToken(opts.json);
116
+ const { headers } = await resolveStepUp(opts);
117
+ const spinner = opts.json
118
+ ? null
119
+ : ora({
120
+ text: `Atualizando env vars em ${opts.service}…`,
121
+ color: 'blue',
122
+ }).start();
123
+ const path = `/api/cli/v1/cloud-run/${encodeURIComponent(opts.service)}/env${opts.dryRun ? '?dryRun=true' : ''}`;
124
+ let res;
125
+ try {
126
+ res = await apiRequest(path, {
127
+ method: 'POST',
128
+ token,
129
+ headers,
130
+ body: { envVars, secretRefs, unset },
131
+ });
132
+ spinner?.stop();
133
+ }
134
+ catch (error) {
135
+ spinner?.fail('Falha ao atualizar env vars.');
136
+ handleApiError(error, opts.json);
137
+ }
138
+ const setSummary = [
139
+ Object.keys(envVars).length ? `${Object.keys(envVars).length} var(s)` : null,
140
+ Object.keys(secretRefs).length ? `${Object.keys(secretRefs).length} secret(s)` : null,
141
+ unset.length ? `${unset.length} removida(s)` : null,
142
+ ]
143
+ .filter(Boolean)
144
+ .join(' · ');
145
+ printWriteResult(res.dryRun ? res : { service: res.service, totalEnvVars: res.totalEnvVars }, `Env vars atualizadas em ${res.service} (${setSummary}; total agora: ${res.totalEnvVars}).`, opts.json);
146
+ }
56
147
  //# sourceMappingURL=env.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/commands/env.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAClC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAEzC,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,YAAY,CAAU,CAAC;AAU9D,SAAS,WAAW,CAAC,CAAS;IAC5B,OAAQ,SAA+B,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,KAAgB;IAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrC,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9C,IAAI,CAAC,EAAE,CAAC;YACN,KAAK,GAAG,IAAI,CAAC;YACb,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,KAAK,EAAE,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IACD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/D,GAAG,CAAC,IAAI,CAAC,cAAc,KAAK,EAAE,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAsB;IACvD,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,GAAG,YAAY,EAAE,GAAG,IAAI,CAAC;IAClE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,GAAG,CAAC,KAAK,CAAC,WAAW,MAAM,oBAAoB,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACzE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChB,OAAO;IACT,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACtC,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChC,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC;IACD,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,GAAG,CAAC,OAAO,CAAC,cAAc,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC;AACjD,CAAC"}
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/commands/env.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type StepUpOptions } from '../lib/cli-write.js';
2
+ export interface HostingCreateMappingOptions extends StepUpOptions {
3
+ service: string;
4
+ domain: string;
5
+ }
6
+ export declare function runHostingCreateMapping(opts: HostingCreateMappingOptions): Promise<void>;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * `neetru hosting create-mapping` — cria domain mapping pra Cloud Run.
3
+ *
4
+ * Endpoint: POST /api/cli/v1/hosting/mappings
5
+ * body: { service: string, domain: string }
6
+ *
7
+ * Step-up MFA: SIM (pode redirecionar tráfego público).
8
+ * Owner ainda precisa criar o CNAME no DNS provider externo.
9
+ */
10
+ import ora from 'ora';
11
+ import { apiRequest } from '../lib/api-client.js';
12
+ import { requireToken, handleApiError, printWriteResult, resolveStepUp, } from '../lib/cli-write.js';
13
+ import { log } from '../utils/logger.js';
14
+ export async function runHostingCreateMapping(opts) {
15
+ if (!opts.service || !opts.domain) {
16
+ log.error('Uso: neetru hosting create-mapping --service=<service> --domain=<domain>');
17
+ process.exit(1);
18
+ }
19
+ const token = await requireToken(opts.json);
20
+ const { headers } = await resolveStepUp(opts);
21
+ const spinner = opts.json
22
+ ? null
23
+ : ora({
24
+ text: `Criando mapping ${opts.domain} → ${opts.service}…`,
25
+ color: 'blue',
26
+ }).start();
27
+ const path = `/api/cli/v1/hosting/mappings${opts.dryRun ? '?dryRun=true' : ''}`;
28
+ let res;
29
+ try {
30
+ res = await apiRequest(path, {
31
+ method: 'POST',
32
+ token,
33
+ headers,
34
+ body: { service: opts.service, domain: opts.domain },
35
+ });
36
+ spinner?.stop();
37
+ }
38
+ catch (error) {
39
+ spinner?.fail('Falha ao criar domain mapping.');
40
+ handleApiError(error, opts.json);
41
+ }
42
+ const verb = res.alreadyExisted ? 'já existia' : 'criado';
43
+ printWriteResult(res.dryRun
44
+ ? res
45
+ : {
46
+ service: res.service,
47
+ domain: res.domain,
48
+ alreadyExisted: res.alreadyExisted,
49
+ hint: res.hint,
50
+ }, `Domain mapping ${verb}: ${res.domain} → ${res.service}. ${res.hint ?? ''}`.trim(), opts.json);
51
+ }
52
+ //# sourceMappingURL=hosting-write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosting-write.js","sourceRoot":"","sources":["../../src/commands/hosting-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EACL,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,aAAa,GAEd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAOzC,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,IAAiC;IAEjC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAClC,GAAG,CAAC,KAAK,CAAC,0EAA0E,CAAC,CAAC;QACtF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;IAE9C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI;QACvB,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,GAAG,CAAC;YACF,IAAI,EAAE,mBAAmB,IAAI,CAAC,MAAM,MAAM,IAAI,CAAC,OAAO,GAAG;YACzD,KAAK,EAAE,MAAM;SACd,CAAC,CAAC,KAAK,EAAE,CAAC;IAEf,MAAM,IAAI,GAAG,+BAA+B,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IAEhF,IAAI,GAQH,CAAC;IACF,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE;YAC3B,MAAM,EAAE,MAAM;YACd,KAAK;YACL,OAAO;YACP,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;SACrD,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,CAAC;IAClB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,CAAC,gCAAgC,CAAC,CAAC;QAChD,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC1D,gBAAgB,CACd,GAAG,CAAC,MAAM;QACR,CAAC,CAAC,GAAG;QACL,CAAC,CAAC;YACE,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,cAAc,EAAE,GAAG,CAAC,cAAc;YAClC,IAAI,EAAE,GAAG,CAAC,IAAI;SACf,EACL,kBAAkB,IAAI,KAAK,GAAG,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,KAAK,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAClF,IAAI,CAAC,IAAI,CACV,CAAC;AACJ,CAAC"}
@@ -6,30 +6,30 @@ export interface DbListOptions {
6
6
  }
7
7
  export declare function runDbList(opts: DbListOptions): Promise<void>;
8
8
  export interface DbCreateOptions {
9
- productId: string;
10
- label: string;
11
- engine: string;
12
- environment: string;
9
+ productId?: string;
10
+ label?: string;
11
+ engine?: string;
12
+ env?: string;
13
13
  region: string;
14
14
  serverId?: string;
15
15
  replicaCount?: string;
16
16
  json?: boolean;
17
17
  }
18
18
  export declare function runDbCreate(opts: DbCreateOptions): Promise<void>;
19
- export declare function runDbGet(id: string, opts: {
19
+ export declare function runDbGet(id: string | undefined, opts: {
20
20
  json?: boolean;
21
21
  }): Promise<void>;
22
- export declare function runDbStatus(id: string, status: string, opts: {
22
+ export declare function runDbStatus(id: string | undefined, status: string | undefined, opts: {
23
23
  reason?: string;
24
24
  json?: boolean;
25
25
  }): Promise<void>;
26
- export declare function runDbRetry(id: string, opts: {
26
+ export declare function runDbRetry(id: string | undefined, opts: {
27
27
  json?: boolean;
28
28
  }): Promise<void>;
29
- export declare function runDbRotate(id: string, opts: {
29
+ export declare function runDbRotate(id: string | undefined, opts: {
30
30
  json?: boolean;
31
31
  }): Promise<void>;
32
- export declare function runDbDelete(id: string, opts: {
32
+ export declare function runDbDelete(id: string | undefined, opts: {
33
33
  json?: boolean;
34
34
  }): Promise<void>;
35
35
  export declare function runDbEngines(opts: {
@@ -9,6 +9,7 @@ import ora from 'ora';
9
9
  import { apiRequest } from '../lib/api-client.js';
10
10
  import { requireToken, handleApiError } from '../lib/cli-read.js';
11
11
  import { renderTable } from '../lib/render.js';
12
+ import { pickDatabase, pickProductId, pickServer, promptChoice, promptText, } from '../lib/pickers.js';
12
13
  import { log } from '../utils/logger.js';
13
14
  function colorStatus(s) {
14
15
  if (s === 'active')
@@ -59,45 +60,76 @@ export async function runDbList(opts) {
59
60
  }
60
61
  export async function runDbCreate(opts) {
61
62
  const token = await requireToken(opts.json);
63
+ const productId = await pickProductId(token, {
64
+ fromFlag: opts.productId,
65
+ json: opts.json,
66
+ message: 'Produto do banco:',
67
+ });
68
+ const label = opts.label ?? (await promptText('Label do banco:', { json: opts.json }));
69
+ const engine = opts.engine ??
70
+ (await promptChoice('Engine:', [
71
+ { name: 'firestore-instance', value: 'firestore-instance' },
72
+ { name: 'cloud-sql-postgres', value: 'cloud-sql-postgres' },
73
+ { name: 'cloud-sql-mysql', value: 'cloud-sql-mysql' },
74
+ { name: 'vm-postgres-single', value: 'vm-postgres-single' },
75
+ { name: 'vm-postgres-cluster', value: 'vm-postgres-cluster' },
76
+ { name: 'vm-mysql-single', value: 'vm-mysql-single' },
77
+ { name: 'vm-mysql-cluster', value: 'vm-mysql-cluster' },
78
+ ], { default: 'firestore-instance', json: opts.json }));
79
+ const environment = opts.env ??
80
+ (await promptChoice('Ambiente:', [
81
+ { name: 'dev', value: 'dev' },
82
+ { name: 'staging', value: 'staging' },
83
+ { name: 'production', value: 'production' },
84
+ ], { default: 'dev', json: opts.json }));
85
+ const serverId = opts.serverId ??
86
+ (engine.startsWith('vm-')
87
+ ? await pickServer(token, {
88
+ json: opts.json,
89
+ status: 'online',
90
+ capacity: true,
91
+ message: 'Server para o banco VM:',
92
+ })
93
+ : undefined);
62
94
  let provisioning;
63
- if (opts.engine === 'firestore-instance') {
95
+ if (engine === 'firestore-instance') {
64
96
  provisioning = {
65
- engine: opts.engine,
97
+ engine,
66
98
  config: { locationId: opts.region, type: 'regional' },
67
99
  };
68
100
  }
69
- else if (opts.engine.startsWith('cloud-sql-')) {
101
+ else if (engine.startsWith('cloud-sql-')) {
70
102
  provisioning = {
71
- engine: opts.engine,
103
+ engine,
72
104
  config: {
73
105
  region: opts.region,
74
106
  tier: 'db-custom-2-7680',
75
107
  storageGb: 50,
76
- highAvailability: opts.environment === 'production',
108
+ highAvailability: environment === 'production',
77
109
  },
78
110
  };
79
111
  }
80
112
  else {
81
113
  provisioning = {
82
- engine: opts.engine,
114
+ engine,
83
115
  config: {
84
116
  region: opts.region,
85
117
  zone: `${opts.region}-a`,
86
118
  machineType: 'e2-standard-4',
87
119
  diskGb: 100,
88
- ...(opts.engine.endsWith('-cluster')
120
+ ...(engine.endsWith('-cluster')
89
121
  ? { replicaCount: Number(opts.replicaCount ?? '2') || 2 }
90
122
  : {}),
91
123
  },
92
124
  };
93
125
  }
94
126
  const body = {
95
- productId: opts.productId,
96
- label: opts.label,
97
- engine: opts.engine,
98
- environment: opts.environment,
127
+ productId,
128
+ label,
129
+ engine,
130
+ environment,
99
131
  provisioning,
100
- ...(opts.serverId ? { serverId: opts.serverId } : {}),
132
+ ...(serverId ? { serverId } : {}),
101
133
  };
102
134
  const spinner = opts.json ? null : ora({ text: 'Criando banco…', color: 'blue' }).start();
103
135
  try {
@@ -121,8 +153,13 @@ export async function runDbCreate(opts) {
121
153
  // ─── get ─────────────────────────────────────────────────────────────────────
122
154
  export async function runDbGet(id, opts) {
123
155
  const token = await requireToken(opts.json);
156
+ const databaseId = await pickDatabase(token, {
157
+ fromFlag: id,
158
+ json: opts.json,
159
+ message: 'Banco:',
160
+ });
124
161
  try {
125
- const res = await apiRequest(`/api/cli/v1/db/${encodeURIComponent(id)}/get`, { token });
162
+ const res = await apiRequest(`/api/cli/v1/db/${encodeURIComponent(databaseId)}/get`, { token });
126
163
  if (opts.json) {
127
164
  console.log(JSON.stringify(res));
128
165
  return;
@@ -145,17 +182,30 @@ export async function runDbGet(id, opts) {
145
182
  // ─── status / retry / rotate / delete ────────────────────────────────────────
146
183
  export async function runDbStatus(id, status, opts) {
147
184
  const token = await requireToken(opts.json);
185
+ const databaseId = await pickDatabase(token, {
186
+ fromFlag: id,
187
+ json: opts.json,
188
+ message: 'Banco:',
189
+ });
190
+ const resolvedStatus = status ??
191
+ (await promptChoice('Novo status:', [
192
+ { name: 'active', value: 'active' },
193
+ { name: 'provisioning', value: 'provisioning' },
194
+ { name: 'degraded', value: 'degraded' },
195
+ { name: 'failed', value: 'failed' },
196
+ { name: 'archived', value: 'archived' },
197
+ ], { default: 'active', json: opts.json }));
148
198
  try {
149
- const res = await apiRequest(`/api/cli/v1/db/${encodeURIComponent(id)}/status`, {
199
+ const res = await apiRequest(`/api/cli/v1/db/${encodeURIComponent(databaseId)}/status`, {
150
200
  token,
151
201
  method: 'POST',
152
- body: { status, reason: opts.reason },
202
+ body: { status: resolvedStatus, reason: opts.reason },
153
203
  });
154
204
  if (opts.json) {
155
205
  console.log(JSON.stringify(res));
156
206
  return;
157
207
  }
158
- log.success(`Status atualizado: ${chalk.cyan(status)}`);
208
+ log.success(`Status atualizado: ${chalk.cyan(resolvedStatus)}`);
159
209
  }
160
210
  catch (error) {
161
211
  handleApiError(error, opts.json);
@@ -163,8 +213,13 @@ export async function runDbStatus(id, status, opts) {
163
213
  }
164
214
  export async function runDbRetry(id, opts) {
165
215
  const token = await requireToken(opts.json);
216
+ const databaseId = await pickDatabase(token, {
217
+ fromFlag: id,
218
+ json: opts.json,
219
+ message: 'Banco para retry:',
220
+ });
166
221
  try {
167
- const res = await apiRequest(`/api/cli/v1/db/${encodeURIComponent(id)}/retry`, {
222
+ const res = await apiRequest(`/api/cli/v1/db/${encodeURIComponent(databaseId)}/retry`, {
168
223
  token,
169
224
  method: 'POST',
170
225
  body: {},
@@ -181,8 +236,13 @@ export async function runDbRetry(id, opts) {
181
236
  }
182
237
  export async function runDbRotate(id, opts) {
183
238
  const token = await requireToken(opts.json);
239
+ const databaseId = await pickDatabase(token, {
240
+ fromFlag: id,
241
+ json: opts.json,
242
+ message: 'Banco para rotacionar:',
243
+ });
184
244
  try {
185
- const res = await apiRequest(`/api/cli/v1/db/${encodeURIComponent(id)}/rotate-credentials`, { token, method: 'POST', body: {} });
245
+ const res = await apiRequest(`/api/cli/v1/db/${encodeURIComponent(databaseId)}/rotate-credentials`, { token, method: 'POST', body: {} });
186
246
  if (opts.json) {
187
247
  console.log(JSON.stringify(res));
188
248
  return;
@@ -195,8 +255,13 @@ export async function runDbRotate(id, opts) {
195
255
  }
196
256
  export async function runDbDelete(id, opts) {
197
257
  const token = await requireToken(opts.json);
258
+ const databaseId = await pickDatabase(token, {
259
+ fromFlag: id,
260
+ json: opts.json,
261
+ message: 'Banco para arquivar:',
262
+ });
198
263
  try {
199
- const res = await apiRequest(`/api/cli/v1/db/${encodeURIComponent(id)}/delete`, {
264
+ const res = await apiRequest(`/api/cli/v1/db/${encodeURIComponent(databaseId)}/delete`, {
200
265
  token,
201
266
  method: 'POST',
202
267
  body: {},