create-seamless 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +109 -51
  2. package/dist/commands/bootstrapAdmin.js +96 -0
  3. package/dist/commands/bootstrapAdmin.js.map +1 -0
  4. package/dist/commands/check.js +92 -0
  5. package/dist/commands/check.js.map +1 -0
  6. package/dist/commands/help.js +103 -0
  7. package/dist/commands/help.js.map +1 -0
  8. package/dist/commands/init.js +73 -0
  9. package/dist/commands/init.js.map +1 -0
  10. package/dist/core/bootstrapSecret.js +31 -0
  11. package/dist/core/bootstrapSecret.js.map +1 -0
  12. package/dist/core/configure.js +25 -0
  13. package/dist/core/configure.js.map +1 -0
  14. package/dist/core/env.js +35 -0
  15. package/dist/core/env.js.map +1 -0
  16. package/dist/core/exec.js +17 -0
  17. package/dist/core/exec.js.map +1 -0
  18. package/dist/core/fetch.js +9 -0
  19. package/dist/core/fetch.js.map +1 -0
  20. package/dist/core/help.js +75 -0
  21. package/dist/core/help.js.map +1 -0
  22. package/dist/core/inspect.js +15 -0
  23. package/dist/core/inspect.js.map +1 -0
  24. package/dist/core/jwks.js +19 -0
  25. package/dist/core/jwks.js.map +1 -0
  26. package/dist/core/output.js +94 -0
  27. package/dist/core/output.js.map +1 -0
  28. package/dist/core/packageManager.js +10 -0
  29. package/dist/core/packageManager.js.map +1 -0
  30. package/dist/core/paths.js +7 -0
  31. package/dist/core/paths.js.map +1 -0
  32. package/dist/core/secrets.js +8 -0
  33. package/dist/core/secrets.js.map +1 -0
  34. package/dist/generators/auth/auth.js +47 -0
  35. package/dist/generators/auth/auth.js.map +1 -0
  36. package/dist/generators/backend/express.js +13 -0
  37. package/dist/generators/backend/express.js.map +1 -0
  38. package/dist/generators/config/config.js +43 -0
  39. package/dist/generators/config/config.js.map +1 -0
  40. package/dist/generators/docker/docker.js +263 -0
  41. package/dist/generators/docker/docker.js.map +1 -0
  42. package/dist/generators/frontend/react.js +13 -0
  43. package/dist/generators/frontend/react.js.map +1 -0
  44. package/dist/index.js +43 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/prompts/projectSetup.js +73 -0
  47. package/dist/prompts/projectSetup.js.map +1 -0
  48. package/dist/utils/repoUtils.js +23 -0
  49. package/dist/utils/repoUtils.js.map +1 -0
  50. package/dist/utils/writeEnv.js +9 -0
  51. package/dist/utils/writeEnv.js.map +1 -0
  52. package/package.json +7 -5
