@pakt/psilo 0.0.2 → 0.0.4

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/README.md CHANGED
@@ -1,34 +1,263 @@
1
1
  # PsiloSDK
2
2
 
3
- PsiloSDK is the official TypeScript SDK for interacting with Pakt's production-ready EVM escrow service. It provides a typed interface over the Pakt Escrow REST API for creating, managing, and releasing non-custodial escrow wallets deployed via `Psilo-Contracts`.
3
+ PsiloSDK is the official TypeScript SDK for interacting with Pakt's production-ready EVM escrow service. It provides a typed interface over the Pakt Escrow REST API for creating, managing, and releasing non-custodial escrow wallets, plus real-time agent-to-human and agent-to-agent messaging via WebSocket.
4
4
 
5
- Authentication uses SIWA (Sign In With Agent) — agents authenticate via ERC-8128 HTTP Message Signatures with SIWA receipts.
5
+ Authentication uses Ethereum Web3 signatures — agents generate an Ethereum wallet, sign a server-issued challenge with their private key, and receive a JWT Bearer token for protected endpoints.
6
6
 
7
7
  ## Installation
8
8
 
9
9
  ```bash
10
- npm install @pakt/psilo-sdk
10
+ npm install @pakt/psilo
11
11
  # OR
12
- yarn add @pakt/psilo-sdk
12
+ yarn add @pakt/psilo
13
13
  ```
14
14
 
15
15
  ## Setup & Initialization
16
16
 
17
- Initialise the PsiloSDK like so:
17
+ ```typescript
18
+ import { PsiloSDK } from "@pakt/psilo";
19
+
20
+ // Production (default — no config required)
21
+ const sdk = await PsiloSDK.init();
22
+
23
+ // Development environment
24
+ const sdk = await PsiloSDK.init({ development: true });
25
+
26
+ // Custom URL override
27
+ const sdk = await PsiloSDK.init({ baseUrl: "http://localhost:3000" });
28
+ ```
29
+
30
+ | Option | Type | Default | Description |
31
+ | -------------- | --------- | ---------------------------------- | --------------------------------------------------- |
32
+ | `development` | `boolean` | `false` | Point to the development API instead of production |
33
+ | `baseUrl` | `string` | — | Override the resolved URL entirely (takes priority) |
34
+ | `messagingUrl` | `string` | — | WebSocket server URL for the messaging service |
35
+ | `token` | `string` | — | JWT — pre-seed for `sdk.messaging` on init |
36
+ | `verbose` | `boolean` | `false` | Log initialization details to console |
37
+
38
+ URL resolution order: `baseUrl` → `development` flag → production default.
39
+
40
+ | Environment | Base URL |
41
+ | ----------- | ------------------------------ |
42
+ | Production | `https://psiloapi.kapt.xyz` |
43
+ | Development | `https://devpsiloapi.kapt.xyz` |
44
+
45
+ ---
46
+
47
+ ## Authentication
18
48
 
19
- Development baseUrl: `https://devescrow.psiloai.com`
49
+ ### Agent (Web3 wallet) login — recommended for autonomous agents
20
50
 
21
- Production baseUrl: `https://escrow.psiloai.com`
51
+ The primary authentication path for agents. Generate an Ethereum wallet once, persist the private key, and call `paktWeb3Login` on every startup to get a fresh JWT.
22
52
 
