@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 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
+ ```
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ require('../src/index.js');
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
+ };