package/README.md CHANGED
@@ -3,85 +3,142 @@
3
3
  [![License: AGPL-3.0-only](https://img.shields.io/badge/License-AGPL3-yellow.svg)](LICENSE)
4
4
  [![npm version](https://img.shields.io/npm/v/create-seamless.svg?style=flat)](https://www.npmjs.com/package/create-seamless)
5
5
 
6
- `create-seamless` is a project scaffolding tool for building applications with **Seamless Auth**, an open source, passwordless authentication system.
6
+ `create-seamless` is a CLI for bootstrapping applications with Seamless Auth, an open source, passwordless authentication system.
7
7
 
8
- It provisions a local, production-shaped development environment that can include:
8
+ It guides you through creating a fully working authentication stack with a web app, API, and auth server that are already connected and ready to run.
9
9
 
10
- - The Seamless Auth open source server
11
- - A starter web application
12
- - A starter API server
10
+ ---
11
+
12
+ ## Getting started
13
+
14
+ Run the CLI with `npx`:
15
+
16
+ ```bash
17
+ npx create-seamless my-app
18
+ ```
19
+
20
+ Or run it in your current directory:
21
+
22
+ ```bash
23
+ npx create-seamless
24
+ ```
13
25
 
14
- The generated project is fully local and requires no hosted services or external accounts to run.
26
+ You’ll be guided through a short setup process where you can choose:
27
+
28
+ - Whether to create a web application
29
+ - Whether to create an API server
30
+ - How to run the auth server (local or Docker)
31
+ - Whether to run everything with Docker
15
32
 
16
33
  ---
17
34
 
18
- ## What this creates
35
+ ## What gets created
19
36
 
20
- Depending on the flags provided, `create-seamless` scaffolds a local project with the following structure:
37
+ Depending on your selections, the CLI generates a project like this:
21
38
 
22
39
  ```text
23
40
  my-app/
24
- ├─ auth/ # Seamless Auth OSS server
25
- ├─ web/ # Starter web application (optional)
26
- ├─ api/ # Starter API server (optional)
41
+ ├─ auth/ # Seamless Auth server (optional)
42
+ ├─ web/ # React web application (optional)
43
+ ├─ api/ # Express API server (optional)
44
+ ├─ docker-compose.yml (optional)
27
45
  └─ README.md
28
46
  ```
29
47
 
30
- Each service is independently runnable and preconfigured to work together using local URLs and environment variables.
48
+ All services are preconfigured to work together.
31
49
 
32
- The intended development workflow is to run each service in its own terminal:
50
+ - Web calls the API
51
+ - API communicates with the auth server
52
+ - Auth manages sessions and tokens
33
53
 
34
- ```bash
35
- # terminal 1
36
- cd auth && npm run dev
54
+ No manual wiring is required.
55
+
56
+ ---
57
+
58
+ ## Running your project
37
59
 
38
- # terminal 2
39
- cd api && npm run dev
60
+ ### Option 1: Docker
40
61
 
41
- # terminal 3
42
- cd web && npm run dev
62
+ If you choose Docker during setup:
63
+
64
+ ```bash
65
+ docker compose up
43
66
  ```
44
67
 
68
+ This starts:
69
+
70
+ - PostgreSQL
71
+ - Auth server
72
+ - API server
73
+ - Web app
74
+
75
+ All services are configured to communicate correctly inside the container network.
76
+
45
77
  ---
46
78
 
47
- ## Usage
79
+ ### Option 2: Local development
80
+
81
+ If you choose to run locally:
82
+
83
+ #### 1. Start PostgreSQL
84
+
85
+ Make sure you have a local PostgreSQL instance running on port `5432`.
86
+
87
+ ---
48
88
 
49
- Run via `npx`:
89
+ #### 2. Start the auth server
50
90
 
51
91
  ```bash
52
- npx create-seamless my-app
92
+ cd auth
93
+ npm install
94
+
95
+ npm run db:create
96
+ npm run db:migrate
97
+
98
+ npm run dev
53
99
  ```
54
100
 
55
- By default, this scaffolds the full local stack:
101
+ ---
56
102
 
57
- - Seamless Auth server
58
- - Web application
59
- - API server
103
+ #### 3. Start the API
104
+
105
+ ```bash
106
+ cd api
107
+ npm install
108
+ npm run dev
109
+ ```
110
+
111
+ ---
112
+
113
+ #### 4. Start the web app
114
+
115
+ ```bash
116
+ cd web
117
+ npm install
118
+ npm run dev
119
+ ```
60
120
 
61
121
  ---
62
122
 
63
- ## CLI options
123
+ ## What is configured for you
124
+
125
+ create-seamless handles the parts that are usually difficult to get right:
64
126
 
65
- | Flag | Description |
66
- | ------------- | -------------------------------------------- |
67
- | `--auth` | Include the Seamless Auth open source server |
68
- | `--web` | Include the starter web application |
69
- | `--api` | Include the starter API server |
70
- | `--install` | Automatically install dependencies |
71
- | `--no-git` | Skip git initialization |
72
- | `--auth-port` | Auth server port (default: 3000) |
73
- | `--api-port` | API server port (default: 4000) |
74
- | `--web-port` | Web dev server port (default: 5173) |
127
+ - Shared API service tokens
128
+ - JWT signing configuration
129
+ - JWKS key generation for production mode
130
+ - Cross-service environment variables
131
+ - CORS and cookie-based session handling
75
132
 
76
- If no component flags are provided, all components are included.
133
+ Everything is aligned across services so the system works immediately after setup.
77
134
 
78
135
  ---
79
136
 
80
137
  ## Included projects
81
138
 
82
- `create-seamless` pulls directly from the following open source repositories:
139
+ create-seamless pulls from the following repositories:
83
140
 
84
- - Seamless Auth Server
141
+ - Seamless Auth API
85
142
  [https://github.com/fells-code/seamless-auth-api](https://github.com/fells-code/seamless-auth-api)
86
143
 
87
144
  - Seamless Auth React Starter
@@ -90,29 +147,29 @@ If no component flags are provided, all components are included.
90
147
  - Seamless Auth API Starter
91
148
  [https://github.com/fells-code/seamless-auth-starter-express](https://github.com/fells-code/seamless-auth-starter-express)
92
149
 
93
- Each repository can be used independently, but `create-seamless` wires them together for local development out of the box.
150
+ Each project can be used independently, but the CLI connects them into a working system.
94
151
 
95
152
  ---
96
153
 
97
154
  ## Documentation
98
155
 
99
- Full documentation for Seamless Auth, including architecture, configuration, and deployment guidance, is available at:
156
+ Full documentation is available at:
100
157
 
101
- [https://seamlessauth.com/docs](https://seamlessauth.com/docs)
158
+ [https://docs.seamlessauth.com](https://docs.seamlessauth.com)
102
159
 
103
160
  ---
104
161
 
105
162
  ## Philosophy
106
163
 
107
- Seamless Auth is designed around:
164
+ Seamless Auth is built around a few principles:
108
165
 
109
166
  - Passwordless authentication only
110
- - Embedded auth (no redirects or callbacks)
111
- - Self-hosted, open source foundations
167
+ - No redirects or third-party auth providers
168
+ - Self-hosted by default
112
169
  - Production-shaped local development
113
- - Minimal assumptions and explicit configuration
170
+ - Explicit configuration over hidden behavior
114
171
 
115
- `create-seamless` exists to make getting started with this model straightforward and repeatable.
172
+ create-seamless exists to make this setup fast and repeatable.
116
173
 
117
174
  ---
118
175
 
@@ -120,12 +177,13 @@ Seamless Auth is designed around:
120
177
 
121
178
  - Node.js 18 or newer
122
179
  - npm or pnpm
180
+ - Docker (optional)
123
181
 
124
182
  ---
125
183
 
126
184
  ## License
127
185
 
128
- **AGPL-3.0-only** © 2026 Fells Code LLC
186
+ AGPL-3.0-only © 2026 Fells Code LLC
129
187
 
130
188
  This license ensures:
131
189
 
@@ -133,4 +191,4 @@ This license ensures:
133
191
  - freedom to self-host and modify
134
192
  - sustainability of the managed service offering
135
193
 
136
- See [`LICENSE`](./LICENSE) for details.
194
+ See `LICENSE` for details.
@@ -0,0 +1,96 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { intro, outro, text, confirm, spinner } from "@clack/prompts";
4
+ import kleur from "kleur";
5
+ import { resolveBootstrapSecret } from "../core/bootstrapSecret.js";
6
+ function loadConfig() {
7
+ const configPath = path.join(process.cwd(), "seamless.config.json");
8
+ if (!fs.existsSync(configPath)) {
9
+ throw new Error("No seamless.config.json found. Run init first.");
10
+ }
11
+ return JSON.parse(fs.readFileSync(configPath, "utf-8"));
12
+ }
13
+ export async function runBootstrapAdmin(emailArg) {
14
+ intro("Seamless Auth Bootstrap");
15
+ const config = loadConfig();
16
+ let email = emailArg;
17
+ if (!email) {
18
+ email = (await text({
19
+ message: "Admin email address",
20
+ placeholder: "admin@example.com",
21
+ validate: (value) => {
22
+ if (!value || !value.includes("@")) {
23
+ return "Enter a valid email address";
24
+ }
25
+ },
26
+ }));
27
+ }
28
+ const proceed = await confirm({
29
+ message: "Create bootstrap admin invite?",
30
+ initialValue: true,
31
+ });
32
+ if (!proceed) {
33
+ outro("Cancelled.");
34
+ return;
35
+ }
36
+ let apiUrl = process.env.SEAMLESS_API_URL;
37
+ if (!apiUrl) {
38
+ apiUrl = "http://localhost:3000";
39
+ }
40
+ let secret = resolveBootstrapSecret();
41
+ if (secret) {
42
+ console.log(kleur.gray("Using bootstrap secret from local environment"));
43
+ }
44
+ else {
45
+ console.log("");
46
+ console.log(kleur.yellow("No bootstrap secret detected automatically."));
47
+ console.log("This may happen if the project is not initialized locally or running in production.");
48
+ secret = (await text({
49
+ message: "Bootstrap secret",
50
+ placeholder: "Enter your bootstrap secret",
51
+ validate: (value) => {
52
+ if (!value)
53
+ return "Bootstrap secret is required";
54
+ },
55
+ }));
56
+ }
57
+ const s = spinner();
58
+ s.start("Creating bootstrap invite...");
59
+ try {
60
+ const res = await fetch(`${apiUrl}/auth/internal/bootstrap/admin-invite`, {
61
+ method: "POST",
62
+ headers: {
63
+ "Content-Type": "application/json",
64
+ Authorization: `Bearer ${secret}`,
65
+ },
66
+ body: JSON.stringify({ email }),
67
+ });
68
+ const data = await res.json();
69
+ if (!res.ok) {
70
+ s.stop("Failed");
71
+ console.error(kleur.red("Error creating bootstrap invite"));
72
+ console.error(data);
73
+ process.exit(1);
74
+ }
75
+ s.stop("Done");
76
+ console.log("");
77
+ if (data?.data?.url) {
78
+ console.log(kleur.bold("Registration URL"));
79
+ console.log(kleur.cyan(data.data.url));
80
+ }
81
+ else {
82
+ console.log(kleur.green(`Invite sent to ${email}`));
83
+ }
84
+ console.log("");
85
+ console.log("Next step:");
86
+ console.log("The invited user must complete registration to receive admin access.");
87
+ outro("Bootstrap complete.");
88
+ }
89
+ catch (err) {
90
+ s.stop("Failed");
91
+ console.error(kleur.red("Unexpected error"));
92
+ console.error(err.message);
93
+ process.exit(1);
94
+ }
95
+ }
96
+ //# sourceMappingURL=bootstrapAdmin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrapAdmin.js","sourceRoot":"","sources":["../../src/commands/bootstrapAdmin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { execSync } from "child_process";
4
+ export async function runCheck() {
5
+ console.log("\nSeamless Check\n");
6
+ const root = process.cwd();
7
+ const configPath = path.join(root, "seamless.config.json");
8
+ if (!fs.existsSync(configPath)) {
9
+ console.log("✖ seamless.config.json not found");
10
+ console.log("→ Run: seamless init\n");
11
+ return;
12
+ }
13
+ console.log("✔ Config file found");
14
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
15
+ checkStructure(root, config);
16
+ checkDocker();
17
+ checkCompose(root, config);
18
+ checkContainers();
19
+ await checkHealth();
20
+ console.log("\nCheck complete.\n");
21
+ }
22
+ function checkStructure(root, config) {
23
+ const services = config.services;
24
+ if (fs.existsSync(path.join(root, services.web.path))) {
25
+ console.log("✔ Web project detected");
26
+ }
27
+ else {
28
+ console.log("✖ Web project missing");
29
+ }
30
+ if (fs.existsSync(path.join(root, services.api.path))) {
31
+ console.log("✔ API project detected");
32
+ }
33
+ else {
34
+ console.log("✖ API project missing");
35
+ }
36
+ }
37
+ function checkDocker() {
38
+ try {
39
+ execSync("docker --version", { stdio: "ignore" });
40
+ console.log("✔ Docker is installed");
41
+ }
42
+ catch {
43
+ console.log("✖ Docker not found");
44
+ console.log("→ Install Docker: https://docs.docker.com/get-docker/");
45
+ }
46
+ }
47
+ function checkCompose(root, config) {
48
+ const composePath = path.join(root, config.docker.composeFile);
49
+ if (fs.existsSync(composePath)) {
50
+ console.log("✔ Docker Compose file found");
51
+ }
52
+ else {
53
+ console.log("✖ docker-compose.yml missing");
54
+ }
55
+ }
56
+ function checkContainers() {
57
+ try {
58
+ const output = execSync("docker ps --format '{{.Names}}'").toString();
59
+ if (!output.includes("api")) {
60
+ console.log("✖ API container not running");
61
+ console.log("→ Run: docker compose up\n");
62
+ }
63
+ else {
64
+ console.log("✔ Containers running");
65
+ }
66
+ }
67
+ catch {
68
+ console.log("✖ Failed to check containers");
69
+ }
70
+ }
71
+ async function checkHealth() {
72
+ const checks = [
73
+ { name: "API", url: "http://localhost:3000/" },
74
+ { name: "Auth", url: "http://localhost:5312/health/status" },
75
+ { name: "Admin", url: "http://localhost:5174" },
76
+ ];
77
+ for (const check of checks) {
78
+ try {
79
+ const res = await fetch(check.url);
80
+ if (res.ok) {
81
+ console.log(`✔ ${check.name} is healthy`);
82
+ }
83
+ else {
84
+ console.log(`✖ ${check.name} returned ${res.status}`);
85
+ }
86
+ }
87
+ catch {
88
+ console.log(`✖ ${check.name} not reachable`);
89
+ }
90
+ }
91
+ }
92
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ import { VERSION } from "../index.js";
2
+ export function printHelp() {
3
+ console.log(`
4
+ create-seamless v${VERSION}
5
+
6
+ Seamless Auth CLI — scaffold and manage full-stack authentication systems.
7
+
8
+ ────────────────────────────────────────────
9
+
10
+ USAGE
11
+
12
+ seamless init [project-name]
13
+ seamless check
14
+ seamless bootstrap-admin [email]
15
+ seamless --help
16
+ seamless --version
17
+
18
+ ────────────────────────────────────────────
19
+
20
+ COMMANDS
21
+
22
+ init [project-name]
23
+ Scaffold a new Seamless Auth project
24
+
25
+ Without a name:
26
+ • Creates project in current directory
27
+
28
+ With a name:
29
+ • Creates new directory
30
+
31
+ check
32
+ Validate project setup, Docker, and running services
33
+
34
+ bootstrap-admin [email]
35
+ Create a bootstrap admin invite
36
+
37
+ Automatically resolves bootstrap secret from:
38
+ • .env
39
+ • auth/.env
40
+ • docker-compose.yml
41
+
42
+ If not found, you will be prompted.
43
+
44
+ Examples:
45
+ seamless bootstrap-admin
46
+ seamless bootstrap-admin admin@example.com
47
+
48
+ ────────────────────────────────────────────
49
+
50
+ BEHAVIOR
51
+
52
+ seamless <project-name>
53
+
54
+ • Shortcut for: seamless init <project-name>
55
+
56
+ ────────────────────────────────────────────
57
+
58
+ GETTING STARTED
59
+
60
+ 1. seamless init
61
+ 2. docker-compose up
62
+ 3. seamless bootstrap-admin
63
+
64
+ → Complete registration to become admin
65
+
66
+ ────────────────────────────────────────────
67
+
68
+ WHAT YOU GET
69
+
70
+ • Web application (React starter)
71
+ • API server (Express)
72
+ • SeamlessAuth server (Docker or local)
73
+ • Admin dashboard (Docker or source)
74
+ • Docker Compose setup
75
+
76
+ ────────────────────────────────────────────
77
+
78
+ EXAMPLES
79
+
80
+ seamless init
81
+ → Interactive setup in current directory
82
+
83
+ seamless init my-app
84
+ → Create new project in ./my-app
85
+
86
+ seamless my-app
87
+ → Shortcut for init
88
+
89
+ seamless check
90
+ → Validate your project
91
+
92
+ seamless bootstrap-admin
93
+ → Create your first admin user
94
+
95
+ ────────────────────────────────────────────
96
+
97
+ DOCS
98
+
99
+ https://docs.seamlessauth.com
100
+
101
+ `);
102
+ }
103
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/commands/help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,MAAM,UAAU,SAAS;IACvB,OAAO,CAAC,GAAG,CAAC;mBACK,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiGzB,CAAC,CAAC;AACH,CAAC"}
@@ -0,0 +1,73 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import { runProjectSetupPrompts } from "../prompts/projectSetup.js";
4
+ import { generateReactStarter } from "../generators/frontend/react.js";
5
+ import { generateExpressStarter } from "../generators/backend/express.js";
6
+ import { generateAuthServer } from "../generators/auth/auth.js";
7
+ import { configureApiEnv, configureWebEnv } from "../core/configure.js";
8
+ import { configureAuthLocalEnv, generateDockerCompose, } from "../generators/docker/docker.js";
9
+ import { printSuccessOutput } from "../core/output.js";
10
+ import { generateSeamlessConfig } from "../generators/config/config.js";
11
+ export async function runCLI(projectName) {
12
+ const cwd = process.cwd();
13
+ let root = cwd;
14
+ if (projectName) {
15
+ root = path.join(cwd, projectName);
16
+ if (fs.existsSync(root)) {
17
+ throw new Error(`Directory already exists: ${projectName}`);
18
+ }
19
+ fs.mkdirSync(root);
20
+ console.log(`Creating project in ${root}`);
21
+ }
22
+ const files = fs.readdirSync(root);
23
+ const isEmpty = files.length === 0;
24
+ if (!isEmpty) {
25
+ console.log("Existing project detected.");
26
+ console.log("Integration flow coming next.");
27
+ return;
28
+ }
29
+ const answers = await runProjectSetupPrompts();
30
+ if (answers.web && answers.webFramework === "react") {
31
+ await generateReactStarter({ root });
32
+ }
33
+ if (answers.api && answers.apiFramework === "express") {
34
+ await generateExpressStarter({ root });
35
+ }
36
+ let sharedConfig = {};
37
+ if (answers.authMode === "local") {
38
+ await generateAuthServer({ root }, "local");
39
+ sharedConfig = await configureAuthLocalEnv(root);
40
+ }
41
+ if (answers.useDocker) {
42
+ const dockerShared = await generateDockerCompose(root, {
43
+ authMode: answers.authMode,
44
+ adminMode: answers.adminMode,
45
+ includeAdmin: answers.includeAdmin,
46
+ });
47
+ if (answers.authMode === "docker") {
48
+ sharedConfig = dockerShared;
49
+ }
50
+ }
51
+ if (answers.api) {
52
+ configureApiEnv(root, sharedConfig);
53
+ }
54
+ if (answers.web) {
55
+ configureWebEnv(root);
56
+ }
57
+ generateSeamlessConfig(root, {
58
+ projectName,
59
+ webFramework: answers.webFramework,
60
+ apiFramework: answers.apiFramework,
61
+ authMode: answers.authMode,
62
+ adminMode: answers.adminMode,
63
+ });
64
+ printSuccessOutput({
65
+ projectName,
66
+ root,
67
+ webFramework: answers.webFramework,
68
+ apiFramework: answers.apiFramework,
69
+ authMode: answers.authMode,
70
+ useDocker: answers.useDocker,
71
+ });
72
+ }
73
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ export function resolveBootstrapSecret() {
4
+ const root = process.cwd();
5
+ if (process.env.SEAMLESS_BOOTSTRAP_SECRET) {
6
+ return process.env.SEAMLESS_BOOTSTRAP_SECRET;
7
+ }
8
+ const rootEnv = path.join(root, ".env");
9
+ if (fs.existsSync(rootEnv)) {
10
+ const content = fs.readFileSync(rootEnv, "utf-8");
11
+ const match = content.match(/SEAMLESS_BOOTSTRAP_SECRET=(.*)/);
12
+ if (match)
13
+ return match[1].trim();
14
+ }
15
+ const authEnv = path.join(root, "auth", ".env");
16
+ if (fs.existsSync(authEnv)) {
17
+ const content = fs.readFileSync(authEnv, "utf-8");
18
+ const match = content.match(/SEAMLESS_BOOTSTRAP_SECRET=(.*)/);
19
+ if (match)
20
+ return match[1].trim();
21
+ }
22
+ const compose = path.join(root, "docker-compose.yml");
23
+ if (fs.existsSync(compose)) {
24
+ const content = fs.readFileSync(compose, "utf-8");
25
+ const match = content.match(/SEAMLESS_BOOTSTRAP_SECRET:\s*(.*)/);
26
+ if (match)
27
+ return match[1].trim();
28
+ }
29
+ return null;
30
+ }
31
+ //# sourceMappingURL=bootstrapSecret.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrapSecret.js","sourceRoot":"","sources":["../../src/core/bootstrapSecret.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,UAAU,sBAAsB;IACpC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAE3B,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,CAAC;QAC1C,OAAO,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC;IAC/C,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAC9D,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChD,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAC9D,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;IACtD,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAElD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACjE,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}