rapidkit 0.25.5 → 0.25.6

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/dist/index.js CHANGED
@@ -1,3569 +1,168 @@
1
1
  #!/usr/bin/env node
2
- import {e,d as d$1,c as c$2,h as h$3,a as a$3}from'./chunk-UOGFCKQ5.js';import {a as a$2,b as b$1,c as c$1,d,i,h as h$1,e as e$1,f}from'./chunk-KAV65WZO.js';import {c,a as a$1,b as b$2}from'./chunk-EDH5S5JF.js';import {a,h as h$2,i as i$1,b as b$3,c as c$3,d as d$2,f as f$1,k,g}from'./chunk-Z5LKRG57.js';import {Command,Option}from'commander';import l from'chalk';import xe from'inquirer';import h from'path';import {fileURLToPath}from'url';import {exec,spawn}from'child_process';import Gr from'validate-npm-package-name';import*as b from'fs-extra';import b__default from'fs-extra';import _,{promises,createWriteStream}from'fs';import {execa}from'execa';import Wr from'nunjucks';import Ut from'ora';import Vr,{createHash,createVerify,createHmac}from'crypto';import Tn from'os';import {promisify}from'util';import cr from'http';import lr from'https';function Wt(e){let o=Gr(e);if(!o.validForNewPackages){let r=o.errors||[],i=o.warnings||[],n=[...r,...i];throw new e$1(e,`NPM validation failed: ${n.join(", ")}`)}if(!/^[a-z][a-z0-9_-]*$/.test(e))throw new e$1(e,"Must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores");if(["test","tests","src","dist","build","lib","python","pip","poetry","node","npm","rapidkit","rapidkit"].includes(e.toLowerCase()))throw new e$1(e,`"${e}" is a reserved name. Please choose a different name.`);if(e.length<2)throw new e$1(e,"Name must be at least 2 characters long");if(e.length>214)throw new e$1(e,"Name must be less than 214 characters");return true}function Lr(){return d$2()}function qr(e,o){return e==="py"?["-3",...o]:o}function Fr(e){return typeof e=="object"&&e!==null}async function Hr(e,o,t,r=8e3){try{let i=await execa(e,o,{cwd:t,timeout:r,reject:false,stdio:"pipe"});return {ok:i.exitCode===0,exitCode:i.exitCode,stdout:i.stdout,stderr:i.stderr}}catch(i){return {ok:false,exitCode:void 0,stdout:"",stderr:i instanceof Error?i.message:String(i)}}}async function Kr(e,o){let t=["-m","rapidkit",...e],r=Lr();for(let i of r){let n=await Hr(i,qr(i,t),o?.cwd,o?.timeoutMs);if(!n.ok)continue;let a=(n.stdout??"").trim();try{let s=JSON.parse(a);return Fr(s)?{ok:true,command:i,exitCode:n.exitCode,stdout:n.stdout,stderr:n.stderr,data:s}:{ok:false,command:i,exitCode:n.exitCode,stdout:n.stdout,stderr:n.stderr}}catch{return {ok:false,command:i,exitCode:n.exitCode,stdout:n.stdout,stderr:n.stderr}}}return {ok:false}}async function vo(e,o){let t=await Kr(["project","detect","--path",e,"--json"],o);return !t.ok||!t.data||t.data.schema_version!==1?{ok:false,command:t.command,exitCode:t.exitCode,stdout:t.stdout,stderr:t.stderr}:t}var Br=fileURLToPath(import.meta.url),Jr=h.dirname(Br);function Yr(e=32){let o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",t=Vr.randomBytes(e),r="";for(let i=0;i<e;i++)r+=o[t[i]%o.length];return r}async function ko(e,o){let t=o.template||"fastapi",r=t==="fastapi",i=r?"FastAPI":"NestJS",n=Ut(`Generating ${i} project...`).start();try{let a=h.resolve(Jr,".."),s=o.kit_name||`${t}.standard`,c$1;s==="fastapi.ddd"?c$1="fastapi-ddd":s.startsWith("fastapi")?c$1="fastapi-standard":c$1="nestjs-standard";let p=h.join(a,"templates","kits",c$1),d=Wr.configure(p,{autoescape:false,trimBlocks:true,lstripBlocks:true});d.addFilter("generate_secret",function(g,R=32){return Yr(R)});let u={project_name:o.project_name,author:o.author||"RapidKit User",description:o.description||(r?"FastAPI service generated with RapidKit":"NestJS application generated with RapidKit"),app_version:o.app_version||"0.1.0",license:o.license||"MIT",package_manager:o.package_manager||"npm",node_version:o.node_version||"20.0.0",database_type:o.database_type||"postgresql",include_caching:o.include_caching||false,created_at:new Date().toISOString(),rapidkit_version:c()},m;r?m=["src/main.py.j2","src/__init__.py.j2","src/cli.py.j2","src/routing/__init__.py.j2","src/routing/health.py.j2","src/modules/__init__.py.j2","tests/__init__.py.j2","README.md.j2","pyproject.toml.j2","Makefile.j2",".rapidkit/__init__.py.j2",".rapidkit/project.json.j2",".rapidkit/cli.py.j2",".rapidkit/rapidkit.j2",".rapidkit/activate.j2","rapidkit.j2","rapidkit.cmd.j2"]:m=["src/main.ts.j2","src/app.module.ts.j2","src/app.controller.ts.j2","src/app.service.ts.j2","src/config/configuration.ts.j2","src/config/validation.ts.j2","src/config/index.ts.j2","src/modules/index.ts.j2","src/examples/examples.module.ts.j2","src/examples/examples.controller.ts.j2","src/examples/examples.service.ts.j2","src/examples/dto/create-note.dto.ts.j2","test/app.controller.spec.ts.j2","test/examples.controller.spec.ts.j2","test/app.e2e-spec.ts.j2","test/jest-e2e.json.j2","package.json.j2","tsconfig.json.j2","tsconfig.build.json.j2","nest-cli.json.j2","jest.config.ts.j2","eslint.config.cjs.j2",".env.example.j2","docker-compose.yml.j2","Dockerfile.j2","README.md.j2",".rapidkit/project.json.j2",".rapidkit/rapidkit.j2",".rapidkit/rapidkit.cmd.j2",".rapidkit/activate.j2","rapidkit.j2","rapidkit.cmd.j2"];for(let g of m){let R=h.join(p,g);try{await promises.access(R);}catch{continue}let x=await promises.readFile(R,"utf-8"),T;try{T=d.renderString(x,u);}catch(G){throw console.error(`Failed to render template: ${g}`),G}let C=g.replace(/\.j2$/,""),E=h.join(e,C);await promises.mkdir(h.dirname(E),{recursive:true}),await promises.writeFile(E,T),(C.endsWith(".rapidkit/rapidkit")||C.endsWith(".rapidkit/cli.py")||C.endsWith(".rapidkit/activate")||C==="rapidkit")&&await promises.chmod(E,493);}if(r){let g=h.join(p,".rapidkit","context.json"),R=h.join(e,".rapidkit","context.json");try{await promises.mkdir(h.join(e,".rapidkit"),{recursive:true}),await promises.copyFile(g,R);}catch{await promises.mkdir(h.join(e,".rapidkit"),{recursive:true});let T=o.engine||"pip";await promises.writeFile(R,JSON.stringify({engine:T,created_by:"rapidkit-npm-fallback"},null,2));}}let w=r?`# Python
3
- __pycache__/
4
- *.py[cod]
5
- *$py.class
6
- *.so
7
- .Python
8
- build/
9
- develop-eggs/
10
- dist/
11
- downloads/
12
- eggs/
13
- .eggs/
14
- lib/
15
- lib64/
16
- parts/
17
- sdist/
18
- var/
19
- wheels/
20
- *.egg-info/
21
- .installed.cfg
22
- *.egg
23
-
24
- # Virtual environments
25
- .venv/
26
- venv/
27
- ENV/
28
- env/
29
-
30
- # IDEs
31
- .vscode/
32
- .idea/
33
- *.swp
34
- *.swo
35
- *~
36
-
37
- # OS
38
- .DS_Store
39
- Thumbs.db
40
-
41
- # Project specific
42
- .env
43
- .env.local
44
- `:`# Node artifacts
45
- node_modules/
46
- dist/
47
- .tmp/
48
- .env
49
- .env.*
50
- !.env.example
51
-
52
- # Logs
53
- logs/
54
- *.log
55
- npm-debug.log*
56
- yarn-debug.log*
57
- yarn-error.log*
58
- pnpm-debug.log*
59
-
60
- # OS
61
- .DS_Store
62
- Thumbs.db
63
-
64
- # IDEs
65
- .idea/
66
- .vscode/
67
-
68
- # Coverage
69
- coverage/
70
- `;if(await promises.writeFile(h.join(e,".gitignore"),w),n.succeed(`${i} project generated!`),!o.skipGit){let g=Ut("Initializing git repository...").start();try{await execa("git",["init"],{cwd:e}),await execa("git",["add","."],{cwd:e}),await execa("git",["commit","-m",`Initial commit: ${i} project via RapidKit`],{cwd:e}),g.succeed("Git repository initialized");}catch{g.warn("Could not initialize git repository");}}if(!r&&!o.skipInstall){let g=o.package_manager||"npm",R=Ut(`Installing dependencies with ${g}...`).start();try{await execa(g,g==="yarn"?["install"]:g==="pnpm"?["install"]:["install"],{cwd:e}),R.succeed("Dependencies installed");}catch{R.warn(`Could not install dependencies. Run '${g} install' manually.`);}}let f=h.basename(e);console.log(`
71
- ${l.yellow("\u26A0\uFE0F Limited offline mode:")} This project was created using basic templates.
72
- ${l.gray("For full kit features, install Python 3.10+ and rapidkit-core:")}
73
- ${l.cyan(" sudo apt install python3 python3-pip python3-venv")}
74
- ${l.cyan(" pip install rapidkit-core")}
75
- `),console.log(r?`
76
- ${l.green("\u2728 FastAPI project created successfully!")}
77
-
78
- ${l.bold("\u{1F4C2} Project structure:")}
79
- ${e}/
80
- \u251C\u2500\u2500 .rapidkit/ # RapidKit CLI module
81
- \u251C\u2500\u2500 src/
82
- \u2502 \u251C\u2500\u2500 main.py # FastAPI application
83
- \u2502 \u251C\u2500\u2500 cli.py # CLI commands
84
- \u2502 \u251C\u2500\u2500 routing/ # API routes
85
- \u2502 \u2514\u2500\u2500 modules/ # Module system
86
- \u251C\u2500\u2500 tests/ # Test suite
87
- \u251C\u2500\u2500 pyproject.toml # Poetry configuration
88
- \u2514\u2500\u2500 README.md
89
-
90
- ${l.bold("\u{1F680} Get started:")}
91
- ${l.cyan(`cd ${f}`)}
92
- ${l.cyan("npx rapidkit init")} ${l.gray("# Install dependencies")}
93
- ${l.cyan("npx rapidkit dev")} ${l.gray("# Start dev server")}
94
-
95
- ${l.bold("\u{1F4DA} Available commands:")}
96
- npx rapidkit init # Install dependencies (poetry install)
97
- npx rapidkit dev # Start dev server with hot reload
98
- npx rapidkit start # Start production server
99
- npx rapidkit test # Run tests
100
- npx rapidkit lint # Lint code
101
- npx rapidkit format # Format code
102
-
103
- ${l.gray("Alternative: make dev, ./rapidkit dev, poetry run dev")}
104
- ${l.gray("\u{1F4A1} Tip: Install globally (npm i -g rapidkit) to use without npx")}
105
- `:`
106
- ${l.green("\u2728 NestJS project created successfully!")}
107
-
108
- ${l.bold("\u{1F4C2} Project structure:")}
109
- ${e}/
110
- \u251C\u2500\u2500 .rapidkit/ # RapidKit CLI module
111
- \u251C\u2500\u2500 src/
112
- \u2502 \u251C\u2500\u2500 main.ts # Application entry point
113
- \u2502 \u251C\u2500\u2500 app.module.ts # Root module
114
- \u2502 \u251C\u2500\u2500 config/ # Configuration
115
- \u2502 \u2514\u2500\u2500 examples/ # Example module
116
- \u251C\u2500\u2500 test/ # Test files
117
- \u251C\u2500\u2500 package.json # Dependencies
118
- \u2514\u2500\u2500 README.md
119
-
120
- ${l.bold("\u{1F680} Get started:")}
121
- ${l.cyan(`cd ${f}`)}
122
- ${l.cyan("npx rapidkit init")} ${l.gray("# Install dependencies")}
123
- ${l.cyan("cp .env.example .env")}
124
- ${l.cyan("npx rapidkit dev")} ${l.gray("# Start dev server")}
125
-
126
- ${l.bold("\u{1F4DA} Available commands:")}
127
- npx rapidkit init # Install dependencies
128
- npx rapidkit dev # Start dev server with hot reload
129
- npx rapidkit start # Start production server
130
- npx rapidkit build # Build for production
131
- npx rapidkit test # Run tests
132
- npx rapidkit lint # Lint code
133
- npx rapidkit format # Format code
134
-
135
- ${l.bold("\u{1F310} API endpoints:")}
136
- http://localhost:8000/health # Health check
137
- http://localhost:8000/docs # Swagger docs
138
- http://localhost:8000/examples/notes # Example API
139
-
140
- ${l.gray("Alternative: npm run start:dev, ./rapidkit dev")}
141
- ${l.gray("\u{1F4A1} Tip: Install globally (npm i -g rapidkit) to use without npx")}
142
- `);}catch(a){throw n.fail(`Failed to generate ${i} project`),a}}function bo(e){return e.split(/[-_\s]+/).map(o=>o.charAt(0).toUpperCase()+o.slice(1)).join("")}async function Qr(e,o){await promises.mkdir(h.dirname(e),{recursive:true}),await promises.writeFile(e,o,"utf8");}function Xr(e){return `package main
143
-
144
- import (
145
- "fmt"
146
- "log/slog"
147
- "os"
148
- "os/signal"
149
- "syscall"
150
- "time"
151
-
152
- _ "${e.module_path}/docs"
153
- "${e.module_path}/internal/config"
154
- "${e.module_path}/internal/server"
155
- )
156
-
157
- // Build-time variables \u2014 injected via -ldflags.
158
- var (
159
- version = "dev"
160
- commit = "none"
161
- date = "unknown"
162
- )
163
-
164
- func main() {
165
- cfg := config.Load()
166
-
167
- log := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
168
- Level: config.ParseLogLevel(cfg.LogLevel),
169
- }))
170
- slog.SetDefault(log)
171
-
172
- app := server.NewApp(cfg)
173
-
174
- // Graceful shutdown on SIGINT / SIGTERM
175
- quit := make(chan os.Signal, 1)
176
- signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
177
-
178
- go func() {
179
- slog.Info("starting", "port", cfg.Port, "version", version, "commit", commit, "date", date, "env", cfg.Env)
180
- fmt.Printf("\\n\u{1F680} Server \u2192 http://127.0.0.1:%s\\n", cfg.Port)
181
- fmt.Printf("\u{1F4D6} Docs \u2192 http://127.0.0.1:%s/docs\\n\\n", cfg.Port)
182
- if err := app.Listen(":" + cfg.Port); err != nil {
183
- slog.Error("server error", "err", err)
184
- os.Exit(1)
185
- }
186
- }()
187
-
188
- <-quit
189
- slog.Info("shutting down\u2026")
190
- if err := app.ShutdownWithTimeout(5 * time.Second); err != nil {
191
- slog.Error("graceful shutdown failed", "err", err)
192
- os.Exit(1)
193
- }
194
- slog.Info("server stopped")
195
- }
196
- `}function Zr(e){return `module ${e.module_path}
197
-
198
- go ${e.go_version}
199
-
200
- require (
201
- github.com/gofiber/fiber/v2 v2.52.5
202
- github.com/swaggo/fiber-swagger v1.3.0
203
- github.com/swaggo/swag v1.16.3
204
- )
205
-
206
- require (
207
- github.com/KyleBanks/depth v1.2.1 // indirect
208
- github.com/andybalholm/brotli v1.1.0 // indirect
209
- github.com/ghodss/yaml v1.0.0 // indirect
210
- github.com/go-openapi/jsonpointer v0.21.0 // indirect
211
- github.com/go-openapi/jsonreference v0.21.0 // indirect
212
- github.com/go-openapi/spec v0.21.0 // indirect
213
- github.com/go-openapi/swag v0.23.0 // indirect
214
- github.com/josharian/intern v1.0.0 // indirect
215
- github.com/klauspost/compress v1.17.6 // indirect
216
- github.com/mailru/easyjson v0.7.7 // indirect
217
- github.com/mattn/go-colorable v0.1.13 // indirect
218
- github.com/mattn/go-isatty v0.0.20 // indirect
219
- github.com/mattn/go-runewidth v0.0.15 // indirect
220
- github.com/rivo/uniseg v0.2.0 // indirect
221
- github.com/swaggo/files v1.0.1 // indirect
222
- github.com/valyala/bytebufferpool v1.0.0 // indirect
223
- github.com/valyala/fasthttp v1.52.0 // indirect
224
- github.com/valyala/tcplisten v1.0.0 // indirect
225
- golang.org/x/net v0.25.0 // indirect
226
- golang.org/x/sys v0.16.0 // indirect
227
- golang.org/x/tools v0.21.0 // indirect
228
- gopkg.in/yaml.v2 v2.4.0 // indirect
229
- )
230
- `}function ei(e){return `package config
231
-
232
- import (
233
- "log/slog"
234
- "os"
235
- "strings"
236
- )
237
-
238
- // Config holds application configuration loaded from environment variables.
239
- type Config struct {
240
- Port string
241
- Env string
242
- LogLevel string
243
- }
244
-
245
- // Load reads configuration from environment variables with sensible defaults.
246
- func Load() *Config {
247
- env := getEnv("APP_ENV", "development")
248
- return &Config{
249
- Port: getEnv("PORT", "${e.port}"),
250
- Env: env,
251
- LogLevel: getEnv("LOG_LEVEL", defaultLogLevel(env)),
252
- }
253
- }
254
-
255
- // ParseLogLevel maps a level string to the corresponding slog.Level.
256
- // Falls back to Info for unrecognised values.
257
- func ParseLogLevel(s string) slog.Level {
258
- switch strings.ToLower(s) {
259
- case "debug":
260
- return slog.LevelDebug
261
- case "warn", "warning":
262
- return slog.LevelWarn
263
- case "error":
264
- return slog.LevelError
265
- default:
266
- return slog.LevelInfo
267
- }
268
- }
269
-
270
- func defaultLogLevel(env string) string {
271
- if env == "development" {
272
- return "debug"
273
- }
274
- return "info"
275
- }
276
-
277
- func getEnv(key, fallback string) string {
278
- if v, ok := os.LookupEnv(key); ok && v != "" {
279
- return v
280
- }
281
- return fallback
282
- }
283
- `}function ti(e){return `package server
284
-
285
- import (
286
- "net/http"
287
- "time"
288
-
289
- "github.com/gofiber/fiber/v2"
290
- "github.com/gofiber/fiber/v2/middleware/recover"
291
- fiberSwagger "github.com/swaggo/fiber-swagger"
292
-
293
- "${e.module_path}/internal/apierr"
294
- "${e.module_path}/internal/config"
295
- "${e.module_path}/internal/handlers"
296
- "${e.module_path}/internal/middleware"
297
- )
298
-
299
- // NewApp creates and configures the Fiber application.
300
- // Call this from main \u2014 or from tests via server.NewApp(cfg).
301
- func NewApp(cfg *config.Config) *fiber.App {
302
- app := fiber.New(fiber.Config{
303
- AppName: "${e.project_name}",
304
- ReadTimeout: 5 * time.Second,
305
- WriteTimeout: 10 * time.Second,
306
- IdleTimeout: 30 * time.Second,
307
- // Override default error handler to always return JSON.
308
- // The catch-all middleware returns fiber.ErrNotFound so all 404s
309
- // are routed here, keeping error formatting in one place.
310
- ErrorHandler: func(c *fiber.Ctx, err error) error {
311
- code := fiber.StatusInternalServerError
312
- if e, ok := err.(*fiber.Error); ok {
313
- code = e.Code
314
- }
315
- if code == http.StatusNotFound {
316
- return apierr.NotFound(c, "route not found")
317
- }
318
- if code == http.StatusMethodNotAllowed {
319
- return apierr.MethodNotAllowed(c)
320
- }
321
- // Fallback for any unexpected error (e.g. panic-recovered 500).
322
- return apierr.InternalError(c, err)
323
- },
324
- })
325
-
326
- app.Use(recover.New())
327
- app.Use(middleware.CORS())
328
- app.Use(middleware.RequestID())
329
- app.Use(middleware.RateLimit())
330
- app.Use(middleware.Logger())
331
-
332
- // Swagger UI \u2014 /docs redirects to /docs/index.html
333
- app.Get("/docs", func(c *fiber.Ctx) error { return c.Redirect("/docs/index.html", fiber.StatusFound) })
334
- app.Get("/docs/*", fiberSwagger.WrapHandler)
335
-
336
- v1 := app.Group("/api/v1")
337
- v1.Get("/health/live", handlers.Liveness)
338
- v1.Get("/health/ready", handlers.Readiness)
339
- v1.Get("/echo/:name", handlers.EchoParams)
340
-
341
- // 404 catch-all: return fiber.ErrNotFound so it is processed by the
342
- // custom ErrorHandler above, keeping all error formatting in one place.
343
- app.Use(func(c *fiber.Ctx) error {
344
- return fiber.ErrNotFound
345
- })
346
-
347
- return app
348
- }
349
- `}function oi(){return `package handlers
350
-
351
- import (
352
- "time"
353
-
354
- "github.com/gofiber/fiber/v2"
355
- )
356
-
357
- // Liveness signals the process is alive (Kubernetes livenessProbe).
358
- //
359
- // @Summary Liveness probe
360
- // @Description Returns 200 when the process is alive.
361
- // @Tags health
362
- // @Produce json
363
- // @Success 200 {object} map[string]string
364
- // @Router /api/v1/health/live [get]
365
- func Liveness(c *fiber.Ctx) error {
366
- return c.JSON(fiber.Map{
367
- "status": "ok",
368
- "time": time.Now().UTC().Format(time.RFC3339),
369
- })
370
- }
371
-
372
- // Readiness signals the service can accept traffic (Kubernetes readinessProbe).
373
- // Extend this function to check database connectivity, caches, etc.
374
- //
375
- // @Summary Readiness probe
376
- // @Description Returns 200 when the service is ready to accept traffic.
377
- // @Tags health
378
- // @Produce json
379
- // @Success 200 {object} map[string]string
380
- // @Router /api/v1/health/ready [get]
381
- func Readiness(c *fiber.Ctx) error {
382
- return c.JSON(fiber.Map{
383
- "status": "ready",
384
- "time": time.Now().UTC().Format(time.RFC3339),
385
- })
386
- }
387
- `}function ri(e){return `package handlers_test
388
-
389
- import (
390
- "encoding/json"
391
- "io"
392
- "net/http"
393
- "net/http/httptest"
394
- "testing"
395
-
396
- "${e.module_path}/internal/config"
397
- "${e.module_path}/internal/server"
398
- )
399
-
400
- func TestLiveness(t *testing.T) {
401
- app := server.NewApp(config.Load())
402
- req := httptest.NewRequest(http.MethodGet, "/api/v1/health/live", nil)
403
- resp, err := app.Test(req, -1)
404
- if err != nil {
405
- t.Fatalf("request error: %v", err)
406
- }
407
- defer resp.Body.Close()
408
-
409
- if resp.StatusCode != http.StatusOK {
410
- data, _ := io.ReadAll(resp.Body)
411
- t.Fatalf("expected 200, got %d: %s", resp.StatusCode, data)
412
- }
413
-
414
- var body map[string]any
415
- if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
416
- t.Fatalf("invalid JSON: %v", err)
417
- }
418
- if body["status"] != "ok" {
419
- t.Fatalf("expected ok, got %v", body["status"])
420
- }
421
- }
422
-
423
- func TestReadiness(t *testing.T) {
424
- app := server.NewApp(config.Load())
425
- req := httptest.NewRequest(http.MethodGet, "/api/v1/health/ready", nil)
426
- resp, err := app.Test(req, -1)
427
- if err != nil {
428
- t.Fatalf("request error: %v", err)
429
- }
430
- defer resp.Body.Close()
431
-
432
- if resp.StatusCode != http.StatusOK {
433
- t.Fatalf("expected 200, got %d: %s", resp.StatusCode, resp.Status)
434
- }
435
- }
436
- `}function ii(){return `# \u2500\u2500 Build stage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
437
- FROM golang:1.24-alpine AS builder
438
-
439
- # Build-time version injection
440
- ARG VERSION=dev
441
- ARG COMMIT=none
442
- ARG DATE=unknown
443
-
444
- WORKDIR /app
445
- COPY go.mod go.sum ./
446
- RUN go mod download
447
-
448
- COPY . .
449
- RUN CGO_ENABLED=0 GOOS=linux go build \\
450
- -ldflags="-s -w -X main.version=$\${VERSION} -X main.commit=$\${COMMIT} -X main.date=$\${DATE}" \\
451
- -o server ./cmd/server
452
-
453
- # \u2500\u2500 Runtime stage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
454
- # alpine includes busybox wget required for the HEALTHCHECK below.
455
- FROM alpine:3.21
456
-
457
- RUN addgroup -S app && adduser -S -G app app
458
- COPY --from=builder /app/server /server
459
- USER app
460
-
461
- EXPOSE 3000
462
- HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
463
- CMD wget -qO- http://localhost:3000/api/v1/health/live || exit 1
464
- ENTRYPOINT ["/server"]
465
- `}function ni(e){return `version: "3.9"
466
-
467
- services:
468
- api:
469
- build: .
470
- container_name: ${e.project_name}
471
- ports:
472
- - "${e.port}:${e.port}"
473
- environment:
474
- PORT: "${e.port}"
475
- APP_ENV: development
476
- LOG_LEVEL: info
477
- CORS_ALLOW_ORIGINS: "*"
478
- RATE_LIMIT_RPS: "100"
479
- restart: unless-stopped
480
- `}function si(e){return `.PHONY: dev run build test cover lint fmt tidy docs docker-up docker-down
481
-
482
- # Build-time metadata
483
- VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
484
- COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "none")
485
- DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
486
- LDFLAGS = -ldflags "-s -w -X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.date=$(DATE)"
487
- # Go tool binaries are installed to GOPATH/bin; include it so \`air\` and \`swag\` are found.
488
- GOBIN ?= $(shell go env GOPATH)/bin
489
-
490
- # Hot reload \u2014 installs air on first use
491
- dev:
492
- @test -x "$(GOBIN)/air" || go install github.com/air-verse/air@latest
493
- $(GOBIN)/air
494
-
495
- run:
496
- go run $(LDFLAGS) ./cmd/server
497
-
498
- build:
499
- go build $(LDFLAGS) -o bin/${e.project_name} ./cmd/server
500
-
501
- test:
502
- go test ./... -v -race
503
-
504
- cover:
505
- go test ./... -race -coverprofile=coverage.out
506
- go tool cover -html=coverage.out -o coverage.html
507
- @echo "Coverage report: coverage.html"
508
-
509
- # Generate Swagger docs \u2014 installs swag on first use
510
- docs:
511
- @test -x "$(GOBIN)/swag" || go install github.com/swaggo/swag/cmd/swag@latest
512
- $(GOBIN)/swag init -g main.go -d cmd/server,internal/handlers,internal/apierr -o docs --parseDependency
513
- tidy:
514
- go mod tidy
515
-
516
- docker-up:
517
- go mod tidy
518
- docker compose up --build \\
519
- --build-arg VERSION=$(VERSION) \\
520
- --build-arg COMMIT=$(COMMIT) \\
521
- --build-arg DATE=$(DATE) \\
522
- -d
523
-
524
- docker-down:
525
- docker compose down
526
- `}function ai(e){return `# Application
527
- PORT=${e.port}
528
- APP_ENV=development
529
- LOG_LEVEL=debug
530
-
531
- # CORS \u2014 comma-separated list of allowed origins, or * to allow all
532
- CORS_ALLOW_ORIGINS=*
533
-
534
- # Rate limiting \u2014 max requests per IP per second
535
- RATE_LIMIT_RPS=100
536
- `}function ci(){return `# Binaries
537
- bin/
538
- *.exe
539
- *.exe~
540
- *.dll
541
- *.so
542
- *.dylib
543
-
544
- # Test binary
545
- *.test
546
-
547
- # Output of go coverage tool
548
- *.out
549
- coverage.html
550
-
551
- # Go workspace
552
- go.work
553
- go.work.sum
554
-
555
- # Environment
556
- .env
557
- .env.local
558
-
559
- # Hot reload (air)
560
- tmp/
561
-
562
- # Swagger \u2014 generated files (committed stub docs/doc.go; run \`make docs\` to regenerate)
563
- docs/swagger.json
564
- docs/swagger.yaml
565
- docs/docs.go
566
-
567
- # Editor
568
- .idea/
569
- .vscode/
570
- *.swp
571
- *.swo
572
-
573
- # OS
574
- .DS_Store
575
- Thumbs.db
576
- `}function li(e){return `name: CI
577
-
578
- on:
579
- push:
580
- branches: [main, develop]
581
- pull_request:
582
- branches: [main]
583
-
584
- jobs:
585
- test:
586
- name: Test
587
- runs-on: ubuntu-latest
588
-
589
- steps:
590
- - uses: actions/checkout@v4
591
-
592
- - name: Set up Go
593
- uses: actions/setup-go@v5
594
- with:
595
- go-version: "${e.go_version}"
596
- cache: true
597
-
598
- - name: Tidy
599
- run: go mod tidy
600
-
601
- - name: Build
602
- run: go build ./...
603
-
604
- - name: Test
605
- run: go test ./... -race -coverprofile=coverage.out
606
-
607
- - name: Upload coverage
608
- uses: actions/upload-artifact@v4
609
- with:
610
- name: coverage
611
- path: coverage.out
612
-
613
- lint:
614
- name: Lint
615
- runs-on: ubuntu-latest
616
-
617
- steps:
618
- - uses: actions/checkout@v4
619
-
620
- - name: Set up Go
621
- uses: actions/setup-go@v5
622
- with:
623
- go-version: "${e.go_version}"
624
- cache: true
625
-
626
- - name: golangci-lint
627
- uses: golangci/golangci-lint-action@v6
628
- with:
629
- version: latest
630
- `}function di(e){return `# ${bo(e.project_name)}
631
-
632
- > ${e.description}
633
-
634
- Built with [Go](https://go.dev/) + [Fiber v2](https://gofiber.io/) \xB7 Scaffolded by [RapidKit](https://getrapidkit.com)
635
-
636
- ## Quick start
637
-
638
- \`\`\`bash
639
- # Run locally (hot reload)
640
- make dev
641
-
642
- # Run tests
643
- make test
644
-
645
- # Build binary
646
- make build
647
-
648
- # Generate / refresh Swagger docs
649
- make docs
650
-
651
- # Docker
652
- make docker-up
653
- \`\`\`
654
-
655
- ## Swagger / OpenAPI
656
-
657
- After running \`make docs\`, the interactive UI is available at:
658
-
659
- \`\`\`
660
- http://localhost:${e.port}/docs
661
- \`\`\`
662
-
663
- The raw OpenAPI spec is served at \`/docs/doc.json\`.
664
-
665
- ## Endpoints
666
-
667
- | Method | Path | Description |
668
- |--------|------|-------------|
669
- | GET | /api/v1/health/live | Kubernetes livenessProbe |
670
- | GET | /api/v1/health/ready | Kubernetes readinessProbe |
671
- | GET | /api/v1/echo/:name | Example handler \u2014 remove in production |
672
- | GET | /docs/* | Swagger UI (OpenAPI docs) |
673
-
674
- ## Configuration
675
-
676
- All configuration is done through environment variables (see \`.env.example\`):
677
-
678
- | Variable | Default | Description |
679
- |----------|---------|-------------|
680
- | \`PORT\` | \`${e.port}\` | HTTP listen port |
681
- | \`APP_ENV\` | \`development\` | Application environment |
682
- | \`LOG_LEVEL\` | \`debug\` / \`info\` | \`debug\` \\| \`info\` \\| \`warn\` \\| \`error\` |
683
- | \`CORS_ALLOW_ORIGINS\` | \`*\` | Comma-separated list of allowed origins, or \`*\` |
684
- | \`RATE_LIMIT_RPS\` | \`100\` | Max requests per IP per second |
685
-
686
- ## Project structure
687
-
688
- \`\`\`
689
- ${e.project_name}/
690
- \u251C\u2500\u2500 cmd/
691
- \u2502 \u2514\u2500\u2500 server/
692
- \u2502 \u2514\u2500\u2500 main.go # Graceful shutdown + version ldflags
693
- \u251C\u2500\u2500 docs/ # Swagger generated files (\`make docs\`)
694
- \u2502 \u2514\u2500\u2500 doc.go # Package-level OpenAPI annotations
695
- \u251C\u2500\u2500 internal/
696
- \u2502 \u251C\u2500\u2500 apierr/ # Consistent JSON error envelope
697
- \u2502 \u2502 \u251C\u2500\u2500 apierr.go
698
- \u2502 \u2502 \u2514\u2500\u2500 apierr_test.go
699
- \u2502 \u251C\u2500\u2500 config/ # 12-factor configuration
700
- \u2502 \u2502 \u251C\u2500\u2500 config.go
701
- \u2502 \u2502 \u2514\u2500\u2500 config_test.go
702
- \u2502 \u251C\u2500\u2500 handlers/ # HTTP handlers + tests
703
- \u2502 \u2502 \u251C\u2500\u2500 health.go
704
- \u2502 \u2502 \u251C\u2500\u2500 health_test.go
705
- \u2502 \u2502 \u251C\u2500\u2500 example.go # EchoParams \u2014 replace with your own handlers
706
- \u2502 \u2502 \u2514\u2500\u2500 example_test.go
707
- \u2502 \u251C\u2500\u2500 middleware/
708
- \u2502 \u2502 \u251C\u2500\u2500 requestid.go # X-Request-ID + structured logger
709
- \u2502 \u2502 \u251C\u2500\u2500 requestid_test.go
710
- \u2502 \u2502 \u251C\u2500\u2500 cors.go # CORS (CORS_ALLOW_ORIGINS)
711
- \u2502 \u2502 \u251C\u2500\u2500 cors_test.go
712
- \u2502 \u2502 \u251C\u2500\u2500 ratelimit.go # Per-IP limiter (RATE_LIMIT_RPS)
713
- \u2502 \u2502 \u2514\u2500\u2500 ratelimit_test.go
714
- \u2502 \u2514\u2500\u2500 server/
715
- \u2502 \u251C\u2500\u2500 server.go
716
- \u2502 \u2514\u2500\u2500 server_test.go
717
- \u251C\u2500\u2500 .air.toml # Hot reload
718
- \u251C\u2500\u2500 .github/workflows/ci.yml # CI: test + lint
719
- \u251C\u2500\u2500 .golangci.yml
720
- \u251C\u2500\u2500 Dockerfile # Multi-stage, alpine HEALTHCHECK
721
- \u251C\u2500\u2500 docker-compose.yml
722
- \u251C\u2500\u2500 Makefile
723
- \u2514\u2500\u2500 README.md
724
- \`\`\`
725
-
726
- ## Available commands
727
-
728
- | Command | Description |
729
- |---------|-------------|
730
- | \`make dev\` | Hot reload via [air](https://github.com/air-verse/air) |
731
- | \`make run\` | Run without hot reload |
732
- | \`make build\` | Binary with version ldflags |
733
- | \`make test\` | Run tests with race detector |
734
- | \`make cover\` | HTML coverage report |
735
- | \`make docs\` | Re-generate Swagger JSON (needs \`swag\`) |
736
- | \`make lint\` | golangci-lint |
737
- | \`make fmt\` | gofmt |
738
- | \`make tidy\` | go mod tidy |
739
- | \`make docker-up\` | Build & run via Docker Compose |
740
- | \`make docker-down\` | Stop |
741
-
742
- ## License
743
-
744
- ${e.app_version} \xB7 ${e.author}
745
- `}function pi(){return `package middleware
746
-
747
- import (
748
- "crypto/rand"
749
- "encoding/hex"
750
- "log/slog"
751
- "time"
752
-
753
- "github.com/gofiber/fiber/v2"
754
- )
755
-
756
- const headerRequestID = "X-Request-ID"
757
-
758
- // RequestID injects a unique identifier into every request.
759
- // If the caller sends an X-Request-ID header it is reused; otherwise a new one
760
- // is generated and written back in the response.
761
- func RequestID() fiber.Handler {
762
- return func(c *fiber.Ctx) error {
763
- id := c.Get(headerRequestID)
764
- if id == "" {
765
- id = newID()
766
- }
767
- c.Set(headerRequestID, id)
768
- c.Locals("request_id", id)
769
- return c.Next()
770
- }
771
- }
772
-
773
- // Logger emits a structured JSON log line after each request.
774
- func Logger() fiber.Handler {
775
- return func(c *fiber.Ctx) error {
776
- start := time.Now()
777
- err := c.Next()
778
- slog.Info("http",
779
- "method", c.Method(),
780
- "path", c.Path(),
781
- "status", c.Response().StatusCode(),
782
- "bytes", c.Response().Header.ContentLength(),
783
- "latency_ms", time.Since(start).Milliseconds(),
784
- "ip", c.IP(),
785
- "request_id", c.Locals("request_id"),
786
- )
787
- return err
788
- }
789
- }
790
-
791
- func newID() string {
792
- b := make([]byte, 8)
793
- if _, err := rand.Read(b); err != nil {
794
- return "unknown"
795
- }
796
- return hex.EncodeToString(b)
797
- }
798
- `}function ui(e){return `package middleware_test
799
-
800
- import (
801
- "net/http"
802
- "net/http/httptest"
803
- "testing"
804
-
805
- "github.com/gofiber/fiber/v2"
806
-
807
- "${e.module_path}/internal/middleware"
808
- )
809
-
810
- func newTestApp() *fiber.App {
811
- app := fiber.New()
812
- app.Use(middleware.RequestID())
813
- app.Use(middleware.Logger())
814
- app.Get("/ping", func(c *fiber.Ctx) error {
815
- return c.SendString("pong")
816
- })
817
- return app
818
- }
819
-
820
- func TestRequestID_IsGenerated(t *testing.T) {
821
- req := httptest.NewRequest(http.MethodGet, "/ping", nil)
822
- resp, err := newTestApp().Test(req, -1)
823
- if err != nil {
824
- t.Fatalf("request error: %v", err)
825
- }
826
- defer resp.Body.Close()
827
-
828
- id := resp.Header.Get("X-Request-ID")
829
- if id == "" {
830
- t.Fatal("expected X-Request-ID header to be set")
831
- }
832
- if len(id) != 16 { // 8 random bytes \u2192 16 hex chars
833
- t.Fatalf("unexpected request ID length %d, want 16", len(id))
834
- }
835
- }
836
-
837
- func TestRequestID_IsReused(t *testing.T) {
838
- req := httptest.NewRequest(http.MethodGet, "/ping", nil)
839
- req.Header.Set("X-Request-ID", "my-trace-id")
840
- resp, err := newTestApp().Test(req, -1)
841
- if err != nil {
842
- t.Fatalf("request error: %v", err)
843
- }
844
- defer resp.Body.Close()
845
-
846
- id := resp.Header.Get("X-Request-ID")
847
- if id != "my-trace-id" {
848
- t.Fatalf("expected X-Request-ID to be reused, got %q", id)
849
- }
850
- }
851
- `}function gi(){return `// Package apierr provides a consistent JSON error envelope for all API responses.
852
- //
853
- // Every error response looks like:
854
- //
855
- // {"error": "user not found", "code": "NOT_FOUND", "request_id": "a1b2c3d4..."}
856
- package apierr
857
-
858
- import (
859
- "net/http"
860
-
861
- "github.com/gofiber/fiber/v2"
862
- )
863
-
864
- // Response is the standard error envelope returned by all API endpoints.
865
- type Response struct {
866
- Error string \`json:"error"\`
867
- Code string \`json:"code"\`
868
- RequestID string \`json:"request_id,omitempty"\`
869
- }
870
-
871
- func reply(c *fiber.Ctx, status int, msg, code string) error {
872
- rid, _ := c.Locals("request_id").(string)
873
- return c.Status(status).JSON(Response{
874
- Error: msg,
875
- Code: code,
876
- RequestID: rid,
877
- })
878
- }
879
-
880
- // BadRequest responds with 400 and code "BAD_REQUEST".
881
- func BadRequest(c *fiber.Ctx, msg string) error {
882
- return reply(c, http.StatusBadRequest, msg, "BAD_REQUEST")
883
- }
884
-
885
- // NotFound responds with 404 and code "NOT_FOUND".
886
- func NotFound(c *fiber.Ctx, msg string) error {
887
- return reply(c, http.StatusNotFound, msg, "NOT_FOUND")
888
- }
889
-
890
- // Unauthorized responds with 401 and code "UNAUTHORIZED".
891
- func Unauthorized(c *fiber.Ctx) error {
892
- return reply(c, http.StatusUnauthorized, "authentication required", "UNAUTHORIZED")
893
- }
894
-
895
- // Forbidden responds with 403 and code "FORBIDDEN".
896
- func Forbidden(c *fiber.Ctx) error {
897
- return reply(c, http.StatusForbidden, "access denied", "FORBIDDEN")
898
- }
899
-
900
- // MethodNotAllowed responds with 405 and code "METHOD_NOT_ALLOWED".
901
- func MethodNotAllowed(c *fiber.Ctx) error {
902
- return reply(c, http.StatusMethodNotAllowed, "method not allowed", "METHOD_NOT_ALLOWED")
903
- }
904
-
905
- // InternalError responds with 500 and code "INTERNAL_ERROR".
906
- // The original error is intentionally not exposed to the client.
907
- func InternalError(c *fiber.Ctx, _ error) error {
908
- return reply(c, http.StatusInternalServerError, "an internal error occurred", "INTERNAL_ERROR")
909
- }
910
-
911
- // TooManyRequests responds with 429 and code "TOO_MANY_REQUESTS".
912
- func TooManyRequests(c *fiber.Ctx, msg string) error {
913
- return reply(c, http.StatusTooManyRequests, msg, "TOO_MANY_REQUESTS")
914
- }
915
- `}function mi(e){return `package apierr_test
916
-
917
- import (
918
- "encoding/json"
919
- "io"
920
- "net/http"
921
- "net/http/httptest"
922
- "testing"
923
-
924
- "github.com/gofiber/fiber/v2"
925
-
926
- "${e.module_path}/internal/apierr"
927
- )
928
-
929
- func makeApp(fn func(*fiber.Ctx) error) *fiber.App {
930
- app := fiber.New()
931
- app.Get("/test", fn)
932
- return app
933
- }
934
-
935
- func readJSON(t *testing.T, r io.Reader) apierr.Response {
936
- t.Helper()
937
- var out apierr.Response
938
- if err := json.NewDecoder(r).Decode(&out); err != nil {
939
- t.Fatalf("invalid JSON: %v", err)
940
- }
941
- return out
942
- }
943
-
944
- func TestBadRequest(t *testing.T) {
945
- app := makeApp(func(c *fiber.Ctx) error { return apierr.BadRequest(c, "invalid email") })
946
- req := httptest.NewRequest(http.MethodGet, "/test", nil)
947
- resp, _ := app.Test(req, -1)
948
- defer resp.Body.Close()
949
-
950
- if resp.StatusCode != http.StatusBadRequest {
951
- t.Fatalf("expected 400, got %d", resp.StatusCode)
952
- }
953
- body := readJSON(t, resp.Body)
954
- if body.Code != "BAD_REQUEST" {
955
- t.Fatalf("expected BAD_REQUEST, got %q", body.Code)
956
- }
957
- if body.Error != "invalid email" {
958
- t.Fatalf("unexpected error message: %q", body.Error)
959
- }
960
- }
961
-
962
- func TestNotFound(t *testing.T) {
963
- app := makeApp(func(c *fiber.Ctx) error { return apierr.NotFound(c, "user not found") })
964
- req := httptest.NewRequest(http.MethodGet, "/test", nil)
965
- resp, _ := app.Test(req, -1)
966
- defer resp.Body.Close()
967
-
968
- if resp.StatusCode != http.StatusNotFound {
969
- t.Fatalf("expected 404, got %d", resp.StatusCode)
970
- }
971
- body := readJSON(t, resp.Body)
972
- if body.Code != "NOT_FOUND" {
973
- t.Fatalf("expected NOT_FOUND, got %q", body.Code)
974
- }
975
- }
976
-
977
- func TestUnauthorized(t *testing.T) {
978
- app := makeApp(func(c *fiber.Ctx) error { return apierr.Unauthorized(c) })
979
- req := httptest.NewRequest(http.MethodGet, "/test", nil)
980
- resp, _ := app.Test(req, -1)
981
- defer resp.Body.Close()
982
-
983
- if resp.StatusCode != http.StatusUnauthorized {
984
- t.Fatalf("expected 401, got %d", resp.StatusCode)
985
- }
986
- }
987
-
988
- func TestForbidden(t *testing.T) {
989
- app := makeApp(func(c *fiber.Ctx) error { return apierr.Forbidden(c) })
990
- req := httptest.NewRequest(http.MethodGet, "/test", nil)
991
- resp, _ := app.Test(req, -1)
992
- defer resp.Body.Close()
993
-
994
- if resp.StatusCode != http.StatusForbidden {
995
- t.Fatalf("expected 403, got %d", resp.StatusCode)
996
- }
997
- body := readJSON(t, resp.Body)
998
- if body.Code != "FORBIDDEN" {
999
- t.Fatalf("expected FORBIDDEN, got %q", body.Code)
1000
- }
1001
- }
1002
-
1003
- func TestMethodNotAllowed(t *testing.T) {
1004
- app := makeApp(func(c *fiber.Ctx) error { return apierr.MethodNotAllowed(c) })
1005
- req := httptest.NewRequest(http.MethodGet, "/test", nil)
1006
- resp, _ := app.Test(req, -1)
1007
- defer resp.Body.Close()
1008
-
1009
- if resp.StatusCode != http.StatusMethodNotAllowed {
1010
- t.Fatalf("expected 405, got %d", resp.StatusCode)
1011
- }
1012
- body := readJSON(t, resp.Body)
1013
- if body.Code != "METHOD_NOT_ALLOWED" {
1014
- t.Fatalf("expected METHOD_NOT_ALLOWED, got %q", body.Code)
1015
- }
1016
- }
1017
-
1018
- func TestInternalError(t *testing.T) {
1019
- app := makeApp(func(c *fiber.Ctx) error { return apierr.InternalError(c, nil) })
1020
- req := httptest.NewRequest(http.MethodGet, "/test", nil)
1021
- resp, _ := app.Test(req, -1)
1022
- defer resp.Body.Close()
1023
-
1024
- if resp.StatusCode != http.StatusInternalServerError {
1025
- t.Fatalf("expected 500, got %d", resp.StatusCode)
1026
- }
1027
- body := readJSON(t, resp.Body)
1028
- if body.Code != "INTERNAL_ERROR" {
1029
- t.Fatalf("expected INTERNAL_ERROR, got %q", body.Code)
1030
- }
1031
- }
1032
-
1033
- func TestTooManyRequests(t *testing.T) {
1034
- app := makeApp(func(c *fiber.Ctx) error { return apierr.TooManyRequests(c, "slow down") })
1035
- req := httptest.NewRequest(http.MethodGet, "/test", nil)
1036
- resp, _ := app.Test(req, -1)
1037
- defer resp.Body.Close()
1038
-
1039
- if resp.StatusCode != http.StatusTooManyRequests {
1040
- t.Fatalf("expected 429, got %d", resp.StatusCode)
1041
- }
1042
- body := readJSON(t, resp.Body)
1043
- if body.Code != "TOO_MANY_REQUESTS" {
1044
- t.Fatalf("expected TOO_MANY_REQUESTS, got %q", body.Code)
1045
- }
1046
- }
1047
- `}function fi(e){return `// Package docs provides the swaggo-generated OpenAPI specification.
1048
- //
1049
- // Run \`make docs\` to regenerate after changing handler annotations.
1050
- //
1051
- // @title ${bo(e.project_name)} API
1052
- // @version ${e.app_version}
1053
- // @description ${e.description}
1054
- // @host localhost:${e.port}
1055
- // @BasePath /
1056
- // @schemes http https
1057
- //
1058
- // @contact.name ${e.author}
1059
- // @license.name MIT
1060
- package docs
1061
- `}function hi(e){return `package handlers
1062
-
1063
- import (
1064
- "net/http"
1065
-
1066
- "github.com/gofiber/fiber/v2"
1067
-
1068
- "${e.module_path}/internal/apierr"
1069
- )
1070
-
1071
- // EchoResponse is the JSON body returned by EchoParams.
1072
- type EchoResponse struct {
1073
- Name string \`json:"name"\`
1074
- RequestID string \`json:"request_id"\`
1075
- }
1076
-
1077
- // EchoParams is an example handler demonstrating how to:
1078
- // - read URL path parameters
1079
- // - use apierr for consistent JSON error responses
1080
- // - access the request ID injected by RequestID middleware
1081
- //
1082
- // Replace or remove this file once you add your own business logic.
1083
- //
1084
- // @Summary Echo path parameter
1085
- // @Description Returns the :name path parameter together with the request ID.
1086
- // @Tags example
1087
- // @Produce json
1088
- // @Param name path string true "Name to echo"
1089
- // @Success 200 {object} handlers.EchoResponse
1090
- // @Failure 400 {object} apierr.Response
1091
- // @Router /api/v1/echo/{name} [get]
1092
- func EchoParams(c *fiber.Ctx) error {
1093
- name := c.Params("name")
1094
- if name == "" {
1095
- return apierr.BadRequest(c, "name parameter is required")
1096
- }
1097
- rid, _ := c.Locals("request_id").(string)
1098
- return c.Status(http.StatusOK).JSON(EchoResponse{
1099
- Name: name,
1100
- RequestID: rid,
1101
- })
1102
- }
1103
- `}function yi(e){return `package handlers_test
1104
-
1105
- import (
1106
- "encoding/json"
1107
- "net/http"
1108
- "net/http/httptest"
1109
- "testing"
1110
-
1111
- "github.com/gofiber/fiber/v2"
1112
-
1113
- "${e.module_path}/internal/handlers"
1114
- "${e.module_path}/internal/middleware"
1115
- )
1116
-
1117
- func newEchoApp() *fiber.App {
1118
- app := fiber.New()
1119
- app.Use(middleware.RequestID())
1120
- app.Get("/echo/:name", handlers.EchoParams)
1121
- return app
1122
- }
1123
-
1124
- func TestEchoParams_Success(t *testing.T) {
1125
- req := httptest.NewRequest(http.MethodGet, "/echo/alice", nil)
1126
- resp, err := newEchoApp().Test(req, -1)
1127
- if err != nil {
1128
- t.Fatalf("request error: %v", err)
1129
- }
1130
- defer resp.Body.Close()
1131
-
1132
- if resp.StatusCode != http.StatusOK {
1133
- t.Fatalf("expected 200, got %d", resp.StatusCode)
1134
- }
1135
-
1136
- var body map[string]any
1137
- if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
1138
- t.Fatalf("invalid JSON: %v", err)
1139
- }
1140
- if body["name"] != "alice" {
1141
- t.Fatalf("expected name=alice, got %v", body["name"])
1142
- }
1143
- if body["request_id"] == nil || body["request_id"] == "" {
1144
- t.Fatal("expected request_id to be set by RequestID middleware")
1145
- }
1146
- }
1147
-
1148
- // TestEchoParams_EmptyName registers EchoParams on a param-free route so that
1149
- // c.Params("name") returns "" and the 400 guard executes.
1150
- func TestEchoParams_EmptyName(t *testing.T) {
1151
- app := fiber.New()
1152
- app.Get("/echo-bare", handlers.EchoParams)
1153
- req := httptest.NewRequest(http.MethodGet, "/echo-bare", nil)
1154
- resp, err := app.Test(req, -1)
1155
- if err != nil {
1156
- t.Fatalf("request error: %v", err)
1157
- }
1158
- defer resp.Body.Close()
1159
-
1160
- if resp.StatusCode != http.StatusBadRequest {
1161
- t.Fatalf("expected 400, got %d", resp.StatusCode)
1162
- }
1163
- var body map[string]any
1164
- if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
1165
- t.Fatalf("invalid JSON: %v", err)
1166
- }
1167
- if body["code"] != "BAD_REQUEST" {
1168
- t.Fatalf("expected code=BAD_REQUEST, got %v", body["code"])
1169
- }
1170
- }
1171
- `}function wi(e){return `package config_test
1172
-
1173
- import (
1174
- "log/slog"
1175
- "testing"
1176
-
1177
- "${e.module_path}/internal/config"
1178
- )
1179
-
1180
- func TestParseLogLevel(t *testing.T) {
1181
- tests := []struct {
1182
- input string
1183
- want slog.Level
1184
- }{
1185
- {"debug", slog.LevelDebug},
1186
- {"DEBUG", slog.LevelDebug},
1187
- {"warn", slog.LevelWarn},
1188
- {"warning", slog.LevelWarn},
1189
- {"error", slog.LevelError},
1190
- {"info", slog.LevelInfo},
1191
- {"", slog.LevelInfo},
1192
- {"unknown", slog.LevelInfo},
1193
- }
1194
- for _, tc := range tests {
1195
- got := config.ParseLogLevel(tc.input)
1196
- if got != tc.want {
1197
- t.Errorf("ParseLogLevel(%q) = %v, want %v", tc.input, got, tc.want)
1198
- }
1199
- }
1200
- }
1201
-
1202
- func TestLoad_EnvOverride(t *testing.T) {
1203
- t.Setenv("PORT", "9090")
1204
- t.Setenv("APP_ENV", "production")
1205
- t.Setenv("LOG_LEVEL", "warn")
1206
-
1207
- cfg := config.Load()
1208
-
1209
- if cfg.Port != "9090" {
1210
- t.Errorf("expected Port=9090, got %q", cfg.Port)
1211
- }
1212
- if cfg.Env != "production" {
1213
- t.Errorf("expected Env=production, got %q", cfg.Env)
1214
- }
1215
- if cfg.LogLevel != "warn" {
1216
- t.Errorf("expected LogLevel=warn, got %q", cfg.LogLevel)
1217
- }
1218
- }
1219
-
1220
- func TestLoad_Defaults(t *testing.T) {
1221
- // Empty string forces getEnv() to return the built-in fallback value.
1222
- t.Setenv("PORT", "")
1223
- t.Setenv("APP_ENV", "")
1224
- t.Setenv("LOG_LEVEL", "")
1225
-
1226
- cfg := config.Load()
1227
-
1228
- if cfg.Port != "${e.port}" {
1229
- t.Errorf("expected default Port=${e.port}, got %q", cfg.Port)
1230
- }
1231
- if cfg.Env != "development" {
1232
- t.Errorf("expected default Env=development, got %q", cfg.Env)
1233
- }
1234
- // APP_ENV="" \u2192 fallback "development" \u2192 defaultLogLevel \u2192 "debug"
1235
- if cfg.LogLevel != "debug" {
1236
- t.Errorf("expected default LogLevel=debug (development env), got %q", cfg.LogLevel)
1237
- }
1238
- }
1239
- `}function vi(){return `package middleware
1240
-
1241
- import (
1242
- "os"
1243
-
1244
- "github.com/gofiber/fiber/v2"
1245
- "github.com/gofiber/fiber/v2/middleware/cors"
1246
- )
1247
-
1248
- // CORS returns a CORS middleware configured via CORS_ALLOW_ORIGINS env var.
1249
- //
1250
- // Set CORS_ALLOW_ORIGINS="*" for development (the default when unset).
1251
- // In production supply a comma-separated list of allowed origins:
1252
- //
1253
- // CORS_ALLOW_ORIGINS=https://app.example.com,https://admin.example.com
1254
- func CORS() fiber.Handler {
1255
- origins := os.Getenv("CORS_ALLOW_ORIGINS")
1256
- if origins == "" {
1257
- origins = "*"
1258
- }
1259
- return cors.New(cors.Config{
1260
- AllowOrigins: origins,
1261
- AllowMethods: "GET,POST,PUT,PATCH,DELETE,OPTIONS",
1262
- AllowHeaders: "Origin,Content-Type,Authorization,X-Request-ID",
1263
- ExposeHeaders: "X-Request-ID",
1264
- MaxAge: 600,
1265
- })
1266
- }
1267
- `}function ki(e){return `package middleware_test
1268
-
1269
- import (
1270
- "net/http"
1271
- "net/http/httptest"
1272
- "testing"
1273
-
1274
- "github.com/gofiber/fiber/v2"
1275
-
1276
- "${e.module_path}/internal/middleware"
1277
- )
1278
-
1279
- func newCORSApp(t *testing.T) *fiber.App {
1280
- t.Helper()
1281
- app := fiber.New()
1282
- app.Use(middleware.CORS())
1283
- app.Get("/ping", func(c *fiber.Ctx) error { return c.SendStatus(http.StatusOK) })
1284
- return app
1285
- }
1286
-
1287
- func TestCORS_Wildcard(t *testing.T) {
1288
- t.Setenv("CORS_ALLOW_ORIGINS", "*")
1289
- req := httptest.NewRequest(http.MethodGet, "/ping", nil)
1290
- req.Header.Set("Origin", "https://example.com")
1291
- resp, _ := newCORSApp(t).Test(req, -1)
1292
- defer resp.Body.Close()
1293
-
1294
- if got := resp.Header.Get("Access-Control-Allow-Origin"); got != "*" {
1295
- t.Fatalf("expected ACAO=*, got %q", got)
1296
- }
1297
- }
1298
-
1299
- func TestCORS_Preflight(t *testing.T) {
1300
- t.Setenv("CORS_ALLOW_ORIGINS", "*")
1301
- app := fiber.New()
1302
- app.Use(middleware.CORS())
1303
-
1304
- req := httptest.NewRequest(http.MethodOptions, "/ping", nil)
1305
- req.Header.Set("Origin", "https://example.com")
1306
- req.Header.Set("Access-Control-Request-Method", "POST")
1307
- resp, _ := app.Test(req, -1)
1308
- defer resp.Body.Close()
1309
-
1310
- if resp.StatusCode != http.StatusNoContent {
1311
- t.Fatalf("expected 204 preflight, got %d", resp.StatusCode)
1312
- }
1313
- }
1314
-
1315
- func TestCORS_SpecificOrigin_Allowed(t *testing.T) {
1316
- t.Setenv("CORS_ALLOW_ORIGINS", "https://app.example.com")
1317
- req := httptest.NewRequest(http.MethodGet, "/ping", nil)
1318
- req.Header.Set("Origin", "https://app.example.com")
1319
- resp, _ := newCORSApp(t).Test(req, -1)
1320
- defer resp.Body.Close()
1321
-
1322
- if got := resp.Header.Get("Access-Control-Allow-Origin"); got != "https://app.example.com" {
1323
- t.Fatalf("expected ACAO=https://app.example.com, got %q", got)
1324
- }
1325
- }
1326
-
1327
- func TestCORS_Default_Origin(t *testing.T) {
1328
- // When CORS_ALLOW_ORIGINS is unset, middleware must default to "*".
1329
- t.Setenv("CORS_ALLOW_ORIGINS", "")
1330
- req := httptest.NewRequest(http.MethodGet, "/ping", nil)
1331
- req.Header.Set("Origin", "https://anywhere.com")
1332
- resp, _ := newCORSApp(t).Test(req, -1)
1333
- defer resp.Body.Close()
1334
-
1335
- if got := resp.Header.Get("Access-Control-Allow-Origin"); got == "" {
1336
- t.Fatal("expected CORS header when origins defaulting to *")
1337
- }
1338
- }
1339
- `}function bi(e){return `package server_test
1340
-
1341
- import (
1342
- "encoding/json"
1343
- "net/http"
1344
- "net/http/httptest"
1345
- "testing"
1346
-
1347
- "${e.module_path}/internal/config"
1348
- "${e.module_path}/internal/server"
1349
- )
1350
-
1351
- type serverAPIError struct {
1352
- Code string \`json:"code"\`
1353
- Message string \`json:"message"\`
1354
- }
1355
-
1356
- func TestServer_NotFound_JSON(t *testing.T) {
1357
- req := httptest.NewRequest(http.MethodGet, "/no-such-route", nil)
1358
- resp, err := server.NewApp(config.Load()).Test(req, -1)
1359
- if err != nil {
1360
- t.Fatalf("request error: %v", err)
1361
- }
1362
- defer resp.Body.Close()
1363
-
1364
- if resp.StatusCode != http.StatusNotFound {
1365
- t.Fatalf("expected 404, got %d", resp.StatusCode)
1366
- }
1367
- var body serverAPIError
1368
- if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
1369
- t.Fatalf("expected JSON error body: %v", err)
1370
- }
1371
- if body.Code != "NOT_FOUND" {
1372
- t.Fatalf("expected code=NOT_FOUND, got %q", body.Code)
1373
- }
1374
- }
1375
-
1376
- func TestServer_MethodNotAllowed_JSON(t *testing.T) {
1377
- // Fiber v2 does not return 405 automatically \u2014 unmatched methods fall
1378
- // through to the 404 catch-all, which is the expected behaviour.
1379
- req := httptest.NewRequest(http.MethodPost, "/api/v1/health/live", nil)
1380
- resp, err := server.NewApp(config.Load()).Test(req, -1)
1381
- if err != nil {
1382
- t.Fatalf("request error: %v", err)
1383
- }
1384
- defer resp.Body.Close()
1385
-
1386
- if resp.StatusCode != http.StatusNotFound {
1387
- t.Fatalf("expected 404 for unmatched method, got %d", resp.StatusCode)
1388
- }
1389
- var body serverAPIError
1390
- if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
1391
- t.Fatalf("expected JSON error body: %v", err)
1392
- }
1393
- if body.Code != "NOT_FOUND" {
1394
- t.Fatalf("expected code=NOT_FOUND, got %q", body.Code)
1395
- }
1396
- }
1397
-
1398
- func TestServer_CORS_Header(t *testing.T) {
1399
- t.Setenv("CORS_ALLOW_ORIGINS", "*")
1400
- req := httptest.NewRequest(http.MethodGet, "/api/v1/health/live", nil)
1401
- req.Header.Set("Origin", "https://example.com")
1402
- resp, err := server.NewApp(config.Load()).Test(req, -1)
1403
- if err != nil {
1404
- t.Fatalf("request error: %v", err)
1405
- }
1406
- defer resp.Body.Close()
1407
-
1408
- if got := resp.Header.Get("Access-Control-Allow-Origin"); got == "" {
1409
- t.Fatal("expected Access-Control-Allow-Origin header to be set")
1410
- }
1411
- }
1412
-
1413
- func TestServer_Docs_Redirect(t *testing.T) {
1414
- req := httptest.NewRequest(http.MethodGet, "/docs", nil)
1415
- resp, err := server.NewApp(config.Load()).Test(req, -1)
1416
- if err != nil {
1417
- t.Fatalf("request error: %v", err)
1418
- }
1419
- defer resp.Body.Close()
1420
-
1421
- if resp.StatusCode != http.StatusFound {
1422
- t.Fatalf("expected 302 redirect from /docs, got %d", resp.StatusCode)
1423
- }
1424
- if loc := resp.Header.Get("Location"); loc != "/docs/index.html" {
1425
- t.Fatalf("expected Location=/docs/index.html, got %q", loc)
1426
- }
1427
- }
1428
- `}function Ri(e){return `package middleware
1429
-
1430
- import (
1431
- "os"
1432
- "strconv"
1433
- "time"
1434
-
1435
- "github.com/gofiber/fiber/v2"
1436
- "github.com/gofiber/fiber/v2/middleware/limiter"
1437
-
1438
- "${e.module_path}/internal/apierr"
1439
- )
1440
-
1441
- // RateLimit returns a per-IP sliding-window rate limiter.
1442
- // Configure the limit via RATE_LIMIT_RPS env var (requests per second, default 100).
1443
- func RateLimit() fiber.Handler {
1444
- rps := 100
1445
- if raw := os.Getenv("RATE_LIMIT_RPS"); raw != "" {
1446
- if n, err := strconv.Atoi(raw); err == nil && n > 0 {
1447
- rps = n
1448
- }
1449
- }
1450
- return limiter.New(limiter.Config{
1451
- Max: rps,
1452
- Expiration: time.Second,
1453
- KeyGenerator: func(c *fiber.Ctx) string {
1454
- return c.IP()
1455
- },
1456
- LimitReached: func(c *fiber.Ctx) error {
1457
- return apierr.TooManyRequests(c, "rate limit exceeded")
1458
- },
1459
- })
1460
- }
1461
- `}function _i(e){return `package middleware_test
1462
-
1463
- import (
1464
- "net/http"
1465
- "net/http/httptest"
1466
- "testing"
1467
-
1468
- "github.com/gofiber/fiber/v2"
1469
-
1470
- "${e.module_path}/internal/middleware"
1471
- )
1472
-
1473
- func newRateLimitApp(t *testing.T) *fiber.App {
1474
- t.Helper()
1475
- app := fiber.New()
1476
- app.Use(middleware.RateLimit())
1477
- app.Get("/", func(c *fiber.Ctx) error { return c.SendStatus(http.StatusOK) })
1478
- return app
1479
- }
1480
-
1481
- func TestRateLimit_AllowsUnderLimit(t *testing.T) {
1482
- t.Setenv("RATE_LIMIT_RPS", "3")
1483
- app := newRateLimitApp(t)
1484
-
1485
- for i := 0; i < 3; i++ {
1486
- req := httptest.NewRequest(http.MethodGet, "/", nil)
1487
- resp, err := app.Test(req, -1)
1488
- if err != nil {
1489
- t.Fatalf("request %d: %v", i+1, err)
1490
- }
1491
- resp.Body.Close()
1492
- if resp.StatusCode != http.StatusOK {
1493
- t.Fatalf("request %d: expected 200, got %d", i+1, resp.StatusCode)
1494
- }
1495
- }
1496
- }
1497
-
1498
- func TestRateLimit_Blocks_After_Limit(t *testing.T) {
1499
- t.Setenv("RATE_LIMIT_RPS", "2")
1500
- app := newRateLimitApp(t)
1501
-
1502
- // Exhaust the limit.
1503
- for i := 0; i < 2; i++ {
1504
- req := httptest.NewRequest(http.MethodGet, "/", nil)
1505
- resp, _ := app.Test(req, -1)
1506
- resp.Body.Close()
1507
- }
1508
-
1509
- // Next request must be rejected.
1510
- req := httptest.NewRequest(http.MethodGet, "/", nil)
1511
- resp, err := app.Test(req, -1)
1512
- if err != nil {
1513
- t.Fatalf("over-limit request: %v", err)
1514
- }
1515
- defer resp.Body.Close()
1516
-
1517
- if resp.StatusCode != http.StatusTooManyRequests {
1518
- t.Fatalf("expected 429, got %d", resp.StatusCode)
1519
- }
1520
- }
1521
-
1522
- func TestRateLimit_InvalidRPS(t *testing.T) {
1523
- // Invalid value should fall back to default (100 rps) and allow normal requests.
1524
- t.Setenv("RATE_LIMIT_RPS", "not-a-number")
1525
- app := newRateLimitApp(t)
1526
- req := httptest.NewRequest(http.MethodGet, "/", nil)
1527
- resp, err := app.Test(req, -1)
1528
- if err != nil {
1529
- t.Fatalf("request error: %v", err)
1530
- }
1531
- defer resp.Body.Close()
1532
-
1533
- if resp.StatusCode != http.StatusOK {
1534
- t.Fatalf("expected 200 with invalid RPS env, got %d", resp.StatusCode)
1535
- }
1536
- }
1537
- `}function Ci(e){return `# Air \u2014 live reload for Go projects
1538
- # https://github.com/air-verse/air
1539
- root = "."
1540
- tmp_dir = "tmp"
1541
-
1542
- [build]
1543
- pre_cmd = ["$(go env GOPATH)/bin/swag init -g main.go -d cmd/server,internal/handlers,internal/apierr -o docs --parseDependency 2>/dev/null || true"]
1544
- cmd = "go build -o ./tmp/server ./cmd/server"
1545
- bin = "./tmp/server"
1546
- include_ext = ["go", "yaml", "yml", "env"]
1547
- exclude_dir = ["tmp", "vendor", ".git", "testdata", "docs"]
1548
- delay = 500
1549
- rerun_delay = 500
1550
- send_interrupt = true
1551
- kill_delay = "200ms"
1552
-
1553
- [env]
1554
- PORT = "${e.port}"
1555
-
1556
- [misc]
1557
- clean_on_exit = true
1558
-
1559
- [log]
1560
- time = false
1561
- `}function Si(e){return `run:
1562
- timeout: 5m
1563
-
1564
- linters:
1565
- enable:
1566
- - bodyclose
1567
- - durationcheck
1568
- - errcheck
1569
- - errname
1570
- - errorlint
1571
- - gci
1572
- - goimports
1573
- - gosimple
1574
- - govet
1575
- - ineffassign
1576
- - misspell
1577
- - noctx
1578
- - nolintlint
1579
- - prealloc
1580
- - staticcheck
1581
- - unconvert
1582
- - unused
1583
- - wrapcheck
1584
-
1585
- linters-settings:
1586
- gci:
1587
- sections:
1588
- - standard
1589
- - default
1590
- - prefix(${e})
1591
- goimports:
1592
- local-prefixes: "${e}"
1593
- govet:
1594
- enable:
1595
- - shadow
1596
- wrapcheck:
1597
- ignorePackageGlobs:
1598
- - "${e}/*"
1599
-
1600
- issues:
1601
- max-same-issues: 5
1602
- exclude-rules:
1603
- - path: _test.go
1604
- linters:
1605
- - errcheck
1606
- - wrapcheck
1607
- `}function xi(){return JSON.stringify({engine:"npm",runtime:"go"},null,2)}function Pi(e){return `#!/usr/bin/env sh
1608
- # RapidKit Go/Fiber project launcher \u2014 generated by RapidKit CLI
1609
- # https://getrapidkit.com
1610
-
1611
- SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
1612
- CMD="\${1:-}"
1613
- shift 2>/dev/null || true
1614
-
1615
- case "$CMD" in
1616
- init)
1617
- cd "$SCRIPT_DIR"
1618
- echo "\u{1F439} Initializing Go/Fiber project\u2026"
1619
- GOBIN="$(go env GOPATH)/bin"
1620
- echo " \u2192 installing air (hot reload)\u2026"
1621
- go install github.com/air-verse/air@latest 2>/dev/null && echo " \u2713 air" || echo " \u26A0 air install failed (run: go install github.com/air-verse/air@latest)"
1622
- echo " \u2192 installing swag (swagger)\u2026"
1623
- go install github.com/swaggo/swag/cmd/swag@latest 2>/dev/null && echo " \u2713 swag" || echo " \u26A0 swag install failed (run: go install github.com/swaggo/swag/cmd/swag@latest)"
1624
- if [ ! -f ".env" ] && [ -f ".env.example" ]; then
1625
- cp .env.example .env && echo " \u2713 .env created from .env.example"
1626
- fi
1627
- go mod tidy && echo " \u2713 go mod tidy"
1628
- echo " \u2192 generating swagger docs (first build)\u2026"
1629
- "$(go env GOPATH)/bin/swag" init -g main.go -d cmd/server,internal/handlers,internal/apierr -o docs --parseDependency 2>/dev/null && echo " \u2713 swagger docs generated" || echo " \u26A0 swagger docs skipped (run: rapidkit docs)"
1630
- echo "\u2705 Ready \u2014 run: rapidkit dev"
1631
- ;;
1632
- dev)
1633
- cd "$SCRIPT_DIR"
1634
- echo "\u{1F4D6} Syncing swagger docs\u2026"
1635
- "$(go env GOPATH)/bin/swag" init -g main.go -d cmd/server,internal/handlers,internal/apierr -o docs --parseDependency 2>/dev/null || true
1636
- if [ -f "$SCRIPT_DIR/Makefile" ]; then
1637
- exec make -C "$SCRIPT_DIR" dev "$@"
1638
- else
1639
- exec go run ./cmd/server "$@"
1640
- fi
1641
- ;;
1642
- start)
1643
- BIN="$SCRIPT_DIR/bin/${e.project_name}"
1644
- if [ ! -f "$BIN" ]; then
1645
- make -C "$SCRIPT_DIR" build
1646
- fi
1647
- exec "$BIN" "$@"
1648
- ;;
1649
- build)
1650
- exec make -C "$SCRIPT_DIR" build "$@"
1651
- ;;
1652
- test)
1653
- exec make -C "$SCRIPT_DIR" test "$@"
1654
- ;;
1655
- lint)
1656
- exec make -C "$SCRIPT_DIR" lint "$@"
1657
- ;;
1658
- format|fmt)
1659
- exec make -C "$SCRIPT_DIR" fmt "$@"
1660
- ;;
1661
- docs)
1662
- exec make -C "$SCRIPT_DIR" docs "$@"
1663
- ;;
1664
- help|--help|-h)
1665
- echo "RapidKit \u2014 Go/Fiber project: ${e.project_name}"
1666
- echo ""
1667
- echo "Usage: rapidkit <command>"
1668
- echo ""
1669
- echo " init Install tools + create .env (air, swag, go mod tidy)"
1670
- echo " dev Hot reload dev server (make dev \u2014 requires air)"
1671
- echo " start Run compiled binary (make build + bin)"
1672
- echo " build Build binary (make build)"
1673
- echo " docs Generate Swagger docs (make docs \u2014 requires swag)"
1674
- echo " test Run tests (make test)"
1675
- echo " lint Run linter (make lint)"
1676
- echo " format Format code (make fmt)"
1677
- ;;
1678
- *)
1679
- if [ -n "$CMD" ]; then
1680
- echo "rapidkit: unknown command: $CMD" >&2
1681
- fi
1682
- echo "Available: init, dev, start, build, docs, test, lint, format" >&2
1683
- exit 1
1684
- ;;
1685
- esac
1686
- `}function Ei(e){return `@echo off
1687
- rem RapidKit Go/Fiber project launcher \u2014 Windows
1688
- set CMD=%1
1689
- if "%CMD%"=="" goto usage
1690
- shift
1691
-
1692
- if "%CMD%"=="init" (
1693
- echo Initializing Go/Fiber project...
1694
- go install github.com/air-verse/air@latest
1695
- go install github.com/swaggo/swag/cmd/swag@latest
1696
- if not exist .env if exist .env.example copy .env.example .env
1697
- go mod tidy
1698
- exit /b %ERRORLEVEL%
1699
- )
1700
- if "%CMD%"=="dev" ( make dev %* & exit /b %ERRORLEVEL% )
1701
- if "%CMD%"=="build" ( make build %* & exit /b %ERRORLEVEL% )
1702
- if "%CMD%"=="test" ( make test %* & exit /b %ERRORLEVEL% )
1703
- if "%CMD%"=="lint" ( make lint %* & exit /b %ERRORLEVEL% )
1704
- if "%CMD%"=="format" ( make fmt %* & exit /b %ERRORLEVEL% )
1705
- if "%CMD%"=="docs" ( make docs %* & exit /b %ERRORLEVEL% )
1706
- if "%CMD%"=="start" ( bin\\${e.project_name}.exe %* & exit /b %ERRORLEVEL% )
1707
-
1708
- :usage
1709
- echo Available: init, dev, start, build, docs, test, lint, format
1710
- exit /b 1
1711
- `}function Ii(e,o){return JSON.stringify({kit_name:"gofiber.standard",runtime:"go",module_support:false,project_name:e.project_name,module_path:e.module_path,app_version:e.app_version,created_by:"rapidkit-npm",rapidkit_version:o,created_at:new Date().toISOString()},null,2)}async function Vt(e,o){let t={project_name:o.project_name,module_path:o.module_path||o.project_name,author:o.author||"RapidKit User",description:o.description||`Go/Fiber REST API \u2014 ${o.project_name}`,go_version:o.go_version||"1.24",app_version:o.app_version||"0.1.0",port:o.port||"3000",skipGit:o.skipGit??false},r=c();try{await execa("go",["version"],{timeout:3e3});}catch{console.log(l.yellow("\n\u26A0 Go not found in PATH \u2014 project will be scaffolded, but `go mod tidy` requires Go 1.21+")),console.log(l.gray(` Install: https://go.dev/dl/
1712
- `));}let i=Ut(`Generating Go/Fiber project: ${t.project_name}\u2026`).start();try{let n=(c,p)=>Qr(h.join(e,c),p),a=h.join(e,"rapidkit"),s=h.join(e,"rapidkit.cmd");await Promise.all([n("cmd/server/main.go",Xr(t)),n("go.mod",Zr(t)),n("internal/config/config.go",ei(t)),n("internal/server/server.go",ti(t)),n("internal/middleware/requestid.go",pi()),n("internal/middleware/requestid_test.go",ui(t)),n("internal/apierr/apierr.go",gi()),n("internal/apierr/apierr_test.go",mi(t)),n("internal/handlers/health.go",oi()),n("internal/handlers/health_test.go",ri(t)),n("internal/handlers/example.go",hi(t)),n("internal/handlers/example_test.go",yi(t)),n("internal/config/config_test.go",wi(t)),n("internal/middleware/cors.go",vi()),n("internal/middleware/cors_test.go",ki(t)),n("internal/middleware/ratelimit.go",Ri(t)),n("internal/middleware/ratelimit_test.go",_i(t)),n("internal/server/server_test.go",bi(t)),n("docs/doc.go",fi(t)),n(".air.toml",Ci(t)),n("Dockerfile",ii()),n("docker-compose.yml",ni(t)),n("Makefile",si(t)),n(".golangci.yml",Si(t.module_path)),n(".env.example",ai(t)),n(".gitignore",ci()),n(".github/workflows/ci.yml",li(t)),n("README.md",di(t)),n(".rapidkit/project.json",Ii(t,r)),n(".rapidkit/context.json",xi()),n("rapidkit",Pi(t)),n("rapidkit.cmd",Ei(t))]),await promises.chmod(a,493),await promises.chmod(s,493),i.succeed(l.green(`Project created at ${e}`));try{i.start("Fetching Go dependencies\u2026"),await execa("go",["mod","tidy"],{cwd:e,timeout:12e4}),i.succeed(l.gray("\u2713 go mod tidy completed"));}catch{i.warn(l.yellow("\u26A0 go mod tidy failed \u2014 run manually: go mod tidy"));}if(!t.skipGit)try{await execa("git",["init"],{cwd:e}),await execa("git",["add","-A"],{cwd:e}),await execa("git",["commit","-m","chore: initial scaffold (rapidkit gofiber.standard)"],{cwd:e}),console.log(l.gray("\u2713 git repository initialized"));}catch{console.log(l.gray("\u26A0 git init skipped (git not found or error)"));}console.log(""),console.log(l.bold("\u2705 Go/Fiber project ready!")),console.log(""),console.log(l.cyan("Next steps:")),console.log(l.white(` cd ${t.project_name}`)),console.log(l.white(" make run # start dev server")),console.log(l.white(" make test # run tests")),console.log(""),console.log(l.gray("Server will listen on port "+t.port)),console.log(l.gray(" http://localhost:"+t.port+"/api/v1/health/live")),console.log(l.gray(" http://localhost:"+t.port+"/api/v1/health/ready")),console.log(""),console.log(l.yellow("\u2139 RapidKit modules are not available for Go projects (module system uses Python/pip).")),console.log("");}catch(n){throw i.fail(l.red("Failed to generate Go/Fiber project")),n}}function Ro(e){return e.split(/[-_\s]+/).map(o=>o.charAt(0).toUpperCase()+o.slice(1)).join("")}async function Ai(e,o){await promises.mkdir(h.dirname(e),{recursive:true}),await promises.writeFile(e,o,"utf8");}function Oi(e){return `package main
1713
-
1714
- import (
1715
- "context"
1716
- "errors"
1717
- "fmt"
1718
- "log/slog"
1719
- "net/http"
1720
- "os"
1721
- "os/signal"
1722
- "syscall"
1723
- "time"
1724
-
1725
- _ "${e.module_path}/docs"
1726
- "${e.module_path}/internal/config"
1727
- "${e.module_path}/internal/server"
1728
- )
1729
-
1730
- // Build-time variables \u2014 injected via -ldflags.
1731
- var (
1732
- version = "dev"
1733
- commit = "none"
1734
- date = "unknown"
1735
- )
1736
-
1737
- func main() {
1738
- cfg := config.Load()
1739
-
1740
- log := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
1741
- Level: config.ParseLogLevel(cfg.LogLevel),
1742
- }))
1743
- slog.SetDefault(log)
1744
-
1745
- r := server.NewRouter(cfg)
1746
-
1747
- srv := &http.Server{
1748
- Addr: ":" + cfg.Port,
1749
- Handler: r,
1750
- ReadTimeout: 5 * time.Second,
1751
- WriteTimeout: 10 * time.Second,
1752
- IdleTimeout: 30 * time.Second,
1753
- }
1754
-
1755
- go func() {
1756
- slog.Info("starting", "port", cfg.Port, "version", version, "commit", commit, "date", date, "env", cfg.Env)
1757
- fmt.Printf("\\n\u{1F680} Server \u2192 http://127.0.0.1:%s\\n", cfg.Port)
1758
- fmt.Printf("\u{1F4D6} Docs \u2192 http://127.0.0.1:%s/docs\\n\\n", cfg.Port)
1759
- if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
1760
- slog.Error("server error", "err", err)
1761
- os.Exit(1)
1762
- }
1763
- }()
1764
-
1765
- quit := make(chan os.Signal, 1)
1766
- signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
1767
- <-quit
1768
-
1769
- slog.Info("shutting down\u2026")
1770
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
1771
- defer cancel()
1772
-
1773
- if err := srv.Shutdown(ctx); err != nil {
1774
- slog.Error("forced shutdown", "err", err)
1775
- os.Exit(1)
1776
- }
1777
- slog.Info("server stopped")
1778
- }
1779
- `}function Ni(e){return `module ${e.module_path}
1780
-
1781
- go ${e.go_version}
1782
-
1783
- require (
1784
- github.com/gin-gonic/gin v1.10.0
1785
- github.com/swaggo/gin-swagger v1.6.0
1786
- github.com/swaggo/swag v1.16.3
1787
- )
1788
-
1789
- require (
1790
- github.com/KyleBanks/depth v1.2.1 // indirect
1791
- github.com/bytedance/sonic v1.11.6 // indirect
1792
- github.com/bytedance/sonic/loader v0.1.1 // indirect
1793
- github.com/cloudwego/base64x v0.1.4 // indirect
1794
- github.com/cloudwego/iasm v0.2.0 // indirect
1795
- github.com/gabriel-vasile/mimetype v1.4.3 // indirect
1796
- github.com/ghodss/yaml v1.0.0 // indirect
1797
- github.com/gin-contrib/sse v0.1.0 // indirect
1798
- github.com/go-openapi/jsonpointer v0.21.0 // indirect
1799
- github.com/go-openapi/jsonreference v0.21.0 // indirect
1800
- github.com/go-openapi/spec v0.21.0 // indirect
1801
- github.com/go-openapi/swag v0.23.0 // indirect
1802
- github.com/go-playground/locales v0.14.1 // indirect
1803
- github.com/go-playground/universal-translator v0.18.1 // indirect
1804
- github.com/go-playground/validator/v10 v10.20.0 // indirect
1805
- github.com/goccy/go-json v0.10.2 // indirect
1806
- github.com/josharian/intern v1.0.0 // indirect
1807
- github.com/json-iterator/go v1.1.12 // indirect
1808
- github.com/klauspost/cpuid/v2 v2.2.7 // indirect
1809
- github.com/leodido/go-urn v1.4.0 // indirect
1810
- github.com/mailru/easyjson v0.7.7 // indirect
1811
- github.com/mattn/go-isatty v0.0.20 // indirect
1812
- github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
1813
- github.com/modern-go/reflect2 v1.0.2 // indirect
1814
- github.com/pelletier/go-toml/v2 v2.2.2 // indirect
1815
- github.com/swaggo/files v1.0.1 // indirect
1816
- github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
1817
- github.com/ugorji/go/codec v1.2.12 // indirect
1818
- golang.org/x/arch v0.8.0 // indirect
1819
- golang.org/x/crypto v0.23.0 // indirect
1820
- golang.org/x/net v0.25.0 // indirect
1821
- golang.org/x/sys v0.20.0 // indirect
1822
- golang.org/x/text v0.15.0 // indirect
1823
- golang.org/x/tools v0.21.0 // indirect
1824
- google.golang.org/protobuf v1.34.1 // indirect
1825
- gopkg.in/yaml.v2 v2.4.0 // indirect
1826
- gopkg.in/yaml.v3 v3.0.1 // indirect
1827
- )
1828
- `}function ji(e){return `package config
1829
-
1830
- import (
1831
- "log/slog"
1832
- "os"
1833
- "strings"
1834
- )
1835
-
1836
- // Config holds application configuration loaded from environment variables.
1837
- type Config struct {
1838
- Port string
1839
- Env string
1840
- GinMode string
1841
- LogLevel string
1842
- }
1843
-
1844
- // Load reads configuration from environment variables with sensible defaults.
1845
- func Load() *Config {
1846
- env := getEnv("APP_ENV", "development")
1847
- return &Config{
1848
- Port: getEnv("PORT", "${e.port}"),
1849
- Env: env,
1850
- GinMode: getEnv("GIN_MODE", "debug"),
1851
- LogLevel: getEnv("LOG_LEVEL", defaultLogLevel(env)),
1852
- }
1853
- }
1854
-
1855
- // ParseLogLevel maps a level string to the corresponding slog.Level.
1856
- // Falls back to Info for unrecognised values.
1857
- func ParseLogLevel(s string) slog.Level {
1858
- switch strings.ToLower(s) {
1859
- case "debug":
1860
- return slog.LevelDebug
1861
- case "warn", "warning":
1862
- return slog.LevelWarn
1863
- case "error":
1864
- return slog.LevelError
1865
- default:
1866
- return slog.LevelInfo
1867
- }
1868
- }
1869
-
1870
- func defaultLogLevel(env string) string {
1871
- if env == "development" {
1872
- return "debug"
1873
- }
1874
- return "info"
1875
- }
1876
-
1877
- func getEnv(key, fallback string) string {
1878
- if v, ok := os.LookupEnv(key); ok && v != "" {
1879
- return v
1880
- }
1881
- return fallback
1882
- }
1883
- `}function $i(e){return `package server
1884
-
1885
- import (
1886
- "net/http"
1887
-
1888
- "github.com/gin-gonic/gin"
1889
- ginSwagger "github.com/swaggo/gin-swagger"
1890
- swaggerFiles "github.com/swaggo/files"
1891
-
1892
- "${e.module_path}/internal/apierr"
1893
- "${e.module_path}/internal/config"
1894
- "${e.module_path}/internal/handlers"
1895
- "${e.module_path}/internal/middleware"
1896
- )
1897
-
1898
- // NewRouter assembles the Gin engine with all middleware and routes.
1899
- // Call this from main \u2014 or from tests via server.NewRouter(cfg).
1900
- func NewRouter(cfg *config.Config) *gin.Engine {
1901
- if cfg.GinMode == "release" {
1902
- gin.SetMode(gin.ReleaseMode)
1903
- }
1904
-
1905
- r := gin.New()
1906
- r.Use(gin.Recovery())
1907
- r.Use(middleware.CORS())
1908
- r.Use(middleware.RequestID())
1909
- r.Use(middleware.RateLimit())
1910
- r.Use(middleware.Logger())
1911
-
1912
- // Swagger UI \u2014 /docs redirects to /docs/index.html
1913
- r.GET("/docs", func(c *gin.Context) { c.Redirect(http.StatusFound, "/docs/index.html") })
1914
- r.GET("/docs/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
1915
-
1916
- v1 := r.Group("/api/v1")
1917
- {
1918
- v1.GET("/health/live", handlers.Liveness)
1919
- v1.GET("/health/ready", handlers.Readiness)
1920
- v1.GET("/echo/:name", handlers.EchoParams)
1921
- }
1922
-
1923
- // Return JSON for unknown routes/methods instead of Gin's default text responses.
1924
- // HandleMethodNotAllowed must be true so NoMethod handler fires for 405 cases.
1925
- r.HandleMethodNotAllowed = true
1926
- r.NoRoute(func(c *gin.Context) {
1927
- apierr.NotFound(c, "route not found")
1928
- })
1929
- r.NoMethod(func(c *gin.Context) {
1930
- apierr.MethodNotAllowed(c)
1931
- })
1932
-
1933
- return r
1934
- }
1935
- `}function Di(){return `package handlers
1936
-
1937
- import (
1938
- "net/http"
1939
- "time"
1940
-
1941
- "github.com/gin-gonic/gin"
1942
- )
1943
-
1944
- // Liveness signals the process is alive (Kubernetes livenessProbe).
1945
- //
1946
- // @Summary Liveness probe
1947
- // @Description Returns 200 when the process is alive.
1948
- // @Tags health
1949
- // @Produce json
1950
- // @Success 200 {object} map[string]string
1951
- // @Router /api/v1/health/live [get]
1952
- func Liveness(c *gin.Context) {
1953
- c.JSON(http.StatusOK, gin.H{
1954
- "status": "ok",
1955
- "time": time.Now().UTC().Format(time.RFC3339),
1956
- })
1957
- }
1958
-
1959
- // Readiness signals the service can accept traffic (Kubernetes readinessProbe).
1960
- // Extend this function to check database connectivity, caches, etc.
1961
- //
1962
- // @Summary Readiness probe
1963
- // @Description Returns 200 when the service is ready to accept traffic.
1964
- // @Tags health
1965
- // @Produce json
1966
- // @Success 200 {object} map[string]string
1967
- // @Router /api/v1/health/ready [get]
1968
- func Readiness(c *gin.Context) {
1969
- c.JSON(http.StatusOK, gin.H{
1970
- "status": "ready",
1971
- "time": time.Now().UTC().Format(time.RFC3339),
1972
- })
1973
- }
1974
- `}function Gi(e){return `package handlers_test
1975
-
1976
- import (
1977
- "encoding/json"
1978
- "net/http"
1979
- "net/http/httptest"
1980
- "testing"
1981
-
1982
- "github.com/gin-gonic/gin"
1983
-
1984
- "${e.module_path}/internal/config"
1985
- "${e.module_path}/internal/server"
1986
- )
1987
-
1988
- func init() { gin.SetMode(gin.TestMode) }
1989
-
1990
- func newRouter() *gin.Engine { return server.NewRouter(config.Load()) }
1991
-
1992
- func TestLiveness(t *testing.T) {
1993
- w := httptest.NewRecorder()
1994
- req, _ := http.NewRequest(http.MethodGet, "/api/v1/health/live", nil)
1995
- newRouter().ServeHTTP(w, req)
1996
-
1997
- if w.Code != http.StatusOK {
1998
- t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
1999
- }
2000
-
2001
- var body map[string]any
2002
- if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil {
2003
- t.Fatalf("invalid JSON: %v", err)
2004
- }
2005
- if body["status"] != "ok" {
2006
- t.Fatalf("expected ok, got %v", body["status"])
2007
- }
2008
- }
2009
-
2010
- func TestReadiness(t *testing.T) {
2011
- w := httptest.NewRecorder()
2012
- req, _ := http.NewRequest(http.MethodGet, "/api/v1/health/ready", nil)
2013
- newRouter().ServeHTTP(w, req)
2014
-
2015
- if w.Code != http.StatusOK {
2016
- t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
2017
- }
2018
- }
2019
- `}function Mi(){return `# \u2500\u2500 Build stage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
2020
- FROM golang:1.24-alpine AS builder
2021
-
2022
- # Build-time version injection
2023
- ARG VERSION=dev
2024
- ARG COMMIT=none
2025
- ARG DATE=unknown
2026
-
2027
- WORKDIR /app
2028
- COPY go.mod go.sum ./
2029
- RUN go mod download
2030
-
2031
- COPY . .
2032
- RUN CGO_ENABLED=0 GOOS=linux go build \\
2033
- -ldflags="-s -w -X main.version=$\${VERSION} -X main.commit=$\${COMMIT} -X main.date=$\${DATE}" \\
2034
- -o server ./cmd/server
2035
-
2036
- # \u2500\u2500 Runtime stage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
2037
- # alpine includes busybox wget required for the HEALTHCHECK below.
2038
- FROM alpine:3.21
2039
-
2040
- RUN addgroup -S app && adduser -S -G app app
2041
- COPY --from=builder /app/server /server
2042
- USER app
2043
-
2044
- EXPOSE 8080
2045
- HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
2046
- CMD wget -qO- http://localhost:8080/api/v1/health/live || exit 1
2047
- ENTRYPOINT ["/server"]
2048
- `}function Li(e){return `version: "3.9"
2049
-
2050
- services:
2051
- api:
2052
- build: .
2053
- container_name: ${e.project_name}
2054
- ports:
2055
- - "${e.port}:${e.port}"
2056
- environment:
2057
- PORT: "${e.port}"
2058
- APP_ENV: development
2059
- GIN_MODE: debug
2060
- LOG_LEVEL: info
2061
- CORS_ALLOW_ORIGINS: "*"
2062
- RATE_LIMIT_RPS: "100"
2063
- restart: unless-stopped
2064
- `}function qi(e){return `.PHONY: dev run build test cover lint fmt tidy docs docker-up docker-down
2065
-
2066
- # Build-time metadata
2067
- VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
2068
- COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "none")
2069
- DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
2070
- LDFLAGS = -ldflags "-s -w -X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.date=$(DATE)"
2071
- # Go tool binaries are installed to GOPATH/bin; include it so \`air\` and \`swag\` are found.
2072
- GOBIN ?= $(shell go env GOPATH)/bin
2073
-
2074
- # Hot reload \u2014 installs air on first use
2075
- dev:
2076
- @test -x "$(GOBIN)/air" || go install github.com/air-verse/air@latest
2077
- GIN_MODE=debug $(GOBIN)/air
2078
-
2079
- run:
2080
- GIN_MODE=debug go run $(LDFLAGS) ./cmd/server
2081
-
2082
- build:
2083
- go build $(LDFLAGS) -o bin/${e.project_name} ./cmd/server
2084
-
2085
- test:
2086
- GIN_MODE=test go test ./... -v -race
2087
-
2088
- cover:
2089
- GIN_MODE=test go test ./... -race -coverprofile=coverage.out
2090
- go tool cover -html=coverage.out -o coverage.html
2091
- @echo "Coverage report: coverage.html"
2092
-
2093
- # Generate Swagger docs \u2014 installs swag on first use
2094
- docs:
2095
- @test -x "$(GOBIN)/swag" || go install github.com/swaggo/swag/cmd/swag@latest
2096
- $(GOBIN)/swag init -g main.go -d cmd/server,internal/handlers,internal/apierr -o docs --parseDependency
2097
-
2098
- lint:
2099
- @command -v golangci-lint >/dev/null 2>&1 || (echo "golangci-lint not found. Install: https://golangci-lint.run/usage/install/" && exit 1)
2100
- golangci-lint run ./...
2101
-
2102
- fmt:
2103
- gofmt -w .
2104
-
2105
- tidy:
2106
- go mod tidy
2107
-
2108
- docker-up:
2109
- go mod tidy
2110
- docker compose up --build \\
2111
- --build-arg VERSION=$(VERSION) \\
2112
- --build-arg COMMIT=$(COMMIT) \\
2113
- --build-arg DATE=$(DATE) \\
2114
- -d
2115
-
2116
- docker-down:
2117
- docker compose down
2118
- `}function Fi(e){return `# Application
2119
- PORT=${e.port}
2120
- APP_ENV=development
2121
- GIN_MODE=debug
2122
- LOG_LEVEL=debug
2123
-
2124
- # CORS \u2014 comma-separated list of allowed origins, or * to allow all
2125
- CORS_ALLOW_ORIGINS=*
2126
-
2127
- # Rate limiting \u2014 max requests per IP per second
2128
- RATE_LIMIT_RPS=100
2129
- `}function Hi(){return `# Binaries
2130
- bin/
2131
- *.exe
2132
- *.exe~
2133
- *.dll
2134
- *.so
2135
- *.dylib
2136
-
2137
- # Test binary
2138
- *.test
2139
-
2140
- # Output of go coverage tool
2141
- *.out
2142
- coverage.html
2143
-
2144
- # Go workspace
2145
- go.work
2146
- go.work.sum
2147
-
2148
- # Environment
2149
- .env
2150
- .env.local
2151
-
2152
- # Hot reload (air)
2153
- tmp/
2154
-
2155
- # Swagger \u2014 generated files (committed stub docs/doc.go; run \`make docs\` to regenerate)
2156
- docs/swagger.json
2157
- docs/swagger.yaml
2158
- docs/docs.go
2159
-
2160
- # Editor
2161
- .idea/
2162
- .vscode/
2163
- *.swp
2164
- *.swo
2165
-
2166
- # OS
2167
- .DS_Store
2168
- Thumbs.db
2169
- `}function Ki(e){return `name: CI
2170
-
2171
- on:
2172
- push:
2173
- branches: [main, develop]
2174
- pull_request:
2175
- branches: [main]
2176
-
2177
- jobs:
2178
- test:
2179
- name: Test
2180
- runs-on: ubuntu-latest
2181
-
2182
- steps:
2183
- - uses: actions/checkout@v4
2184
-
2185
- - name: Set up Go
2186
- uses: actions/setup-go@v5
2187
- with:
2188
- go-version: "${e.go_version}"
2189
- cache: true
2190
-
2191
- - name: Tidy
2192
- run: go mod tidy
2193
-
2194
- - name: Build
2195
- run: go build ./...
2196
-
2197
- - name: Test
2198
- run: GIN_MODE=test go test ./... -race -coverprofile=coverage.out
2199
-
2200
- - name: Upload coverage
2201
- uses: actions/upload-artifact@v4
2202
- with:
2203
- name: coverage
2204
- path: coverage.out
2205
-
2206
- lint:
2207
- name: Lint
2208
- runs-on: ubuntu-latest
2209
-
2210
- steps:
2211
- - uses: actions/checkout@v4
2212
-
2213
- - name: Set up Go
2214
- uses: actions/setup-go@v5
2215
- with:
2216
- go-version: "${e.go_version}"
2217
- cache: true
2218
-
2219
- - name: golangci-lint
2220
- uses: golangci/golangci-lint-action@v6
2221
- with:
2222
- version: latest
2223
- `}function Wi(e){return `# ${Ro(e.project_name)}
2224
-
2225
- > ${e.description}
2226
-
2227
- Built with [Go](https://go.dev/) + [Gin](https://gin-gonic.com/) \xB7 Scaffolded by [RapidKit](https://getrapidkit.com)
2228
-
2229
- ## Quick start
2230
-
2231
- \`\`\`bash
2232
- # Run locally (hot reload)
2233
- make dev
2234
-
2235
- # Run tests
2236
- make test
2237
-
2238
- # Build binary
2239
- make build
2240
-
2241
- # Generate / refresh Swagger docs
2242
- make docs
2243
-
2244
- # Docker
2245
- make docker-up
2246
- \`\`\`
2247
-
2248
- ## Swagger / OpenAPI
2249
-
2250
- After running \`make docs\`, the interactive UI is available at:
2251
-
2252
- \`\`\`
2253
- http://localhost:${e.port}/docs
2254
- \`\`\`
2255
-
2256
- The raw OpenAPI spec is served at \`/docs/doc.json\`.
2257
-
2258
- ## Endpoints
2259
-
2260
- | Method | Path | Description |
2261
- |--------|------|--------------|
2262
- | GET | /api/v1/health/live | Kubernetes livenessProbe |
2263
- | GET | /api/v1/health/ready | Kubernetes readinessProbe |
2264
- | GET | /api/v1/echo/:name | Example handler \u2014 remove in production |
2265
- | GET | /docs/* | Swagger UI (OpenAPI docs) |
2266
-
2267
- ## Configuration
2268
-
2269
- All configuration is done through environment variables (see \`.env.example\`):
2270
-
2271
- | Variable | Default | Description |
2272
- |----------|---------|-------------|
2273
- | \`PORT\` | \`${e.port}\` | HTTP listen port |
2274
- | \`APP_ENV\` | \`development\` | Application environment |
2275
- | \`GIN_MODE\` | \`debug\` | \`debug\` \\| \`release\` \\| \`test\` |
2276
- | \`LOG_LEVEL\` | \`debug\` / \`info\` | \`debug\` \\| \`info\` \\| \`warn\` \\| \`error\` |
2277
- | \`CORS_ALLOW_ORIGINS\` | \`*\` | Comma-separated list of allowed origins, or \`*\` |
2278
- | \`RATE_LIMIT_RPS\` | \`100\` | Max requests per IP per second |
2279
-
2280
- ## Project structure
2281
-
2282
- \`\`\`
2283
- ${e.project_name}/
2284
- \u251C\u2500\u2500 cmd/
2285
- \u2502 \u2514\u2500\u2500 server/
2286
- \u2502 \u2514\u2500\u2500 main.go # Graceful shutdown + version ldflags
2287
- \u251C\u2500\u2500 docs/ # Swagger generated files (\`make docs\`)
2288
- \u2502 \u2514\u2500\u2500 doc.go # Package-level OpenAPI annotations
2289
- \u251C\u2500\u2500 internal/
2290
- \u2502 \u251C\u2500\u2500 apierr/ # Consistent JSON error envelope
2291
- \u2502 \u2502 \u251C\u2500\u2500 apierr.go
2292
- \u2502 \u2502 \u2514\u2500\u2500 apierr_test.go
2293
- \u2502 \u251C\u2500\u2500 config/ # 12-factor configuration
2294
- \u2502 \u2502 \u251C\u2500\u2500 config.go
2295
- \u2502 \u2502 \u2514\u2500\u2500 config_test.go
2296
- \u2502 \u251C\u2500\u2500 handlers/ # HTTP handlers + tests
2297
- \u2502 \u2502 \u251C\u2500\u2500 health.go
2298
- \u2502 \u2502 \u251C\u2500\u2500 health_test.go
2299
- \u2502 \u2502 \u251C\u2500\u2500 example.go # EchoParams \u2014 replace with your own handlers
2300
- \u2502 \u2502 \u2514\u2500\u2500 example_test.go
2301
- \u2502 \u251C\u2500\u2500 middleware/
2302
- \u2502 \u2502 \u251C\u2500\u2500 requestid.go # X-Request-ID + structured logger
2303
- \u2502 \u2502 \u251C\u2500\u2500 requestid_test.go
2304
- \u2502 \u2502 \u251C\u2500\u2500 cors.go # CORS (CORS_ALLOW_ORIGINS)
2305
- \u2502 \u2502 \u251C\u2500\u2500 cors_test.go
2306
- \u2502 \u2502 \u251C\u2500\u2500 ratelimit.go # Per-IP limiter (RATE_LIMIT_RPS)
2307
- \u2502 \u2502 \u2514\u2500\u2500 ratelimit_test.go
2308
- \u2502 \u2514\u2500\u2500 server/
2309
- \u2502 \u251C\u2500\u2500 server.go
2310
- \u2502 \u2514\u2500\u2500 server_test.go
2311
- \u251C\u2500\u2500 .air.toml # Hot reload
2312
- \u251C\u2500\u2500 .github/workflows/ci.yml # CI: test + lint
2313
- \u251C\u2500\u2500 .golangci.yml
2314
- \u251C\u2500\u2500 Dockerfile # Multi-stage, alpine HEALTHCHECK
2315
- \u251C\u2500\u2500 docker-compose.yml
2316
- \u251C\u2500\u2500 Makefile
2317
- \u2514\u2500\u2500 README.md
2318
- \`\`\`
2319
-
2320
- ## Available commands
2321
-
2322
- | Command | Description |
2323
- |---------|-------------|
2324
- | \`make dev\` | Hot reload via [air](https://github.com/air-verse/air) |
2325
- | \`make run\` | Run without hot reload |
2326
- | \`make build\` | Binary with version ldflags |
2327
- | \`make test\` | Run tests with race detector |
2328
- | \`make cover\` | HTML coverage report |
2329
- | \`make docs\` | Re-generate Swagger JSON (needs \`swag\`) |
2330
- | \`make lint\` | golangci-lint |
2331
- | \`make fmt\` | gofmt |
2332
- | \`make tidy\` | go mod tidy |
2333
- | \`make docker-up\` | Build & run via Docker Compose |
2334
- | \`make docker-down\` | Stop |
2335
-
2336
- ## License
2337
-
2338
- ${e.app_version} \xB7 ${e.author}
2339
- `}function Ui(){return `package middleware
2340
-
2341
- import (
2342
- "crypto/rand"
2343
- "encoding/hex"
2344
- "log/slog"
2345
- "time"
2346
-
2347
- "github.com/gin-gonic/gin"
2348
- )
2349
-
2350
- const headerRequestID = "X-Request-ID"
2351
-
2352
- // RequestID injects a unique identifier into every request.
2353
- // If the caller sends an X-Request-ID header it is reused; otherwise a new one
2354
- // is generated and written back in the response.
2355
- func RequestID() gin.HandlerFunc {
2356
- return func(c *gin.Context) {
2357
- id := c.GetHeader(headerRequestID)
2358
- if id == "" {
2359
- id = newID()
2360
- }
2361
- c.Set(headerRequestID, id)
2362
- c.Header(headerRequestID, id)
2363
- c.Next()
2364
- }
2365
- }
2366
-
2367
- // Logger emits a structured JSON log line after each request.
2368
- func Logger() gin.HandlerFunc {
2369
- return func(c *gin.Context) {
2370
- start := time.Now()
2371
- c.Next()
2372
- slog.Info("http",
2373
- "method", c.Request.Method,
2374
- "path", c.Request.URL.Path,
2375
- "status", c.Writer.Status(),
2376
- "bytes", c.Writer.Size(),
2377
- "latency_ms", time.Since(start).Milliseconds(),
2378
- "ip", c.ClientIP(),
2379
- "request_id", c.GetString(headerRequestID),
2380
- )
2381
- }
2382
- }
2383
-
2384
- func newID() string {
2385
- b := make([]byte, 8)
2386
- if _, err := rand.Read(b); err != nil {
2387
- return "unknown"
2388
- }
2389
- return hex.EncodeToString(b)
2390
- }
2391
- `}function Vi(e){return `package middleware_test
2392
-
2393
- import (
2394
- "net/http"
2395
- "net/http/httptest"
2396
- "testing"
2397
-
2398
- "github.com/gin-gonic/gin"
2399
-
2400
- "${e.module_path}/internal/middleware"
2401
- )
2402
-
2403
- func init() { gin.SetMode(gin.TestMode) }
2404
-
2405
- func newTestRouter() *gin.Engine {
2406
- r := gin.New()
2407
- r.Use(middleware.RequestID())
2408
- r.Use(middleware.Logger())
2409
- r.GET("/ping", func(c *gin.Context) {
2410
- c.String(http.StatusOK, "pong")
2411
- })
2412
- return r
2413
- }
2414
-
2415
- func TestRequestID_IsGenerated(t *testing.T) {
2416
- w := httptest.NewRecorder()
2417
- req, _ := http.NewRequest(http.MethodGet, "/ping", nil)
2418
- newTestRouter().ServeHTTP(w, req)
2419
-
2420
- id := w.Header().Get("X-Request-ID")
2421
- if id == "" {
2422
- t.Fatal("expected X-Request-ID header to be set")
2423
- }
2424
- if len(id) != 16 { // 8 random bytes \u2192 16 hex chars
2425
- t.Fatalf("unexpected request ID length %d, want 16", len(id))
2426
- }
2427
- }
2428
-
2429
- func TestRequestID_IsReused(t *testing.T) {
2430
- w := httptest.NewRecorder()
2431
- req, _ := http.NewRequest(http.MethodGet, "/ping", nil)
2432
- req.Header.Set("X-Request-ID", "my-trace-id")
2433
- newTestRouter().ServeHTTP(w, req)
2434
-
2435
- id := w.Header().Get("X-Request-ID")
2436
- if id != "my-trace-id" {
2437
- t.Fatalf("expected X-Request-ID to be reused, got %q", id)
2438
- }
2439
- }
2440
- `}function Bi(){return `// Package apierr provides a consistent JSON error envelope for all API responses.
2441
- //
2442
- // Every error response looks like:
2443
- //
2444
- // {"error": "user not found", "code": "NOT_FOUND", "request_id": "a1b2c3d4..."}
2445
- package apierr
2446
-
2447
- import (
2448
- "net/http"
2449
-
2450
- "github.com/gin-gonic/gin"
2451
- )
2452
-
2453
- // Response is the standard error envelope returned by all API endpoints.
2454
- type Response struct {
2455
- Error string \`json:"error"\`
2456
- Code string \`json:"code"\`
2457
- RequestID string \`json:"request_id,omitempty"\`
2458
- }
2459
-
2460
- func reply(c *gin.Context, status int, msg, code string) {
2461
- c.AbortWithStatusJSON(status, Response{
2462
- Error: msg,
2463
- Code: code,
2464
- RequestID: c.GetString("X-Request-ID"),
2465
- })
2466
- }
2467
-
2468
- // BadRequest responds with 400 and code "BAD_REQUEST".
2469
- func BadRequest(c *gin.Context, msg string) {
2470
- reply(c, http.StatusBadRequest, msg, "BAD_REQUEST")
2471
- }
2472
-
2473
- // NotFound responds with 404 and code "NOT_FOUND".
2474
- func NotFound(c *gin.Context, msg string) {
2475
- reply(c, http.StatusNotFound, msg, "NOT_FOUND")
2476
- }
2477
-
2478
- // Unauthorized responds with 401 and code "UNAUTHORIZED".
2479
- func Unauthorized(c *gin.Context) {
2480
- reply(c, http.StatusUnauthorized, "authentication required", "UNAUTHORIZED")
2481
- }
2482
-
2483
- // Forbidden responds with 403 and code "FORBIDDEN".
2484
- func Forbidden(c *gin.Context) {
2485
- reply(c, http.StatusForbidden, "access denied", "FORBIDDEN")
2486
- }
2487
-
2488
- // MethodNotAllowed responds with 405 and code "METHOD_NOT_ALLOWED".
2489
- func MethodNotAllowed(c *gin.Context) {
2490
- reply(c, http.StatusMethodNotAllowed, "method not allowed", "METHOD_NOT_ALLOWED")
2491
- }
2492
-
2493
- // InternalError responds with 500 and code "INTERNAL_ERROR".
2494
- // The original error is intentionally not exposed to the client.
2495
- func InternalError(c *gin.Context, _ error) {
2496
- reply(c, http.StatusInternalServerError, "an internal error occurred", "INTERNAL_ERROR")
2497
- }
2498
-
2499
- // TooManyRequests responds with 429 and code "TOO_MANY_REQUESTS".
2500
- func TooManyRequests(c *gin.Context) {
2501
- reply(c, http.StatusTooManyRequests, "rate limit exceeded", "TOO_MANY_REQUESTS")
2502
- }
2503
- `}function Ji(e){return `package apierr_test
2504
-
2505
- import (
2506
- "encoding/json"
2507
- "net/http"
2508
- "net/http/httptest"
2509
- "testing"
2510
-
2511
- "github.com/gin-gonic/gin"
2512
-
2513
- "${e.module_path}/internal/apierr"
2514
- )
2515
-
2516
- func init() { gin.SetMode(gin.TestMode) }
2517
-
2518
- func makeRouter(fn gin.HandlerFunc) *gin.Engine {
2519
- r := gin.New()
2520
- r.GET("/test", fn)
2521
- return r
2522
- }
2523
-
2524
- func readJSON(t *testing.T, w *httptest.ResponseRecorder) apierr.Response {
2525
- t.Helper()
2526
- var out apierr.Response
2527
- if err := json.NewDecoder(w.Body).Decode(&out); err != nil {
2528
- t.Fatalf("invalid JSON: %v", err)
2529
- }
2530
- return out
2531
- }
2532
-
2533
- func TestBadRequest(t *testing.T) {
2534
- w := httptest.NewRecorder()
2535
- req, _ := http.NewRequest(http.MethodGet, "/test", nil)
2536
- makeRouter(func(c *gin.Context) { apierr.BadRequest(c, "invalid email") }).ServeHTTP(w, req)
2537
-
2538
- if w.Code != http.StatusBadRequest {
2539
- t.Fatalf("expected 400, got %d", w.Code)
2540
- }
2541
- body := readJSON(t, w)
2542
- if body.Code != "BAD_REQUEST" {
2543
- t.Fatalf("expected BAD_REQUEST, got %q", body.Code)
2544
- }
2545
- if body.Error != "invalid email" {
2546
- t.Fatalf("unexpected error message: %q", body.Error)
2547
- }
2548
- }
2549
-
2550
- func TestNotFound(t *testing.T) {
2551
- w := httptest.NewRecorder()
2552
- req, _ := http.NewRequest(http.MethodGet, "/test", nil)
2553
- makeRouter(func(c *gin.Context) { apierr.NotFound(c, "user not found") }).ServeHTTP(w, req)
2554
-
2555
- if w.Code != http.StatusNotFound {
2556
- t.Fatalf("expected 404, got %d", w.Code)
2557
- }
2558
- body := readJSON(t, w)
2559
- if body.Code != "NOT_FOUND" {
2560
- t.Fatalf("expected NOT_FOUND, got %q", body.Code)
2561
- }
2562
- }
2563
-
2564
- func TestUnauthorized(t *testing.T) {
2565
- w := httptest.NewRecorder()
2566
- req, _ := http.NewRequest(http.MethodGet, "/test", nil)
2567
- makeRouter(func(c *gin.Context) { apierr.Unauthorized(c) }).ServeHTTP(w, req)
2568
-
2569
- if w.Code != http.StatusUnauthorized {
2570
- t.Fatalf("expected 401, got %d", w.Code)
2571
- }
2572
- }
2573
-
2574
- func TestForbidden(t *testing.T) {
2575
- w := httptest.NewRecorder()
2576
- req, _ := http.NewRequest(http.MethodGet, "/test", nil)
2577
- makeRouter(func(c *gin.Context) { apierr.Forbidden(c) }).ServeHTTP(w, req)
2578
-
2579
- if w.Code != http.StatusForbidden {
2580
- t.Fatalf("expected 403, got %d", w.Code)
2581
- }
2582
- body := readJSON(t, w)
2583
- if body.Code != "FORBIDDEN" {
2584
- t.Fatalf("expected FORBIDDEN, got %q", body.Code)
2585
- }
2586
- }
2587
-
2588
- func TestMethodNotAllowed(t *testing.T) {
2589
- w := httptest.NewRecorder()
2590
- req, _ := http.NewRequest(http.MethodGet, "/test", nil)
2591
- makeRouter(func(c *gin.Context) { apierr.MethodNotAllowed(c) }).ServeHTTP(w, req)
2592
-
2593
- if w.Code != http.StatusMethodNotAllowed {
2594
- t.Fatalf("expected 405, got %d", w.Code)
2595
- }
2596
- body := readJSON(t, w)
2597
- if body.Code != "METHOD_NOT_ALLOWED" {
2598
- t.Fatalf("expected METHOD_NOT_ALLOWED, got %q", body.Code)
2599
- }
2600
- }
2601
-
2602
- func TestInternalError(t *testing.T) {
2603
- w := httptest.NewRecorder()
2604
- req, _ := http.NewRequest(http.MethodGet, "/test", nil)
2605
- makeRouter(func(c *gin.Context) { apierr.InternalError(c, nil) }).ServeHTTP(w, req)
2606
-
2607
- if w.Code != http.StatusInternalServerError {
2608
- t.Fatalf("expected 500, got %d", w.Code)
2609
- }
2610
- body := readJSON(t, w)
2611
- if body.Code != "INTERNAL_ERROR" {
2612
- t.Fatalf("expected INTERNAL_ERROR, got %q", body.Code)
2613
- }
2614
- }
2615
-
2616
- func TestTooManyRequests(t *testing.T) {
2617
- w := httptest.NewRecorder()
2618
- req, _ := http.NewRequest(http.MethodGet, "/test", nil)
2619
- makeRouter(func(c *gin.Context) { apierr.TooManyRequests(c) }).ServeHTTP(w, req)
2620
-
2621
- if w.Code != http.StatusTooManyRequests {
2622
- t.Fatalf("expected 429, got %d", w.Code)
2623
- }
2624
- body := readJSON(t, w)
2625
- if body.Code != "TOO_MANY_REQUESTS" {
2626
- t.Fatalf("expected TOO_MANY_REQUESTS, got %q", body.Code)
2627
- }
2628
- }
2629
- `}function Yi(e){return `// Package docs provides the swaggo-generated OpenAPI specification.
2630
- //
2631
- // Run \`make docs\` to regenerate after changing handler annotations.
2632
- //
2633
- // @title ${Ro(e.project_name)} API
2634
- // @version ${e.app_version}
2635
- // @description ${e.description}
2636
- // @host localhost:${e.port}
2637
- // @BasePath /
2638
- // @schemes http https
2639
- //
2640
- // @contact.name ${e.author}
2641
- // @license.name MIT
2642
- package docs
2643
- `}function zi(e){return `package handlers
2644
-
2645
- import (
2646
- "net/http"
2647
-
2648
- "github.com/gin-gonic/gin"
2649
-
2650
- "${e.module_path}/internal/apierr"
2651
- )
2652
-
2653
- // EchoResponse is the JSON body returned by EchoParams.
2654
- type EchoResponse struct {
2655
- Name string \`json:"name"\`
2656
- RequestID string \`json:"request_id"\`
2657
- }
2658
-
2659
- // EchoParams is an example handler demonstrating how to:
2660
- // - read URL path parameters
2661
- // - use apierr for consistent JSON error responses
2662
- // - access the request ID injected by RequestID middleware
2663
- //
2664
- // Replace or remove this file once you add your own business logic.
2665
- //
2666
- // @Summary Echo path parameter
2667
- // @Description Returns the :name path parameter together with the request ID.
2668
- // @Tags example
2669
- // @Produce json
2670
- // @Param name path string true "Name to echo"
2671
- // @Success 200 {object} handlers.EchoResponse
2672
- // @Failure 400 {object} apierr.Response
2673
- // @Router /api/v1/echo/{name} [get]
2674
- func EchoParams(c *gin.Context) {
2675
- name := c.Param("name")
2676
- if name == "" {
2677
- apierr.BadRequest(c, "name parameter is required")
2678
- return
2679
- }
2680
- c.JSON(http.StatusOK, EchoResponse{
2681
- Name: name,
2682
- RequestID: c.GetString("X-Request-ID"),
2683
- })
2684
- }
2685
- `}function Qi(e){return `package handlers_test
2686
-
2687
- import (
2688
- "encoding/json"
2689
- "net/http"
2690
- "net/http/httptest"
2691
- "testing"
2692
-
2693
- "github.com/gin-gonic/gin"
2694
-
2695
- "${e.module_path}/internal/handlers"
2696
- "${e.module_path}/internal/middleware"
2697
- )
2698
-
2699
- func newEchoRouter() *gin.Engine {
2700
- r := gin.New()
2701
- r.Use(middleware.RequestID())
2702
- r.GET("/echo/:name", handlers.EchoParams)
2703
- return r
2704
- }
2705
-
2706
- func TestEchoParams_Success(t *testing.T) {
2707
- w := httptest.NewRecorder()
2708
- req, _ := http.NewRequest(http.MethodGet, "/echo/alice", nil)
2709
- newEchoRouter().ServeHTTP(w, req)
2710
-
2711
- if w.Code != http.StatusOK {
2712
- t.Fatalf("expected 200, got %d", w.Code)
2713
- }
2714
-
2715
- var body map[string]any
2716
- if err := json.NewDecoder(w.Body).Decode(&body); err != nil {
2717
- t.Fatalf("invalid JSON: %v", err)
2718
- }
2719
- if body["name"] != "alice" {
2720
- t.Fatalf("expected name=alice, got %v", body["name"])
2721
- }
2722
- if body["request_id"] == nil || body["request_id"] == "" {
2723
- t.Fatal("expected request_id to be set by RequestID middleware")
2724
- }
2725
- }
2726
-
2727
- // TestEchoParams_EmptyName registers EchoParams on a param-free route so that
2728
- // c.Param("name") returns "" and the 400 guard executes.
2729
- func TestEchoParams_EmptyName(t *testing.T) {
2730
- gin.SetMode(gin.TestMode)
2731
- r := gin.New()
2732
- r.Use(middleware.RequestID())
2733
- r.GET("/echo-bare", handlers.EchoParams)
2734
-
2735
- w := httptest.NewRecorder()
2736
- req, _ := http.NewRequest(http.MethodGet, "/echo-bare", nil)
2737
- r.ServeHTTP(w, req)
2738
-
2739
- if w.Code != http.StatusBadRequest {
2740
- t.Fatalf("expected 400, got %d", w.Code)
2741
- }
2742
- var body map[string]any
2743
- if err := json.NewDecoder(w.Body).Decode(&body); err != nil {
2744
- t.Fatalf("invalid JSON: %v", err)
2745
- }
2746
- if body["code"] != "BAD_REQUEST" {
2747
- t.Fatalf("expected code=BAD_REQUEST, got %v", body["code"])
2748
- }
2749
- }
2750
- `}function Xi(e){return `package config_test
2751
-
2752
- import (
2753
- "log/slog"
2754
- "testing"
2755
-
2756
- "${e.module_path}/internal/config"
2757
- )
2758
-
2759
- func TestParseLogLevel(t *testing.T) {
2760
- tests := []struct {
2761
- input string
2762
- want slog.Level
2763
- }{
2764
- {"debug", slog.LevelDebug},
2765
- {"DEBUG", slog.LevelDebug},
2766
- {"warn", slog.LevelWarn},
2767
- {"warning", slog.LevelWarn},
2768
- {"error", slog.LevelError},
2769
- {"info", slog.LevelInfo},
2770
- {"", slog.LevelInfo},
2771
- {"unknown", slog.LevelInfo},
2772
- }
2773
- for _, tc := range tests {
2774
- got := config.ParseLogLevel(tc.input)
2775
- if got != tc.want {
2776
- t.Errorf("ParseLogLevel(%q) = %v, want %v", tc.input, got, tc.want)
2777
- }
2778
- }
2779
- }
2780
-
2781
- func TestLoad_EnvOverride(t *testing.T) {
2782
- t.Setenv("PORT", "9090")
2783
- t.Setenv("APP_ENV", "production")
2784
- t.Setenv("LOG_LEVEL", "warn")
2785
- t.Setenv("GIN_MODE", "release")
2786
-
2787
- cfg := config.Load()
2788
-
2789
- if cfg.Port != "9090" {
2790
- t.Errorf("expected Port=9090, got %q", cfg.Port)
2791
- }
2792
- if cfg.Env != "production" {
2793
- t.Errorf("expected Env=production, got %q", cfg.Env)
2794
- }
2795
- if cfg.LogLevel != "warn" {
2796
- t.Errorf("expected LogLevel=warn, got %q", cfg.LogLevel)
2797
- }
2798
- if cfg.GinMode != "release" {
2799
- t.Errorf("expected GinMode=release, got %q", cfg.GinMode)
2800
- }
2801
- }
2802
-
2803
- func TestLoad_Defaults(t *testing.T) {
2804
- // Empty string forces getEnv() to return the built-in fallback value.
2805
- t.Setenv("PORT", "")
2806
- t.Setenv("APP_ENV", "")
2807
- t.Setenv("LOG_LEVEL", "")
2808
- t.Setenv("GIN_MODE", "")
2809
-
2810
- cfg := config.Load()
2811
-
2812
- if cfg.Port != "${e.port}" {
2813
- t.Errorf("expected default Port=${e.port}, got %q", cfg.Port)
2814
- }
2815
- if cfg.Env != "development" {
2816
- t.Errorf("expected default Env=development, got %q", cfg.Env)
2817
- }
2818
- // APP_ENV="" \u2192 fallback "development" \u2192 defaultLogLevel \u2192 "debug"
2819
- if cfg.LogLevel != "debug" {
2820
- t.Errorf("expected default LogLevel=debug (development env), got %q", cfg.LogLevel)
2821
- }
2822
- if cfg.GinMode != "debug" {
2823
- t.Errorf("expected default GinMode=debug, got %q", cfg.GinMode)
2824
- }
2825
- }
2826
- `}function Zi(){return `package middleware
2827
-
2828
- import (
2829
- "net/http"
2830
- "os"
2831
- "strings"
2832
-
2833
- "github.com/gin-gonic/gin"
2834
- )
2835
-
2836
- // CORS returns a Gin middleware configured via CORS_ALLOW_ORIGINS env var.
2837
- //
2838
- // Set CORS_ALLOW_ORIGINS="*" for development (the default when unset).
2839
- // In production supply a comma-separated list of allowed origins:
2840
- //
2841
- // CORS_ALLOW_ORIGINS=https://app.example.com,https://admin.example.com
2842
- func CORS() gin.HandlerFunc {
2843
- allowed := os.Getenv("CORS_ALLOW_ORIGINS")
2844
- if allowed == "" {
2845
- allowed = "*"
2846
- }
2847
- allowAll := allowed == "*"
2848
-
2849
- return func(c *gin.Context) {
2850
- origin := c.Request.Header.Get("Origin")
2851
- if allowAll {
2852
- c.Header("Access-Control-Allow-Origin", "*")
2853
- } else if origin != "" {
2854
- for _, o := range strings.Split(allowed, ",") {
2855
- if strings.TrimSpace(o) == origin {
2856
- c.Header("Access-Control-Allow-Origin", origin)
2857
- c.Header("Vary", "Origin")
2858
- break
2859
- }
2860
- }
2861
- }
2862
- c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
2863
- c.Header("Access-Control-Allow-Headers", "Origin,Content-Type,Authorization,X-Request-ID")
2864
- c.Header("Access-Control-Expose-Headers", "X-Request-ID")
2865
- c.Header("Access-Control-Max-Age", "600")
2866
-
2867
- if c.Request.Method == http.MethodOptions {
2868
- c.AbortWithStatus(http.StatusNoContent)
2869
- return
2870
- }
2871
- c.Next()
2872
- }
2873
- }
2874
- `}function en(e){return `package middleware_test
2875
-
2876
- import (
2877
- "net/http"
2878
- "net/http/httptest"
2879
- "testing"
2880
-
2881
- "github.com/gin-gonic/gin"
2882
-
2883
- "${e.module_path}/internal/middleware"
2884
- )
2885
-
2886
- func newCORSRouter(t *testing.T) *gin.Engine {
2887
- t.Helper()
2888
- gin.SetMode(gin.TestMode)
2889
- r := gin.New()
2890
- r.Use(middleware.CORS())
2891
- r.GET("/ping", func(c *gin.Context) { c.Status(http.StatusOK) })
2892
- return r
2893
- }
2894
-
2895
- func TestCORS_Wildcard(t *testing.T) {
2896
- t.Setenv("CORS_ALLOW_ORIGINS", "*")
2897
- w := httptest.NewRecorder()
2898
- req, _ := http.NewRequest(http.MethodGet, "/ping", nil)
2899
- req.Header.Set("Origin", "https://example.com")
2900
- newCORSRouter(t).ServeHTTP(w, req)
2901
-
2902
- if got := w.Header().Get("Access-Control-Allow-Origin"); got != "*" {
2903
- t.Fatalf("expected ACAO=*, got %q", got)
2904
- }
2905
- }
2906
-
2907
- func TestCORS_Preflight(t *testing.T) {
2908
- t.Setenv("CORS_ALLOW_ORIGINS", "*")
2909
- gin.SetMode(gin.TestMode)
2910
- r := gin.New()
2911
- r.Use(middleware.CORS())
2912
-
2913
- w := httptest.NewRecorder()
2914
- req, _ := http.NewRequest(http.MethodOptions, "/ping", nil)
2915
- req.Header.Set("Origin", "https://example.com")
2916
- req.Header.Set("Access-Control-Request-Method", "POST")
2917
- r.ServeHTTP(w, req)
2918
-
2919
- if w.Code != http.StatusNoContent {
2920
- t.Fatalf("expected 204 preflight, got %d", w.Code)
2921
- }
2922
- }
2923
-
2924
- func TestCORS_SpecificOrigin_Allowed(t *testing.T) {
2925
- t.Setenv("CORS_ALLOW_ORIGINS", "https://app.example.com")
2926
- w := httptest.NewRecorder()
2927
- req, _ := http.NewRequest(http.MethodGet, "/ping", nil)
2928
- req.Header.Set("Origin", "https://app.example.com")
2929
- newCORSRouter(t).ServeHTTP(w, req)
2930
-
2931
- if got := w.Header().Get("Access-Control-Allow-Origin"); got != "https://app.example.com" {
2932
- t.Fatalf("expected ACAO=https://app.example.com, got %q", got)
2933
- }
2934
- }
2935
-
2936
- func TestCORS_SpecificOrigin_Denied(t *testing.T) {
2937
- t.Setenv("CORS_ALLOW_ORIGINS", "https://app.example.com")
2938
- w := httptest.NewRecorder()
2939
- req, _ := http.NewRequest(http.MethodGet, "/ping", nil)
2940
- req.Header.Set("Origin", "https://evil.com")
2941
- newCORSRouter(t).ServeHTTP(w, req)
2942
-
2943
- if got := w.Header().Get("Access-Control-Allow-Origin"); got != "" {
2944
- t.Fatalf("expected no ACAO header for denied origin, got %q", got)
2945
- }
2946
- }
2947
-
2948
- func TestCORS_Default_Origin(t *testing.T) {
2949
- // When CORS_ALLOW_ORIGINS is unset the middleware must default to "*".
2950
- t.Setenv("CORS_ALLOW_ORIGINS", "")
2951
- w := httptest.NewRecorder()
2952
- req, _ := http.NewRequest(http.MethodGet, "/ping", nil)
2953
- req.Header.Set("Origin", "https://anywhere.com")
2954
- newCORSRouter(t).ServeHTTP(w, req)
2955
-
2956
- if got := w.Header().Get("Access-Control-Allow-Origin"); got == "" {
2957
- t.Fatal("expected CORS header when CORS_ALLOW_ORIGINS defaults to *")
2958
- }
2959
- }
2960
- `}function tn(e){return `package server_test
2961
-
2962
- import (
2963
- "encoding/json"
2964
- "net/http"
2965
- "net/http/httptest"
2966
- "testing"
2967
-
2968
- "github.com/gin-gonic/gin"
2969
-
2970
- "${e.module_path}/internal/config"
2971
- "${e.module_path}/internal/server"
2972
- )
2973
-
2974
- func init() { gin.SetMode(gin.TestMode) }
2975
-
2976
- type serverAPIError struct {
2977
- Code string \`json:"code"\`
2978
- Message string \`json:"message"\`
2979
- }
2980
-
2981
- func TestServer_NotFound_JSON(t *testing.T) {
2982
- w := httptest.NewRecorder()
2983
- req, _ := http.NewRequest(http.MethodGet, "/no-such-route", nil)
2984
- server.NewRouter(config.Load()).ServeHTTP(w, req)
2985
-
2986
- if w.Code != http.StatusNotFound {
2987
- t.Fatalf("expected 404, got %d", w.Code)
2988
- }
2989
- var body serverAPIError
2990
- if err := json.NewDecoder(w.Body).Decode(&body); err != nil {
2991
- t.Fatalf("expected JSON error body: %v", err)
2992
- }
2993
- if body.Code != "NOT_FOUND" {
2994
- t.Fatalf("expected code=NOT_FOUND, got %q", body.Code)
2995
- }
2996
- }
2997
-
2998
- func TestServer_MethodNotAllowed_JSON(t *testing.T) {
2999
- w := httptest.NewRecorder()
3000
- req, _ := http.NewRequest(http.MethodPost, "/api/v1/health/live", nil)
3001
- server.NewRouter(config.Load()).ServeHTTP(w, req)
3002
-
3003
- if w.Code != http.StatusMethodNotAllowed {
3004
- t.Fatalf("expected 405, got %d", w.Code)
3005
- }
3006
- var body serverAPIError
3007
- if err := json.NewDecoder(w.Body).Decode(&body); err != nil {
3008
- t.Fatalf("expected JSON error body: %v", err)
3009
- }
3010
- if body.Code != "METHOD_NOT_ALLOWED" {
3011
- t.Fatalf("expected code=METHOD_NOT_ALLOWED, got %q", body.Code)
3012
- }
3013
- }
3014
-
3015
- func TestServer_CORS_Header(t *testing.T) {
3016
- t.Setenv("CORS_ALLOW_ORIGINS", "*")
3017
- w := httptest.NewRecorder()
3018
- req, _ := http.NewRequest(http.MethodGet, "/api/v1/health/live", nil)
3019
- req.Header.Set("Origin", "https://example.com")
3020
- server.NewRouter(config.Load()).ServeHTTP(w, req)
3021
-
3022
- if got := w.Header().Get("Access-Control-Allow-Origin"); got == "" {
3023
- t.Fatal("expected Access-Control-Allow-Origin header to be set")
3024
- }
3025
- }
3026
-
3027
- func TestServer_HealthLive(t *testing.T) {
3028
- w := httptest.NewRecorder()
3029
- req, _ := http.NewRequest(http.MethodGet, "/api/v1/health/live", nil)
3030
- server.NewRouter(config.Load()).ServeHTTP(w, req)
3031
-
3032
- if w.Code != http.StatusOK {
3033
- t.Fatalf("expected 200, got %d", w.Code)
3034
- }
3035
- var body map[string]any
3036
- if err := json.NewDecoder(w.Body).Decode(&body); err != nil {
3037
- t.Fatalf("invalid JSON: %v", err)
3038
- }
3039
- if body["status"] != "ok" {
3040
- t.Fatalf("expected status=ok, got %v", body["status"])
3041
- }
3042
- }
3043
-
3044
- func TestServer_Docs_Redirect(t *testing.T) {
3045
- w := httptest.NewRecorder()
3046
- req, _ := http.NewRequest(http.MethodGet, "/docs", nil)
3047
- server.NewRouter(config.Load()).ServeHTTP(w, req)
3048
-
3049
- if w.Code != http.StatusFound {
3050
- t.Fatalf("expected 302 redirect from /docs, got %d", w.Code)
3051
- }
3052
- if loc := w.Header().Get("Location"); loc != "/docs/index.html" {
3053
- t.Fatalf("expected Location=/docs/index.html, got %q", loc)
3054
- }
3055
- }
3056
-
3057
- func TestServer_ReleaseMode(t *testing.T) {
3058
- // Covers the gin.SetMode(gin.ReleaseMode) branch in NewRouter.
3059
- // Restore test mode after so other tests are not affected.
3060
- t.Cleanup(func() { gin.SetMode(gin.TestMode) })
3061
- t.Setenv("GIN_MODE", "release")
3062
- w := httptest.NewRecorder()
3063
- req, _ := http.NewRequest(http.MethodGet, "/api/v1/health/live", nil)
3064
- server.NewRouter(config.Load()).ServeHTTP(w, req)
3065
-
3066
- if w.Code != http.StatusOK {
3067
- t.Fatalf("expected 200 in release mode, got %d", w.Code)
3068
- }
3069
- }
3070
- `}function on(e){return `package middleware
3071
-
3072
- import (
3073
- "os"
3074
- "strconv"
3075
- "sync"
3076
- "time"
3077
-
3078
- "github.com/gin-gonic/gin"
3079
-
3080
- "${e.module_path}/internal/apierr"
3081
- )
3082
-
3083
- // ipBucket tracks the per-IP fixed-window request counter.
3084
- type ipBucket struct {
3085
- mu sync.Mutex
3086
- count int
3087
- windowStart time.Time
3088
- }
3089
-
3090
- // RateLimit returns a per-IP fixed-window rate limiter.
3091
- // Configure the limit via RATE_LIMIT_RPS env var (requests per second, default 100).
3092
- func RateLimit() gin.HandlerFunc {
3093
- rps := 100
3094
- if raw := os.Getenv("RATE_LIMIT_RPS"); raw != "" {
3095
- if n, err := strconv.Atoi(raw); err == nil && n > 0 {
3096
- rps = n
3097
- }
3098
- }
3099
- var buckets sync.Map
3100
- return func(c *gin.Context) {
3101
- ip := c.ClientIP()
3102
- now := time.Now()
3103
- v, _ := buckets.LoadOrStore(ip, &ipBucket{windowStart: now})
3104
- b := v.(*ipBucket)
3105
- b.mu.Lock()
3106
- if now.Sub(b.windowStart) >= time.Second {
3107
- b.count = 0
3108
- b.windowStart = now
3109
- }
3110
- b.count++
3111
- count := b.count
3112
- b.mu.Unlock()
3113
- if count > rps {
3114
- apierr.TooManyRequests(c)
3115
- c.Header("Retry-After", "1")
3116
- return
3117
- }
3118
- c.Next()
3119
- }
3120
- }
3121
- `}function rn(e){return `package middleware_test
3122
-
3123
- import (
3124
- "net/http"
3125
- "net/http/httptest"
3126
- "testing"
3127
-
3128
- "github.com/gin-gonic/gin"
3129
-
3130
- "${e.module_path}/internal/middleware"
3131
- )
3132
-
3133
- func newRateLimitRouter(t *testing.T) *gin.Engine {
3134
- t.Helper()
3135
- gin.SetMode(gin.TestMode)
3136
- r := gin.New()
3137
- r.Use(middleware.RateLimit())
3138
- r.GET("/", func(c *gin.Context) { c.Status(http.StatusOK) })
3139
- return r
3140
- }
3141
-
3142
- func TestRateLimit_AllowsUnderLimit(t *testing.T) {
3143
- t.Setenv("RATE_LIMIT_RPS", "3")
3144
- r := newRateLimitRouter(t)
3145
-
3146
- for i := 0; i < 3; i++ {
3147
- w := httptest.NewRecorder()
3148
- req, _ := http.NewRequest(http.MethodGet, "/", nil)
3149
- r.ServeHTTP(w, req)
3150
- if w.Code != http.StatusOK {
3151
- t.Fatalf("request %d: expected 200, got %d", i+1, w.Code)
3152
- }
3153
- }
3154
- }
3155
-
3156
- func TestRateLimit_Blocks_After_Limit(t *testing.T) {
3157
- t.Setenv("RATE_LIMIT_RPS", "2")
3158
- r := newRateLimitRouter(t)
3159
-
3160
- // Exhaust the limit.
3161
- for i := 0; i < 2; i++ {
3162
- w := httptest.NewRecorder()
3163
- req, _ := http.NewRequest(http.MethodGet, "/", nil)
3164
- r.ServeHTTP(w, req)
3165
- }
3166
-
3167
- // Next request must be rejected.
3168
- w := httptest.NewRecorder()
3169
- req, _ := http.NewRequest(http.MethodGet, "/", nil)
3170
- r.ServeHTTP(w, req)
3171
-
3172
- if w.Code != http.StatusTooManyRequests {
3173
- t.Fatalf("expected 429 after limit, got %d", w.Code)
3174
- }
3175
- }
3176
-
3177
- func TestRateLimit_InvalidRPS(t *testing.T) {
3178
- // When RATE_LIMIT_RPS is not a valid positive integer, the middleware
3179
- // must fall back to the default limit (100 rps) and allow normal requests.
3180
- t.Setenv("RATE_LIMIT_RPS", "not-a-number")
3181
- r := newRateLimitRouter(t)
3182
- w := httptest.NewRecorder()
3183
- req, _ := http.NewRequest(http.MethodGet, "/", nil)
3184
- r.ServeHTTP(w, req)
3185
-
3186
- if w.Code != http.StatusOK {
3187
- t.Fatalf("expected 200 with invalid RPS env, got %d", w.Code)
3188
- }
3189
- }
3190
- `}function nn(e){return `# Air \u2014 live reload for Go projects
3191
- # https://github.com/air-verse/air
3192
- root = "."
3193
- tmp_dir = "tmp"
3194
-
3195
- [build]
3196
- pre_cmd = ["$(go env GOPATH)/bin/swag init -g main.go -d cmd/server,internal/handlers,internal/apierr -o docs --parseDependency 2>/dev/null || true"]
3197
- cmd = "go build -o ./tmp/server ./cmd/server"
3198
- bin = "./tmp/server"
3199
- include_ext = ["go", "yaml", "yml", "env"]
3200
- exclude_dir = ["tmp", "vendor", ".git", "testdata", "docs"]
3201
- delay = 500
3202
- rerun_delay = 500
3203
- send_interrupt = true
3204
- kill_delay = "200ms"
3205
-
3206
- [env]
3207
- GIN_MODE = "debug"
3208
- PORT = "${e.port}"
3209
-
3210
- [misc]
3211
- clean_on_exit = true
3212
-
3213
- [log]
3214
- time = false
3215
- `}function sn(e){return `run:
3216
- timeout: 5m
3217
-
3218
- linters:
3219
- enable:
3220
- - bodyclose
3221
- - durationcheck
3222
- - errcheck
3223
- - errname
3224
- - errorlint
3225
- - gci
3226
- - goimports
3227
- - gosimple
3228
- - govet
3229
- - ineffassign
3230
- - misspell
3231
- - noctx
3232
- - nolintlint
3233
- - prealloc
3234
- - staticcheck
3235
- - unconvert
3236
- - unused
3237
- - wrapcheck
3238
-
3239
- linters-settings:
3240
- gci:
3241
- sections:
3242
- - standard
3243
- - default
3244
- - prefix(${e})
3245
- goimports:
3246
- local-prefixes: "${e}"
3247
- govet:
3248
- enable:
3249
- - shadow
3250
- wrapcheck:
3251
- ignorePackageGlobs:
3252
- - "${e}/*"
3253
-
3254
- issues:
3255
- max-same-issues: 5
3256
- exclude-rules:
3257
- - path: _test.go
3258
- linters:
3259
- - errcheck
3260
- - wrapcheck
3261
- `}function an(){return JSON.stringify({engine:"npm",runtime:"go"},null,2)}function cn(e,o){return JSON.stringify({kit_name:"gogin.standard",runtime:"go",module_support:false,project_name:e.project_name,module_path:e.module_path,app_version:e.app_version,created_by:"rapidkit-npm",rapidkit_version:o,created_at:new Date().toISOString()},null,2)}function ln(e){return `#!/usr/bin/env sh
3262
- # RapidKit Go/Gin project launcher \u2014 generated by RapidKit CLI
3263
- # https://getrapidkit.com
3264
-
3265
- SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
3266
- CMD="\${1:-}"
3267
- shift 2>/dev/null || true
3268
-
3269
- case "$CMD" in
3270
- init)
3271
- cd "$SCRIPT_DIR"
3272
- echo "\u{1F439} Initializing Go/Gin project\u2026"
3273
- GOBIN="$(go env GOPATH)/bin"
3274
- echo " \u2192 installing air (hot reload)\u2026"
3275
- go install github.com/air-verse/air@latest 2>/dev/null && echo " \u2713 air" || echo " \u26A0 air install failed (run: go install github.com/air-verse/air@latest)"
3276
- echo " \u2192 installing swag (swagger)\u2026"
3277
- go install github.com/swaggo/swag/cmd/swag@latest 2>/dev/null && echo " \u2713 swag" || echo " \u26A0 swag install failed (run: go install github.com/swaggo/swag/cmd/swag@latest)"
3278
- if [ ! -f ".env" ] && [ -f ".env.example" ]; then
3279
- cp .env.example .env && echo " \u2713 .env created from .env.example"
3280
- fi
3281
- go mod tidy && echo " \u2713 go mod tidy"
3282
- echo " \u2192 generating swagger docs (first build)\u2026"
3283
- "$(go env GOPATH)/bin/swag" init -g main.go -d cmd/server,internal/handlers,internal/apierr -o docs --parseDependency 2>/dev/null && echo " \u2713 swagger docs generated" || echo " \u26A0 swagger docs skipped (run: rapidkit docs)"
3284
- echo "\u2705 Ready \u2014 run: rapidkit dev"
3285
- ;;
3286
- dev)
3287
- cd "$SCRIPT_DIR"
3288
- echo "\u{1F4D6} Syncing swagger docs\u2026"
3289
- "$(go env GOPATH)/bin/swag" init -g main.go -d cmd/server,internal/handlers,internal/apierr -o docs --parseDependency 2>/dev/null || true
3290
- if [ -f "$SCRIPT_DIR/Makefile" ]; then
3291
- exec make -C "$SCRIPT_DIR" dev "$@"
3292
- else
3293
- cd "$SCRIPT_DIR" && GIN_MODE=debug exec go run ./cmd/server "$@"
3294
- fi
3295
- ;;
3296
- start)
3297
- BIN="$SCRIPT_DIR/bin/${e.project_name}"
3298
- if [ ! -f "$BIN" ]; then
3299
- make -C "$SCRIPT_DIR" build
3300
- fi
3301
- exec "$BIN" "$@"
3302
- ;;
3303
- build)
3304
- exec make -C "$SCRIPT_DIR" build "$@"
3305
- ;;
3306
- test)
3307
- exec make -C "$SCRIPT_DIR" test "$@"
3308
- ;;
3309
- lint)
3310
- exec make -C "$SCRIPT_DIR" lint "$@"
3311
- ;;
3312
- format|fmt)
3313
- exec make -C "$SCRIPT_DIR" fmt "$@"
3314
- ;;
3315
- docs)
3316
- exec make -C "$SCRIPT_DIR" docs "$@"
3317
- ;;
3318
- help|--help|-h)
3319
- echo "RapidKit \u2014 Go/Gin project: ${e.project_name}"
3320
- echo ""
3321
- echo "Usage: rapidkit <command>"
3322
- echo ""
3323
- echo " init Install tools + create .env (air, swag, go mod tidy)"
3324
- echo " dev Hot reload dev server (make dev \u2014 requires air)"
3325
- echo " start Run compiled binary (make build + bin)"
3326
- echo " build Build binary (make build)"
3327
- echo " docs Generate Swagger docs (make docs \u2014 requires swag)"
3328
- echo " test Run tests (make test)"
3329
- echo " lint Run linter (make lint)"
3330
- echo " format Format code (make fmt)"
3331
- ;;
3332
- *)
3333
- if [ -n "$CMD" ]; then
3334
- echo "rapidkit: unknown command: $CMD" >&2
3335
- fi
3336
- echo "Available: init, dev, start, build, docs, test, lint, format" >&2
3337
- exit 1
3338
- ;;
3339
- esac
3340
- `}function dn(e){return `@echo off
3341
- rem RapidKit Go/Gin project launcher \u2014 Windows
3342
- set CMD=%1
3343
- if "%CMD%"=="" goto usage
3344
- shift
3345
-
3346
- if "%CMD%"=="init" (
3347
- echo Initializing Go/Gin project...
3348
- go install github.com/air-verse/air@latest
3349
- go install github.com/swaggo/swag/cmd/swag@latest
3350
- if not exist .env if exist .env.example copy .env.example .env
3351
- go mod tidy
3352
- exit /b %ERRORLEVEL%
3353
- )
3354
- if "%CMD%"=="dev" ( make dev %* & exit /b %ERRORLEVEL% )
3355
- if "%CMD%"=="build" ( make build %* & exit /b %ERRORLEVEL% )
3356
- if "%CMD%"=="test" ( make test %* & exit /b %ERRORLEVEL% )
3357
- if "%CMD%"=="lint" ( make lint %* & exit /b %ERRORLEVEL% )
3358
- if "%CMD%"=="format" ( make fmt %* & exit /b %ERRORLEVEL% )
3359
- if "%CMD%"=="docs" ( make docs %* & exit /b %ERRORLEVEL% )
3360
- if "%CMD%"=="start" ( bin\\${e.project_name}.exe %* & exit /b %ERRORLEVEL% )
3361
-
3362
- :usage
3363
- echo Available: init, dev, start, build, docs, test, lint, format
3364
- exit /b 1
3365
- `}async function Bt(e,o){let t={project_name:o.project_name,module_path:o.module_path||o.project_name,author:o.author||"RapidKit User",description:o.description||`Go/Gin REST API \u2014 ${o.project_name}`,go_version:o.go_version||"1.24",app_version:o.app_version||"0.1.0",port:o.port||"8080",skipGit:o.skipGit??false},r=c();try{await execa("go",["version"],{timeout:3e3});}catch{console.log(l.yellow("\n\u26A0 Go not found in PATH \u2014 project will be scaffolded, but `go mod tidy` requires Go 1.21+")),console.log(l.gray(` Install: https://go.dev/dl/
3366
- `));}let i=Ut(`Generating Go/Gin project: ${t.project_name}\u2026`).start();try{let n=(c,p)=>Ai(h.join(e,c),p),a=h.join(e,"rapidkit"),s=h.join(e,"rapidkit.cmd");await Promise.all([n("cmd/server/main.go",Oi(t)),n("go.mod",Ni(t)),n("internal/config/config.go",ji(t)),n("internal/server/server.go",$i(t)),n("internal/middleware/requestid.go",Ui()),n("internal/middleware/requestid_test.go",Vi(t)),n("internal/apierr/apierr.go",Bi()),n("internal/apierr/apierr_test.go",Ji(t)),n("internal/handlers/health.go",Di()),n("internal/handlers/health_test.go",Gi(t)),n("internal/handlers/example.go",zi(t)),n("internal/handlers/example_test.go",Qi(t)),n("internal/config/config_test.go",Xi(t)),n("internal/middleware/cors.go",Zi()),n("internal/middleware/cors_test.go",en(t)),n("internal/middleware/ratelimit.go",on(t)),n("internal/middleware/ratelimit_test.go",rn(t)),n("internal/server/server_test.go",tn(t)),n("docs/doc.go",Yi(t)),n(".air.toml",nn(t)),n("Dockerfile",Mi()),n("docker-compose.yml",Li(t)),n("Makefile",qi(t)),n(".golangci.yml",sn(t.module_path)),n(".env.example",Fi(t)),n(".gitignore",Hi()),n(".github/workflows/ci.yml",Ki(t)),n("README.md",Wi(t)),n(".rapidkit/project.json",cn(t,r)),n(".rapidkit/context.json",an()),n("rapidkit",ln(t)),n("rapidkit.cmd",dn(t))]),await promises.chmod(a,493),await promises.chmod(s,493),i.succeed(l.green(`Project created at ${e}`));try{i.start("Fetching Go dependencies\u2026"),await execa("go",["mod","tidy"],{cwd:e,timeout:12e4}),i.succeed(l.gray("\u2713 go mod tidy completed"));}catch{i.warn(l.yellow("\u26A0 go mod tidy failed \u2014 run manually: go mod tidy"));}if(!t.skipGit)try{await execa("git",["init"],{cwd:e}),await execa("git",["add","-A"],{cwd:e}),await execa("git",["commit","-m","chore: initial scaffold (rapidkit gogin.standard)"],{cwd:e}),console.log(l.gray("\u2713 git repository initialized"));}catch{console.log(l.gray("\u26A0 git init skipped (git not found or error)"));}console.log(""),console.log(l.bold("\u2705 Go/Gin project ready!")),console.log(""),console.log(l.cyan("Next steps:")),console.log(l.white(` cd ${t.project_name}`)),console.log(l.white(" make run # start dev server")),console.log(l.white(" make test # run tests")),console.log(""),console.log(l.gray("Server will listen on port "+t.port)),console.log(l.gray(" http://localhost:"+t.port+"/api/v1/health/live")),console.log(l.gray(" http://localhost:"+t.port+"/api/v1/health/ready")),console.log(""),console.log(l.yellow("\u2139 RapidKit modules are not available for Go projects (module system uses Python/pip).")),console.log("");}catch(n){throw i.fail(l.red("Failed to generate Go/Gin project")),n}}function un(e){return [...new Set(e.filter(o=>o&&o.trim().length>0))]}function gn(){let e=k().map(r=>h.join(r,a()?"poetry.exe":"poetry")),o=a()?[h.join(process.env.APPDATA||"","Python","Scripts","poetry.exe"),h.join(process.env.USERPROFILE||"","AppData","Roaming","Python","Scripts","poetry.exe")]:[],t=a()?[]:["/usr/local/bin/poetry","/usr/bin/poetry"];return un([...e,...o,...t])}function mn(e){let o=k().map(c=>({location:"Global (user-local)",path:h.join(c,a()?"rapidkit.exe":"rapidkit")})),t=[{location:"Global (pipx)",path:h.join(e,".local","bin","rapidkit")},{location:"Global (pipx)",path:h.join(e,"AppData","Roaming","Python","Scripts","rapidkit.exe")},{location:"Global (pyenv)",path:h.join(e,".pyenv","shims","rapidkit")},{location:"Global (system)",path:"/usr/local/bin/rapidkit"},{location:"Global (system)",path:"/usr/bin/rapidkit"}],r=g(h.join(process.cwd(),".venv")),i=i$1(process.cwd()),n=[{location:"Workspace (.venv)",path:r},...i.map(c=>({location:"Workspace (launcher)",path:c}))],a$1=[...o,...t,...n],s=new Set;return a$1.filter(c=>s.has(c.path)?false:(s.add(c.path),true))}function fn(e){let o=new Map([["Workspace (.venv)",0],["Global (user-local)",1],["Global (pipx)",2],["Global (pyenv)",3],["Global (system)",4]]);return [...e].sort((t,r)=>{let i=o.get(t.location)??Number.MAX_SAFE_INTEGER,n=o.get(r.location)??Number.MAX_SAFE_INTEGER;return i!==n?i-n:t.path.localeCompare(r.path)})}function Ye(e,o){return a()?`cd "${e}"; ${o}`:`cd ${e} && ${o}`}function _o(e){return a()?Ye(e,"Copy-Item .env.example .env"):Ye(e,"cp .env.example .env")}async function Co(e){try{let o=await b__default.stat(e);return `${h.basename(e)}:${o.isDirectory()?"d":"f"}:${o.size}:${o.mtimeMs}`}catch{return `${h.basename(e)}:missing`}}async function hn(e){try{let o=new Set([".git",".venv","node_modules",".rapidkit","dist","build","coverage","__pycache__"]),t=new Set;await Jt(e)&&t.add(e);let r=async(i,n)=>{if(n<0)return;let a=await To(i);for(let s of a){if(Ao(s,o))continue;let c=h.join(i,s);if(await Jt(c)){t.add(c);continue}n>0&&await r(c,n-1);}};return await r(e,1),t.size===0&&(await Pn(e,3,o)).forEach(n=>t.add(n)),Array.from(t).sort((i,n)=>i.localeCompare(n))}catch{return []}}async function yn(e,o){let t=[h.join(e,".rapidkit-workspace"),h.join(e,".rapidkit","workspace.json"),h.join(e,".rapidkit","policies.yml"),h.join(e,".rapidkit","toolchain.lock"),h.join(e,".rapidkit","cache-config.yml")],r=[".rapidkit/project.json",".rapidkit/context.json",".rapidkit/file-hashes.json","package.json","pyproject.toml","go.mod","go.sum","requirements.txt","Dockerfile","Makefile",".env",".env.example","src","modules","tests","test",".venv","node_modules"],i=await Promise.all(t.map(Co)),n=await Promise.all(o.map(async a=>{let s=await Promise.all(r.map(c=>Co(h.join(a,c))));return `${a}::${s.join("|")}`}));return [...i,...n].join("||")}async function wn(e,o){try{if(!await b__default.pathExists(e))return null;let t=await b__default.readJSON(e);return !t||t.signature!==o||!Array.isArray(t.projects)?null:t}catch{return null}}async function vn(e,o){try{await b__default.ensureDir(h.dirname(e)),await b__default.writeJSON(e,o,{spaces:2});}catch{}}async function kn(e,o,t){let r=h.join(e,".rapidkit","reports","doctor-last-run.json");try{return await b__default.ensureDir(h.dirname(r)),await b__default.writeJSON(r,{generatedAt:new Date().toISOString(),workspacePath:e,workspaceName:o.workspaceName,projectScanCached:o.projectScanCached??false,projectScanSignature:o.projectScanSignature,cachePath:t,healthScore:o.healthScore,system:{python:o.python,poetry:o.poetry,pipx:o.pipx,go:o.go,rapidkitCore:o.rapidkitCore,versions:{core:o.coreVersion,npm:o.npmVersion}},projects:o.projects,summary:{totalProjects:o.projects.length,totalIssues:o.projects.reduce((i,n)=>i+n.issues.length,0),hasSystemErrors:[o.python,o.rapidkitCore].some(i=>i.status==="error")}},{spaces:2}),r}catch{return}}async function Io(){let[e,o,t,r,i]=await Promise.all([bn(),Rn(),_n(),Cn(),Sn()]);return {python:e,poetry:o,pipx:t,go:r,rapidkitCore:i}}async function bn(){let e=d$2();for(let o of e)try{let{stdout:t}=await execa(o,["--version"],{timeout:3e3}),r=t.match(/Python (\d+\.\d+\.\d+)/);if(r){let i=r[1],[n,a]=i.split(".").map(Number);return n<3||n===3&&a<10?{status:"warn",message:`Python ${i} (requires 3.10+)`,details:`${o} found but version is below minimum requirement`}:{status:"ok",message:`Python ${i}`,details:`Using ${o}`}}}catch{continue}return {status:"error",message:"Python not found",details:"Install Python 3.10+ and ensure it's in PATH"}}async function Rn(){try{let{stdout:e}=await execa("poetry",["--version"],{timeout:3e3}),o=e.match(/Poetry .*version ([\d.]+)/);return o?{status:"ok",message:`Poetry ${o[1]}`,details:"Available for dependency management"}:{status:"warn",message:"Poetry version unknown"}}catch{let e=d$2().map(o=>({cmd:o,args:o==="py"?["-3","-m","poetry","--version"]:["-m","poetry","--version"]}));for(let o of e)try{let{stdout:t}=await execa(o.cmd,o.args,{timeout:3e3,shell:b$3()}),r=t.match(/Poetry .*version ([\d.]+)/)||t.match(/([\d.]+)/);return {status:"ok",message:r?.[1]?`Poetry ${r[1]}`:"Poetry detected",details:`Available via ${o.cmd} ${o.args.join(" ")}`}}catch{continue}for(let o of gn())try{if(!await b__default.pathExists(o))continue;let{stdout:t}=await execa(o,["--version"],{timeout:3e3,shell:b$3()}),r=t.match(/Poetry .*version ([\d.]+)/)||t.match(/([\d.]+)/);return {status:"ok",message:r?.[1]?`Poetry ${r[1]}`:"Poetry detected",details:`Available at ${o}`}}catch{continue}return {status:"warn",message:"Poetry not installed",details:"Optional: Install for better dependency management"}}}async function _n(){try{let{stdout:e}=await execa("pipx",["--version"],{timeout:3e3});return {status:"ok",message:`pipx ${e.trim()}`,details:"Available for global tool installation"}}catch{let e=d$2();for(let o of e)try{let t=o==="py"?["-3","-m","pipx","--version"]:["-m","pipx","--version"],{stdout:r}=await execa(o,t,{timeout:3e3,shell:b$3()});return {status:"ok",message:`pipx ${r.trim()}`,details:`Available via ${o} ${t.join(" ")}`}}catch{continue}return {status:"warn",message:"pipx not installed",details:"Optional: Install for isolated Python tools"}}}async function Cn(){try{let{stdout:e}=await execa("go",["version"],{timeout:3e3}),o=e.match(/go version go(\d+\.\d+(?:\.\d+)?)/);return o?{status:"ok",message:`Go ${o[1]}`,details:"Available for Go/Fiber and Go/Gin projects"}:{status:"ok",message:"Go (version unknown)",details:"go found in PATH"}}catch{return {status:"warn",message:"Go not installed",details:"Optional: Required only for gofiber.standard / gogin.standard projects \u2014 https://go.dev/dl/"}}}async function Sn(){let e=process.env.HOME||process.env.USERPROFILE||"",o=[],t=mn(e);for(let{location:i,path:n}of t)try{if(await b__default.pathExists(n)){let{stdout:a,exitCode:s}=await execa(n,["--version"],{timeout:3e3,reject:false});if(s===0&&(a.includes("RapidKit Version")||a.includes("RapidKit"))){let c=a.match(/v?([\d.]+(?:rc\d+)?(?:a\d+)?(?:b\d+)?)/);c&&o.push({location:i,path:n,version:c[1]});}}}catch{continue}if(o.length>0){let i=o.filter(a=>a.location!=="Workspace (launcher)");if(i.length>0){let a=fn(i);return {status:"ok",message:`RapidKit Core ${a[0].version}`,paths:a.map(c=>({location:c.location,path:c.path,version:c.version}))}}return {status:"ok",message:`RapidKit Core ${o[0].version}`,details:"Detected via workspace launcher"}}try{let{stdout:i,exitCode:n}=await execa("rapidkit",["--version"],{timeout:3e3,reject:false});if(n===0&&(i.includes("RapidKit Version")||i.includes("RapidKit"))){let a=i.match(/v?([\d.]+(?:rc\d+)?(?:a\d+)?(?:b\d+)?)/);if(a)return {status:"ok",message:`RapidKit Core ${a[1]}`,details:"Available via PATH"}}}catch{}try{let{stdout:i,exitCode:n}=await execa("poetry",["run","rapidkit","--version"],{timeout:3e3,reject:false});if(n===0&&(i.includes("RapidKit Version")||i.includes("RapidKit"))){let a=i.match(/v?([\d.]+(?:rc\d+)?(?:a\d+)?(?:b\d+)?)/);if(a)return {status:"ok",message:`RapidKit Core ${a[1]}`,details:"Available via Poetry"}}}catch{}let r=d$2();for(let i of r)try{let{stdout:n,exitCode:a}=await execa(i,["-c","import rapidkit_core; print(rapidkit_core.__version__)"],{timeout:3e3,reject:false});if(a===0&&n&&!n.includes("Traceback")&&!n.includes("ModuleNotFoundError")){let s=n.trim();if(s)return {status:"ok",message:`RapidKit Core ${s}`,details:`Available in ${i} environment`}}}catch{continue}return {status:"error",message:"RapidKit Core not installed",details:"Install with: pipx install rapidkit-core"}}async function Rt(e,o){let t=h.join(e,"Dockerfile");o.hasDocker=await b__default.pathExists(t);let r=h.join(e,"tests"),i=h.join(e,"test"),n=await b__default.pathExists(r)||await b__default.pathExists(i),a=false;if(o.framework==="Go/Fiber"||o.framework==="Go/Gin")try{let s=[{dir:e,depth:0}],c=4,p=new Set([".git",".venv","node_modules","dist","build","vendor"]);for(;s.length>0&&!a;){let d=s.shift();if(!d)break;let u=[];try{u=await b__default.readdir(d.dir);}catch{continue}for(let m of u){let w=h.join(d.dir,m),f;try{f=await b__default.stat(w);}catch{continue}if(f.isFile()&&m.endsWith("_test.go")){a=true;break}f.isDirectory()&&d.depth<c&&!p.has(m)&&!m.startsWith(".")&&s.push({dir:w,depth:d.depth+1});}}}catch{}if(o.hasTests=n||a,o.framework==="NestJS"){let s=h.join(e,".eslintrc.js"),c=h.join(e,".eslintrc.json");o.hasCodeQuality=await b__default.pathExists(s)||await b__default.pathExists(c);}else if(o.framework==="Go/Fiber"||o.framework==="Go/Gin"){let s=h.join(e,".golangci.yml"),c=h.join(e,".golangci.yaml"),p=h.join(e,"Makefile"),d=await b__default.pathExists(p)&&(await b__default.readFile(p,"utf8")).includes("golangci-lint");o.hasCodeQuality=await b__default.pathExists(s)||await b__default.pathExists(c)||d;}else if(o.framework==="FastAPI"){let s=h.join(e,"ruff.toml"),c=h.join(e,"pyproject.toml");if(await b__default.pathExists(c))try{let p=await b__default.readFile(c,"utf8");o.hasCodeQuality=p.includes("[tool.ruff]")||await b__default.pathExists(s);}catch{o.hasCodeQuality=await b__default.pathExists(s);}}try{if(o.framework==="NestJS"){let{stdout:s}=await execa("npm",["audit","--json"],{cwd:e,reject:false});if(s)try{let p=JSON.parse(s).metadata?.vulnerabilities;p&&(o.vulnerabilities=(p.high||0)+(p.critical||0)+(p.moderate||0));}catch{}}else if(o.framework==="FastAPI"){let s=h.join(e,".venv"),c=f$1(s);if(await b__default.pathExists(c))try{let{stdout:p}=await execa(c,["-m","pip","list","--format=json"],{timeout:5e3,reject:false});if(p){JSON.parse(p);o.vulnerabilities=0;}}catch{}}}catch{}}async function xn(e){let t={name:h.basename(e),path:e,venvActive:false,depsInstalled:false,coreInstalled:false,issues:[],fixCommands:[]},r=h.join(e,".rapidkit");if(!await b__default.pathExists(r))return t.issues.push("Not a valid RapidKit project (missing .rapidkit directory)"),t;try{let u=h.join(e,"registry.json");if(await b__default.pathExists(u)){let m=await b__default.readJson(u);m.installed_modules&&(t.stats={modules:m.installed_modules.length});}}catch{}let i=null;try{let u=h.join(r,"project.json");if(await b__default.pathExists(u)){i=await b__default.readJson(u);let m=i?.kit_name||i?.kit;m&&(t.kit=m);}}catch{}try{let u=h.join(e,".git");if(await b__default.pathExists(u)){let{stdout:m}=await execa("git",["log","-1","--format=%cr"],{cwd:e,reject:false});m&&(t.lastModified=m.trim());}else {let m=await b__default.stat(e),f=Date.now()-m.mtime.getTime(),g=Math.floor(f/(1e3*60*60*24));t.lastModified=g===0?"today":`${g} day${g>1?"s":""} ago`;}}catch{}let n=h.join(e,"package.json"),a=h.join(e,"pyproject.toml"),s=h.join(e,"go.mod");if(await b__default.pathExists(s)||i?.runtime==="go"||typeof i?.kit_name=="string"&&(i.kit_name.startsWith("gofiber")||i.kit_name.startsWith("gogin"))){let u=i?.kit_name??"";t.framework=u.startsWith("gogin")?"Go/Gin":"Go/Fiber",t.isGoProject=true,t.venvActive=true,t.coreInstalled=false;try{await execa("go",["version"],{timeout:3e3});}catch{t.issues.push("Go toolchain not found \u2014 install from https://go.dev/dl/"),t.fixCommands?.push("https://go.dev/dl/");}let m=h.join(e,"go.sum");return await b__default.pathExists(m)?t.depsInstalled=true:(t.depsInstalled=false,t.issues.push("Go dependencies not downloaded (go.sum missing)"),t.fixCommands?.push(Ye(e,"go mod tidy"))),await Rt(e,t),t}let p=await b__default.pathExists(n),d=await b__default.pathExists(a);if(p){t.framework="NestJS",t.venvActive=true;let u=h.join(e,"node_modules");if(await b__default.pathExists(u))try{let g=(await b__default.readdir(u)).filter(R=>!R.startsWith(".")&&!R.startsWith("_"));t.depsInstalled=g.length>0;}catch{t.depsInstalled=false;}t.depsInstalled||(t.issues.push("Dependencies not installed (node_modules empty or missing)"),t.fixCommands?.push(Ye(e,"rapidkit init"))),t.coreInstalled=false;let m=h.join(e,".env");if(t.hasEnvFile=await b__default.pathExists(m),!t.hasEnvFile){let f=h.join(e,".env.example");await b__default.pathExists(f)&&(t.issues.push("Environment file missing (found .env.example)"),t.fixCommands?.push(_o(e)));}let w=h.join(e,"src");if(t.modulesHealthy=true,t.missingModules=[],await b__default.pathExists(w))try{let f=await b__default.readdir(w);t.modulesHealthy=f.length>0;}catch{t.modulesHealthy=false;}return await Rt(e,t),t}if(d){t.framework="FastAPI";let u=h.join(e,".venv");if(await b__default.pathExists(u)){t.venvActive=true;let g=f$1(u);if(await b__default.pathExists(g)){try{let{stdout:R}=await execa(g,["-c","import rapidkit_core; print(rapidkit_core.__version__)"],{timeout:2e3});t.coreInstalled=true,t.coreVersion=R.trim();}catch{t.coreInstalled=false;}try{await execa(g,["-c","import fastapi"],{timeout:2e3}),t.depsInstalled=true;}catch{try{let R=h.join(u,"lib");if(await b__default.pathExists(R)){let T=(await b__default.readdir(R)).find(C=>C.startsWith("python"));if(T){let C=h.join(R,T,"site-packages");if(await b__default.pathExists(C)){let G=(await b__default.readdir(C)).filter(N=>!N.startsWith("_")&&!N.includes("dist-info")&&!["pip","setuptools","wheel","pkg_resources"].includes(N));t.depsInstalled=G.length>0;}}}t.depsInstalled||(t.issues.push("Dependencies not installed"),t.fixCommands?.push(Ye(e,"rapidkit init")));}catch{t.issues.push("Could not verify dependency installation");}}}else t.issues.push("Virtual environment exists but Python executable not found");}else t.issues.push("Virtual environment not created"),t.fixCommands?.push(Ye(e,"rapidkit init"));let m=h.join(e,".env");if(t.hasEnvFile=await b__default.pathExists(m),!t.hasEnvFile){let g=h.join(e,".env.example");await b__default.pathExists(g)&&(t.issues.push("Environment file missing (found .env.example)"),t.fixCommands?.push(_o(e)));}let w=h.join(e,"src"),f=h.join(e,"modules");if(t.modulesHealthy=true,t.missingModules=[],await b__default.pathExists(w)){let g=h.join(w,"__init__.py");await b__default.pathExists(g)||(t.modulesHealthy=false,t.missingModules.push("src/__init__.py"));}if(await b__default.pathExists(f))try{let g=await To(f);for(let R of g){let x=h.join(f,R,"__init__.py");await b__default.pathExists(x)||(t.modulesHealthy=false,t.missingModules.push(`modules/${R}/__init__.py`));}}catch{}return !t.modulesHealthy&&t.missingModules.length>0&&t.issues.push(`Missing module init files: ${t.missingModules.join(", ")}`),await Rt(e,t),t}return t.issues.push("Unknown project type (no package.json or pyproject.toml)"),await Rt(e,t),t}async function To(e){try{return (await b__default.readdir(e,{withFileTypes:true})).filter(t=>t.isDirectory()).map(t=>t.name)}catch{try{let o=await b__default.readdir(e),t=[];for(let r of o)try{(await b__default.stat(h.join(e,r))).isDirectory()&&t.push(r);}catch{continue}return t}catch{return []}}}async function Jt(e){let o=h.join(e,".rapidkit");if(!await b__default.pathExists(o))return false;let t=["project.json","context.json","file-hashes.json"];for(let r of t)if(await b__default.pathExists(h.join(o,r)))return true;return false}function Ao(e,o){if(o.has(e))return true;let t=e.toLowerCase();return !!(t==="dist"||t.startsWith("dist-")||t.startsWith("dist_")||t==="build"||t.startsWith("build-")||t.startsWith("build_"))}async function Pn(e,o,t){let r=new Set,i=[{dir:e,depth:0}];for(;i.length>0;){let n=i.shift();if(!n)break;try{let a=await b__default.readdir(n.dir);for(let s of a){if(Ao(s,t))continue;let c=h.join(n.dir,s),p;try{p=await b__default.stat(c);}catch{continue}if(p.isDirectory()){if(await Jt(c)){r.add(c);continue}n.depth<o&&i.push({dir:c,depth:n.depth+1});}}}catch{continue}}return Array.from(r)}async function So(e){let o=e,t=h.parse(o).root;for(;o!==t;){let r=[h.join(o,".rapidkit-workspace"),h.join(o,".rapidkit","workspace-marker.json"),h.join(o,".rapidkit","config.json")];for(let i of r)if(await b__default.pathExists(i))return o;o=h.dirname(o);}return null}function En(e,o){let t=0,r=0,i=0;return e.forEach(a=>{a.status==="ok"?t++:a.status==="warn"?r++:a.status==="error"&&i++;}),o.forEach(a=>{(a.isGoProject?a.issues.length===0&&a.depsInstalled:a.issues.length===0&&a.venvActive&&a.depsInstalled)?t++:a.issues.length>0&&r++;}),{total:t+r+i,passed:t,warnings:r,errors:i}}async function xo(e,o=true){let t=h.basename(e);try{let d=h.join(e,".rapidkit-workspace");await b__default.pathExists(d)&&(t=(await b__default.readJSON(d)).name||t);}catch{try{let d=h.join(e,".rapidkit","config.json");t=(await b__default.readJSON(d)).workspace_name||t;}catch{}}let[r,i]=await Promise.all([Io(),hn(e)]),n={workspacePath:e,workspaceName:t,python:r.python,poetry:r.poetry,pipx:r.pipx,go:r.go,rapidkitCore:r.rapidkitCore,projects:[]};a$1.debug(`Workspace scan found ${i.length} project(s)`);let a=await yn(e,i),s=h.join(e,".rapidkit","reports","doctor-workspace-cache.json"),c=o?await wn(s,a):null;if(c)n.projects=c.projects,n.projectScanCached=true,a$1.debug(`Workspace project health cache hit: ${s}`);else try{let d=await Promise.all(i.map(u=>xn(u)));n.projects=d,n.projectScanCached=false,await vn(s,{signature:a,generatedAt:new Date().toISOString(),projects:d}),a$1.debug(`Workspace project health cache refreshed: ${s}`);}catch(d){a$1.debug(`Failed to scan workspace projects: ${d}`);}n.projectScanSignature=a,n.projectScanCachePath=s;let p=[n.python,n.poetry,n.pipx,n.go,n.rapidkitCore];if(n.healthScore=En(p,n.projects),n.rapidkitCore.status==="ok"){let d=n.rapidkitCore.message.match(/([\d.]+(?:rc\d+)?(?:a\d+)?(?:b\d+)?)/);d&&(n.coreVersion=d[1]);}return n.evidencePath=await kn(e,n,c?s:null),n}function Ce(e,o){let t=e.status==="ok"?"\u2705":e.status==="warn"?"\u26A0\uFE0F":"\u274C",r=e.status==="ok"?l.green:e.status==="warn"?l.yellow:l.red;console.log(`${t} ${l.bold(o)}: ${r(e.message)}`),e.paths&&e.paths.length>0?e.paths.forEach(i=>{let n=i.version?l.cyan(` -> ${i.version}`):"";console.log(` ${l.cyan("\u2022")} ${l.gray(i.location)}: ${l.dim(i.path)}${n}`);}):e.details&&console.log(` ${l.gray(e.details)}`);}function In(e){let o=e.issues.length>0,t=o?"\u26A0\uFE0F":"\u2705",r=o?l.yellow:l.green;if(console.log(`
3367
- ${t} ${l.bold("Project")}: ${r(e.name)}`),e.framework){let c=e.framework==="FastAPI"?"\u{1F40D}":e.framework==="NestJS"?"\u{1F985}":e.framework==="Go/Fiber"||e.framework==="Go/Gin"?"\u{1F439}":"\u{1F4E6}";console.log(` ${c} Framework: ${l.cyan(e.framework)}${e.kit?l.gray(` (${e.kit})`):""}`);}console.log(` ${l.gray(`Path: ${e.path}`)}`);let i=e.isGoProject===true,n=!i&&e.venvActive&&!e.coreInstalled;if(!i&&!n&&(e.venvActive?console.log(` \u2705 Virtual environment: ${l.green("Active")}`):console.log(` \u274C Virtual environment: ${l.red("Not found")}`),e.coreInstalled?console.log(` ${l.dim("\u2139")} RapidKit Core: ${l.gray(e.coreVersion||"In venv")} ${l.dim("(optional)")}`):console.log(` ${l.dim("\u2139")} RapidKit Core: ${l.gray("Using global installation")} ${l.dim("(recommended)")}`)),e.depsInstalled?console.log(` \u2705 Dependencies: ${l.green("Installed")}`):console.log(` \u26A0\uFE0F Dependencies: ${l.yellow("Not installed")}`),e.hasEnvFile!==void 0&&(e.hasEnvFile?console.log(` \u2705 Environment: ${l.green(".env configured")}`):console.log(` \u26A0\uFE0F Environment: ${l.yellow(".env missing")}`)),e.modulesHealthy!==void 0&&(e.modulesHealthy?console.log(` \u2705 Modules: ${l.green("Healthy")}`):e.missingModules&&e.missingModules.length>0&&console.log(` \u26A0\uFE0F Modules: ${l.yellow(`Missing ${e.missingModules.length} init file(s)`)}`)),e.stats){let c=[];e.stats.modules!==void 0&&c.push(`${e.stats.modules} module${e.stats.modules!==1?"s":""}`),c.length>0&&console.log(` \u{1F4CA} Stats: ${l.cyan(c.join(" \u2022 "))}`);}e.lastModified&&console.log(` \u{1F552} Last Modified: ${l.gray(e.lastModified)}`);let s=[];if(e.hasTests!==void 0&&s.push(e.hasTests?"\u2705 Tests":l.dim("\u2298 No tests")),e.hasDocker!==void 0&&s.push(e.hasDocker?"\u2705 Docker":l.dim("\u2298 No Docker")),e.hasCodeQuality!==void 0){let c=e.framework==="NestJS"?"ESLint":e.framework==="Go/Fiber"||e.framework==="Go/Gin"?"golangci-lint":"Ruff";s.push(e.hasCodeQuality?`\u2705 ${c}`:l.dim(`\u2298 No ${c}`));}s.length>0&&console.log(` ${s.join(" \u2022 ")}`),e.vulnerabilities!==void 0&&e.vulnerabilities>0&&console.log(` \u26A0\uFE0F Security: ${l.yellow(`${e.vulnerabilities} vulnerability(ies) found`)}`),e.issues.length>0&&(console.log(` ${l.bold("Issues:")}`),e.issues.forEach(c=>{console.log(` \u2022 ${l.yellow(c)}`);}),e.fixCommands&&e.fixCommands.length>0&&(console.log(`
3368
- ${l.bold.cyan("\u{1F527} Quick Fix:")}`),e.fixCommands.forEach(c=>{console.log(` ${l.cyan("$")} ${l.white(c)}`);})));}async function Po(){try{return (await execa("go",["version"],{timeout:3e3,reject:false})).exitCode===0}catch{return false}}async function Eo(e,o=false){let t=e.filter(p=>p.fixCommands&&p.fixCommands.length>0),r=null;if(t.length===0){console.log(l.green(`
3369
- \u2705 No fixes needed - all projects are healthy!`));return}console.log(l.bold.cyan(`
3370
- \u{1F527} Available Fixes:
3371
- `));for(let p of t)console.log(l.bold(`Project: ${l.yellow(p.name)}`)),p.fixCommands.forEach((d,u)=>{console.log(` ${u+1}. ${l.cyan(d)}`);}),console.log();let i=0;for(let p of t)for(let d of p.fixCommands){if(/^https?:\/\//i.test(d.trim()))continue;if(s(d,"cp\\s+\\.env\\.example\\s+\\.env")||s(d,"copy-item\\s+\\.env\\.example\\s+\\.env")||s(d,"rapidkit\\s+init")){i+=1;continue}if(s(d,"go\\s+mod\\s+tidy")){r===null&&(r=await Po()),r&&(i+=1);continue}i+=1;}if(i===0){console.log(l.gray("\u{1F4A1} No automatic fixes can be applied right now.")),r===false&&console.log(l.gray(" Install Go to enable go mod tidy fixes, then rerun `rapidkit doctor workspace --fix`."));return}if(!o){console.log(l.gray('\u{1F4A1} Run "npx rapidkit doctor workspace --fix" to apply fixes automatically'));return}let{confirm:n}=await xe.prompt([{type:"confirm",name:"confirm",message:`Apply ${t.reduce((p,d)=>p+d.fixCommands.length,0)} fix(es)?`,default:false}]);if(!n){console.log(l.yellow(`
3372
- \u26A0\uFE0F Fixes cancelled by user`));return}console.log(l.bold.cyan(`
3373
- \u{1F680} Applying fixes...
3374
- `));let a=p=>/^https?:\/\//i.test(p.trim());function s(p,d){let u=[new RegExp(`^cd\\s+"([^"]+)"\\s*(?:&&|;)\\s*${d}\\s*$`,"i"),new RegExp(`^cd\\s+'([^']+)'\\s*(?:&&|;)\\s*${d}\\s*$`,"i"),new RegExp(`^cd\\s+(.+?)\\s*(?:&&|;)\\s*${d}\\s*$`,"i")];for(let m of u){let w=p.match(m);if(w?.[1])return {projectPath:w[1].trim()}}return null}function c(p){return s(p,"cp\\s+\\.env\\.example\\s+\\.env")||s(p,"copy-item\\s+\\.env\\.example\\s+\\.env")}for(let p of t){console.log(l.bold(`Fixing ${l.cyan(p.name)}...`));for(let d of p.fixCommands)try{if(console.log(l.gray(` $ ${d}`)),a(d)){console.log(l.yellow(` \u2139 Manual action required: open ${d}`)),console.log(l.green(` \u2705 Recorded as guidance
3375
- `));continue}let u=c(d);if(u){let f=h.join(u.projectPath,".env.example"),g=h.join(u.projectPath,".env");if(!await b__default.pathExists(f))throw new Error(`.env.example not found at ${f}`);if(await b__default.pathExists(g)){console.log(l.green(` \u2705 .env already exists
3376
- `));continue}await b__default.copy(f,g,{overwrite:false,errorOnExist:false}),console.log(l.green(` \u2705 Success
3377
- `));continue}let m=s(d,"rapidkit\\s+init");if(m){await execa("rapidkit",["init"],{cwd:m.projectPath,shell:b$3(),stdio:"inherit"}),console.log(l.green(` \u2705 Success
3378
- `));continue}let w=s(d,"go\\s+mod\\s+tidy");if(w){if(r===null&&(r=await Po()),!r){console.log(l.yellow(" \u26A0 Go toolchain is not installed \u2014 skipping go mod tidy; install Go to apply this fix.")),console.log(l.green(` \u2705 Recorded as guidance
3379
- `));continue}await execa("go",["mod","tidy"],{cwd:w.projectPath,shell:b$3(),stdio:"inherit"}),console.log(l.green(` \u2705 Success
3380
- `));continue}await execa(d,{shell:true,stdio:"inherit"}),console.log(l.green(` \u2705 Success
3381
- `));}catch(u){console.log(l.red(` \u274C Failed: ${u instanceof Error?u.message:String(u)}
3382
- `));}}console.log(l.bold.green(`
3383
- \u2705 Fix process completed!`));}async function Oo(e={}){let o=!e.workspace&&e.fix?await So(process.cwd()):null,t=e.workspace||!!o;if(e.json||console.log(l.bold.cyan(`
3384
- \u{1FA7A} RapidKit Health Check
3385
- `)),t){let r=o??await So(process.cwd());r||(a$1.error("No RapidKit workspace found in current directory or parents"),a$1.info('Run this command from within a workspace, or use "rapidkit doctor" for system check'),process.exit(1)),e.json||(o&&console.log(l.gray("\u2139\uFE0F Detected workspace context; enabling workspace checks for --fix")),console.log(l.bold(`Workspace: ${l.cyan(h.basename(r))}`)),console.log(l.gray(`Path: ${r}`)));let i=await xo(r);if(e.json||(i.projectScanCached&&console.log(l.gray(`\u2139\uFE0F Reused cached project scan${i.projectScanCachePath?` (${h.basename(i.projectScanCachePath)})`:""}`)),i.evidencePath&&console.log(l.gray(`\u2139\uFE0F Evidence saved: ${i.evidencePath}`))),e.json){let s={workspace:{name:h.basename(r),path:r},cache:{projectScan:i.projectScanCached??false,projectScanPath:i.projectScanCachePath,evidencePath:i.evidencePath},healthScore:i.healthScore,system:{python:i.python,poetry:i.poetry,pipx:i.pipx,rapidkitCore:i.rapidkitCore,versions:{core:i.coreVersion,npm:i.npmVersion}},projects:i.projects.map(c=>({name:c.name,path:c.path,venvActive:c.venvActive,depsInstalled:c.depsInstalled,coreInstalled:c.coreInstalled,coreVersion:c.coreVersion,issues:c.issues,fixCommands:c.fixCommands})),summary:{totalProjects:i.projects.length,totalIssues:i.projects.reduce((c,p)=>c+p.issues.length,0),hasSystemErrors:[i.python,i.rapidkitCore].some(c=>c.status==="error")}};console.log(JSON.stringify(s,null,2));return}if(i.healthScore){let s=i.healthScore,c=Math.round(s.passed/s.total*100),p=c>=80?l.green:c>=50?l.yellow:l.red,d="\u2588".repeat(Math.floor(c/5))+"\u2591".repeat(20-Math.floor(c/5));console.log(l.bold(`
3386
- \u{1F4CA} Health Score:`)),console.log(` ${p(`${c}%`)} ${l.gray(d)}`),console.log(` ${l.green(`\u2705 ${s.passed} passed`)} ${l.gray("|")} ${l.yellow(`\u26A0\uFE0F ${s.warnings} warnings`)} ${l.gray("|")} ${l.red(`\u274C ${s.errors} errors`)}`);}if(console.log(l.bold(`
3387
-
3388
- System Tools:
3389
- `)),Ce(i.python,"Python"),Ce(i.poetry,"Poetry"),Ce(i.pipx,"pipx"),Ce(i.go,"Go"),Ce(i.rapidkitCore,"RapidKit Core"),i.coreVersion&&i.npmVersion){let s=i.coreVersion.split(".")[1],c=i.npmVersion.split(".")[1];s!==c&&(console.log(l.yellow(`
3390
- \u26A0\uFE0F Version mismatch: Core ${i.coreVersion} / CLI ${i.npmVersion}`)),console.log(l.gray(" Consider updating to matching versions for best compatibility")));}i.projects.length>0?(console.log(l.bold(`
3391
- \u{1F4E6} Projects (${i.projects.length}):`)),i.projects.forEach(s=>In(s))):(console.log(l.bold(`
3392
- \u{1F4E6} Projects:`)),console.log(l.gray(" No RapidKit projects found in workspace")));let n=i.projects.reduce((s,c)=>s+c.issues.length,0),a=[i.python,i.rapidkitCore].some(s=>s.status==="error");if(a||n>0)if(console.log(l.bold.yellow(`
3393
- \u26A0\uFE0F Found ${n} project issue(s)`)),a&&console.log(l.bold.red("\u274C System requirements not met")),e.fix){if(await Eo(i.projects,true),!e.json){let s=await xo(r,false),c=s.projects.reduce((d,u)=>d+u.issues.length,0),p=[s.python,s.rapidkitCore].some(d=>d.status==="error");p||c>0?(console.log(l.bold.yellow(`
3394
- \u26A0\uFE0F Post-fix verification found ${c} remaining issue(s)`)),p&&console.log(l.bold.red("\u274C System requirements still not met"))):console.log(l.bold.green(`
3395
- \u2705 Post-fix verification passed. Workspace is healthy.`)),s.projectScanCached&&console.log(l.gray(`\u2139\uFE0F Reused cached project scan${s.projectScanCachePath?` (${h.basename(s.projectScanCachePath)})`:""}`)),s.evidencePath&&console.log(l.gray(`\u2139\uFE0F Evidence refreshed: ${s.evidencePath}`));}}else n>0&&await Eo(i.projects,false);else console.log(l.bold.green(`
3396
- \u2705 All checks passed! Workspace is healthy.`));}else {console.log(l.bold(`System Tools:
3397
- `));let r=await Io(),i=r.python,n=r.poetry,a=r.pipx,s=r.go,c=r.rapidkitCore;Ce(i,"Python"),Ce(n,"Poetry"),Ce(a,"pipx"),Ce(s,"Go"),Ce(c,"RapidKit Core"),[i,c].some(d=>d.status==="error")?(console.log(l.bold.red(`
3398
- \u274C Some required tools are missing`)),e.fix&&console.log(l.gray(`
3399
- Tip: Project auto-fix runs in workspace mode. Run from a workspace and use "rapidkit doctor workspace --fix"`)),console.log(l.gray(`
3400
- Tip: Run "rapidkit doctor workspace" for detailed project checks`))):(console.log(l.bold.green(`
3401
- \u2705 All required tools are installed!`)),e.fix&&console.log(l.gray(`
3402
- Tip: Project auto-fix runs in workspace mode. Run from a workspace and use "rapidkit doctor workspace --fix"`)),console.log(l.gray(`
3403
- Tip: Run "rapidkit doctor workspace" for detailed project checks`)));}console.log("");}var Yt=h.join(Tn.homedir(),".rapidkit"),_t=h.join(Yt,"config.json");function ze(){try{if(!_.existsSync(_t))return {};let e=_.readFileSync(_t,"utf-8");return JSON.parse(e)}catch{return {}}}function Ct(e){let t={...ze(),...e};_.existsSync(Yt)||_.mkdirSync(Yt,{recursive:true}),_.writeFileSync(_t,JSON.stringify(t,null,2),"utf-8");}function nt(){return process.env.OPENAI_API_KEY||ze().openaiApiKey||null}function zt(){return ze().aiEnabled!==false}function Qt(){return _t}async function jo(){return (await import('inquirer')).default}function $o(e){let o=e.command("config").description("Configure RapidKit settings");o.command("set-api-key").description("Set OpenAI API key for AI features").option("--key <key>","API key (or enter interactively)").action(async t=>{let r=t.key;r?r.startsWith("sk-")||(console.log(l.red(`
2
+ import {e as e$1,d,c as c$1,h as h$1,a as a$4}from'./chunk-UOGFCKQ5.js';import {a as a$2,b as b$1,c,e,j}from'./chunk-ZAZJEYYT.js';import {a,h,i,c as c$2,d as d$1,b as b$2,f as f$1}from'./chunk-Z5LKRG57.js';import {b,a as a$3}from'./chunk-Q7ULIFQA.js';import {a as a$1}from'./chunk-VM2TOHNX.js';import {Command,Option}from'commander';import s from'chalk';import he from'inquirer';import f from'path';import {fileURLToPath}from'url';import {exec,spawn}from'child_process';import zo from'validate-npm-package-name';import*as x from'fs-extra';import x__default from'fs-extra';import w,{promises,createWriteStream}from'fs';import {execa}from'execa';import rr from'os';import {promisify}from'util';import kr from'ora';import {createVerify,createHash,createHmac}from'crypto';import bo from'http';import Po from'https';function vt(t){let e=zo(t);if(!e.validForNewPackages){let r=e.errors||[],n=e.warnings||[],i=[...r,...n];throw new j(t,`NPM validation failed: ${i.join(", ")}`)}if(!/^[a-z][a-z0-9_-]*$/.test(t))throw new j(t,"Must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores");if(["test","tests","src","dist","build","lib","python","pip","poetry","node","npm","rapidkit","rapidkit"].includes(t.toLowerCase()))throw new j(t,`"${t}" is a reserved name. Please choose a different name.`);if(t.length<2)throw new j(t,"Name must be at least 2 characters long");if(t.length>214)throw new j(t,"Name must be less than 214 characters");return true}function Zo(){return d$1()}function Xo(t,e){return t==="py"?["-3",...e]:e}function er(t){return typeof t=="object"&&t!==null}async function tr(t,e,o,r=8e3){try{let n=await execa(t,e,{cwd:o,timeout:r,reject:false,stdio:"pipe"});return {ok:n.exitCode===0,exitCode:n.exitCode,stdout:n.stdout,stderr:n.stderr}}catch(n){return {ok:false,exitCode:void 0,stdout:"",stderr:n instanceof Error?n.message:String(n)}}}async function or(t,e){let o=["-m","rapidkit",...t],r=Zo();for(let n of r){let i=await tr(n,Xo(n,o),e?.cwd,e?.timeoutMs);if(!i.ok)continue;let a=(i.stdout??"").trim();try{let c=JSON.parse(a);return er(c)?{ok:true,command:n,exitCode:i.exitCode,stdout:i.stdout,stderr:i.stderr,data:c}:{ok:false,command:n,exitCode:i.exitCode,stdout:i.stdout,stderr:i.stderr}}catch{return {ok:false,command:n,exitCode:i.exitCode,stdout:i.stdout,stderr:i.stderr}}}return {ok:false}}async function Ut(t,e){let o=await or(["project","detect","--path",t,"--json"],e);return !o.ok||!o.data||o.data.schema_version!==1?{ok:false,command:o.command,exitCode:o.exitCode,stdout:o.stdout,stderr:o.stderr}:o}var bt=f.join(rr.homedir(),".rapidkit"),et=f.join(bt,"config.json");function Te(){try{if(!w.existsSync(et))return {};let t=w.readFileSync(et,"utf-8");return JSON.parse(t)}catch{return {}}}function tt(t){let o={...Te(),...t};w.existsSync(bt)||w.mkdirSync(bt,{recursive:true}),w.writeFileSync(et,JSON.stringify(o,null,2),"utf-8");}function Fe(){return process.env.OPENAI_API_KEY||Te().openaiApiKey||null}function Pt(){return Te().aiEnabled!==false}function Ct(){return et}async function Jt(){return (await import('inquirer')).default}function Vt(t){let e=t.command("config").description("Configure RapidKit settings");e.command("set-api-key").description("Set OpenAI API key for AI features").option("--key <key>","API key (or enter interactively)").action(async o=>{let r=o.key;r?r.startsWith("sk-")||(console.log(s.red(`
3404
3
  \u274C Invalid API key format (should start with sk-)
3405
- `)),process.exit(1)):r=(await(await jo()).prompt([{type:"password",name:"apiKey",message:"Enter your OpenAI API key:",validate:a=>a?a.startsWith("sk-")?a.length<20?"API key seems too short":true:"Invalid API key format (should start with sk-)":"API key is required"}])).apiKey,Ct({openaiApiKey:r}),console.log(l.green(`
4
+ `)),process.exit(1)):r=(await(await Jt()).prompt([{type:"password",name:"apiKey",message:"Enter your OpenAI API key:",validate:a=>a?a.startsWith("sk-")?a.length<20?"API key seems too short":true:"Invalid API key format (should start with sk-)":"API key is required"}])).apiKey,tt({openaiApiKey:r}),console.log(s.green(`
3406
5
  \u2705 OpenAI API key saved successfully!
3407
- `)),console.log(l.gray(`Stored in: ${Qt()}`)),console.log(l.cyan(`
3408
- \u{1F389} You can now use AI features:`)),console.log(l.white(' rapidkit ai recommend "I need user authentication"')),console.log(l.gray(`
3409
- \u{1F4A1} To generate module embeddings (one-time):`)),console.log(l.white(" cd rapidkit-npm")),console.log(l.white(` npx tsx src/ai/generate-embeddings.ts
3410
- `));}),o.command("show").description("Show current configuration").action(()=>{let t=ze();if(console.log(l.bold(`
6
+ `)),console.log(s.gray(`Stored in: ${Ct()}`)),console.log(s.cyan(`
7
+ \u{1F389} You can now use AI features:`)),console.log(s.white(' rapidkit ai recommend "I need user authentication"')),console.log(s.gray(`
8
+ \u{1F4A1} To generate module embeddings (one-time):`)),console.log(s.white(" cd rapidkit-npm")),console.log(s.white(` npx tsx src/ai/generate-embeddings.ts
9
+ `));}),e.command("show").description("Show current configuration").action(()=>{let o=Te();if(console.log(s.bold(`
3411
10
  \u2699\uFE0F RapidKit Configuration
3412
- `)),t.openaiApiKey){let r=t.openaiApiKey.substring(0,8)+"..."+t.openaiApiKey.slice(-4);console.log(l.cyan("OpenAI API Key:"),l.white(r));}else console.log(l.cyan("OpenAI API Key:"),l.red("Not set")),console.log(l.gray(" Set with: rapidkit config set-api-key"));console.log(l.cyan("AI Features:"),t.aiEnabled!==false?l.green("Enabled"):l.red("Disabled")),console.log(l.gray(`
3413
- \u{1F4C1} Config file: ${Qt()}
3414
- `));}),o.command("remove-api-key").description("Remove stored OpenAI API key").action(async()=>{if(!ze().openaiApiKey){console.log(l.yellow(`
11
+ `)),o.openaiApiKey){let r=o.openaiApiKey.substring(0,8)+"..."+o.openaiApiKey.slice(-4);console.log(s.cyan("OpenAI API Key:"),s.white(r));}else console.log(s.cyan("OpenAI API Key:"),s.red("Not set")),console.log(s.gray(" Set with: rapidkit config set-api-key"));console.log(s.cyan("AI Features:"),o.aiEnabled!==false?s.green("Enabled"):s.red("Disabled")),console.log(s.gray(`
12
+ \u{1F4C1} Config file: ${Ct()}
13
+ `));}),e.command("remove-api-key").description("Remove stored OpenAI API key").action(async()=>{if(!Te().openaiApiKey){console.log(s.yellow(`
3415
14
  \u26A0\uFE0F No API key is currently stored
3416
- `));return}(await(await jo()).prompt([{type:"confirm",name:"confirm",message:"Are you sure you want to remove your OpenAI API key?",default:false}])).confirm?(Ct({openaiApiKey:void 0}),console.log(l.green(`
15
+ `));return}(await(await Jt()).prompt([{type:"confirm",name:"confirm",message:"Are you sure you want to remove your OpenAI API key?",default:false}])).confirm?(tt({openaiApiKey:void 0}),console.log(s.green(`
3417
16
  \u2705 API key removed successfully
3418
- `))):console.log(l.gray(`
17
+ `))):console.log(s.gray(`
3419
18
  Cancelled
3420
- `));}),o.command("ai <action>").description("Enable or disable AI features (enable|disable)").action(t=>{t!=="enable"&&t!=="disable"&&(console.log(l.red(`
3421
- \u274C Invalid action: ${t}`)),console.log(l.gray(`Use: rapidkit config ai enable|disable
3422
- `)),process.exit(1));let r=t==="enable";Ct({aiEnabled:r}),console.log(l.green(`
19
+ `));}),e.command("ai <action>").description("Enable or disable AI features (enable|disable)").action(o=>{o!=="enable"&&o!=="disable"&&(console.log(s.red(`
20
+ \u274C Invalid action: ${o}`)),console.log(s.gray(`Use: rapidkit config ai enable|disable
21
+ `)),process.exit(1));let r=o==="enable";tt({aiEnabled:r}),console.log(s.green(`
3423
22
  \u2705 AI features ${r?"enabled":"disabled"}
3424
- `));});}var St=null,xt=false,Xt=null;async function An(){return Xt||(Xt=(await import('openai')).default),Xt}function Do(){xt=true;}function Go(e){let t=new Array(1536),r=0;for(let n=0;n<e.length;n++)r=(r<<5)-r+e.charCodeAt(n),r=r&r;for(let n=0;n<1536;n++)r=r*1664525+1013904223&4294967295,t[n]=r/4294967295*2-1;let i=Math.sqrt(t.reduce((n,a)=>n+a*a,0));return t.map(n=>n/i)}async function Pt(e){let o=await An();St=new o({apiKey:e});}function Mo(){if(!St)throw new Error("OpenAI client not initialized. Call initOpenAI() first with your API key.");return St}async function Lo(e){return xt?Go(e):(await Mo().embeddings.create({model:"text-embedding-3-small",input:e,encoding_format:"float"})).data[0].embedding}async function qo(e){return xt?e.map(Go):(await Mo().embeddings.create({model:"text-embedding-3-small",input:e,encoding_format:"float"})).data.map(r=>r.embedding)}function Fo(){return St!==null}function Ho(){return xt}var jn=promisify(exec),Wo=[{id:"authentication-core",name:"Authentication Core",category:"auth",description:"Complete authentication system with password hashing, JWT tokens, OAuth 2.0, and secure session management",longDescription:"Production-ready authentication with bcrypt password hashing, JWT access/refresh tokens, OAuth 2.0 providers (Google, GitHub, etc), rate limiting, and security best practices.",keywords:["auth","login","password","jwt","oauth","token","authentication","security","signin","signup"],framework:"both",dependencies:[],useCases:["User login and logout","Password reset flow","OAuth social login (Google, GitHub)","JWT authentication","Secure session management","Token refresh","Rate limiting"]},{id:"users-core",name:"Users Core",category:"auth",description:"User management system with profiles, roles, permissions, and user CRUD operations",longDescription:"Complete user management with user profiles, role-based access control (RBAC), permissions, user search, soft delete, and audit trails.",keywords:["user","profile","role","permission","rbac","management","admin","accounts"],framework:"both",dependencies:["authentication-core"],useCases:["User registration","User profile management","Role management (admin, user, etc)","Permission system","User administration dashboard","Soft delete users"]},{id:"session-management",name:"Session Management",category:"auth",description:"Secure session handling with Redis storage, session rotation, and device tracking",longDescription:"Advanced session management with Redis-backed storage, automatic session rotation, device fingerprinting, IP tracking, and session revocation.",keywords:["session","redis","cookie","storage","device","tracking"],framework:"both",dependencies:["authentication-core","redis-cache"],useCases:["User session management","Remember me functionality","Device tracking","Session security","Logout from all devices","Session expiration"]},{id:"db-postgres",name:"PostgreSQL",category:"database",description:"PostgreSQL integration with async SQLAlchemy, migrations, connection pooling, and query optimization",longDescription:"Production-ready PostgreSQL with async SQLAlchemy 2.0, Alembic migrations, connection pooling, query optimization, JSON support, and full-text search.",keywords:["postgres","postgresql","database","sql","sqlalchemy","migration","orm","relational"],framework:"both",dependencies:[],useCases:["Relational database","Complex SQL queries","Database transactions","Data integrity","Production-grade database","ACID compliance"]},{id:"db-mongodb",name:"MongoDB",category:"database",description:"MongoDB integration with Motor async driver, schema validation, and aggregation pipelines",longDescription:"Async MongoDB with Motor driver, Pydantic schema validation, aggregation pipelines, indexes, and Atlas integration.",keywords:["mongodb","mongo","nosql","document","database","motor"],framework:"both",dependencies:[],useCases:["Document storage","Flexible schema","Real-time data","JSON documents","Unstructured data","Analytics"]},{id:"stripe-payment",name:"Stripe Payment",category:"payment",description:"Stripe integration with payment intents, subscriptions, webhooks, and customer portal",longDescription:"Complete Stripe integration with Payment Intents API, subscription management, automatic webhooks, customer portal, refunds, and SCA compliance.",keywords:["stripe","payment","subscription","billing","checkout","webhook","credit card"],framework:"both",dependencies:[],useCases:["Accept credit card payments","Subscription billing","One-time payments","Checkout flow","Payment webhooks","Refunds and disputes"]},{id:"email",name:"Email",category:"communication",description:"Email sending with templates, SMTP/SendGrid/AWS SES support, and queue management",longDescription:"Production email system with Jinja2 templates, multiple providers (SMTP, SendGrid, AWS SES), queue management, retry logic, and bounce handling.",keywords:["email","mail","smtp","sendgrid","ses","template","notification"],framework:"both",dependencies:[],useCases:["Welcome emails","Password reset emails","Notifications","Marketing emails","Transactional emails","Email templates"]},{id:"sms",name:"SMS",category:"communication",description:"SMS sending with Twilio, verification codes, and delivery tracking",longDescription:"SMS integration with Twilio, verification codes, two-factor authentication, delivery tracking, and international support.",keywords:["sms","twilio","text","message","2fa","verification","otp"],framework:"both",dependencies:[],useCases:["2FA verification codes","SMS notifications","Phone verification","OTP generation","SMS alerts"]},{id:"redis-cache",name:"Redis Cache",category:"infrastructure",description:"Redis caching with decorators, TTL management, and cache invalidation patterns",longDescription:"Redis integration with async client, caching decorators, TTL management, cache invalidation, pub/sub, and rate limiting.",keywords:["redis","cache","memory","performance","speed","pubsub"],framework:"both",dependencies:[],useCases:["API response caching","Session storage","Rate limiting","Real-time features","Performance optimization","Pub/sub messaging"]},{id:"celery",name:"Celery",category:"infrastructure",description:"Background task processing with Celery, periodic tasks, and monitoring",longDescription:"Celery task queue with Redis/RabbitMQ backend, periodic tasks (cron), task monitoring, retry logic, and failure handling.",keywords:["celery","task","background","queue","async","worker","job","cron"],framework:"fastapi",dependencies:["redis-cache"],useCases:["Background email sending","Data processing","Report generation","Scheduled tasks","Long-running jobs"]},{id:"storage",name:"Storage",category:"infrastructure",description:"File storage with S3, local filesystem, and image processing",longDescription:"Unified storage interface for AWS S3, local files, image resizing, format conversion, CDN integration, and presigned URLs.",keywords:["storage","s3","file","upload","image","cdn","aws"],framework:"both",dependencies:[],useCases:["File uploads","Image storage","Document management","Profile pictures","Media files","CDN integration"]}],Qe=null,Ko=0,$n=300*1e3;function Dn(e){return {id:e.name||e.id||e.module_id||"",name:e.display_name||e.name||"",category:Gn(e.category||"infrastructure"),description:e.description||e.summary||"",longDescription:e.long_description||e.description||"",keywords:e.keywords||e.tags||[],framework:Mn(e.framework),dependencies:e.dependencies||[],useCases:e.use_cases||e.useCases||[]}}function Gn(e){return {auth:"auth",authentication:"auth",database:"database",payment:"payment",billing:"payment",communication:"communication",infrastructure:"infrastructure",security:"security",analytics:"analytics"}[e.toLowerCase()]||"infrastructure"}function Mn(e){if(!e)return "both";if(typeof e=="string"){if(e.toLowerCase().includes("fastapi"))return "fastapi";if(e.toLowerCase().includes("nest"))return "nestjs"}return "both"}async function Ln(){try{let{stdout:e}=await jn("rapidkit modules list --json-schema 1",{timeout:1e4,maxBuffer:10485760}),o=e.match(/\{[\s\S]*\}/),t=o?o[0]:e,r=JSON.parse(t),i=[];return Array.isArray(r)?i=r:r.modules&&Array.isArray(r.modules)?i=r.modules:r.data&&Array.isArray(r.data)&&(i=r.data),i.map(Dn).filter(n=>n.id&&n.name)}catch(e){return e.code==="ENOENT"?console.warn("\u26A0\uFE0F RapidKit Python Core not found in PATH"):e.killed?console.warn("\u26A0\uFE0F Python Core command timed out"):console.warn("\u26A0\uFE0F Failed to fetch modules from Python Core:",e.message),console.warn(" Using fallback module catalog (11 modules)"),Wo}}async function Et(){let e=Date.now();return Qe&&e-Ko<$n||(Qe=await Ln(),Ko=e,Qe.length===0&&(console.warn("\u26A0\uFE0F No modules found, using fallback catalog"),Qe=Wo)),Qe}var Fn=fileURLToPath(import.meta.url),Vo=h.dirname(Fn),st=null;function Hn(){if(st)return st;let e=[h.join(Vo,"../../data/modules-embeddings.json"),h.join(Vo,"../data/modules-embeddings.json"),h.join(process.cwd(),"data/modules-embeddings.json")],o=null;for(let i of e)if(_.existsSync(i)){o=i;break}if(!o)throw new Error("embeddings file not found");let t=_.readFileSync(o,"utf-8"),r=JSON.parse(t);return Array.isArray(r)?st={model:"mock-or-text-embedding-3-small",dimension:r[0]?.embedding?.length||1536,generated_at:new Date().toISOString(),modules:r}:st=r,st}function Kn(e,o){if(e.length!==o.length)throw new Error("Vectors must have the same length");let t=0,r=0,i=0;for(let a=0;a<e.length;a++)t+=e[a]*o[a],r+=e[a]*e[a],i+=o[a]*o[a];let n=Math.sqrt(r)*Math.sqrt(i);return n===0?0:t/n}function Wn(e,o){let t=o.toLowerCase(),r=e.keywords.filter(i=>t.includes(i)||i.includes(t));return r.length>0?`Matches: ${r.slice(0,3).join(", ")}`:`Relevant for: ${e.useCases[0]}`}async function Bo(e,o=5){let t=Hn(),r=await Et(),i=await Lo(e),n=t.modules.map(a=>{let s=r.find(p=>p.id===a.id);if(!s)return null;let c=Kn(i,a.embedding);return {module:s,score:c,reason:Wn(s,e)}}).filter(a=>a!==null);return n.sort((a,s)=>s.score-a.score),n.slice(0,o)}var Bn=fileURLToPath(import.meta.url),Jo=h.dirname(Bn);async function Yo(){return (await import('inquirer')).default}function Jn(){return [h.join(Jo,"../../data/modules-embeddings.json"),h.join(Jo,"../data/modules-embeddings.json"),h.join(process.cwd(),"data/modules-embeddings.json")]}function zo(){let e=Jn();for(let o of e)if(_.existsSync(o))try{let t=JSON.parse(_.readFileSync(o,"utf-8")),r=Array.isArray(t)?t:t.modules||[];return {exists:true,path:o,moduleCount:r.length,generatedAt:t.generated_at||null}}catch{continue}return {exists:false,path:null,moduleCount:0,generatedAt:null}}async function Tt(e=true,o){try{if(!Fo()&&!Ho())return console.log(l.red(`
3425
- \u274C OpenAI not initialized`)),console.log(l.yellow("Please set your API key:")),console.log(l.white(" rapidkit config set-api-key")),console.log(l.gray(` OR set: export OPENAI_API_KEY="sk-..."
3426
- `)),false;console.log(l.blue(`
23
+ `));});}var ot=null,rt=false,_t=null;async function nr(){return _t||(_t=(await import('openai')).default),_t}function Bt(){rt=true;}function Yt(t){let o=new Array(1536),r=0;for(let i=0;i<t.length;i++)r=(r<<5)-r+t.charCodeAt(i),r=r&r;for(let i=0;i<1536;i++)r=r*1664525+1013904223&4294967295,o[i]=r/4294967295*2-1;let n=Math.sqrt(o.reduce((i,a)=>i+a*a,0));return o.map(i=>i/n)}async function nt(t){let e=await nr();ot=new e({apiKey:t});}function zt(){if(!ot)throw new Error("OpenAI client not initialized. Call initOpenAI() first with your API key.");return ot}async function Qt(t){return rt?Yt(t):(await zt().embeddings.create({model:"text-embedding-3-small",input:t,encoding_format:"float"})).data[0].embedding}async function Zt(t){return rt?t.map(Yt):(await zt().embeddings.create({model:"text-embedding-3-small",input:t,encoding_format:"float"})).data.map(r=>r.embedding)}function Xt(){return ot!==null}function eo(){return rt}var ar=promisify(exec),oo=[{id:"authentication-core",name:"Authentication Core",category:"auth",description:"Complete authentication system with password hashing, JWT tokens, OAuth 2.0, and secure session management",longDescription:"Production-ready authentication with bcrypt password hashing, JWT access/refresh tokens, OAuth 2.0 providers (Google, GitHub, etc), rate limiting, and security best practices.",keywords:["auth","login","password","jwt","oauth","token","authentication","security","signin","signup"],framework:"both",dependencies:[],useCases:["User login and logout","Password reset flow","OAuth social login (Google, GitHub)","JWT authentication","Secure session management","Token refresh","Rate limiting"]},{id:"users-core",name:"Users Core",category:"auth",description:"User management system with profiles, roles, permissions, and user CRUD operations",longDescription:"Complete user management with user profiles, role-based access control (RBAC), permissions, user search, soft delete, and audit trails.",keywords:["user","profile","role","permission","rbac","management","admin","accounts"],framework:"both",dependencies:["authentication-core"],useCases:["User registration","User profile management","Role management (admin, user, etc)","Permission system","User administration dashboard","Soft delete users"]},{id:"session-management",name:"Session Management",category:"auth",description:"Secure session handling with Redis storage, session rotation, and device tracking",longDescription:"Advanced session management with Redis-backed storage, automatic session rotation, device fingerprinting, IP tracking, and session revocation.",keywords:["session","redis","cookie","storage","device","tracking"],framework:"both",dependencies:["authentication-core","redis-cache"],useCases:["User session management","Remember me functionality","Device tracking","Session security","Logout from all devices","Session expiration"]},{id:"db-postgres",name:"PostgreSQL",category:"database",description:"PostgreSQL integration with async SQLAlchemy, migrations, connection pooling, and query optimization",longDescription:"Production-ready PostgreSQL with async SQLAlchemy 2.0, Alembic migrations, connection pooling, query optimization, JSON support, and full-text search.",keywords:["postgres","postgresql","database","sql","sqlalchemy","migration","orm","relational"],framework:"both",dependencies:[],useCases:["Relational database","Complex SQL queries","Database transactions","Data integrity","Production-grade database","ACID compliance"]},{id:"db-mongodb",name:"MongoDB",category:"database",description:"MongoDB integration with Motor async driver, schema validation, and aggregation pipelines",longDescription:"Async MongoDB with Motor driver, Pydantic schema validation, aggregation pipelines, indexes, and Atlas integration.",keywords:["mongodb","mongo","nosql","document","database","motor"],framework:"both",dependencies:[],useCases:["Document storage","Flexible schema","Real-time data","JSON documents","Unstructured data","Analytics"]},{id:"stripe-payment",name:"Stripe Payment",category:"payment",description:"Stripe integration with payment intents, subscriptions, webhooks, and customer portal",longDescription:"Complete Stripe integration with Payment Intents API, subscription management, automatic webhooks, customer portal, refunds, and SCA compliance.",keywords:["stripe","payment","subscription","billing","checkout","webhook","credit card"],framework:"both",dependencies:[],useCases:["Accept credit card payments","Subscription billing","One-time payments","Checkout flow","Payment webhooks","Refunds and disputes"]},{id:"email",name:"Email",category:"communication",description:"Email sending with templates, SMTP/SendGrid/AWS SES support, and queue management",longDescription:"Production email system with Jinja2 templates, multiple providers (SMTP, SendGrid, AWS SES), queue management, retry logic, and bounce handling.",keywords:["email","mail","smtp","sendgrid","ses","template","notification"],framework:"both",dependencies:[],useCases:["Welcome emails","Password reset emails","Notifications","Marketing emails","Transactional emails","Email templates"]},{id:"sms",name:"SMS",category:"communication",description:"SMS sending with Twilio, verification codes, and delivery tracking",longDescription:"SMS integration with Twilio, verification codes, two-factor authentication, delivery tracking, and international support.",keywords:["sms","twilio","text","message","2fa","verification","otp"],framework:"both",dependencies:[],useCases:["2FA verification codes","SMS notifications","Phone verification","OTP generation","SMS alerts"]},{id:"redis-cache",name:"Redis Cache",category:"infrastructure",description:"Redis caching with decorators, TTL management, and cache invalidation patterns",longDescription:"Redis integration with async client, caching decorators, TTL management, cache invalidation, pub/sub, and rate limiting.",keywords:["redis","cache","memory","performance","speed","pubsub"],framework:"both",dependencies:[],useCases:["API response caching","Session storage","Rate limiting","Real-time features","Performance optimization","Pub/sub messaging"]},{id:"celery",name:"Celery",category:"infrastructure",description:"Background task processing with Celery, periodic tasks, and monitoring",longDescription:"Celery task queue with Redis/RabbitMQ backend, periodic tasks (cron), task monitoring, retry logic, and failure handling.",keywords:["celery","task","background","queue","async","worker","job","cron"],framework:"fastapi",dependencies:["redis-cache"],useCases:["Background email sending","Data processing","Report generation","Scheduled tasks","Long-running jobs"]},{id:"storage",name:"Storage",category:"infrastructure",description:"File storage with S3, local filesystem, and image processing",longDescription:"Unified storage interface for AWS S3, local files, image resizing, format conversion, CDN integration, and presigned URLs.",keywords:["storage","s3","file","upload","image","cdn","aws"],framework:"both",dependencies:[],useCases:["File uploads","Image storage","Document management","Profile pictures","Media files","CDN integration"]}],Oe=null,to=0,cr=300*1e3;function lr(t){return {id:t.name||t.id||t.module_id||"",name:t.display_name||t.name||"",category:dr(t.category||"infrastructure"),description:t.description||t.summary||"",longDescription:t.long_description||t.description||"",keywords:t.keywords||t.tags||[],framework:pr(t.framework),dependencies:t.dependencies||[],useCases:t.use_cases||t.useCases||[]}}function dr(t){return {auth:"auth",authentication:"auth",database:"database",payment:"payment",billing:"payment",communication:"communication",infrastructure:"infrastructure",security:"security",analytics:"analytics"}[t.toLowerCase()]||"infrastructure"}function pr(t){if(!t)return "both";if(typeof t=="string"){if(t.toLowerCase().includes("fastapi"))return "fastapi";if(t.toLowerCase().includes("nest"))return "nestjs"}return "both"}async function ur(){try{let{stdout:t}=await ar("rapidkit modules list --json-schema 1",{timeout:1e4,maxBuffer:10485760}),e=t.match(/\{[\s\S]*\}/),o=e?e[0]:t,r=JSON.parse(o),n=[];return Array.isArray(r)?n=r:r.modules&&Array.isArray(r.modules)?n=r.modules:r.data&&Array.isArray(r.data)&&(n=r.data),n.map(lr).filter(i=>i.id&&i.name)}catch(t){return t.code==="ENOENT"?console.warn("\u26A0\uFE0F RapidKit Python Core not found in PATH"):t.killed?console.warn("\u26A0\uFE0F Python Core command timed out"):console.warn("\u26A0\uFE0F Failed to fetch modules from Python Core:",t.message),console.warn(" Using fallback module catalog (11 modules)"),oo}}async function it(){let t=Date.now();return Oe&&t-to<cr||(Oe=await ur(),to=t,Oe.length===0&&(console.warn("\u26A0\uFE0F No modules found, using fallback catalog"),Oe=oo)),Oe}var fr=fileURLToPath(import.meta.url),no=f.dirname(fr),We=null;function gr(){if(We)return We;let t=[f.join(no,"../../data/modules-embeddings.json"),f.join(no,"../data/modules-embeddings.json"),f.join(process.cwd(),"data/modules-embeddings.json")],e=null;for(let n of t)if(w.existsSync(n)){e=n;break}if(!e)throw new Error("embeddings file not found");let o=w.readFileSync(e,"utf-8"),r=JSON.parse(o);return Array.isArray(r)?We={model:"mock-or-text-embedding-3-small",dimension:r[0]?.embedding?.length||1536,generated_at:new Date().toISOString(),modules:r}:We=r,We}function yr(t,e){if(t.length!==e.length)throw new Error("Vectors must have the same length");let o=0,r=0,n=0;for(let a=0;a<t.length;a++)o+=t[a]*e[a],r+=t[a]*t[a],n+=e[a]*e[a];let i=Math.sqrt(r)*Math.sqrt(n);return i===0?0:o/i}function hr(t,e){let o=e.toLowerCase(),r=t.keywords.filter(n=>o.includes(n)||n.includes(o));return r.length>0?`Matches: ${r.slice(0,3).join(", ")}`:`Relevant for: ${t.useCases[0]}`}async function io(t,e=5){let o=gr(),r=await it(),n=await Qt(t),i=o.modules.map(a=>{let c=r.find(d=>d.id===a.id);if(!c)return null;let m=yr(n,a.embedding);return {module:c,score:m,reason:hr(c,t)}}).filter(a=>a!==null);return i.sort((a,c)=>c.score-a.score),i.slice(0,e)}var vr=fileURLToPath(import.meta.url),so=f.dirname(vr);async function ao(){return (await import('inquirer')).default}function br(){return [f.join(so,"../../data/modules-embeddings.json"),f.join(so,"../data/modules-embeddings.json"),f.join(process.cwd(),"data/modules-embeddings.json")]}function co(){let t=br();for(let e of t)if(w.existsSync(e))try{let o=JSON.parse(w.readFileSync(e,"utf-8")),r=Array.isArray(o)?o:o.modules||[];return {exists:true,path:e,moduleCount:r.length,generatedAt:o.generated_at||null}}catch{continue}return {exists:false,path:null,moduleCount:0,generatedAt:null}}async function at(t=true,e){try{if(!Xt()&&!eo())return console.log(s.red(`
24
+ \u274C OpenAI not initialized`)),console.log(s.yellow("Please set your API key:")),console.log(s.white(" rapidkit config set-api-key")),console.log(s.gray(` OR set: export OPENAI_API_KEY="sk-..."
25
+ `)),false;console.log(s.blue(`
3427
26
  \u{1F916} Generating AI embeddings for RapidKit modules...
3428
- `)),console.log(l.gray("\u{1F4E1} Fetching modules from RapidKit..."));let t=await Et();console.log(l.green(`\u2713 Found ${t.length} modules
3429
- `));let r=t.length*50/1e6*.02;if(console.log(l.cyan(`\u{1F4B0} Estimated cost: ~$${r.toFixed(3)}`)),console.log(l.gray(` (Based on ${t.length} modules at $0.02/1M tokens)
3430
- `)),e){let a=await Yo(),{confirm:s}=await a.prompt([{type:"confirm",name:"confirm",message:"Generate embeddings now?",default:true}]);if(!s)return console.log(l.yellow(`
27
+ `)),console.log(s.gray("\u{1F4E1} Fetching modules from RapidKit..."));let o=await it();console.log(s.green(`\u2713 Found ${o.length} modules
28
+ `));let r=o.length*50/1e6*.02;if(console.log(s.cyan(`\u{1F4B0} Estimated cost: ~$${r.toFixed(3)}`)),console.log(s.gray(` (Based on ${o.length} modules at $0.02/1M tokens)
29
+ `)),t){let a=await ao(),{confirm:c}=await a.prompt([{type:"confirm",name:"confirm",message:"Generate embeddings now?",default:true}]);if(!c)return console.log(s.yellow(`
3431
30
  \u26A0\uFE0F Embeddings generation cancelled
3432
- `)),false}let i=t.map(a=>`${a.name}. ${a.description}. ${a.longDescription}. Keywords: ${a.keywords.join(", ")}. Use cases: ${a.useCases.join(", ")}.`),n=Ut(`Generating embeddings for ${t.length} modules...`).start();try{let a=await qo(i);n.succeed(`Generated embeddings for ${t.length} modules`);let s={model:"text-embedding-3-small",dimension:a[0].length,generated_at:new Date().toISOString(),modules:t.map((d,u)=>({id:d.id,name:d.name,embedding:a[u]}))},c=o||h.join(process.cwd(),"data","modules-embeddings.json"),p=h.dirname(c);return _.existsSync(p)||_.mkdirSync(p,{recursive:true}),_.writeFileSync(c,JSON.stringify(s,null,2)),console.log(l.green(`
3433
- \u2705 Embeddings generated successfully!`)),console.log(l.gray(`\u{1F4C1} Saved to: ${c}`)),console.log(l.gray(`\u{1F4CA} Size: ${t.length} modules, ${a[0].length} dimensions
3434
- `)),true}catch(a){return n.fail("Failed to generate embeddings"),a.message?.includes("429")?(console.log(l.red(`
3435
- \u274C OpenAI API quota exceeded`)),console.log(l.yellow(`Please check your billing: https://platform.openai.com/account/billing
3436
- `))):a.message?.includes("401")?(console.log(l.red(`
3437
- \u274C Invalid API key`)),console.log(l.yellow("Please set a valid API key:")),console.log(l.white(` rapidkit config set-api-key
3438
- `))):console.log(l.red(`
31
+ `)),false}let n=o.map(a=>`${a.name}. ${a.description}. ${a.longDescription}. Keywords: ${a.keywords.join(", ")}. Use cases: ${a.useCases.join(", ")}.`),i=kr(`Generating embeddings for ${o.length} modules...`).start();try{let a=await Zt(n);i.succeed(`Generated embeddings for ${o.length} modules`);let c={model:"text-embedding-3-small",dimension:a[0].length,generated_at:new Date().toISOString(),modules:o.map((l,u)=>({id:l.id,name:l.name,embedding:a[u]}))},m=e||f.join(process.cwd(),"data","modules-embeddings.json"),d=f.dirname(m);return w.existsSync(d)||w.mkdirSync(d,{recursive:true}),w.writeFileSync(m,JSON.stringify(c,null,2)),console.log(s.green(`
32
+ \u2705 Embeddings generated successfully!`)),console.log(s.gray(`\u{1F4C1} Saved to: ${m}`)),console.log(s.gray(`\u{1F4CA} Size: ${o.length} modules, ${a[0].length} dimensions
33
+ `)),true}catch(a){return i.fail("Failed to generate embeddings"),a.message?.includes("429")?(console.log(s.red(`
34
+ \u274C OpenAI API quota exceeded`)),console.log(s.yellow(`Please check your billing: https://platform.openai.com/account/billing
35
+ `))):a.message?.includes("401")?(console.log(s.red(`
36
+ \u274C Invalid API key`)),console.log(s.yellow("Please set a valid API key:")),console.log(s.white(` rapidkit config set-api-key
37
+ `))):console.log(s.red(`
3439
38
  \u274C Error: ${a.message}
3440
- `)),false}}catch(t){return console.log(l.red(`
3441
- \u274C Failed to generate embeddings: ${t.message}
3442
- `)),false}}async function Qo(e=true){if(zo().exists)return true;if(console.log(l.yellow(`
3443
- \u26A0\uFE0F Module embeddings not found`)),console.log(l.gray(`AI recommendations require embeddings to be generated.
3444
- `)),!e)return console.log(l.red("\u274C Cannot generate embeddings in non-interactive mode")),console.log(l.white(`Run: rapidkit ai generate-embeddings
3445
- `)),false;let t=await Yo(),{action:r}=await t.prompt([{type:"list",name:"action",message:"What would you like to do?",choices:[{name:"\u{1F680} Generate embeddings now (requires OpenAI API key)",value:"generate"},{name:"\u{1F4DD} Show me how to generate them manually",value:"manual"},{name:"\u274C Cancel",value:"cancel"}]}]);return r==="generate"?await Tt(true):(r==="manual"&&(console.log(l.cyan(`
39
+ `)),false}}catch(o){return console.log(s.red(`
40
+ \u274C Failed to generate embeddings: ${o.message}
41
+ `)),false}}async function lo(t=true){if(co().exists)return true;if(console.log(s.yellow(`
42
+ \u26A0\uFE0F Module embeddings not found`)),console.log(s.gray(`AI recommendations require embeddings to be generated.
43
+ `)),!t)return console.log(s.red("\u274C Cannot generate embeddings in non-interactive mode")),console.log(s.white(`Run: rapidkit ai generate-embeddings
44
+ `)),false;let o=await ao(),{action:r}=await o.prompt([{type:"list",name:"action",message:"What would you like to do?",choices:[{name:"\u{1F680} Generate embeddings now (requires OpenAI API key)",value:"generate"},{name:"\u{1F4DD} Show me how to generate them manually",value:"manual"},{name:"\u274C Cancel",value:"cancel"}]}]);return r==="generate"?await at(true):(r==="manual"&&(console.log(s.cyan(`
3446
45
  \u{1F4DD} To generate embeddings manually:
3447
- `)),console.log(l.white("1. Get OpenAI API key from: https://platform.openai.com/api-keys")),console.log(l.white("2. Set the API key:")),console.log(l.gray(" rapidkit config set-api-key")),console.log(l.gray(` OR: export OPENAI_API_KEY="sk-..."
3448
- `)),console.log(l.white("3. Generate embeddings:")),console.log(l.gray(` rapidkit ai generate-embeddings
3449
- `)),console.log(l.cyan(`\u{1F4B0} Cost: ~$0.50 one-time
3450
- `))),false)}async function Xo(){let e=zo();return e.exists?(console.log(l.blue(`
3451
- \u{1F504} Updating embeddings...`)),console.log(l.gray(`Current: ${e.moduleCount} modules`)),console.log(l.gray(`Generated: ${e.generatedAt||"unknown"}
3452
- `)),await Tt(true,e.path)):(console.log(l.yellow(`
3453
- \u26A0\uFE0F No existing embeddings found`)),console.log(l.gray(`Use: rapidkit ai generate-embeddings
3454
- `)),false)}async function Zo(){return (await import('inquirer')).default}function er(e){let o=e.command("ai").description("AI-powered features");o.command("recommend").description("Get AI-powered module recommendations").argument("[query]",'What do you want to build? (e.g., "user authentication with email")').option("-n, --number <count>","Number of recommendations","5").option("--json","Output as JSON").action(async(t,r)=>{try{zt()||(console.log(l.yellow(`
3455
- \u26A0\uFE0F AI features are disabled`)),console.log(l.gray(`Enable with: rapidkit config ai enable
3456
- `)),process.exit(1));let i=nt();i?await Pt(i):(console.log(l.yellow(`
46
+ `)),console.log(s.white("1. Get OpenAI API key from: https://platform.openai.com/api-keys")),console.log(s.white("2. Set the API key:")),console.log(s.gray(" rapidkit config set-api-key")),console.log(s.gray(` OR: export OPENAI_API_KEY="sk-..."
47
+ `)),console.log(s.white("3. Generate embeddings:")),console.log(s.gray(` rapidkit ai generate-embeddings
48
+ `)),console.log(s.cyan(`\u{1F4B0} Cost: ~$0.50 one-time
49
+ `))),false)}async function po(){let t=co();return t.exists?(console.log(s.blue(`
50
+ \u{1F504} Updating embeddings...`)),console.log(s.gray(`Current: ${t.moduleCount} modules`)),console.log(s.gray(`Generated: ${t.generatedAt||"unknown"}
51
+ `)),await at(true,t.path)):(console.log(s.yellow(`
52
+ \u26A0\uFE0F No existing embeddings found`)),console.log(s.gray(`Use: rapidkit ai generate-embeddings
53
+ `)),false)}async function uo(){return (await import('inquirer')).default}function mo(t){let e=t.command("ai").description("AI-powered features");e.command("recommend").description("Get AI-powered module recommendations").argument("[query]",'What do you want to build? (e.g., "user authentication with email")').option("-n, --number <count>","Number of recommendations","5").option("--json","Output as JSON").action(async(o,r)=>{try{Pt()||(console.log(s.yellow(`
54
+ \u26A0\uFE0F AI features are disabled`)),console.log(s.gray(`Enable with: rapidkit config ai enable
55
+ `)),process.exit(1));let n=Fe();n?await nt(n):(console.log(s.yellow(`
3457
56
  \u26A0\uFE0F OpenAI API key not configured - using MOCK MODE for testing
3458
- `)),console.log(l.gray("\u{1F4DD} Note: Mock embeddings provide approximate results for testing.")),console.log(l.gray(` For production, configure your OpenAI API key:
3459
- `)),console.log(l.white(" 1. Get your key from: https://platform.openai.com/api-keys")),console.log(l.white(" 2. Configure it: rapidkit config set-api-key")),console.log(l.gray(` OR set: export OPENAI_API_KEY="sk-proj-..."
3460
- `)),Do());let n=t;n||(n=(await(await Zo()).prompt([{type:"input",name:"query",message:"\u{1F916} What do you want to build?",validate:f=>f.length===0?"Please enter a description":f.length<3?"Please be more specific (at least 3 characters)":true}])).query),r.json||console.log(l.blue(`
57
+ `)),console.log(s.gray("\u{1F4DD} Note: Mock embeddings provide approximate results for testing.")),console.log(s.gray(` For production, configure your OpenAI API key:
58
+ `)),console.log(s.white(" 1. Get your key from: https://platform.openai.com/api-keys")),console.log(s.white(" 2. Configure it: rapidkit config set-api-key")),console.log(s.gray(` OR set: export OPENAI_API_KEY="sk-proj-..."
59
+ `)),Bt());let i=o;i||(i=(await(await uo()).prompt([{type:"input",name:"query",message:"\u{1F916} What do you want to build?",validate:p=>p.length===0?"Please enter a description":p.length<3?"Please be more specific (at least 3 characters)":true}])).query),r.json||console.log(s.blue(`
3461
60
  \u{1F916} Analyzing your request...
3462
- `)),await Qo(!r.json)||(console.log(l.yellow(`
61
+ `)),await lo(!r.json)||(console.log(s.yellow(`
3463
62
  \u26A0\uFE0F Cannot proceed without embeddings
3464
- `)),process.exit(1));let s=parseInt(r.number,10),c=await Bo(n,s);if(c.length===0||c[0].score<.3)if(console.log(l.yellow(`
63
+ `)),process.exit(1));let c=parseInt(r.number,10),m=await io(i,c);if(m.length===0||m[0].score<.3)if(console.log(s.yellow(`
3465
64
  \u26A0\uFE0F No matching modules found in RapidKit registry.
3466
- `)),console.log(l.cyan(`\u{1F4A1} Options:
3467
- `)),console.log(l.white("1. Create custom module:")),console.log(l.gray(" rapidkit modules scaffold <name> --category <category>")),console.log(l.gray(` Example: rapidkit modules scaffold blockchain-integration --category integrations
3468
- `)),console.log(l.white("2. Search with different keywords")),console.log(l.gray(` Try more general terms (e.g., "storage" instead of "blockchain")
3469
- `)),console.log(l.white("3. Request feature:")),console.log(l.gray(` https://github.com/getrapidkit/rapidkit/issues
3470
- `)),c.length>0)console.log(l.yellow(`\u26A0\uFE0F Low confidence matches found:
3471
- `));else return;if(r.json){console.log(JSON.stringify({query:n,recommendations:c},null,2));return}console.log(l.green.bold(`\u{1F4E6} Recommended Modules:
3472
- `)),c.forEach((m,w)=>{let f=(m.score*100).toFixed(1),g=m.score>.8?" \u2B50":"";console.log(l.bold(`${w+1}. ${m.module.name}${g}`)),console.log(l.gray(` ${m.module.description}`)),console.log(l.cyan(` Match: ${f}%`)+l.gray(` - ${m.reason}`)),console.log(l.yellow(` Category: ${m.module.category}`)),m.module.dependencies.length>0&&console.log(l.magenta(` Requires: ${m.module.dependencies.join(", ")}`)),console.log();});let p=c.slice(0,3).map(m=>m.module.id);console.log(l.cyan("\u{1F4A1} Quick install (top 3):")),console.log(l.white(` rapidkit add module ${p.join(" ")}
3473
- `));let d=await Zo(),{shouldInstall:u}=await d.prompt([{type:"confirm",name:"shouldInstall",message:"Would you like to install these modules now?",default:false}]);if(u){let{selectedModules:m}=await d.prompt([{type:"checkbox",name:"selectedModules",message:"Select modules to install:",choices:c.map(w=>({name:`${w.module.name} - ${w.module.description}`,value:w.module.id,checked:w.score>.7}))}]);m.length>0?(console.log(l.blue(`
3474
- \u{1F4E6} Installing ${m.length} modules...
3475
- `)),console.log(l.gray(`Command: rapidkit add module ${m.join(" ")}`)),console.log(l.yellow(`
3476
- \u26A0\uFE0F Note: Module installation not yet implemented`)),console.log(l.gray(`Coming soon in next version!
3477
- `))):console.log(l.gray(`
65
+ `)),console.log(s.cyan(`\u{1F4A1} Options:
66
+ `)),console.log(s.white("1. Create custom module:")),console.log(s.gray(" rapidkit modules scaffold <name> --category <category>")),console.log(s.gray(` Example: rapidkit modules scaffold blockchain-integration --category integrations
67
+ `)),console.log(s.white("2. Search with different keywords")),console.log(s.gray(` Try more general terms (e.g., "storage" instead of "blockchain")
68
+ `)),console.log(s.white("3. Request feature:")),console.log(s.gray(` https://github.com/getrapidkit/rapidkit/issues
69
+ `)),m.length>0)console.log(s.yellow(`\u26A0\uFE0F Low confidence matches found:
70
+ `));else return;if(r.json){console.log(JSON.stringify({query:i,recommendations:m},null,2));return}console.log(s.green.bold(`\u{1F4E6} Recommended Modules:
71
+ `)),m.forEach((y,v)=>{let p=(y.score*100).toFixed(1),g=y.score>.8?" \u2B50":"";console.log(s.bold(`${v+1}. ${y.module.name}${g}`)),console.log(s.gray(` ${y.module.description}`)),console.log(s.cyan(` Match: ${p}%`)+s.gray(` - ${y.reason}`)),console.log(s.yellow(` Category: ${y.module.category}`)),y.module.dependencies.length>0&&console.log(s.magenta(` Requires: ${y.module.dependencies.join(", ")}`)),console.log();});let d=m.slice(0,3).map(y=>y.module.id);console.log(s.cyan("\u{1F4A1} Quick install (top 3):")),console.log(s.white(` rapidkit add module ${d.join(" ")}
72
+ `));let l=await uo(),{shouldInstall:u}=await l.prompt([{type:"confirm",name:"shouldInstall",message:"Would you like to install these modules now?",default:false}]);if(u){let{selectedModules:y}=await l.prompt([{type:"checkbox",name:"selectedModules",message:"Select modules to install:",choices:m.map(v=>({name:`${v.module.name} - ${v.module.description}`,value:v.module.id,checked:v.score>.7}))}]);y.length>0?(console.log(s.blue(`
73
+ \u{1F4E6} Installing ${y.length} modules...
74
+ `)),console.log(s.gray(`Command: rapidkit add module ${y.join(" ")}`)),console.log(s.yellow(`
75
+ \u26A0\uFE0F Note: Module installation not yet implemented`)),console.log(s.gray(`Coming soon in next version!
76
+ `))):console.log(s.gray(`
3478
77
  No modules selected
3479
- `));}}catch(i){a$1.error(`
3480
- \u274C Error:`,i.message),i.code==="invalid_api_key"?(console.log(l.yellow(`
3481
- \u{1F4A1} Your API key may be invalid or expired`)),console.log(l.cyan(` Update it: rapidkit config set-api-key
3482
- `))):i.message.includes("embeddings file not found")&&(console.log(l.yellow(`
3483
- \u{1F4A1} Module embeddings not generated yet`)),console.log(l.cyan(" Generate them (one-time):")),console.log(l.white(" cd rapidkit-npm")),console.log(l.white(' export OPENAI_API_KEY="sk-proj-..."')),console.log(l.white(` npx tsx src/ai/generate-embeddings.ts
3484
- `))),process.exit(1);}}),o.command("info").description("Show AI features information").action(()=>{let t=nt(),r=zt();console.log(l.bold(`
78
+ `));}}catch(n){a$1.error(`
79
+ \u274C Error:`,n.message),n.code==="invalid_api_key"?(console.log(s.yellow(`
80
+ \u{1F4A1} Your API key may be invalid or expired`)),console.log(s.cyan(` Update it: rapidkit config set-api-key
81
+ `))):n.message.includes("embeddings file not found")&&(console.log(s.yellow(`
82
+ \u{1F4A1} Module embeddings not generated yet`)),console.log(s.cyan(" Generate them (one-time):")),console.log(s.white(" cd rapidkit-npm")),console.log(s.white(' export OPENAI_API_KEY="sk-proj-..."')),console.log(s.white(` npx tsx src/ai/generate-embeddings.ts
83
+ `))),process.exit(1);}}),e.command("info").description("Show AI features information").action(()=>{let o=Fe(),r=Pt();console.log(s.bold(`
3485
84
  \u{1F916} RapidKit AI Features
3486
- `)),console.log(l.cyan("Status:"),r?l.green("Enabled"):l.red("Disabled")),console.log(l.cyan("API Key:"),t?l.green("Configured \u2713"):l.red("Not configured \u2717")),console.log(l.bold(`
85
+ `)),console.log(s.cyan("Status:"),r?s.green("Enabled"):s.red("Disabled")),console.log(s.cyan("API Key:"),o?s.green("Configured \u2713"):s.red("Not configured \u2717")),console.log(s.bold(`
3487
86
  \u{1F4E6} Available Features:
3488
- `)),console.log(l.white("\u2022 Module Recommender")+l.gray(" - AI-powered module suggestions")),console.log(l.gray(' Usage: rapidkit ai recommend "I need authentication"')),console.log(l.bold(`
87
+ `)),console.log(s.white("\u2022 Module Recommender")+s.gray(" - AI-powered module suggestions")),console.log(s.gray(' Usage: rapidkit ai recommend "I need authentication"')),console.log(s.bold(`
3489
88
  \u{1F4B0} Pricing:
3490
- `)),console.log(l.white("\u2022 Per query: ~$0.0002")+l.gray(" (practically free)")),console.log(l.white("\u2022 100 queries: ~$0.02")+l.gray(" (2 cents)")),console.log(l.white("\u2022 1000 queries: ~$0.20")+l.gray(" (20 cents)")),console.log(l.bold(`
89
+ `)),console.log(s.white("\u2022 Per query: ~$0.0002")+s.gray(" (practically free)")),console.log(s.white("\u2022 100 queries: ~$0.02")+s.gray(" (2 cents)")),console.log(s.white("\u2022 1000 queries: ~$0.20")+s.gray(" (20 cents)")),console.log(s.bold(`
3491
90
  \u{1F680} Getting Started:
3492
- `)),t?(console.log(l.green("\u2713 You're all set!")),console.log(l.white(' Try: rapidkit ai recommend "user authentication"'))):(console.log(l.white("1. Get OpenAI API key: https://platform.openai.com/api-keys")),console.log(l.white("2. Configure: rapidkit config set-api-key")),console.log(l.white('3. Try: rapidkit ai recommend "user authentication"'))),console.log();}),o.command("generate-embeddings").description("Generate AI embeddings for all modules (one-time setup)").option("--force","Force regeneration even if embeddings exist").action(async()=>{try{let t=nt();t||(console.log(l.red(`
91
+ `)),o?(console.log(s.green("\u2713 You're all set!")),console.log(s.white(' Try: rapidkit ai recommend "user authentication"'))):(console.log(s.white("1. Get OpenAI API key: https://platform.openai.com/api-keys")),console.log(s.white("2. Configure: rapidkit config set-api-key")),console.log(s.white('3. Try: rapidkit ai recommend "user authentication"'))),console.log();}),e.command("generate-embeddings").description("Generate AI embeddings for all modules (one-time setup)").option("--force","Force regeneration even if embeddings exist").action(async()=>{try{let o=Fe();o||(console.log(s.red(`
3493
92
  \u274C OpenAI API key not configured
3494
- `)),console.log(l.cyan(`To generate embeddings, you need an OpenAI API key:
3495
- `)),console.log(l.white("1. Get your key from: https://platform.openai.com/api-keys")),console.log(l.white("2. Configure it: rapidkit config set-api-key")),console.log(l.gray(`
3496
- OR set environment variable:`)),console.log(l.white(` export OPENAI_API_KEY="sk-proj-..."
3497
- `)),process.exit(1)),Pt(t);let r=await Tt(true);r&&(console.log(l.green("\u2705 Ready to use AI recommendations!")),console.log(l.cyan(`Try: rapidkit ai recommend "authentication"
3498
- `))),process.exit(r?0:1);}catch(t){a$1.error("Failed to generate embeddings:",t.message),process.exit(1);}}),o.command("update-embeddings").description("Update existing embeddings with latest modules").action(async()=>{try{let t=nt();t||(console.log(l.red(`
93
+ `)),console.log(s.cyan(`To generate embeddings, you need an OpenAI API key:
94
+ `)),console.log(s.white("1. Get your key from: https://platform.openai.com/api-keys")),console.log(s.white("2. Configure it: rapidkit config set-api-key")),console.log(s.gray(`
95
+ OR set environment variable:`)),console.log(s.white(` export OPENAI_API_KEY="sk-proj-..."
96
+ `)),process.exit(1)),nt(o);let r=await at(true);r&&(console.log(s.green("\u2705 Ready to use AI recommendations!")),console.log(s.cyan(`Try: rapidkit ai recommend "authentication"
97
+ `))),process.exit(r?0:1);}catch(o){a$1.error("Failed to generate embeddings:",o.message),process.exit(1);}}),e.command("update-embeddings").description("Update existing embeddings with latest modules").action(async()=>{try{let o=Fe();o||(console.log(s.red(`
3499
98
  \u274C OpenAI API key not configured
3500
- `)),console.log(l.white(`Set your API key: rapidkit config set-api-key
3501
- `)),process.exit(1)),Pt(t);let r=await Xo();process.exit(r?0:1);}catch(t){a$1.error("Failed to update embeddings:",t.message),process.exit(1);}});}var At=class{constructor(o){this.runCommand=o;}runtime="go";async run(o,t,r){return {exitCode:await this.runCommand(o,t,r)}}async ensureGoInstalled(o){return (await this.run("go",["version"],o)).exitCode===0?null:{exitCode:1,message:"Go toolchain is not installed or not available on PATH. Install Go from https://go.dev/dl/ and retry."}}findWorkspaceRoot(o){let t=o;for(;;){if(_.existsSync(h.join(t,".rapidkit-workspace")))return t;let r=h.dirname(t);if(r===t)break;t=r;}return null}resolveDependencyMode(o){let t=process.env.RAPIDKIT_DEP_SHARING_MODE?.toLowerCase();if(t==="shared-runtime-caches"||t==="shared-node-deps"||t==="isolated")return t;let r=this.findWorkspaceRoot(o);if(!r)return "isolated";let i=h.join(r,".rapidkit","policies.yml");if(!_.existsSync(i))return "isolated";try{let s=_.readFileSync(i,"utf-8").match(/^\s*dependency_sharing_mode:\s*([a-zA-Z\-]+)\s*(?:#.*)?$/m)?.[1]?.toLowerCase();if(s==="shared-runtime-caches"||s==="shared-node-deps"||s==="isolated")return s}catch{}return "isolated"}withGoCacheEnv(o,t){let r=this.resolveDependencyMode(o),i=process.env.RAPIDKIT_WORKSPACE_PATH||this.findWorkspaceRoot(o),n=r==="shared-runtime-caches"?h.join(i||o,".rapidkit","cache","go"):h.join(o,".rapidkit","cache","go"),a=process.env.GOMODCACHE,s=process.env.GOCACHE;return process.env.GOMODCACHE=h.join(n,"mod"),process.env.GOCACHE=h.join(n,"build"),t().finally(()=>{typeof a>"u"?delete process.env.GOMODCACHE:process.env.GOMODCACHE=a,typeof s>"u"?delete process.env.GOCACHE:process.env.GOCACHE=s;})}async checkPrereqs(){return this.run("go",["version"],process.cwd())}async warmSetupCache(o){return this.withGoCacheEnv(o,async()=>{try{return process.env.GOMODCACHE&&_.mkdirSync(process.env.GOMODCACHE,{recursive:true}),process.env.GOCACHE&&_.mkdirSync(process.env.GOCACHE,{recursive:true}),{exitCode:0}}catch{return {exitCode:1,message:"Failed to prepare Go cache directories"}}})}async initProject(o){return this.withGoCacheEnv(o,async()=>{let t=await this.ensureGoInstalled(o);return t||this.run("go",["mod","tidy"],o)})}async runDev(o){return this.withGoCacheEnv(o,()=>(async()=>{let t=await this.ensureGoInstalled(o);if(t)return t;let r=h.join(o,"Makefile");return _.existsSync(r)?this.run("make",["run"],o):this.run("go",["run","./main.go"],o)})())}async runTest(o){return this.withGoCacheEnv(o,async()=>{let t=await this.ensureGoInstalled(o);return t||this.run("go",["test","./..."],o)})}async runBuild(o){return this.withGoCacheEnv(o,async()=>{let t=await this.ensureGoInstalled(o);return t||this.run("go",["build","./..."],o)})}async runStart(o){return this.withGoCacheEnv(o,async()=>{let r=(a()?[h.join(o,"server.exe"),h.join(o,"server")]:[h.join(o,"server")]).find(n=>_.existsSync(n));if(r)return this.run(r,[],o);let i=await this.ensureGoInstalled(o);return i||this.run("go",["run","./main.go"],o)})}async doctorHints(o){return ["Install Go from https://go.dev/dl/ if missing.","Run go mod tidy when dependencies are out of sync.","Use make run for hot-reload if Makefile exists."]}};var Ot=class{constructor(o){this.runCommand=o;}runtime="node";async run(o,t,r){return {exitCode:await this.runCommand(o,t,r)}}findWorkspaceRoot(o){let t=o;for(;;){if(_.existsSync(h.join(t,".rapidkit-workspace")))return t;let r=h.dirname(t);if(r===t)break;t=r;}return null}resolveDependencyMode(o){let t=process.env.RAPIDKIT_DEP_SHARING_MODE?.toLowerCase();if(t==="shared-runtime-caches"||t==="shared-node-deps"||t==="isolated")return t;let r=this.findWorkspaceRoot(o);if(!r)return "isolated";let i=h.join(r,".rapidkit","policies.yml");if(!_.existsSync(i))return "isolated";try{let s=_.readFileSync(i,"utf-8").match(/^\s*dependency_sharing_mode:\s*([a-zA-Z\-]+)\s*(?:#.*)?$/m)?.[1]?.toLowerCase();if(s==="shared-runtime-caches"||s==="shared-node-deps"||s==="isolated")return s}catch{}return "isolated"}withDependencyEnv(o,t,r){let i=this.resolveDependencyMode(o),n=process.env.RAPIDKIT_WORKSPACE_PATH||this.findWorkspaceRoot(o),a=i==="isolated"?h.join(o,".rapidkit","cache","node"):h.join(n||o,".rapidkit","cache","node"),s=process.env.npm_config_cache,c=process.env.npm_config_store_dir;return t==="pnpm"?(process.env.npm_config_store_dir=h.join(a,"pnpm-store"),process.env.npm_config_cache=h.join(a,"pnpm-cache")):t==="yarn"?process.env.npm_config_cache=h.join(a,"yarn-cache"):process.env.npm_config_cache=h.join(a,"npm-cache"),r().finally(()=>{typeof s>"u"?delete process.env.npm_config_cache:process.env.npm_config_cache=s,typeof c>"u"?delete process.env.npm_config_store_dir:process.env.npm_config_store_dir=c;})}detectPackageManager(o){return _.existsSync(h.join(o,"pnpm-lock.yaml"))?"pnpm":_.existsSync(h.join(o,"yarn.lock"))?"yarn":"npm"}scriptArgs(o,t){return o==="npm"?["run",t]:["run",t]}async checkPrereqs(){return this.run("node",["--version"],process.cwd())}async warmSetupCache(o){let t=this.detectPackageManager(o);return this.withDependencyEnv(o,t,async()=>{try{return process.env.npm_config_cache&&_.mkdirSync(process.env.npm_config_cache,{recursive:true}),t==="pnpm"&&process.env.npm_config_store_dir&&_.mkdirSync(process.env.npm_config_store_dir,{recursive:true}),{exitCode:0}}catch{return {exitCode:1,message:"Failed to prepare Node cache directories"}}})}async initProject(o){let t=this.detectPackageManager(o),r=this.resolveDependencyMode(o),i=r==="shared-runtime-caches"||r==="shared-node-deps"?["install","--prefer-offline"]:["install"];return this.withDependencyEnv(o,t,()=>this.run(t,i,o))}async runDev(o){let t=this.detectPackageManager(o);return this.withDependencyEnv(o,t,()=>this.run(t,this.scriptArgs(t,"dev"),o))}async runTest(o){let t=this.detectPackageManager(o);return this.withDependencyEnv(o,t,()=>this.run(t,this.scriptArgs(t,"test"),o))}async runBuild(o){let t=this.detectPackageManager(o);return this.withDependencyEnv(o,t,()=>this.run(t,this.scriptArgs(t,"build"),o))}async runStart(o){let t=this.detectPackageManager(o);return this.withDependencyEnv(o,t,()=>this.run(t,this.scriptArgs(t,"start"),o))}async doctorHints(o){return ["Install Node.js LTS and ensure node/npm are on PATH.","Use lockfiles (package-lock.json, pnpm-lock.yaml, yarn.lock) for deterministic installs.","Run install before dev/test/build if dependencies changed."]}};var Nt=class{constructor(o){this.runCore=o;}runtime="python";async run(o,t){return {exitCode:await this.withPythonCacheEnv(t,()=>this.runCore(o,t))}}findWorkspaceRoot(o){let t=o;for(;;){if(_.existsSync(h.join(t,".rapidkit-workspace")))return t;let r=h.dirname(t);if(r===t)break;t=r;}return null}resolveDependencyMode(o){let t=process.env.RAPIDKIT_DEP_SHARING_MODE?.toLowerCase();if(t==="shared-runtime-caches"||t==="shared-node-deps"||t==="isolated")return t;let r=this.findWorkspaceRoot(o);if(!r)return "isolated";let i=h.join(r,".rapidkit","policies.yml");if(!_.existsSync(i))return "isolated";try{let s=_.readFileSync(i,"utf-8").match(/^\s*dependency_sharing_mode:\s*([a-zA-Z\-]+)\s*(?:#.*)?$/m)?.[1]?.toLowerCase();if(s==="shared-runtime-caches"||s==="shared-node-deps"||s==="isolated")return s}catch{}return "isolated"}withPythonCacheEnv(o,t){let r=this.resolveDependencyMode(o),i=process.env.RAPIDKIT_WORKSPACE_PATH||this.findWorkspaceRoot(o),n=r==="shared-runtime-caches"?h.join(i||o,".rapidkit","cache","python"):h.join(o,".rapidkit","cache","python"),a=process.env.PIP_CACHE_DIR,s=process.env.POETRY_CACHE_DIR;return process.env.PIP_CACHE_DIR=h.join(n,"pip"),process.env.POETRY_CACHE_DIR=h.join(n,"poetry"),t().finally(()=>{typeof a>"u"?delete process.env.PIP_CACHE_DIR:process.env.PIP_CACHE_DIR=a,typeof s>"u"?delete process.env.POETRY_CACHE_DIR:process.env.POETRY_CACHE_DIR=s;})}async checkPrereqs(){let o=process.cwd(),t=await this.run(["doctor","check"],o);return t.exitCode===0?t:this.run(["doctor"],o)}async initProject(o){return this.run(["init"],o)}async runDev(o){return this.run(["dev"],o)}async runTest(o){return this.run(["test"],o)}async runBuild(o){return this.run(["build"],o)}async runStart(o){return this.run(["start"],o)}async doctorHints(o){return ['Run "npx rapidkit doctor workspace" for a full workspace scan.','Use "npx rapidkit init" after adding or changing modules.','Use workspace launcher "./rapidkit" to avoid environment drift.']}};function Yn(){let e={...process.env},o=e.PATH||"";if(o){let t=o.split(h.delimiter).filter(r=>!r.replace(/\\/g,"/").includes("/.pyenv/shims")).join(h.delimiter);e.PATH=t;}return e.PYENV_VERSION="system",e.POETRY_PYTHON=e.POETRY_PYTHON||c$3(),typeof e.RAPIDKIT_SKIP_LOCK_SYNC>"u"&&(e.RAPIDKIT_SKIP_LOCK_SYNC="1"),e}function He(e,o){return e==="go"?new At((t,r,i)=>o.runCommandInCwd(t,r,i)):e==="node"?new Ot((t,r,i)=>o.runCommandInCwd(t,r,i)):new Nt((t,r)=>o.runCoreRapidkit(t,{cwd:r,env:Yn()}))}var or=h.join(Tn.homedir(),".rapidkit","cache"),rr=1440*60*1e3;function eo(){let e=process.env.RAPIDKIT_CACHE_DIR?.trim();if(e)return e;let o=process.env.VITEST_WORKER_ID?.trim();return o?h.join(or,`vitest-${o}`):or}var $t=class e{static instance;memoryCache=new Map;constructor(){}static getInstance(){return e.instance||(e.instance=new e),e.instance}getCacheKey(o){return createHash("md5").update(o).digest("hex")}getCachePath(o){return h.join(eo(),`${this.getCacheKey(o)}.json`)}async get(o,t="1.0"){let r=this.memoryCache.get(o);if(r&&r.version===t&&Date.now()-r.timestamp<rr)return a$1.debug(`Cache hit (memory): ${o}`),r.data;try{let i=this.getCachePath(o),n=await promises.readFile(i,"utf-8"),a=JSON.parse(n);if(a.version===t&&Date.now()-a.timestamp<rr)return a$1.debug(`Cache hit (disk): ${o}`),this.memoryCache.set(o,a),a.data;await promises.unlink(i).catch(()=>{});}catch{a$1.debug(`Cache miss: ${o}`);}return null}async set(o,t,r="1.0"){let i={data:t,timestamp:Date.now(),version:r};this.memoryCache.set(o,i);try{await promises.mkdir(eo(),{recursive:true});let n=this.getCachePath(o);await promises.writeFile(n,JSON.stringify(i),"utf-8"),a$1.debug(`Cache set: ${o}`);}catch(n){a$1.debug(`Cache write failed: ${o}`,n);}}async invalidate(o){this.memoryCache.delete(o);try{let t=this.getCachePath(o);await promises.unlink(t),a$1.debug(`Cache invalidated: ${o}`);}catch{}}async clear(){this.memoryCache.clear();try{let o=eo(),t=await promises.readdir(o);await Promise.all(t.map(r=>promises.unlink(h.join(o,r)))),a$1.debug("Cache cleared");}catch{}}};function Se(e){let o=e;for(;;){let t=h.join(o,".rapidkit","project.json");if(_.existsSync(t))try{return JSON.parse(_.readFileSync(t,"utf8"))}catch{return null}let r=h.dirname(o);if(r===o)break;o=r;}return null}function pe(e,o){let t=e?.runtime?.toLowerCase(),r=e?.kit_name?.toLowerCase(),i=_.existsSync(h.join(o,"go.mod"));return t==="go"||(r?.startsWith("gofiber")??false)||(r?.startsWith("gogin")??false)||i}function ue(e,o){let t=e?.runtime?.toLowerCase(),r=e?.kit_name?.toLowerCase(),i=_.existsSync(h.join(o,"package.json"));return t==="node"||t==="typescript"||(r?.startsWith("nestjs")??false)||i}function me(e,o){let t=e?.runtime?.toLowerCase(),r=e?.kit_name?.toLowerCase(),i=_.existsSync(h.join(o,"pyproject.toml")),n=_.existsSync(h.join(o,"requirements.txt"))||_.existsSync(h.join(o,"requirements.in"));return t==="python"||(r?.startsWith("fastapi")??false)||i||n}async function ts(e){let o=await promises.readFile(e);return createHash("sha256").update(o).digest("hex")}async function ir(e,o){await b.outputFile(e,`${JSON.stringify(o,null,2)}
3502
- `,"utf-8");}function be(e,o){return h.isAbsolute(o)?o:h.join(e,o)}function os(e,o){if(e.target)return e.target;if(e.source)return h.basename(e.source);if(e.url)try{let t=new URL(e.url).pathname,r=h.basename(t);if(r&&r!=="/")return r}catch{}return `${o}.artifact`}async function rs(e){let o=h.join(e,".rapidkit","trusted-sources.lock"),t=new Set(["localhost","127.0.0.1"]);if(!await b.pathExists(o))return t;try{let i=(await promises.readFile(o,"utf-8")).split(/\r?\n/).map(n=>n.trim()).filter(n=>n.length>0&&!n.startsWith("#"));for(let n of i)t.add(n.toLowerCase());}catch{}return t}async function is(e,o,t){await b.ensureDir(h.dirname(o)),await new Promise((r,i)=>{let a=(e.startsWith("https://")?lr:cr).get(e,s=>{if(!s.statusCode||s.statusCode<200||s.statusCode>=300){i(new Error(`HTTP ${s.statusCode||"unknown"}`)),s.resume();return}let c=createWriteStream(o);s.pipe(c),c.on("finish",()=>{c.close(),r();}),c.on("error",p=>{i(p);});});a.setTimeout(t,()=>{a.destroy(new Error(`Request timeout after ${t}ms`));}),a.on("error",s=>{i(s);});});}async function ns(e,o,t,r,i){let n=new URL(e),a=JSON.stringify(o),s=n.protocol==="https:"?lr:cr;await new Promise((c,p)=>{let d=s.request({method:"POST",hostname:n.hostname,port:n.port||(n.protocol==="https:"?443:80),path:`${n.pathname}${n.search}`,headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(a),...r?{Authorization:`Bearer ${r}`}:{},...i||{}}},u=>{if(!u.statusCode||u.statusCode<200||u.statusCode>=300){p(new Error(`HTTP ${u.statusCode||"unknown"}`)),u.resume();return}u.resume(),c();});d.setTimeout(t,()=>{d.destroy(new Error(`Request timeout after ${t}ms`));}),d.on("error",u=>{p(u);}),d.write(a),d.end();});}function nr(e){return new Promise(o=>setTimeout(o,e))}async function ss(e,o,t,r){let i=be(e,o||".rapidkit/reports/evidence-export-dead-letter.ndjson");return await b.ensureDir(h.dirname(i)),await promises.appendFile(i,`${JSON.stringify({timestamp:new Date().toISOString(),reason:r,payload:t})}
3503
- `,"utf-8"),i}function as(e,o){if(!o?.enabled)return {headers:{}};let t=process.env[o.hmacKeyEnv];if(!t)return {headers:{},error:`Evidence signing key env is missing: ${o.hmacKeyEnv}`};let r=(o.algorithm||"sha256").toLowerCase(),i=o.headerName||"x-rapidkit-evidence-signature",n=createHmac(r,t).update(JSON.stringify(e)).digest("hex");return {headers:{[i]:n,"x-rapidkit-evidence-signature-alg":r}}}async function cs(e,o,t){let r=(t.algorithm||"sha256").toLowerCase(),i=be(e,t.publicKeyPath);if(!await b.pathExists(i))return {verified:false,algorithm:r,publicKeyPath:i,publicKeyFingerprint:"",signature:t.signature,message:`Public key not found: ${i}`};try{let n=await promises.readFile(i,"utf-8"),a=await promises.readFile(o),s=createVerify(r);s.update(a),s.end();let c=Buffer.from(t.signature,"base64"),p=s.verify(n,c),d=createHash("sha256").update(n).digest("hex");return {verified:p,algorithm:r,publicKeyPath:i,publicKeyFingerprint:d,signature:t.signature,message:p?"Attestation verified.":"Attestation signature verification failed."}}catch(n){return {verified:false,algorithm:r,publicKeyPath:i,publicKeyFingerprint:"",signature:t.signature,message:`Attestation verification error: ${n.message}`}}}async function ls(e,o,t,r){let i=process.env.RAPIDKIT_SIGSTORE_MOCK;if(i==="success")return {verified:true,tlogVerified:r.requireTransparencyLog,message:"Sigstore verification passed (mock).",identity:t.identity||null,issuer:t.issuer||null,rekorUrl:t.rekorUrl||null,bundlePath:t.bundlePath||null,certificatePath:t.certificatePath||null,signaturePath:t.signaturePath||null};if(i==="fail")return {verified:false,tlogVerified:false,message:"Sigstore verification failed (mock).",identity:t.identity||null,issuer:t.issuer||null,rekorUrl:t.rekorUrl||null,bundlePath:t.bundlePath||null,certificatePath:t.certificatePath||null,signaturePath:t.signaturePath||null};let n=t.signaturePath?be(e,t.signaturePath):null;if(!n||!await b.pathExists(n))return {verified:false,tlogVerified:false,message:"Sigstore signaturePath is missing or not found.",identity:t.identity||null,issuer:t.issuer||null,rekorUrl:t.rekorUrl||null,bundlePath:t.bundlePath||null,certificatePath:t.certificatePath||null,signaturePath:n};let a=["verify-blob",o,"--signature",n],s=t.certificatePath?be(e,t.certificatePath):null;s&&a.push("--certificate",s);let c=t.bundlePath?be(e,t.bundlePath):null;c&&a.push("--bundle",c);let p=t.keyPath?be(e,t.keyPath):null;p&&a.push("--key",p),t.identity&&a.push("--certificate-identity",t.identity),t.issuer&&a.push("--certificate-oidc-issuer",t.issuer),t.rekorUrl&&a.push("--rekor-url",t.rekorUrl),r.requireTransparencyLog||a.push("--insecure-ignore-tlog");try{let d=await execa("cosign",a,{reject:false});return d.exitCode===0?{verified:true,tlogVerified:r.requireTransparencyLog,message:"Sigstore verification passed.",identity:t.identity||null,issuer:t.issuer||null,rekorUrl:t.rekorUrl||null,bundlePath:c,certificatePath:s,signaturePath:n}:{verified:false,tlogVerified:false,message:`Sigstore verification failed: ${d.stderr||d.stdout||"unknown error"}`,identity:t.identity||null,issuer:t.issuer||null,rekorUrl:t.rekorUrl||null,bundlePath:c,certificatePath:s,signaturePath:n}}catch(d){return {verified:false,tlogVerified:false,message:`Sigstore verification error: ${d.message}`,identity:t.identity||null,issuer:t.issuer||null,rekorUrl:t.rekorUrl||null,bundlePath:c,certificatePath:s,signaturePath:n}}}async function ds(e,o){let t=o.algorithm||"sha256",r=be(e,o.policyPath),i=be(e,o.signaturePath),n=be(e,o.publicKeyPath);if(!await b.pathExists(r))return {verified:false,message:`Governance policy bundle not found: ${r}`,policies:null};if(!await b.pathExists(i))return {verified:false,message:`Governance policy signature not found: ${i}`,policies:null};if(!await b.pathExists(n))return {verified:false,message:`Governance policy public key not found: ${n}`,policies:null};try{let a=await promises.readFile(r,"utf-8"),s=(await promises.readFile(i,"utf-8")).trim(),c=await promises.readFile(n,"utf-8"),p=createVerify(t);return p.update(a),p.end(),p.verify(c,Buffer.from(s,"base64"))?{verified:true,message:"Governance policy bundle verified.",policies:JSON.parse(a).policies||{}}:{verified:false,message:"Governance policy bundle signature verification failed.",policies:null}}catch(a){return {verified:false,message:`Governance policy bundle verification error: ${a.message}`,policies:null}}}async function to(e,o){let t=[],r={syncedArtifacts:0,verifiedArtifacts:0,rotatedFiles:0,lockWritten:false,governanceBundleVerified:false,transparencyEvidenceWritten:false,transparencyEvidenceRecords:0,evidenceExported:false,evidenceExportTarget:null},i=h.join(e,".rapidkit"),n=h.join(i,"mirror-config.json"),a=h.join(i,"mirror.lock"),s=h.join(i,"mirror","artifacts"),c=h.join(i,"reports"),p=await rs(e);if(!await b.pathExists(n))return t.push({id:"mirror.lifecycle",status:"skipped",message:"Mirror lifecycle skipped: .rapidkit/mirror-config.json not found."}),{checks:t,details:r};let d={};try{d=JSON.parse(await promises.readFile(n,"utf-8"));}catch{return t.push({id:"mirror.lifecycle.config",status:"failed",message:"Mirror lifecycle failed: invalid JSON in mirror-config.json."}),{checks:t,details:r}}if(!(o.forceRun===true||o.ciMode||o.offlineMode||d.mode==="offline-only"))return t.push({id:"mirror.lifecycle",status:"skipped",message:"Mirror lifecycle skipped: not in ci/offline mode."}),{checks:t,details:r};await b.ensureDir(s);let m=Math.max(0,d.prefetch?.retries??2),w=Math.max(0,d.prefetch?.backoffMs??250),f=Math.max(1e3,d.prefetch?.timeoutMs??15e3),g=d.security?.requireAttestation===true,R=d.security?.requireSigstore===true,x=d.security?.requireTransparencyLog===true,T=d.security?.requireSignedGovernance===true,C=d.security?.evidenceExport,E=(process.env.RAPIDKIT_ENV||d.security?.governance?.environment||"dev").toLowerCase(),G=d.security?.governance?.policies||{};if(d.security?.governanceBundle){let z=await ds(e,d.security.governanceBundle);if(t.push({id:"governance.bundle.verify",status:z.verified?"passed":"failed",message:z.message}),z.verified&&z.policies)G=z.policies,r.governanceBundleVerified=true;else if(T)return {checks:t,details:r}}let N=G[E],$=x||N?.requireTransparencyLog===true,Re=[],et=Array.isArray(d.artifacts)?d.artifacts:[],we=[];for(let z=0;z<et.length;z+=1){let L=et[z],S=L.id||`artifact-${z+1}`,se=L.source?be(e,L.source):null,ae=os(L,S),H=h.join(s,ae),ie=false,le={sourceType:"path",source:se||L.url||"unknown",host:null,fetchedAt:new Date().toISOString(),attempts:1,trusted:true};if(se&&await b.pathExists(se))await b.ensureDir(h.dirname(H)),await b.copyFile(se,H),r.syncedArtifacts+=1,ie=true,le={sourceType:"path",source:se,host:null,fetchedAt:new Date().toISOString(),attempts:1,trusted:true},t.push({id:`mirror.sync.${S}`,status:"passed",message:`Mirrored artifact ${S} from source path.`});else if(L.url){let U="";try{U=new URL(L.url).hostname.toLowerCase();}catch{t.push({id:`mirror.prefetch.${S}`,status:"failed",message:`Invalid URL for ${S}: ${L.url}`});continue}if(!(process.env.RAPIDKIT_TRUSTED_SOURCES==="1"||p.has(U))){t.push({id:`mirror.prefetch.trust.${S}`,status:"failed",message:`Untrusted mirror host for ${S}: ${U}. Add host to .rapidkit/trusted-sources.lock or set RAPIDKIT_TRUSTED_SOURCES=1.`});continue}if(o.offlineMode&&(await b.pathExists(H)?(ie=true,le={sourceType:"url",source:L.url,host:U,fetchedAt:new Date().toISOString(),attempts:0,trusted:true},t.push({id:`mirror.prefetch.${S}`,status:"passed",message:`Offline mode reused existing mirrored artifact ${S}.`})):t.push({id:`mirror.prefetch.${S}`,status:"failed",message:`Offline mode cannot prefetch remote artifact ${S} without an existing mirrored copy.`}),!ie))continue;if(!ie){let ne=null,Ae=0;for(let Ee=1;Ee<=m+1;Ee+=1){Ae=Ee;try{await is(L.url,H,f),r.syncedArtifacts+=1,ie=true,le={sourceType:"url",source:L.url,host:U,fetchedAt:new Date().toISOString(),attempts:Ae,trusted:true},t.push({id:`mirror.prefetch.${S}`,status:"passed",message:Ae>1?`Prefetched artifact ${S} from ${U} after ${Ae} attempts.`:`Prefetched artifact ${S} from ${U}.`});break}catch(ut){if(ne=ut,Ee<=m){await nr(w*Ee);continue}}}if(!ie){t.push({id:`mirror.prefetch.${S}`,status:"failed",message:`Failed to prefetch ${S} after ${m+1} attempt(s): ${ne?.message||"unknown error"}`});continue}}if(!ie){t.push({id:`mirror.prefetch.${S}`,status:"failed",message:`Failed to prefetch ${S}.`});continue}}if(!ie){L.required||o.offlineMode?t.push({id:`mirror.sync.${S}`,status:"failed",message:`Mirror source missing for ${S}${se?`: ${se}`:""}`}):t.push({id:`mirror.sync.${S}`,status:"skipped",message:`Mirror source not found for optional artifact ${S}.`});continue}let I=await ts(H);if(L.sha256&&L.sha256.toLowerCase()!==I.toLowerCase()){t.push({id:`mirror.verify.${S}`,status:"failed",message:`Checksum mismatch for ${S}.`});continue}r.verifiedArtifacts+=1,t.push({id:`mirror.verify.${S}`,status:"passed",message:`Checksum verified for ${S}.`});let O=L.attestation?await cs(e,H,L.attestation):null;if(L.attestation){if(t.push({id:`mirror.attest.${S}`,status:O?.verified?"passed":"failed",message:O?.message||"Attestation verification failed."}),!O?.verified)continue}else if(g){t.push({id:`mirror.attest.${S}`,status:"failed",message:`Attestation is required but missing for ${S}.`});continue}else t.push({id:`mirror.attest.${S}`,status:"skipped",message:`No attestation provided for ${S}.`});let M=L.attestation?.sigstore,j=M?await ls(e,H,M,{requireTransparencyLog:$}):null;if(M){if(t.push({id:`mirror.sigstore.${S}`,status:j?.verified?"passed":"failed",message:j?.message||"Sigstore verification failed."}),Re.push({artifactId:S,verified:!!j?.verified,tlogVerified:!!j?.tlogVerified,identity:j?.identity||null,issuer:j?.issuer||null,rekorUrl:j?.rekorUrl||null,timestamp:new Date().toISOString(),environment:E}),!j?.verified)continue}else if(R){t.push({id:`mirror.sigstore.${S}`,status:"failed",message:`Sigstore attestation is required but missing for ${S}.`});continue}else t.push({id:`mirror.sigstore.${S}`,status:"skipped",message:`No Sigstore attestation provided for ${S}.`});if(M&&j?.verified&&N){let U=N.allowedIdentities||[];if(U.length>0){let ne=!!j.identity&&U.includes(j.identity);if(t.push({id:`mirror.sigstore.policy.identity.${S}`,status:ne?"passed":"failed",message:ne?`Sigstore identity policy passed for ${S} in ${E}.`:`Sigstore identity policy failed for ${S} in ${E}.`}),!ne)continue}let Te=N.allowedIssuers||[];if(Te.length>0){let ne=!!j.issuer&&Te.includes(j.issuer);if(t.push({id:`mirror.sigstore.policy.issuer.${S}`,status:ne?"passed":"failed",message:ne?`Sigstore issuer policy passed for ${S} in ${E}.`:`Sigstore issuer policy failed for ${S} in ${E}.`}),!ne)continue}let Pe=N.allowedRekorUrls||[];if(Pe.length>0){let ne=!!j.rekorUrl&&Pe.includes(j.rekorUrl);if(t.push({id:`mirror.sigstore.policy.rekor.${S}`,status:ne?"passed":"failed",message:ne?`Sigstore Rekor policy passed for ${S} in ${E}.`:`Sigstore Rekor policy failed for ${S} in ${E}.`}),!ne)continue}}else N&&t.push({id:`mirror.sigstore.policy.${S}`,status:"skipped",message:`Sigstore governance policy configured for ${E} but no verified Sigstore attestation for ${S}.`});let ge=await promises.stat(H);we.push({id:S,path:h.relative(e,H),sha256:I,size:ge.size,provenance:le,attestation:{detached:{provided:!!L.attestation,verified:O?.verified||false,algorithm:O?.algorithm||null,publicKeyPath:O?.publicKeyPath||null,publicKeyFingerprint:O?.publicKeyFingerprint||null,signature:O?.signature||null,verifiedAt:O?.verified?new Date().toISOString():null},sigstore:{provided:!!M,verified:j?.verified||false,tlogVerified:j?.tlogVerified||false,identity:j?.identity||null,issuer:j?.issuer||null,rekorUrl:j?.rekorUrl||null,bundlePath:j?.bundlePath||null,certificatePath:j?.certificatePath||null,signaturePath:j?.signaturePath||null,verifiedAt:j?.verified?new Date().toISOString():null}}});}let fe=d.retention?.keepLast;if(typeof fe=="number"&&fe>0){let L=(await promises.readdir(s,{withFileTypes:true})).filter(S=>S.isFile()).map(S=>h.join(s,S.name));if(L.length>fe){let S=await Promise.all(L.map(async ae=>({filePath:ae,stat:await promises.stat(ae)})));S.sort((ae,H)=>H.stat.mtimeMs-ae.stat.mtimeMs);let se=S.slice(fe);for(let ae of se)await promises.unlink(ae.filePath),r.rotatedFiles+=1;}}t.push({id:"mirror.rotate",status:"passed",message:r.rotatedFiles>0?`Mirror retention rotation removed ${r.rotatedFiles} file(s).`:"Mirror retention rotation completed with no removals."});let Ve={schemaVersion:"1.0",generatedAt:new Date().toISOString(),mode:d.mode||null,environment:E,artifacts:we};if(await promises.writeFile(a,`${JSON.stringify(Ve,null,2)}
3504
- `,"utf-8"),r.lockWritten=true,t.push({id:"mirror.lock.write",status:"passed",message:`Mirror lock updated at ${h.relative(e,a)}.`}),r.transparencyEvidenceRecords=Re.length,Re.length>0){let z={schemaVersion:"1.0",generatedAt:new Date().toISOString(),environment:E,records:Re},L=new Date().toISOString().replace(/[:.]/g,"-"),S=h.join(c,`transparency-evidence-${L}.json`),se=h.join(c,"transparency-evidence.latest.json");if(await b.ensureDir(c),await ir(S,z),await ir(se,z),r.transparencyEvidenceWritten=true,t.push({id:"sigstore.evidence.write",status:"passed",message:`Transparency evidence written to ${h.relative(e,se)}.`}),C?.enabled){let ae=Math.max(1e3,C.timeoutMs??1e4);if(C.target==="file")if(!C.filePath)t.push({id:"sigstore.evidence.export.file",status:"failed",message:"Evidence export target=file requires security.evidenceExport.filePath."});else try{let H=be(e,C.filePath);await b.ensureDir(h.dirname(H)),await promises.appendFile(H,`${JSON.stringify(z)}
3505
- `,"utf-8"),r.evidenceExported=true,r.evidenceExportTarget=H,t.push({id:"sigstore.evidence.export.file",status:"passed",message:`Transparency evidence exported to file sink ${H}.`});}catch(H){t.push({id:"sigstore.evidence.export.file",status:"failed",message:`Evidence file export failed: ${H.message}`});}else if(C.target==="http")if(!C.endpoint)t.push({id:"sigstore.evidence.export.http",status:"failed",message:"Evidence export target=http requires security.evidenceExport.endpoint."});else {let H=Math.max(0,C.retries??0),ie=Math.max(0,C.backoffMs??500),le=as(z,C.signing);le.error&&t.push({id:"sigstore.evidence.export.http",status:"failed",message:le.error});try{let I=C.authTokenEnv?process.env[C.authTokenEnv]:void 0,O=false,M=null;for(let j=1;j<=H+1;j+=1)try{if(le.error)throw new Error(le.error);await ns(C.endpoint,z,ae,I,le.headers),r.evidenceExported=true,r.evidenceExportTarget=C.endpoint,t.push({id:"sigstore.evidence.export.http",status:"passed",message:j>1?`Transparency evidence exported to HTTP endpoint ${C.endpoint} after ${j} attempts.`:`Transparency evidence exported to HTTP endpoint ${C.endpoint}.`}),O=true;break}catch(ge){M=ge,j<=H&&await nr(ie*j);}if(!O)throw M||new Error("unknown evidence export error")}catch(I){let O=`Evidence HTTP export failed: ${I.message}`;t.push({id:"sigstore.evidence.export.http",status:"failed",message:O});try{let M=await ss(e,C.deadLetterPath,z,O);t.push({id:"sigstore.evidence.export.deadletter",status:"passed",message:`Evidence export failure persisted to dead-letter sink ${M}.`});}catch(M){t.push({id:"sigstore.evidence.export.deadletter",status:"failed",message:`Evidence dead-letter write failed: ${M.message}`});}}}if(C.failOnError&&t.some(ie=>ie.status==="failed"&&(ie.id==="sigstore.evidence.export.file"||ie.id==="sigstore.evidence.export.http")))return {checks:t,details:r}}else t.push({id:"sigstore.evidence.export",status:"skipped",message:"Central evidence export not configured (security.evidenceExport.enabled=false)."});}else t.push({id:"sigstore.evidence.write",status:"skipped",message:"No Sigstore records available for transparency evidence output."}),t.push({id:"sigstore.evidence.export",status:"skipped",message:"Central evidence export skipped because no transparency evidence records exist."});return {checks:t,details:r}}function oo(e){if(!e||typeof e!="object")return null;let o=e.code;return o==="PYTHON_NOT_FOUND"||o==="BRIDGE_VENV_BOOTSTRAP_FAILED"?o:null}function us(e){let o=e.trim().toLowerCase();return o?o.startsWith("fastapi")?"fastapi":o.startsWith("nestjs")?"nestjs":null:null}function qt(e){let o=e.trim().toLowerCase();return o.startsWith("gofiber")||o==="go"||o==="go.standard"||o==="fiber"}function lt(e){let o=e.trim().toLowerCase();return o.startsWith("gogin")||o==="gin"}function pt(e,o){let t=e.indexOf(o);if(t>=0&&t+1<e.length)return e[t+1];let r=e.find(i=>i.startsWith(`${o}=`));if(r)return r.slice(o.length+1)}function co(){return d$2()}function pr(){let e={...process.env},o=e.PATH||"";return o&&(e.PATH=o.split(h.delimiter).filter(t=>!t.replace(/\\/g,"/").includes("/.pyenv/shims")).join(h.delimiter)),e.PYENV_VERSION="system",e.POETRY_PYTHON||(e.POETRY_PYTHON=c$3()),e.RAPIDKIT_SKIP_LOCK_SYNC||(e.RAPIDKIT_SKIP_LOCK_SYNC="1"),e.POETRY_KEYRING_ENABLED||(e.POETRY_KEYRING_ENABLED="false"),e.PYTHON_KEYRING_BACKEND||(e.PYTHON_KEYRING_BACKEND="keyring.backends.null.Keyring"),e.POETRY_NO_INTERACTION||(e.POETRY_NO_INTERACTION="1"),e}function gs(e){return f$1(h.join(e,".venv"))}async function _r(e,o){return await W(e,["--version"],o)===0}async function ro(e){let o=h.join(e,"go.mod");if(await b.pathExists(o))return "go";let t=h.join(e,"package.json");if(await b.pathExists(t))return "node";let r=h.join(e,"pyproject.toml"),i=h.join(e,"requirements.txt"),n=h.join(e,"poetry.lock");return await b.pathExists(r)||await b.pathExists(i)||await b.pathExists(n)?"python":null}async function ms(e){for(let o of co())if(await W(o,o==="py"?["-3","-m","venv",".venv"]:["-m","venv",".venv"],e)===0)return 0;return 1}async function Cr(e){for(let o of co())if(await W(o,o==="py"?["-3","-m","venv",".venv"]:["-m","venv",".venv"],e)===0)return 0;return 1}async function fs(e){let o=f$1(h.join(e,".venv"));if(!await b.pathExists(o)){let n=await Cr(e);if(n!==0)return n}if(!await _r("poetry",e))return 0;let r=await W("poetry",["config","virtualenvs.in-project","true","--local"],e);if(r!==0)return r;let i=await W("poetry",["env","use",o],e);return i!==0?i:0}async function hs(e){let o=f$1(h.join(e,".venv"));if(!await b.pathExists(o)){let i=await Cr(e);if(i!==0)return i}await W(o,["-m","pip","install","--upgrade","pip","setuptools","wheel"],e);let t=h.join(e,"requirements.txt");if(await b.pathExists(t)&&await W(o,["-m","pip","install","-r","requirements.txt"],e)===0)return 0;let r=h.join(e,"pyproject.toml");return await b.pathExists(r)&&(await W(o,["-m","pip","install","-e","."],e)===0||await W(o,["-m","pip","install","."],e)===0)?0:1}async function Dt(e,o){return await fs(e)!==0&&console.log(l.yellow("\u26A0\uFE0F Could not fully configure Poetry local venv. Trying fallback installer...")),(await o.initProject(e)).exitCode===0&&await b.pathExists(h.join(e,".venv"))?0:(console.log(l.yellow("\u26A0\uFE0F Python init fallback: installing dependencies directly into project .venv")),await hs(e))}async function Gt(e){let o=await dt("init",e);if(o===0)return 0;let t=["npm","pnpm","yarn"];for(let r of t){if(!await _r(r,e))continue;if(await W(r,["install"],e)===0)return console.log(l.green(`\u2705 Node init fallback succeeded with ${r} install`)),0}return o}async function ur(e){if(e[0]!=="create"||e[1]!=="project")return 1;let o=e[2],t=e[3];if(!o||!t)return process.stderr.write(`Usage: rapidkit create project gofiber.standard <name> [--output <dir>]
3506
- `),1;let r=pt(e,"--output")||process.cwd(),i=h.resolve(r,t),n=e.includes("--skip-git")||e.includes("--no-git");try{let{default:a}=await import('fs-extra');if(await a.ensureDir(h.dirname(i)),await a.pathExists(i))return process.stderr.write(`\u274C Directory "${i}" already exists
3507
- `),1;await a.ensureDir(i),await Vt(i,{project_name:t,module_path:t,skipGit:n});let s=Y(process.cwd());if(s){let{syncWorkspaceProjects:c}=await import('./workspace-KRZ3DWL4.js');await c(s,true);}return 0}catch(a){return process.stderr.write(`RapidKit Go/Fiber generator failed: ${a?.message??a}
3508
- `),1}}async function gr(e){if(e[0]!=="create"||e[1]!=="project")return 1;let o=e[2],t=e[3];if(!o||!t)return process.stderr.write(`Usage: rapidkit create project gogin.standard <name> [--output <dir>]
3509
- `),1;let r=pt(e,"--output")||process.cwd(),i=h.resolve(r,t),n=e.includes("--skip-git")||e.includes("--no-git");try{let{default:a}=await import('fs-extra');if(await a.ensureDir(h.dirname(i)),await a.pathExists(i))return process.stderr.write(`\u274C Directory "${i}" already exists
3510
- `),1;await a.ensureDir(i),await Bt(i,{project_name:t,module_path:t,skipGit:n});let s=Y(process.cwd());if(s){let{syncWorkspaceProjects:c}=await import('./workspace-KRZ3DWL4.js');await c(s,true);}return 0}catch(a){return process.stderr.write(`RapidKit Go/Gin generator failed: ${a?.message??a}
3511
- `),1}}async function io(e,o){if(e.includes("--json"))return process.stderr.write("RapidKit (npm) offline fallback does not support --json for `create` commands.\nInstall Python 3.10+ and retry the same command.\n"),1;if(e[0]!=="create")return 1;if(e[1]!=="project")return process.stderr.write(`RapidKit (npm) could not run the Python core engine for \`create\`.
3512
- Reason: ${o}.
99
+ `)),console.log(s.white(`Set your API key: rapidkit config set-api-key
100
+ `)),process.exit(1)),nt(o);let r=await po();process.exit(r?0:1);}catch(o){a$1.error("Failed to update embeddings:",o.message),process.exit(1);}});}var ct=class{constructor(e){this.runCommand=e;}runtime="go";async run(e,o,r){return {exitCode:await this.runCommand(e,o,r)}}async ensureGoInstalled(e){return (await this.run("go",["version"],e)).exitCode===0?null:{exitCode:1,message:"Go toolchain is not installed or not available on PATH. Install Go from https://go.dev/dl/ and retry."}}findWorkspaceRoot(e){let o=e;for(;;){if(w.existsSync(f.join(o,".rapidkit-workspace")))return o;let r=f.dirname(o);if(r===o)break;o=r;}return null}resolveDependencyMode(e){let o=process.env.RAPIDKIT_DEP_SHARING_MODE?.toLowerCase();if(o==="shared-runtime-caches"||o==="shared-node-deps"||o==="isolated")return o;let r=this.findWorkspaceRoot(e);if(!r)return "isolated";let n=f.join(r,".rapidkit","policies.yml");if(!w.existsSync(n))return "isolated";try{let c=w.readFileSync(n,"utf-8").match(/^\s*dependency_sharing_mode:\s*([a-zA-Z\-]+)\s*(?:#.*)?$/m)?.[1]?.toLowerCase();if(c==="shared-runtime-caches"||c==="shared-node-deps"||c==="isolated")return c}catch{}return "isolated"}withGoCacheEnv(e,o){let r=this.resolveDependencyMode(e),n=process.env.RAPIDKIT_WORKSPACE_PATH||this.findWorkspaceRoot(e),i=r==="shared-runtime-caches"?f.join(n||e,".rapidkit","cache","go"):f.join(e,".rapidkit","cache","go"),a=process.env.GOMODCACHE,c=process.env.GOCACHE;return process.env.GOMODCACHE=f.join(i,"mod"),process.env.GOCACHE=f.join(i,"build"),o().finally(()=>{typeof a>"u"?delete process.env.GOMODCACHE:process.env.GOMODCACHE=a,typeof c>"u"?delete process.env.GOCACHE:process.env.GOCACHE=c;})}async checkPrereqs(){return this.run("go",["version"],process.cwd())}async warmSetupCache(e){return this.withGoCacheEnv(e,async()=>{try{return process.env.GOMODCACHE&&w.mkdirSync(process.env.GOMODCACHE,{recursive:true}),process.env.GOCACHE&&w.mkdirSync(process.env.GOCACHE,{recursive:true}),{exitCode:0}}catch{return {exitCode:1,message:"Failed to prepare Go cache directories"}}})}async initProject(e){return this.withGoCacheEnv(e,async()=>{let o=await this.ensureGoInstalled(e);return o||this.run("go",["mod","tidy"],e)})}async runDev(e){return this.withGoCacheEnv(e,()=>(async()=>{let o=await this.ensureGoInstalled(e);if(o)return o;let r=f.join(e,"Makefile");return w.existsSync(r)?this.run("make",["run"],e):this.run("go",["run","./main.go"],e)})())}async runTest(e){return this.withGoCacheEnv(e,async()=>{let o=await this.ensureGoInstalled(e);return o||this.run("go",["test","./..."],e)})}async runBuild(e){return this.withGoCacheEnv(e,async()=>{let o=await this.ensureGoInstalled(e);return o||this.run("go",["build","./..."],e)})}async runStart(e){return this.withGoCacheEnv(e,async()=>{let r=(a()?[f.join(e,"server.exe"),f.join(e,"server")]:[f.join(e,"server")]).find(i=>w.existsSync(i));if(r)return this.run(r,[],e);let n=await this.ensureGoInstalled(e);return n||this.run("go",["run","./main.go"],e)})}async doctorHints(e){return ["Install Go from https://go.dev/dl/ if missing.","Run go mod tidy when dependencies are out of sync.","Use make run for hot-reload if Makefile exists."]}};var lt=class{constructor(e){this.runCommand=e;}runtime="node";async run(e,o,r){return {exitCode:await this.runCommand(e,o,r)}}findWorkspaceRoot(e){let o=e;for(;;){if(w.existsSync(f.join(o,".rapidkit-workspace")))return o;let r=f.dirname(o);if(r===o)break;o=r;}return null}resolveDependencyMode(e){let o=process.env.RAPIDKIT_DEP_SHARING_MODE?.toLowerCase();if(o==="shared-runtime-caches"||o==="shared-node-deps"||o==="isolated")return o;let r=this.findWorkspaceRoot(e);if(!r)return "isolated";let n=f.join(r,".rapidkit","policies.yml");if(!w.existsSync(n))return "isolated";try{let c=w.readFileSync(n,"utf-8").match(/^\s*dependency_sharing_mode:\s*([a-zA-Z\-]+)\s*(?:#.*)?$/m)?.[1]?.toLowerCase();if(c==="shared-runtime-caches"||c==="shared-node-deps"||c==="isolated")return c}catch{}return "isolated"}withDependencyEnv(e,o,r){let n=this.resolveDependencyMode(e),i=process.env.RAPIDKIT_WORKSPACE_PATH||this.findWorkspaceRoot(e),a=n==="isolated"?f.join(e,".rapidkit","cache","node"):f.join(i||e,".rapidkit","cache","node"),c=process.env.npm_config_cache,m=process.env.npm_config_store_dir;return o==="pnpm"?(process.env.npm_config_store_dir=f.join(a,"pnpm-store"),process.env.npm_config_cache=f.join(a,"pnpm-cache")):o==="yarn"?process.env.npm_config_cache=f.join(a,"yarn-cache"):process.env.npm_config_cache=f.join(a,"npm-cache"),r().finally(()=>{typeof c>"u"?delete process.env.npm_config_cache:process.env.npm_config_cache=c,typeof m>"u"?delete process.env.npm_config_store_dir:process.env.npm_config_store_dir=m;})}detectPackageManager(e){return w.existsSync(f.join(e,"pnpm-lock.yaml"))?"pnpm":w.existsSync(f.join(e,"yarn.lock"))?"yarn":"npm"}scriptArgs(e,o){return e==="npm"?["run",o]:["run",o]}async checkPrereqs(){return this.run("node",["--version"],process.cwd())}async warmSetupCache(e){let o=this.detectPackageManager(e);return this.withDependencyEnv(e,o,async()=>{try{return process.env.npm_config_cache&&w.mkdirSync(process.env.npm_config_cache,{recursive:true}),o==="pnpm"&&process.env.npm_config_store_dir&&w.mkdirSync(process.env.npm_config_store_dir,{recursive:true}),{exitCode:0}}catch{return {exitCode:1,message:"Failed to prepare Node cache directories"}}})}async initProject(e){let o=this.detectPackageManager(e),r=this.resolveDependencyMode(e),n=r==="shared-runtime-caches"||r==="shared-node-deps"?["install","--prefer-offline"]:["install"];return this.withDependencyEnv(e,o,()=>this.run(o,n,e))}async runDev(e){let o=this.detectPackageManager(e);return this.withDependencyEnv(e,o,()=>this.run(o,this.scriptArgs(o,"dev"),e))}async runTest(e){let o=this.detectPackageManager(e);return this.withDependencyEnv(e,o,()=>this.run(o,this.scriptArgs(o,"test"),e))}async runBuild(e){let o=this.detectPackageManager(e);return this.withDependencyEnv(e,o,()=>this.run(o,this.scriptArgs(o,"build"),e))}async runStart(e){let o=this.detectPackageManager(e);return this.withDependencyEnv(e,o,()=>this.run(o,this.scriptArgs(o,"start"),e))}async doctorHints(e){return ["Install Node.js LTS and ensure node/npm are on PATH.","Use lockfiles (package-lock.json, pnpm-lock.yaml, yarn.lock) for deterministic installs.","Run install before dev/test/build if dependencies changed."]}};var dt=class{constructor(e){this.runCore=e;}runtime="python";async run(e,o){return {exitCode:await this.withPythonCacheEnv(o,()=>this.runCore(e,o))}}findWorkspaceRoot(e){let o=e;for(;;){if(w.existsSync(f.join(o,".rapidkit-workspace")))return o;let r=f.dirname(o);if(r===o)break;o=r;}return null}resolveDependencyMode(e){let o=process.env.RAPIDKIT_DEP_SHARING_MODE?.toLowerCase();if(o==="shared-runtime-caches"||o==="shared-node-deps"||o==="isolated")return o;let r=this.findWorkspaceRoot(e);if(!r)return "isolated";let n=f.join(r,".rapidkit","policies.yml");if(!w.existsSync(n))return "isolated";try{let c=w.readFileSync(n,"utf-8").match(/^\s*dependency_sharing_mode:\s*([a-zA-Z\-]+)\s*(?:#.*)?$/m)?.[1]?.toLowerCase();if(c==="shared-runtime-caches"||c==="shared-node-deps"||c==="isolated")return c}catch{}return "isolated"}withPythonCacheEnv(e,o){let r=this.resolveDependencyMode(e),n=process.env.RAPIDKIT_WORKSPACE_PATH||this.findWorkspaceRoot(e),i=r==="shared-runtime-caches"?f.join(n||e,".rapidkit","cache","python"):f.join(e,".rapidkit","cache","python"),a=process.env.PIP_CACHE_DIR,c=process.env.POETRY_CACHE_DIR;return process.env.PIP_CACHE_DIR=f.join(i,"pip"),process.env.POETRY_CACHE_DIR=f.join(i,"poetry"),o().finally(()=>{typeof a>"u"?delete process.env.PIP_CACHE_DIR:process.env.PIP_CACHE_DIR=a,typeof c>"u"?delete process.env.POETRY_CACHE_DIR:process.env.POETRY_CACHE_DIR=c;})}async checkPrereqs(){let e=process.cwd(),o=await this.run(["doctor","check"],e);return o.exitCode===0?o:this.run(["doctor"],e)}async initProject(e){return this.run(["init"],e)}async runDev(e){return this.run(["dev"],e)}async runTest(e){return this.run(["test"],e)}async runBuild(e){return this.run(["build"],e)}async runStart(e){return this.run(["start"],e)}async doctorHints(e){return ['Run "npx rapidkit doctor workspace" for a full workspace scan.','Use "npx rapidkit init" after adding or changing modules.','Use workspace launcher "./rapidkit" to avoid environment drift.']}};function Pr(){let t={...process.env},e=t.PATH||"";if(e){let o=e.split(f.delimiter).filter(r=>!r.replace(/\\/g,"/").includes("/.pyenv/shims")).join(f.delimiter);t.PATH=o;}return t.PYENV_VERSION="system",t.POETRY_PYTHON=t.POETRY_PYTHON||c$2(),typeof t.RAPIDKIT_SKIP_LOCK_SYNC>"u"&&(t.RAPIDKIT_SKIP_LOCK_SYNC="1"),t}function Se(t,e){return t==="go"?new ct((o,r,n)=>e.runCommandInCwd(o,r,n)):t==="node"?new lt((o,r,n)=>e.runCommandInCwd(o,r,n)):new dt((o,r)=>e.runCoreRapidkit(o,{cwd:r,env:Pr()}))}var go=f.join(rr.homedir(),".rapidkit","cache"),yo=1440*60*1e3;function Rt(){let t=process.env.RAPIDKIT_CACHE_DIR?.trim();if(t)return t;let e=process.env.VITEST_WORKER_ID?.trim();return e?f.join(go,`vitest-${e}`):go}var ut=class t{static instance;memoryCache=new Map;constructor(){}static getInstance(){return t.instance||(t.instance=new t),t.instance}getCacheKey(e){return createHash("md5").update(e).digest("hex")}getCachePath(e){return f.join(Rt(),`${this.getCacheKey(e)}.json`)}async get(e,o="1.0"){let r=this.memoryCache.get(e);if(r&&r.version===o&&Date.now()-r.timestamp<yo)return a$1.debug(`Cache hit (memory): ${e}`),r.data;try{let n=this.getCachePath(e),i=await promises.readFile(n,"utf-8"),a=JSON.parse(i);if(a.version===o&&Date.now()-a.timestamp<yo)return a$1.debug(`Cache hit (disk): ${e}`),this.memoryCache.set(e,a),a.data;await promises.unlink(n).catch(()=>{});}catch{a$1.debug(`Cache miss: ${e}`);}return null}async set(e,o,r="1.0"){let n={data:o,timestamp:Date.now(),version:r};this.memoryCache.set(e,n);try{await promises.mkdir(Rt(),{recursive:true});let i=this.getCachePath(e);await promises.writeFile(i,JSON.stringify(n),"utf-8"),a$1.debug(`Cache set: ${e}`);}catch(i){a$1.debug(`Cache write failed: ${e}`,i);}}async invalidate(e){this.memoryCache.delete(e);try{let o=this.getCachePath(e);await promises.unlink(o),a$1.debug(`Cache invalidated: ${e}`);}catch{}}async clear(){this.memoryCache.clear();try{let e=Rt(),o=await promises.readdir(e);await Promise.all(o.map(r=>promises.unlink(f.join(e,r)))),a$1.debug("Cache cleared");}catch{}}};function ye(t){let e=t;for(;;){let o=f.join(e,".rapidkit","project.json");if(w.existsSync(o))try{return JSON.parse(w.readFileSync(o,"utf8"))}catch{return null}let r=f.dirname(e);if(r===e)break;e=r;}return null}function ne(t,e){let o=t?.runtime?.toLowerCase(),r=t?.kit_name?.toLowerCase(),n=w.existsSync(f.join(e,"go.mod"));return o==="go"||(r?.startsWith("gofiber")??false)||(r?.startsWith("gogin")??false)||n}function ie(t,e){let o=t?.runtime?.toLowerCase(),r=t?.kit_name?.toLowerCase(),n=w.existsSync(f.join(e,"package.json"));return o==="node"||o==="typescript"||(r?.startsWith("nestjs")??false)||n}function se(t,e){let o=t?.runtime?.toLowerCase(),r=t?.kit_name?.toLowerCase(),n=w.existsSync(f.join(e,"pyproject.toml")),i=w.existsSync(f.join(e,"requirements.txt"))||w.existsSync(f.join(e,"requirements.in"));return o==="python"||(r?.startsWith("fastapi")??false)||n||i}async function Er(t){let e=await promises.readFile(t);return createHash("sha256").update(e).digest("hex")}async function ho(t,e){await x.outputFile(t,`${JSON.stringify(e,null,2)}
101
+ `,"utf-8");}function me(t,e){return f.isAbsolute(e)?e:f.join(t,e)}function Ar(t,e){if(t.target)return t.target;if(t.source)return f.basename(t.source);if(t.url)try{let o=new URL(t.url).pathname,r=f.basename(o);if(r&&r!=="/")return r}catch{}return `${e}.artifact`}async function Ir(t){let e=f.join(t,".rapidkit","trusted-sources.lock"),o=new Set(["localhost","127.0.0.1"]);if(!await x.pathExists(e))return o;try{let n=(await promises.readFile(e,"utf-8")).split(/\r?\n/).map(i=>i.trim()).filter(i=>i.length>0&&!i.startsWith("#"));for(let i of n)o.add(i.toLowerCase());}catch{}return o}async function jr(t,e,o){await x.ensureDir(f.dirname(e)),await new Promise((r,n)=>{let a=(t.startsWith("https://")?Po:bo).get(t,c=>{if(!c.statusCode||c.statusCode<200||c.statusCode>=300){n(new Error(`HTTP ${c.statusCode||"unknown"}`)),c.resume();return}let m=createWriteStream(e);c.pipe(m),m.on("finish",()=>{m.close(),r();}),m.on("error",d=>{n(d);});});a.setTimeout(o,()=>{a.destroy(new Error(`Request timeout after ${o}ms`));}),a.on("error",c=>{n(c);});});}async function $r(t,e,o,r,n){let i=new URL(t),a=JSON.stringify(e),c=i.protocol==="https:"?Po:bo;await new Promise((m,d)=>{let l=c.request({method:"POST",hostname:i.hostname,port:i.port||(i.protocol==="https:"?443:80),path:`${i.pathname}${i.search}`,headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(a),...r?{Authorization:`Bearer ${r}`}:{},...n||{}}},u=>{if(!u.statusCode||u.statusCode<200||u.statusCode>=300){d(new Error(`HTTP ${u.statusCode||"unknown"}`)),u.resume();return}u.resume(),m();});l.setTimeout(o,()=>{l.destroy(new Error(`Request timeout after ${o}ms`));}),l.on("error",u=>{d(u);}),l.write(a),l.end();});}function wo(t){return new Promise(e=>setTimeout(e,t))}async function Tr(t,e,o,r){let n=me(t,e||".rapidkit/reports/evidence-export-dead-letter.ndjson");return await x.ensureDir(f.dirname(n)),await promises.appendFile(n,`${JSON.stringify({timestamp:new Date().toISOString(),reason:r,payload:o})}
102
+ `,"utf-8"),n}function Or(t,e){if(!e?.enabled)return {headers:{}};let o=process.env[e.hmacKeyEnv];if(!o)return {headers:{},error:`Evidence signing key env is missing: ${e.hmacKeyEnv}`};let r=(e.algorithm||"sha256").toLowerCase(),n=e.headerName||"x-rapidkit-evidence-signature",i=createHmac(r,o).update(JSON.stringify(t)).digest("hex");return {headers:{[n]:i,"x-rapidkit-evidence-signature-alg":r}}}async function Mr(t,e,o){let r=(o.algorithm||"sha256").toLowerCase(),n=me(t,o.publicKeyPath);if(!await x.pathExists(n))return {verified:false,algorithm:r,publicKeyPath:n,publicKeyFingerprint:"",signature:o.signature,message:`Public key not found: ${n}`};try{let i=await promises.readFile(n,"utf-8"),a=await promises.readFile(e),c=createVerify(r);c.update(a),c.end();let m=Buffer.from(o.signature,"base64"),d=c.verify(i,m),l=createHash("sha256").update(i).digest("hex");return {verified:d,algorithm:r,publicKeyPath:n,publicKeyFingerprint:l,signature:o.signature,message:d?"Attestation verified.":"Attestation signature verification failed."}}catch(i){return {verified:false,algorithm:r,publicKeyPath:n,publicKeyFingerprint:"",signature:o.signature,message:`Attestation verification error: ${i.message}`}}}async function Dr(t,e,o,r){let n=process.env.RAPIDKIT_SIGSTORE_MOCK;if(n==="success")return {verified:true,tlogVerified:r.requireTransparencyLog,message:"Sigstore verification passed (mock).",identity:o.identity||null,issuer:o.issuer||null,rekorUrl:o.rekorUrl||null,bundlePath:o.bundlePath||null,certificatePath:o.certificatePath||null,signaturePath:o.signaturePath||null};if(n==="fail")return {verified:false,tlogVerified:false,message:"Sigstore verification failed (mock).",identity:o.identity||null,issuer:o.issuer||null,rekorUrl:o.rekorUrl||null,bundlePath:o.bundlePath||null,certificatePath:o.certificatePath||null,signaturePath:o.signaturePath||null};let i=o.signaturePath?me(t,o.signaturePath):null;if(!i||!await x.pathExists(i))return {verified:false,tlogVerified:false,message:"Sigstore signaturePath is missing or not found.",identity:o.identity||null,issuer:o.issuer||null,rekorUrl:o.rekorUrl||null,bundlePath:o.bundlePath||null,certificatePath:o.certificatePath||null,signaturePath:i};let a=["verify-blob",e,"--signature",i],c=o.certificatePath?me(t,o.certificatePath):null;c&&a.push("--certificate",c);let m=o.bundlePath?me(t,o.bundlePath):null;m&&a.push("--bundle",m);let d=o.keyPath?me(t,o.keyPath):null;d&&a.push("--key",d),o.identity&&a.push("--certificate-identity",o.identity),o.issuer&&a.push("--certificate-oidc-issuer",o.issuer),o.rekorUrl&&a.push("--rekor-url",o.rekorUrl),r.requireTransparencyLog||a.push("--insecure-ignore-tlog");try{let l=await execa("cosign",a,{reject:false});return l.exitCode===0?{verified:true,tlogVerified:r.requireTransparencyLog,message:"Sigstore verification passed.",identity:o.identity||null,issuer:o.issuer||null,rekorUrl:o.rekorUrl||null,bundlePath:m,certificatePath:c,signaturePath:i}:{verified:false,tlogVerified:false,message:`Sigstore verification failed: ${l.stderr||l.stdout||"unknown error"}`,identity:o.identity||null,issuer:o.issuer||null,rekorUrl:o.rekorUrl||null,bundlePath:m,certificatePath:c,signaturePath:i}}catch(l){return {verified:false,tlogVerified:false,message:`Sigstore verification error: ${l.message}`,identity:o.identity||null,issuer:o.issuer||null,rekorUrl:o.rekorUrl||null,bundlePath:m,certificatePath:c,signaturePath:i}}}async function Nr(t,e){let o=e.algorithm||"sha256",r=me(t,e.policyPath),n=me(t,e.signaturePath),i=me(t,e.publicKeyPath);if(!await x.pathExists(r))return {verified:false,message:`Governance policy bundle not found: ${r}`,policies:null};if(!await x.pathExists(n))return {verified:false,message:`Governance policy signature not found: ${n}`,policies:null};if(!await x.pathExists(i))return {verified:false,message:`Governance policy public key not found: ${i}`,policies:null};try{let a=await promises.readFile(r,"utf-8"),c=(await promises.readFile(n,"utf-8")).trim(),m=await promises.readFile(i,"utf-8"),d=createVerify(o);return d.update(a),d.end(),d.verify(m,Buffer.from(c,"base64"))?{verified:true,message:"Governance policy bundle verified.",policies:JSON.parse(a).policies||{}}:{verified:false,message:"Governance policy bundle signature verification failed.",policies:null}}catch(a){return {verified:false,message:`Governance policy bundle verification error: ${a.message}`,policies:null}}}async function St(t,e){let o=[],r={syncedArtifacts:0,verifiedArtifacts:0,rotatedFiles:0,lockWritten:false,governanceBundleVerified:false,transparencyEvidenceWritten:false,transparencyEvidenceRecords:0,evidenceExported:false,evidenceExportTarget:null},n=f.join(t,".rapidkit"),i=f.join(n,"mirror-config.json"),a=f.join(n,"mirror.lock"),c=f.join(n,"mirror","artifacts"),m=f.join(n,"reports"),d=await Ir(t);if(!await x.pathExists(i))return o.push({id:"mirror.lifecycle",status:"skipped",message:"Mirror lifecycle skipped: .rapidkit/mirror-config.json not found."}),{checks:o,details:r};let l={};try{l=JSON.parse(await promises.readFile(i,"utf-8"));}catch{return o.push({id:"mirror.lifecycle.config",status:"failed",message:"Mirror lifecycle failed: invalid JSON in mirror-config.json."}),{checks:o,details:r}}if(!(e.forceRun===true||e.ciMode||e.offlineMode||l.mode==="offline-only"))return o.push({id:"mirror.lifecycle",status:"skipped",message:"Mirror lifecycle skipped: not in ci/offline mode."}),{checks:o,details:r};await x.ensureDir(c);let y=Math.max(0,l.prefetch?.retries??2),v=Math.max(0,l.prefetch?.backoffMs??250),p=Math.max(1e3,l.prefetch?.timeoutMs??15e3),g=l.security?.requireAttestation===true,A=l.security?.requireSigstore===true,P=l.security?.requireTransparencyLog===true,S=l.security?.requireSignedGovernance===true,b=l.security?.evidenceExport,_=(process.env.RAPIDKIT_ENV||l.security?.governance?.environment||"dev").toLowerCase(),M=l.security?.governance?.policies||{};if(l.security?.governanceBundle){let U=await Nr(t,l.security.governanceBundle);if(o.push({id:"governance.bundle.verify",status:U.verified?"passed":"failed",message:U.message}),U.verified&&U.policies)M=U.policies,r.governanceBundleVerified=true;else if(S)return {checks:o,details:r}}let T=M[_],I=P||T?.requireTransparencyLog===true,fe=[],Ne=Array.isArray(l.artifacts)?l.artifacts:[],le=[];for(let U=0;U<Ne.length;U+=1){let D=Ne[U],k=D.id||`artifact-${U+1}`,Q=D.source?me(t,D.source):null,Z=Ar(D,k),K=f.join(c,Z),B=false,ee={sourceType:"path",source:Q||D.url||"unknown",host:null,fetchedAt:new Date().toISOString(),attempts:1,trusted:true};if(Q&&await x.pathExists(Q))await x.ensureDir(f.dirname(K)),await x.copyFile(Q,K),r.syncedArtifacts+=1,B=true,ee={sourceType:"path",source:Q,host:null,fetchedAt:new Date().toISOString(),attempts:1,trusted:true},o.push({id:`mirror.sync.${k}`,status:"passed",message:`Mirrored artifact ${k} from source path.`});else if(D.url){let H="";try{H=new URL(D.url).hostname.toLowerCase();}catch{o.push({id:`mirror.prefetch.${k}`,status:"failed",message:`Invalid URL for ${k}: ${D.url}`});continue}if(!(process.env.RAPIDKIT_TRUSTED_SOURCES==="1"||d.has(H))){o.push({id:`mirror.prefetch.trust.${k}`,status:"failed",message:`Untrusted mirror host for ${k}: ${H}. Add host to .rapidkit/trusted-sources.lock or set RAPIDKIT_TRUSTED_SOURCES=1.`});continue}if(e.offlineMode&&(await x.pathExists(K)?(B=true,ee={sourceType:"url",source:D.url,host:H,fetchedAt:new Date().toISOString(),attempts:0,trusted:true},o.push({id:`mirror.prefetch.${k}`,status:"passed",message:`Offline mode reused existing mirrored artifact ${k}.`})):o.push({id:`mirror.prefetch.${k}`,status:"failed",message:`Offline mode cannot prefetch remote artifact ${k} without an existing mirrored copy.`}),!B))continue;if(!B){let z=null,ve=0;for(let ke=1;ke<=y+1;ke+=1){ve=ke;try{await jr(D.url,K,p),r.syncedArtifacts+=1,B=true,ee={sourceType:"url",source:D.url,host:H,fetchedAt:new Date().toISOString(),attempts:ve,trusted:true},o.push({id:`mirror.prefetch.${k}`,status:"passed",message:ve>1?`Prefetched artifact ${k} from ${H} after ${ve} attempts.`:`Prefetched artifact ${k} from ${H}.`});break}catch(Ve){if(z=Ve,ke<=y){await wo(v*ke);continue}}}if(!B){o.push({id:`mirror.prefetch.${k}`,status:"failed",message:`Failed to prefetch ${k} after ${y+1} attempt(s): ${z?.message||"unknown error"}`});continue}}if(!B){o.push({id:`mirror.prefetch.${k}`,status:"failed",message:`Failed to prefetch ${k}.`});continue}}if(!B){D.required||e.offlineMode?o.push({id:`mirror.sync.${k}`,status:"failed",message:`Mirror source missing for ${k}${Q?`: ${Q}`:""}`}):o.push({id:`mirror.sync.${k}`,status:"skipped",message:`Mirror source not found for optional artifact ${k}.`});continue}let C=await Er(K);if(D.sha256&&D.sha256.toLowerCase()!==C.toLowerCase()){o.push({id:`mirror.verify.${k}`,status:"failed",message:`Checksum mismatch for ${k}.`});continue}r.verifiedArtifacts+=1,o.push({id:`mirror.verify.${k}`,status:"passed",message:`Checksum verified for ${k}.`});let R=D.attestation?await Mr(t,K,D.attestation):null;if(D.attestation){if(o.push({id:`mirror.attest.${k}`,status:R?.verified?"passed":"failed",message:R?.message||"Attestation verification failed."}),!R?.verified)continue}else if(g){o.push({id:`mirror.attest.${k}`,status:"failed",message:`Attestation is required but missing for ${k}.`});continue}else o.push({id:`mirror.attest.${k}`,status:"skipped",message:`No attestation provided for ${k}.`});let O=D.attestation?.sigstore,j=O?await Dr(t,K,O,{requireTransparencyLog:I}):null;if(O){if(o.push({id:`mirror.sigstore.${k}`,status:j?.verified?"passed":"failed",message:j?.message||"Sigstore verification failed."}),fe.push({artifactId:k,verified:!!j?.verified,tlogVerified:!!j?.tlogVerified,identity:j?.identity||null,issuer:j?.issuer||null,rekorUrl:j?.rekorUrl||null,timestamp:new Date().toISOString(),environment:_}),!j?.verified)continue}else if(A){o.push({id:`mirror.sigstore.${k}`,status:"failed",message:`Sigstore attestation is required but missing for ${k}.`});continue}else o.push({id:`mirror.sigstore.${k}`,status:"skipped",message:`No Sigstore attestation provided for ${k}.`});if(O&&j?.verified&&T){let H=T.allowedIdentities||[];if(H.length>0){let z=!!j.identity&&H.includes(j.identity);if(o.push({id:`mirror.sigstore.policy.identity.${k}`,status:z?"passed":"failed",message:z?`Sigstore identity policy passed for ${k} in ${_}.`:`Sigstore identity policy failed for ${k} in ${_}.`}),!z)continue}let de=T.allowedIssuers||[];if(de.length>0){let z=!!j.issuer&&de.includes(j.issuer);if(o.push({id:`mirror.sigstore.policy.issuer.${k}`,status:z?"passed":"failed",message:z?`Sigstore issuer policy passed for ${k} in ${_}.`:`Sigstore issuer policy failed for ${k} in ${_}.`}),!z)continue}let we=T.allowedRekorUrls||[];if(we.length>0){let z=!!j.rekorUrl&&we.includes(j.rekorUrl);if(o.push({id:`mirror.sigstore.policy.rekor.${k}`,status:z?"passed":"failed",message:z?`Sigstore Rekor policy passed for ${k} in ${_}.`:`Sigstore Rekor policy failed for ${k} in ${_}.`}),!z)continue}}else T&&o.push({id:`mirror.sigstore.policy.${k}`,status:"skipped",message:`Sigstore governance policy configured for ${_} but no verified Sigstore attestation for ${k}.`});let Y=await promises.stat(K);le.push({id:k,path:f.relative(t,K),sha256:C,size:Y.size,provenance:ee,attestation:{detached:{provided:!!D.attestation,verified:R?.verified||false,algorithm:R?.algorithm||null,publicKeyPath:R?.publicKeyPath||null,publicKeyFingerprint:R?.publicKeyFingerprint||null,signature:R?.signature||null,verifiedAt:R?.verified?new Date().toISOString():null},sigstore:{provided:!!O,verified:j?.verified||false,tlogVerified:j?.tlogVerified||false,identity:j?.identity||null,issuer:j?.issuer||null,rekorUrl:j?.rekorUrl||null,bundlePath:j?.bundlePath||null,certificatePath:j?.certificatePath||null,signaturePath:j?.signaturePath||null,verifiedAt:j?.verified?new Date().toISOString():null}}});}let ae=l.retention?.keepLast;if(typeof ae=="number"&&ae>0){let D=(await promises.readdir(c,{withFileTypes:true})).filter(k=>k.isFile()).map(k=>f.join(c,k.name));if(D.length>ae){let k=await Promise.all(D.map(async Z=>({filePath:Z,stat:await promises.stat(Z)})));k.sort((Z,K)=>K.stat.mtimeMs-Z.stat.mtimeMs);let Q=k.slice(ae);for(let Z of Q)await promises.unlink(Z.filePath),r.rotatedFiles+=1;}}o.push({id:"mirror.rotate",status:"passed",message:r.rotatedFiles>0?`Mirror retention rotation removed ${r.rotatedFiles} file(s).`:"Mirror retention rotation completed with no removals."});let je={schemaVersion:"1.0",generatedAt:new Date().toISOString(),mode:l.mode||null,environment:_,artifacts:le};if(await promises.writeFile(a,`${JSON.stringify(je,null,2)}
103
+ `,"utf-8"),r.lockWritten=true,o.push({id:"mirror.lock.write",status:"passed",message:`Mirror lock updated at ${f.relative(t,a)}.`}),r.transparencyEvidenceRecords=fe.length,fe.length>0){let U={schemaVersion:"1.0",generatedAt:new Date().toISOString(),environment:_,records:fe},D=new Date().toISOString().replace(/[:.]/g,"-"),k=f.join(m,`transparency-evidence-${D}.json`),Q=f.join(m,"transparency-evidence.latest.json");if(await x.ensureDir(m),await ho(k,U),await ho(Q,U),r.transparencyEvidenceWritten=true,o.push({id:"sigstore.evidence.write",status:"passed",message:`Transparency evidence written to ${f.relative(t,Q)}.`}),b?.enabled){let Z=Math.max(1e3,b.timeoutMs??1e4);if(b.target==="file")if(!b.filePath)o.push({id:"sigstore.evidence.export.file",status:"failed",message:"Evidence export target=file requires security.evidenceExport.filePath."});else try{let K=me(t,b.filePath);await x.ensureDir(f.dirname(K)),await promises.appendFile(K,`${JSON.stringify(U)}
104
+ `,"utf-8"),r.evidenceExported=true,r.evidenceExportTarget=K,o.push({id:"sigstore.evidence.export.file",status:"passed",message:`Transparency evidence exported to file sink ${K}.`});}catch(K){o.push({id:"sigstore.evidence.export.file",status:"failed",message:`Evidence file export failed: ${K.message}`});}else if(b.target==="http")if(!b.endpoint)o.push({id:"sigstore.evidence.export.http",status:"failed",message:"Evidence export target=http requires security.evidenceExport.endpoint."});else {let K=Math.max(0,b.retries??0),B=Math.max(0,b.backoffMs??500),ee=Or(U,b.signing);ee.error&&o.push({id:"sigstore.evidence.export.http",status:"failed",message:ee.error});try{let C=b.authTokenEnv?process.env[b.authTokenEnv]:void 0,R=false,O=null;for(let j=1;j<=K+1;j+=1)try{if(ee.error)throw new Error(ee.error);await $r(b.endpoint,U,Z,C,ee.headers),r.evidenceExported=true,r.evidenceExportTarget=b.endpoint,o.push({id:"sigstore.evidence.export.http",status:"passed",message:j>1?`Transparency evidence exported to HTTP endpoint ${b.endpoint} after ${j} attempts.`:`Transparency evidence exported to HTTP endpoint ${b.endpoint}.`}),R=true;break}catch(Y){O=Y,j<=K&&await wo(B*j);}if(!R)throw O||new Error("unknown evidence export error")}catch(C){let R=`Evidence HTTP export failed: ${C.message}`;o.push({id:"sigstore.evidence.export.http",status:"failed",message:R});try{let O=await Tr(t,b.deadLetterPath,U,R);o.push({id:"sigstore.evidence.export.deadletter",status:"passed",message:`Evidence export failure persisted to dead-letter sink ${O}.`});}catch(O){o.push({id:"sigstore.evidence.export.deadletter",status:"failed",message:`Evidence dead-letter write failed: ${O.message}`});}}}if(b.failOnError&&o.some(B=>B.status==="failed"&&(B.id==="sigstore.evidence.export.file"||B.id==="sigstore.evidence.export.http")))return {checks:o,details:r}}else o.push({id:"sigstore.evidence.export",status:"skipped",message:"Central evidence export not configured (security.evidenceExport.enabled=false)."});}else o.push({id:"sigstore.evidence.write",status:"skipped",message:"No Sigstore records available for transparency evidence output."}),o.push({id:"sigstore.evidence.export",status:"skipped",message:"Central evidence export skipped because no transparency evidence records exist."});return {checks:o,details:r}}function Et(t){if(!t||typeof t!="object")return null;let e=t.code;return e==="PYTHON_NOT_FOUND"||e==="BRIDGE_VENV_BOOTSTRAP_FAILED"?e:null}function Lr(t){let e=t.trim().toLowerCase();return e?e.startsWith("fastapi")?"fastapi":e.startsWith("nestjs")?"nestjs":null:null}function ht(t){let e=t.trim().toLowerCase();return e.startsWith("gofiber")||e==="go"||e==="go.standard"||e==="fiber"}function Ue(t){let e=t.trim().toLowerCase();return e.startsWith("gogin")||e==="gin"}function Je(t,e){let o=t.indexOf(e);if(o>=0&&o+1<t.length)return t[o+1];let r=t.find(n=>n.startsWith(`${e}=`));if(r)return r.slice(e.length+1)}function Ot(){return d$1()}function _o(){let t={...process.env},e=t.PATH||"";return e&&(t.PATH=e.split(f.delimiter).filter(o=>!o.replace(/\\/g,"/").includes("/.pyenv/shims")).join(f.delimiter)),t.PYENV_VERSION="system",t.POETRY_PYTHON||(t.POETRY_PYTHON=c$2()),t.RAPIDKIT_SKIP_LOCK_SYNC||(t.RAPIDKIT_SKIP_LOCK_SYNC="1"),t.POETRY_KEYRING_ENABLED||(t.POETRY_KEYRING_ENABLED="false"),t.PYTHON_KEYRING_BACKEND||(t.PYTHON_KEYRING_BACKEND="keyring.backends.null.Keyring"),t.POETRY_NO_INTERACTION||(t.POETRY_NO_INTERACTION="1"),t}function Fr(t){return f$1(f.join(t,".venv"))}async function Do(t,e){return await F(t,["--version"],e)===0}async function At(t){let e=f.join(t,"go.mod");if(await x__default.pathExists(e))return "go";let o=f.join(t,"package.json");if(await x__default.pathExists(o))return "node";let r=f.join(t,"pyproject.toml"),n=f.join(t,"requirements.txt"),i=f.join(t,"poetry.lock");return await x__default.pathExists(r)||await x__default.pathExists(n)||await x__default.pathExists(i)?"python":null}async function Wr(t){for(let e of Ot())if(await F(e,e==="py"?["-3","-m","venv",".venv"]:["-m","venv",".venv"],t)===0)return 0;return 1}async function No(t){for(let e of Ot())if(await F(e,e==="py"?["-3","-m","venv",".venv"]:["-m","venv",".venv"],t)===0)return 0;return 1}async function Gr(t){let e=f$1(f.join(t,".venv"));if(!await x__default.pathExists(e)){let i=await No(t);if(i!==0)return i}if(!await Do("poetry",t))return 0;let r=await F("poetry",["config","virtualenvs.in-project","true","--local"],t);if(r!==0)return r;let n=await F("poetry",["env","use",e],t);return n!==0?n:0}async function qr(t){let e=f$1(f.join(t,".venv"));if(!await x__default.pathExists(e)){let n=await No(t);if(n!==0)return n}await F(e,["-m","pip","install","--upgrade","pip","setuptools","wheel"],t);let o=f.join(t,"requirements.txt");if(await x__default.pathExists(o)&&await F(e,["-m","pip","install","-r","requirements.txt"],t)===0)return 0;let r=f.join(t,"pyproject.toml");return await x__default.pathExists(r)&&(await F(e,["-m","pip","install","-e","."],t)===0||await F(e,["-m","pip","install","."],t)===0)?0:1}async function mt(t,e){return await Gr(t)!==0&&console.log(s.yellow("\u26A0\uFE0F Could not fully configure Poetry local venv. Trying fallback installer...")),(await e.initProject(t)).exitCode===0&&await x__default.pathExists(f.join(t,".venv"))?0:(console.log(s.yellow("\u26A0\uFE0F Python init fallback: installing dependencies directly into project .venv")),await qr(t))}async function ft(t){let e=await He("init",t);if(e===0)return 0;let o=["npm","pnpm","yarn"];for(let r of o){if(!await Do(r,t))continue;if(await F(r,["install"],t)===0)return console.log(s.green(`\u2705 Node init fallback succeeded with ${r} install`)),0}return e}async function xo(t){if(t[0]!=="create"||t[1]!=="project")return 1;let e=t[2],o=t[3];if(!e||!o)return process.stderr.write(`Usage: rapidkit create project gofiber.standard <name> [--output <dir>]
105
+ `),1;let r=Je(t,"--output")||process.cwd(),n=f.resolve(r,o),i=t.includes("--skip-git")||t.includes("--no-git");try{let{default:a}=await import('fs-extra');if(await a.ensureDir(f.dirname(n)),await a.pathExists(n))return process.stderr.write(`\u274C Directory "${n}" already exists
106
+ `),1;await a.ensureDir(n);let{generateGoFiberKit:c}=await import('./gofiber-standard-NQHMZTFK.js');await c(n,{project_name:o,module_path:o,skipGit:i});let m=q(process.cwd());if(m){let{syncWorkspaceProjects:d}=await import('./workspace-VXNLNKCM.js');await d(m,true);}return 0}catch(a){return process.stderr.write(`RapidKit Go/Fiber generator failed: ${a?.message??a}
107
+ `),1}}async function Ro(t){if(t[0]!=="create"||t[1]!=="project")return 1;let e=t[2],o=t[3];if(!e||!o)return process.stderr.write(`Usage: rapidkit create project gogin.standard <name> [--output <dir>]
108
+ `),1;let r=Je(t,"--output")||process.cwd(),n=f.resolve(r,o),i=t.includes("--skip-git")||t.includes("--no-git");try{let{default:a}=await import('fs-extra');if(await a.ensureDir(f.dirname(n)),await a.pathExists(n))return process.stderr.write(`\u274C Directory "${n}" already exists
109
+ `),1;await a.ensureDir(n);let{generateGoGinKit:c}=await import('./gogin-standard-2G2C3VQL.js');await c(n,{project_name:o,module_path:o,skipGit:i});let m=q(process.cwd());if(m){let{syncWorkspaceProjects:d}=await import('./workspace-VXNLNKCM.js');await d(m,true);}return 0}catch(a){return process.stderr.write(`RapidKit Go/Gin generator failed: ${a?.message??a}
110
+ `),1}}async function It(t,e){if(t.includes("--json"))return process.stderr.write("RapidKit (npm) offline fallback does not support --json for `create` commands.\nInstall Python 3.10+ and retry the same command.\n"),1;if(t[0]!=="create")return 1;if(t[1]!=="project")return process.stderr.write(`RapidKit (npm) could not run the Python core engine for \`create\`.
111
+ Reason: ${e}.
3513
112
  Install Python 3.10+ to use the interactive wizard and full kit catalog.
3514
- `),1;let i=e[2],n=e[3];if(!i||!n)return process.stderr.write(`Usage: rapidkit create project <kit> <name> [--output <dir>]
113
+ `),1;let n=t[2],i=t[3];if(!n||!i)return process.stderr.write(`Usage: rapidkit create project <kit> <name> [--output <dir>]
3515
114
  Tip: offline fallback supports only fastapi* and nestjs* kits.
3516
- `),1;let a=us(i);if(!a)return process.stderr.write(`RapidKit (npm) could not run the Python core engine to create this kit.
3517
- Reason: ${o}.
3518
- Requested kit: ${i}
115
+ `),1;let a=Lr(n);if(!a)return process.stderr.write(`RapidKit (npm) could not run the Python core engine to create this kit.
116
+ Reason: ${e}.
117
+ Requested kit: ${n}
3519
118
  Offline fallback only supports: fastapi.standard, nestjs.standard (and their shorthands).
3520
119
  Install Python 3.10+ to access all kits.
3521
- `),1;let s=pt(e,"--output")||process.cwd(),c=h.resolve(s,n),p=e.includes("--skip-git")||e.includes("--no-git"),d=e.includes("--skip-install");try{if(await b.ensureDir(h.dirname(c)),await b.pathExists(c))return process.stderr.write(`\u274C Directory "${c}" already exists
3522
- `),1;let u="pip",m=Y(process.cwd());if(m)try{let{readWorkspaceMarker:w}=await import('./workspace-marker-IOPQ42A7.js'),f=await w(m);f?.metadata?.npm?.installMethod&&(u=f.metadata.npm.installMethod,a$1.debug(`Detected workspace engine: ${u}`));}catch(w){a$1.debug("Failed to read workspace marker",w);}else a$1.debug("No workspace found, using default engine: pip");if(await b.ensureDir(c),await ko(c,{project_name:n,template:a,kit_name:i,skipGit:p,skipInstall:d,engine:u}),m){let{syncWorkspaceProjects:w}=await import('./workspace-KRZ3DWL4.js');await w(m,true);}return 0}catch(u){return process.stderr.write(`RapidKit (npm) offline fallback failed: ${u?.message??u}
3523
- `),1}}async function Sr(e){let o=new Set(["--yes","-y","--skip-git","--skip-install","--debug","--dry-run","--no-update-check","--create-workspace","--no-workspace"]);if(e[0]==="create"&&(!e[1]||e[1].startsWith("-"))){let t=e.includes("--yes")||e.includes("-y"),r=e.slice(1),i;!process.stdin.isTTY||t?(i="workspace",process.stdin.isTTY&&console.log(l.gray("\u2139\uFE0F No subcommand provided for `create`; defaulting to `create workspace`."))):i=(await xe.prompt([{type:"rawlist",name:"createTarget",message:"What do you want to create?",choices:[{name:"workspace",value:"workspace"},{name:"project",value:"project"}]}])).createTarget;let n=["create",i,...r];return await Sr(n)}if(e[0]==="create"&&e[1]==="workspace")try{let t=e.includes("--yes")||e.includes("-y"),r=e.includes("--skip-git")||e.includes("--no-git"),i=e[2]&&!e[2].startsWith("-")?e[2]:void 0,n=pt(e,"--install-method"),a=n==="poetry"||n==="venv"||n==="pipx"?n:void 0,s=pt(e,"--profile"),c=s==="minimal"||s==="go-only"||s==="python-only"||s==="node-only"||s==="polyglot"||s==="enterprise"?s:void 0,p=i||(t?"my-workspace":(await xe.prompt([{type:"input",name:"workspaceName",message:"Workspace name:",default:"my-workspace"}])).workspaceName);if(!p||!p.trim())return process.stderr.write(`Workspace name is required.
3524
- `),1;try{Wt(p);}catch(w){if(w instanceof d)return process.stderr.write(`${w.message}
3525
- `),1;throw w}let d$1=h.resolve(process.cwd(),p);if(await b.pathExists(d$1))return process.stderr.write(`\u274C Directory "${p}" already exists
3526
- `),1;let u=await a$2(),m=u.author||process.env.USER||"RapidKit User";if(!t){let w=await xe.prompt([{type:"input",name:"author",message:"Author name:",default:m}]);w.author?.trim()&&(m=w.author.trim());}return await h$1(p,{skipGit:r,yes:t,userConfig:{...u,author:m},installMethod:a,profile:c}),0}catch(t){return process.stderr.write(`RapidKit (npm) failed to create workspace: ${t?.message??t}
3527
- `),1}try{if(e[0]==="create"&&e[1]==="project"){if(e.includes("--help")||e.includes("-h"))try{return await c$2(),await d$1(["create","project","--help"],{cwd:process.cwd()})}catch(f){return process.stderr.write(`RapidKit (npm) failed to run the Python core engine: ${f?.message??f}
3528
- `),1}if(!e[2]||e[2].startsWith("-")){console.log(l.bold(`
120
+ `),1;let c=Je(t,"--output")||process.cwd(),m=f.resolve(c,i),d=t.includes("--skip-git")||t.includes("--no-git"),l=t.includes("--skip-install");try{if(await x__default.ensureDir(f.dirname(m)),await x__default.pathExists(m))return process.stderr.write(`\u274C Directory "${m}" already exists
121
+ `),1;let u="pip",y=q(process.cwd());if(y)try{let{readWorkspaceMarker:p}=await import('./workspace-marker-IOPQ42A7.js'),g=await p(y);g?.metadata?.npm?.installMethod&&(u=g.metadata.npm.installMethod,a$1.debug(`Detected workspace engine: ${u}`));}catch(p){a$1.debug("Failed to read workspace marker",p);}else a$1.debug("No workspace found, using default engine: pip");await x__default.ensureDir(m);let{generateDemoKit:v}=await import('./demo-kit-63CFMCPD.js');if(await v(m,{project_name:i,template:a,kit_name:n,skipGit:d,skipInstall:l,engine:u}),y){let{syncWorkspaceProjects:p}=await import('./workspace-VXNLNKCM.js');await p(y,true);}return 0}catch(u){return process.stderr.write(`RapidKit (npm) offline fallback failed: ${u?.message??u}
122
+ `),1}}async function Ko(t){let e$1=new Set(["--yes","-y","--skip-git","--skip-install","--debug","--dry-run","--no-update-check","--create-workspace","--no-workspace"]);if(t[0]==="create"&&(!t[1]||t[1].startsWith("-"))){let o=t.includes("--yes")||t.includes("-y"),r=t.slice(1),n;!process.stdin.isTTY||o?(n="workspace",process.stdin.isTTY&&console.log(s.gray("\u2139\uFE0F No subcommand provided for `create`; defaulting to `create workspace`."))):n=(await he.prompt([{type:"rawlist",name:"createTarget",message:"What do you want to create?",choices:[{name:"workspace",value:"workspace"},{name:"project",value:"project"}]}])).createTarget;let i=["create",n,...r];return await Ko(i)}if(t[0]==="create"&&t[1]==="workspace")try{let o=t.includes("--yes")||t.includes("-y"),r=t.includes("--skip-git")||t.includes("--no-git"),n=t[2]&&!t[2].startsWith("-")?t[2]:void 0,i=Je(t,"--install-method"),a=i==="poetry"||i==="venv"||i==="pipx"?i:void 0,c=Je(t,"--profile"),m=c==="minimal"||c==="go-only"||c==="python-only"||c==="node-only"||c==="polyglot"||c==="enterprise"?c:void 0,d=n||(o?"my-workspace":(await he.prompt([{type:"input",name:"workspaceName",message:"Workspace name:",default:"my-workspace"}])).workspaceName);if(!d||!d.trim())return process.stderr.write(`Workspace name is required.
123
+ `),1;try{vt(d);}catch(p){if(p instanceof e)return process.stderr.write(`${p.message}
124
+ `),1;throw p}let l=f.resolve(process.cwd(),d);if(await x__default.pathExists(l))return process.stderr.write(`\u274C Directory "${d}" already exists
125
+ `),1;let u=await a$2(),y=u.author||process.env.USER||"RapidKit User";if(!o){let p=await he.prompt([{type:"input",name:"author",message:"Author name:",default:y}]);p.author?.trim()&&(y=p.author.trim());}let{createProject:v}=await import('./create-27NVMJAR.js');return await v(d,{skipGit:r,yes:o,userConfig:{...u,author:y},installMethod:a,profile:m}),0}catch(o){return process.stderr.write(`RapidKit (npm) failed to create workspace: ${o?.message??o}
126
+ `),1}try{if(t[0]==="create"&&t[1]==="project"){if(t.includes("--help")||t.includes("-h"))try{return await c$1(),await d(["create","project","--help"],{cwd:process.cwd()})}catch(p){return process.stderr.write(`RapidKit (npm) failed to run the Python core engine: ${p?.message??p}
127
+ `),1}if(!t[2]||t[2].startsWith("-")){console.log(s.bold(`
3529
128
  \u{1F680} RapidKit
3530
- `));let{kitChoice:f}=await xe.prompt([{type:"rawlist",name:"kitChoice",message:"Select a kit to scaffold:",choices:[{name:"fastapi \u2014 FastAPI Standard Kit",value:"fastapi.standard"},{name:"fastapi \u2014 FastAPI DDD Kit",value:"fastapi.ddd"},{name:"nestjs \u2014 NestJS Standard Kit",value:"nestjs.standard"},{name:"go/fiber \u2014 Go Fiber Standard Kit",value:"gofiber.standard"},{name:"go/gin \u2014 Go Gin Standard Kit",value:"gogin.standard"}]}]);if(qt(f)||lt(f)){let{projectName:R}=await xe.prompt([{type:"input",name:"projectName",message:"Project name:",validate:T=>T.trim().length>0||"Project name is required"}]),x=e.slice(2).filter(T=>T.startsWith("-"));return lt(f)?await gr(["create","project",f,R.trim(),...x]):await ur(["create","project",f,R.trim(),...x])}let{projectName:g}=await xe.prompt([{type:"input",name:"projectName",message:"Project name:",validate:R=>R.trim().length>0||"Project name is required"}]);e.splice(2,0,f,g.trim());}{let f=Y(process.cwd()),g=(e[2]||"").toLowerCase();if(f&&g){let R=h.join(f,".rapidkit","workspace.json"),x=h.join(f,".rapidkit","policies.yml");try{let[T,C]=await Promise.all([b.pathExists(R).then(fe=>fe?_.promises.readFile(R,"utf-8"):"{}"),b.pathExists(x).then(fe=>fe?_.promises.readFile(x,"utf-8"):"")]),E=JSON.parse(T).profile,N=C.match(/^\s*mode:\s*(warn|strict)\s*(?:#.*)?$/m)?.[1]??"warn",$=qt(g)||lt(g)||g.startsWith("go"),Re=["nestjs","react","vue","nextjs","next","vite","angular","svelte","express","koa","fastify"].some(fe=>g.includes(fe)),et=!$&&!Re,we=null;if(E==="python-only"&&!et?we=`Kit "${g}" is not a Python kit, but workspace profile is "python-only".`:E==="node-only"&&!Re?we=`Kit "${g}" is not a Node kit, but workspace profile is "node-only".`:E==="go-only"&&!$&&(we=`Kit "${g}" is not a Go kit, but workspace profile is "go-only".`),we){if(N==="strict")return console.log(l.red(`\u274C Profile violation (strict mode): ${we}`)),console.log(l.gray("\u{1F4A1} Change workspace profile or use --no-workspace to skip enforcement.")),1;console.log(l.yellow(`\u26A0\uFE0F Profile warning: ${we}`)),console.log(l.gray('\u{1F4A1} Consider using a "polyglot" workspace profile for multi-language projects.'));}}catch{}}}if(qt(e[2]||""))return await ur(e);if(lt(e[2]||""))return await gr(e);let r=e.includes("--create-workspace"),i$1=e.includes("--no-workspace"),n=e.includes("--yes")||e.includes("-y"),a=e.includes("--skip-git")||e.includes("--no-git");if(!!!so(process.cwd())){if(r)await i(process.cwd(),{skipGit:a,yes:n,userConfig:await a$2()});else if(!i$1)if(n)await i(process.cwd(),{skipGit:a,yes:true,userConfig:await a$2()});else {let{createWs:f}=await xe.prompt([{type:"confirm",name:"createWs",message:"This project will be created outside a RapidKit workspace. Create and register a workspace here?",default:true}]);f&&await i(process.cwd(),{skipGit:a,yes:false,userConfig:await a$2()});}}let p=[...e.filter(f=>{let g=f.split("=")[0];return !o.has(f)&&!o.has(g)})],d=Y(process.cwd()),w=e.includes("--skip-install")||!!d?{...process.env,RAPIDKIT_SKIP_LOCKS:"1",RAPIDKIT_GENERATE_LOCKS:"0"}:void 0;try{await c$2();let f=await d$1(p,{cwd:process.cwd(),env:w});if(f===0&&d&&!e.includes("--skip-install")&&(console.log(l.gray("\u2139\uFE0F Fast create mode (workspace): dependencies were deferred.")),console.log(l.white(" Next: cd <project-name> && npx rapidkit init"))),f===0){let g=d||Y(process.cwd());if(g){try{let x=e[3];if(x){let T=e.indexOf("--output"),C=T>=0?e[T+1]:".",E=h.resolve(process.cwd(),C,x),G=h.join(g,".python-version"),N=h.join(E,".python-version");if(_.existsSync(G)&&_.existsSync(E)){let $=_.readFileSync(G,"utf-8");_.writeFileSync(N,$.trim()+`
3531
- `),a$1.debug(`Synced Python version ${$.trim()} from workspace to ${x}`);}}}catch(x){a$1.debug("Could not sync Python version from workspace:",x);}let{syncWorkspaceProjects:R}=await import('./workspace-KRZ3DWL4.js');await R(g,true);}}return f}catch(f){let g=oo(f);return g?await io(p,g):(process.stderr.write(`RapidKit (npm) failed to run the Python core engine: ${f?.message??f}
3532
- `),1)}}if(e[0]==="create"&&e[1]!=="project")try{await c$2();let t=await d$1(e,{cwd:process.cwd()});if(t===0){let r=Y(process.cwd());if(r){let{syncWorkspaceProjects:i}=await import('./workspace-KRZ3DWL4.js');await i(r,true);}}return t}catch(t){let r=oo(t);return r?await io(e,r):(process.stderr.write(`RapidKit (npm) failed to run the Python core engine: ${t?.message??t}
3533
- `),1)}return await c$2(),await d$1(e,{cwd:process.cwd()})}catch(t){let r=oo(t);return r?await io(e,r):(process.stderr.write(`RapidKit (npm) failed to run the Python core engine: ${t?.message??t}
3534
- `),1)}}var mr=["init","dev","start","build","test","docs","lint","format","create","help","--help","-h"],ys=["doctor","workspace","bootstrap","setup","cache","mirror","ai","config","shell"],ws=["doctor","workspace","ai","config","shell"],vs=["bootstrap","setup","cache","mirror"],xr=["lint","format","docs"],ks=["init"],Pr=["build","dev","start","test"],fr=[...Pr,...xr];function Ft(e){return !!e&&ys.includes(e)}function hr(e){return !!e&&ws.includes(e)}function bs(e){return !!e&&vs.includes(e)}function Er(e){return _.existsSync(h.join(e,".rapidkit-workspace"))||_.existsSync(h.join(e,".rapidkit","workspace.json"))}function Ir(e){let o=e;for(;;){let t=h.join(o,".rapidkit","context.json");if(_.existsSync(t))return t;let r=h.dirname(o);if(r===o)break;o=r;}return null}function so(e){let o=e;for(;;){let t=h.join(o,".rapidkit-workspace");if(_.existsSync(t))return t;let r=h.dirname(o);if(r===o)break;o=r;}return null}function Y(e){let o=e;for(;;){let t=h.join(o,".rapidkit-workspace");if(_.existsSync(t))return o;let r=h.dirname(o);if(r===o)break;o=r;}return null}async function Rs(e,o=process.cwd(),t=process.platform){if(!a(t))return {detected:false};if(!(e.workspaceFlag||e.scope==="workspace"))return {detected:false};let i=i$1(o,t);for(let n of i){if(!await b.pathExists(n))continue;let a=n.toLowerCase();if(a.endsWith("rapidkit.cmd")||a.endsWith("rapidkit.exe"))return {detected:true,candidatePath:n,reason:"Found workspace-local rapidkit launcher on Windows."}}return {detected:false}}function _s(e){let o=e;for(;;){let t=h.join(o,".rapidkit-workspace"),r=h.join(o,".rapidkit","workspace.json");if(!_.existsSync(t)&&_.existsSync(r))return o;let i=h.dirname(o);if(i===o)break;o=i;}return null}var Tr={enforce_workspace_marker:true,enforce_toolchain_lock:false,disallow_untrusted_tool_sources:false,enforce_compatibility_matrix:false,require_mirror_lock_for_offline:true};function Cs(e){return e&&e.match(/^[\t ]*mode:\s*(warn|strict)\s*(?:#.*)?$/m)?.[1]==="strict"?"strict":"warn"}function ct(e,o){let t=e.match(new RegExp(`^[\\t ]*${o}:\\s*(true|false)\\s*(?:#.*)?$`,"m"));return t?t[1]==="true":Tr[o]}function yr(e){let o=e??"";return {mode:Cs(o),dependency_sharing_mode:Ar(o),rules:{enforce_workspace_marker:ct(o,"enforce_workspace_marker"),enforce_toolchain_lock:ct(o,"enforce_toolchain_lock"),disallow_untrusted_tool_sources:ct(o,"disallow_untrusted_tool_sources"),enforce_compatibility_matrix:ct(o,"enforce_compatibility_matrix"),require_mirror_lock_for_offline:ct(o,"require_mirror_lock_for_offline")}}}function wr(e,o,t){let r=`${o}: ${t}`,i=o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),n=new RegExp(`^[\\t ]*${i}:\\s*.*$`,"m");if(n.test(e))return e.replace(n,r);let a=/^[\t ]*rules:\s*(?:#.*)?$/m;return a.test(e)?e.replace(a,`${r}
3535
- rules:`):`${e.endsWith(`
3536
- `)?e:`${e}
129
+ `));let{kitChoice:p}=await he.prompt([{type:"rawlist",name:"kitChoice",message:"Select a kit to scaffold:",choices:[{name:"fastapi \u2014 FastAPI Standard Kit",value:"fastapi.standard"},{name:"fastapi \u2014 FastAPI DDD Kit",value:"fastapi.ddd"},{name:"nestjs \u2014 NestJS Standard Kit",value:"nestjs.standard"},{name:"go/fiber \u2014 Go Fiber Standard Kit",value:"gofiber.standard"},{name:"go/gin \u2014 Go Gin Standard Kit",value:"gogin.standard"}]}]);if(ht(p)||Ue(p)){let{projectName:A}=await he.prompt([{type:"input",name:"projectName",message:"Project name:",validate:S=>S.trim().length>0||"Project name is required"}]),P=t.slice(2).filter(S=>S.startsWith("-"));return Ue(p)?await Ro(["create","project",p,A.trim(),...P]):await xo(["create","project",p,A.trim(),...P])}let{projectName:g}=await he.prompt([{type:"input",name:"projectName",message:"Project name:",validate:A=>A.trim().length>0||"Project name is required"}]);t.splice(2,0,p,g.trim());}{let p=q(process.cwd()),g=(t[2]||"").toLowerCase();if(p&&g){let A=f.join(p,".rapidkit","workspace.json"),P=f.join(p,".rapidkit","policies.yml");try{let[S,b]=await Promise.all([x__default.pathExists(A).then(ae=>ae?w.promises.readFile(A,"utf-8"):"{}"),x__default.pathExists(P).then(ae=>ae?w.promises.readFile(P,"utf-8"):"")]),_=JSON.parse(S).profile,T=b.match(/^\s*mode:\s*(warn|strict)\s*(?:#.*)?$/m)?.[1]??"warn",I=ht(g)||Ue(g)||g.startsWith("go"),fe=["nestjs","react","vue","nextjs","next","vite","angular","svelte","express","koa","fastify"].some(ae=>g.includes(ae)),Ne=!I&&!fe,le=null;if(_==="python-only"&&!Ne?le=`Kit "${g}" is not a Python kit, but workspace profile is "python-only".`:_==="node-only"&&!fe?le=`Kit "${g}" is not a Node kit, but workspace profile is "node-only".`:_==="go-only"&&!I&&(le=`Kit "${g}" is not a Go kit, but workspace profile is "go-only".`),le){if(T==="strict")return console.log(s.red(`\u274C Profile violation (strict mode): ${le}`)),console.log(s.gray("\u{1F4A1} Change workspace profile or use --no-workspace to skip enforcement.")),1;console.log(s.yellow(`\u26A0\uFE0F Profile warning: ${le}`)),console.log(s.gray('\u{1F4A1} Consider using a "polyglot" workspace profile for multi-language projects.'));}}catch{}}}if(ht(t[2]||""))return await xo(t);if(Ue(t[2]||""))return await Ro(t);let r=t.includes("--create-workspace"),n=t.includes("--no-workspace"),i=t.includes("--yes")||t.includes("-y"),a=t.includes("--skip-git")||t.includes("--no-git");if(!!!$t(process.cwd())){let{registerWorkspaceAtPath:p}=await import('./create-27NVMJAR.js');if(r)await p(process.cwd(),{skipGit:a,yes:i,userConfig:await a$2()});else if(!n)if(i)await p(process.cwd(),{skipGit:a,yes:true,userConfig:await a$2()});else {let{createWs:g}=await he.prompt([{type:"confirm",name:"createWs",message:"This project will be created outside a RapidKit workspace. Create and register a workspace here?",default:true}]);g&&await p(process.cwd(),{skipGit:a,yes:false,userConfig:await a$2()});}}let d$1=[...t.filter(p=>{let g=p.split("=")[0];return !e$1.has(p)&&!e$1.has(g)})],l=q(process.cwd()),v=t.includes("--skip-install")||!!l?{...process.env,RAPIDKIT_SKIP_LOCKS:"1",RAPIDKIT_GENERATE_LOCKS:"0"}:void 0;try{await c$1();let p=await d(d$1,{cwd:process.cwd(),env:v});if(p===0&&l&&!t.includes("--skip-install")&&(console.log(s.gray("\u2139\uFE0F Fast create mode (workspace): dependencies were deferred.")),console.log(s.white(" Next: cd <project-name> && npx rapidkit init"))),p===0){let g=l||q(process.cwd());if(g){try{let P=t[3];if(P){let S=t.indexOf("--output"),b=S>=0?t[S+1]:".",_=f.resolve(process.cwd(),b,P),M=f.join(g,".python-version"),T=f.join(_,".python-version");if(w.existsSync(M)&&w.existsSync(_)){let I=w.readFileSync(M,"utf-8");w.writeFileSync(T,I.trim()+`
130
+ `),a$1.debug(`Synced Python version ${I.trim()} from workspace to ${P}`);}}}catch(P){a$1.debug("Could not sync Python version from workspace:",P);}let{syncWorkspaceProjects:A}=await import('./workspace-VXNLNKCM.js');await A(g,true);}}return p}catch(p){let g=Et(p);return g?await It(d$1,g):(process.stderr.write(`RapidKit (npm) failed to run the Python core engine: ${p?.message??p}
131
+ `),1)}}if(t[0]==="create"&&t[1]!=="project")try{await c$1();let o=await d(t,{cwd:process.cwd()});if(o===0){let r=q(process.cwd());if(r){let{syncWorkspaceProjects:n}=await import('./workspace-VXNLNKCM.js');await n(r,true);}}return o}catch(o){let r=Et(o);return r?await It(t,r):(process.stderr.write(`RapidKit (npm) failed to run the Python core engine: ${o?.message??o}
132
+ `),1)}return await c$1(),await d(t,{cwd:process.cwd()})}catch(o){let r=Et(o);return r?await It(t,r):(process.stderr.write(`RapidKit (npm) failed to run the Python core engine: ${o?.message??o}
133
+ `),1)}}var So=["init","dev","start","build","test","docs","lint","format","create","help","--help","-h"],Ur=["doctor","workspace","bootstrap","setup","cache","mirror","ai","config","shell"],Hr=["doctor","workspace","ai","config","shell"],Jr=["bootstrap","setup","cache","mirror"],Lo=["lint","format","docs"],Vr=["init"],Fo=["build","dev","start","test"],Eo=[...Fo,...Lo];function wt(t){return !!t&&Ur.includes(t)}function Ao(t){return !!t&&Hr.includes(t)}function Br(t){return !!t&&Jr.includes(t)}function Wo(t){return w.existsSync(f.join(t,".rapidkit-workspace"))||w.existsSync(f.join(t,".rapidkit","workspace.json"))}function Go(t){let e=t;for(;;){let o=f.join(e,".rapidkit","context.json");if(w.existsSync(o))return o;let r=f.dirname(e);if(r===e)break;e=r;}return null}function $t(t){let e=t;for(;;){let o=f.join(e,".rapidkit-workspace");if(w.existsSync(o))return o;let r=f.dirname(e);if(r===e)break;e=r;}return null}function q(t){let e=t;for(;;){let o=f.join(e,".rapidkit-workspace");if(w.existsSync(o))return e;let r=f.dirname(e);if(r===e)break;e=r;}return null}async function Yr(t,e=process.cwd(),o=process.platform){if(!(t.workspaceFlag||t.scope==="workspace"))return {detected:false};let n=i(e,o);for(let i of n)if(await x__default.pathExists(i)){if(a(o)){let a=i.toLowerCase();if(a.endsWith("rapidkit.cmd")||a.endsWith("rapidkit.exe"))return {detected:true,candidatePath:i,reason:"Found workspace-local rapidkit launcher on Windows."}}else if(f.basename(i)==="rapidkit")return {detected:true,candidatePath:i,reason:"Found workspace-local rapidkit bash launcher on Linux/macOS."}}return {detected:false}}function zr(t){let e=t;for(;;){let o=f.join(e,".rapidkit-workspace"),r=f.join(e,".rapidkit","workspace.json");if(!w.existsSync(o)&&w.existsSync(r))return e;let n=f.dirname(e);if(n===e)break;e=n;}return null}var qo={enforce_workspace_marker:true,enforce_toolchain_lock:false,disallow_untrusted_tool_sources:false,enforce_compatibility_matrix:false,require_mirror_lock_for_offline:true};function Qr(t){return t&&t.match(/^[\t ]*mode:\s*(warn|strict)\s*(?:#.*)?$/m)?.[1]==="strict"?"strict":"warn"}function qe(t,e){let o=t.match(new RegExp(`^[\\t ]*${e}:\\s*(true|false)\\s*(?:#.*)?$`,"m"));return o?o[1]==="true":qo[e]}function Io(t){let e=t??"";return {mode:Qr(e),dependency_sharing_mode:Uo(e),rules:{enforce_workspace_marker:qe(e,"enforce_workspace_marker"),enforce_toolchain_lock:qe(e,"enforce_toolchain_lock"),disallow_untrusted_tool_sources:qe(e,"disallow_untrusted_tool_sources"),enforce_compatibility_matrix:qe(e,"enforce_compatibility_matrix"),require_mirror_lock_for_offline:qe(e,"require_mirror_lock_for_offline")}}}function jo(t,e,o){let r=`${e}: ${o}`,n=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),i=new RegExp(`^[\\t ]*${n}:\\s*.*$`,"m");if(i.test(t))return t.replace(i,r);let a=/^[\t ]*rules:\s*(?:#.*)?$/m;return a.test(t)?t.replace(a,`${r}
134
+ rules:`):`${t.endsWith(`
135
+ `)?t:`${t}
3537
136
  `}${r}
3538
- `}function Ss(e,o,t){let r=` ${o}: ${t?"true":"false"}`,i=o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),n=new RegExp(`^[\\t ]+${i}:\\s*.*$`,"m");if(n.test(e))return e.replace(n,r);let a=/^[\t ]*rules:\s*(?:#.*)?$/m;return a.test(e)?e.replace(a,`rules:
3539
- ${r}`):`${e.endsWith(`
3540
- `)?e:`${e}
137
+ `}function Zr(t,e,o){let r=` ${e}: ${o?"true":"false"}`,n=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),i=new RegExp(`^[\\t ]+${n}:\\s*.*$`,"m");if(i.test(t))return t.replace(i,r);let a=/^[\t ]*rules:\s*(?:#.*)?$/m;return a.test(t)?t.replace(a,`rules:
138
+ ${r}`):`${t.endsWith(`
139
+ `)?t:`${t}
3541
140
  `}rules:
3542
141
  ${r}
3543
- `}function xs(){return ['version: "1.0"','mode: warn # "warn" or "strict"','dependency_sharing_mode: isolated # "isolated" or "shared-runtime-caches" or "shared-node-deps"',"# change profile (recommended): npx rapidkit bootstrap --profile polyglot","# change mode/dependency manually: edit this file and rerun npx rapidkit init","rules:"," enforce_workspace_marker: true"," enforce_toolchain_lock: false"," disallow_untrusted_tool_sources: false"," enforce_compatibility_matrix: false"," require_mirror_lock_for_offline: true",""].join(`
3544
- `)}async function vr(e){let o=h.join(e,".rapidkit","policies.yml");return await b.pathExists(o)?_.promises.readFile(o,"utf-8"):xs()}async function Ps(e,o){let t=h.join(e,".rapidkit"),r=h.join(t,"policies.yml");await b.ensureDir(t);let i=o.endsWith(`
3545
- `)?o:`${o}
3546
- `;await _.promises.writeFile(r,i,"utf-8");}function Es(e){let o=e.trim().toLowerCase();return o==="true"||o==="1"||o==="on"?true:o==="false"||o==="0"||o==="off"?false:null}function Ar(e){if(!e)return "isolated";let t=e.match(/^\s*dependency_sharing_mode:\s*([a-zA-Z\-]+)\s*(?:#.*)?$/m)?.[1]?.toLowerCase();return t==="shared-runtime-caches"||t==="shared-node-deps"||t==="isolated"?t:"isolated"}function Or(e){if(!e)return {mode:"isolated",status:"skipped",message:"No policies.yml found; dependency_sharing_mode defaults to isolated."};let o=e.match(/^\s*dependency_sharing_mode:\s*([a-zA-Z\-]+)\s*(?:#.*)?$/m);if(!o)return {mode:"isolated",status:"skipped",message:"dependency_sharing_mode is not set; defaulting to isolated."};let t=o[1].toLowerCase();return t==="isolated"||t==="shared-runtime-caches"||t==="shared-node-deps"?{mode:t,status:"passed",message:`dependency_sharing_mode is valid: ${t}.`}:{mode:"isolated",status:"failed",message:`Invalid dependency_sharing_mode: ${t}. Use one of: isolated, shared-runtime-caches, shared-node-deps.`}}async function Nr(e,o){let t=Y(e),r=t?h.join(t,".rapidkit","policies.yml"):null,i="isolated";if(r&&await b.pathExists(r))try{let s=await _.promises.readFile(r,"utf-8"),c=Or(s);if(c.status==="failed")return console.log(l.red(`\u274C ${c.message}`)),{ok:false,code:1};i=c.mode;}catch{return console.log(l.red("\u274C Failed to read workspace policy file (.rapidkit/policies.yml).")),{ok:false,code:1}}let n=process.env.RAPIDKIT_DEP_SHARING_MODE,a=process.env.RAPIDKIT_WORKSPACE_PATH;process.env.RAPIDKIT_DEP_SHARING_MODE=i,t&&(process.env.RAPIDKIT_WORKSPACE_PATH=t);try{return {ok:true,value:await o()}}finally{typeof n>"u"?delete process.env.RAPIDKIT_DEP_SHARING_MODE:process.env.RAPIDKIT_DEP_SHARING_MODE=n,typeof a>"u"?delete process.env.RAPIDKIT_WORKSPACE_PATH:process.env.RAPIDKIT_WORKSPACE_PATH=a;}}async function W(e,o,t){return await new Promise(r=>{let i=spawn(e,o,{stdio:"inherit",cwd:t,shell:b$3()});i.on("close",n=>r(n??1)),i.on("error",()=>r(1));})}async function Ze(e,o){await b.outputFile(e,`${JSON.stringify(o,null,2)}
3547
- `,"utf-8");}async function Is(e){let o=new Set(["go-only"]);try{let r=h.join(e,".rapidkit","workspace.json"),i=JSON.parse(await _.promises.readFile(r,"utf-8"));if(o.has(i.profile??""))return 0}catch{}let t="poetry";try{let{readWorkspaceMarker:r}=await import('./workspace-marker-IOPQ42A7.js'),n=(await r(e))?.metadata?.npm?.installMethod;(n==="poetry"||n==="venv"||n==="pipx"||n==="pip")&&(t=n);}catch{}if(t==="poetry"||t==="venv"){let r=h.join(e,"pyproject.toml"),i=false;try{i=(await _.promises.readFile(r,"utf-8")).includes("rapidkit-core");}catch{i=false;}let n=process.env.RAPIDKIT_DEV_PATH,a=n?await b.pathExists(n):false;if(i){let s=gs(e);if(!await b.pathExists(s)){let d=await ms(e);if(d!==0)return d}let p=await W(s,a&&n?["-m","pip","install",n,"--quiet","--disable-pip-version-check"]:["-m","pip","install","rapidkit-core","--quiet","--disable-pip-version-check"],e);if(p!==0)return p}else {let s=await W("poetry",["install","--no-root"],e);if(s!==0)return s;let c=await W("poetry",["add","rapidkit-core"],e);if(c!==0)return c}try{let{writeWorkspaceLauncher:s}=await import('./create-PIVSRLDS.js');await s(e,"poetry");}catch{}return 0}return 0}async function jr(e){let o=await _.promises.readdir(e,{withFileTypes:true}),t=[];for(let r of o){if(!r.isDirectory()||r.name.startsWith("."))continue;let i=h.join(e,r.name),n=h.join(i,".rapidkit","context.json"),a=h.join(i,".rapidkit","project.json");(await b.pathExists(n)||await b.pathExists(a))&&t.push(i);}return t}function Ts(e){let o="my-workspace",t=1;for(;;){let r=t===1?o:`${o}-${t}`,i=h.join(e,r);if(!_.existsSync(i))return {name:r,targetPath:i};t+=1;}}async function Mt(e){let t=await He("go",{runCommandInCwd:W,runCoreRapidkit:d$1}).initProject(e);return t.message&&console.log(l.red(`\u274C ${t.message}`)),t.exitCode}async function dt(e,o){let t=He("node",{runCommandInCwd:W,runCoreRapidkit:d$1});return e==="init"?(await t.initProject(o)).exitCode:e==="dev"?(await t.runDev(o)).exitCode:e==="test"?(await t.runTest(o)).exitCode:e==="build"?(await t.runBuild(o)).exitCode:(await t.runStart(o)).exitCode}async function As(e,o=ao){let t=process.env.RAPIDKIT_SKIP_LOCK_SYNC;typeof t>"u"&&(process.env.RAPIDKIT_SKIP_LOCK_SYNC="1");try{let n=function(I){if(!I)return null;let O=I.trim().toLowerCase();return O==="minimal"||O==="go-only"||O==="python-only"||O==="node-only"||O==="polyglot"||O==="enterprise"?O:null},a=function(I){let j=I.match(/^\s*mode:\s*([a-zA-Z]+)\s*(?:#.*)?$/m)?.[1]?.toLowerCase()==="strict"?"strict":"warn",ge=(U,Te)=>{let Pe=I.match(new RegExp(`^\\s*${U}:\\s*(true|false)\\s*(?:#.*)?$`,"m"));return Pe?Pe[1].toLowerCase()==="true":Te};return {mode:j,dependency_sharing_mode:Ar(I),rules:{enforce_workspace_marker:ge("enforce_workspace_marker",true),enforce_toolchain_lock:ge("enforce_toolchain_lock",false),disallow_untrusted_tool_sources:ge("disallow_untrusted_tool_sources",false),enforce_compatibility_matrix:ge("enforce_compatibility_matrix",false),require_mirror_lock_for_offline:ge("require_mirror_lock_for_offline",true)}}};let s=["init"],c,p=false,d=false,u=false;for(let I=1;I<e.length;I+=1){let O=e[I];if(O==="--ci"){p=true;continue}if(O==="--offline"){d=true;continue}if(O==="--json"){u=true;continue}if(O==="--profile"){let M=e[I+1];if(!M||M.startsWith("-"))return console.log(l.yellow("Usage: rapidkit bootstrap [path] [--profile <minimal|go-only|python-only|node-only|polyglot|enterprise>] [--ci] [--offline] [--json]")),1;c=M,I+=1;continue}if(O.startsWith("--profile=")){c=O.slice(10);continue}s.push(O);}let m=n(c);if(c&&!m)return console.log(l.red(`Invalid profile: ${c}. Use one of: minimal, go-only, python-only, node-only, polyglot, enterprise.`)),1;let w=process.cwd(),f$1=Y(w);f$1||(f$1=_s(w));let g=[],R=null,x=null;if(f$1)try{let I=h.join(f$1,".rapidkit","workspace.json"),O=await _.promises.readFile(I,"utf-8"),M=JSON.parse(O);x=n(M.profile);}catch{x=null;}let T=["minimal","python-only","node-only","go-only","polyglot","enterprise"],C={minimal:"minimal \u2014 Foundation files only (fastest bootstrap, mixed projects)","python-only":"python-only \u2014 Python + Poetry (FastAPI, Django, ML pipelines)","node-only":"node-only \u2014 Node.js runtime (NestJS, Express, Next.js)","go-only":"go-only \u2014 Go runtime (Fiber, Gin, gRPC, microservices)",polyglot:"polyglot \u2014 Python + Node.js + Go multi-runtime workspace",enterprise:"enterprise \u2014 Polyglot + governance + Sigstore verification"},E=m;if(!!f$1&&!m&&!p&&!u&&!!process.stdin.isTTY&&!!process.stdout.isTTY){let I=x||"minimal",{chosenProfile:O}=await xe.prompt([{type:"rawlist",name:"chosenProfile",message:`Select workspace profile for bootstrap (current: ${I})`,choices:T.map(M=>({name:M===I?`${C[M]} \u2190 current`:C[M],value:M})),default:T.indexOf(I)}]);E=O;}let N=E||x||"minimal";if(f$1)try{let O=N==="python-only"||N==="polyglot"||N==="enterprise"?"poetry":"venv",M;try{let U=(await _.promises.readFile(h.join(f$1,".python-version"),"utf-8")).trim();U&&(M=U);}catch{}let j=await f(f$1,{workspaceName:h.basename(f$1),installMethod:O,pythonVersion:M,profile:N,writeMarker:true,writeGitignore:true,onlyIfMissing:true});g.push({id:"workspace.legacy.sync",status:j.length>0?"passed":"skipped",message:j.length>0?`Legacy workspace foundation synchronized: ${j.join(", ")}`:"Workspace foundation files are already up to date."});}catch(I){g.push({id:"workspace.legacy.sync",status:"failed",message:`Failed to synchronize legacy workspace foundation files: ${I.message}`});}if(f$1&&E&&E!==x)try{let I=h.join(f$1,".rapidkit","workspace.json"),O=await _.promises.readFile(I,"utf-8"),M=JSON.parse(O);M.profile=E,await _.promises.writeFile(I,JSON.stringify(M,null,2)+`
3548
- `,"utf-8");}catch{}let $={mode:"warn",dependency_sharing_mode:"isolated",rules:{enforce_workspace_marker:true,enforce_toolchain_lock:false,disallow_untrusted_tool_sources:false,enforce_compatibility_matrix:false,require_mirror_lock_for_offline:true}},Re=null;if(f$1)try{let I=await _.promises.readFile(h.join(f$1,".rapidkit","policies.yml"),"utf-8");Re=I,$=a(I);}catch{g.push({id:"policy.file",status:"skipped",message:"No workspace policy file found; using default bootstrap policy."});}else g.push({id:"workspace.detect",status:"skipped",message:"No workspace marker found; bootstrap runs in project/single-path mode."});if(f$1){let I=Or(Re);$.dependency_sharing_mode=I.mode,g.push({id:"policy.schema.dependency_sharing_mode",status:I.status,message:I.message}),g.push({id:"policy.dependency_sharing_mode.effective",status:"passed",message:$.dependency_sharing_mode==="isolated"?"Effective dependency mode: isolated (default secure mode).":$.dependency_sharing_mode==="shared-node-deps"?"Effective dependency mode: shared-node-deps (Node projects share workspace-level caches).":"Effective dependency mode: shared-runtime-caches (Node/Python/Go share workspace-level caches)."});let O=_.existsSync(h.join(f$1,".rapidkit-workspace"));g.push({id:"policy.enforce_workspace_marker",status:!$.rules.enforce_workspace_marker||O?"passed":"failed",message:!$.rules.enforce_workspace_marker||O?"Workspace marker policy satisfied.":"Workspace marker policy failed: .rapidkit-workspace is missing."});let M=_.existsSync(h.join(f$1,".rapidkit","toolchain.lock"));g.push({id:"policy.enforce_toolchain_lock",status:!$.rules.enforce_toolchain_lock||M?"passed":"failed",message:!$.rules.enforce_toolchain_lock||M?"Toolchain lock policy satisfied.":"Toolchain lock policy failed: .rapidkit/toolchain.lock is missing."});let j=process.env.RAPIDKIT_TRUSTED_SOURCES==="1"||_.existsSync(h.join(f$1,".rapidkit","trusted-sources.lock"));g.push({id:"policy.disallow_untrusted_tool_sources",status:!$.rules.disallow_untrusted_tool_sources||j?"passed":"failed",message:!$.rules.disallow_untrusted_tool_sources||j?"Trusted tool sources policy satisfied.":"Trusted tool sources policy failed: set RAPIDKIT_TRUSTED_SOURCES=1 or provide .rapidkit/trusted-sources.lock."});let ge=h.join(f$1,".rapidkit","compatibility-matrix.json"),U=_.existsSync(ge),Te=$.rules.enforce_compatibility_matrix;if(g.push({id:"policy.enforce_compatibility_matrix",status:!Te||U?"passed":"failed",message:!Te||U?"Compatibility matrix policy satisfied.":"Compatibility matrix policy failed: .rapidkit/compatibility-matrix.json is missing."}),U)try{let F=await _.promises.readFile(ge,"utf-8"),X=JSON.parse(F),gt=!!X&&typeof X=="object";g.push({id:"compatibility.matrix.parse",status:gt?"passed":"failed",message:gt?"Compatibility matrix parsed successfully.":"Compatibility matrix parse failed: invalid JSON object."});}catch{g.push({id:"compatibility.matrix.parse",status:"failed",message:"Compatibility matrix parse failed: invalid JSON."});}let Pe=h.join(f$1,".rapidkit","mirror-config.json"),ne=h.join(f$1,".rapidkit","mirror.lock"),Ae=_.existsSync(Pe),Ee=_.existsSync(ne),ut={};if(Ae)try{ut=JSON.parse(await _.promises.readFile(Pe,"utf-8")),g.push({id:"mirror.config.parse",status:"passed",message:"Mirror configuration parsed successfully."});}catch{g.push({id:"mirror.config.parse",status:"failed",message:"Mirror configuration parse failed: invalid JSON in .rapidkit/mirror-config.json."});}let Ht=await to(f$1,{ciMode:p,offlineMode:d});if(g.push(...Ht.checks.map(F=>({id:F.id,status:F.status,message:F.message}))),R=Ht.details,Ht.details.lockWritten&&(Ee=true),d){let F=process.env.RAPIDKIT_MIRROR_ENABLED==="1"||ut.enabled===true;g.push({id:"offline.mirror.enabled",status:F?"passed":"failed",message:F?"Offline mode mirror is enabled.":'Offline mode requires mirror enablement (set RAPIDKIT_MIRROR_ENABLED=1 or .rapidkit/mirror-config.json {"enabled": true}).'});let X=$.rules.require_mirror_lock_for_offline;g.push({id:"offline.mirror.lock",status:!X||Ee?"passed":"failed",message:!X||Ee?"Offline mode mirror lock policy satisfied.":"Offline mode mirror lock policy failed: .rapidkit/mirror.lock is missing."});}else g.push({id:"offline.mirror.enabled",status:"skipped",message:"Offline mirror checks skipped (offline mode is disabled)."});let Dr=await jr(f$1),de=new Set;for(let F of Dr){let X=Se(F);if(pe(X,F)){de.add("go");continue}if(ue(X,F)){de.add("node");continue}if(me(X,F)){de.add("python");continue}de.add("unknown");}if(N==="go-only"){let F=de.size===0||[...de].every(X=>X==="go");g.push({id:"profile.go-only",status:F?"passed":"failed",message:F?"go-only profile validated for discovered projects.":`go-only profile mismatch: detected runtimes [${[...de].join(", ")}].`});}else if(N==="python-only"){let F=de.size===0||[...de].every(X=>X==="python");g.push({id:"profile.python-only",status:F?"passed":"failed",message:F?"python-only profile validated for discovered projects.":`python-only profile mismatch: detected runtimes [${[...de].join(", ")}].`});}else if(N==="node-only"){let F=de.size===0||[...de].every(X=>X==="node");g.push({id:"profile.node-only",status:F?"passed":"failed",message:F?"node-only profile validated for discovered projects.":`node-only profile mismatch: detected runtimes [${[...de].join(", ")}].`});}else if(N==="minimal"){let F=[...de].filter(gt=>gt!=="unknown"),X=F.length<=1;g.push({id:"profile.minimal",status:X?"passed":"failed",message:X?"minimal profile is compatible with detected runtime mix.":`minimal profile mismatch: multiple runtimes detected [${F.join(", ")}].`});}else N==="enterprise"&&(g.push({id:"profile.enterprise.ci",status:p?"passed":"failed",message:p?"enterprise profile running with --ci.":"enterprise profile expects --ci for deterministic non-interactive mode."}),g.push({id:"profile.enterprise.compatibility-matrix",status:U?"passed":"failed",message:U?"enterprise profile has compatibility matrix.":"enterprise profile requires .rapidkit/compatibility-matrix.json."}),g.push({id:"profile.enterprise.mirror-config",status:Ae?"passed":"failed",message:Ae?"enterprise profile has mirror configuration.":"enterprise profile requires .rapidkit/mirror-config.json."}));}p&&(process.env.RAPIDKIT_BOOTSTRAP_CI="1"),d&&(process.env.RAPIDKIT_OFFLINE_MODE="1");let we=g.some(I=>I.id.startsWith("policy.schema.")&&I.status==="failed")||$.mode==="strict"&&g.some(I=>I.status==="failed"),fe=f$1||w,Ve=h.join(fe,".rapidkit","reports"),z=new Date().toISOString().replace(/[:.]/g,"-"),L=h.join(Ve,`bootstrap-compliance-${z}.json`),S=h.join(Ve,"bootstrap-compliance.latest.json"),se={command:"bootstrap",timestamp:new Date().toISOString(),workspacePath:f$1,profile:N,options:{ci:p,offline:d,strict:$.mode==="strict"},policyMode:$.mode,policyRules:$.rules,mirrorLifecycle:R,checks:g};if(we){let I={...se,result:"blocked",initExitCode:null};return await b.ensureDir(Ve),await Ze(L,I),await Ze(S,I),u?process.stdout.write(`${JSON.stringify(I,null,2)}
3549
- `):(console.log(l.red("\u274C Bootstrap blocked by strict policy checks.")),console.log(l.gray(`Compliance report: ${L}`))),1}let ae=0;u||(ae=await o(s));let H=g.filter(I=>I.status==="failed").length,ie=ae!==0?"failed":H>0?"ok_with_warnings":"ok",le={...se,result:ie,initExitCode:ae};if(await b.ensureDir(Ve),await Ze(L,le),await Ze(S,le),u)process.stdout.write(`${JSON.stringify(le,null,2)}
3550
- `);else {let I=g.filter(O=>O.status==="failed").length;I>0&&console.log(l.yellow(`\u26A0\uFE0F Bootstrap completed with ${I} policy/profile warnings.`)),console.log(l.gray(`Compliance report: ${L}`));}return ae}finally{typeof t>"u"?delete process.env.RAPIDKIT_SKIP_LOCK_SYNC:process.env.RAPIDKIT_SKIP_LOCK_SYNC=t;}}async function Os(e){let o=(e[1]||"").toLowerCase(),t=e.includes("--warm-deps")||e.includes("--warm-dependencies");if(!o||!["python","node","go"].includes(o))return console.log(l.yellow("Usage: rapidkit setup <python|node|go> [--warm-deps]")),1;let r=async(p,d)=>{if(p==="node"){if(!_.existsSync(h.join(d,"package.json")))return {exitCode:0,message:"Node warm-up skipped: package.json not found in current directory."};let m=_.existsSync(h.join(d,"pnpm-lock.yaml")),w=_.existsSync(h.join(d,"yarn.lock"));return m?{exitCode:await W("pnpm",["install","--lockfile-only","--ignore-scripts"],d)}:w?{exitCode:await W("yarn",["install","--ignore-scripts"],d)}:{exitCode:await W("npm",["install","--package-lock-only","--ignore-scripts"],d)}}return p==="go"?_.existsSync(h.join(d,"go.mod"))?{exitCode:await W("go",["mod","download"],d)}:{exitCode:0,message:"Go warm-up skipped: go.mod not found in current directory."}:{exitCode:0,message:"Dependency warm-up currently applies to node/go runtimes."}},i=He(o,{runCommandInCwd:W,runCoreRapidkit:(p,d)=>d$1(p,{...d,cwd:void 0})}),n=await i.checkPrereqs(),a=await i.doctorHints(process.cwd()),s=Y(process.cwd()),c=s||process.cwd();if(n.exitCode===0){console.log(l.green(`\u2705 ${o} prerequisites look good.`));let p=["python","node","go"].filter(d=>d!==o).join("/");if(console.log(l.gray(` Scope: validated ${o} runtime only. ${p} checks are optional unless your workspace profile uses them.`)),o==="python"&&console.log(l.gray(" Note: Poetry is recommended, but venv/pipx-based flows are supported in workspace creation.")),i.warmSetupCache&&((await i.warmSetupCache(c)).exitCode===0?console.log(l.gray(` ${o} cache warm-up completed.`)):console.log(l.yellow(` ${o} cache warm-up skipped (non-fatal).`))),t){let d=await r(o,c),u=/skipped/i.test(d.message||"");d.message&&console.log(l.gray(` ${d.message}`)),d.exitCode===0&&!u?console.log(l.gray(` ${o} dependency warm-up completed (--warm-deps).`)):d.exitCode!==0&&console.log(l.yellow(` ${o} dependency warm-up failed (non-fatal).`));}if(s)try{let d=h.join(s,".rapidkit","toolchain.lock"),u={};try{u=JSON.parse(await _.promises.readFile(d,"utf-8"));}catch{}(!u.runtime||typeof u.runtime!="object")&&(u.runtime={});let m=u.runtime;if(o==="python"){let w=null;try{let{execa:f}=await import('execa');for(let g of co()){let x=await f(g,g==="py"?["-3","--version"]:["--version"],{cwd:s,stdio:"pipe",reject:false,timeout:3e3});if(x.exitCode===0){let C=(x.stdout||x.stderr||"").match(/Python\s+(\S+)/);if(w=C?C[1]:null,w)break}}}catch{}m.python={...m.python||{},version:w,last_setup:new Date().toISOString()};}else if(o==="node")m.node={...m.node||{},version:process.version,last_setup:new Date().toISOString()};else if(o==="go"){let w=null;try{let{execa:f}=await import('execa'),R=((await f("go",["version"],{cwd:s,stdio:"pipe"})).stdout||"").match(/go(\d+\.\d+(?:\.\d+)?)/i);w=R?R[1]:null;}catch{}m.go={...m.go||{},version:w,last_setup:new Date().toISOString()};}u.updated_at=new Date().toISOString(),await _.promises.writeFile(d,JSON.stringify(u,null,2)+`
3551
- `,"utf-8"),console.log(l.gray(" toolchain.lock updated (.rapidkit/toolchain.lock)"));}catch{}}else console.log(l.red(`\u274C ${o} prerequisites check failed.`));if(a.length>0){console.log(l.gray(`
3552
- Hints:`));for(let p of a)console.log(l.gray(`- ${p}`));}return n.exitCode}function Ns(e){let o={strategy:"shared",prune_on_bootstrap:false,self_heal:true,verify_integrity:false};for(let t of e.split(`
3553
- `)){let r=t.trim(),i=r.match(/^strategy:\s*(\S+)/);i&&(o.strategy=i[1].replace(/['"]]/g,""));let n=r.match(/^prune_on_bootstrap:\s*(true|false)/);n&&(o.prune_on_bootstrap=n[1]==="true");let a=r.match(/^self_heal:\s*(true|false)/);a&&(o.self_heal=a[1]==="true");let s=r.match(/^verify_integrity:\s*(true|false)/);s&&(o.verify_integrity=s[1]==="true");}return o}async function js(e){let o=(e[1]||"status").toLowerCase(),t=$t.getInstance(),r=Y(process.cwd()),i={strategy:"shared",prune_on_bootstrap:false,self_heal:true,verify_integrity:false};if(r)try{let n=await _.promises.readFile(h.join(r,".rapidkit","cache-config.yml"),"utf-8");i=Ns(n);}catch{}return o==="status"?(console.log(l.cyan("RapidKit cache is enabled")),console.log(l.cyan("RapidKit cache status")),r?(console.log(l.gray(` Workspace: ${r}`)),console.log(l.gray(` Strategy: ${i.strategy}`)),console.log(l.gray(` Self-heal: ${i.self_heal}`)),console.log(l.gray(` Prune on bootstrap:${i.prune_on_bootstrap}`)),console.log(l.gray(` Verify integrity: ${i.verify_integrity}`))):console.log(l.gray(" (not inside a workspace \u2014 showing in-memory cache only)")),console.log(l.gray(" In-memory cache: enabled")),console.log(l.gray(" Use: rapidkit cache clear|prune|repair")),0):o==="clear"?(await t.clear(),console.log(l.green("Cache clear completed")),console.log(l.green("\u2705 Cache cleared (all entries removed).")),0):o==="prune"?(await t.clear(),console.log(l.green("\u2705 Cache pruned (stale entries removed).")),i.prune_on_bootstrap||console.log(l.gray(" Tip: set prune_on_bootstrap: true in .rapidkit/cache-config.yml to auto-prune on every bootstrap.")),0):o==="repair"?i.self_heal?(await t.clear(),console.log(l.green("\u2705 Cache repaired (self-heal applied, stale entries evicted).")),i.verify_integrity&&console.log(l.gray(" Integrity verification is enabled in cache-config.yml.")),0):(console.log(l.yellow("\u26A0\uFE0F self_heal is disabled in .rapidkit/cache-config.yml \u2014 skipping repair.")),0):(console.log(l.yellow("Usage: rapidkit cache <status|clear|prune|repair>")),1)}async function $s(e,o,t,r){let i=(o||"show").toLowerCase(),n=h.join(e,".rapidkit","policies.yml");if(i==="show"||i==="status"||i==="get"){let d=await vr(e),u=yr(d);return console.log(l.cyan(`Policy file: ${n}`)),console.log(l.gray(` mode: ${u.mode}`)),console.log(l.gray(` dependency_sharing_mode: ${u.dependency_sharing_mode}`)),console.log(l.gray(" rules:")),console.log(l.gray(` enforce_workspace_marker: ${u.rules.enforce_workspace_marker}`)),console.log(l.gray(` enforce_toolchain_lock: ${u.rules.enforce_toolchain_lock}`)),console.log(l.gray(` disallow_untrusted_tool_sources: ${u.rules.disallow_untrusted_tool_sources}`)),console.log(l.gray(` enforce_compatibility_matrix: ${u.rules.enforce_compatibility_matrix}`)),console.log(l.gray(` require_mirror_lock_for_offline: ${u.rules.require_mirror_lock_for_offline}`)),console.log(l.gray("Examples:")),console.log(l.gray(" npx rapidkit workspace policy set mode strict")),console.log(l.gray(" npx rapidkit workspace policy set dependency_sharing_mode shared-runtime-caches")),console.log(l.gray(" npx rapidkit workspace policy set rules.enforce_toolchain_lock true")),0}if(i!=="set")return console.log(l.red(`Unknown workspace policy action: ${o||""}`)),console.log(l.gray("Available: show, set")),1;if(!t||typeof r>"u")return console.log(l.yellow("Usage: rapidkit workspace policy set <key> <value>")),console.log(l.gray("Allowed keys:")),console.log(l.gray(" mode (warn|strict)")),console.log(l.gray(" dependency_sharing_mode (isolated|shared-runtime-caches|shared-node-deps)")),console.log(l.gray(" rules.enforce_workspace_marker (true|false)")),console.log(l.gray(" rules.enforce_toolchain_lock (true|false)")),console.log(l.gray(" rules.disallow_untrusted_tool_sources (true|false)")),console.log(l.gray(" rules.enforce_compatibility_matrix (true|false)")),console.log(l.gray(" rules.require_mirror_lock_for_offline (true|false)")),1;let a=t.trim(),c=await vr(e);if(a==="mode"){let d=r.trim().toLowerCase();if(d!=="warn"&&d!=="strict")return console.log(l.red("\u274C Invalid mode. Use: warn | strict")),1;c=wr(c,"mode",`${d} # "warn" or "strict"`);}else if(a==="dependency_sharing_mode"){let d=r.trim().toLowerCase();if(d!=="isolated"&&d!=="shared-runtime-caches"&&d!=="shared-node-deps")return console.log(l.red("\u274C Invalid dependency_sharing_mode. Use: isolated | shared-runtime-caches | shared-node-deps")),1;c=wr(c,"dependency_sharing_mode",`${d} # "isolated" or "shared-runtime-caches" or "shared-node-deps"`);}else if(a.startsWith("rules.")){let d=a.slice(6);if(!(d in Tr))return console.log(l.red(`\u274C Unknown policy rule: ${d}`)),1;let u=Es(r);if(u===null)return console.log(l.red("\u274C Rule values must be boolean: true | false")),1;c=Ss(c,d,u);}else return console.log(l.red(`\u274C Unknown policy key: ${a}`)),1;await Ps(e,c);let p=yr(c);return console.log(l.green(`\u2705 Updated ${a} in .rapidkit/policies.yml`)),console.log(l.gray(` mode: ${p.mode}`)),console.log(l.gray(` dependency_sharing_mode: ${p.dependency_sharing_mode}`)),console.log(l.gray(" Tip: run `npx rapidkit workspace policy show` to inspect all values.")),0}async function Ds(e){let o=(e[1]||"status").toLowerCase(),t=e.includes("--json"),r=Y(process.cwd());if(!r)return console.log(l.red("\u274C Not inside a RapidKit workspace")),console.log(l.gray("\u{1F4A1} Run this command from within a workspace directory")),1;let i=h.join(r,".rapidkit"),n=h.join(i,"mirror-config.json"),a=h.join(i,"mirror.lock"),s=h.join(i,"mirror","artifacts"),c=h.join(i,"reports");async function p(d){let u=new Date().toISOString().replace(/[:.]/g,"-"),m=h.join(c,`mirror-ops-${u}.json`),w=h.join(c,"mirror-ops.latest.json");await b.ensureDir(c),await Ze(m,d),await Ze(w,d);}if(o==="status"){if(!await b.pathExists(n))try{let R={schema_version:"1.0",enabled:false,strategy:"on-demand",artifacts:[],created_at:new Date().toISOString(),note:"Auto-generated by rapidkit mirror status. Set enabled: true and add artifact entries to activate mirroring."};await b.ensureDir(i),await _.promises.writeFile(n,JSON.stringify(R,null,2)+`
3554
- `,"utf-8"),console.log(l.gray(" mirror-config.json created with defaults (.rapidkit/mirror-config.json)"));}catch{}let u=await b.pathExists(n),m=await b.pathExists(s),w=await b.pathExists(a),f=m?(await _.promises.readdir(s,{withFileTypes:true})).filter(R=>R.isFile()).length:0,g={command:"mirror",action:o,result:"ok",timestamp:new Date().toISOString(),workspacePath:r,mirror:{configExists:u,lockExists:w,artifactsCount:f}};return await p(g),t?(process.stdout.write(`${JSON.stringify(g,null,2)}
3555
- `),0):(console.log(l.cyan("RapidKit mirror status")),console.log(l.gray(`Workspace: ${r}`)),console.log(l.gray(`Config: ${u?"present":"missing"} (${n})`)),console.log(l.gray(`Lock: ${w?"present":"missing"} (${a})`)),console.log(l.gray(`Artifacts: ${f}`)),0)}if(o==="sync"||o==="verify"||o==="rotate"){let d=await to(r,{ciMode:true,offlineMode:o==="verify",forceRun:true}),u=d.checks.filter(f=>f.status==="failed"),m=d.checks.some(f=>f.id.startsWith("mirror.verify.")&&f.status==="failed");if(o==="verify"&&m){let f={command:"mirror",action:o,result:"failed",timestamp:new Date().toISOString(),workspacePath:r,details:d.details,checks:d.checks};if(await p(f),t)return process.stdout.write(`${JSON.stringify(f,null,2)}
3556
- `),1;console.log(l.red("\u274C Mirror verify failed."));for(let g of d.checks.filter(R=>R.id.startsWith("mirror.verify.")))console.log(l.gray(`- ${g.id}: ${g.message}`));return 1}if(u.length>0){let f={command:"mirror",action:o,result:"failed",timestamp:new Date().toISOString(),workspacePath:r,details:d.details,checks:d.checks};if(await p(f),t)return process.stdout.write(`${JSON.stringify(f,null,2)}
3557
- `),1;console.log(l.yellow(`\u26A0\uFE0F Mirror ${o} completed with ${u.length} issue(s).`));for(let g of u)console.log(l.gray(`- ${g.id}: ${g.message}`));return 1}let w={command:"mirror",action:o,result:"ok",timestamp:new Date().toISOString(),workspacePath:r,details:d.details,checks:d.checks};return await p(w),t?(process.stdout.write(`${JSON.stringify(w,null,2)}
3558
- `),0):o==="rotate"?(console.log(l.green(`\u2705 Mirror rotate completed. Rotated files: ${d.details.rotatedFiles}.`)),0):o==="verify"?(console.log(l.green(`\u2705 Mirror verify completed. Verified artifacts: ${d.details.verifiedArtifacts}.`)),0):(console.log(l.green(`\u2705 Mirror sync completed. Synced artifacts: ${d.details.syncedArtifacts}.`)),0)}return console.log(l.yellow("Usage: rapidkit mirror <status|sync|verify|rotate> [--json]")),1}async function ao(e){let o=process.env.RAPIDKIT_SKIP_LOCK_SYNC;typeof o>"u"&&(process.env.RAPIDKIT_SKIP_LOCK_SYNC="1");try{let t=process.cwd(),r=await Nr(t,async()=>{let i=Y(t),n=He("python",{runCommandInCwd:W,runCoreRapidkit:d$1});if(e.length>1){let m=h.resolve(t,e[1]),w=Se(m),f=await ro(m);return pe(w,m)||f==="go"?await Mt(m):ue(w,m)||f==="node"?await Gt(m):me(w,m)||f==="python"?await Dt(m,n):await d$1(e,{cwd:t})}let a=Se(t),s=!!Y(t)&&t===Y(t);if(!s&&pe(a,t))return await Mt(t);let c=await ro(t);if(!s&&(ue(a,t)||c==="node"))return await Gt(t);if(!s&&(me(a,t)||c==="python"))return await Dt(t,n);let p=i||Y(t),d=Ir(t),u=d?h.dirname(h.dirname(d)):null;if(u&&u!==p){let m=Se(u),w=await ro(u);return pe(m,u)||w==="go"?await Mt(u):ue(m,u)||w==="node"?await Gt(u):me(m,u)||w==="python"?await Dt(u,n):await d$1(["init"],{cwd:u})}if(p&&t===p){let m=await Is(p);if(m!==0)return m;let w=await jr(p);if(w.length===0){let f="minimal";try{f=JSON.parse(await _.promises.readFile(h.join(p,".rapidkit","workspace.json"),"utf-8")).profile??"minimal";}catch{}return f==="go-only"?(console.log(l.green("\u2714 Go workspace ready")),console.log(l.gray(`
3559
- No projects yet \u2014 create one and then run init inside it:`)),console.log(l.white(" npx rapidkit create project gofiber.standard my-api")),console.log(l.white(" cd my-api && npx rapidkit init")),console.log(l.gray(`
3560
- \u{1F4A1} Go dependencies are managed per-project (go.mod / go mod tidy).`))):(console.log(l.green("\u2714 Workspace ready")),console.log(l.gray(`
3561
- No projects yet \u2014 create one to get started:`)),console.log(l.white(" npx rapidkit create project"))),0}for(let f of w){let g=Se(f);if(pe(g,f)){let R=await Mt(f);if(R!==0)return R}else {if(ue(g,f)){let x=await Gt(f);if(x!==0)return x;continue}if(me(g,f)){let x=await Dt(f,n);if(x!==0)return x;continue}let R=await d$1(["init"],{cwd:f});if(R!==0)return R}}return 0}if(!p){let m=await a$2(),{name:w}=Ts(t);return await h$1(w,{yes:true,userConfig:m}),0}return await d$1(e,{cwd:t})});return r.ok?r.value:r.code}finally{typeof o>"u"?delete process.env.RAPIDKIT_SKIP_LOCK_SYNC:process.env.RAPIDKIT_SKIP_LOCK_SYNC=o;}}async function kr(e){let o=Y(e);if(!o)return [];let t="warn";try{(await _.promises.readFile(h.join(o,".rapidkit","policies.yml"),"utf-8")).match(/^\s*mode:\s*(warn|strict)\s*(?:#.*)?$/m)?.[1]==="strict"&&(t="strict");}catch{return []}if(t!=="strict")return [];let r=[],i=h.join(o,".rapidkit","toolchain.lock");if(!_.existsSync(i))return r.push("toolchain.lock is missing \u2014 run `rapidkit bootstrap` first (strict mode requires a reproducible toolchain)."),r;let n={};try{n=JSON.parse(await _.promises.readFile(i,"utf-8"));}catch{return []}let a=n.runtime??{},s=Se(e);pe(s,e)&&!a.go?.version?r.push("go.version is not pinned in toolchain.lock \u2014 run `rapidkit setup go` first."):ue(s,e)&&!a.node?.version?r.push("node.version is not pinned in toolchain.lock \u2014 run `rapidkit setup node` first."):me(s,e)&&!a.python?.version&&r.push("python.version is not pinned in toolchain.lock \u2014 run `rapidkit setup python` first.");try{let p=JSON.parse(await _.promises.readFile(h.join(o,".rapidkit","workspace.json"),"utf-8")).profile??"";p==="python-only"&&(pe(s,e)||ue(s,e))?r.push('Workspace profile is "python-only" but this project is not Python.'):p==="node-only"&&(pe(s,e)||me(s,e))?r.push('Workspace profile is "node-only" but this project is not Node.'):p==="go-only"&&(ue(s,e)||me(s,e))&&r.push('Workspace profile is "go-only" but this project is not Go.');}catch{}return r}async function Gs(){let e=async x=>{if(!a()||!x.toLowerCase().endsWith(".cmd"))return false;try{let C=(await b.readFile(x,"utf8")).replace(/\r\n/g,`
3562
- `).toLowerCase(),E=C.includes("\\.rapidkit\\rapidkit"),G=C.includes("\\.rapidkit\\rapidkit.cmd")||C.includes("\\.rapidkit\\rapidkit.exe")||C.includes("\\.venv\\scripts\\rapidkit.exe");return E&&!G}catch{return false}},o=process.cwd(),t=process.argv.slice(2),r=t[0],i=r==="init",n=new Set(["dev","start","build","test"]),a$2=!r||r==="--help"||r==="-h"||r==="help",s=Er(o),c=_.existsSync(h.join(o,".rapidkit","project.json")),p=Se(o),d=pe(p,o)||ue(p,o),u=!!r&&n.has(r)&&d;if(Ft(t[0])||t[0]==="create"||t[0]==="init"&&s&&!c)return false;try{let x=r==="shell"&&t[1]==="activate",T=r==="create",C=await vo(o,{cwd:o,timeoutMs:1200});if(C.ok&&C.data?.isRapidkitProject&&C.data.engine==="python"){let E=T||Ft(r);if(!a$2&&!x&&!E&&!i&&!u){if(r&&fr.includes(r)){let N=await kr(o).catch(()=>[]);if(N.length>0){process.stderr.write(l.red("\u274C Strict policy violations prevent running this command:")+`
3563
- `);for(let $ of N)process.stderr.write(l.red(` \u2022 ${$}`)+`
3564
- `);process.exit(1);}}let G=await d$1(process.argv.slice(2),{cwd:o});process.exit(G);}}}catch{}let m=Ir(o),w=a(),f=i$1(o),g=null;for(let x of f)if(await b.pathExists(x)){if(await e(x)){a$1.warn(`Skipping legacy/broken Windows launcher candidate: ${x}. Falling back to core bridge.`);continue}g=x;break}let R=r==="create";if(r==="init"&&s&&!c)return false;if(r&&fr.includes(r)){let x=await kr(o);if(x.length>0){process.stderr.write(l.red("\u274C Strict policy violations prevent running this command:")+`
3565
- `);for(let T of x)process.stderr.write(l.red(` \u2022 ${T}`)+`
3566
- `);process.exit(1);}}if(g&&r&&mr.includes(r)&&!R&&!i&&!u){a$1.debug(`Delegating to local CLI: ${g} ${t.join(" ")}`);let x=r==="init"?pr():process.env,T=spawn(g,t,{stdio:"inherit",cwd:o,shell:w,env:x});return T.on("close",C=>{process.exit(C??0);}),T.on("error",C=>{a$1.error(`Failed to run local rapidkit: ${C.message}`),process.exit(1);}),true}if(m&&await b.pathExists(m))try{if((await b.readJson(m)).engine==="pip"){let T=t[0],C=i$1(o),E=null;for(let G of C)if(await b.pathExists(G)){if(await e(G)){a$1.warn(`Skipping legacy/broken Windows launcher candidate: ${G}. Falling back to core bridge.`);continue}E=G;break}if(E&&T&&mr.includes(T)&&T!=="init"&&!u){a$1.debug(`Delegating to local CLI (early detection): ${E} ${t.join(" ")}`);let G=T==="init"?pr():process.env,N=spawn(E,t,{stdio:"inherit",cwd:o,env:G});return N.on("close",$=>process.exit($??0)),N.on("error",$=>{a$1.error(`Failed to run local rapidkit: ${$.message}`),process.exit(1);}),true}if(T==="shell"&&t[1]==="activate"){let G=a()?`# RapidKit: activation snippet (PowerShell)
142
+ `}function Xr(){return ['version: "1.0"','mode: warn # "warn" or "strict"','dependency_sharing_mode: isolated # "isolated" or "shared-runtime-caches" or "shared-node-deps"',"# change profile (recommended): npx rapidkit bootstrap --profile polyglot","# change mode/dependency manually: edit this file and rerun npx rapidkit init","rules:"," enforce_workspace_marker: true"," enforce_toolchain_lock: false"," disallow_untrusted_tool_sources: false"," enforce_compatibility_matrix: false"," require_mirror_lock_for_offline: true",""].join(`
143
+ `)}async function $o(t){let e=f.join(t,".rapidkit","policies.yml");return await x__default.pathExists(e)?w.promises.readFile(e,"utf-8"):Xr()}async function en(t,e){let o=f.join(t,".rapidkit"),r=f.join(o,"policies.yml");await x__default.ensureDir(o);let n=e.endsWith(`
144
+ `)?e:`${e}
145
+ `;await w.promises.writeFile(r,n,"utf-8");}function tn(t){let e=t.trim().toLowerCase();return e==="true"||e==="1"||e==="on"?true:e==="false"||e==="0"||e==="off"?false:null}function Uo(t){if(!t)return "isolated";let o=t.match(/^\s*dependency_sharing_mode:\s*([a-zA-Z\-]+)\s*(?:#.*)?$/m)?.[1]?.toLowerCase();return o==="shared-runtime-caches"||o==="shared-node-deps"||o==="isolated"?o:"isolated"}function Ho(t){if(!t)return {mode:"isolated",status:"skipped",message:"No policies.yml found; dependency_sharing_mode defaults to isolated."};let e=t.match(/^\s*dependency_sharing_mode:\s*([a-zA-Z\-]+)\s*(?:#.*)?$/m);if(!e)return {mode:"isolated",status:"skipped",message:"dependency_sharing_mode is not set; defaulting to isolated."};let o=e[1].toLowerCase();return o==="isolated"||o==="shared-runtime-caches"||o==="shared-node-deps"?{mode:o,status:"passed",message:`dependency_sharing_mode is valid: ${o}.`}:{mode:"isolated",status:"failed",message:`Invalid dependency_sharing_mode: ${o}. Use one of: isolated, shared-runtime-caches, shared-node-deps.`}}async function Jo(t,e){let o=q(t),r=o?f.join(o,".rapidkit","policies.yml"):null,n="isolated";if(r&&await x__default.pathExists(r))try{let c=await w.promises.readFile(r,"utf-8"),m=Ho(c);if(m.status==="failed")return console.log(s.red(`\u274C ${m.message}`)),{ok:false,code:1};n=m.mode;}catch{return console.log(s.red("\u274C Failed to read workspace policy file (.rapidkit/policies.yml).")),{ok:false,code:1}}let i=process.env.RAPIDKIT_DEP_SHARING_MODE,a=process.env.RAPIDKIT_WORKSPACE_PATH;process.env.RAPIDKIT_DEP_SHARING_MODE=n,o&&(process.env.RAPIDKIT_WORKSPACE_PATH=o);try{return {ok:true,value:await e()}}finally{typeof i>"u"?delete process.env.RAPIDKIT_DEP_SHARING_MODE:process.env.RAPIDKIT_DEP_SHARING_MODE=i,typeof a>"u"?delete process.env.RAPIDKIT_WORKSPACE_PATH:process.env.RAPIDKIT_WORKSPACE_PATH=a;}}async function F(t,e,o){return await new Promise(r=>{let n=spawn(t,e,{stdio:"inherit",cwd:o,shell:b$2()});n.on("close",i=>r(i??1)),n.on("error",()=>r(1));})}async function De(t,e){await x__default.outputFile(t,`${JSON.stringify(e,null,2)}
146
+ `,"utf-8");}async function on(t){let e=new Set(["go-only"]);try{let r=f.join(t,".rapidkit","workspace.json"),n=JSON.parse(await w.promises.readFile(r,"utf-8"));if(e.has(n.profile??""))return 0}catch{}let o="poetry";try{let{readWorkspaceMarker:r}=await import('./workspace-marker-IOPQ42A7.js'),i=(await r(t))?.metadata?.npm?.installMethod;(i==="poetry"||i==="venv"||i==="pipx"||i==="pip")&&(o=i);}catch{}if(o==="poetry"||o==="venv"){let r=f.join(t,"pyproject.toml"),n=false;try{n=(await w.promises.readFile(r,"utf-8")).includes("rapidkit-core");}catch{n=false;}let i=process.env.RAPIDKIT_DEV_PATH,a=i?await x__default.pathExists(i):false;if(n){let c=Fr(t);if(!await x__default.pathExists(c)){let l=await Wr(t);if(l!==0)return l}let d=await F(c,a&&i?["-m","pip","install",i,"--quiet","--disable-pip-version-check"]:["-m","pip","install","rapidkit-core","--quiet","--disable-pip-version-check"],t);if(d!==0)return d}else {let c=await F("poetry",["install","--no-root"],t);if(c!==0)return c;let m=await F("poetry",["add","rapidkit-core"],t);if(m!==0)return m}try{let{writeWorkspaceLauncher:c}=await import('./create-27NVMJAR.js');await c(t,"poetry");}catch{}return 0}return 0}async function Vo(t){let e=await w.promises.readdir(t,{withFileTypes:true}),o=[];for(let r of e){if(!r.isDirectory()||r.name.startsWith("."))continue;let n=f.join(t,r.name),i=f.join(n,".rapidkit","context.json"),a=f.join(n,".rapidkit","project.json");(await x__default.pathExists(i)||await x__default.pathExists(a))&&o.push(n);}return o}function rn(t){let e="my-workspace",o=1;for(;;){let r=o===1?e:`${e}-${o}`,n=f.join(t,r);if(!w.existsSync(n))return {name:r,targetPath:n};o+=1;}}async function gt(t){let o=await Se("go",{runCommandInCwd:F,runCoreRapidkit:d}).initProject(t);return o.message&&console.log(s.red(`\u274C ${o.message}`)),o.exitCode}async function He(t,e){let o=Se("node",{runCommandInCwd:F,runCoreRapidkit:d});return t==="init"?(await o.initProject(e)).exitCode:t==="dev"?(await o.runDev(e)).exitCode:t==="test"?(await o.runTest(e)).exitCode:t==="build"?(await o.runBuild(e)).exitCode:(await o.runStart(e)).exitCode}async function nn(t,e=Tt){let o=process.env.RAPIDKIT_SKIP_LOCK_SYNC;typeof o>"u"&&(process.env.RAPIDKIT_SKIP_LOCK_SYNC="1");try{let i=function(C){if(!C)return null;let R=C.trim().toLowerCase();return R==="minimal"||R==="go-only"||R==="python-only"||R==="node-only"||R==="polyglot"||R==="enterprise"?R:null},a=function(C){let j=C.match(/^\s*mode:\s*([a-zA-Z]+)\s*(?:#.*)?$/m)?.[1]?.toLowerCase()==="strict"?"strict":"warn",Y=(H,de)=>{let we=C.match(new RegExp(`^\\s*${H}:\\s*(true|false)\\s*(?:#.*)?$`,"m"));return we?we[1].toLowerCase()==="true":de};return {mode:j,dependency_sharing_mode:Uo(C),rules:{enforce_workspace_marker:Y("enforce_workspace_marker",true),enforce_toolchain_lock:Y("enforce_toolchain_lock",false),disallow_untrusted_tool_sources:Y("disallow_untrusted_tool_sources",false),enforce_compatibility_matrix:Y("enforce_compatibility_matrix",false),require_mirror_lock_for_offline:Y("require_mirror_lock_for_offline",true)}}};let c=["init"],m,d=false,l=false,u=false;for(let C=1;C<t.length;C+=1){let R=t[C];if(R==="--ci"){d=true;continue}if(R==="--offline"){l=true;continue}if(R==="--json"){u=true;continue}if(R==="--profile"){let O=t[C+1];if(!O||O.startsWith("-"))return console.log(s.yellow("Usage: rapidkit bootstrap [path] [--profile <minimal|go-only|python-only|node-only|polyglot|enterprise>] [--ci] [--offline] [--json]")),1;m=O,C+=1;continue}if(R.startsWith("--profile=")){m=R.slice(10);continue}c.push(R);}let y=i(m);if(m&&!y)return console.log(s.red(`Invalid profile: ${m}. Use one of: minimal, go-only, python-only, node-only, polyglot, enterprise.`)),1;let v=process.cwd(),p=q(v);p||(p=zr(v));let g=[],A=null,P=null;if(p)try{let C=f.join(p,".rapidkit","workspace.json"),R=await w.promises.readFile(C,"utf-8"),O=JSON.parse(R);P=i(O.profile);}catch{P=null;}let S=["minimal","python-only","node-only","go-only","polyglot","enterprise"],b={minimal:"minimal \u2014 Foundation files only (fastest bootstrap, mixed projects)","python-only":"python-only \u2014 Python + Poetry (FastAPI, Django, ML pipelines)","node-only":"node-only \u2014 Node.js runtime (NestJS, Express, Next.js)","go-only":"go-only \u2014 Go runtime (Fiber, Gin, gRPC, microservices)",polyglot:"polyglot \u2014 Python + Node.js + Go multi-runtime workspace",enterprise:"enterprise \u2014 Polyglot + governance + Sigstore verification"},_=y;if(!!p&&!y&&!d&&!u&&!!process.stdin.isTTY&&!!process.stdout.isTTY){let C=P||"minimal",{chosenProfile:R}=await he.prompt([{type:"rawlist",name:"chosenProfile",message:`Select workspace profile for bootstrap (current: ${C})`,choices:S.map(O=>({name:O===C?`${b[O]} \u2190 current`:b[O],value:O})),default:S.indexOf(C)}]);_=R;}let T=_||P||"minimal";if(p)try{let R=T==="python-only"||T==="polyglot"||T==="enterprise"?"poetry":"venv",O;try{let de=(await w.promises.readFile(f.join(p,".python-version"),"utf-8")).trim();de&&(O=de);}catch{}let{syncWorkspaceFoundationFiles:j}=await import('./create-27NVMJAR.js'),Y=await j(p,{workspaceName:f.basename(p),installMethod:R,pythonVersion:O,profile:T,writeMarker:true,writeGitignore:true,onlyIfMissing:true});g.push({id:"workspace.legacy.sync",status:Y.length>0?"passed":"skipped",message:Y.length>0?`Legacy workspace foundation synchronized: ${Y.join(", ")}`:"Workspace foundation files are already up to date."});}catch(C){g.push({id:"workspace.legacy.sync",status:"failed",message:`Failed to synchronize legacy workspace foundation files: ${C.message}`});}if(p&&_&&_!==P)try{let C=f.join(p,".rapidkit","workspace.json"),R=await w.promises.readFile(C,"utf-8"),O=JSON.parse(R);O.profile=_,await w.promises.writeFile(C,JSON.stringify(O,null,2)+`
147
+ `,"utf-8");}catch{}let I={mode:"warn",dependency_sharing_mode:"isolated",rules:{enforce_workspace_marker:true,enforce_toolchain_lock:false,disallow_untrusted_tool_sources:false,enforce_compatibility_matrix:false,require_mirror_lock_for_offline:true}},fe=null;if(p)try{let C=await w.promises.readFile(f.join(p,".rapidkit","policies.yml"),"utf-8");fe=C,I=a(C);}catch{g.push({id:"policy.file",status:"skipped",message:"No workspace policy file found; using default bootstrap policy."});}else g.push({id:"workspace.detect",status:"skipped",message:"No workspace marker found; bootstrap runs in project/single-path mode."});if(p){let C=Ho(fe);I.dependency_sharing_mode=C.mode,g.push({id:"policy.schema.dependency_sharing_mode",status:C.status,message:C.message}),g.push({id:"policy.dependency_sharing_mode.effective",status:"passed",message:I.dependency_sharing_mode==="isolated"?"Effective dependency mode: isolated (default secure mode).":I.dependency_sharing_mode==="shared-node-deps"?"Effective dependency mode: shared-node-deps (Node projects share workspace-level caches).":"Effective dependency mode: shared-runtime-caches (Node/Python/Go share workspace-level caches)."});let R=w.existsSync(f.join(p,".rapidkit-workspace"));g.push({id:"policy.enforce_workspace_marker",status:!I.rules.enforce_workspace_marker||R?"passed":"failed",message:!I.rules.enforce_workspace_marker||R?"Workspace marker policy satisfied.":"Workspace marker policy failed: .rapidkit-workspace is missing."});let O=w.existsSync(f.join(p,".rapidkit","toolchain.lock"));g.push({id:"policy.enforce_toolchain_lock",status:!I.rules.enforce_toolchain_lock||O?"passed":"failed",message:!I.rules.enforce_toolchain_lock||O?"Toolchain lock policy satisfied.":"Toolchain lock policy failed: .rapidkit/toolchain.lock is missing."});let j=process.env.RAPIDKIT_TRUSTED_SOURCES==="1"||w.existsSync(f.join(p,".rapidkit","trusted-sources.lock"));g.push({id:"policy.disallow_untrusted_tool_sources",status:!I.rules.disallow_untrusted_tool_sources||j?"passed":"failed",message:!I.rules.disallow_untrusted_tool_sources||j?"Trusted tool sources policy satisfied.":"Trusted tool sources policy failed: set RAPIDKIT_TRUSTED_SOURCES=1 or provide .rapidkit/trusted-sources.lock."});let Y=f.join(p,".rapidkit","compatibility-matrix.json"),H=w.existsSync(Y),de=I.rules.enforce_compatibility_matrix;if(g.push({id:"policy.enforce_compatibility_matrix",status:!de||H?"passed":"failed",message:!de||H?"Compatibility matrix policy satisfied.":"Compatibility matrix policy failed: .rapidkit/compatibility-matrix.json is missing."}),H)try{let N=await w.promises.readFile(Y,"utf-8"),V=JSON.parse(N),Be=!!V&&typeof V=="object";g.push({id:"compatibility.matrix.parse",status:Be?"passed":"failed",message:Be?"Compatibility matrix parsed successfully.":"Compatibility matrix parse failed: invalid JSON object."});}catch{g.push({id:"compatibility.matrix.parse",status:"failed",message:"Compatibility matrix parse failed: invalid JSON."});}let we=f.join(p,".rapidkit","mirror-config.json"),z=f.join(p,".rapidkit","mirror.lock"),ve=w.existsSync(we),ke=w.existsSync(z),Ve={};if(ve)try{Ve=JSON.parse(await w.promises.readFile(we,"utf-8")),g.push({id:"mirror.config.parse",status:"passed",message:"Mirror configuration parsed successfully."});}catch{g.push({id:"mirror.config.parse",status:"failed",message:"Mirror configuration parse failed: invalid JSON in .rapidkit/mirror-config.json."});}let kt=await St(p,{ciMode:d,offlineMode:l});if(g.push(...kt.checks.map(N=>({id:N.id,status:N.status,message:N.message}))),A=kt.details,kt.details.lockWritten&&(ke=true),l){let N=process.env.RAPIDKIT_MIRROR_ENABLED==="1"||Ve.enabled===true;g.push({id:"offline.mirror.enabled",status:N?"passed":"failed",message:N?"Offline mode mirror is enabled.":'Offline mode requires mirror enablement (set RAPIDKIT_MIRROR_ENABLED=1 or .rapidkit/mirror-config.json {"enabled": true}).'});let V=I.rules.require_mirror_lock_for_offline;g.push({id:"offline.mirror.lock",status:!V||ke?"passed":"failed",message:!V||ke?"Offline mode mirror lock policy satisfied.":"Offline mode mirror lock policy failed: .rapidkit/mirror.lock is missing."});}else g.push({id:"offline.mirror.enabled",status:"skipped",message:"Offline mirror checks skipped (offline mode is disabled)."});let Yo=await Vo(p),te=new Set;for(let N of Yo){let V=ye(N);if(ne(V,N)){te.add("go");continue}if(ie(V,N)){te.add("node");continue}if(se(V,N)){te.add("python");continue}te.add("unknown");}if(T==="go-only"){let N=te.size===0||[...te].every(V=>V==="go");g.push({id:"profile.go-only",status:N?"passed":"failed",message:N?"go-only profile validated for discovered projects.":`go-only profile mismatch: detected runtimes [${[...te].join(", ")}].`});}else if(T==="python-only"){let N=te.size===0||[...te].every(V=>V==="python");g.push({id:"profile.python-only",status:N?"passed":"failed",message:N?"python-only profile validated for discovered projects.":`python-only profile mismatch: detected runtimes [${[...te].join(", ")}].`});}else if(T==="node-only"){let N=te.size===0||[...te].every(V=>V==="node");g.push({id:"profile.node-only",status:N?"passed":"failed",message:N?"node-only profile validated for discovered projects.":`node-only profile mismatch: detected runtimes [${[...te].join(", ")}].`});}else if(T==="minimal"){let N=[...te].filter(Be=>Be!=="unknown"),V=N.length<=1;g.push({id:"profile.minimal",status:V?"passed":"failed",message:V?"minimal profile is compatible with detected runtime mix.":`minimal profile mismatch: multiple runtimes detected [${N.join(", ")}].`});}else T==="enterprise"&&(g.push({id:"profile.enterprise.ci",status:d?"passed":"failed",message:d?"enterprise profile running with --ci.":"enterprise profile expects --ci for deterministic non-interactive mode."}),g.push({id:"profile.enterprise.compatibility-matrix",status:H?"passed":"failed",message:H?"enterprise profile has compatibility matrix.":"enterprise profile requires .rapidkit/compatibility-matrix.json."}),g.push({id:"profile.enterprise.mirror-config",status:ve?"passed":"failed",message:ve?"enterprise profile has mirror configuration.":"enterprise profile requires .rapidkit/mirror-config.json."}));}d&&(process.env.RAPIDKIT_BOOTSTRAP_CI="1"),l&&(process.env.RAPIDKIT_OFFLINE_MODE="1");let le=g.some(C=>C.id.startsWith("policy.schema.")&&C.status==="failed")||I.mode==="strict"&&g.some(C=>C.status==="failed"),ae=p||v,je=f.join(ae,".rapidkit","reports"),U=new Date().toISOString().replace(/[:.]/g,"-"),D=f.join(je,`bootstrap-compliance-${U}.json`),k=f.join(je,"bootstrap-compliance.latest.json"),Q={command:"bootstrap",timestamp:new Date().toISOString(),workspacePath:p,profile:T,options:{ci:d,offline:l,strict:I.mode==="strict"},policyMode:I.mode,policyRules:I.rules,mirrorLifecycle:A,checks:g};if(le){let C={...Q,result:"blocked",initExitCode:null};return await x__default.ensureDir(je),await De(D,C),await De(k,C),u?process.stdout.write(`${JSON.stringify(C,null,2)}
148
+ `):(console.log(s.red("\u274C Bootstrap blocked by strict policy checks.")),console.log(s.gray(`Compliance report: ${D}`))),1}let Z=0;u||(Z=await e(c));let K=g.filter(C=>C.status==="failed").length,B=Z!==0?"failed":K>0?"ok_with_warnings":"ok",ee={...Q,result:B,initExitCode:Z};if(await x__default.ensureDir(je),await De(D,ee),await De(k,ee),u)process.stdout.write(`${JSON.stringify(ee,null,2)}
149
+ `);else {let C=g.filter(R=>R.status==="failed").length;C>0&&console.log(s.yellow(`\u26A0\uFE0F Bootstrap completed with ${C} policy/profile warnings.`)),console.log(s.gray(`Compliance report: ${D}`));}return Z}finally{typeof o>"u"?delete process.env.RAPIDKIT_SKIP_LOCK_SYNC:process.env.RAPIDKIT_SKIP_LOCK_SYNC=o;}}async function sn(t){let e=(t[1]||"").toLowerCase(),o=t.includes("--warm-deps")||t.includes("--warm-dependencies");if(!e||!["python","node","go"].includes(e))return console.log(s.yellow("Usage: rapidkit setup <python|node|go> [--warm-deps]")),1;let r=async(d,l)=>{if(d==="node"){if(!w.existsSync(f.join(l,"package.json")))return {exitCode:0,message:"Node warm-up skipped: package.json not found in current directory."};let y=w.existsSync(f.join(l,"pnpm-lock.yaml")),v=w.existsSync(f.join(l,"yarn.lock"));return y?{exitCode:await F("pnpm",["install","--lockfile-only","--ignore-scripts"],l)}:v?{exitCode:await F("yarn",["install","--ignore-scripts"],l)}:{exitCode:await F("npm",["install","--package-lock-only","--ignore-scripts"],l)}}return d==="go"?w.existsSync(f.join(l,"go.mod"))?{exitCode:await F("go",["mod","download"],l)}:{exitCode:0,message:"Go warm-up skipped: go.mod not found in current directory."}:{exitCode:0,message:"Dependency warm-up currently applies to node/go runtimes."}},n=Se(e,{runCommandInCwd:F,runCoreRapidkit:(d$1,l)=>d(d$1,{...l,cwd:void 0})}),i=await n.checkPrereqs(),a=await n.doctorHints(process.cwd()),c=q(process.cwd()),m=c||process.cwd();if(i.exitCode===0){console.log(s.green(`\u2705 ${e} prerequisites look good.`));let d=["python","node","go"].filter(l=>l!==e).join("/");if(console.log(s.gray(` Scope: validated ${e} runtime only. ${d} checks are optional unless your workspace profile uses them.`)),e==="python"&&console.log(s.gray(" Note: Poetry is recommended, but venv/pipx-based flows are supported in workspace creation.")),n.warmSetupCache&&((await n.warmSetupCache(m)).exitCode===0?console.log(s.gray(` ${e} cache warm-up completed.`)):console.log(s.yellow(` ${e} cache warm-up skipped (non-fatal).`))),o){let l=await r(e,m),u=/skipped/i.test(l.message||"");l.message&&console.log(s.gray(` ${l.message}`)),l.exitCode===0&&!u?console.log(s.gray(` ${e} dependency warm-up completed (--warm-deps).`)):l.exitCode!==0&&console.log(s.yellow(` ${e} dependency warm-up failed (non-fatal).`));}if(c)try{let l=f.join(c,".rapidkit","toolchain.lock"),u={};try{u=JSON.parse(await w.promises.readFile(l,"utf-8"));}catch{}(!u.runtime||typeof u.runtime!="object")&&(u.runtime={});let y=u.runtime;if(e==="python"){let v=null;try{let{execa:p}=await import('execa');for(let g of Ot()){let P=await p(g,g==="py"?["-3","--version"]:["--version"],{cwd:c,stdio:"pipe",reject:false,timeout:3e3});if(P.exitCode===0){let b=(P.stdout||P.stderr||"").match(/Python\s+(\S+)/);if(v=b?b[1]:null,v)break}}}catch{}y.python={...y.python||{},version:v,last_setup:new Date().toISOString()};}else if(e==="node")y.node={...y.node||{},version:process.version,last_setup:new Date().toISOString()};else if(e==="go"){let v=null;try{let{execa:p}=await import('execa'),A=((await p("go",["version"],{cwd:c,stdio:"pipe"})).stdout||"").match(/go(\d+\.\d+(?:\.\d+)?)/i);v=A?A[1]:null;}catch{}y.go={...y.go||{},version:v,last_setup:new Date().toISOString()};}u.updated_at=new Date().toISOString(),await w.promises.writeFile(l,JSON.stringify(u,null,2)+`
150
+ `,"utf-8"),console.log(s.gray(" toolchain.lock updated (.rapidkit/toolchain.lock)"));}catch{}}else console.log(s.red(`\u274C ${e} prerequisites check failed.`));if(a.length>0){console.log(s.gray(`
151
+ Hints:`));for(let d of a)console.log(s.gray(`- ${d}`));}return i.exitCode}function an(t){let e={strategy:"shared",prune_on_bootstrap:false,self_heal:true,verify_integrity:false};for(let o of t.split(`
152
+ `)){let r=o.trim(),n=r.match(/^strategy:\s*(\S+)/);n&&(e.strategy=n[1].replace(/['"]]/g,""));let i=r.match(/^prune_on_bootstrap:\s*(true|false)/);i&&(e.prune_on_bootstrap=i[1]==="true");let a=r.match(/^self_heal:\s*(true|false)/);a&&(e.self_heal=a[1]==="true");let c=r.match(/^verify_integrity:\s*(true|false)/);c&&(e.verify_integrity=c[1]==="true");}return e}async function cn(t){let e=(t[1]||"status").toLowerCase(),o=ut.getInstance(),r=q(process.cwd()),n={strategy:"shared",prune_on_bootstrap:false,self_heal:true,verify_integrity:false};if(r)try{let i=await w.promises.readFile(f.join(r,".rapidkit","cache-config.yml"),"utf-8");n=an(i);}catch{}return e==="status"?(console.log(s.cyan("RapidKit cache is enabled")),console.log(s.cyan("RapidKit cache status")),r?(console.log(s.gray(` Workspace: ${r}`)),console.log(s.gray(` Strategy: ${n.strategy}`)),console.log(s.gray(` Self-heal: ${n.self_heal}`)),console.log(s.gray(` Prune on bootstrap:${n.prune_on_bootstrap}`)),console.log(s.gray(` Verify integrity: ${n.verify_integrity}`))):console.log(s.gray(" (not inside a workspace \u2014 showing in-memory cache only)")),console.log(s.gray(" In-memory cache: enabled")),console.log(s.gray(" Use: rapidkit cache clear|prune|repair")),0):e==="clear"?(await o.clear(),console.log(s.green("Cache clear completed")),console.log(s.green("\u2705 Cache cleared (all entries removed).")),0):e==="prune"?(await o.clear(),console.log(s.green("\u2705 Cache pruned (stale entries removed).")),n.prune_on_bootstrap||console.log(s.gray(" Tip: set prune_on_bootstrap: true in .rapidkit/cache-config.yml to auto-prune on every bootstrap.")),0):e==="repair"?n.self_heal?(await o.clear(),console.log(s.green("\u2705 Cache repaired (self-heal applied, stale entries evicted).")),n.verify_integrity&&console.log(s.gray(" Integrity verification is enabled in cache-config.yml.")),0):(console.log(s.yellow("\u26A0\uFE0F self_heal is disabled in .rapidkit/cache-config.yml \u2014 skipping repair.")),0):(console.log(s.yellow("Usage: rapidkit cache <status|clear|prune|repair>")),1)}async function ln(t,e,o,r){let n=(e||"show").toLowerCase(),i=f.join(t,".rapidkit","policies.yml");if(n==="show"||n==="status"||n==="get"){let l=await $o(t),u=Io(l);return console.log(s.cyan(`Policy file: ${i}`)),console.log(s.gray(` mode: ${u.mode}`)),console.log(s.gray(` dependency_sharing_mode: ${u.dependency_sharing_mode}`)),console.log(s.gray(" rules:")),console.log(s.gray(` enforce_workspace_marker: ${u.rules.enforce_workspace_marker}`)),console.log(s.gray(` enforce_toolchain_lock: ${u.rules.enforce_toolchain_lock}`)),console.log(s.gray(` disallow_untrusted_tool_sources: ${u.rules.disallow_untrusted_tool_sources}`)),console.log(s.gray(` enforce_compatibility_matrix: ${u.rules.enforce_compatibility_matrix}`)),console.log(s.gray(` require_mirror_lock_for_offline: ${u.rules.require_mirror_lock_for_offline}`)),console.log(s.gray("Examples:")),console.log(s.gray(" npx rapidkit workspace policy set mode strict")),console.log(s.gray(" npx rapidkit workspace policy set dependency_sharing_mode shared-runtime-caches")),console.log(s.gray(" npx rapidkit workspace policy set rules.enforce_toolchain_lock true")),0}if(n!=="set")return console.log(s.red(`Unknown workspace policy action: ${e||""}`)),console.log(s.gray("Available: show, set")),1;if(!o||typeof r>"u")return console.log(s.yellow("Usage: rapidkit workspace policy set <key> <value>")),console.log(s.gray("Allowed keys:")),console.log(s.gray(" mode (warn|strict)")),console.log(s.gray(" dependency_sharing_mode (isolated|shared-runtime-caches|shared-node-deps)")),console.log(s.gray(" rules.enforce_workspace_marker (true|false)")),console.log(s.gray(" rules.enforce_toolchain_lock (true|false)")),console.log(s.gray(" rules.disallow_untrusted_tool_sources (true|false)")),console.log(s.gray(" rules.enforce_compatibility_matrix (true|false)")),console.log(s.gray(" rules.require_mirror_lock_for_offline (true|false)")),1;let a=o.trim(),m=await $o(t);if(a==="mode"){let l=r.trim().toLowerCase();if(l!=="warn"&&l!=="strict")return console.log(s.red("\u274C Invalid mode. Use: warn | strict")),1;m=jo(m,"mode",`${l} # "warn" or "strict"`);}else if(a==="dependency_sharing_mode"){let l=r.trim().toLowerCase();if(l!=="isolated"&&l!=="shared-runtime-caches"&&l!=="shared-node-deps")return console.log(s.red("\u274C Invalid dependency_sharing_mode. Use: isolated | shared-runtime-caches | shared-node-deps")),1;m=jo(m,"dependency_sharing_mode",`${l} # "isolated" or "shared-runtime-caches" or "shared-node-deps"`);}else if(a.startsWith("rules.")){let l=a.slice(6);if(!(l in qo))return console.log(s.red(`\u274C Unknown policy rule: ${l}`)),1;let u=tn(r);if(u===null)return console.log(s.red("\u274C Rule values must be boolean: true | false")),1;m=Zr(m,l,u);}else return console.log(s.red(`\u274C Unknown policy key: ${a}`)),1;await en(t,m);let d=Io(m);return console.log(s.green(`\u2705 Updated ${a} in .rapidkit/policies.yml`)),console.log(s.gray(` mode: ${d.mode}`)),console.log(s.gray(` dependency_sharing_mode: ${d.dependency_sharing_mode}`)),console.log(s.gray(" Tip: run `npx rapidkit workspace policy show` to inspect all values.")),0}async function dn(t){let e=(t[1]||"status").toLowerCase(),o=t.includes("--json"),r=q(process.cwd());if(!r)return console.log(s.red("\u274C Not inside a RapidKit workspace")),console.log(s.gray("\u{1F4A1} Run this command from within a workspace directory")),1;let n=f.join(r,".rapidkit"),i=f.join(n,"mirror-config.json"),a=f.join(n,"mirror.lock"),c=f.join(n,"mirror","artifacts"),m=f.join(n,"reports");async function d(l){let u=new Date().toISOString().replace(/[:.]/g,"-"),y=f.join(m,`mirror-ops-${u}.json`),v=f.join(m,"mirror-ops.latest.json");await x__default.ensureDir(m),await De(y,l),await De(v,l);}if(e==="status"){if(!await x__default.pathExists(i))try{let A={schema_version:"1.0",enabled:false,strategy:"on-demand",artifacts:[],created_at:new Date().toISOString(),note:"Auto-generated by rapidkit mirror status. Set enabled: true and add artifact entries to activate mirroring."};await x__default.ensureDir(n),await w.promises.writeFile(i,JSON.stringify(A,null,2)+`
153
+ `,"utf-8"),console.log(s.gray(" mirror-config.json created with defaults (.rapidkit/mirror-config.json)"));}catch{}let u=await x__default.pathExists(i),y=await x__default.pathExists(c),v=await x__default.pathExists(a),p=y?(await w.promises.readdir(c,{withFileTypes:true})).filter(A=>A.isFile()).length:0,g={command:"mirror",action:e,result:"ok",timestamp:new Date().toISOString(),workspacePath:r,mirror:{configExists:u,lockExists:v,artifactsCount:p}};return await d(g),o?(process.stdout.write(`${JSON.stringify(g,null,2)}
154
+ `),0):(console.log(s.cyan("RapidKit mirror status")),console.log(s.gray(`Workspace: ${r}`)),console.log(s.gray(`Config: ${u?"present":"missing"} (${i})`)),console.log(s.gray(`Lock: ${v?"present":"missing"} (${a})`)),console.log(s.gray(`Artifacts: ${p}`)),0)}if(e==="sync"||e==="verify"||e==="rotate"){let l=await St(r,{ciMode:true,offlineMode:e==="verify",forceRun:true}),u=l.checks.filter(p=>p.status==="failed"),y=l.checks.some(p=>p.id.startsWith("mirror.verify.")&&p.status==="failed");if(e==="verify"&&y){let p={command:"mirror",action:e,result:"failed",timestamp:new Date().toISOString(),workspacePath:r,details:l.details,checks:l.checks};if(await d(p),o)return process.stdout.write(`${JSON.stringify(p,null,2)}
155
+ `),1;console.log(s.red("\u274C Mirror verify failed."));for(let g of l.checks.filter(A=>A.id.startsWith("mirror.verify.")))console.log(s.gray(`- ${g.id}: ${g.message}`));return 1}if(u.length>0){let p={command:"mirror",action:e,result:"failed",timestamp:new Date().toISOString(),workspacePath:r,details:l.details,checks:l.checks};if(await d(p),o)return process.stdout.write(`${JSON.stringify(p,null,2)}
156
+ `),1;console.log(s.yellow(`\u26A0\uFE0F Mirror ${e} completed with ${u.length} issue(s).`));for(let g of u)console.log(s.gray(`- ${g.id}: ${g.message}`));return 1}let v={command:"mirror",action:e,result:"ok",timestamp:new Date().toISOString(),workspacePath:r,details:l.details,checks:l.checks};return await d(v),o?(process.stdout.write(`${JSON.stringify(v,null,2)}
157
+ `),0):e==="rotate"?(console.log(s.green(`\u2705 Mirror rotate completed. Rotated files: ${l.details.rotatedFiles}.`)),0):e==="verify"?(console.log(s.green(`\u2705 Mirror verify completed. Verified artifacts: ${l.details.verifiedArtifacts}.`)),0):(console.log(s.green(`\u2705 Mirror sync completed. Synced artifacts: ${l.details.syncedArtifacts}.`)),0)}return console.log(s.yellow("Usage: rapidkit mirror <status|sync|verify|rotate> [--json]")),1}async function Tt(t){let e=process.env.RAPIDKIT_SKIP_LOCK_SYNC;typeof e>"u"&&(process.env.RAPIDKIT_SKIP_LOCK_SYNC="1");try{let o=process.cwd(),r=await Jo(o,async()=>{let n=q(o),i=Se("python",{runCommandInCwd:F,runCoreRapidkit:d});if(t.length>1){let y=f.resolve(o,t[1]),v=ye(y),p=await At(y);return ne(v,y)||p==="go"?await gt(y):ie(v,y)||p==="node"?await ft(y):se(v,y)||p==="python"?await mt(y,i):await d(t,{cwd:o})}let a=ye(o),c=!!q(o)&&o===q(o);if(!c&&ne(a,o))return await gt(o);let m=await At(o);if(!c&&(ie(a,o)||m==="node"))return await ft(o);if(!c&&(se(a,o)||m==="python"))return await mt(o,i);let d$1=n||q(o),l=Go(o),u=l?f.dirname(f.dirname(l)):null;if(u&&u!==d$1){let y=ye(u),v=await At(u);return ne(y,u)||v==="go"?await gt(u):ie(y,u)||v==="node"?await ft(u):se(y,u)||v==="python"?await mt(u,i):await d(["init"],{cwd:u})}if(d$1&&o===d$1){let y=await on(d$1);if(y!==0)return y;let v=await Vo(d$1);if(v.length===0){let p="minimal";try{p=JSON.parse(await w.promises.readFile(f.join(d$1,".rapidkit","workspace.json"),"utf-8")).profile??"minimal";}catch{}return p==="go-only"?(console.log(s.green("\u2714 Go workspace ready")),console.log(s.gray(`
158
+ No projects yet \u2014 create one and then run init inside it:`)),console.log(s.white(" npx rapidkit create project gofiber.standard my-api")),console.log(s.white(" cd my-api && npx rapidkit init")),console.log(s.gray(`
159
+ \u{1F4A1} Go dependencies are managed per-project (go.mod / go mod tidy).`))):(console.log(s.green("\u2714 Workspace ready")),console.log(s.gray(`
160
+ No projects yet \u2014 create one to get started:`)),console.log(s.white(" npx rapidkit create project"))),0}for(let p of v){let g=ye(p);if(ne(g,p)){let A=await gt(p);if(A!==0)return A}else {if(ie(g,p)){let P=await ft(p);if(P!==0)return P;continue}if(se(g,p)){let P=await mt(p,i);if(P!==0)return P;continue}let A=await d(["init"],{cwd:p});if(A!==0)return A}}return 0}if(!d$1){let y=await a$2(),{name:v}=rn(o),{createProject:p}=await import('./create-27NVMJAR.js');return await p(v,{yes:true,userConfig:y}),0}return await d(t,{cwd:o})});return r.ok?r.value:r.code}finally{typeof e>"u"?delete process.env.RAPIDKIT_SKIP_LOCK_SYNC:process.env.RAPIDKIT_SKIP_LOCK_SYNC=e;}}async function To(t){let e=q(t);if(!e)return [];let o="warn";try{(await w.promises.readFile(f.join(e,".rapidkit","policies.yml"),"utf-8")).match(/^\s*mode:\s*(warn|strict)\s*(?:#.*)?$/m)?.[1]==="strict"&&(o="strict");}catch{return []}if(o!=="strict")return [];let r=[],n=f.join(e,".rapidkit","toolchain.lock");if(!w.existsSync(n))return r.push("toolchain.lock is missing \u2014 run `rapidkit bootstrap` first (strict mode requires a reproducible toolchain)."),r;let i={};try{i=JSON.parse(await w.promises.readFile(n,"utf-8"));}catch{return []}let a=i.runtime??{},c=ye(t);ne(c,t)&&!a.go?.version?r.push("go.version is not pinned in toolchain.lock \u2014 run `rapidkit setup go` first."):ie(c,t)&&!a.node?.version?r.push("node.version is not pinned in toolchain.lock \u2014 run `rapidkit setup node` first."):se(c,t)&&!a.python?.version&&r.push("python.version is not pinned in toolchain.lock \u2014 run `rapidkit setup python` first.");try{let d=JSON.parse(await w.promises.readFile(f.join(e,".rapidkit","workspace.json"),"utf-8")).profile??"";d==="python-only"&&(ne(c,t)||ie(c,t))?r.push('Workspace profile is "python-only" but this project is not Python.'):d==="node-only"&&(ne(c,t)||se(c,t))?r.push('Workspace profile is "node-only" but this project is not Node.'):d==="go-only"&&(ie(c,t)||se(c,t))&&r.push('Workspace profile is "go-only" but this project is not Go.');}catch{}return r}async function pn(){let t=async P=>{if(!a()||!P.toLowerCase().endsWith(".cmd"))return false;try{let b=(await x__default.readFile(P,"utf8")).replace(/\r\n/g,`
161
+ `).toLowerCase(),_=b.includes("\\.rapidkit\\rapidkit"),M=b.includes("\\.rapidkit\\rapidkit.cmd")||b.includes("\\.rapidkit\\rapidkit.exe")||b.includes("\\.venv\\scripts\\rapidkit.exe");return _&&!M}catch{return false}},e=process.cwd(),o=process.argv.slice(2),r=o[0],n=r==="init",i$1=new Set(["dev","start","build","test"]),a$2=!r||r==="--help"||r==="-h"||r==="help",c=Wo(e),m=w.existsSync(f.join(e,".rapidkit","project.json")),d$1=ye(e),l=ne(d$1,e)||ie(d$1,e),u=!!r&&i$1.has(r)&&l;if(wt(o[0])||o[0]==="create"||o[0]==="init"&&c&&!m)return false;try{let P=r==="shell"&&o[1]==="activate",S=r==="create",b=await Ut(e,{cwd:e,timeoutMs:1200});if(b.ok&&b.data?.isRapidkitProject&&b.data.engine==="python"){let _=S||wt(r);if(!a$2&&!P&&!_&&!n&&!u){if(r&&Eo.includes(r)){let T=await To(e).catch(()=>[]);if(T.length>0){process.stderr.write(s.red("\u274C Strict policy violations prevent running this command:")+`
162
+ `);for(let I of T)process.stderr.write(s.red(` \u2022 ${I}`)+`
163
+ `);process.exit(1);}}let M=await d(process.argv.slice(2),{cwd:e});process.exit(M);}}}catch{}let y=Go(e),v=a(),p=i(e),g=null;for(let P of p)if(await x__default.pathExists(P)){if(await t(P)){a$1.warn(`Skipping legacy/broken Windows launcher candidate: ${P}. Falling back to core bridge.`);continue}g=P;break}let A=r==="create";if(r==="init"&&c&&!m)return false;if(r&&Eo.includes(r)){let P=await To(e);if(P.length>0){process.stderr.write(s.red("\u274C Strict policy violations prevent running this command:")+`
164
+ `);for(let S of P)process.stderr.write(s.red(` \u2022 ${S}`)+`
165
+ `);process.exit(1);}}if(g&&r&&So.includes(r)&&!A&&!n&&!u){a$1.debug(`Delegating to local CLI: ${g} ${o.join(" ")}`);let P=r==="init"?_o():process.env,S=spawn(g,o,{stdio:"inherit",cwd:e,shell:v,env:P});return S.on("close",b=>{process.exit(b??0);}),S.on("error",b=>{a$1.error(`Failed to run local rapidkit: ${b.message}`),process.exit(1);}),true}if(y&&await x__default.pathExists(y))try{if((await x__default.readJson(y)).engine==="pip"){let S=o[0],b=i(e),_=null;for(let M of b)if(await x__default.pathExists(M)){if(await t(M)){a$1.warn(`Skipping legacy/broken Windows launcher candidate: ${M}. Falling back to core bridge.`);continue}_=M;break}if(_&&S&&So.includes(S)&&S!=="init"&&!u){a$1.debug(`Delegating to local CLI (early detection): ${_} ${o.join(" ")}`);let M=S==="init"?_o():process.env,T=spawn(_,o,{stdio:"inherit",cwd:e,env:M});return T.on("close",I=>process.exit(I??0)),T.on("error",I=>{a$1.error(`Failed to run local rapidkit: ${I.message}`),process.exit(1);}),true}if(S==="shell"&&o[1]==="activate"){let M=a()?`# RapidKit: activation snippet (PowerShell)
3567
166
  $venv = ".venv"
3568
167
  if (Test-Path "$venv\\Scripts\\Activate.ps1") { . "$venv\\Scripts\\Activate.ps1" }
3569
168
  $env:RAPIDKIT_PROJECT_ROOT = (Get-Location).Path
@@ -3581,14 +180,14 @@ elif [ -f "$VENV/bin/activate.fish" ]; then
3581
180
  fi
3582
181
  export RAPIDKIT_PROJECT_ROOT="$(pwd)"
3583
182
  export PATH="$(pwd)/.rapidkit:$(pwd):$PATH"
3584
- `;console.log(l.green.bold(`
183
+ `;console.log(s.green.bold(`
3585
184
  \u2705 Activation snippet \u2014 run the following to activate this project in your current shell:
3586
- `)),console.log(G),console.log(l.gray(`
185
+ `)),console.log(M),console.log(s.gray(`
3587
186
  \u{1F4A1} After activation you can run: rapidkit dev
3588
- `)),process.exit(0);}if(!a$2&&!Ft(T)&&T!=="init"&&!u){let G=await d$1(t,{cwd:o});process.exit(G);}}}catch{}return false}var $e=null,Lt=false,ye=new Command,Ms=process.env.RAPIDKIT_SHOW_LEGACY==="1"||process.env.RAPIDKIT_SHOW_LEGACY?.toLowerCase()==="true";async function Ls(e){if(e.length===0)return false;let o=e[0],t=e[1];if(ks.includes(o))return false;if(xr.includes(o))return true;if(Ft(o)||o==="shell"&&t==="activate")return false;if(e.includes("--tui"))return true;if(o==="--help"||o==="-h"||o==="help"||o==="--version"||o==="-V"||e.includes("--template")||e.includes("-t"))return false;let r=new Set(["--yes","-y","--skip-git","--skip-install","--debug","--dry-run","--no-update-check","--create-workspace","--no-workspace"]);if(e.some(n=>r.has(n)))return false;let i=await h$3();return i?i.has(o):!!(a$3.has(o)||e.length>1)}ye.name("rapidkit").description("Create RapidKit workspaces and projects").version(c());var qs=a()?"npx rapidkit init; npx rapidkit dev":"npx rapidkit init && npx rapidkit dev";ye.addHelpText("beforeAll",`RapidKit NPM CLI
187
+ `)),process.exit(0);}if(!a$2&&!wt(S)&&S!=="init"&&!u){let M=await d(o,{cwd:e});process.exit(M);}}}catch{}return false}var Pe=null,yt=false,ce=new Command,un=process.env.RAPIDKIT_SHOW_LEGACY==="1"||process.env.RAPIDKIT_SHOW_LEGACY?.toLowerCase()==="true";async function mn(t){if(t.length===0)return false;let e=t[0],o=t[1];if(Vr.includes(e))return false;if(Lo.includes(e))return true;if(wt(e)||e==="shell"&&o==="activate")return false;if(t.includes("--tui"))return true;if(e==="--help"||e==="-h"||e==="help"||e==="--version"||e==="-V"||t.includes("--template")||t.includes("-t"))return false;let r=new Set(["--yes","-y","--skip-git","--skip-install","--debug","--dry-run","--no-update-check","--create-workspace","--no-workspace"]);if(t.some(i=>r.has(i)))return false;let n=await h$1();return n?n.has(e):!!(a$4.has(e)||t.length>1)}ce.name("rapidkit").description("Create RapidKit workspaces and projects").version(b());var fn=a()?"npx rapidkit init; npx rapidkit dev":"npx rapidkit init && npx rapidkit dev";ce.addHelpText("beforeAll",`RapidKit NPM CLI
3589
188
 
3590
189
  Create workspaces, scaffold projects, and manage your development toolchain.
3591
- `);ye.addHelpText("afterAll",`
190
+ `);ce.addHelpText("afterAll",`
3592
191
  Workspace Setup Commands
3593
192
  rapidkit bootstrap Bootstrap projects in workspace (--profile python-only|node-only|go-only|polyglot|enterprise)
3594
193
  rapidkit setup <runtime> Set up runtime toolchain (runtime: python | node | go)
@@ -3607,42 +206,42 @@ Quick start:
3607
206
  npx rapidkit my-workspace # Create + bootstrap workspace
3608
207
  cd my-workspace
3609
208
  npx rapidkit create project # Interactive kit picker
3610
- ${qs} # Install deps + run
209
+ ${fn} # Install deps + run
3611
210
 
3612
211
  Notes:
3613
212
  --skip-install (npm wrapper) enables fast-path for lock/dependency steps.
3614
213
  It is different from core --skip-essentials (essential module installation).
3615
214
 
3616
215
  Use "rapidkit help <command>" for more information.
3617
- `);ye.argument("[name]","Name of the workspace or project directory").addOption(new Option("-t, --template <template>","Legacy: create a project with template (fastapi, nestjs) instead of a workspace").hideHelp()).option("-y, --yes","Skip prompts and use defaults").option("--author <name>","Author/team name for workspace metadata").addOption(new Option("--skip-git","Skip git initialization").hideHelp()).addOption(new Option("--skip-install","Legacy: skip installing dependencies (template mode)").hideHelp()).option("--debug","Enable debug logging").addOption(new Option("--dry-run","Show what would be created without creating it").hideHelp()).addOption(new Option("--install-method <method>","Installation method: poetry, venv, or pipx").choices(["poetry","venv","pipx"]).hideHelp()).addOption(new Option("--profile <profile>","Workspace bootstrap profile: minimal, python-only, node-only, go-only, polyglot, enterprise").choices(["minimal","python-only","node-only","go-only","polyglot","enterprise"]).hideHelp()).addOption(new Option("--create-workspace","When creating a project outside a workspace: create and register a workspace in the current directory").hideHelp()).addOption(new Option("--no-workspace","When creating a project outside a workspace: do not create a workspace").hideHelp()).option("--no-update-check","Skip checking for updates").action(async(e$1,o)=>{try{o.debug&&(a$1.setDebug(true),a$1.debug("Debug mode enabled"));let t=await a$2();a$1.debug("User config loaded",t);let r=await b$1();a$1.debug("RapidKit config loaded",r);let i$1=c$1(t,r,{author:o.author,pythonVersion:void 0,skipGit:o.skipGit});a$1.debug("Merged config",i$1),o.updateCheck!==false&&await b$2(),console.log(l.blue.bold(`
216
+ `);ce.argument("[name]","Name of the workspace or project directory").addOption(new Option("-t, --template <template>","Legacy: create a project with template (fastapi, nestjs) instead of a workspace").hideHelp()).option("-y, --yes","Skip prompts and use defaults").option("--author <name>","Author/team name for workspace metadata").addOption(new Option("--skip-git","Skip git initialization").hideHelp()).addOption(new Option("--skip-install","Legacy: skip installing dependencies (template mode)").hideHelp()).option("--debug","Enable debug logging").addOption(new Option("--dry-run","Show what would be created without creating it").hideHelp()).addOption(new Option("--install-method <method>","Installation method: poetry, venv, or pipx").choices(["poetry","venv","pipx"]).hideHelp()).addOption(new Option("--profile <profile>","Workspace bootstrap profile: minimal, python-only, node-only, go-only, polyglot, enterprise").choices(["minimal","python-only","node-only","go-only","polyglot","enterprise"]).hideHelp()).addOption(new Option("--create-workspace","When creating a project outside a workspace: create and register a workspace in the current directory").hideHelp()).addOption(new Option("--no-workspace","When creating a project outside a workspace: do not create a workspace").hideHelp()).option("--no-update-check","Skip checking for updates").action(async(t,e$2)=>{try{e$2.debug&&(a$1.setDebug(true),a$1.debug("Debug mode enabled"));let o=await a$2();a$1.debug("User config loaded",o);let r=await b$1();a$1.debug("RapidKit config loaded",r);let n=c(o,r,{author:e$2.author,pythonVersion:void 0,skipGit:e$2.skipGit});a$1.debug("Merged config",n),e$2.updateCheck!==false&&await a$3(),console.log(s.blue.bold(`
3618
217
  \u{1F680} Welcome to RapidKit NPM CLI!
3619
- `)),e$1||($r(),process.exit(0));try{Wt(e$1);}catch(s){throw s instanceof d&&(a$1.error(`
3620
- \u274C ${s.message}`),s.details&&a$1.warn(`\u{1F4A1} ${s.details}
3621
- `),process.exit(1)),s}let n=h.resolve(process.cwd(),e$1);$e=n,await b.pathExists(n)&&(a$1.error(`
3622
- \u274C Directory "${e$1}" already exists`),console.log(l.cyan(`
218
+ `)),t||(Bo(),process.exit(0));try{vt(t);}catch(c){throw c instanceof e&&(a$1.error(`
219
+ \u274C ${c.message}`),c.details&&a$1.warn(`\u{1F4A1} ${c.details}
220
+ `),process.exit(1)),c}let i=f.resolve(process.cwd(),t);Pe=i,await x__default.pathExists(i)&&(a$1.error(`
221
+ \u274C Directory "${t}" already exists`),console.log(s.cyan(`
3623
222
  \u{1F4A1} Choose a different name or delete the existing directory.
3624
- `)),process.exit(1));let a=!!o.template;if(o.dryRun){console.log(l.cyan(`
223
+ `)),process.exit(1));let a=!!e$2.template;if(e$2.dryRun){console.log(s.cyan(`
3625
224
  \u{1F50D} Dry-run mode - showing what would be created:
3626
- `)),console.log(l.white("\u{1F4C2} Path:"),n),console.log(l.white("\u{1F4E6} Type:"),a?`Project (${o.template})`:"Workspace"),console.log();return}if(!o.yes&&!a?await xe.prompt([{type:"input",name:"author",message:"Author name:",default:process.env.USER||"RapidKit User"}]):o.yes&&console.log(l.gray(`Using default values (--yes flag)
3627
- `)),a){let s=String(o.template||"").trim(),c=s.toLowerCase(),p=c==="fastapi"?"fastapi.standard":c==="nestjs"?"nestjs.standard":c==="go"||c==="fiber"?"gofiber.standard":c==="gin"?"gogin.standard":s;if(qt(p)){let T=h.resolve(process.cwd(),e$1);await Vt(T,{project_name:e$1,module_path:e$1,skipGit:o.skipGit});return}if(lt(p)){let T=h.resolve(process.cwd(),e$1);await Bt(T,{project_name:e$1,module_path:e$1,skipGit:o.skipGit});return}if(!!!so(process.cwd())){if(o.createWorkspace)await i(process.cwd(),{skipGit:o.skipGit,yes:o.yes,userConfig:t});else if(!o.noWorkspace)if(o.yes)await i(process.cwd(),{skipGit:o.skipGit,yes:true,userConfig:t});else {let{createWs:T}=await xe.prompt([{type:"confirm",name:"createWs",message:"This project will be created outside a RapidKit workspace. Create and register a workspace here?",default:true}]);T&&await i(process.cwd(),{skipGit:o.skipGit,yes:false,userConfig:t});}}let u=["create","project",p,e$1,"--output",process.cwd()];o.yes&&u.push("--yes");let m=Y(process.cwd()),w=!!o.skipInstall,f=w||!!m;w&&u.push("--skip-essentials");let g=f?{...process.env,RAPIDKIT_SKIP_LOCKS:"1",RAPIDKIT_GENERATE_LOCKS:"0"}:void 0,R=await e(u,{cwd:process.cwd(),env:g});R!==0&&process.exit(R),m&&!o.skipInstall&&(console.log(l.gray("\u2139\uFE0F Fast create mode (workspace): dependencies were deferred.")),console.log(l.white(" Next: cd <project-name> && npx rapidkit init")));let x=so(process.cwd());if(x){let T=h.dirname(x),C=h.join(T,".python-version"),E=h.join(n,".python-version");try{if(await b.pathExists(C)){let G=_.readFileSync(C,"utf-8");_.writeFileSync(E,G.trim()+`
3628
- `),a$1.debug(`Synced Python version ${G.trim()} from workspace to project`);}}catch(G){a$1.debug("Could not sync Python version from workspace:",G);}}if(!o.skipInstall){let T=await d$1(["init",n],{cwd:process.cwd()});if(T!==0&&process.exit(T),x){let C=h.dirname(x),E=h.join(C,".python-version"),G=h.join(n,".python-version");try{if(await b.pathExists(E)){let N=_.readFileSync(E,"utf-8");_.writeFileSync(G,N.trim()+`
3629
- `),a$1.debug(`Re-synced Python version ${N.trim()} after init`);}}catch(N){a$1.debug("Could not re-sync Python version after init:",N);}}}}else await h$1(e$1,{skipGit:o.skipGit,dryRun:o.dryRun,yes:o.yes,userConfig:i$1,installMethod:o.installMethod,profile:o.profile});}catch(t){t instanceof d?(a$1.error(`
3630
- \u274C ${t.message}`),t.details&&a$1.warn(`\u{1F4A1} ${t.details}`),a$1.debug("Error code:",t.code)):(a$1.error(`
3631
- \u274C An unexpected error occurred:`),console.error(t)),process.exit(1);}finally{$e=null;}});er(ye);$o(ye);ye.command("shell <action>").description("Shell helpers (activate virtualenv in current shell)").action(async e=>{e!=="activate"&&(console.log(l.red(`Unknown shell command: ${e}`)),process.exit(1));let o=process.cwd();function t(c){let p=c;for(;;){let d=h.join(p,".rapidkit","context.json");if(_.existsSync(d))return d;let u=h.dirname(p);if(u===p)break;p=u;}return null}let r=t(o);function i(c){let p=c;for(;;){let d=h.join(p,".venv"),u=h.join(p,".rapidkit","activate");if(_.existsSync(u)||_.existsSync(d))return {venv:d,activateFile:u};let m=h.dirname(p);if(m===p)break;p=m;}return null}let n=i(o);!r&&!n&&(console.log(l.yellow("No RapidKit project found in this directory")),process.exit(1));let a$1;n&&_.existsSync(n.activateFile)?a$1=n.activateFile:n&&_.existsSync(n.venv)?a$1=h$2(n.venv):(console.log(l.yellow("No virtual environment found")),process.exit(1));let s=a();console.log(s?`call "${a$1}"`:`. "${a$1}"`);});ye.command("doctor [scope]").description("\u{1FA7A} Check RapidKit system health by default; use workspace for full workspace checks").option("--workspace","Check entire workspace (including all projects)").option("--json","Output results in JSON format (for CI/CD pipelines)").option("--fix","Automatically fix common issues (with confirmation)").action(async(e,o)=>{e&&e!=="workspace"&&(console.log(l.red(`Unknown doctor scope: ${e}`)),console.log(l.gray("Available: workspace")),console.log(l.gray("Usage: npx rapidkit doctor or npx rapidkit doctor workspace")),process.exit(1));let t=await Rs({scope:e,workspaceFlag:o.workspace});t.detected&&!o.json&&(console.log(l.yellow("\u26A0\uFE0F Windows launcher shadow detected for doctor workspace checks.")),t.candidatePath&&console.log(l.gray(` Candidate: ${t.candidatePath}`)),console.log(l.gray(" Running npm-wrapper doctor workflow directly as safe fallback to avoid ambiguous rapidkit binary resolution."))),await Oo({...o,workspace:o.workspace||e==="workspace"});});ye.command("workspace <action> [subaction] [key] [value]").description("Manage RapidKit workspaces (list, sync, policy)").action(async(e,o,t,r)=>{if(e==="list"){let{listWorkspaces:i}=await import('./workspace-KRZ3DWL4.js');await i();}else if(e==="sync"){let i=Y(process.cwd());i||(console.log(l.red("\u274C Not inside a RapidKit workspace")),console.log(l.gray("\u{1F4A1} Run this command from within a workspace directory")),process.exit(1));let{syncWorkspaceProjects:n}=await import('./workspace-KRZ3DWL4.js');console.log(l.cyan(`\u{1F4C2} Scanning workspace: ${h.basename(i)}`)),await n(i);}else if(e==="policy"){let i=Y(process.cwd());i||(console.log(l.red("\u274C Not inside a RapidKit workspace")),console.log(l.gray("\u{1F4A1} Run this command from within a workspace directory")),process.exit(1));let n=await $s(i,o,t,r);n!==0&&process.exit(n);}else console.log(l.red(`Unknown workspace action: ${e}`)),console.log(l.gray("Available: list, sync, policy")),process.exit(1);});function $r(){let e=a()?"npx rapidkit init; npx rapidkit dev":"npx rapidkit init && npx rapidkit dev";console.log(l.white(`Usage:
3632
- `)),console.log(l.cyan(` npx rapidkit <workspace-name> [options]
3633
- `)),console.log(l.bold("Quick start \u2014 workspace workflow:")),console.log(l.cyan(" npx rapidkit my-workspace ")+l.gray("# Create workspace (interactive profile picker)")),console.log(l.cyan(" cd my-workspace")),console.log(l.cyan(" npx rapidkit bootstrap ")+l.gray("# Bootstrap all runtime toolchains")),console.log(l.cyan(" npx rapidkit create project ")+l.gray("# Interactive kit picker")),console.log(l.cyan(" cd my-api")),console.log(l.cyan(` ${e}
3634
- `)),console.log(l.bold("Workspace profiles (asked during creation):")),console.log(l.gray(" minimal Foundation files only \u2014 fastest bootstrap (default)")),console.log(l.gray(" python-only Python + Poetry (FastAPI, Django, ML)")),console.log(l.gray(" node-only Node.js runtime (NestJS, Express, Next.js)")),console.log(l.gray(" go-only Go runtime (Fiber, Gin, gRPC)")),console.log(l.gray(" polyglot Python + Node.js + Go multi-runtime")),console.log(l.gray(` enterprise Polyglot + governance + Sigstore
3635
- `)),console.log(l.bold("Workspace commands (inside a workspace):")),console.log(l.gray(" npx rapidkit bootstrap [--profile <p>] Re-bootstrap toolchains")),console.log(l.gray(" npx rapidkit workspace list List registered workspaces")),console.log(l.gray(" npx rapidkit workspace policy show Show effective workspace policies")),console.log(l.gray(" npx rapidkit workspace policy set <k> <v> Update workspace policy values")),console.log(l.gray(" npx rapidkit setup python|node|go [--warm-deps] Set up runtime (+ optional deps warm-up)")),console.log(l.gray(" npx rapidkit mirror [status|sync|verify|rotate] Registry mirror management")),console.log(l.gray(` npx rapidkit cache [status|clear|prune|repair] Package cache management
3636
- `)),console.log(l.bold("Options (workspace creation):")),console.log(l.gray(" -y, --yes Skip prompts and use defaults")),console.log(l.gray(" --author <name> Author/team name for workspace metadata")),console.log(l.gray(" --skip-git Skip git initialization")),console.log(l.gray(" --debug Enable debug logging")),console.log(l.gray(" --dry-run Show what would be created")),console.log(l.gray(" --create-workspace When creating a project outside a workspace: create and register a workspace in the current directory")),console.log(l.gray(" --no-workspace When creating a project outside a workspace: do not create a workspace")),console.log(l.gray(` --no-update-check Skip checking for updates
3637
- `)),console.log(l.bold("Project commands (inside a project):")),console.log(l.gray(" npx rapidkit create project Scaffold a new project")),console.log(l.gray(" cd my-api Change directory to the new project")),console.log(l.gray(" npx rapidkit init Install project dependencies")),console.log(l.gray(" npx rapidkit dev Start dev server")),console.log(l.gray(" npx rapidkit build Build for production")),console.log(l.gray(` npx rapidkit test Run tests
3638
- `)),console.log(l.bold("Flags clarification:")),console.log(l.gray(" --skip-install npm fast-path for lock/dependency steps")),console.log(l.gray(` --skip-essentials core flag for skipping essential module installation
3639
- `)),Ms?(console.log(l.bold("Legacy (shown because RAPIDKIT_SHOW_LEGACY=1):")),console.log(l.gray(" npx rapidkit my-project --template fastapi")),console.log(l.gray(" npx rapidkit my-project --template nestjs")),console.log(l.gray(` --skip-install Fast-path lock/deps (legacy template mode) \u2014 not same as --skip-essentials
3640
- `))):console.log(l.gray(`Tip: set RAPIDKIT_SHOW_LEGACY=1 to show legacy template flags in help.
3641
- `));}var br="__rapidkit_signal_handlers_registered__",Rr=globalThis;Rr[br]||(Rr[br]=true,process.on("SIGINT",async()=>{if(!Lt){if(Lt=true,console.log(l.yellow(`
3642
-
3643
- \u26A0\uFE0F Interrupted by user`)),$e&&await b.pathExists($e)){console.log(l.gray("Cleaning up partial installation..."));try{await b.remove($e),console.log(l.green("\u2713 Cleanup complete"));}catch(e){a$1.debug("Cleanup failed:",e);}}process.exit(130);}}),process.on("SIGTERM",async()=>{if(!Lt){if(Lt=true,a$1.debug("Received SIGTERM"),$e&&await b.pathExists($e))try{await b.remove($e);}catch(e){a$1.debug("Cleanup failed:",e);}process.exit(143);}}));var Fs=process.env.VITEST==="true"||process.env.VITEST==="1"||process.env.NODE_ENV==="test",Hs=(()=>{let e=process.argv[1];if(!e)return false;try{return _.realpathSync(e)===_.realpathSync(fileURLToPath(import.meta.url))}catch{return h.resolve(e)===h.resolve(fileURLToPath(import.meta.url))}})(),Ks=!Fs||Hs;if(Ks){let e=process.argv.slice(2),o=e[0],t=process.cwd(),r=Er(t),i=_.existsSync(h.join(t,".rapidkit","project.json")),n=hr(o),a=o==="init"&&r&&!i;(e.length===0||e.length===1&&(o==="--help"||o==="-h"||o==="help"))&&(console.log(l.blue.bold(`
225
+ `)),console.log(s.white("\u{1F4C2} Path:"),i),console.log(s.white("\u{1F4E6} Type:"),a?`Project (${e$2.template})`:"Workspace"),console.log();return}if(!e$2.yes&&!a?await he.prompt([{type:"input",name:"author",message:"Author name:",default:process.env.USER||"RapidKit User"}]):e$2.yes&&console.log(s.gray(`Using default values (--yes flag)
226
+ `)),a){let c=String(e$2.template||"").trim(),m=c.toLowerCase(),d$1=m==="fastapi"?"fastapi.standard":m==="nestjs"?"nestjs.standard":m==="go"||m==="fiber"?"gofiber.standard":m==="gin"?"gogin.standard":c;if(ht(d$1)){let S=f.resolve(process.cwd(),t),{generateGoFiberKit:b}=await import('./gofiber-standard-NQHMZTFK.js');await b(S,{project_name:t,module_path:t,skipGit:e$2.skipGit});return}if(Ue(d$1)){let S=f.resolve(process.cwd(),t),{generateGoGinKit:b}=await import('./gogin-standard-2G2C3VQL.js');await b(S,{project_name:t,module_path:t,skipGit:e$2.skipGit});return}if(!!!$t(process.cwd())){let{registerWorkspaceAtPath:S}=await import('./create-27NVMJAR.js');if(e$2.createWorkspace)await S(process.cwd(),{skipGit:e$2.skipGit,yes:e$2.yes,userConfig:o});else if(!e$2.noWorkspace)if(e$2.yes)await S(process.cwd(),{skipGit:e$2.skipGit,yes:true,userConfig:o});else {let{createWs:b}=await he.prompt([{type:"confirm",name:"createWs",message:"This project will be created outside a RapidKit workspace. Create and register a workspace here?",default:true}]);b&&await S(process.cwd(),{skipGit:e$2.skipGit,yes:false,userConfig:o});}}let u=["create","project",d$1,t,"--output",process.cwd()];e$2.yes&&u.push("--yes");let y=q(process.cwd()),v=!!e$2.skipInstall,p=v||!!y;v&&u.push("--skip-essentials");let g=p?{...process.env,RAPIDKIT_SKIP_LOCKS:"1",RAPIDKIT_GENERATE_LOCKS:"0"}:void 0,A=await e$1(u,{cwd:process.cwd(),env:g});A!==0&&process.exit(A),y&&!e$2.skipInstall&&(console.log(s.gray("\u2139\uFE0F Fast create mode (workspace): dependencies were deferred.")),console.log(s.white(" Next: cd <project-name> && npx rapidkit init")));let P=$t(process.cwd());if(P){let S=f.dirname(P),b=f.join(S,".python-version"),_=f.join(i,".python-version");try{if(await x__default.pathExists(b)){let M=w.readFileSync(b,"utf-8");w.writeFileSync(_,M.trim()+`
227
+ `),a$1.debug(`Synced Python version ${M.trim()} from workspace to project`);}}catch(M){a$1.debug("Could not sync Python version from workspace:",M);}}if(!e$2.skipInstall){let S=await d(["init",i],{cwd:process.cwd()});if(S!==0&&process.exit(S),P){let b=f.dirname(P),_=f.join(b,".python-version"),M=f.join(i,".python-version");try{if(await x__default.pathExists(_)){let T=w.readFileSync(_,"utf-8");w.writeFileSync(M,T.trim()+`
228
+ `),a$1.debug(`Re-synced Python version ${T.trim()} after init`);}}catch(T){a$1.debug("Could not re-sync Python version after init:",T);}}}}else {let{createProject:c}=await import('./create-27NVMJAR.js');await c(t,{skipGit:e$2.skipGit,dryRun:e$2.dryRun,yes:e$2.yes,userConfig:n,installMethod:e$2.installMethod,profile:e$2.profile});}}catch(o){o instanceof e?(a$1.error(`
229
+ \u274C ${o.message}`),o.details&&a$1.warn(`\u{1F4A1} ${o.details}`),a$1.debug("Error code:",o.code)):(a$1.error(`
230
+ \u274C An unexpected error occurred:`),console.error(o)),process.exit(1);}finally{Pe=null;}});mo(ce);Vt(ce);ce.command("shell <action>").description("Shell helpers (activate virtualenv in current shell)").action(async t=>{t!=="activate"&&(console.log(s.red(`Unknown shell command: ${t}`)),process.exit(1));let e=process.cwd();function o(m){let d=m;for(;;){let l=f.join(d,".rapidkit","context.json");if(w.existsSync(l))return l;let u=f.dirname(d);if(u===d)break;d=u;}return null}let r=o(e);function n(m){let d=m;for(;;){let l=f.join(d,".venv"),u=f.join(d,".rapidkit","activate");if(w.existsSync(u)||w.existsSync(l))return {venv:l,activateFile:u};let y=f.dirname(d);if(y===d)break;d=y;}return null}let i=n(e);!r&&!i&&(console.log(s.yellow("No RapidKit project found in this directory")),process.exit(1));let a$1;i&&w.existsSync(i.activateFile)?a$1=i.activateFile:i&&w.existsSync(i.venv)?a$1=h(i.venv):(console.log(s.yellow("No virtual environment found")),process.exit(1));let c=a();console.log(c?`call "${a$1}"`:`. "${a$1}"`);});ce.command("doctor [scope]").description("\u{1FA7A} Check RapidKit system health by default; use workspace for full workspace checks").option("--workspace","Check entire workspace (including all projects)").option("--json","Output results in JSON format (for CI/CD pipelines)").option("--fix","Automatically fix common issues (with confirmation)").action(async(t,e)=>{t&&t!=="workspace"&&(console.log(s.red(`Unknown doctor scope: ${t}`)),console.log(s.gray("Available: workspace")),console.log(s.gray("Usage: npx rapidkit doctor or npx rapidkit doctor workspace")),process.exit(1));let o=await Yr({scope:t,workspaceFlag:e.workspace});o.detected&&!e.json&&(console.log(s.yellow("\u26A0\uFE0F Local launcher shadow detected for doctor workspace checks.")),o.candidatePath&&console.log(s.gray(` Candidate: ${o.candidatePath}`)),console.log(s.gray(" Running npm-wrapper doctor workflow directly as safe fallback to avoid ambiguous rapidkit binary resolution.")));let{runDoctor:r}=await import('./doctor-P57TTWEP.js');await r({...e,workspace:e.workspace||t==="workspace"});});ce.command("workspace <action> [subaction] [key] [value]").description("Manage RapidKit workspaces (list, sync, policy)").action(async(t,e,o,r)=>{if(t==="list"){let{listWorkspaces:n}=await import('./workspace-VXNLNKCM.js');await n();}else if(t==="sync"){let n=q(process.cwd());n||(console.log(s.red("\u274C Not inside a RapidKit workspace")),console.log(s.gray("\u{1F4A1} Run this command from within a workspace directory")),process.exit(1));let{syncWorkspaceProjects:i}=await import('./workspace-VXNLNKCM.js');console.log(s.cyan(`\u{1F4C2} Scanning workspace: ${f.basename(n)}`)),await i(n);}else if(t==="policy"){let n=q(process.cwd());n||(console.log(s.red("\u274C Not inside a RapidKit workspace")),console.log(s.gray("\u{1F4A1} Run this command from within a workspace directory")),process.exit(1));let i=await ln(n,e,o,r);i!==0&&process.exit(i);}else console.log(s.red(`Unknown workspace action: ${t}`)),console.log(s.gray("Available: list, sync, policy")),process.exit(1);});function Bo(){let t=a()?"npx rapidkit init; npx rapidkit dev":"npx rapidkit init && npx rapidkit dev";console.log(s.white(`Usage:
231
+ `)),console.log(s.cyan(` npx rapidkit <workspace-name> [options]
232
+ `)),console.log(s.bold("Quick start \u2014 workspace workflow:")),console.log(s.cyan(" npx rapidkit my-workspace ")+s.gray("# Create workspace (interactive profile picker)")),console.log(s.cyan(" cd my-workspace")),console.log(s.cyan(" npx rapidkit bootstrap ")+s.gray("# Bootstrap all runtime toolchains")),console.log(s.cyan(" npx rapidkit create project ")+s.gray("# Interactive kit picker")),console.log(s.cyan(" cd my-api")),console.log(s.cyan(` ${t}
233
+ `)),console.log(s.bold("Workspace profiles (asked during creation):")),console.log(s.gray(" minimal Foundation files only \u2014 fastest bootstrap (default)")),console.log(s.gray(" python-only Python + Poetry (FastAPI, Django, ML)")),console.log(s.gray(" node-only Node.js runtime (NestJS, Express, Next.js)")),console.log(s.gray(" go-only Go runtime (Fiber, Gin, gRPC)")),console.log(s.gray(" polyglot Python + Node.js + Go multi-runtime")),console.log(s.gray(` enterprise Polyglot + governance + Sigstore
234
+ `)),console.log(s.bold("Workspace commands (inside a workspace):")),console.log(s.gray(" npx rapidkit bootstrap [--profile <p>] Re-bootstrap toolchains")),console.log(s.gray(" npx rapidkit workspace list List registered workspaces")),console.log(s.gray(" npx rapidkit workspace policy show Show effective workspace policies")),console.log(s.gray(" npx rapidkit workspace policy set <k> <v> Update workspace policy values")),console.log(s.gray(" npx rapidkit setup python|node|go [--warm-deps] Set up runtime (+ optional deps warm-up)")),console.log(s.gray(" npx rapidkit mirror [status|sync|verify|rotate] Registry mirror management")),console.log(s.gray(` npx rapidkit cache [status|clear|prune|repair] Package cache management
235
+ `)),console.log(s.bold("Options (workspace creation):")),console.log(s.gray(" -y, --yes Skip prompts and use defaults")),console.log(s.gray(" --author <name> Author/team name for workspace metadata")),console.log(s.gray(" --skip-git Skip git initialization")),console.log(s.gray(" --debug Enable debug logging")),console.log(s.gray(" --dry-run Show what would be created")),console.log(s.gray(" --create-workspace When creating a project outside a workspace: create and register a workspace in the current directory")),console.log(s.gray(" --no-workspace When creating a project outside a workspace: do not create a workspace")),console.log(s.gray(` --no-update-check Skip checking for updates
236
+ `)),console.log(s.bold("Project commands (inside a project):")),console.log(s.gray(" npx rapidkit create project Scaffold a new project")),console.log(s.gray(" cd my-api Change directory to the new project")),console.log(s.gray(" npx rapidkit init Install project dependencies")),console.log(s.gray(" npx rapidkit dev Start dev server")),console.log(s.gray(" npx rapidkit build Build for production")),console.log(s.gray(` npx rapidkit test Run tests
237
+ `)),console.log(s.bold("Flags clarification:")),console.log(s.gray(" --skip-install npm fast-path for lock/dependency steps")),console.log(s.gray(` --skip-essentials core flag for skipping essential module installation
238
+ `)),un?(console.log(s.bold("Legacy (shown because RAPIDKIT_SHOW_LEGACY=1):")),console.log(s.gray(" npx rapidkit my-project --template fastapi")),console.log(s.gray(" npx rapidkit my-project --template nestjs")),console.log(s.gray(` --skip-install Fast-path lock/deps (legacy template mode) \u2014 not same as --skip-essentials
239
+ `))):console.log(s.gray(`Tip: set RAPIDKIT_SHOW_LEGACY=1 to show legacy template flags in help.
240
+ `));}var Oo="__rapidkit_signal_handlers_registered__",Mo=globalThis;Mo[Oo]||(Mo[Oo]=true,process.on("SIGINT",async()=>{if(!yt){if(yt=true,console.log(s.yellow(`
241
+
242
+ \u26A0\uFE0F Interrupted by user`)),Pe&&await x__default.pathExists(Pe)){console.log(s.gray("Cleaning up partial installation..."));try{await x__default.remove(Pe),console.log(s.green("\u2713 Cleanup complete"));}catch(t){a$1.debug("Cleanup failed:",t);}}process.exit(130);}}),process.on("SIGTERM",async()=>{if(!yt){if(yt=true,a$1.debug("Received SIGTERM"),Pe&&await x__default.pathExists(Pe))try{await x__default.remove(Pe);}catch(t){a$1.debug("Cleanup failed:",t);}process.exit(143);}}));var gn=process.env.VITEST==="true"||process.env.VITEST==="1"||process.env.NODE_ENV==="test",yn=(()=>{let t=process.argv[1];if(!t)return false;try{return w.realpathSync(t)===w.realpathSync(fileURLToPath(import.meta.url))}catch{return f.resolve(t)===f.resolve(fileURLToPath(import.meta.url))}})(),hn=!gn||yn;if(hn){let t=process.argv.slice(2),e=t[0],o=process.cwd(),r=Wo(o),n=w.existsSync(f.join(o,".rapidkit","project.json")),i=Ao(e),a=e==="init"&&r&&!n;(t.length===0||t.length===1&&(e==="--help"||e==="-h"||e==="help"))&&(console.log(s.blue.bold(`
3644
243
  \u{1F680} Welcome to RapidKit NPM CLI!
3645
- `)),$r(),process.exit(0)),n?ye.parse():a?ao(e).then(c=>process.exit(c)).catch(c=>{process.stderr.write(`RapidKit (npm) failed to run workspace init: ${c?.message??c}
3646
- `),process.exit(1);}):Gs().then(async c=>{if(!c){let p=process.argv.slice(2);if(process.env.RAPIDKIT_NPM_DEBUG_ARGS==="1"&&process.stderr.write(`[rapidkit-npm] argv=${JSON.stringify(p)}
3647
- `),hr(p[0])){ye.parse();return}if(p[0]==="create"){let u=await Sr(p);process.exit(u);}if(p[0]==="init"){let u=await ao(p);process.exit(u);}if(bs(p[0])){if(p[0]==="bootstrap"){let m=await As(p);process.exit(m);}if(p[0]==="setup"){let m=await Os(p);process.exit(m);}if(p[0]==="cache"){let m=await js(p);process.exit(m);}let u=await Ds(p);process.exit(u);}if(Pr.includes(p[0])){let u=p[0],m=Se(process.cwd()),w=Y(process.cwd());if(w){let g=h.join(w,".rapidkit","policies.yml");if(await b.pathExists(g))try{let R=await _.promises.readFile(g,"utf-8");if(((R.match(/^\s*enforcement_mode:\s*(warn|strict)\s*(?:#.*)?$/m)??R.match(/^\s*mode:\s*(warn|strict)\s*(?:#.*)?$/m))?.[1]??"warn")==="strict"){let C=h.join(w,".rapidkit","toolchain.lock"),E=[];if(!await b.pathExists(C))E.push("toolchain.lock is missing \u2014 run `rapidkit bootstrap` first (strict mode requires a reproducible toolchain).");else try{let $=JSON.parse(await _.promises.readFile(C,"utf-8")).runtime??{};pe(m,process.cwd())&&!$.go?.version&&E.push("Go runtime version is not pinned in toolchain.lock \u2014 run `rapidkit setup go` first."),ue(m,process.cwd())&&!$.node?.version&&E.push("Node runtime version is not pinned in toolchain.lock \u2014 run `rapidkit setup node` first."),me(m,process.cwd())&&!$.python?.version&&E.push("Python runtime version is not pinned in toolchain.lock \u2014 run `rapidkit setup python` first.");}catch{}let G=h.join(w,".rapidkit","workspace.json");if(await b.pathExists(G))try{let $=JSON.parse(await _.promises.readFile(G,"utf-8")).profile??"";$==="python-only"&&(pe(m,process.cwd())||ue(m,process.cwd()))&&E.push('Workspace profile is "python-only" but this project is not Python. Update the workspace profile or use a polyglot workspace.'),$==="node-only"&&(pe(m,process.cwd())||me(m,process.cwd()))&&E.push('Workspace profile is "node-only" but this project is not Node. Update the workspace profile or use a polyglot workspace.'),$==="go-only"&&(me(m,process.cwd())||ue(m,process.cwd()))&&E.push('Workspace profile is "go-only" but this project is not Go. Update the workspace profile or use a polyglot workspace.');}catch{}if(E.length>0){console.log(l.red(`\u274C Strict policy violations block \`${u}\`:`));for(let N of E)console.log(l.red(` \u2022 ${N}`));console.log(l.gray("\u{1F4A1} Fix violations or switch to warn mode: set mode: warn in .rapidkit/policies.yml")),process.exit(1);}}}catch{}}let f=await Nr(process.cwd(),async()=>{if(pe(m,process.cwd())){let g=He("go",{runCommandInCwd:W,runCoreRapidkit:d$1}),R=u==="dev"?await g.runDev(process.cwd()):u==="test"?await g.runTest(process.cwd()):u==="build"?await g.runBuild(process.cwd()):await g.runStart(process.cwd());return R.message&&console.log(l.red(`\u274C ${R.message}`)),R.exitCode}if(ue(m,process.cwd()))return u==="dev"?await dt("dev",process.cwd()):u==="test"?await dt("test",process.cwd()):u==="build"?await dt("build",process.cwd()):await dt("start",process.cwd());if(me(m,process.cwd())){let g=He("python",{runCommandInCwd:W,runCoreRapidkit:d$1});return u==="dev"?(await g.runDev(process.cwd())).exitCode:u==="test"?(await g.runTest(process.cwd())).exitCode:u==="build"?(await g.runBuild(process.cwd())).exitCode:(await g.runStart(process.cwd())).exitCode}return -1});f.ok||process.exit(f.code),f.value>=0&&process.exit(f.value);}if(p[0]==="add"||p[0]==="module"&&p[1]==="add"){let u=Se(process.cwd());(u?.runtime==="go"||u?.module_support===false)&&(console.error(l.red("\u274C RapidKit modules are not available for Go projects.")),console.error(l.gray(" The module system requires Python and is only supported for FastAPI and NestJS projects.")),process.exit(1));}let d=await Ls(p);if(process.env.RAPIDKIT_NPM_DEBUG_ARGS==="1"&&process.stderr.write(`[rapidkit-npm] shouldForwardToCore=${d}
3648
- `),d){let u=await d$1(p,{cwd:process.cwd()});process.exit(u);}ye.parse();}});}export{ys as NPM_ONLY_TOP_LEVEL_COMMANDS,ks as WRAPPER_ORCHESTRATED_PROJECT_COMMANDS,Rs as detectWindowsDoctorWorkspaceShadow,As as handleBootstrapCommand,js as handleCacheCommand,Sr as handleCreateOrFallback,ao as handleInitCommand,Ds as handleMirrorCommand,Os as handleSetupCommand,Ls as shouldForwardToCore};
244
+ `)),Bo(),process.exit(0)),i?ce.parse():a?Tt(t).then(m=>process.exit(m)).catch(m=>{process.stderr.write(`RapidKit (npm) failed to run workspace init: ${m?.message??m}
245
+ `),process.exit(1);}):pn().then(async m=>{if(!m){let d$1=process.argv.slice(2);if(process.env.RAPIDKIT_NPM_DEBUG_ARGS==="1"&&process.stderr.write(`[rapidkit-npm] argv=${JSON.stringify(d$1)}
246
+ `),Ao(d$1[0])){ce.parse();return}if(d$1[0]==="create"){let u=await Ko(d$1);process.exit(u);}if(d$1[0]==="init"){let u=await Tt(d$1);process.exit(u);}if(Br(d$1[0])){if(d$1[0]==="bootstrap"){let y=await nn(d$1);process.exit(y);}if(d$1[0]==="setup"){let y=await sn(d$1);process.exit(y);}if(d$1[0]==="cache"){let y=await cn(d$1);process.exit(y);}let u=await dn(d$1);process.exit(u);}if(Fo.includes(d$1[0])){let u=d$1[0],y=ye(process.cwd()),v=q(process.cwd());if(v){let g=f.join(v,".rapidkit","policies.yml");if(await x__default.pathExists(g))try{let A=await w.promises.readFile(g,"utf-8");if(((A.match(/^\s*enforcement_mode:\s*(warn|strict)\s*(?:#.*)?$/m)??A.match(/^\s*mode:\s*(warn|strict)\s*(?:#.*)?$/m))?.[1]??"warn")==="strict"){let b=f.join(v,".rapidkit","toolchain.lock"),_=[];if(!await x__default.pathExists(b))_.push("toolchain.lock is missing \u2014 run `rapidkit bootstrap` first (strict mode requires a reproducible toolchain).");else try{let I=JSON.parse(await w.promises.readFile(b,"utf-8")).runtime??{};ne(y,process.cwd())&&!I.go?.version&&_.push("Go runtime version is not pinned in toolchain.lock \u2014 run `rapidkit setup go` first."),ie(y,process.cwd())&&!I.node?.version&&_.push("Node runtime version is not pinned in toolchain.lock \u2014 run `rapidkit setup node` first."),se(y,process.cwd())&&!I.python?.version&&_.push("Python runtime version is not pinned in toolchain.lock \u2014 run `rapidkit setup python` first.");}catch{}let M=f.join(v,".rapidkit","workspace.json");if(await x__default.pathExists(M))try{let I=JSON.parse(await w.promises.readFile(M,"utf-8")).profile??"";I==="python-only"&&(ne(y,process.cwd())||ie(y,process.cwd()))&&_.push('Workspace profile is "python-only" but this project is not Python. Update the workspace profile or use a polyglot workspace.'),I==="node-only"&&(ne(y,process.cwd())||se(y,process.cwd()))&&_.push('Workspace profile is "node-only" but this project is not Node. Update the workspace profile or use a polyglot workspace.'),I==="go-only"&&(se(y,process.cwd())||ie(y,process.cwd()))&&_.push('Workspace profile is "go-only" but this project is not Go. Update the workspace profile or use a polyglot workspace.');}catch{}if(_.length>0){console.log(s.red(`\u274C Strict policy violations block \`${u}\`:`));for(let T of _)console.log(s.red(` \u2022 ${T}`));console.log(s.gray("\u{1F4A1} Fix violations or switch to warn mode: set mode: warn in .rapidkit/policies.yml")),process.exit(1);}}}catch{}}let p=await Jo(process.cwd(),async()=>{if(ne(y,process.cwd())){let g=Se("go",{runCommandInCwd:F,runCoreRapidkit:d}),A=u==="dev"?await g.runDev(process.cwd()):u==="test"?await g.runTest(process.cwd()):u==="build"?await g.runBuild(process.cwd()):await g.runStart(process.cwd());return A.message&&console.log(s.red(`\u274C ${A.message}`)),A.exitCode}if(ie(y,process.cwd()))return u==="dev"?await He("dev",process.cwd()):u==="test"?await He("test",process.cwd()):u==="build"?await He("build",process.cwd()):await He("start",process.cwd());if(se(y,process.cwd())){let g=Se("python",{runCommandInCwd:F,runCoreRapidkit:d});return u==="dev"?(await g.runDev(process.cwd())).exitCode:u==="test"?(await g.runTest(process.cwd())).exitCode:u==="build"?(await g.runBuild(process.cwd())).exitCode:(await g.runStart(process.cwd())).exitCode}return -1});p.ok||process.exit(p.code),p.value>=0&&process.exit(p.value);}if(d$1[0]==="add"||d$1[0]==="module"&&d$1[1]==="add"){let u=ye(process.cwd());(u?.runtime==="go"||u?.module_support===false)&&(console.error(s.red("\u274C RapidKit modules are not available for Go projects.")),console.error(s.gray(" The module system requires Python and is only supported for FastAPI and NestJS projects.")),process.exit(1));}let l=await mn(d$1);if(process.env.RAPIDKIT_NPM_DEBUG_ARGS==="1"&&process.stderr.write(`[rapidkit-npm] shouldForwardToCore=${l}
247
+ `),l){let u=await d(d$1,{cwd:process.cwd()});process.exit(u);}ce.parse();}});}export{Ur as NPM_ONLY_TOP_LEVEL_COMMANDS,Vr as WRAPPER_ORCHESTRATED_PROJECT_COMMANDS,Yr as detectWindowsDoctorWorkspaceShadow,nn as handleBootstrapCommand,cn as handleCacheCommand,Ko as handleCreateOrFallback,Tt as handleInitCommand,dn as handleMirrorCommand,sn as handleSetupCommand,mn as shouldForwardToCore};