@ambasada/sdk 0.0.0-stage → 0.1.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.
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Receipt verification. A paid call reaches a seller with `x-village-receipt`: an Ed25519 JWT signed
3
+ * by the village, committing to the buyer, seller, offering, amount and the SHA-256 of the exact body.
4
+ * The seller checks it offline against `<api>/.well-known/jwks.json` (cached), no round-trip.
5
+ */
6
+ import type { CheckoutReceiptClaims, Jwk, ReceiptClaims } from "./types.js";
7
+ import { type HeaderSource } from "./webhook.js";
8
+ export declare const RECEIPT_HEADER = "x-village-receipt";
9
+ export type KeySource = Jwk[] | (() => Promise<Jwk[]>);
10
+ /** A fetch `Request`, or a Node / Express request (raw body in `body` / `rawBody`, or an unread stream). */
11
+ export type IncomingRequest = Request | {
12
+ headers: HeaderSource;
13
+ body?: unknown;
14
+ rawBody?: unknown;
15
+ [Symbol.asyncIterator]?: () => AsyncIterator<unknown>;
16
+ };
17
+ export interface Incoming {
18
+ headers: HeaderSource;
19
+ body: Uint8Array;
20
+ }
21
+ /** Reads headers and the raw body without consuming a fetch `Request` (it is cloned). */
22
+ export declare function readIncoming(req: IncomingRequest): Promise<Incoming>;
23
+ export interface CallReceiptCheck {
24
+ keys: KeySource;
25
+ /** Your company id: required, so a receipt bought from someone else can't be replayed at you. */
26
+ seller: string;
27
+ /** `<seller>/<offering>`, when you want to pin the offering. */
28
+ offering?: string;
29
+ issuer?: string;
30
+ now?: number;
31
+ }
32
+ /** Verifies a call receipt token against the raw body. Throws `ReceiptInvalidError`. */
33
+ export declare function verifyCallReceipt(token: string | null, body: Uint8Array, check: CallReceiptCheck): Promise<ReceiptClaims>;
34
+ export interface CheckoutReceiptCheck {
35
+ keys: KeySource;
36
+ seller: string;
37
+ session?: string;
38
+ issuer?: string;
39
+ now?: number;
40
+ clockSkewSeconds?: number;
41
+ }
42
+ /** Verifies a checkout receipt (from pay.js `village:paid` or `session.receipt`) with `@ambasada/protocol`. Throws `ReceiptInvalidError`. */
43
+ export declare function verifyCheckoutReceiptToken(token: string | null | undefined, check: CheckoutReceiptCheck): Promise<CheckoutReceiptClaims>;
44
+ /** Remembers call ids until their receipt expires, so one receipt pays for one call (per process). */
45
+ export declare class ReplayGuard {
46
+ private seen;
47
+ /** Returns false if the call id was already used. */
48
+ claim(callId: string, exp: number): boolean;
49
+ }
50
+ export declare function receiptHeader(headers: HeaderSource): string | null;
@@ -0,0 +1,321 @@
1
+ /** Wire types. TON amounts are decimal numbers (`1`, `0.5`). */
2
+ /** Ed25519 public key as served by `/.well-known/jwks.json`. */
3
+ export interface Jwk {
4
+ kty: "OKP";
5
+ crv: "Ed25519";
6
+ x: string;
7
+ d?: string;
8
+ kid?: string;
9
+ alg?: "EdDSA";
10
+ use?: "sig";
11
+ }
12
+ /** What a seller learns from a Gateway call receipt (`x-village-receipt`). Same shape as `@ambasada/protocol`. */
13
+ export interface ReceiptClaims {
14
+ iss: string;
15
+ jti: string;
16
+ call_id: string;
17
+ /** Buyer company id. */
18
+ buyer: string;
19
+ /** Your company id. */
20
+ seller: string;
21
+ /** `<seller>/<offering name>` */
22
+ offering: string;
23
+ /** TON */
24
+ amount: number;
25
+ /** 1 for a direct buy; forward it with `buy(…, { parent })`. */
26
+ depth: number;
27
+ /** TON you may still spend downstream on behalf of this call. */
28
+ budget?: number;
29
+ deal_id?: string;
30
+ body_sha256: string;
31
+ iat: number;
32
+ exp: number;
33
+ }
34
+ /** A verified paid call: the receipt plus the parsed JSON input. */
35
+ export interface PaidCall<Input = any> extends ReceiptClaims {
36
+ input: Input;
37
+ }
38
+ /** A checkout receipt (pay.js `village:paid`, or `session.receipt`). */
39
+ export interface CheckoutReceiptClaims {
40
+ iss: string;
41
+ jti: string;
42
+ kind: "checkout";
43
+ session: string;
44
+ /** The person's pseudonymous wallet id (`p_…`). */
45
+ buyer: string;
46
+ seller: string;
47
+ amount: number;
48
+ description: string;
49
+ ref?: string;
50
+ log_index: number;
51
+ iat: number;
52
+ exp: number;
53
+ }
54
+ export interface CheckoutInput {
55
+ /** TON, 0.1–500. Or give `offering` to charge one of your offerings' price. */
56
+ amount?: number;
57
+ offering?: string;
58
+ /** Shown to the person and written to the public ledger as the memo. No personal data. */
59
+ description: string;
60
+ /** `{CHECKOUT_SESSION_ID}` anywhere in the URL is replaced; otherwise `?session=cs_…` is appended. */
61
+ success_url: string;
62
+ cancel_url?: string;
63
+ client_reference_id?: string;
64
+ metadata?: Record<string, string>;
65
+ /** Seconds, 300–86400 (default 3600). */
66
+ expires_in?: number;
67
+ }
68
+ export interface CheckoutSession {
69
+ id: string;
70
+ object: "checkout.session";
71
+ url: string;
72
+ status: "open" | "paid" | "expired" | (string & {});
73
+ amount: number;
74
+ description: string;
75
+ offering: string | null;
76
+ /** The payment link it came from (`pl_…`), if any. */
77
+ link: string | null;
78
+ client_reference_id: string | null;
79
+ metadata: Record<string, string>;
80
+ success_url: string;
81
+ cancel_url: string | null;
82
+ buyer: string | null;
83
+ paid_at: string | null;
84
+ log_index: number | null;
85
+ receipt: string | null;
86
+ created_at: string;
87
+ expires_at: string;
88
+ [key: string]: unknown;
89
+ }
90
+ export interface PaymentLinkInput {
91
+ amount?: number;
92
+ offering?: string;
93
+ description: string;
94
+ success_url: string;
95
+ cancel_url?: string;
96
+ metadata?: Record<string, string>;
97
+ }
98
+ export interface PaymentLink {
99
+ id: string;
100
+ object: "checkout.link";
101
+ url: string;
102
+ active: boolean;
103
+ amount: number;
104
+ description: string;
105
+ offering: string | null;
106
+ metadata: Record<string, string>;
107
+ success_url: string;
108
+ cancel_url: string | null;
109
+ created_at: string;
110
+ [key: string]: unknown;
111
+ }
112
+ export interface WebhookDelivery {
113
+ id: string;
114
+ type: string;
115
+ status: "pending" | "delivered" | "failed" | (string & {});
116
+ attempts: number;
117
+ [key: string]: unknown;
118
+ }
119
+ export interface WebhookEvent<Data = any> {
120
+ /** `webhook-id` header: dedupe on it, retries reuse it. */
121
+ id: string;
122
+ type: "checkout.paid" | "webhook.test" | (string & {});
123
+ timestamp: string;
124
+ data: Data;
125
+ }
126
+ export interface BuyOptions {
127
+ /** Refuse (PRICE_ABOVE_MAX) if the offering costs more than this, in TON. */
128
+ maxPrice?: number;
129
+ /** Retries with the same key never charge twice. Default: a random key per call (reused by the SDK's own retry). */
130
+ idempotencyKey?: string;
131
+ /** Inside a paid handler: forward the chain depth and remaining budget of the call you are serving. */
132
+ parent?: Pick<ReceiptClaims, "depth" | "budget">;
133
+ /** TON this call may spend downstream (`x-village-budget`). */
134
+ budget?: number;
135
+ /** An approved approval id, after APPROVAL_REQUIRED. */
136
+ approval?: string;
137
+ /** Why you are buying; shown to your team on approval requests. */
138
+ reason?: string;
139
+ signal?: AbortSignal;
140
+ }
141
+ export interface BuyResult<Output = any> {
142
+ output: Output;
143
+ callId: string | null;
144
+ /** TON charged. */
145
+ amount: number | null;
146
+ /** The signed receipt for the call (you hold it too). */
147
+ receipt: string | null;
148
+ /** Position in the public ledger log. */
149
+ logIndex: number | null;
150
+ /** True when the Gateway replayed a previous result for the same idempotency key (no new charge). */
151
+ replayed: boolean;
152
+ /** For `need()`: which offering served it (`company/name`), if the Gateway says. */
153
+ offering: string | null;
154
+ }
155
+ export type ChatModel = "deepseek-v4.1-flash" | "deepseek-v4-pro" | "glm-5.3-flash" | (string & {});
156
+ export interface ChatMessage {
157
+ role: "system" | "user" | "assistant" | "tool";
158
+ content: string;
159
+ [key: string]: unknown;
160
+ }
161
+ /** OpenAI-compatible chat request. */
162
+ export interface ChatRequest {
163
+ model?: ChatModel;
164
+ messages: ChatMessage[];
165
+ temperature?: number;
166
+ max_tokens?: number;
167
+ response_format?: {
168
+ type: "text" | "json_object";
169
+ } | Record<string, unknown>;
170
+ [key: string]: unknown;
171
+ }
172
+ export interface ChatResponse {
173
+ id: string;
174
+ object: "chat.completion";
175
+ model: string;
176
+ choices: {
177
+ index: number;
178
+ message: ChatMessage;
179
+ finish_reason: string | null;
180
+ }[];
181
+ usage?: {
182
+ prompt_tokens: number;
183
+ completion_tokens: number;
184
+ total_tokens: number;
185
+ };
186
+ [key: string]: unknown;
187
+ }
188
+ export interface ImageRequest {
189
+ model?: "nano_banana" | "gpt-image-2" | (string & {});
190
+ prompt: string;
191
+ }
192
+ export interface ImageResult {
193
+ /** Re-hosted in the village files store; safe to pass to other products. */
194
+ url: string;
195
+ /** TON charged. */
196
+ cost: number;
197
+ call_id: string;
198
+ }
199
+ export interface ToolListing {
200
+ id: string;
201
+ name: string;
202
+ description: string;
203
+ price_ton?: number;
204
+ price?: number;
205
+ unit?: string;
206
+ input?: unknown;
207
+ output?: unknown;
208
+ [key: string]: unknown;
209
+ }
210
+ export interface ToolResult<Output = any> {
211
+ output: Output;
212
+ callId: string | null;
213
+ /** TON charged. */
214
+ amount: number | null;
215
+ replayed: boolean;
216
+ }
217
+ export interface FileOptions {
218
+ contentType?: string;
219
+ ttlSeconds?: number;
220
+ }
221
+ /** Offering field schema shorthand: `{ text: "string", to: "string", tone: "string?" }`. */
222
+ export type FieldSchema = Record<string, string>;
223
+ export interface ListingInput {
224
+ description: string;
225
+ /** TON, 0.1–500. */
226
+ price: number;
227
+ /** Absolute URL the Gateway POSTs paid calls to. */
228
+ endpoint: string;
229
+ input?: FieldSchema | null;
230
+ output?: FieldSchema | null;
231
+ timeout_ms?: number;
232
+ status?: "open" | "paused";
233
+ preview?: boolean;
234
+ }
235
+ export interface Listing extends ListingInput {
236
+ id: string;
237
+ company: string;
238
+ name: string;
239
+ version: number;
240
+ [key: string]: unknown;
241
+ }
242
+ export interface Wallet {
243
+ account: string;
244
+ balance: number;
245
+ held: number;
246
+ available: number;
247
+ }
248
+ export interface LedgerEntry {
249
+ seq: number;
250
+ id: string;
251
+ ts: string;
252
+ kind: string;
253
+ direction: "in" | "out";
254
+ counterparty: string;
255
+ amount: number;
256
+ balance_after: number;
257
+ ref: string | null;
258
+ memo: string | null;
259
+ hold_id?: string | null;
260
+ log_index?: number | null;
261
+ [key: string]: unknown;
262
+ }
263
+ export interface LedgerPage {
264
+ entries: LedgerEntry[];
265
+ /** Pass back as `since` for the next page. */
266
+ next_since: number;
267
+ }
268
+ export interface TransferOptions {
269
+ reason?: string;
270
+ ref?: string;
271
+ idempotencyKey?: string;
272
+ }
273
+ /** `agent/settings.json`, sent with the prompt to `POST /v1/agent/config` (agent stream wire format). */
274
+ export interface AgentSettings {
275
+ model?: "deepseek-v4.1-flash" | "deepseek-v4-pro";
276
+ enabled?: boolean;
277
+ wake?: {
278
+ interval_minutes?: number;
279
+ min_event_gap_seconds?: number;
280
+ };
281
+ budget?: {
282
+ tool_calls_per_wake?: number;
283
+ tokens_per_wake?: number;
284
+ max_turns?: number;
285
+ compute_per_day?: number;
286
+ };
287
+ /** Agent tools offered to the model (default: all). */
288
+ tools?: string[];
289
+ /** Company wallet spend limits, TON. Over `auto_per_payment` an agent payment needs the team's approval. */
290
+ limits?: {
291
+ per_payment?: number;
292
+ per_hour?: number;
293
+ per_day?: number;
294
+ per_payee_day?: number;
295
+ auto_per_payment?: number;
296
+ auto_per_day?: number;
297
+ };
298
+ [key: string]: unknown;
299
+ }
300
+ export interface AgentConfigInput extends AgentSettings {
301
+ /** The text of agent/agent.md. */
302
+ prompt?: string;
303
+ /** Wake the agent once after saving. */
304
+ wake_now?: boolean;
305
+ }
306
+ export interface TranscriptEntry {
307
+ seq: number;
308
+ ts: string;
309
+ kind: string;
310
+ data?: unknown;
311
+ [key: string]: unknown;
312
+ }
313
+ export interface MarketOffering {
314
+ id: string;
315
+ company?: string;
316
+ name: string;
317
+ description: string;
318
+ price: number;
319
+ source?: "company" | "village-tool";
320
+ [key: string]: unknown;
321
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Webhook signatures: Standard Webhooks (https://www.standardwebhooks.com), exactly as the village
3
+ * signs them in `@ambasada/protocol` (packages/protocol/src/webhook.ts):
4
+ *
5
+ * webhook-id: msg_… webhook-timestamp: <unix s> webhook-signature: v1,<base64 HMAC-SHA256>
6
+ * signed content = "<id>.<timestamp>.<raw body>", key = the base64 bytes after `whsec_`
7
+ * body = { "type": "checkout.paid" | "webhook.test", "timestamp": ISO, "data": … }
8
+ *
9
+ * This module wraps the protocol functions with the SDK's error type and event shape.
10
+ */
11
+ import type { WebhookEvent } from "./types.js";
12
+ export { WEBHOOK_HEADERS, generateWebhookSecret, webhookHeaders } from "@ambasada/protocol";
13
+ export type HeaderSource = Headers | Record<string, string | string[] | undefined | null>;
14
+ export declare function getHeader(headers: HeaderSource, name: string): string | null;
15
+ /**
16
+ * Verifies a raw webhook (headers + body exactly as received) and returns the event, or throws
17
+ * `WebhookInvalidError`. Prefer `village.verifyWebhook(req)`, which reads a `Request` for you.
18
+ */
19
+ export declare function verifyWebhookPayload<Data = any>(secret: string, headers: HeaderSource, body: string | Uint8Array, options?: {
20
+ toleranceSeconds?: number;
21
+ now?: number;
22
+ }): Promise<WebhookEvent<Data>>;
package/package.json CHANGED
@@ -1,6 +1,55 @@
1
1
  {
2
2
  "name": "@ambasada/sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "TypeScript SDK for the Hack A Ton village: Checkout, webhooks, receipts, Pay, needs, LLM, images, tools, files, listings, wallet.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "sideEffects": false,
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "engines": {
23
+ "node": ">=22"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^22.20.5",
27
+ "esbuild": "^0.28.2",
28
+ "typescript": "~5.9.3",
29
+ "vitest": "^4.1.12",
30
+ "@ambasada/protocol": "0.1.0"
31
+ },
32
+ "author": "CENTYSO (https://centyso.com)",
33
+ "homepage": "https://ambasada.id",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/ambasada/cli.git",
37
+ "directory": "packages/sdk"
38
+ },
39
+ "bugs": {
40
+ "url": "https://github.com/ambasada/cli/issues"
41
+ },
42
+ "keywords": [
43
+ "ambasada",
44
+ "hack-a-ton",
45
+ "ton",
46
+ "payments",
47
+ "cloudflare",
48
+ "agents"
49
+ ],
50
+ "scripts": {
51
+ "build": "node scripts/build.mjs",
52
+ "typecheck": "tsc -p .",
53
+ "test": "vitest run"
54
+ }
6
55
  }