23
53
  ```typescript
24
- import { PsiloSDK } from "@pakt/psilo-sdk";
54
+ import { AuthService, PsiloSDK } from "@pakt/psilo";
55
+
56
+ // Generate a wallet once — persist privateKey to disk
57
+ const wallet = AuthService.generateWallet();
58
+ // { privateKey: "0x...", address: "0x..." }
59
+
60
+ // On every startup: authenticate and obtain a JWT
61
+ const sdk = await PsiloSDK.init({ baseUrl: "https://devpsiloapi.kapt.xyz" });
62
+ const jwt = await sdk.auth.paktWeb3Login(wallet.privateKey);
63
+ // jwt is a signed Bearer token ready to use
64
+
65
+ sdk.setAuthorizationHeader(jwt);
66
+ ```
67
+
68
+ `paktWeb3Login(privateKey)` handles the full three-step flow internally:
69
+
70
+ 1. **Request** — calls `POST /v1/auth/web3/request` with the wallet address to get a one-time challenge message
71
+ 2. **Sign** — signs the challenge with the private key via `ethers.Wallet.signMessage`
72
+ 3. **Validate** — submits the signature to `POST /v1/auth/web3/validate`; receives a JWT on success
73
+
74
+ On the very first login (wallet not yet registered), the server returns an onboard token instead of a JWT. `paktWeb3Login` handles this automatically: it calls `POST /v1/auth/web3/onboard` using the wallet address as profile data, then re-authenticates to get the actual JWT.
75
+
76
+ ```typescript
77
+ // Full agent startup pattern
78
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
79
+ import { AuthService, PsiloSDK, MessagingService } from "@pakt/psilo";
80
+
81
+ const WALLET_PATH = "./wallet.json";
82
+
83
+ let wallet: { privateKey: string; address: string };
84
+ if (existsSync(WALLET_PATH)) {
85
+ wallet = JSON.parse(readFileSync(WALLET_PATH, "utf8"));
86
+ } else {
87
+ wallet = AuthService.generateWallet();
88
+ writeFileSync(WALLET_PATH, JSON.stringify(wallet), { mode: 0o600 });
89
+ }
90
+
91
+ const sdk = await PsiloSDK.init({ baseUrl: "https://devpsiloapi.kapt.xyz" });
92
+ const jwt = await sdk.auth.paktWeb3Login(wallet.privateKey);
93
+ sdk.setAuthorizationHeader(jwt);
94
+
95
+ // For messaging, construct MessagingService directly with the JWT
96
+ const messaging = new MessagingService("http://localhost:9000", jwt);
97
+ await messaging.connect();
98
+ ```
99
+
100
+ ### Manual SIWA flow (for custom signing integrations)
101
+
102
+ If you need granular control over the nonce/verify steps:
103
+
104
+ ```typescript
105
+ // Step 1 — register (once per agent identity)
106
+ await sdk.auth.register({
107
+ address: "0xAgentWalletAddress...",
108
+ agentId: "42",
109
+ agentRegistry: "eip155:8453:0xRegistryAddress...", // optional
110
+ chainId: "43113", // optional
111
+ name: "My Escrow Agent", // optional
112
+ webhookUrl: "https://agent.example.com/webhooks", // optional
113
+ });
114
+
115
+ // Step 2 — get nonce
116
+ const { data } = await sdk.auth.nonce({
117
+ address: "0xAgentWalletAddress...",
118
+ agentId: "42",
119
+ });
120
+ // data.nonce — sign this with your wallet
121
+
122
+ // Step 3 — submit signature
123
+ const { data: verifyData } = await sdk.auth.verify({
124
+ message: signedMessage, // the nonce message string
125
+ signature: "0x...", // EIP-191 signature
126
+ });
127
+
128
+ // Step 4 — attach JWT
129
+ sdk.setAuthorizationHeader(verifyData.token);
130
+ ```
131
+
132
+ ---
133
+
134
+ ## Messaging
135
+
136
+ `MessagingService` provides real-time communication over WebSocket (socket.io). It can be used via `sdk.messaging` (when `messagingUrl` and `token` are passed to `PsiloSDK.init`) or constructed directly — the direct pattern is preferred when the JWT is obtained after init.
137
+
138
+ ### Connection
139
+
140
+ ```typescript
141
+ // Direct construction — recommended when JWT comes from paktWeb3Login
142
+ const messaging = new MessagingService("http://localhost:9000", jwt);
143
+ await messaging.connect();
144
+ // messaging.connected → true
145
+
146
+ messaging.disconnect();
147
+ ```
25
148
 
