@neetru/cli 2.24.1 → 2.26.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 (53) hide show
  1. package/dist/commands/ai.js +8 -8
  2. package/dist/commands/bootstrap.js +18 -1
  3. package/dist/commands/bootstrap.js.map +1 -1
  4. package/dist/commands/build.js +12 -17
  5. package/dist/commands/build.js.map +1 -1
  6. package/dist/commands/dev-up.d.ts +10 -26
  7. package/dist/commands/dev-up.js +78 -140
  8. package/dist/commands/dev-up.js.map +1 -1
  9. package/dist/commands/doctor.js +10 -4
  10. package/dist/commands/doctor.js.map +1 -1
  11. package/dist/commands/init.js +147 -147
  12. package/dist/commands/logs.d.ts +29 -0
  13. package/dist/commands/logs.js +161 -0
  14. package/dist/commands/logs.js.map +1 -1
  15. package/dist/commands/webhooks.d.ts +34 -0
  16. package/dist/commands/webhooks.js +162 -0
  17. package/dist/commands/webhooks.js.map +1 -0
  18. package/dist/index.js +109 -27
  19. package/dist/index.js.map +1 -1
  20. package/dist/lib/ai/context.js +91 -91
  21. package/dist/lib/compose-generator.d.ts +36 -0
  22. package/dist/lib/compose-generator.js +152 -0
  23. package/dist/lib/compose-generator.js.map +1 -0
  24. package/dist/lib/config-schema.d.ts +18 -0
  25. package/dist/lib/config-schema.js +4 -0
  26. package/dist/lib/config-schema.js.map +1 -1
  27. package/dist/utils/safe-exit.d.ts +23 -0
  28. package/dist/utils/safe-exit.js +53 -1
  29. package/dist/utils/safe-exit.js.map +1 -1
  30. package/package.json +1 -1
  31. package/templates/auth/callback.ts +22 -22
  32. package/templates/auth/sign-in.tsx +41 -41
  33. package/templates/billing/checkout.ts +38 -22
  34. package/templates/billing/page.tsx +84 -43
  35. package/templates/billing/webhooks.ts +83 -0
  36. package/templates/dev/Dockerfile.agent +39 -0
  37. package/templates/dev/agent-entrypoint.sh +41 -0
  38. package/templates/dev/docker-compose.dev.yml +63 -0
  39. package/templates/support/ticket-form.tsx +68 -68
  40. package/templates/usage/track.ts +30 -30
  41. package/templates/users/profile.tsx +43 -43
  42. package/dist/commands/fn.d.ts +0 -6
  43. package/dist/commands/fn.js +0 -88
  44. package/dist/commands/fn.js.map +0 -1
  45. package/dist/commands/marketplace.d.ts +0 -36
  46. package/dist/commands/marketplace.js +0 -585
  47. package/dist/commands/marketplace.js.map +0 -1
  48. package/dist/lib/code-agent-neetru-tools.d.ts +0 -15
  49. package/dist/lib/code-agent-neetru-tools.js +0 -571
  50. package/dist/lib/code-agent-neetru-tools.js.map +0 -1
  51. package/dist/lib/code-agent-report.d.ts +0 -22
  52. package/dist/lib/code-agent-report.js +0 -43
  53. package/dist/lib/code-agent-report.js.map +0 -1
