@nimbuslab/cli 0.3.4 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -37,12 +37,49 @@ nimbus help
37
37
  | `help` | Mostrar ajuda |
38
38
  | `version` | Mostrar versao |
39
39
 
40
+ ## Flags
41
+
42
+ ### Tipo de projeto
43
+
44
+ | Flag | Descricao |
45
+ |------|-----------|
46
+ | `--fast` | Landing page (template fast) |
47
+ | `--fast-plus` | SaaS single-repo (template fast+) |
48
+ | `--turborepo` | SaaS monorepo (template fast+ turborepo) |
49
+
50
+ ### Opcoes
51
+
52
+ | Flag | Descricao |
53
+ |------|-----------|
54
+ | `-y, --yes` | Aceitar defaults |
55
+ | `--no-git` | Nao inicializar Git |
56
+ | `--no-install` | Nao instalar dependencias |
57
+ | `--railway` | Configurar Railway automaticamente |
58
+ | `--template <url>` | Usar template customizado |
59
+
60
+ ### Exemplos
61
+
62
+ ```bash
63
+ # Landing page rapida
64
+ nimbus create meu-site --fast
65
+
66
+ # SaaS com Railway
67
+ nimbus create meu-saas --fast-plus --railway
68
+
69
+ # Monorepo sem instalar deps
70
+ nimbus create meu-app --turborepo --no-install
71
+
72
+ # Template customizado
73
+ nimbus create projeto --template usuario/repositorio
74
+ ```
75
+
40
76
  ## Tipos de Projeto
41
77
 
42
78
  | Tipo | Descricao |
43
79
  |------|-----------|
44
80
  | `fast` | Landing page em 6 dias |
45
81
  | `fast+` | SaaS completo com backend |
82
+ | `fast+ (monorepo)` | SaaS com Turborepo |
46
83
 
47
84
  ## Requisitos
48
85
 
@@ -95,10 +132,55 @@ npm version major
95
132
  ### Fluxo de release
96
133
 
97
134
  1. Desenvolva na branch `develop`
98
- 2. Avance a versao: `npm version patch|minor|major`
135
+ 2. Avance a versao no `package.json`
99
136
  3. Commit e push: `git push origin develop`
