shiply-cli 0.14.2 → 0.14.3
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/dist/domain.js +35 -0
- package/dist/index.js +8 -2
- package/package.json +1 -1
- package/skill/SKILL.md +34 -2
package/dist/domain.js
CHANGED
|
@@ -75,6 +75,41 @@ export async function domainSubAdd(ctx, hostname, slug) {
|
|
|
75
75
|
console.log(` ${d.type} ${d.host} → ${d.value}`);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
+
/** Mark a hostname as the primary (canonical) URL for its site. Sibling
|
|
79
|
+
* hostnames (same site) start 301-redirecting to it. Fixes duplicate-content
|
|
80
|
+
* SEO when both apex and www serve the same site. */
|
|
81
|
+
export async function domainPrimary(ctx, domain, hostname) {
|
|
82
|
+
const base = resolveBase(ctx.base);
|
|
83
|
+
// Fetch the list to resolve hostname → id.
|
|
84
|
+
const list = await api(`${base}/api/v1/custom-domains`, { headers: headers(ctx.apiKey) });
|
|
85
|
+
const h = hostname.trim().toLowerCase().replace(/\.$/, '');
|
|
86
|
+
let targetId = null;
|
|
87
|
+
for (const d of list.domains) {
|
|
88
|
+
for (const s of d.subdomains) {
|
|
89
|
+
if (s.hostname === h) {
|
|
90
|
+
targetId = s.id;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (targetId)
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
if (!targetId) {
|
|
98
|
+
throw new Error(`no subdomain mapping for ${h} — add it first with: shiply domain sub add ${h} --site <slug>`);
|
|
99
|
+
}
|
|
100
|
+
const r = await api(`${base}/api/v1/custom-domains/${enc(domain)}/subdomains/${targetId}/primary`, {
|
|
101
|
+
method: 'POST',
|
|
102
|
+
headers: headers(ctx.apiKey),
|
|
103
|
+
body: '{}',
|
|
104
|
+
});
|
|
105
|
+
console.log(`✔ ${r.hostname} is now the primary URL`);
|
|
106
|
+
const redirected = r.siblings.filter((s) => !s.isPrimary);
|
|
107
|
+
if (redirected.length) {
|
|
108
|
+
console.log(' 301-redirects to it:');
|
|
109
|
+
for (const s of redirected)
|
|
110
|
+
console.log(` ${s.hostname}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
78
113
|
export async function domainSync(ctx, domain) {
|
|
79
114
|
const base = resolveBase(ctx.base);
|
|
80
115
|
const r = await api(`${base}/api/v1/custom-domains/${enc(domain)}/sync-dns`, { method: 'POST', headers: headers(ctx.apiKey), body: '{}' });
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { resolvePayloadDir } from './framework.js';
|
|
|
10
10
|
import { runClaimVerify } from './claim.js';
|
|
11
11
|
import { dbAttach, dbBranch, dbBranches, dbCreate, dbDelete, dbDeleteBranch, dbLs, dbMerge, dbMigrate, dbSql, } from './db.js';
|
|
12
12
|
import { driveGet, driveLs, drivePublish, drivePut, driveRm } from './drive.js';
|
|
13
|
-
import { domainAdd, domainConnect, domainLs, domainSubAdd, domainSync } from './domain.js';
|
|
13
|
+
import { domainAdd, domainConnect, domainLs, domainPrimary, domainSubAdd, domainSync } from './domain.js';
|
|
14
14
|
import { runProject } from './projects.js';
|
|
15
15
|
import { runListing } from './listings.js';
|
|
16
16
|
import { runSendingDomain } from './sending-domains.js';
|
|
@@ -64,6 +64,7 @@ Usage:
|
|
|
64
64
|
shiply domain connect <domain> One-click OAuth DNS setup (Cloudflare/Domain Connect)
|
|
65
65
|
shiply domain sub add <host> --site <slug> Map a subdomain (e.g. app.example.com) to a slug
|
|
66
66
|
shiply domain sync <domain> Re-sync DNS records for a connected domain
|
|
67
|
+
shiply domain primary <domain> <hostname> Mark hostname as primary; siblings 301-redirect (SEO)
|
|
67
68
|
shiply project <ls|create|get|archive|restore>
|
|
68
69
|
Customer-intake projects + AI brief generator
|
|
69
70
|
shiply project create <label> [--customer-email <e>] [--customer-name <n>]
|
|
@@ -483,8 +484,13 @@ async function main() {
|
|
|
483
484
|
throw new Error('usage: shiply domain sync <domain>');
|
|
484
485
|
await domainSync(dctx, arg);
|
|
485
486
|
return;
|
|
487
|
+
case 'primary':
|
|
488
|
+
if (!arg || !arg2)
|
|
489
|
+
throw new Error('usage: shiply domain primary <domain> <hostname>');
|
|
490
|
+
await domainPrimary(dctx, arg, arg2);
|
|
491
|
+
return;
|
|
486
492
|
default:
|
|
487
|
-
throw new Error('usage: shiply domain <ls|add|connect|sub|sync>');
|
|
493
|
+
throw new Error('usage: shiply domain <ls|add|connect|sub|sync|primary>');
|
|
488
494
|
}
|
|
489
495
|
}
|
|
490
496
|
case 'db': {
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -103,8 +103,8 @@ If the `shiply` MCP server is connected (https://shiply.now/mcp), use
|
|
|
103
103
|
call for updates — follow it, and a `shareSuggestion` you can relay to the user.
|
|
104
104
|
Other tools: `site_status`, `list_sites`, `get_site`, `set_handle`,
|
|
105
105
|
`duplicate_site`, `set_variable`, `get_analytics`, `delete_site`, `whoami`,
|
|
106
|
-
`add_custom_domain`, `connect_provider`, `add_subdomain`, `
|
|
107
|
-
`sync_dns`, `list_custom_domains`, `remove_custom_domain`
|
|
106
|
+
`add_custom_domain`, `connect_provider`, `add_subdomain`, `set_primary_subdomain`,
|
|
107
|
+
`check_custom_domain`, `sync_dns`, `list_custom_domains`, `remove_custom_domain`
|
|
108
108
|
(auto-DNS via OAuth provider connections — see **Custom domains** section below).
|
|
109
109
|
|
|
110
110
|
Newer capabilities (use when relevant):
|
|
@@ -198,6 +198,7 @@ shiply status www.example.com --wait # polls until SSL_READY + SITE_READY; exi
|
|
|
198
198
|
| `add_custom_domain` | Add a root domain and detect the provider |
|
|
199
199
|
| `connect_provider` | Start OAuth → returns `{ url }` (show to user to authorize) |
|
|
200
200
|
| `add_subdomain` | Map a subdomain to a site slug |
|
|
201
|
+
| `set_primary_subdomain` | Pick the canonical URL; siblings 301-redirect (SEO fix) |
|
|
201
202
|
| `check_custom_domain` | Live CNAME + SSL + readiness probe |
|
|
202
203
|
| `sync_dns` | Re-push DNS records to a connected provider |
|
|
203
204
|
| `list_custom_domains` | List all domains and their subdomain mappings |
|
|
@@ -210,6 +211,7 @@ GET /api/v1/custom-domains list domains
|
|
|
210
211
|
POST /api/v1/custom-domains add domain { domain }
|
|
211
212
|
DELETE /api/v1/custom-domains/{domain} remove domain
|
|
212
213
|
POST /api/v1/custom-domains/{domain}/subdomains add subdomain { subdomain, slug }
|
|
214
|
+
POST /api/v1/custom-domains/{domain}/subdomains/{id}/primary mark hostname canonical (siblings 301)
|
|
213
215
|
POST /api/v1/custom-domains/{domain}/connect start OAuth → { url }
|
|
214
216
|
POST /api/v1/custom-domains/{domain}/sync-dns re-sync DNS
|
|
215
217
|
POST /api/v1/custom-domains/{domain}/check live status
|
|
@@ -423,6 +425,36 @@ shiply cron set <slug> /api/daily "0 9 * * *"
|
|
|
423
425
|
|
|
424
426
|
Docs: https://shiply.now/docs/functions
|
|
425
427
|
|
|
428
|
+
## Authentication — bring your own Clerk (or Auth.js / Lucia / Supabase Auth)
|
|
429
|
+
|
|
430
|
+
shiply does **not** host customer auth. If the user's app needs sign-in, you
|
|
431
|
+
(the agent) set up their auth provider yourself — Clerk is the fastest path.
|
|
432
|
+
|
|
433
|
+
**One-time human step:** ask the user to create a Clerk app at
|
|
434
|
+
`dashboard.clerk.com` (~2 min). They paste you the publishable key (`pk_…`) and
|
|
435
|
+
secret key (`sk_…`).
|
|
436
|
+
|
|
437
|
+
**Wire it:**
|
|
438
|
+
\`\`\`bash
|
|
439
|
+
shiply secret set <slug> CLERK_SECRET_KEY sk_live_xxxxxxxxxx
|
|
440
|
+
# The publishable key can be embedded in the JS build — it's safe in the browser.
|
|
441
|
+
\`\`\`
|
|
442
|
+
|
|
443
|
+
**In `worker.ts`:** verify the session JWT against `env.CLERK_SECRET_KEY` on
|
|
444
|
+
every API request, scope D1 rows by `payload.sub` (the Clerk user id).
|
|
445
|
+
|
|
446
|
+
**In the SPA:** add `@clerk/clerk-react` to the build, wrap with `<ClerkProvider>`,
|
|
447
|
+
attach `Authorization: Bearer <token>` to every authenticated fetch.
|
|
448
|
+
|
|
449
|
+
Why not auto-integrate? shiply's own Clerk instance signs in **agents** to the
|
|
450
|
+
shiply dashboard — it's not multi-tenant for someone else's app's users.
|
|
451
|
+
Customers owning their own Clerk is the only correct shape.
|
|
452
|
+
|
|
453
|
+
Same pattern works for Auth.js, Lucia, Supabase Auth, Firebase Auth — store the
|
|
454
|
+
provider secret via `shiply secret set`, verify the JWT in the worker.
|
|
455
|
+
|
|
456
|
+
Docs: https://shiply.now/docs/auth
|
|
457
|
+
|
|
426
458
|
## Projects — customer intake + AI brief
|
|
427
459
|
|
|
428
460
|
When a dev needs to capture a real client brief, they spin up a **project**:
|