@ambasada/skills 0.2.0 → 0.4.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ambasada/skills",
3
- "version": "0.2.0",
3
+ "version": "0.4.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": [
@@ -43,14 +43,50 @@ const res = await village.llm.chat({ model: "deepseek-v4-pro", messages }); //
43
43
  const { url } = await village.images.generate({ prompt: "a lighthouse", model: "nano_banana" });
44
44
  const tools = await village.tools.list(); // curated catalog, TON prices
45
45
  const { output } = await village.tools.call("web.search", { query: "Mamaia weather" });
46
- const file = await village.files.put(bytes, { contentType: "image/png" }); // public URL for other products
46
+ const file = await village.files.put(bytes, { contentType: "image/png" }); // { id, url }: public URL, 0.1 TON
47
47
  ```
48
48
 
49
49
  HTTP: `POST /v1/llm/chat` (OpenAI body), `POST /v1/images { model, prompt }` → `{ url, cost }`,
50
- `GET /v1/tools`, `POST /v1/tools/:id` → `{ output, cost, call_id }`, `POST /v1/files`.
50
+ `GET /v1/tools`, `POST /v1/tools/:id` → `{ output, cost, call_id }`, `POST /v1/files` (raw body +
51
+ content-type, or `{ url }`) → `{ id, url, cost }`.
51
52
  LLM models: `deepseek-v4.1-flash` (default, cheap), `deepseek-v4-pro`, `glm-5.3-flash`.
52
53
  Images: `nano_banana` (2 TON), `gpt-image-2` (8 TON).
53
54
 
55
+ ## Store and hosting
56
+
57
+ `village.store` is your company's JSON key-value store: free, 1 MB per company (keys + values), so a
58
+ hosted product needs no database. `ambasada deploy` hosts the product at `https://<slug>.ambasada.dev`
59
+ with `AMBASADA_API_KEY`, `AMBASADA_API_URL`, `AMBASADA_COMPANY` and `AMBASADA_WEBHOOK_SECRET` already set.
60
+
61
+ ```ts
62
+ await village.store.set("cart:ana", { items: ["translate"] }); // any JSON value
63
+ const cart = await village.store.get<{ items: string[] }>("cart:ana"); // null when not set
64
+ await village.store.delete("cart:ana");
65
+ const { keys, used_bytes } = await village.store.list("cart:");
66
+ ```
67
+
68
+ HTTP: `GET|PUT|DELETE /v1/store/:key` (PUT body = the JSON value), `GET /v1/store?prefix=`;
69
+ `POST /v1/product/deploy` (what `ambasada deploy` calls), `GET /v1/product`, `DELETE /v1/product`.
70
+
71
+ ## Sign in with Ambasada
72
+
73
+ People sign in to your hosted product with their Ambasada ID. There is nothing to set up: `ambasada deploy`
74
+ injects `AMBASADA_AUTH_CLIENT_ID`, `AMBASADA_AUTH_CLIENT_SECRET` and `AMBASADA_AUTH_ISSUER`, and `ambasada dev`
75
+ injects fake ones (the mock signs you in as "Fake Visitor"). The templates already have the routes and a
76
+ header button.
77
+
78
+ ```ts
79
+ // GET /api/auth/login?returnTo=/ GET /api/auth/callback GET /api/auth/logout
80
+ return village.auth.login(req, { returnTo: "/", email: false }); // email: true asks for it on the consent screen
81
+ return village.auth.callback(req); // sets an HttpOnly session cookie
82
+ return village.auth.logout(req);
83
+ const user = await village.auth.user(req); // null, or { id, name, avatar_url, builder, builder_since, member_since, pass, email? }
84
+ ```
85
+
86
+ `user.id` (`user_…`) is stable: key your data by it (``village.store.set(`user:${user.id}`, …)``).
87
+ `pass` is `"builder" | "visitor" | "staff" | null` for the current event. Server code only; the browser asks
88
+ `/api/auth/me`. It works on `https://<slug>.ambasada.dev` and under `ambasada dev`, not with `--own` hosting.
89
+
54
90
  ## Errors
55
91
 
56
92
  `{ "error": { "code", "message", call_id?, charged?: false, refunded?, approval_id? } }`. The SDK throws
@@ -14,6 +14,8 @@ 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
18
+ app/account.tsx the header: "Sign in with Ambasada", or the person's name and avatar
17
19
  lib/ambasada.ts the Village client for server code (reads the Worker env)
18
20
  offerings.ts what agents can buy: name, price, endpoint, input and output
19
21
  agent/agent.md your agent's instructions, in plain English
@@ -48,6 +50,23 @@ where `env` comes from `getCloudflareContext()` (deployed); under `next dev` the
48
50
  It reads `AMBASADA_API_KEY`, `AMBASADA_API_URL`, `AMBASADA_COMPANY`, `AMBASADA_WEBHOOK_SECRET`.
49
51
  Everything the client can do is in the ambasada-payments skill.
50
52
 
53
+ ## Who is signed in
54
+
55
+ Every hosted product gets **Sign in with Ambasada** (Ambasada ID) for free: no keys, no settings.
56
+ The template's header already has the button. In server code:
57
+
58
+ ```ts
59
+ import { currentUser } from "../lib/ambasada.ts";
60
+
61
+ const user = await currentUser(); // null, or { id, name, avatar_url, builder, pass, member_since, … }
62
+ if (!user) redirect("/api/auth/login?returnTo=/orders");
63
+ await ambasada().store.set(`orders:${user.id}`, orders); // key per-person data by user.id
64
+ ```
65
+
66
+ In a route handler, `ambasada().auth.user(req)` does the same. Client components fetch `/api/auth/me`.
67
+ Ask for the email only if you need it: `/api/auth/login` calls `auth.login(req, { email: true })`.
68
+ Don't build your own login or accounts table, and don't add Clerk, Auth.js or Supabase Auth.
69
+
51
70
  ## Next.js: allowed and banned
52
71
 
53
72
  Allowed, and enough for any product here:
@@ -93,7 +112,8 @@ File URLs are public: don't put private data in them.
93
112
 
94
113
  - Local: `.env` in the project (Cloudflare's `.dev.vars` works too). The template writes it with test
95
114
  values for `ambasada dev`. Both are in `.gitignore`; keep them there.
96
- - Deployed: `ambasada deploy` injects `AMBASADA_API_KEY`, `AMBASADA_API_URL` and `AMBASADA_COMPANY` for you.
115
+ - Deployed: `ambasada deploy` injects `AMBASADA_API_KEY`, `AMBASADA_API_URL`, `AMBASADA_COMPANY` and the
116
+ Sign in with Ambasada credentials (`AMBASADA_AUTH_*`) for you.
97
117
  With `--own`, set them yourself: `npx wrangler secret put AMBASADA_API_KEY`.
98
118
  - Other services' keys (an OpenAI key, say): same place, read on the server only.
99
119
  - Anything named `NEXT_PUBLIC_*` is copied into the browser. Never put a key there.