@@ -0,0 +1,36 @@
1
+ export interface ComposeDbConfig {
2
+ engine: string;
3
+ image: string;
4
+ env: Record<string, string>;
5
+ port: number;
6
+ internalPort: number;
7
+ dataDir: string;
8
+ healthcheck: {
9
+ test: string[];
10
+ interval: string;
11
+ timeout: string;
12
+ retries: number;
13
+ };
14
+ }
15
+ /**
16
+ * Mapeia engine de banco → config Docker Compose.
17
+ * Usa imagens oficiais — mesmas engines que rodam em produção na VM.
18
+ */
19
+ export declare function resolveDbConfig(engine: string): ComposeDbConfig | null;
20
+ export interface ComposeGeneratorInput {
21
+ productSlug: string;
22
+ dbEngine: string;
23
+ appPort?: number;
24
+ coreUrl?: string;
25
+ firebaseProject?: string;
26
+ firebaseApiKey?: string;
27
+ }
28
+ /**
29
+ * Gera o conteúdo do docker-compose.dev.yml baseado na config do produto.
30
+ * Template em templates/dev/docker-compose.dev.yml com substituições {{VAR}}.
31
+ */
32
+ export declare function generateComposeDev(input: ComposeGeneratorInput): string;
33
+ /**
34
+ * Escreve o compose + Dockerfile.agent no diretório do projeto.
35
+ */
36
+ export declare function writeComposeDev(cwd: string, input: ComposeGeneratorInput): string;
@@ -0,0 +1,152 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ /**
4
+ * Mapeia engine de banco → config Docker Compose.
5
+ * Usa imagens oficiais — mesmas engines que rodam em produção na VM.
6
+ */
7
+ export function resolveDbConfig(engine) {
8
+ const configs = {
9
+ 'vm-postgres-single': {
10
+ engine: 'PostgreSQL 15',
11
+ image: 'postgres:15-alpine',
12
+ env: { POSTGRES_PASSWORD: 'neetru_dev', POSTGRES_DB: 'app' },
13
+ port: 5432,
14
+ internalPort: 5432,
15
+ dataDir: '/var/lib/postgresql/data',
16
+ healthcheck: { test: ['CMD-SHELL', 'pg_isready -U postgres'], interval: '5s', timeout: '3s', retries: 10 },
17
+ },
18
+ 'vm-mysql-single': {
19
+ engine: 'MySQL 8',
20
+ image: 'mysql:8.0',
21
+ env: { MYSQL_ROOT_PASSWORD: 'neetru_dev', MYSQL_DATABASE: 'app' },
22
+ port: 3306,
23
+ internalPort: 3306,
24
+ dataDir: '/var/lib/mysql',
25
+ healthcheck: { test: ['CMD', 'mysqladmin', 'ping', '-h', 'localhost'], interval: '5s', timeout: '3s', retries: 10 },
26
+ },
27
+ 'nosql-vm': {
28
+ engine: 'MongoDB 7',
29
+ image: 'mongo:7',
30
+ env: { MONGO_INITDB_DATABASE: 'app' },
31
+ port: 27017,
32
+ internalPort: 27017,
33
+ dataDir: '/data/db',
34
+ healthcheck: { test: ['CMD', 'mongosh', '--eval', "db.adminCommand('ping')"], interval: '5s', timeout: '3s', retries: 10 },
35
+ },
36
+ 'firestore-instance': {
37
+ engine: 'Firestore (Firebase Emulator)',
38
+ // Pinado num tag específico — imagem de terceiro em `:latest` deriva sem
39
+ // aviso e num dia ruim puxa uma versão quebrada/comprometida no dev local.
40
+ image: 'andreysenov/firebase-tools:15.24.0',
41
+ env: {},
42
+ port: 8080,
43
+ internalPort: 8080,
44
+ dataDir: '/data',
45
+ healthcheck: { test: ['CMD', 'curl', '-f', 'http://localhost:8080'], interval: '5s', timeout: '3s', retries: 15 },
46
+ },
47
+ };
48
+ // Normaliza engine (vm-postgres-single, cloud-sql-postgres → postgres config)
49
+ if (engine.includes('postgres'))
50
+ return configs['vm-postgres-single'];
51
+ if (engine.includes('mysql'))
52
+ return configs['vm-mysql-single'];
53
+ if (engine.includes('nosql') || engine.includes('mongo'))
54
+ return configs['nosql-vm'];
55
+ if (engine.includes('firestore'))
56
+ return configs['firestore-instance'];
57
+ return configs['vm-postgres-single'] ?? null; // default
58
+ }
59
+ /**
60
+ * Gera o conteúdo do docker-compose.dev.yml baseado na config do produto.
61
+ * Template em templates/dev/docker-compose.dev.yml com substituições {{VAR}}.
62
+ */
63
+ export function generateComposeDev(input) {
64
+ const db = resolveDbConfig(input.dbEngine) ?? resolveDbConfig('vm-postgres-single');
65
+ const appPort = input.appPort ?? 3000;
66
+ // Bloco `environment:` só entra quando há vars — senão vira `environment:`
67
+ // vazio, que o docker compose rejeita ("environment must be a mapping").
68
+ // Firestore (env: {}) caía nisso → `dev up` firestore gerava compose inválido.
69
+ const dbEnvEntries = Object.entries(db.env);
70
+ const dbEnvBlock = dbEnvEntries.length
71
+ ? ` environment:\n${dbEnvEntries.map(([k, v]) => ` - ${k}=${v}`).join('\n')}\n`
72
+ : '';
73
+ const dbHealthcheck = [
74
+ ` test: ${JSON.stringify(db.healthcheck.test)}`,
75
+ ` interval: ${db.healthcheck.interval}`,
76
+ ` timeout: ${db.healthcheck.timeout}`,
77
+ ` retries: ${db.healthcheck.retries}`,
78
+ ].join('\n');
79
+ return `# Neetru Dev Local — emulador de VM (${db.engine})
80
+ # Gerado por \`neetru dev up\`. Modifique o neetru.config.json e rode novamente.
81
+
82
+ services:
83
+ database:
84
+ image: ${db.image}
85
+ container_name: neetru-dev-db
86
+ ${dbEnvBlock} ports:
87
+ - "${db.port}:${db.internalPort}"
88
+ volumes:
89
+ - neetru-db-data:${db.dataDir}
90
+ healthcheck:
91
+ ${dbHealthcheck}
92
+ restart: unless-stopped
93
+ networks:
94
+ - neetru-dev-net
95
+
96
+ agent:
97
+ build:
98
+ context: .
99
+ dockerfile: Dockerfile.agent
100
+ container_name: neetru-dev-agent
101
+ environment:
102
+ - NEETRU_SERVER_ID=dev-local-${input.productSlug}
103
+ - NEETRU_CORE_URL=${input.coreUrl ?? 'https://core.neetru.com'}
104
+ - NEETRU_FIREBASE_PROJECT=${input.firebaseProject ?? 'neetru'}
105
+ - NEETRU_FIREBASE_API_KEY=${input.firebaseApiKey ?? ''}
106
+ - NEETRU_DEV_MODE=true
107
+ - NEETRU_APP_PORT=${appPort}
108
+ - DATABASE_URL=${db.internalPort === 5432 ? 'postgresql://postgres:neetru_dev@database:5432/app' : db.internalPort === 3306 ? 'mysql://root:neetru_dev@database:3306/app' : db.internalPort === 27017 ? 'mongodb://database:27017/app' : ''}
109
+ ports:
110
+ - "${appPort}:${appPort}"
111
+ volumes:
112
+ - /var/run/docker.sock:/var/run/docker.sock
113
+ - neetru-agent-data:/var/cache/neetru
114
+ - .:/app/product:cached
115
+ depends_on:
116
+ database:
117
+ condition: service_healthy
118
+ restart: unless-stopped
119
+ networks:
120
+ - neetru-dev-net
121
+
122
+ volumes:
123
+ neetru-db-data:
124
+ neetru-agent-data:
125
+
126
+ networks:
127
+ neetru-dev-net:
128
+ driver: bridge
129
+ `;
130
+ }
131
+ /**
132
+ * Escreve o compose + Dockerfile.agent no diretório do projeto.
133
+ */
134
+ export function writeComposeDev(cwd, input) {
135
+ const composePath = path.join(cwd, 'neetru-compose.dev.yml');
136
+ const dockerfilePath = path.join(cwd, 'Dockerfile.agent');
137
+ const entrypointPath = path.join(cwd, 'agent-entrypoint.sh');
138
+ const compose = generateComposeDev(input);
139
+ fs.writeFileSync(composePath, compose, 'utf8');
140
+ // Copia o Dockerfile.agent do template embutido
141
+ const templateDir = path.join(path.dirname(new URL(import.meta.url).pathname), '..', '..', 'templates', 'dev');
142
+ // Em modo pkg/compilado, busca do __dirname ou do diretório do CLI
143
+ const altTemplate = path.join(process.cwd(), 'node_modules', '@neetru', 'cli', 'templates', 'dev');
144
+ const dockerfileSrc = fs.existsSync(path.join(templateDir, 'Dockerfile.agent'))
145
+ ? path.join(templateDir, 'Dockerfile.agent')
146
+ : path.join(altTemplate, 'Dockerfile.agent');
147
+ if (fs.existsSync(dockerfileSrc)) {
148
+ fs.copyFileSync(dockerfileSrc, dockerfilePath);
149
+ }
150
+ return composePath;
151
+ }
152
+ //# sourceMappingURL=compose-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose-generator.js","sourceRoot":"","sources":["../../src/lib/compose-generator.ts"],"names":[],"mappings":"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"}
@@ -79,6 +79,16 @@ export declare const neetruConfigSchema: z.ZodObject<{
79
79
  region: z.ZodOptional<z.ZodString>;
80
80
  serviceName: z.ZodOptional<z.ZodString>;
81
81
  }, z.ZodTypeAny, "passthrough">>>;
