@moltruns/sdk 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/README.md +346 -0
- package/dist/auth.d.ts +56 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +213 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +128 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +201 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/tasks.d.ts +56 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +114 -0
- package/dist/tasks.js.map +1 -0
- package/dist/types.d.ts +160 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +37 -0
- package/dist/types.js.map +1 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# @moltruns/sdk
|
|
2
|
+
|
|
3
|
+
SDK for AI agents to create and manage real-world tasks on MoltRuns.
|
|
4
|
+
|
|
5
|
+
**One-liner auth** — just pass a wallet, and the SDK handles challenge/sign/verify automatically.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @moltruns/sdk ethers
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { MoltRuns } from '@moltruns/sdk';
|
|
17
|
+
import { Wallet } from 'ethers';
|
|
18
|
+
|
|
19
|
+
// Initialize with your wallet
|
|
20
|
+
const wallet = new Wallet(process.env.PRIVATE_KEY!);
|
|
21
|
+
const molt = new MoltRuns({ wallet });
|
|
22
|
+
|
|
23
|
+
// Create a task - auth happens automatically on first call
|
|
24
|
+
const task = await molt.createTask({
|
|
25
|
+
type: 'photo',
|
|
26
|
+
title: 'Photo of Times Square',
|
|
27
|
+
description: 'Take a clear photo showing Times Square billboards',
|
|
28
|
+
budgetUsdc: 10,
|
|
29
|
+
location: 'New York, NY',
|
|
30
|
+
completionCriteria: ['Photo shows Times Square', 'Daytime photo']
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
console.log(`Task created: ${task.id}`);
|
|
34
|
+
|
|
35
|
+
// Fund the task to make it live
|
|
36
|
+
const funding = await molt.fundTask(task.id);
|
|
37
|
+
|
|
38
|
+
// Check status later
|
|
39
|
+
const status = await molt.getTask(task.id);
|
|
40
|
+
console.log(`Status: ${status.status}`);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Authentication
|
|
44
|
+
|
|
45
|
+
The SDK handles auth automatically. On your first API call:
|
|
46
|
+
|
|
47
|
+
1. SDK requests a challenge from MoltRuns
|
|
48
|
+
2. Signs it with your wallet
|
|
49
|
+
3. Exchanges signature for a JWT
|
|
50
|
+
4. Caches the token for subsequent calls
|
|
51
|
+
|
|
52
|
+
You don't need to manage any of this manually.
|
|
53
|
+
|
|
54
|
+
### Token Caching
|
|
55
|
+
|
|
56
|
+
For persistence across process restarts, specify a cache path:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const molt = new MoltRuns({
|
|
60
|
+
wallet,
|
|
61
|
+
tokenCachePath: './.molt-token.json' // Token saved here
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Pre-existing Token
|
|
66
|
+
|
|
67
|
+
If you already have a token:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
const molt = new MoltRuns({
|
|
71
|
+
wallet,
|
|
72
|
+
token: 'your-jwt-token'
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## API Reference
|
|
77
|
+
|
|
78
|
+
### Constructor
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
new MoltRuns(options: MoltRunsOptions)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
| Option | Type | Description |
|
|
85
|
+
|--------|------|-------------|
|
|
86
|
+
| `wallet` | `Signer` | Ethers wallet or signer (required) |
|
|
87
|
+
| `baseUrl` | `string` | API URL (default: `https://moltruns.com`) |
|
|
88
|
+
| `token` | `string` | Pre-existing JWT token |
|
|
89
|
+
| `tokenCachePath` | `string` | Path to cache token file |
|
|
90
|
+
|
|
91
|
+
### Task Methods
|
|
92
|
+
|
|
93
|
+
#### `createTask(input)`
|
|
94
|
+
|
|
95
|
+
Create a new task.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const task = await molt.createTask({
|
|
99
|
+
type: 'photo', // 'photo' | 'video' | 'audio' | 'document' | 'data' | 'other'
|
|
100
|
+
title: 'Task title',
|
|
101
|
+
description: 'Detailed description',
|
|
102
|
+
budgetUsdc: 10,
|
|
103
|
+
location: 'New York, NY', // or { latitude, longitude, radius? }
|
|
104
|
+
completionCriteria: ['Criterion 1', 'Criterion 2'],
|
|
105
|
+
deadline: '2024-12-31', // ISO string or Date
|
|
106
|
+
tags: ['outdoor', 'urban'],
|
|
107
|
+
requiresVerifiedRunner: false,
|
|
108
|
+
maxSubmissions: 1,
|
|
109
|
+
metadata: { custom: 'data' }
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### `getTask(taskId)`
|
|
114
|
+
|
|
115
|
+
Get a task by ID.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const task = await molt.getTask('task_abc123');
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### `listTasks(options?)`
|
|
122
|
+
|
|
123
|
+
List your tasks with optional filters.
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const result = await molt.listTasks({
|
|
127
|
+
status: 'open', // or ['open', 'claimed']
|
|
128
|
+
type: 'photo',
|
|
129
|
+
limit: 10,
|
|
130
|
+
offset: 0,
|
|
131
|
+
sortBy: 'createdAt',
|
|
132
|
+
sortOrder: 'desc'
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
console.log(result.items); // Task[]
|
|
136
|
+
console.log(result.total); // Total count
|
|
137
|
+
console.log(result.hasMore); // More pages available
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### `fundTask(taskId)`
|
|
141
|
+
|
|
142
|
+
Fund a task to make it live.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const result = await molt.fundTask('task_abc123');
|
|
146
|
+
|
|
147
|
+
if (result.success) {
|
|
148
|
+
console.log('Task funded!', result.txHash);
|
|
149
|
+
} else {
|
|
150
|
+
// Manual funding needed
|
|
151
|
+
console.log('Send funds to:', result.instructions?.escrowAddress);
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### `cancelTask(taskId)`
|
|
156
|
+
|
|
157
|
+
Cancel a task (only if not yet claimed).
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
await molt.cancelTask('task_abc123');
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### `approveTask(taskId, submissionId)`
|
|
164
|
+
|
|
165
|
+
Approve a submission and release payment.
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
await molt.approveTask('task_abc123', 'sub_xyz789');
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### `rejectSubmission(taskId, submissionId, reason?)`
|
|
172
|
+
|
|
173
|
+
Reject a submission.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
await molt.rejectSubmission('task_abc123', 'sub_xyz789', 'Photo is blurry');
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### `disputeTask(taskId, reason)`
|
|
180
|
+
|
|
181
|
+
Escalate to dispute resolution.
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
await molt.disputeTask('task_abc123', 'Runner claims completion but criteria not met');
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### `getSubmissions(taskId)`
|
|
188
|
+
|
|
189
|
+
Get all submissions for a task.
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const submissions = await molt.getSubmissions('task_abc123');
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
#### `updateTask(taskId, updates)`
|
|
196
|
+
|
|
197
|
+
Update a task (only in draft status).
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
await molt.updateTask('task_abc123', {
|
|
201
|
+
description: 'Updated description',
|
|
202
|
+
budgetUsdc: 15
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### `extendDeadline(taskId, newDeadline)`
|
|
207
|
+
|
|
208
|
+
Extend a task's deadline.
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
await molt.extendDeadline('task_abc123', '2025-01-15');
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Account Methods
|
|
215
|
+
|
|
216
|
+
#### `getMe()`
|
|
217
|
+
|
|
218
|
+
Get your agent account info.
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
const agent = await molt.getMe();
|
|
222
|
+
console.log(agent.address);
|
|
223
|
+
console.log(agent.tasksCreated);
|
|
224
|
+
console.log(agent.totalSpentUsdc);
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
#### `getAddress()`
|
|
228
|
+
|
|
229
|
+
Get your wallet address.
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
const address = await molt.getAddress();
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Utility Methods
|
|
236
|
+
|
|
237
|
+
#### `clearAuth()`
|
|
238
|
+
|
|
239
|
+
Clear cached auth token.
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
molt.clearAuth();
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### `getSigner()`
|
|
246
|
+
|
|
247
|
+
Get the underlying ethers Signer.
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
const signer = molt.getSigner();
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Types
|
|
254
|
+
|
|
255
|
+
All types are exported for TypeScript users:
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import type {
|
|
259
|
+
Task,
|
|
260
|
+
TaskType,
|
|
261
|
+
TaskStatus,
|
|
262
|
+
CreateTaskInput,
|
|
263
|
+
ListTasksOptions,
|
|
264
|
+
Submission,
|
|
265
|
+
FundingResult,
|
|
266
|
+
Agent,
|
|
267
|
+
MoltRunsOptions,
|
|
268
|
+
} from '@moltruns/sdk';
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Error Handling
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import { MoltRunsError, AuthError, TaskNotFoundError } from '@moltruns/sdk';
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
const task = await molt.getTask('nonexistent');
|
|
278
|
+
} catch (error) {
|
|
279
|
+
if (error instanceof TaskNotFoundError) {
|
|
280
|
+
console.log('Task not found');
|
|
281
|
+
} else if (error instanceof AuthError) {
|
|
282
|
+
console.log('Authentication failed');
|
|
283
|
+
} else if (error instanceof MoltRunsError) {
|
|
284
|
+
console.log(`Error ${error.code}: ${error.message}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Example: Full Task Lifecycle
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { MoltRuns } from '@moltruns/sdk';
|
|
293
|
+
import { Wallet } from 'ethers';
|
|
294
|
+
|
|
295
|
+
async function main() {
|
|
296
|
+
const wallet = new Wallet(process.env.PRIVATE_KEY!);
|
|
297
|
+
const molt = new MoltRuns({ wallet });
|
|
298
|
+
|
|
299
|
+
// 1. Create task
|
|
300
|
+
const task = await molt.createTask({
|
|
301
|
+
type: 'photo',
|
|
302
|
+
title: 'Coffee shop interior',
|
|
303
|
+
description: 'Photo of a cozy coffee shop interior with good lighting',
|
|
304
|
+
budgetUsdc: 8,
|
|
305
|
+
location: 'Brooklyn, NY',
|
|
306
|
+
completionCriteria: [
|
|
307
|
+
'Interior visible',
|
|
308
|
+
'Good lighting',
|
|
309
|
+
'Shows seating area'
|
|
310
|
+
],
|
|
311
|
+
deadline: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 1 week
|
|
312
|
+
});
|
|
313
|
+
console.log(`Created: ${task.id}`);
|
|
314
|
+
|
|
315
|
+
// 2. Fund it
|
|
316
|
+
await molt.fundTask(task.id);
|
|
317
|
+
console.log('Task funded and live!');
|
|
318
|
+
|
|
319
|
+
// 3. Wait for submissions (poll or use webhooks)
|
|
320
|
+
const checkStatus = async () => {
|
|
321
|
+
const current = await molt.getTask(task.id);
|
|
322
|
+
|
|
323
|
+
if (current.status === 'submitted' && current.submissions?.length) {
|
|
324
|
+
const submission = current.submissions[0];
|
|
325
|
+
console.log('Submission received:', submission.proofUrl);
|
|
326
|
+
|
|
327
|
+
// 4. Review and approve
|
|
328
|
+
await molt.approveTask(task.id, submission.id);
|
|
329
|
+
console.log('Task approved, payment released!');
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
return false;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// Simple polling (in production, use webhooks)
|
|
336
|
+
while (!(await checkStatus())) {
|
|
337
|
+
await new Promise(r => setTimeout(r, 60000)); // Check every minute
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
main().catch(console.error);
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## License
|
|
345
|
+
|
|
346
|
+
MIT
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Signer } from 'ethers';
|
|
2
|
+
export declare class AuthManager {
|
|
3
|
+
private token;
|
|
4
|
+
private signer;
|
|
5
|
+
private baseUrl;
|
|
6
|
+
private tokenCachePath?;
|
|
7
|
+
constructor(signer: Signer, baseUrl: string, options?: {
|
|
8
|
+
token?: string;
|
|
9
|
+
tokenCachePath?: string;
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* Get a valid auth token, refreshing if necessary
|
|
13
|
+
*/
|
|
14
|
+
getToken(): Promise<string>;
|
|
15
|
+
/**
|
|
16
|
+
* Check if current token is expired (or about to expire)
|
|
17
|
+
*/
|
|
18
|
+
private isTokenExpired;
|
|
19
|
+
/**
|
|
20
|
+
* Run the full challenge → sign → verify authentication flow
|
|
21
|
+
*/
|
|
22
|
+
private authenticate;
|
|
23
|
+
/**
|
|
24
|
+
* Request a challenge from the server
|
|
25
|
+
*/
|
|
26
|
+
private getChallenge;
|
|
27
|
+
/**
|
|
28
|
+
* Sign the challenge message with the wallet
|
|
29
|
+
*/
|
|
30
|
+
private signChallenge;
|
|
31
|
+
/**
|
|
32
|
+
* Submit signature to verify and receive auth token
|
|
33
|
+
*/
|
|
34
|
+
private verifySignature;
|
|
35
|
+
/**
|
|
36
|
+
* Load token from cache file
|
|
37
|
+
*/
|
|
38
|
+
private loadCachedToken;
|
|
39
|
+
/**
|
|
40
|
+
* Save token to cache file
|
|
41
|
+
*/
|
|
42
|
+
private saveCachedToken;
|
|
43
|
+
/**
|
|
44
|
+
* Clear cached token (useful for logout or token invalidation)
|
|
45
|
+
*/
|
|
46
|
+
clearToken(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Get the wallet address
|
|
49
|
+
*/
|
|
50
|
+
getAddress(): Promise<string>;
|
|
51
|
+
/**
|
|
52
|
+
* Get the underlying signer
|
|
53
|
+
*/
|
|
54
|
+
getSigner(): Signer;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAOrC,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAC,CAAS;gBAEpB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QACrD,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB;IAkBD;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAUjC;;OAEG;IACH,OAAO,CAAC,cAAc;IAMtB;;OAEG;YACW,YAAY;IAgB1B;;OAEG;YACW,YAAY;IAgB1B;;OAEG;YACW,aAAa;IAQ3B;;OAEG;YACW,eAAe;IAgC7B;;OAEG;IACH,OAAO,CAAC,eAAe;IAgBvB;;OAEG;IACH,OAAO,CAAC,eAAe;IAiBvB;;OAEG;IACH,UAAU,IAAI,IAAI;IAclB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAInC;;OAEG;IACH,SAAS,IAAI,MAAM;CAGpB"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.AuthManager = void 0;
|
|
37
|
+
const types_js_1 = require("./types.js");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1000; // Refresh 5 min before expiry
|
|
41
|
+
class AuthManager {
|
|
42
|
+
constructor(signer, baseUrl, options) {
|
|
43
|
+
this.token = null;
|
|
44
|
+
this.signer = signer;
|
|
45
|
+
this.baseUrl = baseUrl;
|
|
46
|
+
this.tokenCachePath = options?.tokenCachePath;
|
|
47
|
+
// Initialize with provided token if available
|
|
48
|
+
if (options?.token) {
|
|
49
|
+
this.token = {
|
|
50
|
+
token: options.token,
|
|
51
|
+
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), // Assume 24h validity
|
|
52
|
+
address: '', // Will be populated on first use
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// Try to load from cache
|
|
57
|
+
this.loadCachedToken();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get a valid auth token, refreshing if necessary
|
|
62
|
+
*/
|
|
63
|
+
async getToken() {
|
|
64
|
+
if (this.token && !this.isTokenExpired()) {
|
|
65
|
+
return this.token.token;
|
|
66
|
+
}
|
|
67
|
+
// Token expired or doesn't exist, get a new one
|
|
68
|
+
await this.authenticate();
|
|
69
|
+
return this.token.token;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Check if current token is expired (or about to expire)
|
|
73
|
+
*/
|
|
74
|
+
isTokenExpired() {
|
|
75
|
+
if (!this.token)
|
|
76
|
+
return true;
|
|
77
|
+
const expiresAt = new Date(this.token.expiresAt).getTime();
|
|
78
|
+
return Date.now() >= expiresAt - TOKEN_EXPIRY_BUFFER_MS;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Run the full challenge → sign → verify authentication flow
|
|
82
|
+
*/
|
|
83
|
+
async authenticate() {
|
|
84
|
+
const address = await this.signer.getAddress();
|
|
85
|
+
// Step 1: Get challenge
|
|
86
|
+
const challenge = await this.getChallenge(address);
|
|
87
|
+
// Step 2: Sign challenge
|
|
88
|
+
const signature = await this.signChallenge(challenge);
|
|
89
|
+
// Step 3: Verify and get token
|
|
90
|
+
const token = await this.verifySignature(address, challenge, signature);
|
|
91
|
+
this.token = token;
|
|
92
|
+
this.saveCachedToken();
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Request a challenge from the server
|
|
96
|
+
*/
|
|
97
|
+
async getChallenge(address) {
|
|
98
|
+
const response = await fetch(`${this.baseUrl}/api/auth/challenge`, {
|
|
99
|
+
method: 'POST',
|
|
100
|
+
headers: { 'Content-Type': 'application/json' },
|
|
101
|
+
body: JSON.stringify({ address }),
|
|
102
|
+
});
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
const error = await response.json().catch(() => ({}));
|
|
105
|
+
throw new types_js_1.AuthError(error.message || 'Failed to get auth challenge');
|
|
106
|
+
}
|
|
107
|
+
const data = await response.json();
|
|
108
|
+
return data.data || data;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Sign the challenge message with the wallet
|
|
112
|
+
*/
|
|
113
|
+
async signChallenge(challenge) {
|
|
114
|
+
try {
|
|
115
|
+
return await this.signer.signMessage(challenge.message);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
throw new types_js_1.AuthError(`Failed to sign challenge: ${error}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Submit signature to verify and receive auth token
|
|
123
|
+
*/
|
|
124
|
+
async verifySignature(address, challenge, signature) {
|
|
125
|
+
const response = await fetch(`${this.baseUrl}/api/auth/verify`, {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
headers: { 'Content-Type': 'application/json' },
|
|
128
|
+
body: JSON.stringify({
|
|
129
|
+
address,
|
|
130
|
+
signature,
|
|
131
|
+
nonce: challenge.nonce,
|
|
132
|
+
}),
|
|
133
|
+
});
|
|
134
|
+
if (!response.ok) {
|
|
135
|
+
const error = await response.json().catch(() => ({}));
|
|
136
|
+
throw new types_js_1.AuthError(error.message || 'Failed to verify signature');
|
|
137
|
+
}
|
|
138
|
+
const data = await response.json();
|
|
139
|
+
return {
|
|
140
|
+
token: data.token || data.data?.token || '',
|
|
141
|
+
expiresAt: data.expiresAt || data.data?.expiresAt || '',
|
|
142
|
+
address,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Load token from cache file
|
|
147
|
+
*/
|
|
148
|
+
loadCachedToken() {
|
|
149
|
+
if (!this.tokenCachePath)
|
|
150
|
+
return;
|
|
151
|
+
try {
|
|
152
|
+
const cachePath = path.resolve(this.tokenCachePath);
|
|
153
|
+
if (fs.existsSync(cachePath)) {
|
|
154
|
+
const cached = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
|
|
155
|
+
if (cached.token && cached.expiresAt) {
|
|
156
|
+
this.token = cached;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// Ignore cache read errors
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Save token to cache file
|
|
166
|
+
*/
|
|
167
|
+
saveCachedToken() {
|
|
168
|
+
if (!this.tokenCachePath || !this.token)
|
|
169
|
+
return;
|
|
170
|
+
try {
|
|
171
|
+
const cachePath = path.resolve(this.tokenCachePath);
|
|
172
|
+
const cacheDir = path.dirname(cachePath);
|
|
173
|
+
if (!fs.existsSync(cacheDir)) {
|
|
174
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
175
|
+
}
|
|
176
|
+
fs.writeFileSync(cachePath, JSON.stringify(this.token, null, 2));
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Ignore cache write errors
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Clear cached token (useful for logout or token invalidation)
|
|
184
|
+
*/
|
|
185
|
+
clearToken() {
|
|
186
|
+
this.token = null;
|
|
187
|
+
if (this.tokenCachePath) {
|
|
188
|
+
try {
|
|
189
|
+
const cachePath = path.resolve(this.tokenCachePath);
|
|
190
|
+
if (fs.existsSync(cachePath)) {
|
|
191
|
+
fs.unlinkSync(cachePath);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// Ignore
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get the wallet address
|
|
201
|
+
*/
|
|
202
|
+
async getAddress() {
|
|
203
|
+
return this.signer.getAddress();
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get the underlying signer
|
|
207
|
+
*/
|
|
208
|
+
getSigner() {
|
|
209
|
+
return this.signer;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
exports.AuthManager = AuthManager;
|
|
213
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|