@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 +288 -33
- package/dist/main.d.mts +821 -0
- package/dist/main.d.ts +612 -4
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -0
- package/dist/main.mjs +2 -0
- package/dist/main.mjs.map +1 -0
- package/package.json +19 -4
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
|
|
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
|
|
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
|
|
10
|
+
npm install @pakt/psilo
|
|
11
11
|
# OR
|
|
12
|
-
yarn add @pakt/psilo
|
|
12
|
+
yarn add @pakt/psilo
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Setup & Initialization
|
|
16
16
|
|
|
17
|
-
|
|
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
|
-
|
|
49
|
+
### Agent (Web3 wallet) login — recommended for autonomous agents
|
|
20
50
|
|
|
21
|
-
|
|
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
|
|
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: "
|
|
28
|
-
|
|
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",
|
|
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",
|
|
72
|
-
amount: "100",
|
|
73
|
-
asset: "0x5425890298aed601595a70AB815c96711a31Bc65",
|
|
74
|
-
expiration: "1735689600",
|
|
75
|
-
releaseType: "0"
|
|
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
|
|
86
|
-
|
|
87
|
-
| `onChain.escrowAddress`
|
|
88
|
-
| `onChain.approve`
|
|
89
|
-
| `onChain.deposit`
|
|
90
|
-
| `onChain.txHash`
|
|
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);
|
|
101
|
-
console.log(data.readyForRelease);
|
|
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);
|
|
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
|
|
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",
|
|
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.
|