82
+ database: z.ZodOptional<z.ZodObject<{
83
+ engine: z.ZodOptional<z.ZodEnum<["vm-postgres-single", "vm-mysql-single", "nosql-vm", "firestore-instance"]>>;
84
+ dbId: z.ZodOptional<z.ZodString>;
85
+ }, "strip", z.ZodTypeAny, {
86
+ engine?: "firestore-instance" | "vm-postgres-single" | "vm-mysql-single" | "nosql-vm" | undefined;
87
+ dbId?: string | undefined;
88
+ }, {
89
+ engine?: "firestore-instance" | "vm-postgres-single" | "vm-mysql-single" | "nosql-vm" | undefined;
90
+ dbId?: string | undefined;
91
+ }>>;
82
92
  }, "strip", z.ZodTypeAny, {
83
93
  name: string;
84
94
  slug: string;
@@ -98,6 +108,10 @@ export declare const neetruConfigSchema: z.ZodObject<{
98
108
  ctaHref?: string | undefined;
99
109
  ctaLabel?: string | undefined;
100
110
  } | undefined;
111
+ database?: {
112
+ engine?: "firestore-instance" | "vm-postgres-single" | "vm-mysql-single" | "nosql-vm" | undefined;
113
+ dbId?: string | undefined;
114
+ } | undefined;
101
115
  }, {
102
116
  name: string;
103
117
  slug: string;
@@ -117,6 +131,10 @@ export declare const neetruConfigSchema: z.ZodObject<{
117
131
  ctaHref?: string | undefined;
118
132
  ctaLabel?: string | undefined;
119
133
  } | undefined;
134
+ database?: {
135
+ engine?: "firestore-instance" | "vm-postgres-single" | "vm-mysql-single" | "nosql-vm" | undefined;
136
+ dbId?: string | undefined;
137
+ } | undefined;
120
138
  }>;