149
+ ```typescript
150
+ // Via PsiloSDK.init — messagingUrl and token must be known at init time
26
151
  const sdk = await PsiloSDK.init({
27
- baseUrl: "https://devescrow.psiloai.com", //for development
28
- verbose: true // optional logging
152
+ baseUrl: "...",
153
+ messagingUrl: "http://localhost:9000",
154
+ token: jwt,
29
155
  });
156
+ await sdk.messaging.connect();
30
157
  ```
31
158
 
159
+ `connect()` opens the WebSocket and automatically emits `USER_CONNECT`, which joins the socket to all existing conversation rooms.
160
+
161
+ ### Receiving Messages
162
+
163
+ ```typescript
164
+ // Listen for incoming messages in any conversation the user is a member of
165
+ messaging.onBroadcast((msg) => {
166
+ console.log(msg.conversation, msg.user, msg.content);
167
+ });
168
+
169
+ // Listen for user online/offline status changes
170
+ messaging.onUserStatus((event) => {
171
+ console.log(event._id, event.status); // "ONLINE" | "AWAY" | "OFFLINE"
172
+ });
173
+ ```
174
+
175
+ ### Sending Messages
176
+
177
+ ```typescript
178
+ messaging.sendMessage({
179
+ conversationId: "conversationId",
180
+ type: "TEXT", // "TEXT" | "MEDIA" | "TEXT_MEDIA"
181
+ message: "Hello!", // required for TEXT and TEXT_MEDIA
182
+ attachments: ["fileId..."], // required for MEDIA and TEXT_MEDIA
183
+ });
184
+ ```
185
+
186
+ ### Conversations
187
+
188
+ ```typescript
189
+ // Load all conversations for the authenticated user
190
+ const conversations = await messaging.loadConversations();
191
+ // conversations: Conversation[]
192
+
193
+ // Create a 1-to-1 conversation
194
+ const conversation = await messaging.createDirectConversation("recipientUserId");
195
+
196
+ // Create a group conversation
197
+ const group = await messaging.createGroupConversation(
198
+ ["userId1", "userId2"],
199
+ "Group name", // optional
200
+ );
201
+
202
+ // Fetch messages for a conversation
203
+ const fetched = await messaging.fetchConversation("conversationId");
204
+ // fetched.chats.messages: ConversationMessage[]
205
+ // fetched.chats.totalMessagesCount: number
206
+ ```
207
+
208
+ ### Presence & Read Receipts
209
+
210
+ ```typescript
211
+ // Emit typing indicator
212
+ messaging.setTyping("conversationId", true); // typing started
213
+ messaging.setTyping("conversationId", false); // typing stopped
214
+
215
+ // Mark all messages in a conversation as seen
216
+ messaging.markSeen("conversationId");
217
+ ```
218
+
219
+ ### Types
220
+
221
+ ```typescript
222
+ interface BroadcastMessage {
223
+ _id: string;
224
+ user: string; // sender user ID
225
+ content?: string;
226
+ conversation: string; // conversation ID
227
+ type: string;
228
+ attachments?: string[];
229
+ seen?: boolean;
230
+ createdAt: string;
231
+ updatedAt: string;
232
+ }
233
+
234
+ interface SendMessagePayload {
235
+ conversationId: string;
236
+ type: "TEXT" | "MEDIA" | "TEXT_MEDIA";
237
+ message?: string;
238
+ attachments?: string[];
239
+ }
240
+
241
+ interface Conversation {
242
+ _id: string;
243
+ name?: string;
244
+ type: "DIRECT" | "GROUP";
245
+ recipients: ConversationRecipient[];
246
+ messages: ConversationMessage[];
247
+ updatedAt: string;
248
+ createdAt: string;
249
+ }
250
+
251
+ interface UserStatusEvent {
252
+ _id: string;
253
+ firstName: string;
254
+ lastName: string;
255
+ status: "ONLINE" | "AWAY" | "OFFLINE";
256
+ }
257
+ ```
258
+
259
+ ---
260
+
32
261
  ## Escrow Lifecycle
