@neetru/cli 1.0.1 → 2.1.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 (192) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/README.md +109 -152
  3. package/dist/commands/add.d.ts +8 -3
  4. package/dist/commands/add.js +70 -143
  5. package/dist/commands/add.js.map +1 -1
  6. package/dist/commands/agent-release.d.ts +13 -0
  7. package/dist/commands/agent-release.js +204 -0
  8. package/dist/commands/agent-release.js.map +1 -0
  9. package/dist/commands/agent-write.d.ts +12 -0
  10. package/dist/commands/agent-write.js +94 -0
  11. package/dist/commands/agent-write.js.map +1 -0
  12. package/dist/commands/ai.d.ts +4 -0
  13. package/dist/commands/ai.js +88 -0
  14. package/dist/commands/ai.js.map +1 -0
  15. package/dist/commands/api-catalog.d.ts +20 -0
  16. package/dist/commands/api-catalog.js +126 -0
  17. package/dist/commands/api-catalog.js.map +1 -0
  18. package/dist/commands/audit.d.ts +8 -0
  19. package/dist/commands/audit.js +69 -0
  20. package/dist/commands/audit.js.map +1 -0
  21. package/dist/commands/autocomplete.d.ts +7 -0
  22. package/dist/commands/autocomplete.js +107 -0
  23. package/dist/commands/autocomplete.js.map +1 -0
  24. package/dist/commands/billing.d.ts +6 -0
  25. package/dist/commands/billing.js +69 -0
  26. package/dist/commands/billing.js.map +1 -0
  27. package/dist/commands/build.d.ts +18 -0
  28. package/dist/commands/build.js +295 -0
  29. package/dist/commands/build.js.map +1 -0
  30. package/dist/commands/cloud-run.d.ts +11 -0
  31. package/dist/commands/cloud-run.js +87 -0
  32. package/dist/commands/cloud-run.js.map +1 -0
  33. package/dist/commands/config.d.ts +3 -0
  34. package/dist/commands/config.js +70 -0
  35. package/dist/commands/config.js.map +1 -0
  36. package/dist/commands/db.d.ts +14 -0
  37. package/dist/commands/db.js +187 -0
  38. package/dist/commands/db.js.map +1 -0
  39. package/dist/commands/deploy.d.ts +23 -3
  40. package/dist/commands/deploy.js +530 -177
  41. package/dist/commands/deploy.js.map +1 -1
  42. package/dist/commands/deployments.d.ts +11 -0
  43. package/dist/commands/deployments.js +69 -0
  44. package/dist/commands/deployments.js.map +1 -0
  45. package/dist/commands/doctor.d.ts +27 -0
  46. package/dist/commands/doctor.js +211 -0
  47. package/dist/commands/doctor.js.map +1 -0
  48. package/dist/commands/dr.d.ts +11 -0
  49. package/dist/commands/dr.js +79 -0
  50. package/dist/commands/dr.js.map +1 -0
  51. package/dist/commands/env.d.ts +15 -0
  52. package/dist/commands/env.js +56 -0
  53. package/dist/commands/env.js.map +1 -0
  54. package/dist/commands/fn.d.ts +6 -0
  55. package/dist/commands/fn.js +87 -0
  56. package/dist/commands/fn.js.map +1 -0
  57. package/dist/commands/infra-read.d.ts +9 -0
  58. package/dist/commands/infra-read.js +113 -0
  59. package/dist/commands/infra-read.js.map +1 -0
  60. package/dist/commands/init.d.ts +10 -3
  61. package/dist/commands/init.js +275 -142
  62. package/dist/commands/init.js.map +1 -1
  63. package/dist/commands/login.d.ts +6 -3
  64. package/dist/commands/login.js +222 -92
  65. package/dist/commands/login.js.map +1 -1
  66. package/dist/commands/logout.d.ts +1 -0
  67. package/dist/commands/logout.js +28 -0
  68. package/dist/commands/logout.js.map +1 -0
  69. package/dist/commands/logs.d.ts +14 -3
  70. package/dist/commands/logs.js +132 -106
  71. package/dist/commands/logs.js.map +1 -1
  72. package/dist/commands/mocks.d.ts +5 -0
  73. package/dist/commands/mocks.js +23 -0
  74. package/dist/commands/mocks.js.map +1 -0
  75. package/dist/commands/open.d.ts +4 -3
  76. package/dist/commands/open.js +53 -85
  77. package/dist/commands/open.js.map +1 -1
  78. package/dist/commands/products-db.d.ts +37 -0
  79. package/dist/commands/products-db.js +230 -0
  80. package/dist/commands/products-db.js.map +1 -0
  81. package/dist/commands/products.d.ts +12 -0
  82. package/dist/commands/products.js +97 -0
  83. package/dist/commands/products.js.map +1 -0
  84. package/dist/commands/promote.d.ts +9 -0
  85. package/dist/commands/promote.js +114 -0
  86. package/dist/commands/promote.js.map +1 -0
  87. package/dist/commands/publish.d.ts +14 -0
  88. package/dist/commands/publish.js +180 -0
  89. package/dist/commands/publish.js.map +1 -0
  90. package/dist/commands/servers.d.ts +23 -0
  91. package/dist/commands/servers.js +166 -0
  92. package/dist/commands/servers.js.map +1 -0
  93. package/dist/commands/status.d.ts +5 -3
  94. package/dist/commands/status.js +91 -93
  95. package/dist/commands/status.js.map +1 -1
  96. package/dist/commands/support.d.ts +25 -0
  97. package/dist/commands/support.js +184 -0
  98. package/dist/commands/support.js.map +1 -0
  99. package/dist/commands/surface-status.d.ts +5 -0
  100. package/dist/commands/surface-status.js +63 -0
  101. package/dist/commands/surface-status.js.map +1 -0
  102. package/dist/commands/tenants.d.ts +34 -0
  103. package/dist/commands/tenants.js +179 -0
  104. package/dist/commands/tenants.js.map +1 -0
  105. package/dist/commands/upgrade.d.ts +12 -0
  106. package/dist/commands/upgrade.js +77 -0
  107. package/dist/commands/upgrade.js.map +1 -0
  108. package/dist/commands/validate.d.ts +1 -3
  109. package/dist/commands/validate.js +83 -91
  110. package/dist/commands/validate.js.map +1 -1
  111. package/dist/commands/whoami.d.ts +5 -3
  112. package/dist/commands/whoami.js +76 -28
  113. package/dist/commands/whoami.js.map +1 -1
  114. package/dist/commands/workspaces.d.ts +15 -0
  115. package/dist/commands/workspaces.js +72 -0
  116. package/dist/commands/workspaces.js.map +1 -0
  117. package/dist/index.d.ts +0 -1
  118. package/dist/index.js +1094 -36
  119. package/dist/index.js.map +1 -1
  120. package/dist/lib/ai/context.d.ts +11 -0
  121. package/dist/lib/ai/context.js +112 -0
  122. package/dist/lib/ai/context.js.map +1 -0
  123. package/dist/lib/ai/orchestrator.d.ts +10 -0
  124. package/dist/lib/ai/orchestrator.js +92 -0
  125. package/dist/lib/ai/orchestrator.js.map +1 -0
  126. package/dist/lib/api-client.d.ts +39 -0
  127. package/dist/lib/api-client.js +185 -0
  128. package/dist/lib/api-client.js.map +1 -0
  129. package/dist/lib/auth.d.ts +15 -0
  130. package/dist/lib/auth.js +98 -0
  131. package/dist/lib/auth.js.map +1 -0
  132. package/dist/lib/cli-read.d.ts +13 -0
  133. package/dist/lib/cli-read.js +103 -0
  134. package/dist/lib/cli-read.js.map +1 -0
  135. package/dist/lib/cli-write.d.ts +47 -0
  136. package/dist/lib/cli-write.js +137 -0
  137. package/dist/lib/cli-write.js.map +1 -0
  138. package/dist/lib/config-schema.d.ts +165 -0
  139. package/dist/lib/config-schema.js +57 -0
  140. package/dist/lib/config-schema.js.map +1 -0
  141. package/dist/lib/config.d.ts +15 -0
  142. package/dist/lib/config.js +33 -0
  143. package/dist/lib/config.js.map +1 -0
  144. package/dist/lib/render.d.ts +16 -0
  145. package/dist/lib/render.js +74 -0
  146. package/dist/lib/render.js.map +1 -0
  147. package/dist/utils/logger.d.ts +13 -0
  148. package/dist/utils/logger.js +27 -0
  149. package/dist/utils/logger.js.map +1 -0
  150. package/package.json +35 -33
  151. package/templates/auth/callback.ts +22 -0
  152. package/templates/auth/sign-in.tsx +41 -0
  153. package/templates/billing/checkout.ts +22 -0
  154. package/templates/billing/page.tsx +43 -0
  155. package/templates/support/ticket-form.tsx +68 -0
  156. package/templates/usage/track.ts +30 -0
  157. package/templates/users/profile.tsx +43 -0
  158. package/LICENSE +0 -21
  159. package/dist/commands/add.d.ts.map +0 -1
  160. package/dist/commands/deploy.d.ts.map +0 -1
  161. package/dist/commands/generate-types.d.ts +0 -3
  162. package/dist/commands/generate-types.d.ts.map +0 -1
  163. package/dist/commands/generate-types.js +0 -150
  164. package/dist/commands/generate-types.js.map +0 -1
  165. package/dist/commands/init.d.ts.map +0 -1
  166. package/dist/commands/login.d.ts.map +0 -1
  167. package/dist/commands/logs.d.ts.map +0 -1
  168. package/dist/commands/open.d.ts.map +0 -1
  169. package/dist/commands/status.d.ts.map +0 -1
  170. package/dist/commands/validate.d.ts.map +0 -1
  171. package/dist/commands/whoami.d.ts.map +0 -1
  172. package/dist/config.d.ts +0 -14
  173. package/dist/config.d.ts.map +0 -1
  174. package/dist/config.js +0 -83
  175. package/dist/config.js.map +0 -1
  176. package/dist/index.d.ts.map +0 -1
  177. package/dist/scaffold/auth.d.ts +0 -3
  178. package/dist/scaffold/auth.d.ts.map +0 -1
  179. package/dist/scaffold/auth.js +0 -228
  180. package/dist/scaffold/auth.js.map +0 -1
  181. package/dist/scaffold/billing.d.ts +0 -3
  182. package/dist/scaffold/billing.d.ts.map +0 -1
  183. package/dist/scaffold/billing.js +0 -184
  184. package/dist/scaffold/billing.js.map +0 -1
  185. package/dist/scaffold/usage.d.ts +0 -3
  186. package/dist/scaffold/usage.d.ts.map +0 -1
  187. package/dist/scaffold/usage.js +0 -173
  188. package/dist/scaffold/usage.js.map +0 -1
  189. package/dist/scaffold/users.d.ts +0 -3
  190. package/dist/scaffold/users.d.ts.map +0 -1
  191. package/dist/scaffold/users.js +0 -135
  192. package/dist/scaffold/users.js.map +0 -1