121
139
  export type NeetruConfig = z.infer<typeof neetruConfigSchema>;
122
140
  export type PublishBlock = z.infer<typeof publishBlockSchema>;
@@ -38,6 +38,10 @@ export const neetruConfigSchema = z.object({
38
38
  tenantId: z.string().optional(),
39
39
  publish: publishBlockSchema.optional(),
40
40
  deploy: deployBlockSchema.optional(),
41
+ database: z.object({
42
+ engine: z.enum(['vm-postgres-single', 'vm-mysql-single', 'nosql-vm', 'firestore-instance']).optional(),
43
+ dbId: z.string().optional(),
44
+ }).optional(),
41
45
  });
42
46
  export const catalogUpsertPayloadSchema = z.object({
43
47
  slug: slugSchema,
@@ -1 +1 @@
1
- {"version":3,"file":"config-schema.js","sourceRoot":"","sources":["../../src/lib/config-schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config-schema.js","sourceRoot":"","sources":["../../src/lib/config-schema.ts"],"names":[],"mappings":"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"}
@@ -25,9 +25,32 @@
25
25
  *
26
26
  * Paths puramente síncronos podem usar process.exit() direto.
27
27
  */
28
+ /**
29
+ * Sinal interno lançado por `safeExit` em produção para PARAR o caller sem
30
+ * `process.exit()` síncrono. O trap global em `index.ts` (`isSafeExitHalt`) o
31
+ * engole; o `setTimeout` agendado dentro do `safeExit` faz a saída REAL depois
32
+ * que o `uv_async_t` do undici fecha. Não estende `Error` de propósito — não é
33
+ * um erro, é um sinal de controle de fluxo.
34
+ */
35
+ export declare class SafeExitHalt {
36
+ readonly code: number;
37
+ readonly __neetruSafeExit: true;
38
+ constructor(code: number);
39
+ }
40
+ /** Reconhece o sinal de halt do `safeExit` (usado pelos traps globais). */
41
+ export declare function isSafeExitHalt(x: unknown): x is SafeExitHalt;
28
42
  /**
29
43
  * Sai com `code` após cleanup de handles libuv. NEVER returns.
30
44
  * Use em error paths que tocaram I/O assíncrono.
45
+ *
46
+ * Windows + undici (bug_818cf6d): `fetch()` do Node deixa um `uv_async_t`
47
+ * aberto; `process.exit()` SÍNCRONO o mata em `UV_HANDLE_CLOSING` (assertion
48
+ * fatal em `src\win\async.c`). RE-CONFIRMADO empiricamente 2026-07-16 que
49
+ * stdin.destroy() NÃO resolve — só deferir o exit um macrotask real (~100ms)
50
+ * deixa o handle fechar (setImmediate/delay-0 NÃO bastam). Em produção
51
+ * agendamos o exit e lançamos `SafeExitHalt` pra impedir a continuação síncrona
52
+ * do caller (preserva o contrato `never`); o timer é agendado ANTES do throw,
53
+ * então mesmo que um `try/catch` intermediário engula o sinal, o processo sai.
31
54
  */
32
55
  export declare function safeExit(code: number): never;
33
56
  /**
@@ -62,13 +62,65 @@ function cleanupHandles() {
62
62
  // e o exit imediato — sockets http morrem com o processo de qualquer
63
63
  // forma. Manter destroy aqui só prejudicava telemetria keepalive:true.
64
64
  }
65
+ /**
66
+ * Sinal interno lançado por `safeExit` em produção para PARAR o caller sem
67
+ * `process.exit()` síncrono. O trap global em `index.ts` (`isSafeExitHalt`) o
68
+ * engole; o `setTimeout` agendado dentro do `safeExit` faz a saída REAL depois
69
+ * que o `uv_async_t` do undici fecha. Não estende `Error` de propósito — não é
70
+ * um erro, é um sinal de controle de fluxo.
71
+ */
72
+ export class SafeExitHalt {
73
+ code;
74
+ __neetruSafeExit = true;
75
+ constructor(code) {
76
+ this.code = code;
77
+ }
78
+ }
79
+ /** Reconhece o sinal de halt do `safeExit` (usado pelos traps globais). */
80
+ export function isSafeExitHalt(x) {
81
+ return (typeof x === 'object' &&
82
+ x !== null &&
83
+ x.__neetruSafeExit === true);
84
+ }
85
+ /**
86
+ * Em teste (vitest) mantemos `process.exit` SÍNCRONO: a suíte mocka/espera o
87
+ * `process.exit` e o exit deferido penduraria os workers / mudaria a semântica.
88
+ */
89
+ function isTestEnv() {
90
+ return process.env.VITEST === 'true' || process.env.NODE_ENV === 'test';
91
+ }
65
92
  /**
66
93
  * Sai com `code` após cleanup de handles libuv. NEVER returns.
67
94
  * Use em error paths que tocaram I/O assíncrono.
95
+ *
96
+ * Windows + undici (bug_818cf6d): `fetch()` do Node deixa um `uv_async_t`
97
+ * aberto; `process.exit()` SÍNCRONO o mata em `UV_HANDLE_CLOSING` (assertion
98
+ * fatal em `src\win\async.c`). RE-CONFIRMADO empiricamente 2026-07-16 que
99
+ * stdin.destroy() NÃO resolve — só deferir o exit um macrotask real (~100ms)
100
+ * deixa o handle fechar (setImmediate/delay-0 NÃO bastam). Em produção
101
+ * agendamos o exit e lançamos `SafeExitHalt` pra impedir a continuação síncrona
102
+ * do caller (preserva o contrato `never`); o timer é agendado ANTES do throw,
103
+ * então mesmo que um `try/catch` intermediário engula o sinal, o processo sai.
68
104
  */
69
105
  export function safeExit(code) {
70
106
  cleanupHandles();
71
- process.exit(code);
107
+ if (isTestEnv()) {
108
+ // Em teste (vitest) o process.exit é mockado (spy que NÃO sai de fato).
109
+ // O `return` replica EXATAMENTE o safeExit original: chama process.exit e
110
+ // termina — sem cair no defer/throw abaixo (que quebraria a suíte). Em
111
+ // produção este branch nunca roda.
112
+ return process.exit(code);
113
+ }
114
+ process.exitCode = code;
115
+ // Adia o process.exit um macrotask real. O gatilho do UV_HANDLE_CLOSING é o
116
+ // `process.exit()` SÍNCRONO correndo com o teardown do uv_async_t do undici;
117
+ // ~300ms deixa o handle assentar antes da saída (empírico 2026-07-16: 100ms
118
+ // é racy → crash intermitente; 250-300ms → 0 crash em 35+ runs). NÃO fechar
119
+ // o dispatcher do undici aqui: `await dispatcher.close()` + process.exit
120
+ // crasha 25/25 (o force-exit ainda corre com o teardown) e quebra o flush
121
+ // de telemetria keepalive.
122
+ setTimeout(() => process.exit(code), 300);
123
+ throw new SafeExitHalt(code);
72
124
  }
73
125
  /**
74
126
  * Seta process.exitCode mas NÃO sai imediatamente — caller deve `return`.
@@ -1 +1 @@
1
- {"version":3,"file":"safe-exit.js","sourceRoot":"","sources":["../../src/utils/safe-exit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,SAAS,cAAc;IACrB,gEAAgE;IAChE,IAAI,CAAC;QAAC,eAAe,EAAE,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAEtD,yEAAyE;IACzE,mCAAmC;IACnC,EAAE;IACF,+BAA+B;IAC/B,0EAA0E;IAC1E,0EAA0E;IAC1E,sEAAsE;IACtE,yEAAyE;IACzE,sEAAsE;IACtE,sDAAsD;IACtD,EAAE;IACF,uEAAuE;IACvE,qEAAqE;IACrE,yEAAyE;IACzE,yEAAyE;IACzE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YAAC,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAC7D,CAAC;IAED,kEAAkE;IAClE,gFAAgF;IAChF,wEAAwE;IACxE,0EAA0E;IAC1E,mEAAmE;IACnE,uEAAuE;IACvE,wEAAwE;IACxE,0EAA0E;AAC5E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,cAAc,EAAE,CAAC;IACjB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,cAAc,EAAE,CAAC;IACjB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC1B,CAAC"}
1
+ {"version":3,"file":"safe-exit.js","sourceRoot":"","sources":["../../src/utils/safe-exit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,SAAS,cAAc;IACrB,gEAAgE;IAChE,IAAI,CAAC;QAAC,eAAe,EAAE,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAEtD,yEAAyE;IACzE,mCAAmC;IACnC,EAAE;IACF,+BAA+B;IAC/B,0EAA0E;IAC1E,0EAA0E;IAC1E,sEAAsE;IACtE,yEAAyE;IACzE,sEAAsE;IACtE,sDAAsD;IACtD,EAAE;IACF,uEAAuE;IACvE,qEAAqE;IACrE,yEAAyE;IACzE,yEAAyE;IACzE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YAAC,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAC7D,CAAC;IAED,kEAAkE;IAClE,gFAAgF;IAChF,wEAAwE;IACxE,0EAA0E;IAC1E,mEAAmE;IACnE,uEAAuE;IACvE,wEAAwE;IACxE,0EAA0E;AAC5E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,YAAY;IAEK;IADnB,gBAAgB,GAAG,IAAa,CAAC;IAC1C,YAA4B,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;CAC7C;AAED,2EAA2E;AAC3E,MAAM,UAAU,cAAc,CAAC,CAAU;IACvC,OAAO,CACL,OAAO,CAAC,KAAK,QAAQ;QACrB,CAAC,KAAK,IAAI;QACT,CAAoC,CAAC,gBAAgB,KAAK,IAAI,CAChE,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS;IAChB,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,CAAC;AAC1E,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,cAAc,EAAE,CAAC;IACjB,IAAI,SAAS,EAAE,EAAE,CAAC;QAChB,wEAAwE;QACxE,0EAA0E;QAC1E,uEAAuE;QACvE,mCAAmC;QACnC,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IACxB,4EAA4E;IAC5E,6EAA6E;IAC7E,4EAA4E;IAC5E,4EAA4E;IAC5E,yEAAyE;IACzE,0EAA0E;IAC1E,2BAA2B;IAC3B,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1C,MAAM,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,cAAc,EAAE,CAAC;IACjB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC1B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neetru/cli",
3
- "version": "2.24.1",
3
+ "version": "2.26.0",
4
4
  "type": "module",
5
5
  "description": "Neetru Developer Kit — scaffold, AI assistant, publish, build e deploy de produtos SaaS Neetru via Core (VM-based agents)",
6
6
  "keywords": [
@@ -1,22 +1,22 @@
1
- /**
2
- * Template — `src/lib/neetru/auth/callback.ts` (gerado por `neetru add auth`).
3
- *
4
- * Handler de callback OIDC (auth code → id_token). Use em `app/oauth/callback/route.ts`.
5
- */
6
- import { NextRequest, NextResponse } from 'next/server';
7
-
8
- export const runtime = 'nodejs';
9
-
10
- export async function GET(request: NextRequest) {
11
- const { searchParams } = new URL(request.url);
12
- const code = searchParams.get('code');
13
- const state = searchParams.get('state');
14
-
15
- if (!code) {
16
- return NextResponse.json({ error: 'missing_code' }, { status: 400 });
17
- }
18
-
19
- // Trocar code → id_token contra auth.neetru.com/token
20
- // Implementação detalhada em docs/PLATFORM_AUTH.md §4
21
- return NextResponse.redirect(state ?? '/');
22
- }
1
+ /**
2
+ * Template — `src/lib/neetru/auth/callback.ts` (gerado por `neetru add auth`).
3
+ *
4
+ * Handler de callback OIDC (auth code → id_token). Use em `app/oauth/callback/route.ts`.
5
+ */
6
+ import { NextRequest, NextResponse } from 'next/server';
7
+
8
+ export const runtime = 'nodejs';
9
+
10
+ export async function GET(request: NextRequest) {
11
+ const { searchParams } = new URL(request.url);
12
+ const code = searchParams.get('code');
13
+ const state = searchParams.get('state');
14
+
15
+ if (!code) {
16
+ return NextResponse.json({ error: 'missing_code' }, { status: 400 });
17
+ }
18
+
19
+ // Trocar code → id_token contra auth.neetru.com/token
20
+ // Implementação detalhada em docs/PLATFORM_AUTH.md §4
21
+ return NextResponse.redirect(state ?? '/');
22
+ }
@@ -1,41 +1,41 @@
1
- /**
2
- * Template — `src/lib/neetru/auth/sign-in.tsx` (gerado por `neetru add auth`).
3
- *
4
- * Botão Google → redireciona pra auth.neetru.com via SDK (`client.auth.signIn`).
5
- *
6
- * Customização: ajuste `redirectUri` se hospedar fora do domínio default.
7
- */
8
- 'use client';
9
-
10
- import { useState } from 'react';
11
- import { createNeetruClient } from '@neetru/sdk';
12
-
13
- const client = createNeetruClient({
14
- apiKey: process.env.NEXT_PUBLIC_NEETRU_API_KEY,
15
- productId: process.env.NEXT_PUBLIC_NEETRU_PRODUCT_ID,
16
- });
17
-
18
- export function SignInButton() {
19
- const [loading, setLoading] = useState(false);
20
-
21
- async function handleSignIn() {
22
- setLoading(true);
23
- try {
24
- await client.auth.signIn();
25
- } catch (err) {
26
- console.error('signIn failed', err);
27
- setLoading(false);
28
- }
29
- }
30
-
31
- return (
32
- <button
33
- type="button"
34
- onClick={handleSignIn}
35
- disabled={loading}
36
- className="inline-flex items-center justify-center px-4 py-2 border bg-white hover:bg-zinc-50 text-zinc-900 font-medium"
37
- >
38
- {loading ? 'Redirecionando...' : 'Entrar com Neetru'}
39
- </button>
40
- );
41
- }
1
+ /**
2
+ * Template — `src/lib/neetru/auth/sign-in.tsx` (gerado por `neetru add auth`).
3
+ *
4
+ * Botão Google → redireciona pra auth.neetru.com via SDK (`client.auth.signIn`).
5
+ *
6
+ * Customização: ajuste `redirectUri` se hospedar fora do domínio default.
7
+ */
8
+ 'use client';
9
+
10
+ import { useState } from 'react';
11
+ import { createNeetruClient } from '@neetru/sdk';
12
+
13
+ const client = createNeetruClient({
14
+ apiKey: process.env.NEXT_PUBLIC_NEETRU_API_KEY,
15
+ productId: process.env.NEXT_PUBLIC_NEETRU_PRODUCT_ID,
16
+ });
17
+
18
+ export function SignInButton() {
19
+ const [loading, setLoading] = useState(false);
20
+
21
+ async function handleSignIn() {
22
+ setLoading(true);
23
+ try {
24
+ await client.auth.signIn();
25
+ } catch (err) {
26
+ console.error('signIn failed', err);
27
+ setLoading(false);
28
+ }
29
+ }
30
+
31
+ return (
32
+ <button
33
+ type="button"
34
+ onClick={handleSignIn}
35
+ disabled={loading}
36
+ className="inline-flex items-center justify-center px-4 py-2 border bg-white hover:bg-zinc-50 text-zinc-900 font-medium"
37
+ >
38
+ {loading ? 'Redirecionando...' : 'Entrar com Neetru'}
39
+ </button>
40
+ );
41
+ }
@@ -1,22 +1,38 @@
1
- /**
2
- * Template — `src/lib/neetru/billing/checkout.ts` (gerado por `neetru add billing`).
3
- *
4
- * Inicia checkout Stripe via Core. Server route handler.
5
- */
6
- import { NextRequest, NextResponse } from 'next/server';
7
-
8
- export const runtime = 'nodejs';
9
-
10
- export async function POST(request: NextRequest) {
11
- const body = await request.json();
12
- const slug = body?.slug as string | undefined;
13
- if (!slug) {
14
- return NextResponse.json({ error: 'slug required' }, { status: 400 });
15
- }
16
-
17
- // Hit Core REST: POST /api/v1/billing/checkout
18
- // (Sprint 9 expõe via SDK direto)
19
- return NextResponse.json({
20
- redirectUrl: `https://minhaconta.neetru.com/checkout?slug=${slug}`,
21
- });
22
- }
1
+ /**
2
+ * Template — `src/app/api/billing/checkout/route.ts` (gerado por `neetru add billing`).
3
+ *
4
+ * Inicia checkout Stripe via SDK server-side. O cliente chama este endpoint
5
+ * com { planId, productId } e recebe a URL de redirecionamento pro Stripe.
6
+ *
7
+ * Pré-requisito: NEETRU_API_KEY e NEETRU_PRODUCT_ID no .env.local.
8
+ */
9
+ import { NextRequest, NextResponse } from 'next/server';
10
+ import { createNeetruClient } from '@neetru/sdk';
11
+
12
+ export const runtime = 'nodejs';
13
+
14
+ export async function POST(request: NextRequest) {
15
+ const body = await request.json();
16
+ const { planId, productId } = body ?? {};
17
+ const pid = productId || process.env.NEETRU_PRODUCT_ID;
18
+
19
+ if (!planId || !pid) {
20
+ return NextResponse.json(
21
+ { error: 'planId e productId obrigatórios' },
22
+ { status: 400 },
23
+ );
24
+ }
25
+
26
+ const client = createNeetruClient({
27
+ apiKey: process.env.NEETRU_API_KEY!,
28
+ productId: pid,
29
+ });
30
+
31
+ const result = await client.checkout.start({
32
+ productId: pid,
33
+ planId,
34
+ callbackUrl: `${process.env.NEXT_PUBLIC_APP_URL ?? 'http://localhost:3000'}/billing/success`,
35
+ });
36
+
37
+ return NextResponse.json({ redirectUrl: result.redirectUrl });
38
+ }