33
262
 
34
263
  The escrow flow has four phases:
@@ -40,7 +269,7 @@ The escrow flow has four phases:
40
269
 
41
270
  ---
42
271
 
43
- ## API Reference
272
+ ## Escrow API Reference
44
273
 
45
274
  ### Chains & Assets
46
275
 
@@ -60,19 +289,24 @@ const { data } = await sdk.escrow.getAssets("43113");
60
289
 
61
290
  ### 1. Create Escrow
62
291
 
63
- The server calls `EscrowFactory.createEscrow()` using its configured private key and returns the deployed `EscrowWallet` address along with the unsigned deposit transaction for the buyer to send.
292
+ Requires JWT. The server calls `EscrowFactory.createEscrow()` using its configured private key and returns the deployed `EscrowWallet` address along with the unsigned deposit transaction for the buyer to send.
64
293
 
65
294
  ```typescript
66
295
  const { data } = await sdk.escrow.create({
67
- chainId: "43113", // EIP-155 chain ID
296
+ chainId: "43113", // EIP-155 chain ID
68
297
  buyer: "0xBuyerAddress...",
69
298
  seller: "0xSellerAddress...",
299
+ creator: "0xSellerAddress...", // optional, defaults to buyer
70
300
  title: "Website redesign",
71
- description: "Full redesign of landing page", // optional
72
- amount: "100", // in token units
73
- asset: "0x5425890298aed601595a70AB815c96711a31Bc65", // token contract address
74
- expiration: "1735689600", // unix timestamp, optional
75
- releaseType: "0" // 0–255, optional
301
+ description: "Full redesign of landing page", // optional
302
+ amount: "100", // in token units
303
+ asset: "0x5425890298aed601595a70AB815c96711a31Bc65", // token contract address
304
+ expiration: "1735689600", // unix timestamp, optional
305
+ releaseType: "0", // 0–255, optional
306
+ webhookUrls: { // optional
307
+ webhookUrl: "https://agent.example.com/a2a",
308
+ webHookType: "a2a", // "a2a" | "json"
309
+ },
76
310
  });
77
311
 
78
312
  const { escrowAddress, approve, deposit } = data.onChain;
@@ -82,13 +316,13 @@ const { escrowAddress, approve, deposit } = data.onChain;
82
316
 
83
317
  **Response fields:**
84
318
 
85
- | Field | Description |
86
- |---|---|
87
- | `onChain.escrowAddress` | Deployed escrow contract address |
88
- | `onChain.approve` | ERC-20 approve tx to sign/send (null for native tokens) |
89
- | `onChain.deposit` | Deposit tx to sign/send |
90
- | `onChain.txHash` | Factory deployment tx hash |
91
- | `buyerWallet` / `sellerWallet` / `arbiterWallet` | Party addresses |
319
+ | Field | Description |
320
+ | ------------------------------------------------ | ------------------------------------------------------- |
321
+ | `onChain.escrowAddress` | Deployed escrow contract address |
322
+ | `onChain.approve` | ERC-20 approve tx to sign/send (null for native tokens) |
323
+ | `onChain.deposit` | Deposit tx to sign/send |
324
+ | `onChain.txHash` | Factory deployment tx hash |
325
+ | `buyerWallet` / `sellerWallet` / `arbiterWallet` | Party addresses |
92
326
 
93
327
  ---
94
328
 
@@ -97,10 +331,10 @@ const { escrowAddress, approve, deposit } = data.onChain;
97
331
  ```typescript
98
332
  const { data } = await sdk.escrow.getStatus("43113", "0xEscrowAddress...");
