@tollbooth-dpyc/web 1.0.0 → 1.2.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.
Files changed (44) hide show
  1. package/README.md +63 -1
  2. package/dist/client.d.ts +13 -1
  3. package/dist/client.js +18 -2
  4. package/dist/debugLog.js +5 -2
  5. package/dist/errorReport.d.ts +5 -0
  6. package/dist/errorReport.js +14 -0
  7. package/dist/index.d.ts +6 -1
  8. package/dist/index.js +6 -1
  9. package/dist/inlineProof.d.ts +8 -0
  10. package/dist/inlineProof.js +15 -1
  11. package/dist/networkError.d.ts +29 -0
  12. package/dist/networkError.js +61 -0
  13. package/dist/react/CouponsPanel.d.ts +52 -0
  14. package/dist/react/CouponsPanel.js +98 -0
  15. package/dist/react/ErrorBoundary.d.ts +55 -0
  16. package/dist/react/ErrorBoundary.js +49 -0
  17. package/dist/react/PagedTable.d.ts +64 -0
  18. package/dist/react/PagedTable.js +28 -0
  19. package/dist/react/TableFilter.d.ts +93 -0
  20. package/dist/react/TableFilter.js +68 -0
  21. package/dist/react/ThemeToggle.d.ts +22 -0
  22. package/dist/react/ThemeToggle.js +11 -0
  23. package/dist/react/WalletCard.d.ts +3 -0
  24. package/dist/react/WalletCard.js +19 -53
  25. package/dist/react/WalletPage.d.ts +70 -0
  26. package/dist/react/WalletPage.js +104 -0
  27. package/dist/react/index.d.ts +8 -0
  28. package/dist/react/index.js +8 -0
  29. package/dist/react/useSession.d.ts +2 -0
  30. package/dist/react/useSession.js +2 -1
  31. package/dist/react/useTheme.d.ts +7 -0
  32. package/dist/react/useTheme.js +44 -0
  33. package/dist/react/useTopUp.d.ts +30 -0
  34. package/dist/react/useTopUp.js +74 -0
  35. package/dist/standardTools.d.ts +325 -16
  36. package/dist/standardTools.js +58 -4
  37. package/dist/table.d.ts +52 -0
  38. package/dist/table.js +95 -0
  39. package/dist/theme.d.ts +40 -0
  40. package/dist/theme.js +76 -0
  41. package/dist/wallet.d.ts +85 -0
  42. package/dist/wallet.js +153 -0
  43. package/package.json +1 -1
  44. package/theme.css +22 -0
@@ -28,6 +28,7 @@ export function useSession() {
28
28
  setState(read());
29
29
  setNotice("");
30
30
  }, []);
