capuzzella 1.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 +103 -0
- package/bin/capuzzella +411 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Capuzzella CLI
|
|
2
|
+
|
|
3
|
+
Command-line interface for the [Capuzzella](https://capuzzella.com) AI-Powered Website Builder.
|
|
4
|
+
|
|
5
|
+
Manage pages, publishing, scheduling, API keys, AI providers, email, and custom domains — all from your terminal.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Node.js 18+
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g capuzzella
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
Set your API key and server URL:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
export CAPUZZELLA_API_KEY=cap_...
|
|
23
|
+
export CAPUZZELLA_URL=https://your-instance.capuzzella.com
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`CAPUZZELLA_URL` defaults to `http://localhost:3000` if not set.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
capuzzella <command> [options]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Identity
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
capuzzella whoami
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Pages
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
capuzzella pages list
|
|
44
|
+
capuzzella pages get index.html
|
|
45
|
+
capuzzella pages save blog/post.html --file ./post.html
|
|
46
|
+
capuzzella pages delete blog/old.html
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Publishing
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
capuzzella publish all
|
|
53
|
+
capuzzella publish index.html
|
|
54
|
+
capuzzella unpublish index.html
|
|
55
|
+
capuzzella status index.html
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Scheduling
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
capuzzella schedule list
|
|
62
|
+
capuzzella schedule list all
|
|
63
|
+
capuzzella schedule add index.html --at 2026-04-01T09:00:00Z
|
|
64
|
+
capuzzella schedule get 1
|
|
65
|
+
capuzzella schedule cancel 1
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### API Keys
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
capuzzella keys list
|
|
72
|
+
capuzzella keys create --name "CI Deploy" --role editor
|
|
73
|
+
capuzzella keys revoke 3
|
|
74
|
+
capuzzella keys delete 3
|
|
75
|
+
capuzzella keys audit 3
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### AI Provider
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
capuzzella ai-provider get
|
|
82
|
+
capuzzella ai-provider set --provider anthropic --model claude-sonnet-4-20250514 --api-key sk-...
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Transactional Email
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
capuzzella email get
|
|
89
|
+
capuzzella email set --domain example.com --contact-email hello@example.com --api-key re_...
|
|
90
|
+
capuzzella email remove
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Custom Domains
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
capuzzella domains list
|
|
97
|
+
capuzzella domains add example.com
|
|
98
|
+
capuzzella domains delete <domain-id>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
See [LICENSE.md](LICENSE.md).
|
package/bin/capuzzella
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
4
|
+
|
|
5
|
+
const API_KEY = process.env.CAPUZZELLA_API_KEY;
|
|
6
|
+
const BASE_URL = (process.env.CAPUZZELLA_URL || 'http://localhost:3000').replace(/\/$/, '');
|
|
7
|
+
|
|
8
|
+
if (!API_KEY) {
|
|
9
|
+
console.error('Error: CAPUZZELLA_API_KEY environment variable is not set.');
|
|
10
|
+
console.error('Set it with: export CAPUZZELLA_API_KEY=cap_...');
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const headers = {
|
|
15
|
+
'Authorization': `Bearer ${API_KEY}`,
|
|
16
|
+
'Content-Type': 'application/json',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
async function request(method, path, body) {
|
|
20
|
+
const url = `${BASE_URL}${path}`;
|
|
21
|
+
const opts = { method, headers };
|
|
22
|
+
if (body !== undefined) opts.body = JSON.stringify(body);
|
|
23
|
+
|
|
24
|
+
let res;
|
|
25
|
+
try {
|
|
26
|
+
res = await fetch(url, opts);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
console.error(`Connection failed: ${err.message}`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let data;
|
|
33
|
+
const contentType = res.headers.get('content-type') || '';
|
|
34
|
+
if (contentType.includes('application/json')) {
|
|
35
|
+
data = await res.json();
|
|
36
|
+
} else {
|
|
37
|
+
data = await res.text();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!res.ok) {
|
|
41
|
+
const msg = typeof data === 'object' ? data.error || JSON.stringify(data) : data;
|
|
42
|
+
console.error(`Error ${res.status}: ${msg}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return data;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function printJson(data) {
|
|
50
|
+
console.log(JSON.stringify(data, null, 2));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---- Commands ----
|
|
54
|
+
|
|
55
|
+
const commands = {
|
|
56
|
+
async 'whoami'() {
|
|
57
|
+
const data = await request('GET', '/api/me');
|
|
58
|
+
console.log(`Role: ${data.role}`);
|
|
59
|
+
if (data.keyName) console.log(`Key: ${data.keyName} (id ${data.keyId})`);
|
|
60
|
+
if (data.email) console.log(`User: ${data.email}`);
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
async 'pages list'() {
|
|
64
|
+
const data = await request('GET', '/api/pages');
|
|
65
|
+
printJson(data);
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
async 'pages get'(args) {
|
|
69
|
+
const pagePath = args[0];
|
|
70
|
+
if (!pagePath) { console.error('Usage: capuzzella pages get <path>'); process.exit(1); }
|
|
71
|
+
const data = await request('GET', `/api/pages/${pagePath}`);
|
|
72
|
+
if (typeof data === 'object' && data.html) {
|
|
73
|
+
console.log(data.html);
|
|
74
|
+
} else {
|
|
75
|
+
printJson(data);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
async 'pages save'(args) {
|
|
80
|
+
const pagePath = args[0];
|
|
81
|
+
if (!pagePath) { console.error('Usage: capuzzella pages save <path> --file <file>'); process.exit(1); }
|
|
82
|
+
const fileIdx = args.indexOf('--file');
|
|
83
|
+
if (fileIdx === -1 || !args[fileIdx + 1]) { console.error('Missing --file <file>'); process.exit(1); }
|
|
84
|
+
const filePath = args[fileIdx + 1];
|
|
85
|
+
if (!existsSync(filePath)) { console.error(`File not found: ${filePath}`); process.exit(1); }
|
|
86
|
+
const html = readFileSync(filePath, 'utf8');
|
|
87
|
+
const data = await request('PUT', `/api/pages/${pagePath}`, { html });
|
|
88
|
+
printJson(data);
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
async 'pages delete'(args) {
|
|
92
|
+
const pagePath = args[0];
|
|
93
|
+
if (!pagePath) { console.error('Usage: capuzzella pages delete <path>'); process.exit(1); }
|
|
94
|
+
const data = await request('DELETE', `/api/pages/${pagePath}`);
|
|
95
|
+
printJson(data);
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
async 'publish all'() {
|
|
99
|
+
const data = await request('POST', '/publish/');
|
|
100
|
+
printJson(data);
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
async 'publish'(args) {
|
|
104
|
+
const pagePath = args[0];
|
|
105
|
+
if (!pagePath) { console.error('Usage: capuzzella publish <path>'); process.exit(1); }
|
|
106
|
+
const data = await request('POST', `/publish/${pagePath}`);
|
|
107
|
+
printJson(data);
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
async 'unpublish'(args) {
|
|
111
|
+
const pagePath = args[0];
|
|
112
|
+
if (!pagePath) { console.error('Usage: capuzzella unpublish <path>'); process.exit(1); }
|
|
113
|
+
const data = await request('DELETE', `/publish/${pagePath}`);
|
|
114
|
+
printJson(data);
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
async 'status'(args) {
|
|
118
|
+
const pagePath = args[0];
|
|
119
|
+
if (!pagePath) { console.error('Usage: capuzzella status <path>'); process.exit(1); }
|
|
120
|
+
const data = await request('GET', `/publish/status/${pagePath}`);
|
|
121
|
+
printJson(data);
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
async 'schedule list'(args) {
|
|
125
|
+
const showAll = args[0] === 'all';
|
|
126
|
+
const url = showAll ? '/publish/schedule' : '/publish/schedule?status=pending';
|
|
127
|
+
const data = await request('GET', url);
|
|
128
|
+
if (data.schedules && data.schedules.length === 0) {
|
|
129
|
+
console.log(showAll ? 'No scheduled publishes.' : 'No pending scheduled publishes.');
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
for (const s of data.schedules) {
|
|
133
|
+
const dt = new Date(s.publish_at * 1000).toISOString();
|
|
134
|
+
console.log(` #${s.id} ${s.page_path} → ${dt} [${s.status}]`);
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
async 'schedule add'(args) {
|
|
139
|
+
const pagePath = args[0];
|
|
140
|
+
const atIdx = args.indexOf('--at');
|
|
141
|
+
const publishAt = atIdx !== -1 ? args.slice(atIdx + 1).join(' ') : null;
|
|
142
|
+
if (!pagePath || !publishAt) {
|
|
143
|
+
console.error('Usage: capuzzella schedule add <path> --at <ISO datetime>');
|
|
144
|
+
console.error('Example: capuzzella schedule add index.html --at 2026-03-15T09:00:00Z');
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
const data = await request('POST', '/publish/schedule', { pagePath, publishAt });
|
|
148
|
+
if (data.success) {
|
|
149
|
+
const s = data.schedule;
|
|
150
|
+
const dt = new Date(s.publish_at * 1000).toISOString();
|
|
151
|
+
console.log(`Scheduled #${s.id}: ${s.page_path} will publish at ${dt}`);
|
|
152
|
+
} else {
|
|
153
|
+
printJson(data);
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
async 'schedule cancel'(args) {
|
|
158
|
+
const id = args[0];
|
|
159
|
+
if (!id) { console.error('Usage: capuzzella schedule cancel <id>'); process.exit(1); }
|
|
160
|
+
const data = await request('DELETE', `/publish/schedule/${id}`);
|
|
161
|
+
if (data.success) {
|
|
162
|
+
console.log(`Schedule #${id} cancelled.`);
|
|
163
|
+
} else {
|
|
164
|
+
printJson(data);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
async 'schedule get'(args) {
|
|
169
|
+
const id = args[0];
|
|
170
|
+
if (!id) { console.error('Usage: capuzzella schedule get <id>'); process.exit(1); }
|
|
171
|
+
const data = await request('GET', `/publish/schedule/${id}`);
|
|
172
|
+
printJson(data);
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
async 'keys list'() {
|
|
176
|
+
const data = await request('GET', '/settings/api-keys');
|
|
177
|
+
printJson(data);
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
async 'keys create'(args) {
|
|
181
|
+
const nameIdx = args.indexOf('--name');
|
|
182
|
+
const roleIdx = args.indexOf('--role');
|
|
183
|
+
const limitIdx = args.indexOf('--rate-limit');
|
|
184
|
+
const name = nameIdx !== -1 ? args[nameIdx + 1] : null;
|
|
185
|
+
const role = roleIdx !== -1 ? args[roleIdx + 1] : null;
|
|
186
|
+
const rateLimit = limitIdx !== -1 ? args[limitIdx + 1] : '60';
|
|
187
|
+
if (!name || !role) { console.error('Usage: capuzzella keys create --name <name> --role <role> [--rate-limit <n>]'); process.exit(1); }
|
|
188
|
+
const data = await request('POST', '/settings/api-keys', { name, role, rateLimit });
|
|
189
|
+
if (data.key) {
|
|
190
|
+
console.log('API key created. Copy it now — it will not be shown again:');
|
|
191
|
+
console.log(` ${data.key}`);
|
|
192
|
+
} else {
|
|
193
|
+
printJson(data);
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
async 'keys revoke'(args) {
|
|
198
|
+
const id = args[0];
|
|
199
|
+
if (!id) { console.error('Usage: capuzzella keys revoke <id>'); process.exit(1); }
|
|
200
|
+
const data = await request('POST', `/settings/api-keys/${id}/revoke`);
|
|
201
|
+
printJson(data);
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
async 'keys delete'(args) {
|
|
205
|
+
const id = args[0];
|
|
206
|
+
if (!id) { console.error('Usage: capuzzella keys delete <id>'); process.exit(1); }
|
|
207
|
+
const data = await request('POST', `/settings/api-keys/${id}/delete`);
|
|
208
|
+
printJson(data);
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
async 'keys audit'(args) {
|
|
212
|
+
const id = args[0];
|
|
213
|
+
if (!id) { console.error('Usage: capuzzella keys audit <id>'); process.exit(1); }
|
|
214
|
+
const data = await request('GET', `/settings/api-keys/${id}/audit`);
|
|
215
|
+
if (!data.entries || data.entries.length === 0) {
|
|
216
|
+
console.log('No audit entries.');
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
for (const e of data.entries) {
|
|
220
|
+
const ts = new Date(e.timestamp).toISOString();
|
|
221
|
+
console.log(` ${ts} ${e.method.padEnd(6)} ${e.path}`);
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
async 'ai-provider get'() {
|
|
226
|
+
const data = await request('GET', '/settings/ai-provider');
|
|
227
|
+
console.log(`Active provider: ${data.activeProvider}`);
|
|
228
|
+
for (const p of data.providers) {
|
|
229
|
+
const status = [];
|
|
230
|
+
if (p.active) status.push('active');
|
|
231
|
+
if (p.configured) status.push('configured');
|
|
232
|
+
const flags = status.length ? ` [${status.join(', ')}]` : '';
|
|
233
|
+
console.log(` ${p.id} (${p.name})${flags}`);
|
|
234
|
+
console.log(` Model: ${p.selectedModel}`);
|
|
235
|
+
console.log(` Available: ${p.models.join(', ')}`);
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
async 'ai-provider set'(args) {
|
|
240
|
+
const providerIdx = args.indexOf('--provider');
|
|
241
|
+
const modelIdx = args.indexOf('--model');
|
|
242
|
+
const keyIdx = args.indexOf('--api-key');
|
|
243
|
+
const provider = providerIdx !== -1 ? args[providerIdx + 1] : null;
|
|
244
|
+
const model = modelIdx !== -1 ? args[modelIdx + 1] : null;
|
|
245
|
+
const apiKey = keyIdx !== -1 ? args[keyIdx + 1] : null;
|
|
246
|
+
if (!provider) {
|
|
247
|
+
console.error('Usage: capuzzella ai-provider set --provider <id> [--model <model>] [--api-key <key>]');
|
|
248
|
+
console.error('Providers: openai, anthropic');
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
const body = { provider };
|
|
252
|
+
if (model) body.model = model;
|
|
253
|
+
if (apiKey) body.apiKey = apiKey;
|
|
254
|
+
const data = await request('POST', '/settings/ai-provider', body);
|
|
255
|
+
if (data.success) {
|
|
256
|
+
console.log(`AI provider set to ${data.activeProvider}, model: ${data.model}`);
|
|
257
|
+
} else {
|
|
258
|
+
printJson(data);
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
async 'email get'() {
|
|
263
|
+
const data = await request('GET', '/settings/resend');
|
|
264
|
+
if (!data.configured) {
|
|
265
|
+
console.log('Transactional email is not configured.');
|
|
266
|
+
} else {
|
|
267
|
+
console.log('Transactional email configuration:');
|
|
268
|
+
console.log(` Domain: ${data.domain}`);
|
|
269
|
+
console.log(` Contact Email: ${data.contactEmail}`);
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
async 'email set'(args) {
|
|
274
|
+
const keyIdx = args.indexOf('--api-key');
|
|
275
|
+
const domainIdx = args.indexOf('--domain');
|
|
276
|
+
const contactIdx = args.indexOf('--contact-email');
|
|
277
|
+
const apiKey = keyIdx !== -1 ? args[keyIdx + 1] : null;
|
|
278
|
+
const domain = domainIdx !== -1 ? args[domainIdx + 1] : null;
|
|
279
|
+
const contactEmail = contactIdx !== -1 ? args[contactIdx + 1] : null;
|
|
280
|
+
if (!domain || !contactEmail) {
|
|
281
|
+
console.error('Usage: capuzzella email set --domain <domain> --contact-email <email> [--api-key <key>]');
|
|
282
|
+
process.exit(1);
|
|
283
|
+
}
|
|
284
|
+
const body = { domain, contactEmail };
|
|
285
|
+
if (apiKey) body.apiKey = apiKey;
|
|
286
|
+
const data = await request('POST', '/settings/resend', body);
|
|
287
|
+
if (data.success) {
|
|
288
|
+
console.log(`Email configured: domain=${data.domain}, contact=${data.contactEmail}`);
|
|
289
|
+
} else {
|
|
290
|
+
printJson(data);
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
async 'email remove'() {
|
|
295
|
+
const data = await request('POST', '/settings/resend', { action: 'remove' });
|
|
296
|
+
if (data.success) {
|
|
297
|
+
console.log('Transactional email configuration removed.');
|
|
298
|
+
} else {
|
|
299
|
+
printJson(data);
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
async 'domains list'() {
|
|
304
|
+
const data = await request('GET', '/settings/custom-domains');
|
|
305
|
+
if (!data.domains || data.domains.length === 0) {
|
|
306
|
+
console.log('No custom domains configured.');
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
for (const d of data.domains) {
|
|
310
|
+
const status = d.verificationStatus === 'verified' ? 'verified' : 'pending';
|
|
311
|
+
console.log(` ${d.name} [${status}] id: ${d.id}`);
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
async 'domains add'(args) {
|
|
316
|
+
const domainName = args[0];
|
|
317
|
+
if (!domainName) {
|
|
318
|
+
console.error('Usage: capuzzella domains add <domain>');
|
|
319
|
+
console.error('Example: capuzzella domains add example.com');
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
const data = await request('POST', '/settings/custom-domains', { domainName });
|
|
323
|
+
if (data.success) {
|
|
324
|
+
console.log(`Custom domain added: ${data.domain.name || domainName}`);
|
|
325
|
+
console.log('Configure a CNAME or A record at your DNS provider to complete setup.');
|
|
326
|
+
} else {
|
|
327
|
+
printJson(data);
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
async 'domains delete'(args) {
|
|
332
|
+
const domainId = args[0];
|
|
333
|
+
if (!domainId) {
|
|
334
|
+
console.error('Usage: capuzzella domains delete <domain-id>');
|
|
335
|
+
console.error('Use "capuzzella domains list" to find domain IDs.');
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
338
|
+
const data = await request('DELETE', `/settings/custom-domains/${domainId}`);
|
|
339
|
+
if (data.success) {
|
|
340
|
+
console.log('Custom domain removed.');
|
|
341
|
+
} else {
|
|
342
|
+
printJson(data);
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
// ---- Argument Parsing ----
|
|
348
|
+
|
|
349
|
+
const args = process.argv.slice(2);
|
|
350
|
+
const USAGE = `Capuzzella CLI
|
|
351
|
+
|
|
352
|
+
Usage: capuzzella <command> [options]
|
|
353
|
+
|
|
354
|
+
Commands:
|
|
355
|
+
whoami Show current key role and identity
|
|
356
|
+
|
|
357
|
+
pages list List all pages
|
|
358
|
+
pages get <path> Get page HTML
|
|
359
|
+
pages save <path> --file <file> Save page from local file
|
|
360
|
+
pages delete <path> Delete a page
|
|
361
|
+
|
|
362
|
+
publish all Publish all drafts
|
|
363
|
+
publish <path> Publish a single page
|
|
364
|
+
unpublish <path> Unpublish a page
|
|
365
|
+
status <path> Check publish status
|
|
366
|
+
|
|
367
|
+
schedule list List pending scheduled publishes
|
|
368
|
+
schedule list all List all scheduled publishes
|
|
369
|
+
schedule add <path> --at <datetime> Schedule a page for future publish
|
|
370
|
+
schedule get <id> Get details of a scheduled publish
|
|
371
|
+
schedule cancel <id> Cancel a pending scheduled publish
|
|
372
|
+
|
|
373
|
+
keys list List API keys
|
|
374
|
+
keys create --name <n> --role <r> Create a new API key
|
|
375
|
+
keys revoke <id> Revoke an API key
|
|
376
|
+
keys delete <id> Delete an API key
|
|
377
|
+
keys audit <id> Show audit log for an API key
|
|
378
|
+
|
|
379
|
+
ai-provider get Show AI provider configuration
|
|
380
|
+
ai-provider set --provider <id> Set active AI provider
|
|
381
|
+
[--model <model>] [--api-key <key>]
|
|
382
|
+
|
|
383
|
+
email get Show transactional email config
|
|
384
|
+
email set --domain <d> Configure transactional emails
|
|
385
|
+
--contact-email <e> [--api-key <k>]
|
|
386
|
+
email remove Remove transactional email config
|
|
387
|
+
|
|
388
|
+
domains list List custom domains
|
|
389
|
+
domains add <domain> Add a custom domain
|
|
390
|
+
domains delete <domain-id> Remove a custom domain
|
|
391
|
+
|
|
392
|
+
Environment:
|
|
393
|
+
CAPUZZELLA_API_KEY API key (required)
|
|
394
|
+
CAPUZZELLA_URL Server URL (default: http://localhost:3000)`;
|
|
395
|
+
|
|
396
|
+
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
397
|
+
console.log(USAGE);
|
|
398
|
+
process.exit(0);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Match two-word commands first, then single-word
|
|
402
|
+
const twoWord = `${args[0]} ${args[1]}`;
|
|
403
|
+
if (commands[twoWord]) {
|
|
404
|
+
await commands[twoWord](args.slice(2));
|
|
405
|
+
} else if (commands[args[0]]) {
|
|
406
|
+
await commands[args[0]](args.slice(1));
|
|
407
|
+
} else {
|
|
408
|
+
console.error(`Unknown command: ${args.join(' ')}`);
|
|
409
|
+
console.log(USAGE);
|
|
410
|
+
process.exit(1);
|
|
411
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "capuzzella",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI for the Capuzzella AI-Powered Website Builder",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"capuzzella": "bin/capuzzella"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"capuzzella",
|
|
14
|
+
"cli",
|
|
15
|
+
"website-builder",
|
|
16
|
+
"ai",
|
|
17
|
+
"cms"
|
|
18
|
+
],
|
|
19
|
+
"author": "Maurice Wipf",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18.0.0"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/mauricewipf/capuzzella-cli.git"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/mauricewipf/capuzzella-cli#readme",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/mauricewipf/capuzzella-cli/issues"
|
|
31
|
+
}
|
|
32
|
+
}
|