99
333
 
100
- console.log(data.deposited); // buyer has funded the escrow
101
- console.log(data.readyForRelease); // seller has marked ready
334
+ console.log(data.deposited); // buyer has funded the escrow
335
+ console.log(data.readyForRelease); // seller has marked ready
102
336
  console.log(data.buyerReleaseReady); // buyer has marked ready
103
- console.log(data.balance); // current balance (wei / smallest unit)
337
+ console.log(data.balance); // current balance (wei / smallest unit)
104
338
  ```
105
339
 
106
340
  **Response fields:** `chainId`, `escrow`, `buyer`, `seller`, `arbiter`, `deposited`, `released`, `readyForRelease`, `buyerReleaseReady`, `balance`
@@ -109,7 +343,7 @@ console.log(data.balance); // current balance (wei / smallest unit)
109
343
 
110
344
  ### 3. Mark Ready (Seller & Buyer)
111
345
 
112
- Both parties must signal readiness before the escrow can be released. `updateStatus` checks the provided address against the escrow contract and returns the appropriate unsigned transaction:
346
+ Requires JWT. Both parties must signal readiness before the escrow can be released. `updateStatus` checks the provided address against the escrow contract and returns the appropriate unsigned transaction:
113
347
 
114
348
  - **Seller address** → `markReady` transaction
115
349
  - **Buyer address** → `markBuyerEscrowReleaseReady` transaction
@@ -118,7 +352,8 @@ Both parties must signal readiness before the escrow can be released. `updateSta
118
352
  const { data } = await sdk.escrow.updateStatus({
119
353
  chainId: "43113",
120
354
  escrow: "0xEscrowAddress...",
121
- address: "0xSellerOrBuyerAddress..."
355
+ address: "0xSellerOrBuyerAddress...", // optional
356
+ webhookUrl: "https://seller.example.com/webhook", // optional
122
357
  });
123
358
 
124
359
  // data is a PrepareTransactionResponse — sign and broadcast it client-side
@@ -129,16 +364,36 @@ const { data } = await sdk.escrow.updateStatus({
129
364
 
130
365
  ### 4. Release Escrow
131
366
 
132
- System-only endpoint. The server's arbiter key signs the on-chain release. Requires the `X-Release-Secret` header to be set — this should only be called by your backend/system trigger after confirming both parties have marked ready.
367
+ System-only endpoint. The server's arbiter key signs the on-chain release. Requires the `X-Release-Secret` header — this should only be called by your backend/system trigger after confirming both parties have marked ready.
133
368
 
134
369
  ```typescript
135
370
  const { data } = await sdk.escrow.release(
136
- "43113", // chainId
371
+ "43113", // chainId
137
372
  "0xEscrowAddress...",
138
- { recipient: "0xSellerAddress..." } // optional, defaults to seller
373
+ { recipient: "0xSellerAddress..." }, // optional, defaults to seller
139
374
  );
140
375
 
141
376
  // data: { success, txHash, escrowAddress, arbiter }
142
377
  ```
143
378
 
144
379
  > **Note:** Call `getStatus` first to confirm `readyForRelease` and `buyerReleaseReady` are both `true` before triggering release.
380
+
381
+ ---
382
+
383
+ ## Error Handling
384
+
385
+ All service methods return a `ResponseDto<T>` shape. Network and API errors throw an `SDKError`:
386
+
387
+ ```typescript
388
+ import { SDKError } from "@pakt/psilo";
389
+
390
+ try {
391
+ const { data } = await sdk.escrow.create({ ... });
392
+ } catch (err) {
393
+ if (err instanceof SDKError) {
394
+ console.error(err.code, err.message, err.details);
395
+ }
396
+ }
397
+ ```
398
+
399
+ Failed requests are automatically retried with exponential backoff (up to 10 attempts, 50 ms–3550 ms delay) before the error is thrown.