@micropage-sh/cli 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +183 -0
- package/bin/micropage.js +3 -0
- package/package.json +65 -0
- package/src/api.js +51 -0
- package/src/auth.js +123 -0
- package/src/commands/builds.js +496 -0
- package/src/commands/files.js +225 -0
- package/src/commands/forms.js +71 -0
- package/src/commands/links.js +107 -0
- package/src/commands/login.js +255 -0
- package/src/commands/logout.js +16 -0
- package/src/commands/projects.js +573 -0
- package/src/commands/publish.js +10 -0
- package/src/commands/submissions.js +194 -0
- package/src/commands/whoami.js +87 -0
- package/src/config.js +22 -0
- package/src/index.js +243 -0
- package/src/mime.js +18 -0
- package/src/parser.js +72 -0
- package/src/project-ref.js +54 -0
- package/src/supabase.js +637 -0
- package/src/utils.js +47 -0
- package/templates/PROJECT_AGENT.template.md +31 -0
- package/templates/assets/favicon.svg +8 -0
- package/templates/assets/logo.svg +14 -0
- package/templates/examples/components-colors.page +84 -0
- package/templates/examples/components-hero-variants.page +28 -0
- package/templates/examples/components-pricing-and-forms.page +45 -0
- package/templates/examples/mobile-app-landing.page +77 -0
- package/templates/examples/portfolio.page +75 -0
- package/templates/examples/startup-landing.page +100 -0
package/README.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# micropage CLI
|
|
2
|
+
|
|
3
|
+
Command-line interface for [micropage.sh](https://app.micropage.sh) — create, edit, publish, and manage microsites from your terminal.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Node.js ≥ 18
|
|
8
|
+
- A micropage.sh account
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
### Homebrew (macOS / Linux)
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
brew tap micropage-sh/tap
|
|
16
|
+
brew install micropage
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### npm
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install -g micropage
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Authentication
|
|
26
|
+
|
|
27
|
+
Authenticate with your browser (GitHub or magic link):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
micropage login # opens browser for authentication
|
|
31
|
+
micropage whoami # show the currently logged-in user and subscription
|
|
32
|
+
micropage logout # clear the stored session
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Project workflow
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Create a new project and initialise a local folder
|
|
39
|
+
micropage projects create my-site
|
|
40
|
+
|
|
41
|
+
cd my-site
|
|
42
|
+
|
|
43
|
+
# Edit landing.page (or any *.page file) with your content
|
|
44
|
+
nano landing.page
|
|
45
|
+
|
|
46
|
+
# Push local content as a draft build
|
|
47
|
+
micropage push
|
|
48
|
+
|
|
49
|
+
# Publish the current draft (deploys to Cloudflare Pages)
|
|
50
|
+
micropage publish
|
|
51
|
+
|
|
52
|
+
# Pull the latest remote build content to landing.page
|
|
53
|
+
micropage projects pull
|
|
54
|
+
|
|
55
|
+
# Open the live site in the browser
|
|
56
|
+
micropage preview
|
|
57
|
+
|
|
58
|
+
# Copy the live URL to clipboard
|
|
59
|
+
micropage copy-link
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Project domains
|
|
63
|
+
|
|
64
|
+
When you create a project without `-d`, the API assigns `projects.domain` as the initial host for your site (for example `my-site.micropage.sh`). You can optionally attach a custom domain (such as `www.example.com`) in the web editor; when present, the CLI shows that custom domain as the site URL (otherwise the default host).
|
|
65
|
+
|
|
66
|
+
## Commands reference
|
|
67
|
+
|
|
68
|
+
### Auth
|
|
69
|
+
|
|
70
|
+
| Command | Description |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `micropage login [--force]` | Authenticate via browser |
|
|
73
|
+
| `micropage logout` | Clear stored session |
|
|
74
|
+
| `micropage whoami` | Show logged-in user and subscription/plan |
|
|
75
|
+
|
|
76
|
+
### Projects
|
|
77
|
+
|
|
78
|
+
| Command | Description |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `micropage projects list [--json]` | List your projects |
|
|
81
|
+
| `micropage projects show [id] [--json]` | Show project + latest build details |
|
|
82
|
+
| `micropage projects create <name> [-d domain]` | Create a project and init local folder. Omit `-d` to let the API assign `{slugified-name}-{6 hex}` (unique Pages/DNS label, max 58 chars). Use `-d` only to override the slug. |
|
|
83
|
+
| `micropage projects fetch <uuid\|domain>` | Fetch an existing project and init local folder (includes storage → `./assets` sync) |
|
|
84
|
+
| `micropage projects pull` | Pull latest build raw content → `landing.page`, then sync `./assets` with project storage (1:1) |
|
|
85
|
+
| `micropage projects delete [-y]` | Delete project (remote + local `.micropage/`) |
|
|
86
|
+
| `micropage projects deploy <uuid> <token> [-w]` | CI: read `.page` files, create build, and publish with a deploy token (Pro+). No login. |
|
|
87
|
+
|
|
88
|
+
When you create a new project, the CLI also scaffolds:
|
|
89
|
+
|
|
90
|
+
- an `examples/` directory with multiple starter `.page` files (full sites and component-only patterns),
|
|
91
|
+
- default `logo.svg` and `favicon.svg` under `assets/` (referenced by the examples as `logo: <- logo.svg` / `favicon: <- favicon.svg`),
|
|
92
|
+
- a `PROJECT_AGENT.md` file that explains the layout for local tools and agents, and links to the docs at `https://docs.micropage.sh`.
|
|
93
|
+
|
|
94
|
+
### Builds
|
|
95
|
+
|
|
96
|
+
| Command | Description |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `micropage push` | Merge local `.page` files and save as a draft build |
|
|
99
|
+
| `micropage publish` | Push then deploy to Cloudflare Pages |
|
|
100
|
+
| `micropage builds list [--json]` | List builds for the current project |
|
|
101
|
+
| `micropage builds redeploy [version]` | Re-publish an older build as a new deploy |
|
|
102
|
+
| `micropage builds download [version] [-o file]` | Download a deployed build archive as a `.zip` file |
|
|
103
|
+
|
|
104
|
+
**Notes:**
|
|
105
|
+
|
|
106
|
+
- Archives are only available for **deployed** builds.
|
|
107
|
+
- If you omit `version`, the CLI downloads the latest deployed build.
|
|
108
|
+
- The first time you request an archive for a build, the server may need a short time to prepare it; the CLI will wait and then download once ready.
|
|
109
|
+
- By default the archive is saved as `build-v<version>.zip` in the current directory; use `-o` to change the output path.
|
|
110
|
+
|
|
111
|
+
### Links
|
|
112
|
+
|
|
113
|
+
| Command | Description |
|
|
114
|
+
|---|---|
|
|
115
|
+
| `micropage preview` | Open the live site in the browser |
|
|
116
|
+
| `micropage copy-link` | Copy the live URL to clipboard |
|
|
117
|
+
| `micropage open-pricing` | Open the pricing page in the browser |
|
|
118
|
+
|
|
119
|
+
### Files
|
|
120
|
+
|
|
121
|
+
| Command | Description |
|
|
122
|
+
|---|---|
|
|
123
|
+
| `micropage files list [--json]` | List uploaded project files |
|
|
124
|
+
| `micropage files url <filename> [--copy]` | Get (and optionally copy) a file URL |
|
|
125
|
+
| `micropage files sync [-q]` | Download every file from project storage into `./assets` (matches remote list; removes extra local files) |
|
|
126
|
+
|
|
127
|
+
### Form submissions
|
|
128
|
+
|
|
129
|
+
| Command | Description |
|
|
130
|
+
|---|---|
|
|
131
|
+
| `micropage submissions list [--json]` | List form submissions for the project |
|
|
132
|
+
| `micropage submissions show <id> [--json]` | Show a single submission in detail |
|
|
133
|
+
| `micropage submissions export [--format csv|json] [-o file]` | Export all form submissions for the current project |
|
|
134
|
+
|
|
135
|
+
**Examples:**
|
|
136
|
+
|
|
137
|
+
- Export to CSV in the current directory:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
micropage submissions export
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
- Export to a custom CSV path:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
micropage submissions export --output exports/contact-form.csv
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
- Export as JSON:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
micropage submissions export --format json --output submissions.json
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Local content model
|
|
156
|
+
|
|
157
|
+
The CLI uses one or more `*.page` files in the working directory as the source of truth for project content.
|
|
158
|
+
|
|
159
|
+
**Merge order when pushing:**
|
|
160
|
+
1. `landing.page` (canonical primary file)
|
|
161
|
+
2. Any additional `*.page` files, sorted alphabetically
|
|
162
|
+
3. Files are joined with a single blank line between them
|
|
163
|
+
|
|
164
|
+
**Pulling** always writes to `landing.page` only.
|
|
165
|
+
|
|
166
|
+
## Project configuration
|
|
167
|
+
|
|
168
|
+
Each project folder has a `.micropage/project.json` file that stores:
|
|
169
|
+
- `projectId` — the Supabase project ID
|
|
170
|
+
- `buildId` — the ID of the last draft/deployed build (auto-updated by push/pull)
|
|
171
|
+
- `domain` — the Cloudflare Pages domain
|
|
172
|
+
- `name` — the project name
|
|
173
|
+
|
|
174
|
+
Add `.micropage/` to your `.gitignore` if you share the content folder.
|
|
175
|
+
|
|
176
|
+
## Machine-readable output
|
|
177
|
+
|
|
178
|
+
Most list and detail commands accept a `--json` flag to output raw JSON, which is useful for scripting.
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
micropage projects list --json | jq '.[].name'
|
|
182
|
+
micropage builds list --json | jq '.[] | select(.status == "deployed") | .number'
|
|
183
|
+
```
|
package/bin/micropage.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@micropage-sh/cli",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "CLI for micropage.sh - create, sync, and publish microsites",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"micropage": "bin/micropage.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"src",
|
|
12
|
+
"templates",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "node -e \"require('./src/index.js')\"",
|
|
17
|
+
"build:bin": "pkg . --targets node18-linux-x64,node18-macos-x64,node18-macos-arm64 --output dist/micropage",
|
|
18
|
+
"build:linux": "pkg . --target node18-linux-x64 --output dist/micropage-linux-amd64",
|
|
19
|
+
"build:macos-amd64": "pkg . --target node18-macos-x64 --output dist/micropage-darwin-amd64",
|
|
20
|
+
"build:macos-arm64": "pkg . --target node18-macos-arm64 --output dist/micropage-darwin-arm64"
|
|
21
|
+
},
|
|
22
|
+
"pkg": {
|
|
23
|
+
"scripts": [
|
|
24
|
+
"src/**/*.js",
|
|
25
|
+
"bin/**/*.js"
|
|
26
|
+
],
|
|
27
|
+
"assets": [
|
|
28
|
+
"templates/**/*"
|
|
29
|
+
],
|
|
30
|
+
"targets": [
|
|
31
|
+
"node18-linux-x64",
|
|
32
|
+
"node18-macos-x64",
|
|
33
|
+
"node18-macos-arm64"
|
|
34
|
+
],
|
|
35
|
+
"outputPath": "dist"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"micropage",
|
|
39
|
+
"cli",
|
|
40
|
+
"microsite",
|
|
41
|
+
"static-site"
|
|
42
|
+
],
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/micropage-sh/cli.git"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/micropage-sh/cli#readme",
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/micropage-sh/cli/issues"
|
|
50
|
+
},
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=18"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"commander": "^12.0.0",
|
|
60
|
+
"open": "^10.1.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@yao-pkg/pkg": "^5.11.5"
|
|
64
|
+
}
|
|
65
|
+
}
|
package/src/api.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const { getToken } = require('./auth');
|
|
2
|
+
|
|
3
|
+
const BASE_URL = process.env.MICROPAGE_API_URL || 'https://api.microsite.sh';
|
|
4
|
+
|
|
5
|
+
async function request(method, path, body = null) {
|
|
6
|
+
const token = getToken();
|
|
7
|
+
if (!token) {
|
|
8
|
+
const err = new Error('No token set. Run: micropage token set <token>');
|
|
9
|
+
err.code = 'NO_TOKEN';
|
|
10
|
+
throw err;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const url = path.startsWith('http') ? path : `${BASE_URL}${path.startsWith('/') ? '' : '/'}${path}`;
|
|
14
|
+
const options = {
|
|
15
|
+
method,
|
|
16
|
+
headers: {
|
|
17
|
+
'Authorization': `Bearer ${token}`,
|
|
18
|
+
'Content-Type': 'application/json',
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
if (body !== null && body !== undefined) {
|
|
22
|
+
options.body = JSON.stringify(body);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const res = await fetch(url, options);
|
|
26
|
+
const text = await res.text();
|
|
27
|
+
let data;
|
|
28
|
+
try {
|
|
29
|
+
data = text ? JSON.parse(text) : null;
|
|
30
|
+
} catch {
|
|
31
|
+
data = null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!res.ok) {
|
|
35
|
+
const err = new Error(data?.error || data?.message || res.statusText || `HTTP ${res.status}`);
|
|
36
|
+
err.status = res.status;
|
|
37
|
+
err.data = data;
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return data;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const api = {
|
|
45
|
+
get: (path) => request('GET', path),
|
|
46
|
+
post: (path, body) => request('POST', path, body),
|
|
47
|
+
put: (path, body) => request('PUT', path, body),
|
|
48
|
+
delete: (path) => request('DELETE', path),
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
module.exports = { api, BASE_URL };
|
package/src/auth.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const CONFIG_DIR = path.join(
|
|
7
|
+
process.env.HOME || process.env.USERPROFILE || '',
|
|
8
|
+
'.micropage'
|
|
9
|
+
);
|
|
10
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
11
|
+
|
|
12
|
+
const PROJECT_CONFIG_DIR = '.micropage';
|
|
13
|
+
const PROJECT_CONFIG_FILE = path.join(PROJECT_CONFIG_DIR, 'project.json');
|
|
14
|
+
|
|
15
|
+
function ensureConfigDir() {
|
|
16
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
17
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function readConfig() {
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
24
|
+
} catch {
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function writeConfig(config) {
|
|
30
|
+
ensureConfigDir();
|
|
31
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf8');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Session storage (Supabase access_token + refresh_token)
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
function getSession() {
|
|
39
|
+
const config = readConfig();
|
|
40
|
+
if (config.access_token && config.refresh_token) {
|
|
41
|
+
return {
|
|
42
|
+
access_token: config.access_token,
|
|
43
|
+
refresh_token: config.refresh_token,
|
|
44
|
+
user: config.user || null,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function setSession({ access_token, refresh_token, user }) {
|
|
51
|
+
const config = readConfig();
|
|
52
|
+
config.access_token = access_token;
|
|
53
|
+
config.refresh_token = refresh_token;
|
|
54
|
+
if (user) config.user = user;
|
|
55
|
+
writeConfig(config);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function clearSession() {
|
|
59
|
+
const config = readConfig();
|
|
60
|
+
delete config.access_token;
|
|
61
|
+
delete config.refresh_token;
|
|
62
|
+
delete config.user;
|
|
63
|
+
writeConfig(config);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Legacy token support (deprecated – kept for backward compat messaging)
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
function getToken() {
|
|
71
|
+
return readConfig().token || null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function setToken(token) {
|
|
75
|
+
const config = readConfig();
|
|
76
|
+
config.token = token;
|
|
77
|
+
writeConfig(config);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function maskToken(token) {
|
|
81
|
+
if (!token || token.length < 8) return token ? '****' : '(none)';
|
|
82
|
+
return token.slice(0, 4) + '****' + token.slice(-4);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Per-project config (.micropage/project.json)
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
function getProjectConfig(cwd = process.cwd()) {
|
|
90
|
+
const filePath = path.join(cwd, PROJECT_CONFIG_FILE);
|
|
91
|
+
try {
|
|
92
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
93
|
+
} catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function setProjectConfig(cwd, config) {
|
|
99
|
+
const filePath = path.join(cwd, PROJECT_CONFIG_FILE);
|
|
100
|
+
const dir = path.dirname(filePath);
|
|
101
|
+
if (!fs.existsSync(dir)) {
|
|
102
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
103
|
+
}
|
|
104
|
+
fs.writeFileSync(filePath, JSON.stringify(config, null, 2), 'utf8');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = {
|
|
108
|
+
CONFIG_DIR,
|
|
109
|
+
CONFIG_FILE,
|
|
110
|
+
PROJECT_CONFIG_DIR,
|
|
111
|
+
PROJECT_CONFIG_FILE,
|
|
112
|
+
// Session (new)
|
|
113
|
+
getSession,
|
|
114
|
+
setSession,
|
|
115
|
+
clearSession,
|
|
116
|
+
// Legacy token
|
|
117
|
+
getToken,
|
|
118
|
+
setToken,
|
|
119
|
+
maskToken,
|
|
120
|
+
// Project config
|
|
121
|
+
getProjectConfig,
|
|
122
|
+
setProjectConfig,
|
|
123
|
+
};
|