shiply-cli 0.12.1 → 0.14.1

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/skill/SKILL.md CHANGED
@@ -3,6 +3,18 @@ name: shiply
3
3
  description: Publish static sites to the web instantly with shiply.now and manage them (updates, SSL checks, custom domains, variables). Use when the user asks to publish, host, deploy, share, or update a website/page/demo/report, or wants a live URL for generated files. Triggers - "publish this", "host this", "put this online", "give me a link", "update the site", shiply.
4
4
  ---
5
5
 
6
+ ---
7
+
8
+ > **Skill freshness check.** Shiply ships new capabilities weekly. If this
9
+ > file's "Last updated" below is more than 7 days old, re-install it with
10
+ > `npx -y shiply-cli@latest skill --force` OR fetch `https://shiply.now/skill.md`
11
+ > fresh — your cached copy may be missing features like databases, projects,
12
+ > marketplace, or sending domains.
13
+ >
14
+ > **Last updated: 2026-06-20**
15
+
16
+ ---
17
+
6
18
  # shiply — instant web hosting for agents
7
19
 
8
20
  shiply.now puts files on the web in seconds. No account needed to start:
@@ -251,7 +263,7 @@ rest and surfaced to the site's Worker as `env.DATABASE_URL`.
251
263
 
252
264
  ### D1 (default)
253
265
  ```bash
254
- shiply db create app # provision a D1 (binding APP_DB)
266
+ shiply db create app # provision a D1 (binding SITE_DB)
255
267
  shiply db ls # list (name, provider, size, attached site)
256
268
  shiply db sql app "SELECT 1" # one-shot query against your account
257
269
  shiply db sql app "SELECT * FROM t WHERE id=?1" --params '[42]'
@@ -265,7 +277,7 @@ Run `shiply db create` inside a publish directory: it records the new
265
277
  it. Site code then calls the shim:
266
278
 
267
279
  ```js
