fabrica-e-commerce 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -78
- package/package.json +1 -1
- package/src/cli.js +98 -46
- package/src/deploy.js +111 -27
- package/src/deps.js +397 -61
- package/src/prompt.js +59 -22
- package/src/ui.js +37 -29
package/README.md
CHANGED
|
@@ -1,122 +1,157 @@
|
|
|
1
1
|
# fabrica-e-commerce
|
|
2
2
|
|
|
3
|
-
Orange
|
|
3
|
+
Orange-themed NPX CLI for launching a Fabrica e-commerce storefront (Supabase + Vercel + Next.js) from any terminal — Windows, macOS, Linux, and Termux (no root required).
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Quick start
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx fabrica-e-commerce help
|
|
9
8
|
npx fabrica-e-commerce build
|
|
10
9
|
```
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
Or install globally:
|
|
13
12
|
|
|
14
13
|
```bash
|
|
15
14
|
npm install -g fabrica-e-commerce
|
|
16
15
|
fabrica build
|
|
17
16
|
```
|
|
18
17
|
|
|
18
|
+
Both `fabrica` and `fabrica-e-commerce` binaries are available after a global install.
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
---
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
## Commands
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
| Command | Description |
|
|
25
|
+
|---------|-------------|
|
|
26
|
+
| `build` | Full deploy wizard — Supabase → secrets → admin password → clone → Vercel or local |
|
|
27
|
+
| `list` | Show all projects and view details |
|
|
28
|
+
| `env` | Update environment variables for any project |
|
|
29
|
+
| `rerun` | Re-open or restart an existing project |
|
|
30
|
+
| `vins` | Verify and auto-install CLI dependencies (git, gh, vercel) |
|
|
31
|
+
| `clean` | Remove local data, env files, or logout from Vercel / GitHub |
|
|
32
|
+
| `info` | Package, bridge, repo, and storage info |
|
|
33
|
+
| `help` | Show the command guide |
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
---
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
npm run test:pack
|
|
35
|
-
```
|
|
37
|
+
## Build flow (6 steps)
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
### Step 1 — Dependency check
|
|
40
|
+
Verifies `git` is installed. Auto-installs it if missing.
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
### Step 2 — Supabase connect
|
|
43
|
+
Posts your schema/seed SQL to the Fabrica bridge (`sparrow-supabase-connect.lovable.app`), opens the OAuth connect URL in your browser, and polls until the Supabase project is provisioned and returns `{ url, anonKey }`.
|
|
44
|
+
|
|
45
|
+
### Step 3 — Environment variables
|
|
46
|
+
Prompts for your API keys:
|
|
47
|
+
|
|
48
|
+
| Variable | Purpose |
|
|
49
|
+
|----------|---------|
|
|
50
|
+
| `RAZORPAY_KEY_ID` | Razorpay payment key ID |
|
|
51
|
+
| `RAZORPAY_KEY_SECRET` | Razorpay payment secret |
|
|
52
|
+
| `OPENROUTER_API_KEY` | OpenRouter AI API key |
|
|
53
|
+
| `UMAMI_WEBSITE_ID` | Umami analytics website ID |
|
|
54
|
+
| `UMAMI_API_KEY` | Umami analytics API key |
|
|
55
|
+
| `SHIPPO_API_KEY` | Shippo shipping API key |
|
|
56
|
+
|
|
57
|
+
`SUPABASE_URL` and `SUPABASE_ANON_KEY` are filled automatically from the bridge response.
|
|
58
|
+
|
|
59
|
+
### Step 4 — Admin password
|
|
60
|
+
Prompts for a password to protect your store's admin and edit panel. Stored as the `PASS` environment variable.
|
|
61
|
+
|
|
62
|
+
### Step 5 — Clone storefront
|
|
63
|
+
Clones `https://github.com/trucount/fabrica-final-e-c.git` into a local builds directory. Asks for a project name (used as the Vercel project name).
|
|
64
|
+
|
|
65
|
+
### Step 6 — Deploy target
|
|
66
|
+
Choose how to run your store:
|
|
67
|
+
|
|
68
|
+
**Local mode**
|
|
69
|
+
- Writes all collected variables to `.env.local` inside the cloned app.
|
|
70
|
+
- Runs `pnpm install` (or `npm install`) and starts the Next.js dev server at `http://localhost:3000`.
|
|
40
71
|
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
- `OPENROUTER_API_KEY`
|
|
55
|
-
- `UMAMI_WEBSITE_ID`
|
|
56
|
-
- `UMAMI_API_KEY`
|
|
57
|
-
- `SHIPPO_API_KEY`
|
|
58
|
-
4. Adds hardcoded values requested by the project:
|
|
59
|
-
- `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=0000`
|
|
60
|
-
- `UMAMI_API_CLIENT_ENDPOINT=https://api.umami.is/v1`
|
|
61
|
-
- `SUPABASE_SERVICE_ROLE_KEY=0000`
|
|
62
|
-
5. Adds `SUPABASE_URL` and `SUPABASE_ANON_KEY` from the bridge response.
|
|
63
|
-
6. **Step 3 — run target:**
|
|
64
|
-
- Asks whether to run the storefront **locally** or deploy on **Vercel cloud**.
|
|
65
|
-
- Local mode writes every collected variable to `.env.local` inside the cloned Next.js app, installs dependencies (`pnpm install` when available, otherwise `npm install`), and starts `pnpm run dev`/`npm run dev` at `http://localhost:3000`.
|
|
66
|
-
- Vercel mode verifies you're logged in to Vercel (`vercel whoami`); if not, runs an interactive `vercel login` and waits for it to finish.
|
|
67
|
-
- Vercel mode logs in to the GitHub CLI if needed (`gh auth login`) and creates a user-owned repo from the hardcoded storefront source with a fork/template flow, avoiding local `git push` credentials.
|
|
68
|
-
- Vercel mode links a new Vercel project with `vercel link --yes --project <name>` and connects it to the new GitHub repo with `vercel git connect` so future pushes auto-deploy.
|
|
69
|
-
- Vercel mode writes every collected environment variable (including the Supabase URL/anon key) to the **production, preview, and development** environments with `vercel env add`, so the values are permanent for the project, not just the first deploy.
|
|
70
|
-
- Vercel mode creates the production deployment with `vercel --prod --yes`.
|
|
71
|
-
|
|
72
|
-
## Dependency check (`vins`)
|
|
73
|
-
|
|
74
|
-
Run `npx fabrica-e-commerce vins` any time to verify the tools the CLI depends on:
|
|
72
|
+
**Vercel cloud mode**
|
|
73
|
+
- Checks and installs `gh` (GitHub CLI) and `vercel` CLI if missing.
|
|
74
|
+
- Logs in to Vercel interactively if needed.
|
|
75
|
+
- Logs in to GitHub CLI if needed and forks the storefront to a new user-owned repo.
|
|
76
|
+
- Links a new Vercel project and connects it to the GitHub repo for auto-deploys.
|
|
77
|
+
- Writes every environment variable to **production, preview, and development** environments with `vercel env add`.
|
|
78
|
+
- Deploys with `vercel --prod`.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Dependency installer (`vins`)
|
|
83
|
+
|
|
84
|
+
`vins` checks for `git`, `gh`, and the Vercel CLI and auto-installs anything missing — **no root or admin required** on any platform.
|
|
75
85
|
|
|
76
86
|
```bash
|
|
77
87
|
npx fabrica-e-commerce vins
|
|
78
88
|
```
|
|
79
89
|
|
|
80
|
-
|
|
90
|
+
| Platform | Install strategy |
|
|
91
|
+
|----------|-----------------|
|
|
92
|
+
| **Termux** | `pkg install` — no root needed |
|
|
93
|
+
| **Linux (no root)** | Homebrew/Linuxbrew → system pkg manager (tries without sudo first) → conda/mamba → binary download from GitHub releases |
|
|
94
|
+
| **Linux (with sudo)** | apt-get / dnf / yum / pacman / zypper / apk |
|
|
95
|
+
| **macOS** | `brew install` → auto-installs Homebrew if missing → binary download |
|
|
96
|
+
| **Windows (no admin)** | `winget --scope user` → auto-installs Scoop → `scoop install` → Chocolatey |
|
|
81
97
|
|
|
98
|
+
If a tool still can't be installed automatically, `vins` prints the correct manual install command for your platform and exits with a non-zero status.
|
|
82
99
|
|
|
100
|
+
---
|
|
83
101
|
|
|
84
|
-
##
|
|
102
|
+
## Managing projects
|
|
85
103
|
|
|
86
|
-
|
|
104
|
+
### `list`
|
|
105
|
+
Shows all projects saved locally with their type (local/cloud), creation date, GitHub repo, Supabase URL, and live URL.
|
|
87
106
|
|
|
88
|
-
|
|
107
|
+
### `env`
|
|
108
|
+
Lets you update any environment variable for a saved project. For local projects it rewrites `.env.local`. For cloud projects it updates Vercel (production, preview, development) and triggers a redeploy.
|
|
89
109
|
|
|
90
|
-
|
|
110
|
+
### `rerun`
|
|
111
|
+
- **Local projects** — restarts the dev server at `http://localhost:3000`.
|
|
112
|
+
- **Cloud projects** — opens the production URL in your browser.
|
|
91
113
|
|
|
92
|
-
|
|
114
|
+
### `clean`
|
|
115
|
+
Offers four modes: delete local project records, wipe env files, logout from Vercel, logout from GitHub, or a full reset of all of the above.
|
|
93
116
|
|
|
94
|
-
|
|
95
|
-
2. In GitHub, open **Settings → Secrets and variables → Actions**.
|
|
96
|
-
3. Add a repository secret named `NPM_TOKEN` with that npm token.
|
|
117
|
+
---
|
|
97
118
|
|
|
98
|
-
|
|
119
|
+
## Local project records
|
|
99
120
|
|
|
100
|
-
|
|
101
|
-
2. Runs `npm run test:cli`.
|
|
102
|
-
3. Runs `npm run test:pack`.
|
|
103
|
-
4. Checks the current latest version on npm and bumps `package.json` to the next patch version when needed.
|
|
104
|
-
5. Runs `npm publish --access public --provenance`.
|
|
105
|
-
6. Commits the published version back to GitHub with `[skip npm-publish]` to avoid an infinite publish loop.
|
|
121
|
+
Deployment metadata is saved to `~/.fabrica-ecommerce/projects.json`. Secret values are **not** stored — only variable names, project path, repo URL, creation date, and Supabase URL are recorded.
|
|
106
122
|
|
|
107
|
-
|
|
123
|
+
---
|
|
108
124
|
|
|
125
|
+
## Requirements
|
|
109
126
|
|
|
110
|
-
|
|
127
|
+
- **Node.js 18.17+**
|
|
128
|
+
- **Git** — run `npx fabrica-e-commerce vins` to auto-install
|
|
129
|
+
- **GitHub CLI (`gh`)** — run `vins` to auto-install; interactive login handled by `build`
|
|
130
|
+
- **Vercel account** — login is handled interactively by `build` if needed
|
|
131
|
+
- A **Supabase** account connected via the Fabrica bridge
|
|
111
132
|
|
|
112
|
-
|
|
133
|
+
---
|
|
113
134
|
|
|
114
|
-
|
|
135
|
+
## Automatic npm publishing (GitHub Actions)
|
|
115
136
|
|
|
116
|
-
|
|
137
|
+
The workflow at `.github/workflows/npm-publish.yml` verifies and publishes to npm on every push to `main`, `master`, or `work`, and can be triggered manually from the Actions tab.
|
|
138
|
+
|
|
139
|
+
**Setup — add your npm token once:**
|
|
140
|
+
1. Create an npm token with publish permissions.
|
|
141
|
+
2. In GitHub go to **Settings → Secrets and variables → Actions**.
|
|
142
|
+
3. Add a secret named `NPM_TOKEN`.
|
|
143
|
+
|
|
144
|
+
**What the workflow does on each run:**
|
|
145
|
+
1. Upgrades npm to the latest version (avoids bundled-npm bugs on GitHub runners).
|
|
146
|
+
2. Runs `npm install`, `npm run build`, `npm run test:cli`, `npm run test:pack`.
|
|
147
|
+
3. Bumps `package.json` to the next patch version if the current version is already published.
|
|
148
|
+
4. Publishes with `npm publish --access public --provenance`.
|
|
149
|
+
5. Commits the bumped version back to GitHub with `[skip npm-publish]` to prevent an infinite loop.
|
|
150
|
+
|
|
151
|
+
> The `repository.url` in `package.json` must match the GitHub repo used by the workflow — npm uses it to verify the provenance bundle.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Windows notes
|
|
117
156
|
|
|
118
|
-
|
|
119
|
-
- Git installed and available in PATH
|
|
120
|
-
- [GitHub CLI](https://github.com/cli/cli) (`gh`) installed and available in PATH — used to create the new repository and push the storefront code. Logging in (`gh auth login`) is handled interactively by `build` if needed.
|
|
121
|
-
- A Vercel account. The Vercel CLI will prompt/login when required (`build` also runs this check up front).
|
|
122
|
-
- Run `npx fabrica-e-commerce vins` to check for and auto-install `git`/`gh` if either is missing.
|
|
157
|
+
The CLI spawns npm commands through `npx.cmd` on Windows so `npx vercel@latest ...` works correctly from CMD and PowerShell. If you see a Vercel login prompt, complete it in your browser and the CLI will continue automatically.
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -3,14 +3,21 @@ import { readFile } from 'node:fs/promises';
|
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { connectSupabase } from './bridge.js';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
collectEnv, collectAdminPassword, cloneRepo,
|
|
8
|
+
deployToVercel, updateProjectEnv, ensureVercelLogin, runLocally,
|
|
9
|
+
} from './deploy.js';
|
|
7
10
|
import { createGithubRepoFromClone } from './github.js';
|
|
8
11
|
import { ensureDependencies, vinsCommand } from './deps.js';
|
|
9
12
|
import { cleanCommand } from './clean.js';
|
|
10
13
|
import { BRIDGE_ORIGIN, STORE_REPO } from './config.js';
|
|
11
14
|
import { dataDir, readProjects } from './store.js';
|
|
12
15
|
import { choose, ask } from './prompt.js';
|
|
13
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
banner, help, kv, kvSuccess, kvFail, section, endSections, subBox,
|
|
18
|
+
log, logInfo, logWarn, logSuccess, divider, stepHeader,
|
|
19
|
+
orange, dimOrange, bold, dim, green, cyan, yellow, red, white,
|
|
20
|
+
} from './ui.js';
|
|
14
21
|
import { openUrl } from './system.js';
|
|
15
22
|
|
|
16
23
|
async function packageVersion() {
|
|
@@ -23,31 +30,42 @@ async function packageVersion() {
|
|
|
23
30
|
async function build() {
|
|
24
31
|
banner();
|
|
25
32
|
|
|
26
|
-
|
|
33
|
+
// Step 1 — Dependency check
|
|
34
|
+
stepHeader(1, 6, 'Dependency check');
|
|
27
35
|
section('Dependency check');
|
|
28
36
|
await ensureDependencies({ names: ['git'] });
|
|
29
37
|
endSections();
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
// Step 2 — Supabase connect
|
|
40
|
+
stepHeader(2, 6, 'Supabase connect');
|
|
32
41
|
section('Supabase connect');
|
|
33
42
|
kv('Bridge', 'ONLINE');
|
|
34
43
|
kv('SQL', 'Prepared securely (hidden from UI)');
|
|
35
44
|
const supabase = await connectSupabase();
|
|
36
45
|
endSections();
|
|
37
46
|
|
|
38
|
-
|
|
47
|
+
// Step 3 — Environment variables
|
|
48
|
+
stepHeader(3, 6, 'Environment variables');
|
|
39
49
|
const env = await collectEnv(supabase);
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
// Step 4 — Admin password
|
|
52
|
+
stepHeader(4, 6, 'Admin password');
|
|
53
|
+
const adminPassword = await collectAdminPassword();
|
|
54
|
+
env['PASS'] = adminPassword;
|
|
55
|
+
|
|
56
|
+
// Step 5 — Clone storefront
|
|
57
|
+
stepHeader(5, 6, 'Clone storefront');
|
|
42
58
|
const project = await cloneRepo();
|
|
43
59
|
|
|
44
|
-
|
|
60
|
+
// Step 6 — Deploy target
|
|
61
|
+
stepHeader(6, 6, 'Deploy target');
|
|
45
62
|
section('Where should Fabrica deploy?');
|
|
63
|
+
logInfo('Choose how you want to run your store:');
|
|
46
64
|
endSections();
|
|
47
65
|
|
|
48
66
|
const target = await choose('Select deployment target:', [
|
|
49
|
-
{ name: '☁
|
|
50
|
-
{ name: '💻 Run locally on this
|
|
67
|
+
{ name: '☁ Deploy on Vercel — cloud, shareable URL', value: 'vercel' },
|
|
68
|
+
{ name: '💻 Run locally — development on this machine', value: 'local' },
|
|
51
69
|
]);
|
|
52
70
|
|
|
53
71
|
if (target === 'local') {
|
|
@@ -63,14 +81,14 @@ async function build() {
|
|
|
63
81
|
const githubRepo = await createGithubRepoFromClone(project);
|
|
64
82
|
const record = await deployToVercel(project, env, githubRepo);
|
|
65
83
|
|
|
66
|
-
section('✓
|
|
84
|
+
section('✓ Deployment complete');
|
|
67
85
|
divider();
|
|
68
|
-
|
|
86
|
+
kvSuccess('Project', record.projectName);
|
|
69
87
|
kv('GitHub', record.githubRepo || dim('n/a'));
|
|
70
|
-
kv('URL',
|
|
71
|
-
kv('Local path', record.target);
|
|
88
|
+
kv('Live URL', record.productionUrl || dim('n/a'));
|
|
89
|
+
kv('Local path', dim(record.target));
|
|
72
90
|
divider();
|
|
73
|
-
|
|
91
|
+
logSuccess('Your Fabrica store is live! 🎉');
|
|
74
92
|
endSections();
|
|
75
93
|
}
|
|
76
94
|
|
|
@@ -81,18 +99,22 @@ async function list() {
|
|
|
81
99
|
|
|
82
100
|
section('Your Fabrica projects');
|
|
83
101
|
if (!projects.length) {
|
|
84
|
-
logInfo('No projects found');
|
|
85
|
-
log(
|
|
102
|
+
logInfo('No projects found yet');
|
|
103
|
+
log(`Run ${cyan('npx fabrica-e-commerce build')} to create your first store`);
|
|
86
104
|
endSections();
|
|
87
105
|
return;
|
|
88
106
|
}
|
|
89
107
|
|
|
90
|
-
kv('Total
|
|
108
|
+
kv('Total', String(projects.length) + ' project' + (projects.length !== 1 ? 's' : ''));
|
|
91
109
|
divider();
|
|
92
110
|
for (const p of projects) {
|
|
93
|
-
const
|
|
94
|
-
|
|
111
|
+
const badge = p.type === 'local'
|
|
112
|
+
? `${yellow('◉')} ${yellow('local')}`
|
|
113
|
+
: `${green('◉')} ${green('cloud')}`;
|
|
114
|
+
log(`${bold(white(p.projectName))} ${badge} ${dim(p.createdAt?.slice(0, 10) || '')}`);
|
|
95
115
|
}
|
|
116
|
+
divider();
|
|
117
|
+
logInfo('Select a project to view its details');
|
|
96
118
|
endSections();
|
|
97
119
|
|
|
98
120
|
const choices = projects.map((p) => ({
|
|
@@ -103,14 +125,19 @@ async function list() {
|
|
|
103
125
|
const selected = await choose('Select a project to view details:', choices);
|
|
104
126
|
const project = projects.find((p) => p.id === selected);
|
|
105
127
|
|
|
106
|
-
section(`Project
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
kv('
|
|
128
|
+
section(`Project — ${bold(orange(project.projectName))}`);
|
|
129
|
+
divider();
|
|
130
|
+
const typeBadge = project.type === 'local' ? yellow('local') : green('cloud');
|
|
131
|
+
kv('Type', typeBadge);
|
|
132
|
+
kv('Created', project.createdAt ? project.createdAt.slice(0, 19).replace('T', ' ') : dim('unknown'));
|
|
133
|
+
kv('Path', dim(project.target || 'n/a'));
|
|
110
134
|
kv('GitHub', project.githubRepo || dim('n/a'));
|
|
111
135
|
kv('Supabase', project.supabaseUrl || dim('n/a'));
|
|
112
136
|
kv('URL', project.productionUrl || dim('n/a'));
|
|
113
|
-
kv('Env keys', (project.envKeys || []).
|
|
137
|
+
kv('Env keys', (project.envKeys || []).length
|
|
138
|
+
? (project.envKeys || []).join(dim(' · '))
|
|
139
|
+
: dim('none'));
|
|
140
|
+
divider();
|
|
114
141
|
endSections();
|
|
115
142
|
}
|
|
116
143
|
|
|
@@ -120,24 +147,26 @@ async function env() {
|
|
|
120
147
|
const projects = await readProjects();
|
|
121
148
|
|
|
122
149
|
section('Environment manager');
|
|
150
|
+
logInfo('Update API keys and secrets for any project.');
|
|
123
151
|
if (!projects.length) {
|
|
124
|
-
|
|
125
|
-
log('
|
|
152
|
+
divider();
|
|
153
|
+
log(`No projects found — run ${cyan('npx fabrica-e-commerce build')} first`);
|
|
126
154
|
endSections();
|
|
127
155
|
return;
|
|
128
156
|
}
|
|
157
|
+
kv('Total projects', String(projects.length));
|
|
129
158
|
endSections();
|
|
130
159
|
|
|
131
160
|
const typeFilter = await choose('Which projects to show?', [
|
|
132
|
-
{ name: 'All projects',
|
|
133
|
-
{ name: 'Local projects only',
|
|
134
|
-
{ name: 'Cloud (Vercel) projects only',
|
|
161
|
+
{ name: '📋 All projects', value: 'all' },
|
|
162
|
+
{ name: '💻 Local projects only', value: 'local' },
|
|
163
|
+
{ name: '☁ Cloud (Vercel) projects only', value: 'cloud' },
|
|
135
164
|
]);
|
|
136
165
|
|
|
137
166
|
const filtered = typeFilter === 'all' ? projects : projects.filter((p) => p.type === typeFilter);
|
|
138
167
|
if (!filtered.length) {
|
|
139
168
|
section('Environment manager');
|
|
140
|
-
|
|
169
|
+
logWarn(`No ${typeFilter} projects found`);
|
|
141
170
|
endSections();
|
|
142
171
|
return;
|
|
143
172
|
}
|
|
@@ -156,13 +185,22 @@ async function env() {
|
|
|
156
185
|
return;
|
|
157
186
|
}
|
|
158
187
|
|
|
188
|
+
section(`Env keys — ${bold(orange(project.projectName))}`);
|
|
189
|
+
divider();
|
|
190
|
+
for (const k of envKeys) log(`${orange('·')} ${white(k)}`);
|
|
191
|
+
divider();
|
|
192
|
+
endSections();
|
|
193
|
+
|
|
159
194
|
const key = await choose('Select env variable to update:', envKeys.map((k) => ({ name: k, value: k })));
|
|
160
195
|
const currentVal = (project.env || {})[key];
|
|
161
|
-
|
|
196
|
+
|
|
197
|
+
console.log();
|
|
198
|
+
const value = await ask(`New value for ${orange(key)}`, currentVal || '');
|
|
199
|
+
console.log();
|
|
162
200
|
|
|
163
201
|
section('Applying update');
|
|
164
202
|
await updateProjectEnv(project, key, value);
|
|
165
|
-
kvSuccess('Updated', `${key}
|
|
203
|
+
kvSuccess('Updated', `${key} → ${project.type === 'local' ? '.env.local' : 'Vercel + redeployed'}`);
|
|
166
204
|
endSections();
|
|
167
205
|
}
|
|
168
206
|
|
|
@@ -173,17 +211,18 @@ async function rerun() {
|
|
|
173
211
|
|
|
174
212
|
section('Re-run / re-open project');
|
|
175
213
|
if (!projects.length) {
|
|
176
|
-
logInfo('No projects found');
|
|
177
|
-
log(
|
|
214
|
+
logInfo('No projects found yet');
|
|
215
|
+
log(`Run ${cyan('npx fabrica-e-commerce build')} to create your first store`);
|
|
178
216
|
endSections();
|
|
179
217
|
return;
|
|
180
218
|
}
|
|
219
|
+
kv('Total', String(projects.length) + ' project' + (projects.length !== 1 ? 's' : ''));
|
|
181
220
|
endSections();
|
|
182
221
|
|
|
183
222
|
const typeFilter = await choose('Which type of project?', [
|
|
184
|
-
{ name: 'All projects',
|
|
185
|
-
{ name: 'Local projects',
|
|
186
|
-
{ name: 'Cloud (Vercel) projects',
|
|
223
|
+
{ name: '📋 All projects', value: 'all' },
|
|
224
|
+
{ name: '💻 Local projects', value: 'local' },
|
|
225
|
+
{ name: '☁ Cloud (Vercel) projects', value: 'cloud' },
|
|
187
226
|
]);
|
|
188
227
|
|
|
189
228
|
const filtered = typeFilter === 'all' ? projects : projects.filter((p) => p.type === typeFilter);
|
|
@@ -200,12 +239,14 @@ async function rerun() {
|
|
|
200
239
|
})));
|
|
201
240
|
const project = filtered.find((p) => p.id === projectId);
|
|
202
241
|
|
|
203
|
-
section(`Re-running
|
|
242
|
+
section(`Re-running — ${bold(orange(project.projectName))}`);
|
|
204
243
|
|
|
205
244
|
if (project.type === 'local') {
|
|
206
|
-
|
|
245
|
+
divider();
|
|
246
|
+
kv('Path', dim(project.target));
|
|
207
247
|
kv('URL', 'http://localhost:3000');
|
|
208
|
-
log('Starting dev server — browser opens in 3s…');
|
|
248
|
+
log(dim('Starting dev server — browser opens in 3s…'));
|
|
249
|
+
divider();
|
|
209
250
|
endSections();
|
|
210
251
|
|
|
211
252
|
const { runCommand, runCommandCapture } = await import('./system.js');
|
|
@@ -217,11 +258,13 @@ async function rerun() {
|
|
|
217
258
|
await runCommand(devCommand[0], devCommand[1], { cwd: project.target });
|
|
218
259
|
} else {
|
|
219
260
|
const url = project.productionUrl || null;
|
|
261
|
+
divider();
|
|
220
262
|
kv('GitHub', project.githubRepo || dim('n/a'));
|
|
221
263
|
kv('URL', url || dim('n/a'));
|
|
222
264
|
kv('Inspect', project.inspectUrl || dim('n/a'));
|
|
223
|
-
kv('Created', project.createdAt
|
|
224
|
-
|
|
265
|
+
kv('Created', project.createdAt ? project.createdAt.slice(0, 19).replace('T', ' ') : dim('unknown'));
|
|
266
|
+
divider();
|
|
267
|
+
if (url) log(dim(`Opening ${url}…`));
|
|
225
268
|
endSections();
|
|
226
269
|
if (url) await openUrl(url);
|
|
227
270
|
}
|
|
@@ -230,14 +273,21 @@ async function rerun() {
|
|
|
230
273
|
// ── info ──────────────────────────────────────────────────────────────────────
|
|
231
274
|
async function info() {
|
|
232
275
|
banner();
|
|
276
|
+
const version = await packageVersion();
|
|
277
|
+
|
|
233
278
|
section('Package info');
|
|
234
|
-
|
|
279
|
+
divider();
|
|
280
|
+
kvSuccess('Package', `fabrica-e-commerce v${version}`);
|
|
235
281
|
kv('Bridge', BRIDGE_ORIGIN);
|
|
236
282
|
kv('Store repo', STORE_REPO);
|
|
237
|
-
kv('Local data', dataDir);
|
|
283
|
+
kv('Local data', dim(dataDir));
|
|
284
|
+
divider();
|
|
238
285
|
kv('Node.js', process.version);
|
|
239
286
|
kv('Platform', process.platform);
|
|
287
|
+
kv('Arch', process.arch);
|
|
288
|
+
divider();
|
|
240
289
|
kv('Creator', 'SPARROW AI SOLUTION');
|
|
290
|
+
kv('License', 'MIT');
|
|
241
291
|
endSections();
|
|
242
292
|
}
|
|
243
293
|
|
|
@@ -254,7 +304,9 @@ export async function run(args) {
|
|
|
254
304
|
if (command === 'vins' || command === '/vins') return vinsCommand();
|
|
255
305
|
if (command === 'help' || command === '--help' || command === '-h') return help();
|
|
256
306
|
|
|
257
|
-
|
|
258
|
-
|
|
307
|
+
section('Unknown command');
|
|
308
|
+
logWarn(`"${command}" is not a valid command`);
|
|
309
|
+
log(`Run ${cyan('npx fabrica-e-commerce help')} to see all commands`);
|
|
310
|
+
endSections();
|
|
259
311
|
process.exitCode = 1;
|
|
260
312
|
}
|