@aithos/sdk 0.1.0-alpha.40 → 0.1.0-alpha.41
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/dist/src/apps.d.ts +155 -0
- package/dist/src/apps.js +288 -0
- package/dist/src/compute.d.ts +30 -0
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +7 -1
- package/dist/src/sdk.d.ts +7 -0
- package/dist/src/sdk.js +13 -0
- package/dist/test/auth-j3.test.d.ts +2 -0
- package/dist/test/auth-j3.test.js +391 -0
- package/dist/test/auth.test.d.ts +2 -0
- package/dist/test/auth.test.js +175 -0
- package/dist/test/compute-delegate-path.test.d.ts +2 -0
- package/dist/test/compute-delegate-path.test.js +183 -0
- package/dist/test/compute.test.d.ts +2 -0
- package/dist/test/compute.test.js +194 -0
- package/dist/test/endpoints.test.d.ts +2 -0
- package/dist/test/endpoints.test.js +62 -0
- package/dist/test/envelope.test.d.ts +2 -0
- package/dist/test/envelope.test.js +318 -0
- package/dist/test/ethos-first-edition.test.d.ts +2 -0
- package/dist/test/ethos-first-edition.test.js +248 -0
- package/dist/test/ethos.test.d.ts +2 -0
- package/dist/test/ethos.test.js +219 -0
- package/dist/test/key-store.test.d.ts +2 -0
- package/dist/test/key-store.test.js +161 -0
- package/dist/test/mandates-compute.test.d.ts +2 -0
- package/dist/test/mandates-compute.test.js +256 -0
- package/dist/test/mandates.test.d.ts +2 -0
- package/dist/test/mandates.test.js +93 -0
- package/dist/test/sdk.test.d.ts +2 -0
- package/dist/test/sdk.test.js +126 -0
- package/dist/test/signer.test.d.ts +2 -0
- package/dist/test/signer.test.js +117 -0
- package/dist/test/signup-bootstrap.test.d.ts +2 -0
- package/dist/test/signup-bootstrap.test.js +311 -0
- package/dist/test/wallet.test.d.ts +2 -0
- package/dist/test/wallet.test.js +121 -0
- package/dist/test/web.test.d.ts +2 -0
- package/dist/test/web.test.js +270 -0
- package/package.json +1 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { AithosAuth } from "./auth.js";
|
|
2
|
+
import type { AithosSdkEndpoints } from "./endpoints.js";
|
|
3
|
+
/**
|
|
4
|
+
* The audience set scopes which consumers are eligible. `"open"` lets
|
|
5
|
+
* any DID invoke the app; `"list"` restricts to an explicit allowlist.
|
|
6
|
+
*/
|
|
7
|
+
export type AudienceSet = "open" | "list";
|
|
8
|
+
export interface SponsorshipBudgetInput {
|
|
9
|
+
/**
|
|
10
|
+
* Authority-interpreted unit of account. Default `"aithos.mc"` (the
|
|
11
|
+
* platform microcredit, ≈ €0.001 of AWS pass-through cost). Authorities
|
|
12
|
+
* MAY accept other units — see draft §13.3.4.
|
|
13
|
+
*/
|
|
14
|
+
readonly unit?: string;
|
|
15
|
+
/** Lifetime cap per consumer, in `unit`. */
|
|
16
|
+
readonly perUserCap: number;
|
|
17
|
+
/** If set, the per-user cap applies over a sliding window. */
|
|
18
|
+
readonly perUserWindowSeconds?: number | null;
|
|
19
|
+
/** UTC-day cap on total sponsored consumption across all consumers. */
|
|
20
|
+
readonly perDayTotalCap: number;
|
|
21
|
+
/** Lifetime pool cap across all consumers. `null` ≡ no cap. */
|
|
22
|
+
readonly poolCapTotal?: number | null;
|
|
23
|
+
}
|
|
24
|
+
export interface SponsorshipAudienceInput {
|
|
25
|
+
/** App DID the sponsorship covers (typically `sdk.appDid`). */
|
|
26
|
+
readonly appDid: string;
|
|
27
|
+
readonly audienceSet: AudienceSet;
|
|
28
|
+
/** Required iff `audienceSet === "list"`. */
|
|
29
|
+
readonly consumers?: readonly string[];
|
|
30
|
+
}
|
|
31
|
+
export interface SponsorshipAccountingAuthorityInput {
|
|
32
|
+
readonly did: string;
|
|
33
|
+
readonly endpoint: string;
|
|
34
|
+
}
|
|
35
|
+
export interface CreateSponsorshipMandateArgs {
|
|
36
|
+
readonly audience: SponsorshipAudienceInput;
|
|
37
|
+
readonly scopes: readonly string[];
|
|
38
|
+
readonly allowedMethods: readonly string[];
|
|
39
|
+
readonly allowedModels?: readonly string[];
|
|
40
|
+
readonly budget: SponsorshipBudgetInput;
|
|
41
|
+
readonly accountingAuthority: SponsorshipAccountingAuthorityInput;
|
|
42
|
+
/** Defaults to now. */
|
|
43
|
+
readonly notBefore?: Date;
|
|
44
|
+
/** Defaults to 365 days. */
|
|
45
|
+
readonly ttlSeconds?: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Signed sponsorship mandate, ready to seed into the authority's
|
|
49
|
+
* `aithos-app-sponsorships` table. Same shape as
|
|
50
|
+
* `@aithos/protocol-core`'s `SponsorshipMandate`.
|
|
51
|
+
*/
|
|
52
|
+
export interface SignedSponsorshipMandate {
|
|
53
|
+
readonly "aithos-sponsorship-mandate": "0.1.0";
|
|
54
|
+
readonly id: string;
|
|
55
|
+
readonly issuer: string;
|
|
56
|
+
readonly issued_by_key: string;
|
|
57
|
+
readonly audience: {
|
|
58
|
+
readonly app_did: string;
|
|
59
|
+
readonly audience_set: AudienceSet;
|
|
60
|
+
readonly consumers?: readonly string[];
|
|
61
|
+
};
|
|
62
|
+
readonly scopes: readonly string[];
|
|
63
|
+
readonly allowed_methods: readonly string[];
|
|
64
|
+
readonly allowed_models?: readonly string[];
|
|
65
|
+
readonly budget: {
|
|
66
|
+
readonly unit: string;
|
|
67
|
+
readonly per_user_cap: number;
|
|
68
|
+
readonly per_user_window_seconds: number | null;
|
|
69
|
+
readonly per_day_total_cap: number;
|
|
70
|
+
readonly pool_cap_total: number | null;
|
|
71
|
+
};
|
|
72
|
+
readonly accounting_authority: {
|
|
73
|
+
readonly did: string;
|
|
74
|
+
readonly endpoint: string;
|
|
75
|
+
};
|
|
76
|
+
readonly not_before: string;
|
|
77
|
+
readonly not_after: string;
|
|
78
|
+
readonly issued_at: string;
|
|
79
|
+
readonly nonce: string;
|
|
80
|
+
readonly signature: {
|
|
81
|
+
readonly alg: "ed25519";
|
|
82
|
+
readonly key: string;
|
|
83
|
+
readonly value: string;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export interface SignedSponsorshipRevocation {
|
|
87
|
+
readonly "aithos-revocation": "0.1.0";
|
|
88
|
+
readonly mandate_id: string;
|
|
89
|
+
readonly mandate_kind: "sponsorship-mandate";
|
|
90
|
+
readonly issuer: string;
|
|
91
|
+
readonly issued_by_key: string;
|
|
92
|
+
readonly revoked_at: string;
|
|
93
|
+
readonly reason: string;
|
|
94
|
+
readonly signature: {
|
|
95
|
+
readonly alg: "ed25519";
|
|
96
|
+
readonly key: string;
|
|
97
|
+
readonly value: string;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export type AppCreditPackId = "app-credits-10k" | "app-credits-50k" | "app-credits-200k";
|
|
101
|
+
export interface CreateAppTopupSessionArgs {
|
|
102
|
+
/** Which app-credits pack to purchase. */
|
|
103
|
+
readonly packId: AppCreditPackId;
|
|
104
|
+
/** Where Stripe redirects after a successful payment. */
|
|
105
|
+
readonly successUrl: string;
|
|
106
|
+
/** Where Stripe redirects if the user cancels. */
|
|
107
|
+
readonly cancelUrl: string;
|
|
108
|
+
/** Abort signal to cancel the request. */
|
|
109
|
+
readonly signal?: AbortSignal;
|
|
110
|
+
}
|
|
111
|
+
export interface CreateAppTopupSessionResult {
|
|
112
|
+
readonly checkoutUrl: string;
|
|
113
|
+
readonly sessionId: string;
|
|
114
|
+
}
|
|
115
|
+
export interface AppsNamespaceDeps {
|
|
116
|
+
readonly auth: AithosAuth;
|
|
117
|
+
readonly appDid: string;
|
|
118
|
+
readonly endpoints: AithosSdkEndpoints;
|
|
119
|
+
readonly fetch: typeof fetch;
|
|
120
|
+
}
|
|
121
|
+
export declare class AppsNamespace {
|
|
122
|
+
#private;
|
|
123
|
+
constructor(deps: AppsNamespaceDeps);
|
|
124
|
+
/**
|
|
125
|
+
* Build and sign a `SponsorshipMandate` as the calling owner. The
|
|
126
|
+
* returned JSON is signed by the owner's `#public` sphere key, ready
|
|
127
|
+
* to be seeded into the authority's `aithos-app-sponsorships` table.
|
|
128
|
+
*
|
|
129
|
+
* V0.1 has no server endpoint — get the row into DDB via:
|
|
130
|
+
* 1. `aws dynamodb put-item --table-name aithos-app-sponsorships
|
|
131
|
+
* --item file://mandate.json` (raw), or
|
|
132
|
+
* 2. The ops bootstrap script in
|
|
133
|
+
* `innoesate/aithos/platform/scripts/seed-sponsorship.mjs`
|
|
134
|
+
* (planned for V0.2).
|
|
135
|
+
*
|
|
136
|
+
* Hash with `sponsorshipMandateHash()` from `@aithos/protocol-core`
|
|
137
|
+
* if you need to embed it in an envelope's `sponsorship.hash` field.
|
|
138
|
+
*/
|
|
139
|
+
createSponsorshipMandate(args: CreateSponsorshipMandateArgs): Promise<SignedSponsorshipMandate>;
|
|
140
|
+
/**
|
|
141
|
+
* Build and sign a §4.6 revocation document targeting a sponsorship
|
|
142
|
+
* mandate. Same upload caveat as `createSponsorshipMandate`.
|
|
143
|
+
*/
|
|
144
|
+
revokeSponsorshipMandate(mandate: SignedSponsorshipMandate, reason: string): Promise<SignedSponsorshipRevocation>;
|
|
145
|
+
/**
|
|
146
|
+
* Stripe Checkout session for an `app-credits-*` pack. The session is
|
|
147
|
+
* bound to the calling owner's DID (which is treated as the app's
|
|
148
|
+
* funding DID — Option A unified wallet, see plan §3.4).
|
|
149
|
+
*
|
|
150
|
+
* Same endpoint and auth pattern as `sdk.wallet.createTopupSession`,
|
|
151
|
+
* just with the app-credits pack id family.
|
|
152
|
+
*/
|
|
153
|
+
createAppTopupSession(args: CreateAppTopupSessionArgs): Promise<CreateAppTopupSessionResult>;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=apps.d.ts.map
|
package/dist/src/apps.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 Mathieu Colla
|
|
3
|
+
// `sdk.apps` namespace — sponsorship lifecycle for app developers.
|
|
4
|
+
//
|
|
5
|
+
// V0.1 surface (2026-05-27) — minimal but sufficient to bootstrap a free
|
|
6
|
+
// trial flow on Linkedone and similar apps:
|
|
7
|
+
//
|
|
8
|
+
// createSponsorshipMandate(args)
|
|
9
|
+
// Build + sign a SponsorshipMandate as the calling owner. Returns
|
|
10
|
+
// the signed JSON. The caller is responsible for getting the row
|
|
11
|
+
// into the authority's `aithos-app-sponsorships` table — V0.1 has
|
|
12
|
+
// no server endpoint for it (manual `aws dynamodb put-item` or a
|
|
13
|
+
// Terraform-managed seed). V0.2 will add a server endpoint.
|
|
14
|
+
//
|
|
15
|
+
// revokeSponsorshipMandate(mandate, reason)
|
|
16
|
+
// Build + sign a §4.6 revocation document targeting the mandate.
|
|
17
|
+
// Same upload caveat as create — V0.1 has no server endpoint.
|
|
18
|
+
//
|
|
19
|
+
// createAppTopupSession(args)
|
|
20
|
+
// Stripe Checkout session for an `app-credits-*` pack. Reuses the
|
|
21
|
+
// existing wallet-topup endpoint; the difference is the pack id
|
|
22
|
+
// and the DID being credited (the app's, not the user's).
|
|
23
|
+
//
|
|
24
|
+
// Why no `getSponsorshipStatus*` in V0.1: the read side requires either
|
|
25
|
+
// a CloudFront-fronted read endpoint or direct DDB access. We'll add it
|
|
26
|
+
// in V0.2 once the sponsorship-api Lambda exists; for now, devs check
|
|
27
|
+
// their sponsorship via the AWS Console.
|
|
28
|
+
import { base64url, sign } from "@aithos/protocol-client";
|
|
29
|
+
import { walletTopupCheckoutUrl } from "./endpoints.js";
|
|
30
|
+
import { ownerKeyPair } from "./internal/protocol-client-bridge.js";
|
|
31
|
+
import { AithosSDKError } from "./types.js";
|
|
32
|
+
/* -------------------------------------------------------------------------- */
|
|
33
|
+
/* Class */
|
|
34
|
+
/* -------------------------------------------------------------------------- */
|
|
35
|
+
export class AppsNamespace {
|
|
36
|
+
#deps;
|
|
37
|
+
constructor(deps) {
|
|
38
|
+
this.#deps = deps;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build and sign a `SponsorshipMandate` as the calling owner. The
|
|
42
|
+
* returned JSON is signed by the owner's `#public` sphere key, ready
|
|
43
|
+
* to be seeded into the authority's `aithos-app-sponsorships` table.
|
|
44
|
+
*
|
|
45
|
+
* V0.1 has no server endpoint — get the row into DDB via:
|
|
46
|
+
* 1. `aws dynamodb put-item --table-name aithos-app-sponsorships
|
|
47
|
+
* --item file://mandate.json` (raw), or
|
|
48
|
+
* 2. The ops bootstrap script in
|
|
49
|
+
* `innoesate/aithos/platform/scripts/seed-sponsorship.mjs`
|
|
50
|
+
* (planned for V0.2).
|
|
51
|
+
*
|
|
52
|
+
* Hash with `sponsorshipMandateHash()` from `@aithos/protocol-core`
|
|
53
|
+
* if you need to embed it in an envelope's `sponsorship.hash` field.
|
|
54
|
+
*/
|
|
55
|
+
async createSponsorshipMandate(args) {
|
|
56
|
+
const owner = this.#requireOwner();
|
|
57
|
+
validateBudget(args.budget);
|
|
58
|
+
validateAudience(args.audience);
|
|
59
|
+
if (args.scopes.length === 0) {
|
|
60
|
+
throw new AithosSDKError("invalid_input", "scopes must be non-empty");
|
|
61
|
+
}
|
|
62
|
+
if (args.allowedMethods.length === 0) {
|
|
63
|
+
throw new AithosSDKError("invalid_input", "allowedMethods must be non-empty");
|
|
64
|
+
}
|
|
65
|
+
const nb = args.notBefore ?? new Date();
|
|
66
|
+
const ttl = args.ttlSeconds ?? 365 * 24 * 3600;
|
|
67
|
+
const na = new Date(nb.getTime() + ttl * 1000);
|
|
68
|
+
if (na <= nb) {
|
|
69
|
+
throw new AithosSDKError("invalid_input", "ttlSeconds must produce not_after > not_before");
|
|
70
|
+
}
|
|
71
|
+
const nonce = base64url(randomBytes(9));
|
|
72
|
+
const unsigned = {
|
|
73
|
+
"aithos-sponsorship-mandate": "0.1.0",
|
|
74
|
+
id: `spons_${ulid()}`,
|
|
75
|
+
issuer: owner.did,
|
|
76
|
+
issued_by_key: `${owner.did}#public`,
|
|
77
|
+
audience: {
|
|
78
|
+
app_did: args.audience.appDid,
|
|
79
|
+
audience_set: args.audience.audienceSet,
|
|
80
|
+
...(args.audience.consumers !== undefined
|
|
81
|
+
? { consumers: [...args.audience.consumers] }
|
|
82
|
+
: {}),
|
|
83
|
+
},
|
|
84
|
+
scopes: [...args.scopes],
|
|
85
|
+
allowed_methods: [...args.allowedMethods],
|
|
86
|
+
...(args.allowedModels ? { allowed_models: [...args.allowedModels] } : {}),
|
|
87
|
+
budget: {
|
|
88
|
+
unit: args.budget.unit ?? "aithos.mc",
|
|
89
|
+
per_user_cap: args.budget.perUserCap,
|
|
90
|
+
per_user_window_seconds: args.budget.perUserWindowSeconds === undefined
|
|
91
|
+
? null
|
|
92
|
+
: args.budget.perUserWindowSeconds,
|
|
93
|
+
per_day_total_cap: args.budget.perDayTotalCap,
|
|
94
|
+
pool_cap_total: args.budget.poolCapTotal === undefined
|
|
95
|
+
? null
|
|
96
|
+
: args.budget.poolCapTotal,
|
|
97
|
+
},
|
|
98
|
+
accounting_authority: { ...args.accountingAuthority },
|
|
99
|
+
not_before: nb.toISOString(),
|
|
100
|
+
not_after: na.toISOString(),
|
|
101
|
+
issued_at: new Date().toISOString(),
|
|
102
|
+
nonce,
|
|
103
|
+
signature: { alg: "ed25519", key: `${owner.did}#public`, value: "" },
|
|
104
|
+
};
|
|
105
|
+
const bytes = new TextEncoder().encode(jcsCanonicalize(unsigned));
|
|
106
|
+
const ownerPub = ownerKeyPair(owner, "public");
|
|
107
|
+
const sigBytes = sign(bytes, ownerPub.seed);
|
|
108
|
+
return {
|
|
109
|
+
...unsigned,
|
|
110
|
+
signature: { ...unsigned.signature, value: base64url(sigBytes) },
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Build and sign a §4.6 revocation document targeting a sponsorship
|
|
115
|
+
* mandate. Same upload caveat as `createSponsorshipMandate`.
|
|
116
|
+
*/
|
|
117
|
+
async revokeSponsorshipMandate(mandate, reason) {
|
|
118
|
+
const owner = this.#requireOwner();
|
|
119
|
+
if (mandate.issuer !== owner.did) {
|
|
120
|
+
throw new AithosSDKError("invalid_input", `mandate.issuer (${mandate.issuer}) does not match signed-in owner (${owner.did}) — only the sponsor can revoke`);
|
|
121
|
+
}
|
|
122
|
+
const unsigned = {
|
|
123
|
+
"aithos-revocation": "0.1.0",
|
|
124
|
+
mandate_id: mandate.id,
|
|
125
|
+
mandate_kind: "sponsorship-mandate",
|
|
126
|
+
issuer: owner.did,
|
|
127
|
+
issued_by_key: `${owner.did}#public`,
|
|
128
|
+
revoked_at: new Date().toISOString(),
|
|
129
|
+
reason,
|
|
130
|
+
signature: { alg: "ed25519", key: `${owner.did}#public`, value: "" },
|
|
131
|
+
};
|
|
132
|
+
const bytes = new TextEncoder().encode(jcsCanonicalize(unsigned));
|
|
133
|
+
const ownerPub = ownerKeyPair(owner, "public");
|
|
134
|
+
const sigBytes = sign(bytes, ownerPub.seed);
|
|
135
|
+
return {
|
|
136
|
+
...unsigned,
|
|
137
|
+
signature: { ...unsigned.signature, value: base64url(sigBytes) },
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Stripe Checkout session for an `app-credits-*` pack. The session is
|
|
142
|
+
* bound to the calling owner's DID (which is treated as the app's
|
|
143
|
+
* funding DID — Option A unified wallet, see plan §3.4).
|
|
144
|
+
*
|
|
145
|
+
* Same endpoint and auth pattern as `sdk.wallet.createTopupSession`,
|
|
146
|
+
* just with the app-credits pack id family.
|
|
147
|
+
*/
|
|
148
|
+
async createAppTopupSession(args) {
|
|
149
|
+
const { auth, endpoints, fetch: fetchImpl } = this.#deps;
|
|
150
|
+
const owner = auth._getOwnerSigners();
|
|
151
|
+
if (!owner || owner.destroyed) {
|
|
152
|
+
throw new AithosSDKError("sdk_no_owner", "no owner signed in; sign in first");
|
|
153
|
+
}
|
|
154
|
+
const url = walletTopupCheckoutUrl(endpoints);
|
|
155
|
+
let res;
|
|
156
|
+
try {
|
|
157
|
+
res = await fetchImpl(url, {
|
|
158
|
+
method: "POST",
|
|
159
|
+
headers: { "content-type": "application/json" },
|
|
160
|
+
body: JSON.stringify({
|
|
161
|
+
// Same endpoint as user-pack top-ups. The Lambda's pack-table
|
|
162
|
+
// distinguishes `app-credits-*` from `credits-*` and routes
|
|
163
|
+
// the credit to the same `aithos-wallet-user` row keyed on
|
|
164
|
+
// `user_did`, which for an app is its DID (= sdk.appDid).
|
|
165
|
+
user_did: this.#deps.appDid,
|
|
166
|
+
pack_id: args.packId,
|
|
167
|
+
success_url: args.successUrl,
|
|
168
|
+
cancel_url: args.cancelUrl,
|
|
169
|
+
}),
|
|
170
|
+
...(args.signal ? { signal: args.signal } : {}),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
catch (e) {
|
|
174
|
+
throw new AithosSDKError("network", e.message);
|
|
175
|
+
}
|
|
176
|
+
let body;
|
|
177
|
+
try {
|
|
178
|
+
body = await res.json();
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
throw new AithosSDKError("http", `HTTP ${res.status} ${res.statusText} — non-JSON response`, { status: res.status });
|
|
182
|
+
}
|
|
183
|
+
if (!res.ok) {
|
|
184
|
+
const err = body;
|
|
185
|
+
throw new AithosSDKError(err.error ?? "http", err.detail ?? `HTTP ${res.status} ${res.statusText}`, { status: res.status });
|
|
186
|
+
}
|
|
187
|
+
const ok = body;
|
|
188
|
+
if (typeof ok.checkout_url !== "string" ||
|
|
189
|
+
typeof ok.session_id !== "string") {
|
|
190
|
+
throw new AithosSDKError("empty", "wallet response missing checkout_url or session_id");
|
|
191
|
+
}
|
|
192
|
+
return { checkoutUrl: ok.checkout_url, sessionId: ok.session_id };
|
|
193
|
+
}
|
|
194
|
+
#requireOwner() {
|
|
195
|
+
const owner = this.#deps.auth._getOwnerSigners();
|
|
196
|
+
if (!owner || owner.destroyed) {
|
|
197
|
+
throw new AithosSDKError("sdk_no_owner", "no owner signed in; sign in first");
|
|
198
|
+
}
|
|
199
|
+
return owner;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/* -------------------------------------------------------------------------- */
|
|
203
|
+
/* Validation */
|
|
204
|
+
/* -------------------------------------------------------------------------- */
|
|
205
|
+
function validateBudget(b) {
|
|
206
|
+
if (!Number.isInteger(b.perUserCap) || b.perUserCap < 0) {
|
|
207
|
+
throw new AithosSDKError("invalid_input", "budget.perUserCap must be a non-negative integer");
|
|
208
|
+
}
|
|
209
|
+
if (!Number.isInteger(b.perDayTotalCap) || b.perDayTotalCap < 0) {
|
|
210
|
+
throw new AithosSDKError("invalid_input", "budget.perDayTotalCap must be a non-negative integer");
|
|
211
|
+
}
|
|
212
|
+
if (b.perUserWindowSeconds !== undefined &&
|
|
213
|
+
b.perUserWindowSeconds !== null) {
|
|
214
|
+
if (!Number.isInteger(b.perUserWindowSeconds) ||
|
|
215
|
+
b.perUserWindowSeconds <= 0) {
|
|
216
|
+
throw new AithosSDKError("invalid_input", "budget.perUserWindowSeconds must be null or a positive integer");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (b.poolCapTotal !== undefined && b.poolCapTotal !== null) {
|
|
220
|
+
if (!Number.isInteger(b.poolCapTotal) || b.poolCapTotal < 0) {
|
|
221
|
+
throw new AithosSDKError("invalid_input", "budget.poolCapTotal must be null or a non-negative integer");
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function validateAudience(a) {
|
|
226
|
+
if (!a.appDid || typeof a.appDid !== "string") {
|
|
227
|
+
throw new AithosSDKError("invalid_input", "audience.appDid must be a DID string");
|
|
228
|
+
}
|
|
229
|
+
if (a.audienceSet === "open") {
|
|
230
|
+
if (a.consumers !== undefined) {
|
|
231
|
+
throw new AithosSDKError("invalid_input", "audience.consumers MUST be absent when audienceSet is 'open'");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
else if (a.audienceSet === "list") {
|
|
235
|
+
if (!Array.isArray(a.consumers) || a.consumers.length === 0) {
|
|
236
|
+
throw new AithosSDKError("invalid_input", "audience.consumers MUST be a non-empty array when audienceSet is 'list'");
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
throw new AithosSDKError("invalid_input", `audience.audienceSet must be 'open' or 'list' (got: ${String(a.audienceSet)})`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/* -------------------------------------------------------------------------- */
|
|
244
|
+
/* ULID + JCS — local copies to avoid a runtime dep */
|
|
245
|
+
/* -------------------------------------------------------------------------- */
|
|
246
|
+
const ULID_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
247
|
+
function ulid(now = Date.now()) {
|
|
248
|
+
// 10 chars timestamp + 16 chars random = 26 chars
|
|
249
|
+
let timePart = "";
|
|
250
|
+
let t = now;
|
|
251
|
+
for (let i = 0; i < 10; i++) {
|
|
252
|
+
const mod = t % 32;
|
|
253
|
+
timePart = ULID_ALPHABET[mod] + timePart;
|
|
254
|
+
t = (t - mod) / 32;
|
|
255
|
+
}
|
|
256
|
+
let randPart = "";
|
|
257
|
+
for (let i = 0; i < 16; i++) {
|
|
258
|
+
randPart += ULID_ALPHABET[Math.floor(Math.random() * 32)];
|
|
259
|
+
}
|
|
260
|
+
return timePart + randPart;
|
|
261
|
+
}
|
|
262
|
+
function randomBytes(n) {
|
|
263
|
+
const out = new Uint8Array(n);
|
|
264
|
+
crypto.getRandomValues(out);
|
|
265
|
+
return out;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Minimal RFC-8785 (JCS) canonicalization. Sorts object keys
|
|
269
|
+
* lexicographically by code point, no whitespace, JSON.stringify for
|
|
270
|
+
* primitives. Matches the protocol-core canonicalize used to compute
|
|
271
|
+
* the hash the signature commits to.
|
|
272
|
+
*/
|
|
273
|
+
function jcsCanonicalize(value) {
|
|
274
|
+
if (value === null || typeof value !== "object") {
|
|
275
|
+
return JSON.stringify(value);
|
|
276
|
+
}
|
|
277
|
+
if (Array.isArray(value)) {
|
|
278
|
+
return "[" + value.map(jcsCanonicalize).join(",") + "]";
|
|
279
|
+
}
|
|
280
|
+
const obj = value;
|
|
281
|
+
const keys = Object.keys(obj).sort();
|
|
282
|
+
return ("{" +
|
|
283
|
+
keys
|
|
284
|
+
.map((k) => JSON.stringify(k) + ":" + jcsCanonicalize(obj[k]))
|
|
285
|
+
.join(",") +
|
|
286
|
+
"}");
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=apps.js.map
|
package/dist/src/compute.d.ts
CHANGED
|
@@ -58,6 +58,36 @@ export interface InvokeBedrockResult {
|
|
|
58
58
|
readonly walletBalance: number;
|
|
59
59
|
/** Audit log id for traceability. */
|
|
60
60
|
readonly auditId: string;
|
|
61
|
+
/**
|
|
62
|
+
* Which wallet was actually debited (draft §13.8, V0.1 sponsorship).
|
|
63
|
+
* - `"sponsored"` — the app developer's wallet (free trial / promo).
|
|
64
|
+
* - `"grant"` — the user's grant bucket (Aithos-donated credits).
|
|
65
|
+
* - `"purchase"` — the user's own paid credits.
|
|
66
|
+
* Absent on legacy server responses (pre-2026-05-27).
|
|
67
|
+
*/
|
|
68
|
+
readonly fundedBy?: "sponsored" | "grant" | "purchase";
|
|
69
|
+
/**
|
|
70
|
+
* If `fundedBy === "sponsored"`, the sponsor's DID (= the developer
|
|
71
|
+
* who pre-paid the pool). Absent otherwise.
|
|
72
|
+
*/
|
|
73
|
+
readonly sponsoredBy?: string;
|
|
74
|
+
/**
|
|
75
|
+
* If sponsored, the signed `ConsumptionReceipt` id (`rcpt_…`) the
|
|
76
|
+
* authority issued for this debit. The receipt itself can be fetched
|
|
77
|
+
* later via the receipts API (V0.2). Present on every signed call
|
|
78
|
+
* when the authority is configured, regardless of `fundedBy`.
|
|
79
|
+
*/
|
|
80
|
+
readonly receiptId?: string;
|
|
81
|
+
/**
|
|
82
|
+
* If sponsored, remaining microcredits for THIS consumer in this
|
|
83
|
+
* sponsorship pool. Useful for displaying "X free calls remaining"
|
|
84
|
+
* without an extra round-trip. May be `0` even when the user can
|
|
85
|
+
* still consume — when the authority can't compute the remainder
|
|
86
|
+
* cheaply (e.g. windowed caps), the SDK gets a conservative 0 and
|
|
87
|
+
* the SPA should fall back to calling `sdk.apps.getSponsorshipStatusForUser`
|
|
88
|
+
* (V0.2).
|
|
89
|
+
*/
|
|
90
|
+
readonly sponsoredRemainingForUser?: number;
|
|
61
91
|
}
|
|
62
92
|
/**
|
|
63
93
|
* Stable cross-provider image model ids supported by the Aithos compute
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.0-alpha.
|
|
1
|
+
export declare const VERSION = "0.1.0-alpha.41";
|
|
2
2
|
export { AithosSDK } from "./sdk.js";
|
|
3
3
|
export type { AithosSDKConfig } from "./types.js";
|
|
4
4
|
export { AithosSDKError } from "./types.js";
|
|
@@ -20,6 +20,8 @@ export { EthosClient, EthosNamespace, EthosZone, ZONE_NAMES, } from "./ethos.js"
|
|
|
20
20
|
export type { AddSectionInput, PublishResult, StagedChange, UpdateSectionPatch, ZoneName, } from "./ethos.js";
|
|
21
21
|
export { COMPUTE_INVOKE_SCOPE, MandatesNamespace } from "./mandates.js";
|
|
22
22
|
export type { ActorSphere, CreateMandateComputeInput, CreateMandateInput, MintedMandate, OwnedMandate, Scope, } from "./mandates.js";
|
|
23
|
+
export { AppsNamespace } from "./apps.js";
|
|
24
|
+
export type { AudienceSet, AppCreditPackId, CreateAppTopupSessionArgs, CreateAppTopupSessionResult, CreateSponsorshipMandateArgs, SignedSponsorshipMandate, SignedSponsorshipRevocation, SponsorshipAccountingAuthorityInput, SponsorshipAudienceInput, SponsorshipBudgetInput, } from "./apps.js";
|
|
23
25
|
export * as onboarding from "./onboarding.js";
|
|
24
26
|
export { createBrowserIdentity, browserIdentityFromStored, type BrowserIdentity, } from "@aithos/protocol-client";
|
|
25
27
|
export type { Section } from "@aithos/protocol-client";
|
package/dist/src/index.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// Public types specific to the SDK (`AithosSDKConfig`, `AithosSDKError`)
|
|
18
18
|
// are exported from here. Endpoint config (`AithosSdkEndpoints`,
|
|
19
19
|
// `DEFAULT_SDK_ENDPOINTS`) likewise.
|
|
20
|
-
export const VERSION = "0.1.0-alpha.
|
|
20
|
+
export const VERSION = "0.1.0-alpha.41";
|
|
21
21
|
export { AithosSDK } from "./sdk.js";
|
|
22
22
|
export { AithosSDKError } from "./types.js";
|
|
23
23
|
// Re-export protocol-client's JSON-RPC error type so consumers can
|
|
@@ -52,6 +52,12 @@ export { DEFAULT_KEYSTORE_DB_NAME, defaultKeyStore, indexedDbKeyStore, memoryKey
|
|
|
52
52
|
export { EthosClient, EthosNamespace, EthosZone, ZONE_NAMES, } from "./ethos.js";
|
|
53
53
|
// `sdk.mandates` namespace — owner-side mandate lifecycle.
|
|
54
54
|
export { COMPUTE_INVOKE_SCOPE, MandatesNamespace } from "./mandates.js";
|
|
55
|
+
// `sdk.apps` namespace — sponsorship mandates + app-credit top-ups
|
|
56
|
+
// (draft §13, V0.1). Pre-pay a pool that funds your users' compute
|
|
57
|
+
// calls within explicit caps. Per-user caps + per-day caps + lifetime
|
|
58
|
+
// pool cap enforced server-side. Fallback to user wallet when caps
|
|
59
|
+
// are reached or pool is empty.
|
|
60
|
+
export { AppsNamespace } from "./apps.js";
|
|
55
61
|
// Onboarding re-exports kept for advanced callers — the curated API
|
|
56
62
|
// for first-run flows is `auth.signUp` (already shipped in J3).
|
|
57
63
|
export * as onboarding from "./onboarding.js";
|
package/dist/src/sdk.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AithosAuth } from "./auth.js";
|
|
2
|
+
import { AppsNamespace } from "./apps.js";
|
|
2
3
|
import { ComputeNamespace } from "./compute.js";
|
|
3
4
|
import { type AithosSdkEndpoints } from "./endpoints.js";
|
|
4
5
|
import { EthosNamespace } from "./ethos.js";
|
|
@@ -47,6 +48,12 @@ export declare class AithosSDK {
|
|
|
47
48
|
readonly mandates: MandatesNamespace;
|
|
48
49
|
/** Web extraction namespace — aithos.web_extract through the web extractor proxy. */
|
|
49
50
|
readonly web: WebNamespace;
|
|
51
|
+
/**
|
|
52
|
+
* App lifecycle namespace — sponsorship mandates + app-credit top-ups.
|
|
53
|
+
* V0.1 (2026-05-27 — draft §13). Lets a developer pre-pay a pool that
|
|
54
|
+
* funds compute calls from their app's users within explicit caps.
|
|
55
|
+
*/
|
|
56
|
+
readonly apps: AppsNamespace;
|
|
50
57
|
constructor(config: AithosSDKConfig);
|
|
51
58
|
/** DID of the currently signed-in owner, or null if no owner is loaded. */
|
|
52
59
|
get userDid(): string | null;
|
package/dist/src/sdk.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// Copyright 2026 Mathieu Colla
|
|
3
|
+
import { AppsNamespace } from "./apps.js";
|
|
3
4
|
import { ComputeNamespace } from "./compute.js";
|
|
4
5
|
import { resolveEndpoints } from "./endpoints.js";
|
|
5
6
|
import { EthosNamespace } from "./ethos.js";
|
|
@@ -23,6 +24,12 @@ export class AithosSDK {
|
|
|
23
24
|
mandates;
|
|
24
25
|
/** Web extraction namespace — aithos.web_extract through the web extractor proxy. */
|
|
25
26
|
web;
|
|
27
|
+
/**
|
|
28
|
+
* App lifecycle namespace — sponsorship mandates + app-credit top-ups.
|
|
29
|
+
* V0.1 (2026-05-27 — draft §13). Lets a developer pre-pay a pool that
|
|
30
|
+
* funds compute calls from their app's users within explicit caps.
|
|
31
|
+
*/
|
|
32
|
+
apps;
|
|
26
33
|
constructor(config) {
|
|
27
34
|
if (!config.auth) {
|
|
28
35
|
throw new TypeError("AithosSDK: config.auth is required");
|
|
@@ -62,6 +69,12 @@ export class AithosSDK {
|
|
|
62
69
|
endpoints: this.endpoints,
|
|
63
70
|
fetch: fetchImpl,
|
|
64
71
|
});
|
|
72
|
+
this.apps = new AppsNamespace({
|
|
73
|
+
auth: config.auth,
|
|
74
|
+
appDid: config.appDid,
|
|
75
|
+
endpoints: this.endpoints,
|
|
76
|
+
fetch: fetchImpl,
|
|
77
|
+
});
|
|
65
78
|
}
|
|
66
79
|
/** DID of the currently signed-in owner, or null if no owner is loaded. */
|
|
67
80
|
get userDid() {
|