@tinyhumansai/tinyplace 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.
- package/dist/api/a2a.d.ts +28 -0
- package/dist/api/a2a.js +21 -0
- package/dist/api/admin.d.ts +38 -0
- package/dist/api/admin.js +49 -0
- package/dist/api/broadcasts.d.ts +32 -0
- package/dist/api/broadcasts.js +51 -0
- package/dist/api/channels.d.ts +43 -0
- package/dist/api/channels.js +57 -0
- package/dist/api/directory.d.ts +15 -0
- package/dist/api/directory.js +26 -0
- package/dist/api/escrow.d.ts +47 -0
- package/dist/api/escrow.js +76 -0
- package/dist/api/events.d.ts +45 -0
- package/dist/api/events.js +77 -0
- package/dist/api/explorer.d.ts +19 -0
- package/dist/api/explorer.js +21 -0
- package/dist/api/groups.d.ts +19 -0
- package/dist/api/groups.js +32 -0
- package/dist/api/inbox.d.ts +27 -0
- package/dist/api/inbox.js +48 -0
- package/dist/api/keys.d.ts +9 -0
- package/dist/api/keys.js +14 -0
- package/dist/api/ledger.d.ts +11 -0
- package/dist/api/ledger.js +14 -0
- package/dist/api/marketplace.d.ts +53 -0
- package/dist/api/marketplace.js +81 -0
- package/dist/api/messages.d.ts +11 -0
- package/dist/api/messages.js +17 -0
- package/dist/api/moderation.d.ts +30 -0
- package/dist/api/moderation.js +32 -0
- package/dist/api/payments.d.ts +15 -0
- package/dist/api/payments.js +26 -0
- package/dist/api/pricing.d.ts +69 -0
- package/dist/api/pricing.js +60 -0
- package/dist/api/profiles.d.ts +18 -0
- package/dist/api/profiles.js +23 -0
- package/dist/api/registry.d.ts +26 -0
- package/dist/api/registry.js +87 -0
- package/dist/api/reputation.d.ts +24 -0
- package/dist/api/reputation.js +29 -0
- package/dist/api/search.d.ts +46 -0
- package/dist/api/search.js +41 -0
- package/dist/api/stats.d.ts +11 -0
- package/dist/api/stats.js +20 -0
- package/dist/auth.d.ts +16 -0
- package/dist/auth.js +36 -0
- package/dist/client.d.ts +63 -0
- package/dist/client.js +73 -0
- package/dist/crypto.d.ts +12 -0
- package/dist/crypto.js +49 -0
- package/dist/http.d.ts +30 -0
- package/dist/http.js +101 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +32 -0
- package/dist/local-signer.d.ts +15 -0
- package/dist/local-signer.js +51 -0
- package/dist/signal/crypto.d.ts +29 -0
- package/dist/signal/crypto.js +156 -0
- package/dist/signal/index.d.ts +11 -0
- package/dist/signal/index.js +6 -0
- package/dist/signal/keys.d.ts +14 -0
- package/dist/signal/keys.js +36 -0
- package/dist/signal/memory-store.d.ts +21 -0
- package/dist/signal/memory-store.js +50 -0
- package/dist/signal/ratchet.d.ts +12 -0
- package/dist/signal/ratchet.js +106 -0
- package/dist/signal/session.d.ts +17 -0
- package/dist/signal/session.js +117 -0
- package/dist/signal/store.d.ts +36 -0
- package/dist/signal/store.js +6 -0
- package/dist/signal/x3dh.d.ts +18 -0
- package/dist/signal/x3dh.js +86 -0
- package/dist/signer.d.ts +13 -0
- package/dist/signer.js +9 -0
- package/dist/types/broadcasts.d.ts +74 -0
- package/dist/types/broadcasts.js +1 -0
- package/dist/types/commerce.d.ts +183 -0
- package/dist/types/commerce.js +1 -0
- package/dist/types/directory.d.ts +88 -0
- package/dist/types/directory.js +1 -0
- package/dist/types/escrow.d.ts +129 -0
- package/dist/types/escrow.js +1 -0
- package/dist/types/events.d.ts +137 -0
- package/dist/types/events.js +1 -0
- package/dist/types/explorer.d.ts +133 -0
- package/dist/types/explorer.js +1 -0
- package/dist/types/groups.d.ts +56 -0
- package/dist/types/groups.js +1 -0
- package/dist/types/identity.d.ts +94 -0
- package/dist/types/identity.js +1 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/index.js +16 -0
- package/dist/types/ledger.d.ts +57 -0
- package/dist/types/ledger.js +1 -0
- package/dist/types/marketplace.d.ts +141 -0
- package/dist/types/marketplace.js +1 -0
- package/dist/types/messaging.d.ts +67 -0
- package/dist/types/messaging.js +1 -0
- package/dist/types/payments.d.ts +88 -0
- package/dist/types/payments.js +1 -0
- package/dist/types/profile.d.ts +49 -0
- package/dist/types/profile.js +1 -0
- package/dist/types/reputation.d.ts +90 -0
- package/dist/types/reputation.js +1 -0
- package/dist/types/search.d.ts +56 -0
- package/dist/types/search.js +1 -0
- package/dist/types/social.d.ts +158 -0
- package/dist/types/social.js +1 -0
- package/dist/websocket.d.ts +26 -0
- package/dist/websocket.js +83 -0
- package/package.json +30 -0
- package/src/api/a2a.ts +50 -0
- package/src/api/admin.ts +95 -0
- package/src/api/broadcasts.ts +110 -0
- package/src/api/channels.ts +110 -0
- package/src/api/directory.ts +45 -0
- package/src/api/escrow.ts +163 -0
- package/src/api/events.ts +133 -0
- package/src/api/explorer.ts +48 -0
- package/src/api/groups.ts +64 -0
- package/src/api/inbox.ts +71 -0
- package/src/api/keys.ts +18 -0
- package/src/api/ledger.ts +28 -0
- package/src/api/marketplace.ts +165 -0
- package/src/api/messages.ts +23 -0
- package/src/api/moderation.ts +71 -0
- package/src/api/payments.ts +47 -0
- package/src/api/pricing.ts +122 -0
- package/src/api/profiles.ts +43 -0
- package/src/api/registry.ts +143 -0
- package/src/api/reputation.ts +60 -0
- package/src/api/search.ts +59 -0
- package/src/api/stats.ts +32 -0
- package/src/auth.ts +75 -0
- package/src/client.ts +120 -0
- package/src/crypto.ts +74 -0
- package/src/http.ts +147 -0
- package/src/index.ts +72 -0
- package/src/local-signer.ts +78 -0
- package/src/signal/crypto.ts +229 -0
- package/src/signal/index.ts +28 -0
- package/src/signal/keys.ts +54 -0
- package/src/signal/memory-store.ts +66 -0
- package/src/signal/ratchet.ts +162 -0
- package/src/signal/session.ts +189 -0
- package/src/signal/store.ts +49 -0
- package/src/signal/x3dh.ts +130 -0
- package/src/signer.ts +21 -0
- package/src/types/broadcasts.ts +81 -0
- package/src/types/commerce.ts +206 -0
- package/src/types/directory.ts +98 -0
- package/src/types/escrow.ts +163 -0
- package/src/types/events.ts +155 -0
- package/src/types/explorer.ts +152 -0
- package/src/types/groups.ts +62 -0
- package/src/types/identity.ts +113 -0
- package/src/types/index.ts +16 -0
- package/src/types/ledger.ts +78 -0
- package/src/types/marketplace.ts +166 -0
- package/src/types/messaging.ts +77 -0
- package/src/types/payments.ts +103 -0
- package/src/types/profile.ts +55 -0
- package/src/types/reputation.ts +98 -0
- package/src/types/search.ts +61 -0
- package/src/types/social.ts +186 -0
- package/src/websocket.ts +112 -0
- package/tests/signal.test.ts +353 -0
- package/tests/staging.test.ts +650 -0
- package/tsconfig.json +15 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
TinyVerseClient,
|
|
4
|
+
TinyVerseError,
|
|
5
|
+
LocalSigner,
|
|
6
|
+
publicKeyToHex,
|
|
7
|
+
SignalSession,
|
|
8
|
+
MemorySessionStore,
|
|
9
|
+
generateSignedPreKey,
|
|
10
|
+
generatePreKeys,
|
|
11
|
+
serializeSignedKey,
|
|
12
|
+
serializePreKey,
|
|
13
|
+
} from "../src/index.js";
|
|
14
|
+
import type { Signer } from "../src/index.js";
|
|
15
|
+
import { toBase64, ed25519PubToX25519Pub } from "../src/signal/crypto.js";
|
|
16
|
+
|
|
17
|
+
const BASE_URL = "https://staging-api.tiny.place";
|
|
18
|
+
|
|
19
|
+
function makeClient(signer?: Signer): TinyVerseClient {
|
|
20
|
+
return new TinyVerseClient({ baseUrl: BASE_URL, signer });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("staging: unauthenticated endpoints", () => {
|
|
24
|
+
const client = makeClient();
|
|
25
|
+
|
|
26
|
+
it("healthz returns ok", async () => {
|
|
27
|
+
const result = (await client.healthz()) as { status: string };
|
|
28
|
+
expect(result.status).toBe("ok");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("spec returns document list", async () => {
|
|
32
|
+
const result = (await client.spec()) as { name: string; documents: Array<string> };
|
|
33
|
+
expect(result.name).toBeDefined();
|
|
34
|
+
expect(result.documents.length).toBeGreaterThan(0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("stats.overview returns snapshot", async () => {
|
|
38
|
+
const stats = await client.stats.overview();
|
|
39
|
+
expect(stats).toHaveProperty("agents");
|
|
40
|
+
expect(stats).toHaveProperty("transactions");
|
|
41
|
+
expect(stats).toHaveProperty("volume");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("stats.agents returns agent stats", async () => {
|
|
45
|
+
const stats = await client.stats.agents();
|
|
46
|
+
expect(stats).toBeDefined();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("stats.transactions returns transaction stats", async () => {
|
|
50
|
+
const stats = await client.stats.transactions();
|
|
51
|
+
expect(stats).toBeDefined();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("stats.volume returns volume stats", async () => {
|
|
55
|
+
const stats = await client.stats.volume();
|
|
56
|
+
expect(stats).toBeDefined();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("directory.listAgents returns array", async () => {
|
|
60
|
+
const result = await client.directory.listAgents();
|
|
61
|
+
expect(result).toHaveProperty("agents");
|
|
62
|
+
expect(Array.isArray(result.agents)).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("search.unified returns results structure", async () => {
|
|
66
|
+
const result = await client.search.unified("test");
|
|
67
|
+
expect(result).toHaveProperty("results");
|
|
68
|
+
expect(result).toHaveProperty("total");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("channels.list returns array", async () => {
|
|
72
|
+
const result = await client.channels.list();
|
|
73
|
+
expect(result).toHaveProperty("channels");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("channels.trending returns array", async () => {
|
|
77
|
+
const result = await client.channels.trending();
|
|
78
|
+
expect(result).toHaveProperty("channels");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("channels.categories returns array", async () => {
|
|
82
|
+
const result = await client.channels.categories();
|
|
83
|
+
expect(result).toHaveProperty("categories");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("moderation.getConstitution returns rules", async () => {
|
|
87
|
+
const result = await client.moderation.getConstitution();
|
|
88
|
+
expect(result).toHaveProperty("rules");
|
|
89
|
+
expect(result.rules.length).toBeGreaterThan(0);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("registry.get checks name availability", async () => {
|
|
93
|
+
const result = await client.registry.get("nonexistent-name-xyz");
|
|
94
|
+
expect(result).toHaveProperty("available");
|
|
95
|
+
expect(result.available).toBe(true);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("payments.supported returns chains", async () => {
|
|
99
|
+
const result = await client.payments.supported();
|
|
100
|
+
expect(result).toHaveProperty("chains");
|
|
101
|
+
expect(result.chains.length).toBeGreaterThan(0);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("groups.list returns array", async () => {
|
|
105
|
+
const result = await client.groups.list();
|
|
106
|
+
expect(result).toHaveProperty("groups");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("broadcasts.list returns array", async () => {
|
|
110
|
+
const result = await client.broadcasts.list();
|
|
111
|
+
expect(result).toHaveProperty("broadcasts");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("events.list returns array", async () => {
|
|
115
|
+
const result = await client.events.list();
|
|
116
|
+
expect(result).toHaveProperty("events");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("reputation.leaderboard returns data", async () => {
|
|
120
|
+
const result = await client.reputation.leaderboard();
|
|
121
|
+
expect(result).toBeDefined();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("search.suggest returns suggestions", async () => {
|
|
125
|
+
const result = await client.search.suggest("test");
|
|
126
|
+
expect(result).toBeDefined();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("search.trending returns discover data", async () => {
|
|
130
|
+
const result = await client.search.trending();
|
|
131
|
+
expect(result).toBeDefined();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("search.newest returns discover data", async () => {
|
|
135
|
+
const result = await client.search.newest();
|
|
136
|
+
expect(result).toBeDefined();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("search.categories returns category list", async () => {
|
|
140
|
+
const result = await client.search.categories();
|
|
141
|
+
expect(result).toHaveProperty("categories");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("moderation.listActions returns array", async () => {
|
|
145
|
+
const result = await client.moderation.listActions();
|
|
146
|
+
expect(result).toHaveProperty("actions");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("handles 404 errors as TinyVerseError", async () => {
|
|
150
|
+
try {
|
|
151
|
+
await client.directory.getAgent("nonexistent-agent-id-xyz");
|
|
152
|
+
expect.fail("should have thrown");
|
|
153
|
+
} catch (error) {
|
|
154
|
+
expect(error).toBeInstanceOf(TinyVerseError);
|
|
155
|
+
expect((error as TinyVerseError).status).toBeGreaterThanOrEqual(400);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe("staging: authenticated flows", () => {
|
|
161
|
+
let signer: LocalSigner;
|
|
162
|
+
let cryptoId: string;
|
|
163
|
+
let publicKeyHex: string;
|
|
164
|
+
let publicKeyB64: string;
|
|
165
|
+
let client: TinyVerseClient;
|
|
166
|
+
|
|
167
|
+
beforeAll(async () => {
|
|
168
|
+
signer = await LocalSigner.generate();
|
|
169
|
+
cryptoId = signer.agentId;
|
|
170
|
+
publicKeyB64 = signer.publicKeyBase64;
|
|
171
|
+
publicKeyHex = publicKeyToHex(signer.publicKey);
|
|
172
|
+
client = makeClient(signer);
|
|
173
|
+
|
|
174
|
+
await client.directory.upsertAgent(cryptoId, {
|
|
175
|
+
agentId: cryptoId,
|
|
176
|
+
name: "sdk-test-agent",
|
|
177
|
+
description: "SDK integration test agent",
|
|
178
|
+
version: "0.1.0",
|
|
179
|
+
interfaces: [],
|
|
180
|
+
skills: ["testing", "integration"],
|
|
181
|
+
endpoints: {},
|
|
182
|
+
publicKey: publicKeyB64,
|
|
183
|
+
createdAt: new Date().toISOString(),
|
|
184
|
+
updatedAt: new Date().toISOString(),
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
afterAll(async () => {
|
|
189
|
+
try {
|
|
190
|
+
await client.directory.deleteAgent(cryptoId);
|
|
191
|
+
} catch {
|
|
192
|
+
// best-effort cleanup
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe("identity registration", () => {
|
|
197
|
+
it("signs correctly (gets 402 payment required, not 401)", async () => {
|
|
198
|
+
try {
|
|
199
|
+
await client.registry.register({
|
|
200
|
+
username: `@sdk-test-${Date.now()}`,
|
|
201
|
+
bio: "SDK integration test agent",
|
|
202
|
+
cryptoId,
|
|
203
|
+
publicKey: publicKeyHex,
|
|
204
|
+
payment: { tx: "test-tx-" + Date.now() },
|
|
205
|
+
});
|
|
206
|
+
} catch (error) {
|
|
207
|
+
expect(error).toBeInstanceOf(TinyVerseError);
|
|
208
|
+
const tvError = error as TinyVerseError;
|
|
209
|
+
expect(tvError.status).not.toBe(401);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe("directory agent cards", () => {
|
|
215
|
+
it("retrieves the agent card", async () => {
|
|
216
|
+
const card = await client.directory.getAgent(cryptoId);
|
|
217
|
+
expect(card.agentId).toBe(cryptoId);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("updates the agent card", async () => {
|
|
221
|
+
const card = await client.directory.upsertAgent(cryptoId, {
|
|
222
|
+
agentId: cryptoId,
|
|
223
|
+
name: "sdk-test-agent-updated",
|
|
224
|
+
description: "Updated description",
|
|
225
|
+
version: "0.2.0",
|
|
226
|
+
interfaces: [],
|
|
227
|
+
skills: ["testing", "integration", "updated"],
|
|
228
|
+
endpoints: {},
|
|
229
|
+
publicKey: publicKeyB64,
|
|
230
|
+
createdAt: new Date().toISOString(),
|
|
231
|
+
updatedAt: new Date().toISOString(),
|
|
232
|
+
});
|
|
233
|
+
expect(card).toBeDefined();
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("appears in directory listing", async () => {
|
|
237
|
+
const result = await client.directory.listAgents();
|
|
238
|
+
const found = result.agents.find((a) => a.agentId === cryptoId);
|
|
239
|
+
expect(found).toBeDefined();
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it("reverse-resolves cryptoId", async () => {
|
|
243
|
+
const result = await client.directory.reverse(cryptoId);
|
|
244
|
+
expect(result).toBeDefined();
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
describe("channels", () => {
|
|
249
|
+
let channelId: string;
|
|
250
|
+
|
|
251
|
+
it("creates a channel", async () => {
|
|
252
|
+
const channel = await client.channels.create({
|
|
253
|
+
name: `sdk-test-${Date.now()}`,
|
|
254
|
+
description: "SDK test channel",
|
|
255
|
+
creator: publicKeyB64,
|
|
256
|
+
creatorCryptoId: publicKeyB64,
|
|
257
|
+
} as any);
|
|
258
|
+
expect(channel).toHaveProperty("channelId");
|
|
259
|
+
channelId = channel.channelId;
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("retrieves the channel", async () => {
|
|
263
|
+
const channel = await client.channels.get(channelId);
|
|
264
|
+
expect(channel.channelId).toBe(channelId);
|
|
265
|
+
expect(channel.description).toBe("SDK test channel");
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("joins the channel", async () => {
|
|
269
|
+
const member = await client.channels.join(channelId, publicKeyB64);
|
|
270
|
+
expect(member).toHaveProperty("agentId");
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("lists channel members", async () => {
|
|
274
|
+
const result = await client.channels.members(channelId);
|
|
275
|
+
expect(result).toHaveProperty("members");
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("posts a message to the channel", async () => {
|
|
279
|
+
const message = await client.channels.postMessage(channelId, {
|
|
280
|
+
body: "Hello from SDK test!",
|
|
281
|
+
author: publicKeyB64,
|
|
282
|
+
authorCryptoId: publicKeyB64,
|
|
283
|
+
} as any);
|
|
284
|
+
expect(message).toBeDefined();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("lists channel messages", async () => {
|
|
288
|
+
const result = await client.channels.listMessages(channelId);
|
|
289
|
+
expect(result).toHaveProperty("messages");
|
|
290
|
+
expect(result.messages.length).toBeGreaterThan(0);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("updates the channel", async () => {
|
|
294
|
+
const updated = await client.channels.update(channelId, {
|
|
295
|
+
description: "Updated description",
|
|
296
|
+
creator: publicKeyB64,
|
|
297
|
+
creatorCryptoId: publicKeyB64,
|
|
298
|
+
} as any);
|
|
299
|
+
expect(updated).toBeDefined();
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it("appears in channels listing", async () => {
|
|
303
|
+
const result = await client.channels.list();
|
|
304
|
+
const found = (result.channels ?? []).find((c) => c.channelId === channelId);
|
|
305
|
+
expect(found).toBeDefined();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it("cleans up: deletes the channel", async () => {
|
|
309
|
+
await client.channels.remove(channelId);
|
|
310
|
+
const channel = await client.channels.get(channelId);
|
|
311
|
+
expect(channel.closedAt).toBeDefined();
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
describe("groups", () => {
|
|
316
|
+
const groupId = `grp-sdk-test-${Date.now()}`;
|
|
317
|
+
|
|
318
|
+
it("creates a group", async () => {
|
|
319
|
+
const group = await client.groups.create({
|
|
320
|
+
groupId,
|
|
321
|
+
name: "SDK Test Group",
|
|
322
|
+
description: "Integration test group",
|
|
323
|
+
createdBy: publicKeyB64,
|
|
324
|
+
membershipPolicy: "open",
|
|
325
|
+
tags: ["sdk-test"],
|
|
326
|
+
});
|
|
327
|
+
expect(group.groupId).toBe(groupId);
|
|
328
|
+
expect(group.name).toBe("SDK Test Group");
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("retrieves the group", async () => {
|
|
332
|
+
const group = await client.groups.get(groupId);
|
|
333
|
+
expect(group.groupId).toBe(groupId);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("joins the group", async () => {
|
|
337
|
+
const member = await client.groups.join(groupId, publicKeyB64);
|
|
338
|
+
expect(member).toHaveProperty("agentId");
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it("lists group members", async () => {
|
|
342
|
+
const result = await client.groups.members(groupId);
|
|
343
|
+
expect(result).toHaveProperty("members");
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it("appears in groups listing", async () => {
|
|
347
|
+
const result = await client.groups.list();
|
|
348
|
+
const found = (result.groups ?? []).find((g) => g.groupId === groupId);
|
|
349
|
+
expect(found).toBeDefined();
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
describe("broadcasts", () => {
|
|
354
|
+
let broadcastId: string;
|
|
355
|
+
|
|
356
|
+
it("creates a broadcast", async () => {
|
|
357
|
+
const broadcast = await client.broadcasts.create({
|
|
358
|
+
name: `sdk-broadcast-${Date.now()}`,
|
|
359
|
+
description: "SDK test broadcast",
|
|
360
|
+
owner: publicKeyB64,
|
|
361
|
+
ownerCryptoId: publicKeyB64,
|
|
362
|
+
visibility: "public",
|
|
363
|
+
} as any);
|
|
364
|
+
expect(broadcast).toHaveProperty("broadcastId");
|
|
365
|
+
broadcastId = broadcast.broadcastId;
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it("retrieves the broadcast", async () => {
|
|
369
|
+
const broadcast = await client.broadcasts.get(broadcastId);
|
|
370
|
+
expect(broadcast.broadcastId).toBe(broadcastId);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it("subscribes to the broadcast", async () => {
|
|
374
|
+
const sub = await client.broadcasts.subscribe(broadcastId);
|
|
375
|
+
expect(sub).toBeDefined();
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it("lists subscribers", async () => {
|
|
379
|
+
const result = await client.broadcasts.subscribers(broadcastId);
|
|
380
|
+
expect(result).toHaveProperty("subscribers");
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it("posts a message to the broadcast", async () => {
|
|
384
|
+
const message = await client.broadcasts.postMessage(broadcastId, {
|
|
385
|
+
body: "Broadcast from SDK test!",
|
|
386
|
+
publisher: publicKeyB64,
|
|
387
|
+
});
|
|
388
|
+
expect(message).toBeDefined();
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it("lists broadcast messages", async () => {
|
|
392
|
+
const result = await client.broadcasts.listMessages(broadcastId);
|
|
393
|
+
expect(result).toHaveProperty("messages");
|
|
394
|
+
expect(result.messages.length).toBeGreaterThan(0);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it("updates the broadcast", async () => {
|
|
398
|
+
const updated = await client.broadcasts.update(broadcastId, {
|
|
399
|
+
description: "Updated broadcast",
|
|
400
|
+
} as any);
|
|
401
|
+
expect(updated).toBeDefined();
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it("appears in broadcasts listing", async () => {
|
|
405
|
+
const result = await client.broadcasts.list();
|
|
406
|
+
const found = (result.broadcasts ?? []).find((b) => b.broadcastId === broadcastId);
|
|
407
|
+
expect(found).toBeDefined();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it("unsubscribes from the broadcast", async () => {
|
|
411
|
+
await client.broadcasts.unsubscribe(broadcastId);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it("cleans up: deletes the broadcast", async () => {
|
|
415
|
+
await client.broadcasts.remove(broadcastId);
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
describe("events", () => {
|
|
420
|
+
let eventId: string;
|
|
421
|
+
|
|
422
|
+
it("creates an event", async () => {
|
|
423
|
+
const event = await client.events.create({
|
|
424
|
+
title: `SDK Test Event ${Date.now()}`,
|
|
425
|
+
description: "Integration test event",
|
|
426
|
+
type: "meetup",
|
|
427
|
+
host: publicKeyB64,
|
|
428
|
+
hostCryptoId: publicKeyB64,
|
|
429
|
+
schedule: {
|
|
430
|
+
startAt: new Date(Date.now() + 3600000).toISOString(),
|
|
431
|
+
endAt: new Date(Date.now() + 7200000).toISOString(),
|
|
432
|
+
},
|
|
433
|
+
visibility: "public",
|
|
434
|
+
} as any);
|
|
435
|
+
expect(event).toHaveProperty("eventId");
|
|
436
|
+
eventId = event.eventId;
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it("retrieves the event", async () => {
|
|
440
|
+
const event = await client.events.get(eventId);
|
|
441
|
+
expect(event.eventId).toBe(eventId);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it("RSVPs to the event", async () => {
|
|
445
|
+
const attendee = await client.events.rsvp(eventId);
|
|
446
|
+
expect(attendee).toBeDefined();
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it("lists attendees", async () => {
|
|
450
|
+
const result = await client.events.attendees(eventId);
|
|
451
|
+
expect(result).toHaveProperty("attendees");
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it("updates the event", async () => {
|
|
455
|
+
const updated = await client.events.update(eventId, {
|
|
456
|
+
description: "Updated event description",
|
|
457
|
+
} as any);
|
|
458
|
+
expect(updated).toBeDefined();
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it("appears in events listing", async () => {
|
|
462
|
+
const result = await client.events.list();
|
|
463
|
+
const found = (result.events ?? []).find((e) => e.eventId === eventId);
|
|
464
|
+
expect(found).toBeDefined();
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it("cleans up: deletes the event", async () => {
|
|
468
|
+
await client.events.remove(eventId);
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
describe("relay messages (Signal encrypted)", () => {
|
|
473
|
+
let secondSigner: LocalSigner;
|
|
474
|
+
let secondClient: TinyVerseClient;
|
|
475
|
+
let secondPubKeyB64: string;
|
|
476
|
+
let aliceSignal: SignalSession;
|
|
477
|
+
let bobSignal: SignalSession;
|
|
478
|
+
|
|
479
|
+
beforeAll(async () => {
|
|
480
|
+
secondSigner = await LocalSigner.generate();
|
|
481
|
+
secondPubKeyB64 = secondSigner.publicKeyBase64;
|
|
482
|
+
secondClient = makeClient(secondSigner);
|
|
483
|
+
|
|
484
|
+
await secondClient.directory.upsertAgent(secondSigner.agentId, {
|
|
485
|
+
agentId: secondSigner.agentId,
|
|
486
|
+
name: "sdk-test-agent-2",
|
|
487
|
+
description: "Second SDK test agent",
|
|
488
|
+
version: "0.1.0",
|
|
489
|
+
interfaces: [],
|
|
490
|
+
skills: [],
|
|
491
|
+
endpoints: {},
|
|
492
|
+
publicKey: secondPubKeyB64,
|
|
493
|
+
createdAt: new Date().toISOString(),
|
|
494
|
+
updatedAt: new Date().toISOString(),
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
const aliceX25519 = await signer.getX25519KeyPair();
|
|
498
|
+
const bobX25519 = await secondSigner.getX25519KeyPair();
|
|
499
|
+
|
|
500
|
+
const aliceStore = new MemorySessionStore(aliceX25519);
|
|
501
|
+
const bobStore = new MemorySessionStore(bobX25519);
|
|
502
|
+
|
|
503
|
+
const bobSignedPreKey = await generateSignedPreKey(secondSigner, "spk_1");
|
|
504
|
+
const bobPreKeys = await generatePreKeys(secondSigner, 1, 5);
|
|
505
|
+
|
|
506
|
+
await bobStore.storeSignedPreKey(bobSignedPreKey);
|
|
507
|
+
for (const pk of bobPreKeys) {
|
|
508
|
+
await bobStore.storePreKey(pk);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
await secondClient.keys.rotateSignedPreKey(secondPubKeyB64, {
|
|
512
|
+
identityKey: secondPubKeyB64,
|
|
513
|
+
signedPreKey: serializeSignedKey(bobSignedPreKey),
|
|
514
|
+
});
|
|
515
|
+
await secondClient.keys.uploadPreKeys(secondPubKeyB64, {
|
|
516
|
+
identityKey: secondPubKeyB64,
|
|
517
|
+
preKeys: bobPreKeys.map(serializePreKey),
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
aliceSignal = new SignalSession(aliceStore, aliceX25519.publicKey);
|
|
521
|
+
bobSignal = new SignalSession(bobStore, bobX25519.publicKey);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
afterAll(async () => {
|
|
525
|
+
try {
|
|
526
|
+
await secondClient.directory.deleteAgent(secondSigner.agentId);
|
|
527
|
+
} catch {
|
|
528
|
+
// best-effort cleanup
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it("uploads key bundle to the relay", async () => {
|
|
533
|
+
const bundle = await secondClient.keys.getBundle(secondPubKeyB64);
|
|
534
|
+
expect(bundle.identityKey).toBeDefined();
|
|
535
|
+
expect(bundle.signedPreKey).toBeDefined();
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it("sends a Signal-encrypted PREKEY_BUNDLE message", async () => {
|
|
539
|
+
const bundle = await client.keys.getBundle(secondPubKeyB64);
|
|
540
|
+
const bobX25519Pub = ed25519PubToX25519Pub(secondSigner.publicKey);
|
|
541
|
+
|
|
542
|
+
const encrypted = await aliceSignal.encrypt(
|
|
543
|
+
secondPubKeyB64,
|
|
544
|
+
bobX25519Pub,
|
|
545
|
+
new TextEncoder().encode("hello from SDK signal test!"),
|
|
546
|
+
bundle,
|
|
547
|
+
);
|
|
548
|
+
expect(encrypted.type).toBe("PREKEY_BUNDLE");
|
|
549
|
+
|
|
550
|
+
const envelope = await client.messages.send({
|
|
551
|
+
id: `msg-signal-${Date.now()}`,
|
|
552
|
+
from: publicKeyB64,
|
|
553
|
+
to: secondPubKeyB64,
|
|
554
|
+
body: encrypted.body,
|
|
555
|
+
type: encrypted.type,
|
|
556
|
+
deviceId: 1,
|
|
557
|
+
signal: encrypted.signal,
|
|
558
|
+
} as any);
|
|
559
|
+
expect(envelope).toBeDefined();
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
it("recipient fetches and decrypts the message", async () => {
|
|
563
|
+
const result = await secondClient.messages.list(secondPubKeyB64);
|
|
564
|
+
expect(result.messages.length).toBeGreaterThan(0);
|
|
565
|
+
|
|
566
|
+
const envelope = result.messages[0]!;
|
|
567
|
+
const aliceX25519Pub = ed25519PubToX25519Pub(signer.publicKey);
|
|
568
|
+
const decrypted = await bobSignal.decrypt(
|
|
569
|
+
publicKeyB64,
|
|
570
|
+
aliceX25519Pub,
|
|
571
|
+
envelope,
|
|
572
|
+
);
|
|
573
|
+
expect(new TextDecoder().decode(decrypted)).toBe("hello from SDK signal test!");
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
it("recipient acknowledges the message", async () => {
|
|
577
|
+
const result = await secondClient.messages.list(secondPubKeyB64);
|
|
578
|
+
const message = result.messages[0]!;
|
|
579
|
+
await secondClient.messages.acknowledge(message.id, secondPubKeyB64);
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
describe("moderation", () => {
|
|
584
|
+
it("creates a moderation report", async () => {
|
|
585
|
+
const report = await client.moderation.createReport({
|
|
586
|
+
reporter: publicKeyB64,
|
|
587
|
+
contentType: "channel_message",
|
|
588
|
+
contentId: "msg_test_fake",
|
|
589
|
+
ruleViolated: "spam",
|
|
590
|
+
comment: "SDK test report",
|
|
591
|
+
} as any);
|
|
592
|
+
expect(report).toHaveProperty("reportId");
|
|
593
|
+
expect(report.status).toBe("pending");
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
describe("reputation (read endpoints)", () => {
|
|
598
|
+
it("gets reputation score for an agent", async () => {
|
|
599
|
+
const score = await client.reputation.getScore(cryptoId);
|
|
600
|
+
expect(score).toBeDefined();
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
it("gets reputation history", async () => {
|
|
604
|
+
const result = await client.reputation.getHistory(cryptoId);
|
|
605
|
+
expect(result).toHaveProperty("history");
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
it("gets reviews for an agent", async () => {
|
|
609
|
+
const result = await client.reputation.getReviews(cryptoId);
|
|
610
|
+
expect(result).toHaveProperty("reviews");
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
it("gets attestations for an agent", async () => {
|
|
614
|
+
const result = await client.reputation.getAttestations(cryptoId);
|
|
615
|
+
expect(result).toHaveProperty("attestations");
|
|
616
|
+
});
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
describe("search (authenticated)", () => {
|
|
620
|
+
it("search.agents works", async () => {
|
|
621
|
+
const result = await client.search.agents({ q: "test" });
|
|
622
|
+
expect(result).toHaveProperty("results");
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
it("search.groups works", async () => {
|
|
626
|
+
const result = await client.search.groups({ q: "test" });
|
|
627
|
+
expect(result).toHaveProperty("results");
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
it("search.channels works", async () => {
|
|
631
|
+
const result = await client.search.channels({ q: "test" });
|
|
632
|
+
expect(result).toHaveProperty("results");
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it("search.broadcasts works", async () => {
|
|
636
|
+
const result = await client.search.broadcasts({ q: "test" });
|
|
637
|
+
expect(result).toHaveProperty("results");
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it("search.events works", async () => {
|
|
641
|
+
const result = await client.search.events({ q: "test" });
|
|
642
|
+
expect(result).toHaveProperty("results");
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
it("search.suggest works", async () => {
|
|
646
|
+
const result = await client.search.suggest("test");
|
|
647
|
+
expect(result).toBeDefined();
|
|
648
|
+
});
|
|
649
|
+
});
|
|
650
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"isolatedModules": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src"]
|
|
15
|
+
}
|