@aithos/sdk 0.1.0-alpha.3 → 0.1.0-alpha.5
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/auth-api.d.ts +41 -0
- package/dist/src/auth-api.js +82 -0
- package/dist/src/auth.d.ts +114 -75
- package/dist/src/auth.js +553 -73
- package/dist/src/compute.d.ts +8 -6
- package/dist/src/compute.js +19 -11
- package/dist/src/ethos.d.ts +117 -1
- package/dist/src/ethos.js +417 -16
- package/dist/src/index.d.ts +8 -4
- package/dist/src/index.js +26 -8
- package/dist/src/internal/delegate-bundle.d.ts +18 -0
- package/dist/src/internal/delegate-bundle.js +89 -0
- package/dist/src/internal/delegate-state.d.ts +45 -0
- package/dist/src/internal/delegate-state.js +120 -0
- package/dist/src/internal/owner-signers.d.ts +78 -0
- package/dist/src/internal/owner-signers.js +179 -0
- package/dist/src/internal/protocol-client-bridge.d.ts +8 -0
- package/dist/src/internal/protocol-client-bridge.js +20 -0
- package/dist/src/internal/recovery-file.d.ts +29 -0
- package/dist/src/internal/recovery-file.js +98 -0
- package/dist/src/internal/signer.d.ts +59 -0
- package/dist/src/internal/signer.js +86 -0
- package/dist/src/key-store.d.ts +128 -0
- package/dist/src/key-store.js +244 -0
- package/dist/src/mandates.d.ts +88 -1
- package/dist/src/mandates.js +185 -8
- package/dist/src/sdk.d.ts +36 -3
- package/dist/src/sdk.js +27 -23
- package/dist/src/session-store.d.ts +58 -0
- package/dist/src/session-store.js +158 -0
- package/dist/src/wallet.d.ts +4 -6
- package/dist/src/wallet.js +18 -8
- package/dist/test/auth-j3.test.d.ts +2 -0
- package/dist/test/auth-j3.test.js +360 -0
- package/dist/test/compute.test.js +22 -11
- 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.test.d.ts +2 -0
- package/dist/test/mandates.test.js +93 -0
- package/dist/test/sdk.test.js +64 -30
- package/dist/test/signer.test.d.ts +2 -0
- package/dist/test/signer.test.js +117 -0
- package/dist/test/wallet.test.js +20 -9
- package/package.json +4 -3
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 Mathieu Colla
|
|
3
|
+
// Unit tests for the internal Signer abstraction.
|
|
4
|
+
import { strict as assert } from "node:assert";
|
|
5
|
+
import { describe, it } from "node:test";
|
|
6
|
+
import { createBrowserIdentity, verify as ed25519Verify, } from "@aithos/protocol-client";
|
|
7
|
+
import { RawSeedSigner } from "../src/internal/signer.js";
|
|
8
|
+
import { OwnerSigners } from "../src/internal/owner-signers.js";
|
|
9
|
+
/* -------------------------------------------------------------------------- */
|
|
10
|
+
/* RawSeedSigner */
|
|
11
|
+
/* -------------------------------------------------------------------------- */
|
|
12
|
+
describe("RawSeedSigner", () => {
|
|
13
|
+
it("signs a message that verifies under its public key", async () => {
|
|
14
|
+
const id = createBrowserIdentity("alice", "Alice");
|
|
15
|
+
const signer = new RawSeedSigner(id.public.seed, id.public.publicKey);
|
|
16
|
+
const msg = new TextEncoder().encode("hello aithos");
|
|
17
|
+
const sig = await signer.sign(msg);
|
|
18
|
+
assert.equal(sig.length, 64, "Ed25519 signatures are 64 bytes");
|
|
19
|
+
assert.ok(ed25519Verify(sig, msg, signer.publicKey), "signature should verify under the signer's public key");
|
|
20
|
+
});
|
|
21
|
+
it("defensively copies the seed — mutating the input has no effect", async () => {
|
|
22
|
+
const id = createBrowserIdentity("alice", "Alice");
|
|
23
|
+
const seedCopy = new Uint8Array(id.public.seed);
|
|
24
|
+
const signer = new RawSeedSigner(id.public.seed, id.public.publicKey);
|
|
25
|
+
// Tamper with the original seed buffer the caller passed in.
|
|
26
|
+
id.public.seed.fill(0xff);
|
|
27
|
+
const msg = new TextEncoder().encode("hello");
|
|
28
|
+
const sig = await signer.sign(msg);
|
|
29
|
+
// Recompute with the pristine seed → must match.
|
|
30
|
+
const expected = await new RawSeedSigner(seedCopy, id.public.publicKey).sign(msg);
|
|
31
|
+
assert.deepEqual(sig, expected, "signer must use its own copy of the seed");
|
|
32
|
+
});
|
|
33
|
+
it("destroy zeroizes and blocks further use", async () => {
|
|
34
|
+
const id = createBrowserIdentity("alice", "Alice");
|
|
35
|
+
const signer = new RawSeedSigner(id.public.seed, id.public.publicKey);
|
|
36
|
+
signer.destroy();
|
|
37
|
+
await assert.rejects(() => signer.sign(new Uint8Array(1)), /destroyed/, "sign() must reject after destroy()");
|
|
38
|
+
assert.throws(() => signer._unsafeKeyPair(), /destroyed/);
|
|
39
|
+
// Idempotent — second destroy() is a no-op.
|
|
40
|
+
assert.doesNotThrow(() => signer.destroy());
|
|
41
|
+
});
|
|
42
|
+
it("rejects malformed seed/public-key sizes", () => {
|
|
43
|
+
const id = createBrowserIdentity("alice", "Alice");
|
|
44
|
+
assert.throws(() => new RawSeedSigner(new Uint8Array(31), id.public.publicKey), /seed must be 32 bytes/);
|
|
45
|
+
assert.throws(() => new RawSeedSigner(id.public.seed, new Uint8Array(33)), /publicKey must be 32 bytes/);
|
|
46
|
+
});
|
|
47
|
+
it("_unsafeKeyPair returns a KeyPair compatible with protocol-client", async () => {
|
|
48
|
+
const id = createBrowserIdentity("alice", "Alice");
|
|
49
|
+
const signer = new RawSeedSigner(id.public.seed, id.public.publicKey);
|
|
50
|
+
const kp = signer._unsafeKeyPair();
|
|
51
|
+
assert.equal(kp.seed.length, 32);
|
|
52
|
+
assert.equal(kp.publicKey.length, 32);
|
|
53
|
+
assert.deepEqual(kp.publicKey, signer.publicKey);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
/* -------------------------------------------------------------------------- */
|
|
57
|
+
/* OwnerSigners */
|
|
58
|
+
/* -------------------------------------------------------------------------- */
|
|
59
|
+
describe("OwnerSigners", () => {
|
|
60
|
+
it("fromBrowserIdentity yields four working signers", async () => {
|
|
61
|
+
const id = createBrowserIdentity("alice", "Alice");
|
|
62
|
+
const signers = OwnerSigners.fromBrowserIdentity(id);
|
|
63
|
+
assert.equal(signers.did, id.did);
|
|
64
|
+
assert.equal(signers.handle, id.handle);
|
|
65
|
+
assert.equal(signers.displayName, id.displayName);
|
|
66
|
+
for (const sphere of ["root", "public", "circle", "self"]) {
|
|
67
|
+
const sig = await signers[sphere].sign(new TextEncoder().encode(sphere));
|
|
68
|
+
assert.equal(sig.length, 64, `${sphere}: sig length`);
|
|
69
|
+
assert.ok(ed25519Verify(sig, new TextEncoder().encode(sphere), signers[sphere].publicKey), `${sphere}: signature must verify`);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
it("signerForSphere maps strings to the right signer", () => {
|
|
73
|
+
const id = createBrowserIdentity("alice", "Alice");
|
|
74
|
+
const signers = OwnerSigners.fromBrowserIdentity(id);
|
|
75
|
+
assert.strictEqual(signers.signerForSphere("root"), signers.root);
|
|
76
|
+
assert.strictEqual(signers.signerForSphere("public"), signers.public);
|
|
77
|
+
assert.strictEqual(signers.signerForSphere("circle"), signers.circle);
|
|
78
|
+
assert.strictEqual(signers.signerForSphere("self"), signers.self);
|
|
79
|
+
});
|
|
80
|
+
it("destroy zeroizes all signers", async () => {
|
|
81
|
+
const id = createBrowserIdentity("alice", "Alice");
|
|
82
|
+
const signers = OwnerSigners.fromBrowserIdentity(id);
|
|
83
|
+
signers.destroy();
|
|
84
|
+
assert.equal(signers.destroyed, true);
|
|
85
|
+
await assert.rejects(() => signers.public.sign(new Uint8Array(1)), /destroyed/);
|
|
86
|
+
// Idempotent.
|
|
87
|
+
assert.doesNotThrow(() => signers.destroy());
|
|
88
|
+
});
|
|
89
|
+
it("fromStoredIdentity round-trips through hex", async () => {
|
|
90
|
+
const id = createBrowserIdentity("alice", "Alice");
|
|
91
|
+
const stored = {
|
|
92
|
+
version: "0.1.0",
|
|
93
|
+
handle: id.handle,
|
|
94
|
+
displayName: id.displayName,
|
|
95
|
+
did: id.did,
|
|
96
|
+
seeds: {
|
|
97
|
+
root: bytesToHex(id.root.seed),
|
|
98
|
+
public: bytesToHex(id.public.seed),
|
|
99
|
+
circle: bytesToHex(id.circle.seed),
|
|
100
|
+
self: bytesToHex(id.self.seed),
|
|
101
|
+
},
|
|
102
|
+
savedAt: new Date().toISOString(),
|
|
103
|
+
};
|
|
104
|
+
const signers = OwnerSigners.fromStoredIdentity(stored);
|
|
105
|
+
const sig = await signers.public.sign(new TextEncoder().encode("test"));
|
|
106
|
+
assert.ok(ed25519Verify(sig, new TextEncoder().encode("test"), signers.public.publicKey));
|
|
107
|
+
// The pubkey must match what we'd derive from the original BrowserIdentity.
|
|
108
|
+
assert.deepEqual(signers.public.publicKey, id.public.publicKey);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
function bytesToHex(b) {
|
|
112
|
+
let out = "";
|
|
113
|
+
for (let i = 0; i < b.length; i++)
|
|
114
|
+
out += b[i].toString(16).padStart(2, "0");
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=signer.test.js.map
|
package/dist/test/wallet.test.js
CHANGED
|
@@ -4,12 +4,23 @@
|
|
|
4
4
|
import { strict as assert } from "node:assert";
|
|
5
5
|
import { describe, it } from "node:test";
|
|
6
6
|
import { createBrowserIdentity } from "@aithos/protocol-client";
|
|
7
|
-
import { AithosSDK, AithosSDKError } from "../src/index.js";
|
|
7
|
+
import { AithosAuth, AithosSDK, AithosSDKError, memoryKeyStore, noopStore, } from "../src/index.js";
|
|
8
|
+
import { serializeRecoveryFile } from "../src/internal/recovery-file.js";
|
|
8
9
|
const APP_DID = "did:aithos:app:test";
|
|
9
|
-
function makeSdk(fetchImpl) {
|
|
10
|
-
const
|
|
10
|
+
async function makeSdk(fetchImpl) {
|
|
11
|
+
const id = createBrowserIdentity("test-handle", "Test User");
|
|
12
|
+
const auth = new AithosAuth({
|
|
13
|
+
authBaseUrl: "https://auth.test",
|
|
14
|
+
fetch: (() => {
|
|
15
|
+
throw new Error("auth not used in wallet tests");
|
|
16
|
+
}),
|
|
17
|
+
sessionStore: noopStore(),
|
|
18
|
+
keyStore: memoryKeyStore(),
|
|
19
|
+
});
|
|
20
|
+
const { text } = serializeRecoveryFile(id);
|
|
21
|
+
await auth.signInWithRecovery({ file: text });
|
|
11
22
|
return new AithosSDK({
|
|
12
|
-
|
|
23
|
+
auth,
|
|
13
24
|
appDid: APP_DID,
|
|
14
25
|
endpoints: { wallet: "https://wallet.example.test" },
|
|
15
26
|
fetch: fetchImpl,
|
|
@@ -27,7 +38,7 @@ describe("wallet.createTopupSession — happy path", () => {
|
|
|
27
38
|
session_id: "cs_test_xyz",
|
|
28
39
|
}), { status: 200, headers: { "content-type": "application/json" } });
|
|
29
40
|
};
|
|
30
|
-
const sdk = makeSdk(fakeFetch);
|
|
41
|
+
const sdk = await makeSdk(fakeFetch);
|
|
31
42
|
const out = await sdk.wallet.createTopupSession({
|
|
32
43
|
packId: "credits-1m",
|
|
33
44
|
successUrl: "https://app.example.com/?topup=success",
|
|
@@ -47,7 +58,7 @@ describe("wallet.createTopupSession — errors", () => {
|
|
|
47
58
|
const fakeFetch = async () => {
|
|
48
59
|
throw new TypeError("Failed to fetch");
|
|
49
60
|
};
|
|
50
|
-
const sdk = makeSdk(fakeFetch);
|
|
61
|
+
const sdk = await makeSdk(fakeFetch);
|
|
51
62
|
await assert.rejects(sdk.wallet.createTopupSession({
|
|
52
63
|
packId: "credits-100k",
|
|
53
64
|
successUrl: "https://app.example.com/?ok",
|
|
@@ -60,7 +71,7 @@ describe("wallet.createTopupSession — errors", () => {
|
|
|
60
71
|
});
|
|
61
72
|
it("surfaces the proxy's structured error (error/detail) on a 4xx", async () => {
|
|
62
73
|
const fakeFetch = async () => new Response(JSON.stringify({ error: "unknown_pack", pack_id: "credits-9999" }), { status: 400, headers: { "content-type": "application/json" } });
|
|
63
|
-
const sdk = makeSdk(fakeFetch);
|
|
74
|
+
const sdk = await makeSdk(fakeFetch);
|
|
64
75
|
await assert.rejects(sdk.wallet.createTopupSession({
|
|
65
76
|
// @ts-expect-error: deliberately invalid pack id
|
|
66
77
|
packId: "credits-9999",
|
|
@@ -78,7 +89,7 @@ describe("wallet.createTopupSession — errors", () => {
|
|
|
78
89
|
status: 500,
|
|
79
90
|
headers: { "content-type": "text/html" },
|
|
80
91
|
});
|
|
81
|
-
const sdk = makeSdk(fakeFetch);
|
|
92
|
+
const sdk = await makeSdk(fakeFetch);
|
|
82
93
|
await assert.rejects(sdk.wallet.createTopupSession({
|
|
83
94
|
packId: "credits-100k",
|
|
84
95
|
successUrl: "https://app.example.com/?ok",
|
|
@@ -95,7 +106,7 @@ describe("wallet.createTopupSession — errors", () => {
|
|
|
95
106
|
status: 200,
|
|
96
107
|
headers: { "content-type": "application/json" },
|
|
97
108
|
});
|
|
98
|
-
const sdk = makeSdk(fakeFetch);
|
|
109
|
+
const sdk = await makeSdk(fakeFetch);
|
|
99
110
|
await assert.rejects(sdk.wallet.createTopupSession({
|
|
100
111
|
packId: "credits-100k",
|
|
101
112
|
successUrl: "https://app.example.com/?ok",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aithos/sdk",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.5",
|
|
4
4
|
"description": "Aithos SDK — high-level TypeScript developer kit for building agentic apps on the Aithos protocol. Wraps @aithos/protocol-client and exposes the Aithos compute proxy and wallet (Stripe top-up) endpoints.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"aithos",
|
|
@@ -52,11 +52,12 @@
|
|
|
52
52
|
"node": ">=20"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
|
-
"@aithos/protocol-client": ">=0.1.0-alpha.
|
|
55
|
+
"@aithos/protocol-client": ">=0.1.0-alpha.11 <0.2.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@aithos/protocol-client": "^0.1.0-alpha.
|
|
58
|
+
"@aithos/protocol-client": "^0.1.0-alpha.11",
|
|
59
59
|
"@types/node": "^24.12.2",
|
|
60
|
+
"fake-indexeddb": "^6.2.5",
|
|
60
61
|
"typescript": "^5.9.2"
|
|
61
62
|
},
|
|
62
63
|
"publishConfig": {
|