@ambasada/skills 0.5.0 → 0.7.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/package.json +1 -1
- package/skills/ambasada-payments/recipes/nextjs.md +1 -1
- package/skills/ambasada-product/SKILL.md +2 -1
- package/skills/ambasada-product/checklists/sellable.md +1 -1
- package/skills/ambasada-product/reference/dev-and-deploy.md +19 -3
- package/skills/ambasada-product/reference/mini-app.md +75 -0
- package/skills/ambasada-product/reference/stack.md +8 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ambasada/skills",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Skills for Claude Code, Cursor and Codex: ambasada-product (build a product that sells in the Hack A Ton Village) and ambasada-payments (TON Checkout, receipts, webhooks).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -88,7 +88,7 @@ export async function buy() {
|
|
|
88
88
|
## Deliver: the webhook
|
|
89
89
|
|
|
90
90
|
```ts
|
|
91
|
-
// app/api/webhook/route.ts (`ambasada webhook set https://<slug>.
|
|
91
|
+
// app/api/webhook/route.ts (`ambasada webhook set https://<slug>.ambasada.dev/api/webhook`)
|
|
92
92
|
import { ambasada } from "../../../lib/ambasada.ts";
|
|
93
93
|
|
|
94
94
|
export async function POST(req: Request) {
|
|
@@ -9,7 +9,7 @@ The Village is the Hack A Ton market. Everything is paid in **TON**: a play curr
|
|
|
9
9
|
bank, **not** the TON blockchain. Visitors spend TON from their Ambasada ID wallet; other teams' agents
|
|
10
10
|
buy from you through the Gateway.
|
|
11
11
|
|
|
12
|
-
A product is **one website** (one Cloudflare Worker) at `https://<your-slug>.
|
|
12
|
+
A product is **one website** (one Cloudflare Worker) at `https://<your-slug>.ambasada.dev`, plus
|
|
13
13
|
**your agent**, which runs the shop (prices, suppliers, deals) while you sleep.
|
|
14
14
|
|
|
15
15
|
For payment details (Checkout, receipts, webhooks, `buy`/`need`, LLM and images) use the
|
|
@@ -35,6 +35,7 @@ All three have the same `ambasada.config.ts`, `agent/` folder and four endpoints
|
|
|
35
35
|
| know where files go, what Next.js features are allowed, where to keep data and secrets | [reference/stack.md](reference/stack.md) |
|
|
36
36
|
| run it locally, put it online, use their own Cloudflare account | [reference/dev-and-deploy.md](reference/dev-and-deploy.md) |
|
|
37
37
|
| make it look good and sell (one page, one offer) | [reference/design.md](reference/design.md) |
|
|
38
|
+
| show up in the Ambasada app, send push notifications, sign people in inside the app | [reference/mini-app.md](reference/mini-app.md) |
|
|
38
39
|
| build in ChatGPT, Lovable, Bolt or v0 and sell from there | [reference/build-elsewhere.md](reference/build-elsewhere.md) |
|
|
39
40
|
| know if it is ready | [checklists/sellable.md](checklists/sellable.md) |
|
|
40
41
|
|
|
@@ -43,7 +43,7 @@ running, except the last section.
|
|
|
43
43
|
|
|
44
44
|
## Online (after `npx ambasada deploy`)
|
|
45
45
|
|
|
46
|
-
- [ ] `https://<slug>.
|
|
46
|
+
- [ ] `https://<slug>.ambasada.dev` loads on a phone.
|
|
47
47
|
- [ ] `npx ambasada offerings list`: every endpoint starts with your public URL, none with `localhost`.
|
|
48
48
|
- [ ] `npx ambasada webhook test` prints `delivered`.
|
|
49
49
|
- [ ] One real payment by someone outside your team (members can't buy from their own company) shows up
|
|
@@ -24,7 +24,7 @@ Try a sale:
|
|
|
24
24
|
`AMBASADA_API_URL=http://127.0.0.1:8788`, `AMBASADA_API_KEY=sk_test_dev` (already in the project's `.env`).
|
|
25
25
|
More on the mock: the ambasada-payments skill, `reference/test-mode.md`.
|
|
26
26
|
|
|
27
|
-
## `ambasada deploy`: online at `<slug>.
|
|
27
|
+
## `ambasada deploy`: online at `<slug>.ambasada.dev`
|
|
28
28
|
|
|
29
29
|
Once, per laptop:
|
|
30
30
|
|
|
@@ -45,7 +45,7 @@ It does, in order:
|
|
|
45
45
|
1. **build**: `opennextjs-cloudflare build` (Next), `vite build` (spa) or a bundle of the Worker (worker);
|
|
46
46
|
2. **smoke test**: runs the built Worker locally in workerd and requests `/` and `/api/receipt`;
|
|
47
47
|
a crash stops the deploy (skip with `--no-smoke` only if you know why it fails);
|
|
48
|
-
3. **upload** to the Village's hosting: the URL is `https://<your-company-slug>.
|
|
48
|
+
3. **upload** to the Village's hosting: the URL is `https://<your-company-slug>.ambasada.dev`,
|
|
49
49
|
with `AMBASADA_API_KEY`, `AMBASADA_API_URL` and `AMBASADA_COMPANY` set for you;
|
|
50
50
|
4. **agent and offerings**: pushes `agent/agent.md` + `agent/settings.json`, and lists your offerings at the new URL.
|
|
51
51
|
|
|
@@ -55,13 +55,29 @@ offerings list), `--dry-run` (print what it would push).
|
|
|
55
55
|
After the first deploy, point the webhook at it and deploy again so the Worker gets the secret:
|
|
56
56
|
|
|
57
57
|
```sh
|
|
58
|
-
npx ambasada webhook set https://<slug>.
|
|
58
|
+
npx ambasada webhook set https://<slug>.ambasada.dev/api/webhook
|
|
59
59
|
npx ambasada webhook test # should print "delivered"
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
Limits on Village hosting: 10 MB compressed bundle, 1000 static files, short CPU time per request
|
|
63
63
|
(waiting on `fetch` or the LLM does not count). Heavy work belongs in an API you call, not in the Worker.
|
|
64
64
|
|
|
65
|
+
## `ambasada dev --tunnel`: your laptop, live in the Village
|
|
66
|
+
|
|
67
|
+
When real buyers, Checkout, webhooks or other teams' agents need to reach the version on your laptop
|
|
68
|
+
(before deploying, or to debug a sale), log in once, then:
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
npx ambasada dev --tunnel # your dev server, against the real Village, at https://<slug>-dev.ambasada.dev
|
|
72
|
+
npx ambasada tunnel --port 5173 # or: only the tunnel, for a server you started yourself
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Until Ctrl+C, `https://<slug>-dev.ambasada.dev` is your product URL. Offerings and the webhook point
|
|
76
|
+
there, and the Shop tab shows "Live from your laptop". On Ctrl+C everything goes back to where it was.
|
|
77
|
+
There is no mock in this mode, so payments are real TON. The first time takes up to 2 minutes while
|
|
78
|
+
the address is published; after that it is instant. Use `--no-register` to share the URL without
|
|
79
|
+
moving offerings. Only one terminal per company at a time: `--force` takes it over. More: docs/TUNNEL.md.
|
|
80
|
+
|
|
65
81
|
## `ambasada deploy --own`: your own Cloudflare account
|
|
66
82
|
|
|
67
83
|
Runs `wrangler deploy` into your account (you need `npx wrangler login` first), then
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# The product inside the Ambasada app (mini app)
|
|
2
|
+
|
|
3
|
+
Every product on `https://<slug>.ambasada.dev` is also a **mini app**: it can open inside the Ambasada
|
|
4
|
+
mobile app, sign people in without a consent screen and send them push notifications. The template does
|
|
5
|
+
the wiring; this page says what is there and how to use it. Platform spec: docs/MINI-APPS.md in the repo.
|
|
6
|
+
|
|
7
|
+
## Is it listed?
|
|
8
|
+
|
|
9
|
+
`ambasada deploy` registers the product with the Ambasada hub and checks it. It prints one line:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
✓ in the Ambasada app: Listed for the event
|
|
13
|
+
! in the Ambasada app: Unlisted · needs: icon, viewport
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Unlisted products work, but only the team sees them in the app. Fix what `needs` says and deploy again,
|
|
17
|
+
or run `npx ambasada check https://<slug>.ambasada.dev` (or `npx ambasada check` for the local folder).
|
|
18
|
+
The checks: `/.well-known/ambasada.json` valid, `icon.png` a 1024×1024 PNG with no transparency, the page
|
|
19
|
+
has `<meta name="viewport" content="width=device-width, initial-scale=1">`, no dark-only theme, the
|
|
20
|
+
first page answers in under 3 s, `/api/auth/callback` exists, the notifications webhook answers a signed ping.
|
|
21
|
+
Connected tools can do the same through MCP: `app_status`, `app_check`.
|
|
22
|
+
|
|
23
|
+
## Look like the app: @ambasada/ui
|
|
24
|
+
|
|
25
|
+
The templates import `@ambasada/ui` (tokens, Inter + JetBrains Mono, `ui-card`, `ui-button`, `ui-row`,
|
|
26
|
+
`ui-pill`, `ui-sheet`, `ui-stepper`, `ui-empty`, `ui-skeleton`). Use those classes before writing CSS.
|
|
27
|
+
React: `import { Card, Button, Sheet } from "@ambasada/ui/react"`. Plain HTML: one
|
|
28
|
+
`<link rel="stylesheet" href="https://unpkg.com/@ambasada/ui@0.5/dist/ambasada-ui.css">`.
|
|
29
|
+
Leave space for the app's bar: the page uses `--ambasada-safe-area-*` and `--ambasada-host-bar`.
|
|
30
|
+
Light theme only; one red (primary) button per screen.
|
|
31
|
+
|
|
32
|
+
## Notifications
|
|
33
|
+
|
|
34
|
+
Already wired in every template:
|
|
35
|
+
|
|
36
|
+
| Piece | Where |
|
|
37
|
+
|---|---|
|
|
38
|
+
| manifest: `"scopes": ["identity", "notifications"]`, `"notifications": { "webhook": "/api/ambasada/webhook" }`, `"privacy_url": "/privacy"` | `public/.well-known/ambasada.json` (`web/` in the worker template) |
|
|
39
|
+
| the webhook: verifies the hub's signature, saves `token ↔ Ambasada user id` in `ambasada.store` | `app/api/ambasada/webhook/route.ts` (Next), `api/ambasada-webhook.ts` (spa, worker) |
|
|
40
|
+
| `notifyUser(village, userId, { … })`: push to one person if they turned notifications on | `lib/notifications.ts` |
|
|
41
|
+
| the privacy page the scopes need | `app/privacy/page.tsx`, `public/privacy.html`, `web/privacy.html` |
|
|
42
|
+
|
|
43
|
+
Send one when something the person waits for happens (their order is ready), never as marketing:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { notifyUser } from "../lib/notifications.ts";
|
|
47
|
+
|
|
48
|
+
const user = await ambasada().auth.user(req); // signed in with Ambasada
|
|
49
|
+
if (user) await notifyUser(ambasada(), user.id, {
|
|
50
|
+
notification_id: `order-${id}-ready`, // unique per message; repeats are dropped for 24 h
|
|
51
|
+
title: "Your translation is ready", // ≤ 40 characters
|
|
52
|
+
body: "Tap to read it.", // ≤ 140
|
|
53
|
+
url: `/orders/${id}`, // a path on your site: where the tap opens
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
People turn notifications on in the app (the app asks; the page can ask too with
|
|
58
|
+
`notifications.request()` from `@ambasada/miniapp`, from a button). The secret (`AMBASADA_APP_SECRET`) is
|
|
59
|
+
injected by `ambasada deploy`; never put it in the browser.
|
|
60
|
+
|
|
61
|
+
## Signed in without a consent screen
|
|
62
|
+
|
|
63
|
+
Inside the Ambasada app the page signs in silently: `auth.signIn()` from `@ambasada/miniapp` gets a short
|
|
64
|
+
token from the app and POSTs it to `/api/auth/shell`, which sets the same session as "Sign in with
|
|
65
|
+
Ambasada". Server code doesn't change: `currentUser()` / `ambasada().auth.user(req)` work either way.
|
|
66
|
+
On the web, the button and consent screen stay. Silent sign-in needs the app to be Verified; until then
|
|
67
|
+
`signIn()` resolves `null` and the button shows.
|
|
68
|
+
|
|
69
|
+
## Try it locally
|
|
70
|
+
|
|
71
|
+
`npx ambasada dev` plays the hub: when your product starts it sends a signed `ping` and
|
|
72
|
+
`notifications_enabled` to your webhook (the token lands in the store as
|
|
73
|
+
`ambasada/notifications/user_fake_visitor`), and its `/api/apps/notify` accepts pushes for that token
|
|
74
|
+
(they show in the terminal). Open `http://localhost:3000/?ambasada_shell=mock` to pretend to be inside the
|
|
75
|
+
app: the fake shell signs you in silently as "Fake Visitor".
|
|
@@ -14,9 +14,13 @@ my-shop/
|
|
|
14
14
|
app/api/webhook/route.ts checkout.paid arrives here, signed: deliver here
|
|
15
15
|
app/api/receipt/route.ts verifies a pay.js receipt from the browser
|
|
16
16
|
app/api/translate/route.ts the paid offering: agents call it with a receipt
|
|
17
|
-
app/api/auth/*/route.ts Sign in with Ambasada: login, callback, logout, me
|
|
17
|
+
app/api/auth/*/route.ts Sign in with Ambasada: login, callback, logout, me, shell (silent, in the app)
|
|
18
|
+
app/api/ambasada/webhook/ the Ambasada app's events: who turned notifications on (reference/mini-app.md)
|
|
18
19
|
app/account.tsx the header: "Sign in with Ambasada", or the person's name and avatar
|
|
20
|
+
app/miniapp.tsx inside the Ambasada app: ready() and silent sign-in
|
|
21
|
+
app/privacy/page.tsx what you keep about people (the manifest's privacy_url)
|
|
19
22
|
lib/ambasada.ts the Village client for server code (reads the Worker env)
|
|
23
|
+
lib/notifications.ts notifyUser(): a push to one person who turned notifications on
|
|
20
24
|
offerings.ts what agents can buy: name, price, endpoint, input and output
|
|
21
25
|
agent/agent.md your agent's instructions, in plain English
|
|
22
26
|
agent/settings.json its model, how often it wakes, spending limits
|
|
@@ -112,8 +116,9 @@ File URLs are public: don't put private data in them.
|
|
|
112
116
|
|
|
113
117
|
- Local: `.env` in the project (Cloudflare's `.dev.vars` works too). The template writes it with test
|
|
114
118
|
values for `ambasada dev`. Both are in `.gitignore`; keep them there.
|
|
115
|
-
- Deployed: `ambasada deploy` injects `AMBASADA_API_KEY`, `AMBASADA_API_URL`, `AMBASADA_COMPANY
|
|
116
|
-
Sign in with Ambasada credentials (`AMBASADA_AUTH_*`)
|
|
119
|
+
- Deployed: `ambasada deploy` injects `AMBASADA_API_KEY`, `AMBASADA_API_URL`, `AMBASADA_COMPANY`, the
|
|
120
|
+
Sign in with Ambasada credentials (`AMBASADA_AUTH_*`) and the mini-app secret (`AMBASADA_APP_SECRET`,
|
|
121
|
+
with `AMBASADA_HUB_URL` and `AMBASADA_APP_ORIGIN`) for you.
|
|
117
122
|
With `--own`, set them yourself: `npx wrangler secret put AMBASADA_API_KEY`.
|
|
118
123
|
- Other services' keys (an OpenAI key, say): same place, read on the server only.
|
|
119
124
|
- Anything named `NEXT_PUBLIC_*` is copied into the browser. Never put a key there.
|