268
- fetch('/_shiply/db/APP_DB/query', {
280
+ fetch('/_shiply/db/SITE_DB/query', {
269
281
  method: 'POST',
270
282
  headers: { 'content-type': 'application/json' },
271
283
  body: JSON.stringify({ sql: 'SELECT * FROM posts', params: [] }),
@@ -330,6 +342,180 @@ providers; `db_schema` introspects `sqlite_master` (D1) or
330
342
  `information_schema` (Neon). Full docs:
331
343
  https://shiply.now/docs/databases
332
344
 
345
+ ## Functions (Workers Lite) — webhooks, cron, secrets, full backend (Developer plan)
346
+
347
+ shiply Workers Lite lets a published site include server-side code that runs
348
+ on every request. Use when the user needs:
349
+ - A webhook receiver (Stripe, GitHub, etc.) with raw body + signature verification
350
+ - A cron job (daily reminders, periodic sync, retention emails)
351
+ - A privileged API call using secrets (Stripe key, OpenAI key) without exposing them to the browser
352
+ - An authenticated mutation on D1 / Neon (do the auth check in the function before writing)
353
+
354
+ ### How it works
355
+
356
+ Author `worker.js` (or `worker.ts`) at the publish root. On `shiply publish`,
357
+ shiply compiles (TS) + deploys it as a per-site Cloudflare Worker bound to
358
+ the site's hostname. The worker handles every request; pass static asset
359
+ requests through `env.ASSETS.fetch(request)`.
360
+
361
+ ```ts
362
+ import { verifyStripeSig, json } from 'shiply-runtime'
363
+
364
+ export default {
365
+ async fetch(req: Request, env: Env): Promise<Response> {
366
+ const url = new URL(req.url)
367
+ if (url.pathname === '/api/webhooks/stripe' && req.method === 'POST') {
368
+ const body = await req.text()
369
+ if (!await verifyStripeSig(body, req.headers.get('stripe-signature'), env.STRIPE_WEBHOOK_SECRET)) {
370
+ return new Response('bad sig', { status: 400 })
371
+ }
372
+ // ... handle event, write to env.SITE_DB
373
+ return new Response('ok')
374
+ }
375
+ return env.ASSETS.fetch(req)
376
+ },
377
+ async scheduled(event: ScheduledEvent, env: Env) {
378
+ if (event.cron === '0 9 * * *') { /* daily job */ }
379
+ }
380
+ }
381
+ ```
382
+
383
+ `.shiply/crons.json` declares cron schedules. `.shiply/secrets.json` declares
384
+ secret names (values set via CLI/MCP, never reach the publish payload).
385
+
386
+ ### Bindings available in `env`
387
+
388
+ - `env.ASSETS` — fall through to static (`env.ASSETS.fetch(request)`)
389
+ - `env.SITE_DB` — attached D1 (if `shiply db attach` was run)
390
+ - `env.<NAME>` — each shiply Variable becomes a plain-text env var
391
+ - `env.<NAME>` — each secret set via `set_secret` is encrypted, accessible as env var
392
+
393
+ ### MCP tools
394
+
395
+ | Tool | Purpose |
396
+ |---|---|
397
+ | `deploy_function` | Deploy a Worker function (alternative to publish auto-detection) |
398
+ | `get_function` | Read the deployed source + metadata |
399
+ | `remove_function` | Strip function + secrets + crons; fall back to static |
400
+ | `set_secret` / `list_secrets` / `remove_secret` | Manage CF Worker secrets |
401
+ | `set_cron` / `list_crons` / `remove_cron` | Manage cron triggers |
402
+ | `get_function_logs` | Deep-link to CF dashboard for live tail |
403
+
404
+ ### CLI
405
+
406
+ ```bash
407
+ shiply publish . # auto-detects worker.js + crons.json + secrets.json
408
+ shiply function deploy <slug> # alternative: upload worker.js without re-publishing
409
+ shiply function deploy <slug> --ts # uploads worker.ts (server-side compile)
410
+ shiply secret set <slug> STRIPE_KEY sk_xxx # set secret value
411
+ shiply cron set <slug> /api/daily "0 9 * * *"
412
+ ```
413
+
414
+ ### REST
415
+
416
+ `POST/GET/DELETE /api/v1/sites/{slug}/function` · `POST/GET /api/v1/sites/{slug}/secrets` · `DELETE /api/v1/sites/{slug}/secrets/{name}` · `GET/POST/DELETE /api/v1/sites/{slug}/crons`
417
+
418
+ ### Limits + plan
419
+
420
+ - 1 MB compiled script size, 30s CPU per request, V8 isolate runtime (no Node APIs)
421
+ - 50 secrets, 20 cron triggers per site
422
+ - Plan-gated to Developer: Founder/Hobby see `402 payment_required`. Upgrade: shiply.now/dashboard/plan
423
+
424
+ Docs: https://shiply.now/docs/functions
425
+
426
+ ## Projects — customer intake + AI brief
427
+
428
+ When a dev needs to capture a real client brief, they spin up a **project**:
429
+ the dashboard (or `shiply project create`) mints a one-URL intake form to
430
+ share with the customer. The customer fills a 10-step wizard, attaches
431
+ files, hits submit. An AI brief generator (MiniMax-M2 with Anthropic
432
+ fallback) turns the answers into a structured brief the dev reviews and
433
+ edits inline. All files flow into a per-project drive folder.
434
+
435
+ MCP tools:
436
+ ```
437
+ list_projects — your projects (filter status/q)
438
+ create_project — start a new intake; pass customerName/customerEmail to email them the link
439
+ get_project — full project + brief
440
+ update_brief — patch brief jsonb (after AI generation)
441
+ regenerate_brief — re-run AI from current intake responses
442
+ archive_project / restore_project — lifecycle
443
+ list_project_files — files uploaded by the customer
444
+ resend_intake_invite — re-email the customer their intake link (requires customerEmail on project)
445
+ ```
446
+
447
+ REST: `POST/GET /api/v1/projects · GET/PATCH /api/v1/projects/{id} · POST /api/v1/projects/{id}/regenerate-brief`
448
+ (archive: `PATCH /api/v1/projects/{id}` with `{"status":"archived"}` — no
449
+ dedicated `/archive` route).
450
+
451
+ CLI: `shiply project ls · shiply project create <label> [--customer-email <e>] [--customer-name <n>] · shiply project get <id> · shiply project archive <id>`
452
+
453
+ Docs: https://shiply.now/docs/projects
454
+
455
+ ## Marketplace — sell built sites
456
+
457
+ Devs can list any owned site for sale: set a price + short pitch, choose
458
+ standard or custom terms, pick a jurisdiction. Stripe Connect Express
459
+ handles seller payouts — sellers complete a one-time onboarding before
460
+ listing (`get_connect_status` returns the onboarding URL when needed).
461
+ Buyers pay via Stripe Checkout; on `checkout.session.completed` the
462
+ webhook flips the order to `paid` and transfers site ownership
463
+ atomically. Refunds are available within the order's refund window
464
+ (`refundExpiresAt`); a refund reverts ownership to the seller.
465
+
466
+ Prices are passed as `priceCents` (whole-dollar cents between 100 and
467
+ 999900). `termsMode='standard'` uses shiply's template; `'custom'`
468
+ requires `termsCustom` ≥50 chars.
469
+
470
+ MCP tools:
471
+ ```
472
+ list_listings — my listings
473
+ create_listing — list a site (siteSlug, priceCents, pitch?, termsMode, jurisdiction, ...)
474
+ update_listing — change price/pitch/status (draft|live|paused)
475
+ delete_listing — pulls off marketplace (sets status=draft)
476
+ list_my_sales — orders where I'm the seller
477
+ list_my_orders — orders where I'm the buyer
478
+ refund_order — issue Stripe refund (within refund window; goes back to buyer)
479
+ get_connect_status — Stripe Connect onboarding state + actionable URL
480
+ ```
481
+
482
+ REST: `POST /api/v1/listings · PATCH /api/v1/listings/{id} · POST /api/v1/listings/{id}/checkout · POST /api/v1/orders/{id}/refund · GET /api/v1/connect/status`
483
+ (no GET listing index over REST yet — use the MCP `list_listings` /
484
+ `list_my_sales` / `list_my_orders` tools for machine output, or the
485
+ dashboard `/dashboard/sales` page for humans.)
486
+
487
+ CLI: `shiply listing ls · shiply listing create <site-slug> --price <cents> --jurisdiction "<region>" · shiply listing rm <slug> --id <listing-id>`
488
+
489
+ **Stripe Connect note:** Sellers must complete one-time Stripe Connect
490
+ onboarding before listing. Call `get_connect_status` first — when status
491
+ isn't `'ready'` it returns an `onboardingUrl` you should hand to the
492
+ user as a clickable link.
493
+
494
+ Docs: https://shiply.now/docs/marketplace
495
+
496
+ ## Sending domains — outbound email on your domain
497
+
498
+ For agents that want shiply to send emails (demand-test broadcasts,
499
+ project intake invites, transactional notifications) from a custom
500
+ verified domain instead of the shared shiply pool. Backed by Resend —
501
+ add the DNS records they return at your registrar, then call
502
+ `verify_sending_domain` to re-check.
503
+
504
+ MCP tools:
505
+ ```
506
+ list_sending_domains — all your sending domains
507
+ add_sending_domain — start: returns DNS records to add at your registrar
508
+ verify_sending_domain — re-check DNS after adding records
509
+ remove_sending_domain — tear down
510
+ ```
511
+
512
+ REST: `GET/POST /api/v1/sending-domains · POST /api/v1/sending-domains/{id}/verify · DELETE /api/v1/sending-domains/{id}`
513
+
514
+ CLI: `shiply sending-domain ls · shiply sending-domain add <domain> · shiply sending-domain verify <id> · shiply sending-domain rm <id> --yes`
515
+
516
+ Cannot be a `shiply.now` subdomain. Verified status flips once SPF +
517
+ DKIM (+ MX for inbound) all check out.
518
+
333
519
  ## Make a site private (paid plans)
334
520
  To password-protect or restrict a site: PATCH /api/v1/publishes/<slug>/access
335
521
  with {"mode":"password","password":"..."} or {"mode":"restricted",