@@ -0,0 +1,87 @@
1
+ /**
2
+ * `neetru fn` — gestão de Functions/APIs do produto.
3
+ *
4
+ * Sprint 10 (CLI 1.4) — `neetru fn deploy`.
5
+ * Stub: chama API Core (`/cli/v1/fn/deploy`) registrando uma nova versão da
6
+ * API publicada no catálogo `neetru-apis` (Sprint 7 — `api-catalog/`).
7
+ *
8
+ * NOTA: O endpoint real ainda não existe (pendente Sprint 11). Por agora
9
+ * o command apresenta o fluxo + faz uma chamada que retorna 404 ou 501 —
10
+ * trata gracefully como "feature em construção" pra o owner saber que o
11
+ * comando está wired mas o backend não está pronto.
12
+ */
13
+ import * as fs from 'node:fs/promises';
14
+ import * as fsSync from 'node:fs';
15
+ import * as path from 'node:path';
16
+ import chalk from 'chalk';
17
+ import { log } from '../utils/logger.js';
18
+ import { apiRequest, CliApiError, CliNetworkError } from '../lib/api-client.js';
19
+ const CONFIG_FILENAMES = ['neetru.config.json', '.neetru.json'];
20
+ async function loadProductConfig() {
21
+ for (const name of CONFIG_FILENAMES) {
22
+ const filePath = path.resolve(process.cwd(), name);
23
+ if (fsSync.existsSync(filePath)) {
24
+ try {
25
+ const raw = await fs.readFile(filePath, 'utf8');
26
+ return JSON.parse(raw);
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ }
32
+ }
33
+ return null;
34
+ }
35
+ export async function runFnDeploy(opts = {}) {
36
+ log.banner();
37
+ log.heading('neetru fn deploy');
38
+ const cfg = await loadProductConfig();
39
+ if (!cfg) {
40
+ log.error('neetru.config.json não encontrado.');
41
+ process.exit(1);
42
+ return;
43
+ }
44
+ const version = opts.version ?? cfg.apiVersion ?? 'v1';
45
+ const channel = opts.channel ?? 'stable';
46
+ // Optional spec file (OpenAPI / JSON schema). Apenas valida existência —
47
+ // upload real virá no Sprint 11.
48
+ if (opts.spec) {
49
+ const specAbs = path.resolve(process.cwd(), opts.spec);
50
+ if (!fsSync.existsSync(specAbs)) {
51
+ log.error(`Spec não encontrado: ${opts.spec}`);
52
+ process.exit(2);
53
+ return;
54
+ }
55
+ log.info(`Spec carregado: ${chalk.bold(opts.spec)}`);
56
+ }
57
+ log.info(`Registering API ${chalk.bold(cfg.slug)} ${chalk.bold(version)} (${channel}) ...`);
58
+ try {
59
+ const r = await apiRequest('/cli/v1/fn/deploy', {
60
+ method: 'POST',
61
+ body: { productId: cfg.slug, apiVersion: version, channel, hasSpec: !!opts.spec },
62
+ });
63
+ log.success(`API ${r.productId}@${r.apiVersion} registrada no canal ${r.channel}.`);
64
+ log.dim(` registered=${r.registered}`);
65
+ }
66
+ catch (err) {
67
+ if (err instanceof CliApiError) {
68
+ if (err.status === 404 || err.status === 501) {
69
+ log.warn(`Endpoint /cli/v1/fn/deploy ainda não disponível (${err.status}).`);
70
+ log.dim(' Esta funcionalidade entra em build no Sprint 11. Ver docs/PLATFORM_RELATORIO_FINAL_CTO.md.');
71
+ process.exit(0);
72
+ return;
73
+ }
74
+ log.error(`API ${err.status}: ${err.message}`);
75
+ process.exit(3);
76
+ return;
77
+ }
78
+ if (err instanceof CliNetworkError) {
79
+ log.error(`Conexão falhou: ${err.message}`);
80
+ process.exit(4);
81
+ return;
82
+ }
83
+ log.error(err instanceof Error ? err.message : String(err));
84
+ process.exit(5);
85
+ }
86
+ }
87
+ //# sourceMappingURL=fn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fn.js","sourceRoot":"","sources":["../../src/commands/fn.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export declare function runDnsZonesList(opts: {
2
+ json?: boolean;
3
+ }): Promise<void>;
4
+ export declare function runHostingList(opts: {
5
+ json?: boolean;
6
+ }): Promise<void>;
7
+ export declare function runBuildsList(opts: {
8
+ json?: boolean;
9
+ }): Promise<void>;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Comandos read-only de infraestrutura: `neetru dns zones list`,
3
+ * `neetru hosting list`, `neetru builds list` (F5 scope matrix §4.2.0
4
+ * — bucket "✅ DEVE entrar", read-only).
5
+ *
6
+ * Todos batem em `/api/cli/v1/{dns,hosting,builds}/*` com `requireCliAuth`
7
+ * + gate de role.
8
+ */
9
+ import ora from 'ora';
10
+ import { apiRequest } from '../lib/api-client.js';
11
+ import { requireToken, handleApiError } from '../lib/cli-read.js';
12
+ import { renderTable, fmtTimestamp } from '../lib/render.js';
13
+ import { log } from '../utils/logger.js';
14
+ export async function runDnsZonesList(opts) {
15
+ const token = await requireToken(opts.json);
16
+ const spinner = opts.json ? null : ora({ text: 'Buscando managed zones…', color: 'blue' }).start();
17
+ let res;
18
+ try {
19
+ res = await apiRequest('/api/cli/v1/dns/zones', { token });
20
+ spinner?.stop();
21
+ }
22
+ catch (error) {
23
+ spinner?.fail('Falha ao listar zones.');
24
+ handleApiError(error, opts.json);
25
+ }
26
+ if (opts.json) {
27
+ console.log(JSON.stringify(res));
28
+ return;
29
+ }
30
+ if (res.error) {
31
+ log.warn(`${res.error} (${res.errorCode ?? '—'})`);
32
+ return;
33
+ }
34
+ log.heading(`Cloud DNS zones (${res.zones.length}) · projeto ${res.projectId ?? '—'}`);
35
+ if (res.zones.length === 0) {
36
+ log.dim('Nenhuma managed zone encontrada.');
37
+ return;
38
+ }
39
+ renderTable([
40
+ { header: 'NOME', value: (z) => z.name, maxWidth: 24 },
41
+ { header: 'DNS NAME', value: (z) => z.dnsName, maxWidth: 32 },
42
+ { header: 'VISIBILITY', value: (z) => z.visibility },
43
+ { header: 'RECORDS', value: (z) => z.recordCount?.toString() ?? '—' },
44
+ { header: 'CRIADO', value: (z) => fmtTimestamp(z.createdAt) },
45
+ ], res.zones);
46
+ console.log();
47
+ }
48
+ export async function runHostingList(opts) {
49
+ const token = await requireToken(opts.json);
50
+ const spinner = opts.json ? null : ora({ text: 'Buscando customer domains…', color: 'blue' }).start();
51
+ let res;
52
+ try {
53
+ res = await apiRequest('/api/cli/v1/hosting', { token });
54
+ spinner?.stop();
55
+ }
56
+ catch (error) {
57
+ spinner?.fail('Falha ao listar domains.');
58
+ handleApiError(error, opts.json);
59
+ }
60
+ if (opts.json) {
61
+ console.log(JSON.stringify(res));
62
+ return;
63
+ }
64
+ log.heading(`Customer domains (${res.count})`);
65
+ if (res.count === 0) {
66
+ log.dim('Nenhum customer domain registrado.');
67
+ return;
68
+ }
69
+ renderTable([
70
+ { header: 'HOST', value: (d) => d.host ?? '—', maxWidth: 32 },
71
+ { header: 'TENANT', value: (d) => d.tenantId ?? '—', maxWidth: 22 },
72
+ { header: 'STATUS', value: (d) => d.status ?? '—' },
73
+ { header: 'CERT', value: (d) => d.certStatus ?? '—' },
74
+ { header: 'TARGET', value: (d) => d.targetService ?? '—', maxWidth: 24 },
75
+ ], res.domains);
76
+ console.log();
77
+ }
78
+ export async function runBuildsList(opts) {
79
+ const token = await requireToken(opts.json);
80
+ const spinner = opts.json ? null : ora({ text: 'Buscando Cloud Builds…', color: 'blue' }).start();
81
+ let res;
82
+ try {
83
+ res = await apiRequest('/api/cli/v1/builds', { token });
84
+ spinner?.stop();
85
+ }
86
+ catch (error) {
87
+ spinner?.fail('Falha ao listar builds.');
88
+ handleApiError(error, opts.json);
89
+ }
90
+ if (opts.json) {
91
+ console.log(JSON.stringify(res));
92
+ return;
93
+ }
94
+ if (res.error) {
95
+ log.warn(`${res.error} (${res.errorCode ?? '—'}) ${res.hint ?? ''}`);
96
+ if (res.builds.length === 0)
97
+ return;
98
+ }
99
+ log.heading(`Cloud Builds (${res.builds.length})`);
100
+ renderTable([
101
+ { header: 'ID', value: (b) => b.id, maxWidth: 36 },
102
+ { header: 'STATUS', value: (b) => b.status },
103
+ { header: 'REGIÃO', value: (b) => b.region ?? 'global' },
104
+ { header: 'SOURCE', value: (b) => b.source ?? '—', maxWidth: 28 },
105
+ { header: 'INICIADO', value: (b) => fmtTimestamp(b.createTime) },
106
+ {
107
+ header: 'DURAÇÃO',
108
+ value: (b) => (b.durationSeconds != null ? `${b.durationSeconds}s` : '—'),
109
+ },
110
+ ], res.builds);
111
+ console.log();
112
+ }
113
+ //# sourceMappingURL=infra-read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infra-read.js","sourceRoot":"","sources":["../../src/commands/infra-read.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,10 @@
1
- import { Command } from 'commander';
2
- export declare function initCommand(): Command;
3
- //# sourceMappingURL=init.d.ts.map
1
+ interface InitOptions {
2
+ name: string;
3
+ type: 'nextjs' | 'node-api';
4
+ }
5
+ /**
6
+ * Scaffold de projeto SaaS Neetru.
7
+ * Gera estrutura mínima com Core SDK wired: auth, tenant gate, billing hook.
8
+ */
9
+ export declare function runInit({ name, type }: InitOptions): Promise<void>;
10
+ export {};
@@ -1,149 +1,282 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+ import ora from 'ora';
4
+ import { log } from '../utils/logger.js';
5
+ /**
6
+ * Scaffold de projeto SaaS Neetru.
7
+ * Gera estrutura mínima com Core SDK wired: auth, tenant gate, billing hook.
8
+ */
9
+ export async function runInit({ name, type }) {
10
+ log.banner();
11
+ log.heading(`Inicializando produto: ${name}`);
12
+ console.log();
13
+ const dir = path.resolve(process.cwd(), name);
14
+ // Verifica se diretório já existe
15
+ try {
16
+ await fs.access(dir);
17
+ log.error(`Diretório "${name}" já existe.`);
18
+ process.exit(1);
7
19
  }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.initCommand = initCommand;
40
- const commander_1 = require("commander");
41
- const chalk_1 = __importDefault(require("chalk"));
42
- const ora_1 = __importDefault(require("ora"));
43
- const fs = __importStar(require("fs-extra"));
44
- const path = __importStar(require("path"));
45
- const config_1 = require("../config");
46
- // Dynamically import inquirer to avoid top-level await issues
47
- async function prompt(questions) {
48
- const { default: inquirer } = await Promise.resolve().then(() => __importStar(require('inquirer')));
49
- return inquirer.prompt(questions);
50
- }
51
- function initCommand() {
52
- const cmd = new commander_1.Command('init');
53
- cmd
54
- .description('Inicializa a integração Neetru no projeto atual')
55
- .option('--core-url <url>', 'URL do Neetru Core (ex: https://core.neetru.com)')
56
- .option('--client-id <id>', 'OAuth Client ID fornecido pelo Neetru Core')
57
- .option('--yes', 'Pular confirmações interativas')
58
- .action(async (options) => {
59
- console.log('\n' + chalk_1.default.bold.cyan('🚀 Neetru SDK — Inicialização'));
60
- console.log(chalk_1.default.dim('Conectando seu SaaS ao Neetru Core\n'));
61
- // Check if already initialized
62
- if (fs.existsSync('neetru.json') && !options.yes) {
63
- const { overwrite } = await prompt([{
64
- type: 'confirm',
65
- name: 'overwrite',
66
- message: chalk_1.default.yellow('neetru.json já existe. Sobrescrever?'),
67
- default: false,
68
- }]);
69
- if (!overwrite) {
70
- console.log(chalk_1.default.dim('Cancelado.'));
71
- return;
72
- }
73
- }
74
- // Collect info interactively if not provided via flags
75
- const coreUrl = options.coreUrl ?? (await prompt([{
76
- type: 'input',
77
- name: 'coreUrl',
78
- message: 'URL do Neetru Core:',
79
- default: 'https://core.neetru.com',
80
- validate: (v) => v.startsWith('http') || 'URL inválida',
81
- }])).coreUrl;
82
- const clientId = options.clientId ?? (await prompt([{
83
- type: 'input',
84
- name: 'clientId',
85
- message: 'OAuth Client ID (do painel Neetru → Workspaces → Credenciais):',
86
- validate: (v) => v.length > 0 || 'Client ID obrigatório',
87
- }])).clientId;
88
- const projectName = path.basename(process.cwd());
89
- const { features } = await prompt([{
90
- type: 'checkbox',
91
- name: 'features',
92
- message: 'Quais integrações você precisa?',
93
- choices: [
94
- { name: 'auth — Login com OAuth Neetru (SSO)', value: 'auth', checked: true },
95
- { name: 'billing — Assinaturas e planos', value: 'billing', checked: true },
96
- { name: 'usage — Metered billing (reporte de uso)', value: 'usage' },
97
- { name: 'users — API de usuários Neetru', value: 'users' },
98
- ],
99
- }]);
100
- // Validate connection
101
- const spinner = (0, ora_1.default)('Verificando conexão com o Core...').start();
102
- try {
103
- const res = await fetch(`${coreUrl}/api/health`, { signal: AbortSignal.timeout(5000) });
104
- if (res.ok) {
105
- spinner.succeed('Conexão verificada');
106
- }
107
- else {
108
- spinner.warn(`Core respondeu com status ${res.status} — continuando mesmo assim`);
109
- }
110
- }
111
- catch {
112
- spinner.warn('Não foi possível verificar a conexão — continuando offline');
113
- }
114
- // Write neetru.json
115
- (0, config_1.writeLocalConfig)({
116
- coreUrl,
117
- clientId,
118
- projectName,
119
- features,
120
- createdAt: new Date().toISOString(),
121
- });
122
- // Write .env.example entries
123
- const envPath = '.env.example';
124
- const envEntries = [
125
- '',
126
- '# Neetru Core',
127
- `NEETRU_CORE_URL=${coreUrl}`,
128
- `NEETRU_CLIENT_ID=${clientId}`,
129
- 'NEETRU_CLIENT_SECRET=your_client_secret_here',
130
- ...(features.includes('billing') ? ['NEETRU_WEBHOOK_SECRET=your_webhook_secret_here'] : []),
131
- ].join('\n');
132
- if (fs.existsSync(envPath)) {
133
- fs.appendFileSync(envPath, envEntries);
134
- console.log(chalk_1.default.dim(` → Entradas adicionadas ao ${envPath}`));
20
+ catch {
21
+ // ok, não existe
22
+ }
23
+ const spinner = ora({ text: 'Criando estrutura...', color: 'blue' }).start();
24
+ try {
25
+ await fs.mkdir(dir, { recursive: true });
26
+ if (type === 'nextjs') {
27
+ await scaffoldNextjs(dir, name);
135
28
  }
136
29
  else {
137
- fs.writeFileSync(envPath, envEntries.trimStart());
138
- console.log(chalk_1.default.dim(` → ${envPath} criado`));
30
+ await scaffoldNodeApi(dir, name);
139
31
  }
140
- console.log('\n' + chalk_1.default.green.bold('✓ neetru.json criado'));
141
- console.log(chalk_1.default.dim('\nPróximos passos:'));
142
- features.forEach((f) => {
143
- console.log(chalk_1.default.dim(` neetru add ${f}`));
144
- });
145
- console.log('');
32
+ // Arquivo de config do projeto
33
+ const slug = name
34
+ .trim()
35
+ .toLowerCase()
36
+ .replace(/[^a-z0-9-]+/g, '-')
37
+ .replace(/-+/g, '-')
38
+ .replace(/^-|-$/g, '');
39
+ await fs.writeFile(path.join(dir, 'neetru.config.json'), JSON.stringify({
40
+ $schema: 'https://neetru.com/schema/neetru.config.json',
41
+ slug: slug || name,
42
+ name,
43
+ runtime: type,
44
+ tenantId: '',
45
+ publish: {
46
+ tagline: '',
47
+ description: '',
48
+ iconKey: 'spark',
49
+ status: 'soon',
50
+ order: 100,
51
+ },
52
+ }, null, 2));
53
+ spinner.succeed('Projeto criado com sucesso!');
54
+ console.log();
55
+ log.info(`Diretório: ${dir}`);
56
+ log.info(`Próximos passos:`);
57
+ log.dim(` cd ${name}`);
58
+ log.dim(` npm install`);
59
+ log.dim(` neetru ai # inicie o assistente dentro do projeto`);
60
+ log.dim(` neetru dev # inicia servidor de dev local`);
61
+ console.log();
62
+ }
63
+ catch (err) {
64
+ spinner.fail('Falha ao criar projeto.');
65
+ log.error(err?.message);
66
+ process.exit(1);
67
+ }
68
+ }
69
+ async function scaffoldNextjs(dir, name) {
70
+ const src = path.join(dir, 'src', 'app');
71
+ await fs.mkdir(src, { recursive: true });
72
+ await fs.mkdir(path.join(dir, 'src', 'lib'), { recursive: true });
73
+ await fs.mkdir(path.join(dir, 'src', 'components'), { recursive: true });
74
+ // package.json
75
+ await fs.writeFile(path.join(dir, 'package.json'), JSON.stringify({
76
+ name,
77
+ version: '0.1.0',
78
+ private: true,
79
+ scripts: {
80
+ dev: 'next dev -p 3000',
81
+ build: 'next build',
82
+ start: 'next start',
83
+ },
84
+ dependencies: {
85
+ next: '^15.0.0',
86
+ react: '^19.0.0',
87
+ 'react-dom': '^19.0.0',
88
+ '@neetru/sdk': 'latest',
89
+ firebase: '^11.0.0',
90
+ },
91
+ devDependencies: {
92
+ typescript: '^5.7.0',
93
+ '@types/node': '^22.0.0',
94
+ '@types/react': '^19.0.0',
95
+ },
96
+ }, null, 2));
97
+ // next.config.mjs
98
+ await fs.writeFile(path.join(dir, 'next.config.mjs'), `/** @type {import('next').NextConfig} */
99
+ const nextConfig = {};
100
+ export default nextConfig;
101
+ `);
102
+ // Layout raiz
103
+ await fs.writeFile(path.join(src, 'layout.tsx'), `import type { Metadata } from 'next';
104
+
105
+ export const metadata: Metadata = {
106
+ title: '${name}',
107
+ };
108
+
109
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
110
+ return (
111
+ <html lang="pt-BR">
112
+ <body>{children}</body>
113
+ </html>
114
+ );
115
+ }
116
+ `);
117
+ // Página inicial com auth gate via SDK 1.1
118
+ // Padrão @neetru/sdk@1.1 — namespaces auth/catalog/entitlements (sem subpath /server).
119
+ await fs.writeFile(path.join(src, 'page.tsx'), `// Produto Neetru — gerado por neetru init
120
+ // Auth via @neetru/sdk client (createNeetruClient + auth namespace)
121
+
122
+ 'use client';
123
+
124
+ import { createNeetruClient } from '@neetru/sdk';
125
+ import { useEffect, useState } from 'react';
126
+ import type { NeetruUser } from '@neetru/sdk';
127
+
128
+ const neetru = createNeetruClient({
129
+ apiKey: process.env.NEXT_PUBLIC_NEETRU_API_KEY,
130
+ env: (process.env.NEXT_PUBLIC_NEETRU_ENV as 'dev' | 'prod') ?? 'dev',
131
+ });
132
+
133
+ export default function HomePage() {
134
+ const [user, setUser] = useState<NeetruUser | null>(null);
135
+ const [loading, setLoading] = useState(true);
136
+
137
+ useEffect(() => {
138
+ let unsub: (() => void) | undefined;
139
+ neetru.auth.getUser().then((u) => {
140
+ setUser(u);
141
+ setLoading(false);
146
142
  });
147
- return cmd;
143
+ unsub = neetru.auth.onAuthStateChanged((u) => setUser(u));
144
+ return () => unsub?.();
145
+ }, []);
146
+
147
+ if (loading) return <main><p>Carregando...</p></main>;
148
+ if (!user) {
149
+ return (
150
+ <main>
151
+ <h1>${name}</h1>
152
+ <button onClick={() => neetru.auth.signIn()}>Entrar com Neetru</button>
153
+ </main>
154
+ );
155
+ }
156
+ return (
157
+ <main>
158
+ <h1>Bem-vindo, {user.email}</h1>
159
+ <p>UID: {user.uid}</p>
160
+ <button onClick={() => neetru.auth.signOut()}>Sair</button>
161
+ </main>
162
+ );
163
+ }
164
+ `);
165
+ // Middleware Next.js — gate de cookie de sessão em rotas privadas.
166
+ // Padrão simples sem dependência de subpath SDK que ainda não existe.
167
+ await fs.writeFile(path.join(dir, 'src', 'middleware.ts'), `import { NextRequest, NextResponse } from 'next/server';
168
+
169
+ const PUBLIC_PATHS = ['/login', '/api/auth', '/_next', '/favicon.ico'];
170
+
171
+ export function middleware(request: NextRequest) {
172
+ const { pathname } = request.nextUrl;
173
+ if (PUBLIC_PATHS.some((p) => pathname.startsWith(p))) {
174
+ return NextResponse.next();
175
+ }
176
+
177
+ // Cookie '__session' setado pelo OIDC callback do auth.neetru.com.
178
+ const hasSession = request.cookies.has('__session');
179
+ if (!hasSession) {
180
+ const url = request.nextUrl.clone();
181
+ url.pathname = '/login';
182
+ return NextResponse.redirect(url);
183
+ }
184
+
185
+ return NextResponse.next();
186
+ }
187
+
188
+ export const config = {
189
+ matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
190
+ };
191
+ `);
192
+ // tsconfig.json
193
+ await fs.writeFile(path.join(dir, 'tsconfig.json'), JSON.stringify({
194
+ compilerOptions: {
195
+ target: 'ES2017',
196
+ lib: ['dom', 'dom.iterable', 'esnext'],
197
+ allowJs: true,
198
+ skipLibCheck: true,
199
+ strict: true,
200
+ noEmit: true,
201
+ esModuleInterop: true,
202
+ module: 'esnext',
203
+ moduleResolution: 'bundler',
204
+ resolveJsonModule: true,
205
+ isolatedModules: true,
206
+ jsx: 'preserve',
207
+ incremental: true,
208
+ plugins: [{ name: 'next' }],
209
+ paths: { '@/*': ['./src/*'] },
210
+ },
211
+ include: ['next-env.d.ts', '**/*.ts', '**/*.tsx', '.next/types/**/*.ts'],
212
+ exclude: ['node_modules'],
213
+ }, null, 2));
214
+ }
215
+ async function scaffoldNodeApi(dir, name) {
216
+ await fs.mkdir(path.join(dir, 'src', 'routes'), { recursive: true });
217
+ await fs.mkdir(path.join(dir, 'src', 'middleware'), { recursive: true });
218
+ await fs.writeFile(path.join(dir, 'package.json'), JSON.stringify({
219
+ name,
220
+ version: '0.1.0',
221
+ private: true,
222
+ type: 'module',
223
+ scripts: {
224
+ dev: 'tsx --watch src/index.ts',
225
+ build: 'tsc',
226
+ start: 'node dist/index.js',
227
+ },
228
+ dependencies: {
229
+ fastify: '^5.0.0',
230
+ '@neetru/sdk': 'latest',
231
+ },
232
+ devDependencies: {
233
+ tsx: '^4.0.0',
234
+ typescript: '^5.7.0',
235
+ '@types/node': '^22.0.0',
236
+ },
237
+ }, null, 2));
238
+ await fs.writeFile(path.join(dir, 'src', 'index.ts'), `import Fastify from 'fastify';
239
+ import { createNeetruClient } from '@neetru/sdk';
240
+
241
+ // Padrão @neetru/sdk@1.1 — namespaces server-side: entitlements/usage/telemetry.
242
+ // O SDK NÃO publica subpath /fastify; o gate é um preHandler fino que reusa
243
+ // o cliente. \`createNeetruClient\` é seguro em Node com apiKey de produto.
244
+ const neetru = createNeetruClient({
245
+ apiKey: process.env.NEETRU_API_KEY,
246
+ env: (process.env.NEETRU_ENV as 'dev' | 'prod') ?? 'dev',
247
+ });
248
+
249
+ const PRODUCT_SLUG = process.env.NEETRU_PRODUCT_SLUG ?? '${name}';
250
+
251
+ const app = Fastify({ logger: true });
252
+
253
+ // preHandler de entitlement: bloqueia a rota se o portador não tem a feature.
254
+ function requireEntitlement(feature: string) {
255
+ return async (
256
+ _request: import('fastify').FastifyRequest,
257
+ reply: import('fastify').FastifyReply,
258
+ ) => {
259
+ const allowed = await neetru.entitlements.check(PRODUCT_SLUG, feature);
260
+ if (!allowed) {
261
+ return reply.code(403).send({ error: 'entitlement_denied', feature });
262
+ }
263
+ };
264
+ }
265
+
266
+ app.get('/health', async () => ({ status: 'ok' }));
267
+
268
+ // Rota protegida — só responde se o produto tem a feature 'api-access'.
269
+ app.get(
270
+ '/api/data',
271
+ { preHandler: requireEntitlement('api-access') },
272
+ async () => {
273
+ // Registra uso pra billing metered (acumula no mock em NEETRU_ENV=dev).
274
+ await neetru.usage.report('api_call', 1, { productId: PRODUCT_SLUG });
275
+ return { data: [] };
276
+ },
277
+ );
278
+
279
+ await app.listen({ port: 3000, host: '0.0.0.0' });
280
+ `);
148
281
  }
149
282
  //# sourceMappingURL=init.js.map