@dainprotocol/service-sdk 2.0.0 → 2.0.2
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 +1082 -0
- package/dist/__tests__/oauth-context-simple.test.d.ts +1 -0
- package/dist/__tests__/oauth-context-simple.test.js +90 -0
- package/dist/__tests__/oauth-context-simple.test.js.map +1 -0
- package/dist/__tests__/oauth-context.test.d.ts +1 -0
- package/dist/__tests__/oauth-context.test.js +282 -0
- package/dist/__tests__/oauth-context.test.js.map +1 -0
- package/dist/__tests__/oauth2-client-context.test.d.ts +1 -0
- package/dist/__tests__/oauth2-client-context.test.js +165 -0
- package/dist/__tests__/oauth2-client-context.test.js.map +1 -0
- package/dist/__tests__/oauth2-client-simple.test.d.ts +1 -0
- package/dist/__tests__/oauth2-client-simple.test.js +144 -0
- package/dist/__tests__/oauth2-client-simple.test.js.map +1 -0
- package/dist/service/oauth2Manager.js +1 -0
- package/dist/service/oauth2Manager.js.map +1 -1
- package/dist/service/server.js +17 -8
- package/dist/service/server.js.map +1 -1
- package/dist/service/types.d.ts +12 -1
- package/package.json +3 -3
- package/dist/client/service-auth.d.ts +0 -61
- package/dist/client/service-auth.js +0 -93
- package/dist/client/service-auth.js.map +0 -1
- package/dist/client/user-auth.d.ts +0 -74
- package/dist/client/user-auth.js +0 -137
- package/dist/client/user-auth.js.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,1082 @@
|
|
|
1
|
+
# DAIN Service SDK
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@dainprotocol/service-sdk)
|
|
4
|
+
[](https://opensource.org/licenses/ISC)
|
|
5
|
+
|
|
6
|
+
The official TypeScript SDK for building AI-powered services on the DAIN Protocol. Create intelligent services that can be discovered and used by AI agents across multiple platforms.
|
|
7
|
+
|
|
8
|
+
## 📋 Table of Contents
|
|
9
|
+
|
|
10
|
+
- [Features](#-features)
|
|
11
|
+
- [Installation](#-installation)
|
|
12
|
+
- [Quick Start](#-quick-start)
|
|
13
|
+
- [Core Concepts](#-core-concepts)
|
|
14
|
+
- [Multi-Runtime Support](#-multi-runtime-support)
|
|
15
|
+
- [Tools](#-tools)
|
|
16
|
+
- [OAuth2 Integration](#-oauth2-integration)
|
|
17
|
+
- [Plugins](#-plugins)
|
|
18
|
+
- [Client SDK](#-client-sdk)
|
|
19
|
+
- [Advanced Features](#-advanced-features)
|
|
20
|
+
- [API Reference](#-api-reference)
|
|
21
|
+
- [Examples](#-examples)
|
|
22
|
+
|
|
23
|
+
## ✨ Features
|
|
24
|
+
|
|
25
|
+
- 🚀 **Multi-Runtime Support** - Works seamlessly on Node.js, Deno, Cloudflare Workers, and Next.js
|
|
26
|
+
- 🔧 **Type-Safe Tools** - Define tools with Zod schemas for automatic validation and type inference
|
|
27
|
+
- 🔐 **OAuth2 Integration** - Built-in OAuth2 support with flexible storage adapters
|
|
28
|
+
- 🔌 **Plugin System** - Extend functionality with crypto, citations, time, and custom plugins
|
|
29
|
+
- 📡 **Real-Time Streaming** - Server-Sent Events (SSE) for streaming responses
|
|
30
|
+
- 🔄 **Long-Running Processes** - State machine-based process management
|
|
31
|
+
- 💳 **Payment Integration** - Built-in support for Stripe payments
|
|
32
|
+
- 🎨 **UI Components** - Return rich UI responses alongside data
|
|
33
|
+
- 📊 **Contexts & Datasources** - Provide dynamic context and data to AI agents
|
|
34
|
+
- 🛡️ **Signature-Based Auth** - Secure authentication using Ed25519 signatures
|
|
35
|
+
|
|
36
|
+
## 📦 Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install @dainprotocol/service-sdk zod
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Optional Dependencies
|
|
43
|
+
|
|
44
|
+
For OAuth2 with persistent storage:
|
|
45
|
+
```bash
|
|
46
|
+
npm install @dainprotocol/oauth2-token-manager @dainprotocol/oauth2-storage-drizzle
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For process management with Redis:
|
|
50
|
+
```bash
|
|
51
|
+
npm install ioredis
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 🚀 Quick Start
|
|
55
|
+
|
|
56
|
+
### Creating a Simple Weather Service (Node.js)
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { defineDAINService } from '@dainprotocol/service-sdk';
|
|
60
|
+
import { z } from 'zod';
|
|
61
|
+
|
|
62
|
+
// Define a tool
|
|
63
|
+
const getWeatherTool = {
|
|
64
|
+
id: 'get-weather',
|
|
65
|
+
name: 'Get Weather',
|
|
66
|
+
description: 'Fetches current weather for a city',
|
|
67
|
+
input: z.object({
|
|
68
|
+
city: z.string().describe('The name of the city'),
|
|
69
|
+
}),
|
|
70
|
+
output: z.object({
|
|
71
|
+
temperature: z.number().describe('Temperature in Celsius'),
|
|
72
|
+
condition: z.string().describe('Weather condition'),
|
|
73
|
+
}),
|
|
74
|
+
pricing: { pricePerUse: 0.01, currency: 'USD' },
|
|
75
|
+
handler: async ({ city }, agentInfo) => {
|
|
76
|
+
// Your logic here
|
|
77
|
+
return {
|
|
78
|
+
text: `The weather in ${city} is 22°C and Sunny`,
|
|
79
|
+
data: { temperature: 22, condition: 'Sunny' },
|
|
80
|
+
ui: undefined,
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Create and start the service
|
|
86
|
+
const service = defineDAINService({
|
|
87
|
+
metadata: {
|
|
88
|
+
title: 'Weather Service',
|
|
89
|
+
description: 'A service for weather information',
|
|
90
|
+
version: '1.0.0',
|
|
91
|
+
author: 'Your Name',
|
|
92
|
+
tags: ['weather'],
|
|
93
|
+
},
|
|
94
|
+
identity: {
|
|
95
|
+
apiKey: process.env.DAIN_API_KEY,
|
|
96
|
+
},
|
|
97
|
+
tools: [getWeatherTool],
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Start the service
|
|
101
|
+
service.startNode({ port: 3000 }).then(() => {
|
|
102
|
+
console.log('Weather service running on port 3000');
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Using the Client SDK
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { DainServiceConnection, DainClientAuth } from '@dainprotocol/service-sdk/client';
|
|
110
|
+
|
|
111
|
+
// Initialize authentication
|
|
112
|
+
const auth = new DainClientAuth({
|
|
113
|
+
apiKey: process.env.DAIN_API_KEY,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Connect to a service
|
|
117
|
+
const service = new DainServiceConnection('http://localhost:3000', auth);
|
|
118
|
+
|
|
119
|
+
// Call a tool
|
|
120
|
+
const result = await service.callTool('get-weather', {
|
|
121
|
+
city: 'San Francisco',
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
console.log(result.text); // "The weather in San Francisco is 22°C and Sunny"
|
|
125
|
+
console.log(result.data); // { temperature: 22, condition: 'Sunny' }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## 🧩 Core Concepts
|
|
129
|
+
|
|
130
|
+
### Services
|
|
131
|
+
|
|
132
|
+
A **service** is a collection of tools, contexts, datasources, and widgets that provide specific functionality to AI agents.
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
const service = defineDAINService({
|
|
136
|
+
metadata: { /* ... */ },
|
|
137
|
+
identity: { /* ... */ },
|
|
138
|
+
tools: [ /* ... */ ],
|
|
139
|
+
contexts: [ /* ... */ ],
|
|
140
|
+
datasources: [ /* ... */ ],
|
|
141
|
+
widgets: [ /* ... */ ],
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Tools
|
|
146
|
+
|
|
147
|
+
**Tools** are functions that AI agents can call. Each tool has:
|
|
148
|
+
- A unique ID
|
|
149
|
+
- Input/output schemas (using Zod)
|
|
150
|
+
- A handler function
|
|
151
|
+
- Optional pricing information
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
const myTool = {
|
|
155
|
+
id: 'my-tool',
|
|
156
|
+
name: 'My Tool',
|
|
157
|
+
description: 'Does something useful',
|
|
158
|
+
input: z.object({ param: z.string() }),
|
|
159
|
+
output: z.object({ result: z.string() }),
|
|
160
|
+
handler: async (input, agentInfo, context) => {
|
|
161
|
+
return {
|
|
162
|
+
text: 'Human-readable response',
|
|
163
|
+
data: { result: 'structured data' },
|
|
164
|
+
ui: { /* optional UI component */ },
|
|
165
|
+
};
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Agent Info
|
|
171
|
+
|
|
172
|
+
Every tool handler receives `agentInfo` with details about the calling agent:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
interface AgentInfo {
|
|
176
|
+
agentId: string; // Unique agent identifier
|
|
177
|
+
address: string; // Agent's blockchain address
|
|
178
|
+
smartAccountPDA?: string;
|
|
179
|
+
id: string;
|
|
180
|
+
webhookUrl?: string; // For sending async updates
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Tool Context
|
|
185
|
+
|
|
186
|
+
The `context` parameter provides access to:
|
|
187
|
+
- The Hono app instance
|
|
188
|
+
- OAuth2 client for authenticated API calls
|
|
189
|
+
- Custom extra data
|
|
190
|
+
- UI update functions
|
|
191
|
+
- Process management
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
interface ToolContext {
|
|
195
|
+
app: Hono;
|
|
196
|
+
oauth2Client?: OAuth2Client;
|
|
197
|
+
extraData?: any;
|
|
198
|
+
updateUI?: (update: { ui: any }) => Promise<void>;
|
|
199
|
+
addProcess?: (processId: string) => Promise<void>;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## 🌐 Multi-Runtime Support
|
|
204
|
+
|
|
205
|
+
The SDK automatically adapts to your runtime environment through conditional exports.
|
|
206
|
+
|
|
207
|
+
### Node.js
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
import { defineDAINService } from '@dainprotocol/service-sdk';
|
|
211
|
+
|
|
212
|
+
const service = defineDAINService({ /* config */ });
|
|
213
|
+
await service.startNode({ port: 3000 });
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Deno
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
import { defineDAINService } from '@dainprotocol/service-sdk';
|
|
220
|
+
|
|
221
|
+
const service = defineDAINService({ /* config */ });
|
|
222
|
+
await service.startDeno({ port: 3000 });
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Cloudflare Workers
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
import { defineDAINService } from '@dainprotocol/service-sdk';
|
|
229
|
+
|
|
230
|
+
const service = defineDAINService({ /* config */ });
|
|
231
|
+
|
|
232
|
+
export default {
|
|
233
|
+
fetch: service.startWorkers(),
|
|
234
|
+
};
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Next.js (App Router)
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
// app/api/dain/[...dain]/route.ts
|
|
241
|
+
import { createNextDainService } from '@dainprotocol/service-sdk/next';
|
|
242
|
+
|
|
243
|
+
const { GET, POST } = createNextDainService({
|
|
244
|
+
metadata: { /* ... */ },
|
|
245
|
+
identity: { /* ... */ },
|
|
246
|
+
tools: [ /* ... */ ],
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
export { GET, POST };
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## 🔧 Tools
|
|
253
|
+
|
|
254
|
+
### Creating Tools
|
|
255
|
+
|
|
256
|
+
Use the `createTool` helper for better type inference:
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { createTool } from '@dainprotocol/service-sdk';
|
|
260
|
+
|
|
261
|
+
const weatherTool = createTool({
|
|
262
|
+
id: 'get-weather',
|
|
263
|
+
name: 'Get Weather',
|
|
264
|
+
description: 'Get current weather',
|
|
265
|
+
input: z.object({
|
|
266
|
+
lat: z.number(),
|
|
267
|
+
lon: z.number(),
|
|
268
|
+
}),
|
|
269
|
+
output: z.object({
|
|
270
|
+
temp: z.number(),
|
|
271
|
+
description: z.string(),
|
|
272
|
+
}),
|
|
273
|
+
handler: async ({ lat, lon }) => {
|
|
274
|
+
const response = await fetch(
|
|
275
|
+
`https://api.openweather.org/data/2.5/weather?lat=${lat}&lon=${lon}`
|
|
276
|
+
);
|
|
277
|
+
const data = await response.json();
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
text: `Temperature: ${data.main.temp}°C`,
|
|
281
|
+
data: {
|
|
282
|
+
temp: data.main.temp,
|
|
283
|
+
description: data.weather[0].description,
|
|
284
|
+
},
|
|
285
|
+
ui: undefined,
|
|
286
|
+
};
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Tool Interfaces
|
|
292
|
+
|
|
293
|
+
Tools can implement standardized interfaces for better interoperability:
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
import { createToolWithInterface, ToolInterfaceType } from '@dainprotocol/service-sdk';
|
|
297
|
+
|
|
298
|
+
const searchTool = createToolWithInterface({
|
|
299
|
+
id: 'search-docs',
|
|
300
|
+
name: 'Search Documentation',
|
|
301
|
+
description: 'Search through documentation',
|
|
302
|
+
interface: ToolInterfaceType.KNOWLEDGE_SEARCH,
|
|
303
|
+
input: z.object({
|
|
304
|
+
query: z.string(),
|
|
305
|
+
}),
|
|
306
|
+
output: z.object({
|
|
307
|
+
results: z.array(z.object({
|
|
308
|
+
title: z.string(),
|
|
309
|
+
content: z.string(),
|
|
310
|
+
citations: z.array(CitationSchema),
|
|
311
|
+
})),
|
|
312
|
+
}),
|
|
313
|
+
handler: async ({ query }) => {
|
|
314
|
+
// Implementation
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Tool Pricing
|
|
320
|
+
|
|
321
|
+
Add pricing to monetize your tools:
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
const paidTool = {
|
|
325
|
+
id: 'premium-analysis',
|
|
326
|
+
name: 'Premium Analysis',
|
|
327
|
+
description: 'Advanced data analysis',
|
|
328
|
+
pricing: {
|
|
329
|
+
pricePerUse: 0.50, // $0.50 per use
|
|
330
|
+
currency: 'USD',
|
|
331
|
+
},
|
|
332
|
+
// ... rest of tool config
|
|
333
|
+
};
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Toolboxes
|
|
337
|
+
|
|
338
|
+
Group related tools into toolboxes:
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
import { createToolbox } from '@dainprotocol/service-sdk';
|
|
342
|
+
|
|
343
|
+
const weatherToolbox = createToolbox({
|
|
344
|
+
id: 'weather-toolbox',
|
|
345
|
+
name: 'Weather Tools',
|
|
346
|
+
description: 'Complete weather information toolkit',
|
|
347
|
+
tools: ['get-weather', 'get-forecast', 'get-alerts'],
|
|
348
|
+
metadata: {
|
|
349
|
+
complexity: 'simple',
|
|
350
|
+
applicableFields: ['weather', 'climate'],
|
|
351
|
+
},
|
|
352
|
+
recommendedPrompt: 'Use these tools to get comprehensive weather information',
|
|
353
|
+
});
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
## 🔐 OAuth2 Integration
|
|
357
|
+
|
|
358
|
+
The SDK includes comprehensive OAuth2 support with automatic token management and refresh.
|
|
359
|
+
|
|
360
|
+
### Basic OAuth2 Setup
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
import { defineDAINService } from '@dainprotocol/service-sdk';
|
|
364
|
+
import { InMemoryStorageAdapter } from '@dainprotocol/oauth2-token-manager';
|
|
365
|
+
|
|
366
|
+
const service = defineDAINService({
|
|
367
|
+
metadata: { /* ... */ },
|
|
368
|
+
identity: { /* ... */ },
|
|
369
|
+
tools: [ /* ... */ ],
|
|
370
|
+
oauth2: {
|
|
371
|
+
baseUrl: 'https://your-service.com',
|
|
372
|
+
tokenStore: new InMemoryStorageAdapter(),
|
|
373
|
+
providers: {
|
|
374
|
+
github: {
|
|
375
|
+
clientId: process.env.GITHUB_CLIENT_ID!,
|
|
376
|
+
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
|
377
|
+
authorizationUrl: 'https://github.com/login/oauth/authorize',
|
|
378
|
+
tokenUrl: 'https://github.com/login/oauth/access_token',
|
|
379
|
+
scopes: ['user', 'repo'],
|
|
380
|
+
reason: 'Access your GitHub repositories',
|
|
381
|
+
requiredTools: ['get-repos', 'create-gist'],
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Persistent Storage (PostgreSQL)
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
import { DrizzleStorageAdapter } from '@dainprotocol/oauth2-storage-drizzle';
|
|
392
|
+
import { drizzle } from 'drizzle-orm/postgres-js';
|
|
393
|
+
import postgres from 'postgres';
|
|
394
|
+
|
|
395
|
+
const sql = postgres(process.env.DATABASE_URL!);
|
|
396
|
+
const db = drizzle(sql);
|
|
397
|
+
|
|
398
|
+
const service = defineDAINService({
|
|
399
|
+
oauth2: {
|
|
400
|
+
baseUrl: 'https://your-service.com',
|
|
401
|
+
tokenStore: new DrizzleStorageAdapter(db, { dialect: 'postgres' }),
|
|
402
|
+
providers: { /* ... */ },
|
|
403
|
+
},
|
|
404
|
+
// ... rest of config
|
|
405
|
+
});
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Custom Token Paths
|
|
409
|
+
|
|
410
|
+
For OAuth2 providers with non-standard response formats (like Slack):
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
const service = defineDAINService({
|
|
414
|
+
oauth2: {
|
|
415
|
+
baseUrl: 'https://your-service.com',
|
|
416
|
+
providers: {
|
|
417
|
+
slack: {
|
|
418
|
+
clientId: process.env.SLACK_CLIENT_ID!,
|
|
419
|
+
clientSecret: process.env.SLACK_CLIENT_SECRET!,
|
|
420
|
+
authorizationUrl: 'https://slack.com/oauth/v2/authorize',
|
|
421
|
+
tokenUrl: 'https://slack.com/api/oauth.v2.access',
|
|
422
|
+
scopes: ['chat:write', 'channels:read'],
|
|
423
|
+
// Custom paths for extracting tokens from response
|
|
424
|
+
tokenPaths: {
|
|
425
|
+
accessToken: 'authed_user.access_token',
|
|
426
|
+
tokenType: 'token_type',
|
|
427
|
+
scope: 'scope',
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
// ... rest of config
|
|
433
|
+
});
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Using OAuth2 in Tools
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
import { createOAuth2Tool } from '@dainprotocol/service-sdk';
|
|
440
|
+
|
|
441
|
+
const getGitHubProfileTool = createOAuth2Tool({
|
|
442
|
+
id: 'get-github-profile',
|
|
443
|
+
name: 'Get GitHub Profile',
|
|
444
|
+
description: 'Get the authenticated user GitHub profile',
|
|
445
|
+
provider: 'github',
|
|
446
|
+
input: z.object({}),
|
|
447
|
+
output: z.object({
|
|
448
|
+
login: z.string(),
|
|
449
|
+
name: z.string(),
|
|
450
|
+
email: z.string().nullable(),
|
|
451
|
+
bio: z.string().nullable(),
|
|
452
|
+
}),
|
|
453
|
+
handler: async (input, agentInfo, context) => {
|
|
454
|
+
const accessToken = await context.oauth2Client!.getAccessToken(
|
|
455
|
+
'github',
|
|
456
|
+
`${agentInfo.agentId}@dain.local`
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
const response = await fetch('https://api.github.com/user', {
|
|
460
|
+
headers: {
|
|
461
|
+
Authorization: `Bearer ${accessToken}`,
|
|
462
|
+
Accept: 'application/vnd.github.v3+json',
|
|
463
|
+
},
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
const data = await response.json();
|
|
467
|
+
|
|
468
|
+
return {
|
|
469
|
+
text: `GitHub Profile: ${data.login}`,
|
|
470
|
+
data: {
|
|
471
|
+
login: data.login,
|
|
472
|
+
name: data.name,
|
|
473
|
+
email: data.email,
|
|
474
|
+
bio: data.bio,
|
|
475
|
+
},
|
|
476
|
+
ui: undefined,
|
|
477
|
+
};
|
|
478
|
+
},
|
|
479
|
+
});
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### OAuth2 Provider Options
|
|
483
|
+
|
|
484
|
+
```typescript
|
|
485
|
+
interface OAuth2ProviderConfig {
|
|
486
|
+
clientId: string;
|
|
487
|
+
clientSecret: string;
|
|
488
|
+
authorizationUrl: string;
|
|
489
|
+
tokenUrl: string;
|
|
490
|
+
scopes: string[];
|
|
491
|
+
usePKCE?: boolean; // Use PKCE flow (recommended for public clients)
|
|
492
|
+
useBasicAuth?: boolean; // Use Basic Auth for token exchange
|
|
493
|
+
reason?: string; // Explain why OAuth is needed (shown to users)
|
|
494
|
+
requiredTools?: string[]; // Tools that require this OAuth connection
|
|
495
|
+
extraAuthParams?: Record<string, string>; // Additional auth parameters
|
|
496
|
+
responseRootKey?: string; // Root key for token response
|
|
497
|
+
tokenPaths?: { // Custom token extraction paths
|
|
498
|
+
accessToken?: string | string[];
|
|
499
|
+
refreshToken?: string | string[];
|
|
500
|
+
expiresIn?: string | string[];
|
|
501
|
+
tokenType?: string | string[];
|
|
502
|
+
scope?: string | string[];
|
|
503
|
+
};
|
|
504
|
+
onSuccess?: (agentId: string, tokens: OAuth2Tokens) => Promise<void>;
|
|
505
|
+
}
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
## 🔌 Plugins
|
|
509
|
+
|
|
510
|
+
Plugins extend functionality on both the service and client side.
|
|
511
|
+
|
|
512
|
+
### Built-in Plugins
|
|
513
|
+
|
|
514
|
+
#### Crypto Plugin
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
import { CryptoPlugin } from '@dainprotocol/service-sdk/plugins';
|
|
518
|
+
|
|
519
|
+
const cryptoPlugin = new CryptoPlugin();
|
|
520
|
+
|
|
521
|
+
const service = defineDAINService({
|
|
522
|
+
plugins: [cryptoPlugin],
|
|
523
|
+
// ... rest of config
|
|
524
|
+
});
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
Provides:
|
|
528
|
+
- Message signing and verification
|
|
529
|
+
- Encryption/decryption
|
|
530
|
+
- Hash generation
|
|
531
|
+
- Key management
|
|
532
|
+
|
|
533
|
+
#### Citations Plugin
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
import { CitationsPlugin } from '@dainprotocol/service-sdk/plugins';
|
|
537
|
+
|
|
538
|
+
const citationsPlugin = new CitationsPlugin({
|
|
539
|
+
namespace: 'my-service',
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
const service = defineDAINService({
|
|
543
|
+
plugins: [citationsPlugin],
|
|
544
|
+
// ... rest of config
|
|
545
|
+
});
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
Provides:
|
|
549
|
+
- Citation tracking
|
|
550
|
+
- Source verification
|
|
551
|
+
- Citation formatting
|
|
552
|
+
|
|
553
|
+
#### Time Plugin
|
|
554
|
+
|
|
555
|
+
```typescript
|
|
556
|
+
import { TimePlugin } from '@dainprotocol/service-sdk/plugins';
|
|
557
|
+
|
|
558
|
+
const timePlugin = new TimePlugin();
|
|
559
|
+
|
|
560
|
+
const service = defineDAINService({
|
|
561
|
+
plugins: [timePlugin],
|
|
562
|
+
// ... rest of config
|
|
563
|
+
});
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
Provides:
|
|
567
|
+
- Timezone conversion
|
|
568
|
+
- Timestamp validation
|
|
569
|
+
- Time-based operations
|
|
570
|
+
|
|
571
|
+
### Creating Custom Plugins
|
|
572
|
+
|
|
573
|
+
```typescript
|
|
574
|
+
import { DainPlugin } from '@dainprotocol/service-sdk';
|
|
575
|
+
|
|
576
|
+
class MyCustomPlugin implements DainPlugin {
|
|
577
|
+
id = 'my-custom-plugin';
|
|
578
|
+
|
|
579
|
+
async initialize(service: DAINService): Promise<void> {
|
|
580
|
+
console.log('Plugin initialized');
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
async processInputClient?(input: any): Promise<any> {
|
|
584
|
+
// Process input on the client side
|
|
585
|
+
return { myData: 'client-processed' };
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
async processInputService?(input: any, agentInfo: AgentInfo): Promise<any> {
|
|
589
|
+
// Process input on the service side
|
|
590
|
+
return { myData: 'service-processed' };
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
async processOutputService?(output: any, agentInfo: AgentInfo): Promise<any> {
|
|
594
|
+
// Process output before sending to client
|
|
595
|
+
return output;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Use it
|
|
600
|
+
const service = defineDAINService({
|
|
601
|
+
plugins: [new MyCustomPlugin()],
|
|
602
|
+
// ... rest of config
|
|
603
|
+
});
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
## 📡 Client SDK
|
|
607
|
+
|
|
608
|
+
### Connecting to a Service
|
|
609
|
+
|
|
610
|
+
```typescript
|
|
611
|
+
import { DainServiceConnection, DainClientAuth } from '@dainprotocol/service-sdk/client';
|
|
612
|
+
|
|
613
|
+
const auth = new DainClientAuth({
|
|
614
|
+
apiKey: process.env.DAIN_API_KEY,
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
const service = new DainServiceConnection('http://localhost:3000', auth, {
|
|
618
|
+
plugins: [/* optional plugins */],
|
|
619
|
+
});
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
### Getting Service Metadata
|
|
623
|
+
|
|
624
|
+
```typescript
|
|
625
|
+
const metadata = await service.getMetadata();
|
|
626
|
+
console.log(metadata.title);
|
|
627
|
+
console.log(metadata.description);
|
|
628
|
+
console.log(metadata.version);
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### Listing Available Tools
|
|
632
|
+
|
|
633
|
+
```typescript
|
|
634
|
+
const tools = await service.getTools();
|
|
635
|
+
tools.forEach(tool => {
|
|
636
|
+
console.log(`${tool.name}: ${tool.description}`);
|
|
637
|
+
});
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### Calling Tools
|
|
641
|
+
|
|
642
|
+
```typescript
|
|
643
|
+
const result = await service.callTool('tool-id', {
|
|
644
|
+
param1: 'value1',
|
|
645
|
+
param2: 'value2',
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
console.log(result.text); // Human-readable response
|
|
649
|
+
console.log(result.data); // Structured data
|
|
650
|
+
console.log(result.ui); // Optional UI component
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
### Streaming Responses
|
|
654
|
+
|
|
655
|
+
```typescript
|
|
656
|
+
const stream = await service.callToolStream('tool-id', {
|
|
657
|
+
query: 'What is the weather?',
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
for await (const chunk of stream) {
|
|
661
|
+
if (chunk.type === 'text-delta') {
|
|
662
|
+
process.stdout.write(chunk.textDelta);
|
|
663
|
+
} else if (chunk.type === 'tool-result') {
|
|
664
|
+
console.log('\nFinal result:', chunk.result);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
### Working with Contexts
|
|
670
|
+
|
|
671
|
+
```typescript
|
|
672
|
+
const contexts = await service.getContexts();
|
|
673
|
+
const userContext = await service.getContext('user-context', {
|
|
674
|
+
param: 'value',
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
console.log(userContext.data);
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
### Working with Datasources
|
|
681
|
+
|
|
682
|
+
```typescript
|
|
683
|
+
const datasources = await service.getDatasources();
|
|
684
|
+
const data = await service.getDatasource('my-datasource', {
|
|
685
|
+
filter: 'active',
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
console.log(data.data);
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
### OAuth2 from Client
|
|
692
|
+
|
|
693
|
+
```typescript
|
|
694
|
+
// Check available OAuth2 providers
|
|
695
|
+
const providers = await service.getOAuth2Providers();
|
|
696
|
+
|
|
697
|
+
// Initiate OAuth flow
|
|
698
|
+
const authResult = await service.callTool('oauth2-github', {});
|
|
699
|
+
console.log('Auth URL:', authResult.data.authUrl);
|
|
700
|
+
|
|
701
|
+
// After authentication, call OAuth-protected tools
|
|
702
|
+
const profile = await service.callTool('get-github-profile', {});
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
### Using with AI SDK
|
|
706
|
+
|
|
707
|
+
```typescript
|
|
708
|
+
import { generateText } from 'ai';
|
|
709
|
+
import { anthropic } from '@ai-sdk/anthropic';
|
|
710
|
+
|
|
711
|
+
const tools = await service.getToolsAsAISDKTools();
|
|
712
|
+
|
|
713
|
+
const result = await generateText({
|
|
714
|
+
model: anthropic('claude-3-5-sonnet-20241022'),
|
|
715
|
+
prompt: 'What is the weather in San Francisco?',
|
|
716
|
+
tools,
|
|
717
|
+
maxSteps: 10,
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
console.log(result.text);
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
## 🚀 Advanced Features
|
|
724
|
+
|
|
725
|
+
### Contexts
|
|
726
|
+
|
|
727
|
+
Contexts provide dynamic information about the service or user state:
|
|
728
|
+
|
|
729
|
+
```typescript
|
|
730
|
+
const userContext = {
|
|
731
|
+
id: 'user-preferences',
|
|
732
|
+
name: 'User Preferences',
|
|
733
|
+
description: 'User-specific preferences and settings',
|
|
734
|
+
getContextData: async (agentInfo, extraData) => {
|
|
735
|
+
// Fetch user preferences from database
|
|
736
|
+
return {
|
|
737
|
+
theme: 'dark',
|
|
738
|
+
language: 'en',
|
|
739
|
+
timezone: 'America/Los_Angeles',
|
|
740
|
+
};
|
|
741
|
+
},
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
const service = defineDAINService({
|
|
745
|
+
contexts: [userContext],
|
|
746
|
+
// ... rest of config
|
|
747
|
+
});
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
### Datasources
|
|
751
|
+
|
|
752
|
+
Datasources provide queryable data to AI agents:
|
|
753
|
+
|
|
754
|
+
```typescript
|
|
755
|
+
const productDatasource = {
|
|
756
|
+
id: 'products',
|
|
757
|
+
name: 'Product Catalog',
|
|
758
|
+
description: 'Search and browse products',
|
|
759
|
+
type: 'data' as const,
|
|
760
|
+
input: z.object({
|
|
761
|
+
category: z.string().optional(),
|
|
762
|
+
minPrice: z.number().optional(),
|
|
763
|
+
maxPrice: z.number().optional(),
|
|
764
|
+
}),
|
|
765
|
+
getDatasource: async (agentInfo, params, extraData) => {
|
|
766
|
+
// Query database
|
|
767
|
+
const products = await db.products.findMany({
|
|
768
|
+
where: {
|
|
769
|
+
category: params.category,
|
|
770
|
+
price: {
|
|
771
|
+
gte: params.minPrice,
|
|
772
|
+
lte: params.maxPrice,
|
|
773
|
+
},
|
|
774
|
+
},
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
return products;
|
|
778
|
+
},
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
const service = defineDAINService({
|
|
782
|
+
datasources: [productDatasource],
|
|
783
|
+
// ... rest of config
|
|
784
|
+
});
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
### Widgets
|
|
788
|
+
|
|
789
|
+
Widgets display information in the DAIN UI:
|
|
790
|
+
|
|
791
|
+
```typescript
|
|
792
|
+
const dashboardWidget = {
|
|
793
|
+
id: 'dashboard',
|
|
794
|
+
name: 'Dashboard',
|
|
795
|
+
description: 'Overview dashboard',
|
|
796
|
+
icon: 'dashboard',
|
|
797
|
+
size: 'lg' as const,
|
|
798
|
+
getWidget: async (agentInfo, extraData) => {
|
|
799
|
+
const stats = await getStats(agentInfo.agentId);
|
|
800
|
+
|
|
801
|
+
return {
|
|
802
|
+
text: 'Dashboard overview',
|
|
803
|
+
data: stats,
|
|
804
|
+
ui: {
|
|
805
|
+
type: 'dashboard',
|
|
806
|
+
children: [
|
|
807
|
+
{ type: 'stat', label: 'Total Users', value: stats.users },
|
|
808
|
+
{ type: 'stat', label: 'Active Sessions', value: stats.sessions },
|
|
809
|
+
],
|
|
810
|
+
},
|
|
811
|
+
};
|
|
812
|
+
},
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
const service = defineDAINService({
|
|
816
|
+
widgets: [dashboardWidget],
|
|
817
|
+
// ... rest of config
|
|
818
|
+
});
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
### Long-Running Processes
|
|
822
|
+
|
|
823
|
+
For operations that take time to complete:
|
|
824
|
+
|
|
825
|
+
```typescript
|
|
826
|
+
import { MemoryProcessStore } from '@dainprotocol/service-sdk';
|
|
827
|
+
|
|
828
|
+
const service = defineDAINService({
|
|
829
|
+
processStore: new MemoryProcessStore(),
|
|
830
|
+
// ... rest of config
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
// In a tool handler
|
|
834
|
+
const processTool = {
|
|
835
|
+
id: 'start-analysis',
|
|
836
|
+
name: 'Start Analysis',
|
|
837
|
+
description: 'Start a long-running analysis',
|
|
838
|
+
handler: async (input, agentInfo, context) => {
|
|
839
|
+
const processId = generateId();
|
|
840
|
+
|
|
841
|
+
// Start background process
|
|
842
|
+
startAnalysisProcess(processId, input);
|
|
843
|
+
|
|
844
|
+
return {
|
|
845
|
+
text: 'Analysis started',
|
|
846
|
+
data: { processId },
|
|
847
|
+
ui: undefined,
|
|
848
|
+
processes: [{
|
|
849
|
+
id: processId,
|
|
850
|
+
name: 'Data Analysis',
|
|
851
|
+
description: 'Analyzing your data...',
|
|
852
|
+
type: 'one-time',
|
|
853
|
+
}],
|
|
854
|
+
};
|
|
855
|
+
},
|
|
856
|
+
};
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
### Human-in-the-Loop Actions
|
|
860
|
+
|
|
861
|
+
Request human approval or input during tool execution:
|
|
862
|
+
|
|
863
|
+
```typescript
|
|
864
|
+
const service = defineDAINService({
|
|
865
|
+
onHumanActionResponse: async ({ process, stepId, actionId, responseText, data }) => {
|
|
866
|
+
console.log('Human responded:', responseText);
|
|
867
|
+
// Continue the process based on human response
|
|
868
|
+
},
|
|
869
|
+
// ... rest of config
|
|
870
|
+
});
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
### Custom Routes
|
|
874
|
+
|
|
875
|
+
Add custom endpoints to your service:
|
|
876
|
+
|
|
877
|
+
```typescript
|
|
878
|
+
const service = defineDAINService({
|
|
879
|
+
routes: (app) => {
|
|
880
|
+
app.get('/health', (c) => {
|
|
881
|
+
return c.json({ status: 'healthy' });
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
app.post('/webhook', async (c) => {
|
|
885
|
+
const body = await c.req.json();
|
|
886
|
+
// Handle webhook
|
|
887
|
+
return c.json({ received: true });
|
|
888
|
+
});
|
|
889
|
+
},
|
|
890
|
+
// ... rest of config
|
|
891
|
+
});
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
### Payment Integration
|
|
895
|
+
|
|
896
|
+
```typescript
|
|
897
|
+
import { createStripePaymentIntent } from '@dainprotocol/service-sdk/payments';
|
|
898
|
+
|
|
899
|
+
const paidTool = {
|
|
900
|
+
id: 'premium-feature',
|
|
901
|
+
name: 'Premium Feature',
|
|
902
|
+
description: 'A premium paid feature',
|
|
903
|
+
pricing: { pricePerUse: 5.00, currency: 'USD' },
|
|
904
|
+
handler: async (input, agentInfo) => {
|
|
905
|
+
// Request payment
|
|
906
|
+
const paymentIntent = await createStripePaymentIntent({
|
|
907
|
+
amount: 500, // $5.00 in cents
|
|
908
|
+
currency: 'usd',
|
|
909
|
+
metadata: {
|
|
910
|
+
agentId: agentInfo.agentId,
|
|
911
|
+
toolId: 'premium-feature',
|
|
912
|
+
},
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
return {
|
|
916
|
+
text: 'Please complete payment to continue',
|
|
917
|
+
data: {},
|
|
918
|
+
ui: undefined,
|
|
919
|
+
pleasePay: paymentIntent,
|
|
920
|
+
};
|
|
921
|
+
},
|
|
922
|
+
};
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
## 📚 API Reference
|
|
926
|
+
|
|
927
|
+
### Service Configuration
|
|
928
|
+
|
|
929
|
+
```typescript
|
|
930
|
+
interface DAINServiceConfig {
|
|
931
|
+
metadata: {
|
|
932
|
+
title: string;
|
|
933
|
+
description: string;
|
|
934
|
+
version: string;
|
|
935
|
+
author: string;
|
|
936
|
+
tags: string[];
|
|
937
|
+
logo?: string;
|
|
938
|
+
};
|
|
939
|
+
identity: {
|
|
940
|
+
apiKey?: string;
|
|
941
|
+
publicKey?: string;
|
|
942
|
+
privateKey?: string;
|
|
943
|
+
agentId?: string;
|
|
944
|
+
orgId?: string;
|
|
945
|
+
};
|
|
946
|
+
tools: ToolConfig[];
|
|
947
|
+
toolboxes?: ToolboxConfig[];
|
|
948
|
+
services?: ServiceConfig[];
|
|
949
|
+
contexts?: ServiceContext[];
|
|
950
|
+
datasources?: ServiceDatasource[];
|
|
951
|
+
widgets?: ServiceWidget[];
|
|
952
|
+
agents?: ServiceAgent[];
|
|
953
|
+
plugins?: DainPlugin[];
|
|
954
|
+
oauth2?: {
|
|
955
|
+
baseUrl: string;
|
|
956
|
+
tokenStore?: StorageAdapter;
|
|
957
|
+
providers: Record<string, OAuth2ProviderConfig>;
|
|
958
|
+
};
|
|
959
|
+
processStore?: ProcessStoreAdapter;
|
|
960
|
+
routes?: (app: Hono) => void;
|
|
961
|
+
serverExtensions?: NodeServerExtension[];
|
|
962
|
+
baseUrl?: string;
|
|
963
|
+
exampleQueries?: {
|
|
964
|
+
category: string;
|
|
965
|
+
queries: string[];
|
|
966
|
+
}[];
|
|
967
|
+
getUserWidgets?: (agentInfo: AgentInfo, extraData?: any) => Promise<string[]>;
|
|
968
|
+
homeUI?: string | ((agentInfo: AgentInfo, extraData?: any) => Promise<string>);
|
|
969
|
+
onHumanActionResponse?: (response: {
|
|
970
|
+
process: Process;
|
|
971
|
+
stepId: string;
|
|
972
|
+
actionId: string;
|
|
973
|
+
responseText?: string;
|
|
974
|
+
data?: any;
|
|
975
|
+
}) => Promise<void>;
|
|
976
|
+
}
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
### Tool Configuration
|
|
980
|
+
|
|
981
|
+
```typescript
|
|
982
|
+
interface ToolConfig<TInput extends z.ZodType, TOutput extends z.ZodType> {
|
|
983
|
+
id: string;
|
|
984
|
+
name: string;
|
|
985
|
+
description: string;
|
|
986
|
+
input: TInput;
|
|
987
|
+
output: TOutput;
|
|
988
|
+
pricing?: {
|
|
989
|
+
pricePerUse: number;
|
|
990
|
+
currency: string;
|
|
991
|
+
};
|
|
992
|
+
interface?: string;
|
|
993
|
+
suggestConfirmation?: boolean;
|
|
994
|
+
suggestConfirmationUI?: (input: z.input<TInput>) => Promise<{
|
|
995
|
+
success?: boolean;
|
|
996
|
+
ui?: any;
|
|
997
|
+
}>;
|
|
998
|
+
handler: (
|
|
999
|
+
input: z.input<TInput>,
|
|
1000
|
+
agentInfo: AgentInfo,
|
|
1001
|
+
context: ToolContext
|
|
1002
|
+
) => Promise<{
|
|
1003
|
+
text: string;
|
|
1004
|
+
data: z.output<TOutput>;
|
|
1005
|
+
ui: any | undefined;
|
|
1006
|
+
pleasePay?: PaymentIntent;
|
|
1007
|
+
processes?: string[] | ProcessInfo[];
|
|
1008
|
+
}>;
|
|
1009
|
+
handleInputError?: (
|
|
1010
|
+
error: ZodError,
|
|
1011
|
+
agentInfo: AgentInfo,
|
|
1012
|
+
extraData?: any
|
|
1013
|
+
) => Promise<ToolResponse>;
|
|
1014
|
+
}
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
### Runtime Methods
|
|
1018
|
+
|
|
1019
|
+
```typescript
|
|
1020
|
+
// Node.js
|
|
1021
|
+
await service.startNode({ port?: number });
|
|
1022
|
+
|
|
1023
|
+
// Deno
|
|
1024
|
+
await service.startDeno({ port?: number });
|
|
1025
|
+
|
|
1026
|
+
// Cloudflare Workers
|
|
1027
|
+
export default { fetch: service.startWorkers() };
|
|
1028
|
+
|
|
1029
|
+
// Next.js
|
|
1030
|
+
const { GET, POST } = createNextDainService(config);
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
## 📖 Examples
|
|
1034
|
+
|
|
1035
|
+
### Complete Weather Service
|
|
1036
|
+
|
|
1037
|
+
See [example/simpleWeatherService-node.ts](./example/simpleWeatherService-node.ts)
|
|
1038
|
+
|
|
1039
|
+
### OAuth2 Integration
|
|
1040
|
+
|
|
1041
|
+
See [example/oauth-client-example.ts](./example/oauth-client-example.ts)
|
|
1042
|
+
|
|
1043
|
+
### Long-Running Processes
|
|
1044
|
+
|
|
1045
|
+
See [example/processService.ts](./example/processService.ts)
|
|
1046
|
+
|
|
1047
|
+
### Using Plugins
|
|
1048
|
+
|
|
1049
|
+
See [example/crypto-plugin-usage.ts](./example/crypto-plugin-usage.ts)
|
|
1050
|
+
|
|
1051
|
+
### Next.js Integration
|
|
1052
|
+
|
|
1053
|
+
See [examples/nextjs-app-router](./examples/nextjs-app-router)
|
|
1054
|
+
|
|
1055
|
+
## 🔄 Migration Guide
|
|
1056
|
+
|
|
1057
|
+
If you're upgrading from an older version, see [OAUTH_MIGRATION.md](./OAUTH_MIGRATION.md) for OAuth2 token store migration instructions.
|
|
1058
|
+
|
|
1059
|
+
## 🤝 Contributing
|
|
1060
|
+
|
|
1061
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
1062
|
+
|
|
1063
|
+
## 📄 License
|
|
1064
|
+
|
|
1065
|
+
ISC License - see LICENSE file for details
|
|
1066
|
+
|
|
1067
|
+
## 🔗 Links
|
|
1068
|
+
|
|
1069
|
+
- [DAIN Protocol Documentation](https://docs.dainprotocol.ai)
|
|
1070
|
+
- [OAuth2 Token Manager](https://github.com/dainprotocol/oauth2-token-manager)
|
|
1071
|
+
- [Example Services](./example)
|
|
1072
|
+
- [NPM Package](https://www.npmjs.com/package/@dainprotocol/service-sdk)
|
|
1073
|
+
|
|
1074
|
+
## 💬 Support
|
|
1075
|
+
|
|
1076
|
+
- GitHub Issues: [Report a bug or request a feature](https://github.com/dainprotocol/service-sdk/issues)
|
|
1077
|
+
- Discord: [Join our community](https://discord.gg/dainprotocol)
|
|
1078
|
+
- Twitter: [@dainprotocol](https://twitter.com/dainprotocol)
|
|
1079
|
+
|
|
1080
|
+
---
|
|
1081
|
+
|
|
1082
|
+
Built with ❤️ by the DAIN Protocol team
|