31
+ const dismissNotice = useCallback(() => setNotice(""), []);
31
32
  useEffect(() => onProofExpired(() => {
32
33
  // A tab that can sign for THIS npub lost nothing.
33
34
  if (canSignFor(currentClaim()))
@@ -35,5 +36,5 @@ export function useSession() {
35
36
  setNotice("Your session lapsed while you were away. Sign in again to carry on.");
36
37
  setState(read());
37
38
  }), []);
38
- return { ...state, notice, refresh, signOut };
39
+ return { ...state, notice, dismissNotice, refresh, signOut };
39
40
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The theme pick as React state: persisted under `<prefix>:theme`, applied to
3
+ * <html>, followed across tabs, and — in "system" — following the OS live.
4
+ * Call `bootstrapTheme()` in main.tsx too, so the first frame is right.
5
+ */
6
+ import { type Theme } from "../theme.ts";
7
+ export declare function useTheme(fallback?: Theme): [Theme, (next: Theme) => void];
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The theme pick as React state: persisted under `<prefix>:theme`, applied to
3
+ * <html>, followed across tabs, and — in "system" — following the OS live.
4
+ * Call `bootstrapTheme()` in main.tsx too, so the first frame is right.
5
+ */
6
+ import { useCallback, useEffect, useState } from "react";
7
+ import { applyTheme, readTheme, themeStorageKey, writeTheme } from "../theme.js";
8
+ export function useTheme(fallback = "dark") {
9
+ const [theme, setTheme] = useState(() => readTheme(fallback));
10
+ // Another tab changed it.
11
+ useEffect(() => {
12
+ let key;
13
+ try {
14
+ key = themeStorageKey();
15
+ }
16
+ catch {
17
+ return;
18
+ }
19
+ const onStorage = (e) => {
20
+ if (e.key !== key)
21
+ return;
22
+ const next = readTheme(fallback);
23
+ setTheme(next);
24
+ applyTheme(next);
25
+ };
26
+ window.addEventListener("storage", onStorage);
27
+ return () => window.removeEventListener("storage", onStorage);
28
+ }, [fallback]);
29
+ // In "system", the OS changed its mind.
30
+ useEffect(() => {
31
+ if (theme !== "system" || !window.matchMedia)
32
+ return;
33
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
34
+ const onChange = () => applyTheme("system");
35
+ mq.addEventListener("change", onChange);
36
+ return () => mq.removeEventListener("change", onChange);
37
+ }, [theme]);
38
+ const update = useCallback((next) => {
39
+ writeTheme(next);
40
+ applyTheme(next);
41
+ setTheme(next);
42
+ }, []);
43
+ return [theme, update];
44
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * A Lightning top-up as a hook: purchase_credits → an invoice → check_payment
3
+ * until it settles, expires or the patron gives up.
4
+ *
5
+ * The state machine is `topUpReducer` (framework-free, tested). This adds the
6
+ * calls and the polling: while an invoice is out and the tab is visible,
7
+ * check_payment runs every `pollMs` — it is free and safe to repeat — up to
8
+ * `maxChecks` times, after which the patron's own "check" still works.
9
+ */
10
+ import { type TopUpState } from "../wallet.ts";
11
+ export interface UseTopUpOptions {
12
+ /** How often to check an open invoice, in ms. 0 turns polling off. Default 5000. */
13
+ pollMs?: number;
14
+ /** Stop polling after this many checks. Default 60 (five minutes at the default rate). */
15
+ maxChecks?: number;
16
+ /** Called once when an invoice settles, with the sats credited — refresh a balance here. */
17
+ onSettled?: (credited: number) => void;
18
+ }
19
+ export interface TopUp {
20
+ state: TopUpState;
21
+ /** Ask the service for an invoice of `sats`. Ignored while one is being made or is out. */
22
+ create: (sats: number) => void;
23
+ /** Check the open invoice now. */
24
+ check: () => void;
25
+ /** Drop the open invoice (it simply expires at BTCPay). */
26
+ cancel: () => void;
27
+ /** Back to idle after a settlement or failure. */
28
+ reset: () => void;
29
+ }
30
+ export declare function useTopUp({ pollMs, maxChecks, onSettled }?: UseTopUpOptions): TopUp;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * A Lightning top-up as a hook: purchase_credits → an invoice → check_payment
3
+ * until it settles, expires or the patron gives up.
4
+ *
5
+ * The state machine is `topUpReducer` (framework-free, tested). This adds the
6
+ * calls and the polling: while an invoice is out and the tab is visible,
7
+ * check_payment runs every `pollMs` — it is free and safe to repeat — up to
8
+ * `maxChecks` times, after which the patron's own "check" still works.
9
+ */
10
+ import { useCallback, useEffect, useReducer, useRef, useState } from "react";
11
+ import { checkPayment, purchaseCredits } from "../standardTools.js";
12
+ import { shouldPoll, TOP_UP_IDLE, topUpReducer } from "../wallet.js";
13
+ function messageOf(e) {
14
+ return e instanceof Error ? e.message : String(e);
15
+ }
16
+ function tabHidden() {
17
+ return typeof document !== "undefined" && document.visibilityState === "hidden";
18
+ }
19
+ export function useTopUp({ pollMs = 5000, maxChecks = 60, onSettled } = {}) {
20
+ const [state, dispatch] = useReducer(topUpReducer, TOP_UP_IDLE);
21
+ const latest = useRef(state);
22
+ latest.current = state;
23
+ const settledRef = useRef(onSettled);
24
+ settledRef.current = onSettled;
25
+ // Bumped when the tab comes back, so a poll skipped while hidden is re-armed.
26
+ const [wake, setWake] = useState(0);
27
+ const create = useCallback((sats) => {
28
+ const s = latest.current;
29
+ if (s.phase === "creating" || s.phase === "awaiting")
30
+ return;
31
+ dispatch({ type: "create", sats });
32
+ purchaseCredits(sats).then((result) => dispatch({ type: "created", result }), (e) => dispatch({ type: "error", message: messageOf(e) }));
33
+ }, []);
34
+ const check = useCallback(() => {
35
+ const s = latest.current;
36
+ if (s.phase !== "awaiting" || s.checking)
37
+ return;
38
+ const id = s.invoice.id;
39
+ dispatch({ type: "check" });
40
+ checkPayment(id).then((result) => {
41
+ // A late answer for an invoice the patron has since cancelled is dropped.
42
+ const now = latest.current;
43
+ if (now.phase === "awaiting" && now.invoice.id === id)
44
+ dispatch({ type: "checked", result });
45
+ }, (e) => dispatch({ type: "error", message: messageOf(e) }));
46
+ }, []);
47
+ const cancel = useCallback(() => dispatch({ type: "cancel" }), []);
48
+ const reset = useCallback(() => dispatch({ type: "reset" }), []);
49
+ useEffect(() => {
50
+ if (!(pollMs > 0) || !shouldPoll(state, maxChecks))
51
+ return;
52
+ const t = setTimeout(() => {
53
+ if (tabHidden())
54
+ return; // re-armed by the visibility listener below
55
+ check();
56
+ }, pollMs);
57
+ return () => clearTimeout(t);
58
+ }, [state, pollMs, maxChecks, check, wake]);
59
+ useEffect(() => {
60
+ if (typeof document === "undefined")
61
+ return;
62
+ const onVisible = () => {
63
+ if (!tabHidden())
64
+ setWake((n) => n + 1);
65
+ };
66
+ document.addEventListener("visibilitychange", onVisible);
67
+ return () => document.removeEventListener("visibilitychange", onVisible);
68
+ }, []);
69
+ useEffect(() => {
70
+ if (state.phase === "settled")
71
+ settledRef.current?.(state.credited);
72
+ }, [state]);
73
+ return { state, create, check, cancel, reset };
74
+ }
@@ -2,14 +2,45 @@
2
2
  * Typed wrappers for the tools every operator gets from
3
3
  * `register_standard_tools` in the tollbooth-dpyc wheel.
4
4
  */
5
+ /** What a patron must supply beyond the npub proof, per the wheel's `patron_auth_block`. */
6
+ export interface PatronAuth {
7
+ mode: "oauth" | "secure_courier" | "none";
8
+ patron_credentials_required: boolean;
9
+ /** mode "oauth": the provider's credential service. */
10
+ oauth_service?: string;
11
+ /** mode "secure_courier": the service a patron delivers secrets to. */
12
+ credential_service?: string;
13
+ }
14
+ /** The wheel's `service_status`: health and build facts, free and unauthenticated. */
5
15
  export interface ServiceStatus {
16
+ success?: boolean;
17
+ service?: string;
18
+ slug?: string;
19
+ version?: string;
20
+ tollbooth_dpyc_version?: string;
21
+ vault_configured?: boolean;
22
+ courier_has_vault?: boolean;
6
23
  operator_npub_hash?: string;
24
+ process_id?: number;
25
+ /** Deploy metadata from the environment (commit, build time, …), lower-cased keys. */
26
+ build_info?: Record<string, string> | null;
27
+ patron_auth?: PatronAuth;
28
+ /** Only on a server that runs background jobs. */
29
+ async_jobs?: {
30
+ docket_url_set: boolean;
31
+ backend: string;
32
+ durable_across_recycles: boolean;
33
+ };
34
+ durable_jobs?: {
35
+ modal_app: string | null;
36
+ detached_executor_active: boolean;
37
+ detached_executor_resolved: boolean;
38
+ detached_executor_error: string | null;
39
+ last_dispatch_error: string | null;
40
+ dispatching: boolean;
41
+ };
7
42
  lifecycle?: string;
8
43
  message?: string;
9
- version?: string;
10
- tollbooth_dpyc_version?: string;
11
- service?: string;
12
- slug?: string;
13
44
  }
14
45
  export declare function serviceStatus(): Promise<ServiceStatus>;
15
46
  export interface NpubProofResult {
@@ -45,31 +76,79 @@ export interface ProofStatusResult {
45
76
  error_code?: string;
46
77
  }
47
78
  export declare function checkProofStatus(patronNpub: string, dpopToken: string): Promise<ProofStatusResult>;
79
+ /** One credit tranche: a purchase (or grant) and what is left of it. */
80
+ export interface CreditTranche {
81
+ id: string;
82
+ amount_sats: number;
83
+ remaining_sats: number;
84
+ expires_at: string | null;
85
+ created_at: string | null;
86
+ }
48
87
  export interface CheckBalanceResult {
49
88
  success?: boolean;
50
89
  balance_api_sats?: number;
51
90
  total_deposited_api_sats?: number;
52
91
  total_consumed_api_sats?: number;
92
+ total_expired_api_sats?: number;
93
+ pending_invoices?: number;
94
+ pending_invoice_ids?: string[];
95
+ last_deposit_at?: string | null;
53
96
  next_expiration_iso?: string;
54
97
  expiring_within_24h_sats?: number;
98
+ active_tranches?: number;
99
+ tranches?: CreditTranche[];
100
+ expired_tranches?: CreditTranche[];
101
+ seed_balance_granted?: boolean;
102
+ /** The ledger could not be read: the figures may be stale or zero. */
103
+ vault_unavailable?: boolean;
104
+ warning?: string;
105
+ today_usage?: Record<string, {
106
+ calls: number;
107
+ api_sats: number;
108
+ }>;
109
+ invoice_summary?: {
110
+ total_invoices: number;
111
+ settled_count: number;
112
+ pending_count: number;
113
+ total_real_sats: number;
114
+ total_api_sats_credited: number;
115
+ };
55
116
  error?: string;
56
117
  error_code?: string;
57
118
  }
58
119
  export declare function checkBalance(): Promise<CheckBalanceResult>;
120
+ export interface ConstraintEffect {
121
+ type: string;
122
+ message?: string;
123
+ [key: string]: unknown;
124
+ }
125
+ /** The wheel's `check_price` preview. */
59
126
  export interface CheckPriceResult {
60
127
  success: boolean;
61
128
  tool_id?: string;
62
- base_cost_api_sats?: number;
63
- effective_cost_api_sats?: number;
129
+ tool_name?: string;
130
+ /** "flat" | "flat+multipliers" | "percent" */
131
+ pricing_type?: string;
132
+ base_cost_api_sats?: number | null;
133
+ effective_cost_api_sats?: number | null;
134
+ rate_percent?: number;
135
+ rate_param?: string;
136
+ min_cost_sats?: number;
137
+ multipliers?: Record<string, unknown>;
138
+ constraints_enabled?: boolean;
139
+ constraint_effects?: ConstraintEffect[];
140
+ hint?: string;
64
141
  error?: string;
65
142
  error_code?: string;
66
143
  }
67
144
  /**
68
145
  * The fare for one tool in api_sats, as the live pricing model resolves it,
69
- * constraints included — keyed by the tool's frozen UUID. Null when unreadable:
70
- * show the answer without a price rather than invent one.
146
+ * constraints included — keyed by the tool's frozen UUID (or its capability
147
+ * name). `toolKwargs` are the call's own parameters, for a tool priced by
148
+ * them (ad valorem, or a multiplier on a categorical argument). Null when
149
+ * unreadable: show the answer without a price rather than invent one.
71
150
  */
72
- export declare function checkPrice(toolId: string): Promise<number | null>;
151
+ export declare function checkPrice(toolId: string, toolKwargs?: Record<string, unknown>): Promise<number | null>;
73
152
  export interface PurchaseCreditsResult {
74
153
  success?: boolean;
75
154
  invoice_id?: string;
@@ -89,23 +168,107 @@ export interface CheckPaymentResult {
89
168
  invoice_id?: string;
90
169
  credits_granted?: number;
91
170
  balance_api_sats?: number;
171
+ amount_sats?: number;
172
+ /** false: the credit could not be written; nothing was granted. */
173
+ persisted?: boolean;
174
+ source?: string;
92
175
  error?: string;
93
176
  error_code?: string;
94
177
  }
95
178
  export declare function checkPayment(invoiceId: string): Promise<CheckPaymentResult>;
96
- export interface AccountStatementResult {
97
- success?: boolean;
98
- balance_api_sats?: number;
99
- total_deposited_api_sats?: number;
100
- total_consumed_api_sats?: number;
101
- total_expired_api_sats?: number;
102
- today_usage?: Record<string, {
179
+ export interface StatementInvoice {
180
+ invoice_id: string;
181
+ status: string;
182
+ amount_sats: number;
183
+ api_sats_credited: number;
184
+ multiplier?: number;
185
+ created_at: string | null;
186
+ settled_at?: string;
187
+ }
188
+ export interface StatementTranche {
189
+ granted_at: string | null;
190
+ original_sats: number;
191
+ remaining_sats: number;
192
+ invoice_id: string | null;
193
+ expires_at?: string;
194
+ }
195
+ export interface StatementToolUsage {
196
+ tool: string;
197
+ calls: number;
198
+ api_sats: number;
199
+ }
200
+ export interface StatementDay {
201
+ date: string;
202
+ total_calls: number;
203
+ total_api_sats: number;
204
+ tools: Record<string, {
103
205
  calls: number;
104
206
  api_sats: number;
105
207
  }>;
208
+ }
209
+ /** The wheel's `account_statement`: history, tranches and usage, newest first. */
210
+ export interface AccountStatementResult {
211
+ success?: boolean;
212
+ generated_at?: string;
213
+ statement_period_days?: number;
214
+ account_summary?: {
215
+ balance_api_sats: number;
216
+ total_deposited_api_sats: number;
217
+ total_consumed_api_sats: number;
218
+ total_expired_api_sats: number;
219
+ };
220
+ purchase_history?: StatementInvoice[];
221
+ active_tranches?: StatementTranche[];
222
+ tool_usage_all_time?: StatementToolUsage[];
223
+ daily_usage?: StatementDay[];
106
224
  error?: string;
225
+ error_code?: string;
107
226
  }
108
227
  export declare function getAccountStatement(days?: number): Promise<AccountStatementResult>;
228
+ export interface PatronCoupon {
229
+ coupon_id: string;
230
+ name: string;
231
+ discount_percent: number;
232
+ valid_from: string;
233
+ valid_until: string;
234
+ uses_per_patron: number | null;
235
+ use_count: number;
236
+ uses_remaining: number | null;
237
+ total_uses: number | null;
238
+ total_remaining: number | null;
239
+ /** active | window_closed | window_not_started | patron_limit | total_limit */
240
+ status: string;
241
+ }
242
+ export interface ListMyCouponsResult {
243
+ success?: boolean;
244
+ count?: number;
245
+ coupons?: PatronCoupon[];
246
+ error?: string;
247
+ error_code?: string;
248
+ }
249
+ export interface RedeemCouponResult {
250
+ success?: boolean;
251
+ coupon_id?: string;
252
+ name?: string;
253
+ discount_percent?: number;
254
+ valid_until?: string;
255
+ uses_remaining?: number | null;
256
+ uses_per_patron?: number | null;
257
+ error?: string;
258
+ error_code?: string;
259
+ }
260
+ export interface ForgetCouponResult {
261
+ success?: boolean;
262
+ coupon_id?: string;
263
+ error?: string;
264
+ error_code?: string;
265
+ }
266
+ /** The coupons this patron has redeemed here. Free. */
267
+ export declare function listMyCoupons(): Promise<ListMyCouponsResult>;
268
+ /** Redeem an operator's code once; the discount then applies on its own. Free. */
269
+ export declare function redeemCoupon(code: string): Promise<RedeemCouponResult>;
270
+ /** Take a coupon off the patron's list. The code stays re-redeemable while its window allows. */
271
+ export declare function forgetCoupon(couponId: string): Promise<ForgetCouponResult>;
109
272
  export interface Kind0 {
110
273
  name?: string;
111
274
  display_name?: string;
@@ -132,3 +295,149 @@ export interface PublishNostrProfileResult {
132
295
  }
133
296
  /** Relay a kind-0 the BROWSER signed. The wheel verifies it and fans it out. */
134
297
  export declare function publishNostrProfile(npub: string, signedEvent: string): Promise<PublishNostrProfileResult>;
298
+ /** Every lifecycle `session_status` reports. */
299
+ export type SessionLifecycle = "ready" | "warming_up" | "misconfigured" | "quota_exceeded" | "not_registered" | "no_identity";
300
+ /** The patron's stored upstream OAuth token, when the operator has a provider. */
301
+ export interface UpstreamOAuth {
302
+ service: string;
303
+ has_access_token: boolean;
304
+ has_refresh_token: boolean;
305
+ refresh_enabled: boolean;
306
+ access_token_expires_at?: number;
307
+ access_token_expires_in_seconds?: number;
308
+ }
309
+ export interface SessionStatusResult {
310
+ success?: boolean;
311
+ lifecycle?: SessionLifecycle;
312
+ message?: string;
313
+ operator_npub?: string;
314
+ /** Why it is not ready, from the failing layer. */
315
+ detail?: string;
316
+ operator_credential_service?: string;
317
+ patron_credential_service?: string;
318
+ upstream_oauth?: UpstreamOAuth;
319
+ /** The vault could not answer — not the same as "never authorized". */
320
+ upstream_oauth_unreadable?: string;
321
+ error?: string;
322
+ error_code?: string;
323
+ }
324
+ /**
325
+ * Is the operator ready to serve? Free, no proof. With `patronNpub`, also the
326
+ * patron's upstream OAuth token expiry, so a page can refresh ahead of time.
327
+ */
328
+ export declare function sessionStatus(patronNpub?: string): Promise<SessionStatusResult>;
329
+ export interface OnboardingItem {
330
+ field: string;
331
+ /** "identity" | "authority" | "secret" */
332
+ category: string;
333
+ /** "configured" | "missing" | "unknown" */
334
+ status: string;
335
+ how?: string;
336
+ lifecycle?: string;
337
+ delivered_at?: string | null;
338
+ }
339
+ /** The wheel's `get_operator_onboarding_status`: what the operator still has to set up. */
340
+ export interface OperatorOnboardingStatus {
341
+ ready?: boolean;
342
+ configured?: OnboardingItem[];
343
+ missing?: OnboardingItem[];
344
+ optional_missing?: OnboardingItem[];
345
+ summary?: string;
346
+ bootstrap_error?: string;
347
+ vault_ok?: boolean;
348
+ /** Set when the credential vault could not be read: nothing is known either way. */
349
+ vault_situation?: string;
350
+ credential_greeting?: string | null;
351
+ credential_service?: string | null;
352
+ operator_name?: string;
353
+ error?: string;
354
+ error_code?: string;
355
+ }
356
+ export declare function getOperatorOnboardingStatus(): Promise<OperatorOnboardingStatus>;
357
+ /**
358
+ * The operator's own balance at its Authority — what certifies patron top-ups.
359
+ * The Authority's ledger, in the same shape as a patron's `check_balance`.
360
+ */
361
+ export declare function checkAuthorityBalance(): Promise<CheckBalanceResult>;
362
+ export interface PricingStep {
363
+ id: string;
364
+ type: string;
365
+ params?: Record<string, unknown>;
366
+ patron_npubs?: string[];
367
+ }
368
+ export interface ToolPrice {
369
+ tool_id: string;
370
+ tool_name: string;
371
+ price_sats: number;
372
+ category: string;
373
+ intent: string;
374
+ priced: boolean;
375
+ price_type?: string;
376
+ price_formula?: unknown;
377
+ min_cost?: number;
378
+ max_cost?: number;
379
+ multipliers?: Record<string, unknown>;
380
+ chain?: PricingStep[];
381
+ }
382
+ /** The wheel's `get_pricing_model`. `status` is "ok" or "error". */
383
+ export interface PricingModelResult {
384
+ status?: string;
385
+ model_id?: string | null;
386
+ name?: string | null;
387
+ is_active?: boolean | null;
388
+ tools?: ToolPrice[] | null;
389
+ tranche_lifetime?: {
390
+ target_usage_pct: number;
391
+ ttl_days?: number;
392
+ min_days?: number;
393
+ max_days?: number;
394
+ };
395
+ error?: string;
396
+ }
397
+ /** The operator's active pricing model. Free, no proof. */
398
+ export declare function getPricingModel(): Promise<PricingModelResult>;
399
+ export interface CanonicalIdentity {
400
+ tool_id: string;
401
+ mcp_name: string;
402
+ category: string;
403
+ intent: string;
404
+ capability: string;
405
+ registered: boolean;
406
+ reason?: string;
407
+ }
408
+ /** The wheel's `list_canonical_identities`: every tool's frozen UUID and wire name. */
409
+ export interface CanonicalIdentitiesResult {
410
+ success?: boolean;
411
+ operator_npub?: string;
412
+ count?: number;
413
+ unregistered_count?: number;
414
+ tools?: CanonicalIdentity[];
415
+ unregistered?: Array<{
416
+ mcp_name: string;
417
+ reason: string;
418
+ [key: string]: unknown;
419
+ }>;
420
+ error?: string;
421
+ }
422
+ export declare function listCanonicalIdentities(): Promise<CanonicalIdentitiesResult>;
423
+ export interface PatronCredentialFieldsResult {
424
+ success?: boolean;
425
+ fields?: string[];
426
+ /** Per field, when it was delivered; null for secrets vaulted before stamps. */
427
+ delivered_at?: Record<string, string | null>;
428
+ count?: number;
429
+ error?: string;
430
+ error_code?: string;
431
+ }
432
+ export interface PatronCredentialWriteResult {
433
+ success?: boolean;
434
+ message?: string;
435
+ error?: string;
436
+ error_code?: string;
437
+ }
438
+ /** The names of the patron's stored credential fields — never their values. */
439
+ export declare function getPatronCredentialFields(): Promise<PatronCredentialFieldsResult>;
440
+ /** Set one credential field, leaving the others as they are. */
441
+ export declare function updatePatronCredential(field: string, value: string): Promise<PatronCredentialWriteResult>;
442
+ /** Remove one credential field, leaving the others as they are. */
443
+ export declare function deletePatronCredential(field: string): Promise<PatronCredentialWriteResult>;
@@ -39,11 +39,16 @@ export function checkBalance() {
39
39
  }
40
40
  /**
41
41
  * The fare for one tool in api_sats, as the live pricing model resolves it,
42
- * constraints included — keyed by the tool's frozen UUID. Null when unreadable:
43
- * show the answer without a price rather than invent one.
42
+ * constraints included — keyed by the tool's frozen UUID (or its capability
43
+ * name). `toolKwargs` are the call's own parameters, for a tool priced by
44
+ * them (ad valorem, or a multiplier on a categorical argument). Null when
45
+ * unreadable: show the answer without a price rather than invent one.
44
46
  */
45
- export async function checkPrice(toolId) {
46
- const r = await callTool("check_price", { tool_id: toolId });
47
+ export async function checkPrice(toolId, toolKwargs) {
48
+ const r = await callTool("check_price", {
49
+ tool_id: toolId,
50
+ ...(toolKwargs ? { tool_kwargs: JSON.stringify(toolKwargs) } : {}),
51
+ });
47
52
  const v = r.effective_cost_api_sats ?? r.base_cost_api_sats;
48
53
  return typeof v === "number" ? v : null;
49
54
  }
@@ -56,6 +61,18 @@ export function checkPayment(invoiceId) {
56
61
  export function getAccountStatement(days = 30) {
57
62
  return callTool("account_statement", { days });
58
63
  }
64
+ /** The coupons this patron has redeemed here. Free. */
65
+ export function listMyCoupons() {
66
+ return callTool("list_my_coupons", {});
67
+ }
68
+ /** Redeem an operator's code once; the discount then applies on its own. Free. */
69
+ export function redeemCoupon(code) {
70
+ return callTool("redeem_coupon", { code });
71
+ }
72
+ /** Take a coupon off the patron's list. The code stays re-redeemable while its window allows. */
73
+ export function forgetCoupon(couponId) {
74
+ return callTool("forget_coupon", { coupon_id: couponId });
75
+ }
59
76
  export function getNostrProfile(npub) {
60
77
  return callTool("get_nostr_profile", { npub });
61
78
  }
@@ -66,3 +83,40 @@ export function publishNostrProfile(npub, signedEvent) {
66
83
  signed_event: signedEvent,
67
84
  });
68
85
  }
86
+ /**
87
+ * Is the operator ready to serve? Free, no proof. With `patronNpub`, also the
88
+ * patron's upstream OAuth token expiry, so a page can refresh ahead of time.
89
+ */
90
+ export function sessionStatus(patronNpub) {
91
+ return callTool("session_status", patronNpub ? { patron_npub: patronNpub } : {}, { bestEffort: true });
92
+ }
93
+ export function getOperatorOnboardingStatus() {
94
+ return callTool("get_operator_onboarding_status", {}, { bestEffort: true });
95
+ }
96
+ /**
97
+ * The operator's own balance at its Authority — what certifies patron top-ups.
98
+ * The Authority's ledger, in the same shape as a patron's `check_balance`.
99
+ */
100
+ export function checkAuthorityBalance() {
101
+ return callTool("check_authority_balance", {}, { bestEffort: true });
102
+ }
103
+ /** The operator's active pricing model. Free, no proof. */
104
+ export function getPricingModel() {
105
+ return callTool("get_pricing_model", {});
106
+ }
107
+ export function listCanonicalIdentities() {
108
+ return callTool("list_canonical_identities", {});
109
+ }
110
+ /** The names of the patron's stored credential fields — never their values. */
111
+ export function getPatronCredentialFields() {
112
+ return callTool("get_patron_credential_fields", {});
113
+ }
114
+ /** Set one credential field, leaving the others as they are. */
115
+ export function updatePatronCredential(field, value) {
116
+ // `field` first: the log's scrubber keys on it to hide `value`.
117
+ return callTool("update_patron_credential", { field, value });
118
+ }
119
+ /** Remove one credential field, leaving the others as they are. */
120
+ export function deletePatronCredential(field) {
121
+ return callTool("delete_patron_credential", { field });
122
+ }