100
- 4. Crie PR para `main`
101
- 5. Apos merge, GitHub Actions publica automaticamente no npm
137
+ 4. Merge para `main`: `git checkout main && git merge develop && git push origin main`
138
+ 5. GitHub Actions publica automaticamente no npm via OIDC
139
+
140
+ ## CI/CD - Publicacao Automatica
141
+
142
+ O projeto usa **OIDC Trusted Publishing** para publicar no npm sem tokens.
143
+
144
+ ### Como funciona
145
+
146
+ 1. GitHub Actions autentica via OIDC (OpenID Connect)
147
+ 2. npm valida a identidade do workflow
148
+ 3. Pacote e publicado sem necessidade de token
149
+
150
+ ### Configuracao inicial (ja feita)
151
+
152
+ **No npmjs.com:**
153
+ 1. Acesse: https://www.npmjs.com/package/@nimbuslab/cli/access
154
+ 2. Em "Trusted Publisher", adicione:
155
+ - Organization: `nimbuslab`
156
+ - Repository: `cli`
157
+ - Workflow: `publish.yml`
158
+ 3. Em "Publishing access", selecione: "Require 2FA or granular token with bypass"
159
+
160
+ **No GitHub:**
161
+ 1. Workflow em `.github/workflows/publish.yml`
162
+ 2. Permissoes: `id-token: write` (obrigatorio para OIDC)
163
+ 3. Node.js 24+ (versoes anteriores tem bug)
164
+
165
+ ### Limitacoes (repo privado)
166
+
167
+ - `--provenance` NAO funciona em repos privados
168
+ - OIDC Trusted Publishing funciona normalmente
169
+ - Se tornar o repo publico, adicionar `--provenance` ao publish
170
+
171
+ ### Troubleshooting
172
+
173
+ | Erro | Causa | Solucao |
174
+ |------|-------|---------|
175
+ | "Access token expired" | Token classico deprecado | Usar OIDC Trusted Publishing |
176
+ | E404 + "not in registry" | OIDC nao configurado | Configurar Trusted Publisher no npm |
177
+ | E422 + "private repository" | --provenance em repo privado | Remover --provenance |
178
+ | "id-token permission" | Falta permissao no workflow | Adicionar `id-token: write` |
179
+
180
+ ### Referencias
181
+
182
+ - [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers/)
183
+ - [OIDC Announcement](https://github.blog/changelog/2025-07-31-npm-trusted-publishing-with-oidc-is-generally-available/)
102
184
 
103
185
  ## Stack
104
186
 
package/dist/index.js CHANGED
@@ -851,6 +851,45 @@ var TEMPLATES = {
851
851
  "fast+": "nimbuslab-templates/fastplus-template",
852
852
  "fast+-monorepo": "nimbuslab-templates/fastplus-monorepo-template"
853
853
  };
854
+ function parseFlags(args) {
855
+ const flags = {
856
+ yes: false,
857
+ fast: false,
858
+ fastPlus: false,
859
+ turborepo: false,
860
+ noGit: false,
861
+ noInstall: false,
862
+ railway: false,
863
+ template: null
864
+ };
865
+ let projectName;
866
+ let i = 0;
867
+ while (i < args.length) {
868
+ const arg = args[i];
869
+ if (arg === "-y" || arg === "--yes") {
870
+ flags.yes = true;
871
+ } else if (arg === "--fast") {
872
+ flags.fast = true;
873
+ } else if (arg === "--fast-plus") {
874
+ flags.fastPlus = true;
875
+ } else if (arg === "--turborepo") {
876
+ flags.turborepo = true;
877
+ } else if (arg === "--no-git") {
878
+ flags.noGit = true;
879
+ } else if (arg === "--no-install") {
880
+ flags.noInstall = true;
881
+ } else if (arg === "--railway") {
882
+ flags.railway = true;
883
+ } else if (arg === "--template") {
884
+ i++;
885
+ flags.template = args[i] ?? null;
886
+ } else if (!arg.startsWith("-")) {
887
+ projectName = arg;
888
+ }
889
+ i++;
890
+ }
891
+ return { flags, projectName };
892
+ }
854
893
  async function ensureRailwayCli() {
855
894
  const checkCmd = process.platform === "win32" ? "where" : "which";
856
895
  const hasRailway = await $2`${checkCmd} railway`.quiet().then(() => true).catch(() => false);
@@ -939,17 +978,19 @@ async function create(args) {
939
978
  console.log();
940
979
  }
941
980
  }
942
- const hasYes = args.includes("-y") || args.includes("--yes");
943
- const projectName = args.find((a) => !a.startsWith("-"));
981
+ const { flags, projectName } = parseFlags(args);
944
982
  Ie(import_picocolors3.default.bgCyan(import_picocolors3.default.black(" Novo Projeto nimbuslab ")));
945
983
  let config;
946
- if (hasYes && projectName) {
984
+ const hasTypeFlag = flags.fast || flags.fastPlus || flags.turborepo;
985
+ const typeFromFlag = flags.turborepo ? "fast+" : flags.fastPlus ? "fast+" : flags.fast ? "fast" : null;
986
+ const monorepoFromFlag = flags.turborepo;
987
+ if ((flags.yes || hasTypeFlag) && projectName) {
947
988
  config = {
948
989
  name: projectName,
949
- type: "fast",
950
- monorepo: false,
951
- git: true,
952
- install: true,
990
+ type: typeFromFlag || "fast",
991
+ monorepo: monorepoFromFlag,
992
+ git: !flags.noGit,
993
+ install: !flags.noInstall,
953
994
  github: false,
954
995
  githubOrg: null,
955
996
  githubDescription: "",
@@ -960,17 +1001,47 @@ async function create(args) {
960
1001
  railwayProject: "",
961
1002
  railwayToken: "",
962
1003
  stagingUrl: "",
963
- productionUrl: ""
1004
+ productionUrl: "",
1005
+ customTemplate: flags.template
964
1006
  };
1007
+ const typeLabel = flags.turborepo ? "fast+ (monorepo)" : config.type;
965
1008
  console.log(import_picocolors3.default.dim(` Projeto: ${projectName}`));
966
- console.log(import_picocolors3.default.dim(` Tipo: fast`));
967
- console.log(import_picocolors3.default.dim(` Git: sim`));
968
- console.log(import_picocolors3.default.dim(` GitHub: nao`));
969
- console.log(import_picocolors3.default.dim(` Infra: configurar depois`));
970
- console.log(import_picocolors3.default.dim(` Instalar: sim`));
1009
+ console.log(import_picocolors3.default.dim(` Tipo: ${typeLabel}`));
1010
+ console.log(import_picocolors3.default.dim(` Git: ${config.git ? "sim" : "nao"}`));
1011
+ console.log(import_picocolors3.default.dim(` Instalar: ${config.install ? "sim" : "nao"}`));
1012
+ if (flags.railway)
1013
+ console.log(import_picocolors3.default.dim(` Railway: configurar`));
1014
+ if (flags.template)
1015
+ console.log(import_picocolors3.default.dim(` Template: ${flags.template}`));
971
1016
  console.log();
1017
+ if (flags.railway) {
1018
+ const railwayAuthenticated = await isRailwayAuthenticated();
1019
+ if (railwayAuthenticated) {
1020
+ if (config.type === "fast") {
1021
+ const projects = await listRailwayProjects();
1022
+ const fastProject = projects.find((p2) => p2.toLowerCase().includes("fast by nimbuslab"));
1023
+ if (fastProject) {
1024
+ config.railwayProject = fastProject;
1025
+ console.log(import_picocolors3.default.green(` Railway: ${fastProject}`));
1026
+ }
1027
+ } else {
1028
+ console.log(import_picocolors3.default.dim(` Criando projeto Railway: ${projectName}...`));
1029
+ try {
1030
+ const result = await $2`echo "" | railway init -n ${projectName} -w nimbuslab --json`.text();
1031
+ const newProject = JSON.parse(result);
1032
+ config.railwayProject = newProject.name || projectName;
1033
+ console.log(import_picocolors3.default.green(` Railway: ${config.railwayProject} criado`));
1034
+ } catch {
1035
+ console.log(import_picocolors3.default.yellow(` Railway: erro ao criar projeto`));
1036
+ }
1037
+ }
1038
+ } else {
1039
+ console.log(import_picocolors3.default.yellow(` Railway: nao autenticado (railway login)`));
1040
+ }
1041
+ console.log();
1042
+ }
972
1043
  } else {
973
- config = await promptConfig(projectName);
1044
+ config = await promptConfig(projectName, flags);
974
1045
  }
975
1046
  if (pD(config)) {
976
1047
  xe("Operacao cancelada");
@@ -980,7 +1051,7 @@ async function create(args) {
980
1051
  Se(import_picocolors3.default.green("Projeto criado com sucesso!"));
981
1052
  showNextSteps(config);
982
1053
  }
983
- async function promptConfig(initialName) {
1054
+ async function promptConfig(initialName, flags) {
984
1055
  const name = await he({
985
1056
  message: "Nome do projeto:",
986
1057
  placeholder: "meu-projeto",
@@ -1217,14 +1288,22 @@ async function promptConfig(initialName) {
1217
1288
  railwayProject,
1218
1289
  railwayToken,
1219
1290
  stagingUrl,
1220
- productionUrl
1291
+ productionUrl,
1292
+ customTemplate: flags?.template || null
1221
1293
  };
1222
1294
  }
1223
1295
  async function createProject(config) {
1224
1296
  const s = Y2();
1225
- const templateKey = config.type === "fast+" && config.monorepo ? "fast+-monorepo" : config.type;
1226
- const templateRepo = TEMPLATES[templateKey];
1227
- const templateLabel = config.monorepo ? `${config.type} (monorepo)` : config.type;
1297
+ let templateRepo;
1298
+ let templateLabel;
1299
+ if (config.customTemplate) {
1300
+ templateRepo = config.customTemplate;
1301
+ templateLabel = `customizado (${config.customTemplate})`;
1302
+ } else {
1303
+ const templateKey = config.type === "fast+" && config.monorepo ? "fast+-monorepo" : config.type;
1304
+ templateRepo = TEMPLATES[templateKey];
1305
+ templateLabel = config.monorepo ? `${config.type} (monorepo)` : config.type;
1306
+ }
1228
1307
  s.start(`Clonando template ${templateLabel}...`);
1229
1308
  try {
1230
1309
  await $2`gh repo clone ${templateRepo} ${config.name} -- --depth 1`.quiet();
@@ -1427,13 +1506,25 @@ ${import_picocolors4.default.bold("Comandos:")}
1427
1506
  help Mostrar esta ajuda
1428
1507
  version Mostrar versao
1429
1508
 
1509
+ ${import_picocolors4.default.bold("Tipo de projeto:")}
1510
+ --fast Landing page (template fast)
1511
+ --fast-plus SaaS single-repo (template fast+)
1512
+ --turborepo SaaS monorepo (template fast+ turborepo)
1513
+
1430
1514
  ${import_picocolors4.default.bold("Opcoes:")}
1431
- -y, --yes Aceitar defaults (fast, git, install)
1515
+ -y, --yes Aceitar defaults
1516
+ --no-git Nao inicializar Git
1517
+ --no-install Nao instalar dependencias
1518
+ --railway Configurar Railway automaticamente
1519
+ --template <url> Usar template customizado
1432
1520
 
1433
1521
  ${import_picocolors4.default.bold("Exemplos:")}
1434
1522
  ${import_picocolors4.default.dim("$")} nimbus create meu-projeto
1435
1523
  ${import_picocolors4.default.dim("$")} nimbus create meu-projeto -y
1436
- ${import_picocolors4.default.dim("$")} nimbus create
1524
+ ${import_picocolors4.default.dim("$")} nimbus create meu-projeto --fast
1525
+ ${import_picocolors4.default.dim("$")} nimbus create meu-saas --fast-plus --railway
1526
+ ${import_picocolors4.default.dim("$")} nimbus create meu-app --turborepo --no-install
1527
+ ${import_picocolors4.default.dim("$")} nimbus create custom --template user/repo
1437
1528
  `);
1438
1529
  }
1439
1530
  function showVersion() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nimbuslab/cli",
3
- "version": "0.3.4",
3
+ "version": "0.4.1",
4
4
  "description": "CLI para criar projetos nimbuslab",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,6 +31,64 @@ interface ProjectConfig {
31
31
  railwayToken: string
32
32
  stagingUrl: string
33
33
  productionUrl: string
34
+ // Template customizado
35
+ customTemplate: string | null
36
+ }
37
+
38
+ interface CreateFlags {
39
+ yes: boolean
40
+ fast: boolean
41
+ fastPlus: boolean
42
+ turborepo: boolean
43
+ noGit: boolean
44
+ noInstall: boolean
45
+ railway: boolean
46
+ template: string | null
47
+ }
48
+
49
+ function parseFlags(args: string[]): { flags: CreateFlags; projectName: string | undefined } {
50
+ const flags: CreateFlags = {
51
+ yes: false,
52
+ fast: false,
53
+ fastPlus: false,
54
+ turborepo: false,
55
+ noGit: false,
56
+ noInstall: false,
57
+ railway: false,
58
+ template: null,
59
+ }
60
+
61
+ let projectName: string | undefined
62
+ let i = 0
63
+
64
+ while (i < args.length) {
65
+ const arg = args[i]!
66
+
67
+ if (arg === "-y" || arg === "--yes") {
68
+ flags.yes = true
69
+ } else if (arg === "--fast") {
70
+ flags.fast = true
71
+ } else if (arg === "--fast-plus") {
72
+ flags.fastPlus = true
73
+ } else if (arg === "--turborepo") {
74
+ flags.turborepo = true
75
+ } else if (arg === "--no-git") {
76
+ flags.noGit = true
77
+ } else if (arg === "--no-install") {
78
+ flags.noInstall = true
79
+ } else if (arg === "--railway") {
80
+ flags.railway = true
81
+ } else if (arg === "--template") {
82
+ i++
83
+ flags.template = args[i] ?? null
84
+ } else if (!arg.startsWith("-")) {
85
+ projectName = arg
86
+ }
87
+
88
+ i++
89
+ }
90
+
91
+ return { flags, projectName }
34
92
  }
35
93
 
36
94
  // M30: Verificar e instalar Railway CLI
@@ -139,21 +197,25 @@ export async function create(args: string[]) {
139
197
  }
140
198
  }
141
199
 
142
- const hasYes = args.includes("-y") || args.includes("--yes")
143
- const projectName = args.find(a => !a.startsWith("-"))
200
+ const { flags, projectName } = parseFlags(args)
144
201
 
145
202
  p.intro(pc.bgCyan(pc.black(" Novo Projeto nimbuslab ")))
146
203
 
147
204
  let config: ProjectConfig | symbol
148
205
 
149
- if (hasYes && projectName) {
150
- // Modo automatico com defaults (sem configs de infra)
206
+ // Determina tipo baseado nas flags
207
+ const hasTypeFlag = flags.fast || flags.fastPlus || flags.turborepo
208
+ const typeFromFlag = flags.turborepo ? "fast+" : flags.fastPlus ? "fast+" : flags.fast ? "fast" : null
209
+ const monorepoFromFlag = flags.turborepo
210
+
211
+ // Modo automatico: -y com nome OU flags de tipo com nome
212
+ if ((flags.yes || hasTypeFlag) && projectName) {
151
213
  config = {
152
214
  name: projectName,
153
- type: "fast",
154
- monorepo: false,
155
- git: true,
156
- install: true,
215
+ type: typeFromFlag || "fast",
216
+ monorepo: monorepoFromFlag,
217
+ git: !flags.noGit,
218
+ install: !flags.noInstall,
157
219
  github: false,
158
220
  githubOrg: null,
159
221
  githubDescription: "",
@@ -165,16 +227,48 @@ export async function create(args: string[]) {
165
227
  railwayToken: "",
166
228
  stagingUrl: "",
167
229
  productionUrl: "",
230
+ customTemplate: flags.template,
168
231
  }
232
+
233
+ const typeLabel = flags.turborepo ? "fast+ (monorepo)" : config.type
169
234
  console.log(pc.dim(` Projeto: ${projectName}`))
170
- console.log(pc.dim(` Tipo: fast`))
171
- console.log(pc.dim(` Git: sim`))
172
- console.log(pc.dim(` GitHub: nao`))
173
- console.log(pc.dim(` Infra: configurar depois`))
174
- console.log(pc.dim(` Instalar: sim`))
235
+ console.log(pc.dim(` Tipo: ${typeLabel}`))
236
+ console.log(pc.dim(` Git: ${config.git ? "sim" : "nao"}`))
237
+ console.log(pc.dim(` Instalar: ${config.install ? "sim" : "nao"}`))
238
+ if (flags.railway) console.log(pc.dim(` Railway: configurar`))
239
+ if (flags.template) console.log(pc.dim(` Template: ${flags.template}`))
175
240
  console.log()
241
+
242
+ // Railway automatico se flag --railway
243
+ if (flags.railway) {
244
+ const railwayAuthenticated = await isRailwayAuthenticated()
245
+ if (railwayAuthenticated) {
246
+ if (config.type === "fast") {
247
+ const projects = await listRailwayProjects()
248
+ const fastProject = projects.find(p => p.toLowerCase().includes("fast by nimbuslab"))
249
+ if (fastProject) {
250
+ config.railwayProject = fastProject
251
+ console.log(pc.green(` Railway: ${fastProject}`))
252
+ }
253
+ } else {
254
+ // Fast+: cria projeto novo automaticamente
255
+ console.log(pc.dim(` Criando projeto Railway: ${projectName}...`))
256
+ try {
257
+ const result = await $`echo "" | railway init -n ${projectName} -w nimbuslab --json`.text()
258
+ const newProject = JSON.parse(result)
259
+ config.railwayProject = newProject.name || projectName
260
+ console.log(pc.green(` Railway: ${config.railwayProject} criado`))
261
+ } catch {
262
+ console.log(pc.yellow(` Railway: erro ao criar projeto`))
263
+ }
264
+ }
265
+ } else {
266
+ console.log(pc.yellow(` Railway: nao autenticado (railway login)`))
267
+ }
268
+ console.log()
269
+ }
176
270
  } else {
177
- config = await promptConfig(projectName)
271
+ config = await promptConfig(projectName, flags)
178
272
  }
179
273
 
180
274
  if (p.isCancel(config)) {
@@ -189,7 +283,7 @@ export async function create(args: string[]) {
189
283
  showNextSteps(config as ProjectConfig)
190
284
  }
191
285
 
192
- async function promptConfig(initialName?: string): Promise<ProjectConfig | symbol> {
286
+ async function promptConfig(initialName?: string, flags?: CreateFlags): Promise<ProjectConfig | symbol> {
193
287
  const name = await p.text({
194
288
  message: "Nome do projeto:",
195
289
  placeholder: "meu-projeto",
@@ -468,16 +562,26 @@ async function promptConfig(initialName?: string): Promise<ProjectConfig | symbo
468
562
  railwayToken,
469
563
  stagingUrl,
470
564
  productionUrl,
565
+ customTemplate: flags?.template || null,
471
566
  }
472
567
  }
473
568
 
474
569
  async function createProject(config: ProjectConfig) {
475
570
  const s = p.spinner()
476
571
 
477
- // Clone template baseado no tipo (fast ou fast+) e monorepo
478
- const templateKey = config.type === "fast+" && config.monorepo ? "fast+-monorepo" : config.type
479
- const templateRepo = TEMPLATES[templateKey]
480
- const templateLabel = config.monorepo ? `${config.type} (monorepo)` : config.type
572
+ // Determina template: customizado ou padrao
573
+ let templateRepo: string
574
+ let templateLabel: string
575
+
576
+ if (config.customTemplate) {
577
+ templateRepo = config.customTemplate
578
+ templateLabel = `customizado (${config.customTemplate})`
579
+ } else {
580
+ const templateKey = config.type === "fast+" && config.monorepo ? "fast+-monorepo" : config.type
581
+ templateRepo = TEMPLATES[templateKey]
582
+ templateLabel = config.monorepo ? `${config.type} (monorepo)` : config.type
583
+ }
584
+
481
585
  s.start(`Clonando template ${templateLabel}...`)
482
586
  try {
483
587
  await $`gh repo clone ${templateRepo} ${config.name} -- --depth 1`.quiet()
package/src/index.ts CHANGED
@@ -43,13 +43,25 @@ ${pc.bold("Comandos:")}
43
43
  help Mostrar esta ajuda
44
44
  version Mostrar versao
45
45
 
46
+ ${pc.bold("Tipo de projeto:")}
47
+ --fast Landing page (template fast)
48
+ --fast-plus SaaS single-repo (template fast+)
49
+ --turborepo SaaS monorepo (template fast+ turborepo)
50
+
46
51
  ${pc.bold("Opcoes:")}
47
- -y, --yes Aceitar defaults (fast, git, install)
52
+ -y, --yes Aceitar defaults
53
+ --no-git Nao inicializar Git
54
+ --no-install Nao instalar dependencias
55
+ --railway Configurar Railway automaticamente
56
+ --template <url> Usar template customizado
48
57
 
49
58
  ${pc.bold("Exemplos:")}
50
59
  ${pc.dim("$")} nimbus create meu-projeto
51
60
  ${pc.dim("$")} nimbus create meu-projeto -y
52
- ${pc.dim("$")} nimbus create
61
+ ${pc.dim("$")} nimbus create meu-projeto --fast
62
+ ${pc.dim("$")} nimbus create meu-saas --fast-plus --railway
63
+ ${pc.dim("$")} nimbus create meu-app --turborepo --no-install
64
+ ${pc.dim("$")} nimbus create custom --template user/repo
53
65
  `)
54
66
  }
55
67