@gistplus/server 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 ADDED
@@ -0,0 +1,387 @@
1
+ # @gistplus/server
2
+
3
+ [![Twitter Follow](https://img.shields.io/twitter/follow/gistplus?style=social)](https://x.com/gistplus)
4
+
5
+ Server middleware for API providers to monetize endpoints with Gist Plus protocol.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @gistplus/server
11
+ ```
12
+
13
+ ## What is This?
14
+
15
+ This package enables **API providers** to:
16
+ - 💰 Monetize endpoints automatically
17
+ - 🤝 Negotiate pricing with agents
18
+ - 📝 Generate cryptographic receipts
19
+ - ⚖️ Enforce SLA guarantees
20
+ - 💵 Accept SOL/USDC/USDT payments
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import express from 'express';
26
+ import { Connection, Keypair } from '@solana/web3.js';
27
+ import { gistMiddleware } from '@gistplus/server';
28
+
29
+ const app = express();
30
+ const connection = new Connection('https://api.devnet.solana.com');
31
+ const wallet = Keypair.generate(); // Your provider wallet
32
+
33
+ // Add Gist Plus middleware
34
+ app.use('/api/*', gistMiddleware({
35
+ connection,
36
+ wallet,
37
+ endpoint: 'https://your-api.com',
38
+ pricing: {
39
+ basePrice: 0.01,
40
+ token: 'USDC'
41
+ },
42
+ sla: {
43
+ maxLatencyMs: 2000,
44
+ minUptimePercent: 99.5
45
+ }
46
+ }));
47
+
48
+ // Your API endpoint (now monetized!)
49
+ app.post('/api/inference', async (req, res) => {
50
+ // req.gistSession contains session info
51
+ const result = await runInference(req.body);
52
+
53
+ // Automatic receipt generation
54
+ return res.gistReceipt?.(result);
55
+ });
56
+
57
+ app.listen(3000);
58
+ ```
59
+
60
+ That's it! Your API now:
61
+ - ✅ Responds with 402 Payment Required
62
+ - ✅ Negotiates with agents automatically
63
+ - ✅ Validates sessions
64
+ - ✅ Generates signed receipts
65
+ - ✅ Tracks balances
66
+
67
+ ## Core Features
68
+
69
+ ### Automatic 402 Responses
70
+
71
+ ```typescript
72
+ // When agent hits your endpoint without payment:
73
+ GET /api/inference
74
+
75
+ 402 Payment Required
76
+ X-Gist-Offer: {signed offer with your pricing}
77
+ ```
78
+
79
+ ### Dynamic Pricing
80
+
81
+ ```typescript
82
+ import { LoadBasedPricingStrategy } from '@gistplus/server';
83
+
84
+ const pricing = new LoadBasedPricingStrategy(
85
+ 0.01, // base price
86
+ 'USDC',
87
+ () => getCurrentLoad() // returns 0-1
88
+ );
89
+
90
+ app.use(gistMiddleware({
91
+ // ...
92
+ pricing // Price increases with load
93
+ }));
94
+ ```
95
+
96
+ ### Multiple Pricing Strategies
97
+
98
+ ```typescript
99
+ // Static pricing
100
+ pricing: { basePrice: 0.01, token: 'USDC' }
101
+
102
+ // Load-based
103
+ pricing: new LoadBasedPricingStrategy(0.01, 'USDC', getLoad)
104
+
105
+ // Time-based (peak hours)
106
+ pricing: new TimeBasedPricingStrategy(
107
+ 0.02, // peak price
108
+ 0.01, // off-peak price
109
+ { start: 9, end: 17 }, // peak hours
110
+ 'USDC'
111
+ )
112
+
113
+ // SLA-based (stricter SLA = higher price)
114
+ pricing: new SLABasedPricingStrategy(0.01, 'USDC')
115
+ ```
116
+
117
+ ### Receipt Generation
118
+
119
+ ```typescript
120
+ app.post('/api/inference', async (req, res) => {
121
+ const result = await processRequest(req.body);
122
+
123
+ // Middleware automatically:
124
+ // - Generates receipt
125
+ // - Signs with your key
126
+ // - Verifies SLA
127
+ // - Updates session balance
128
+ // - Sends X-Gist-Receipt header
129
+
130
+ return res.gistReceipt?.(result);
131
+ });
132
+ ```
133
+
134
+ ### Session Management
135
+
136
+ ```typescript
137
+ app.post('/api/inference', async (req, res) => {
138
+ // Session info automatically attached
139
+ const session = req.gistSession;
140
+
141
+ console.log(session.remainingBalance);
142
+ console.log(session.requestCount);
143
+ console.log(session.expiresAt);
144
+
145
+ // Process request...
146
+ });
147
+ ```
148
+
149
+ ## API Reference
150
+
151
+ ### gistMiddleware(config)
152
+
153
+ Creates Express middleware for Gist Plus protocol.
154
+
155
+ ```typescript
156
+ app.use(gistMiddleware({
157
+ connection: Connection, // Solana connection
158
+ wallet: Keypair, // Provider wallet
159
+ endpoint: string, // Your API URL
160
+ pricing: PricingStrategy | { // Pricing config
161
+ basePrice: number,
162
+ token: 'SOL' | 'USDC' | 'USDT' | 'BONK'
163
+ },
164
+ sla: { // SLA guarantees
165
+ maxLatencyMs?: number,
166
+ minUptimePercent?: number,
167
+ maxErrorRatePercent?: number
168
+ },
169
+ sessionDurationMs?: number, // Default: 10 minutes
170
+ offerExpirationMs?: number // Default: 1 minute
171
+ }));
172
+ ```
173
+
174
+ ### Request Extensions
175
+
176
+ The middleware adds these to Express Request:
177
+
178
+ ```typescript
179
+ interface Request {
180
+ gistSession?: Session; // Current session
181
+ x402Intent?: Intent; // Agent's intent
182
+ x402RequestStartTime?: number; // For latency calc
183
+ }
184
+ ```
185
+
186
+ ### Response Extensions
187
+
188
+ The middleware adds these to Express Response:
189
+
190
+ ```typescript
191
+ interface Response {
192
+ gistReceipt?: (data: any) => Response; // Generate receipt
193
+ }
194
+ ```
195
+
196
+ ## Usage Patterns
197
+
198
+ ### Basic Protected Endpoint
199
+
200
+ ```typescript
201
+ app.use('/api/*', gistMiddleware(config));
202
+
203
+ app.post('/api/weather', async (req, res) => {
204
+ const data = await getWeather(req.body.city);
205
+ return res.gistReceipt?.(data);
206
+ });
207
+ ```
208
+
209
+ ### Custom Validation
210
+
211
+ ```typescript
212
+ app.post('/api/premium', async (req, res) => {
213
+ const session = req.gistSession;
214
+
215
+ // Check if enough balance for premium feature
216
+ if (session.remainingBalance < 0.1) {
217
+ return res.status(402).json({
218
+ error: 'Insufficient balance for premium feature'
219
+ });
220
+ }
221
+
222
+ const result = await processPremium(req.body);
223
+ return res.gistReceipt?.(result);
224
+ });
225
+ ```
226
+
227
+ ### Multiple Endpoints, Different Pricing
228
+
229
+ ```typescript
230
+ // Cheap endpoint
231
+ app.use('/api/basic', gistMiddleware({
232
+ ...config,
233
+ pricing: { basePrice: 0.001, token: 'USDC' }
234
+ }));
235
+
236
+ // Expensive endpoint
237
+ app.use('/api/premium', gistMiddleware({
238
+ ...config,
239
+ pricing: { basePrice: 0.1, token: 'USDC' }
240
+ }));
241
+ ```
242
+
243
+ ### With Error Handling
244
+
245
+ ```typescript
246
+ app.post('/api/inference', async (req, res) => {
247
+ try {
248
+ const result = await runInference(req.body);
249
+ return res.gistReceipt?.(result);
250
+ } catch (error) {
251
+ // Errors don't charge the session
252
+ res.status(500).json({ error: error.message });
253
+ }
254
+ });
255
+ ```
256
+
257
+ ## Pricing Strategies
258
+
259
+ ### Static Pricing
260
+
261
+ ```typescript
262
+ pricing: {
263
+ basePrice: 0.01,
264
+ token: 'USDC'
265
+ }
266
+ ```
267
+
268
+ ### Load-Based Pricing
269
+
270
+ ```typescript
271
+ import { LoadBasedPricingStrategy } from '@gistplus/server';
272
+
273
+ const pricing = new LoadBasedPricingStrategy(
274
+ 0.01, // base price
275
+ 'USDC',
276
+ () => {
277
+ // Return load factor 0-1
278
+ const currentLoad = getCurrentLoad();
279
+ return currentLoad / maxLoad;
280
+ }
281
+ );
282
+ // Load 0% → 0.01 USDC
283
+ // Load 50% → 0.015 USDC
284
+ // Load 100% → 0.02 USDC
285
+ ```
286
+
287
+ ### Time-Based Pricing
288
+
289
+ ```typescript
290
+ import { TimeBasedPricingStrategy } from '@gistplus/server';
291
+
292
+ const pricing = new TimeBasedPricingStrategy(
293
+ 0.02, // peak price
294
+ 0.01, // off-peak price
295
+ { start: 9, end: 17 }, // 9 AM - 5 PM
296
+ 'USDC'
297
+ );
298
+ ```
299
+
300
+ ### SLA-Based Pricing
301
+
302
+ ```typescript
303
+ import { SLABasedPricingStrategy } from '@gistplus/server';
304
+
305
+ const pricing = new SLABasedPricingStrategy(0.01, 'USDC');
306
+ // <1s latency SLA → 1.5x price
307
+ // <2s latency SLA → 1.25x price
308
+ // >99.5% uptime → 1.3x price
309
+ ```
310
+
311
+ ### Custom Pricing
312
+
313
+ ```typescript
314
+ class MyCustomPricing implements PricingStrategy {
315
+ getPrice(intent: Intent): number {
316
+ // Your custom logic
317
+ if (intent.capability === 'premium') return 0.1;
318
+ if (isWeekend()) return 0.005;
319
+ return 0.01;
320
+ }
321
+ }
322
+
323
+ const pricing = new MyCustomPricing();
324
+ ```
325
+
326
+ ## Examples
327
+
328
+ ### AI Inference API
329
+
330
+ ```typescript
331
+ app.use('/api/inference', gistMiddleware({
332
+ connection,
333
+ wallet,
334
+ endpoint: 'https://ai.example.com',
335
+ pricing: { basePrice: 0.01, token: 'USDC' },
336
+ sla: { maxLatencyMs: 3000 }
337
+ }));
338
+
339
+ app.post('/api/inference', async (req, res) => {
340
+ const result = await model.generate(req.body.prompt);
341
+ return res.gistReceipt?.(result);
342
+ });
343
+ ```
344
+
345
+ ### Image Generation API
346
+
347
+ ```typescript
348
+ app.use('/api/image', gistMiddleware({
349
+ connection,
350
+ wallet,
351
+ endpoint: 'https://image.example.com',
352
+ pricing: { basePrice: 0.05, token: 'USDC' },
353
+ sla: { maxLatencyMs: 10000 }
354
+ }));
355
+
356
+ app.post('/api/image', async (req, res) => {
357
+ const imageUrl = await generateImage(req.body);
358
+ return res.gistReceipt?.({ imageUrl });
359
+ });
360
+ ```
361
+
362
+ ### Data API
363
+
364
+ ```typescript
365
+ app.use('/api/data', gistMiddleware({
366
+ connection,
367
+ wallet,
368
+ endpoint: 'https://data.example.com',
369
+ pricing: { basePrice: 0.001, token: 'USDC' },
370
+ sla: { maxLatencyMs: 1000 }
371
+ }));
372
+
373
+ app.post('/api/data', async (req, res) => {
374
+ const data = await fetchData(req.body.query);
375
+ return res.gistReceipt?.(data);
376
+ });
377
+ ```
378
+
379
+ ## Related Packages
380
+
381
+ - **[@gistplus/core](https://www.npmjs.com/package/@gistplus/core)** - Core protocol (auto-installed)
382
+ - **[@gistplus/client](https://www.npmjs.com/package/@gistplus/client)** - For AI agents
383
+
384
+ ## License
385
+
386
+ Apache 2.0
387
+
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @gistplus/server
3
+ *
4
+ * Server-side middleware for API providers to implement Gist Plus protocol.
5
+ * Handles automatic Intent responses, Offer generation, Session management,
6
+ * and Receipt creation.
7
+ */
8
+ export * from './middleware';
9
+ export * from './provider';
10
+ export * from './session-store';
11
+ export * from './pricing';
12
+ export { Intent, Offer, Session, Receipt, SLA, SessionState, SupportedToken, SUPPORTED_TOKENS, } from '@gistplus/core';
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ /**
3
+ * @gistplus/server
4
+ *
5
+ * Server-side middleware for API providers to implement Gist Plus protocol.
6
+ * Handles automatic Intent responses, Offer generation, Session management,
7
+ * and Receipt creation.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
21
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.SUPPORTED_TOKENS = exports.SessionState = void 0;
25
+ __exportStar(require("./middleware"), exports);
26
+ __exportStar(require("./provider"), exports);
27
+ __exportStar(require("./session-store"), exports);
28
+ __exportStar(require("./pricing"), exports);
29
+ // Re-export core types
30
+ var core_1 = require("@gistplus/core");
31
+ Object.defineProperty(exports, "SessionState", { enumerable: true, get: function () { return core_1.SessionState; } });
32
+ Object.defineProperty(exports, "SUPPORTED_TOKENS", { enumerable: true, get: function () { return core_1.SUPPORTED_TOKENS; } });
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Express middleware for Gist Plus protocol
3
+ */
4
+ import { Request, Response, NextFunction } from 'express';
5
+ import { Intent, Session } from '@gistplus/core';
6
+ import { GistProvider, GistProviderConfig } from './provider';
7
+ declare global {
8
+ namespace Express {
9
+ interface Request {
10
+ gistSession?: Session;
11
+ gistIntent?: Intent;
12
+ gistRequestStartTime?: number;
13
+ }
14
+ interface Response {
15
+ gistReceipt?: (data: any) => Response;
16
+ }
17
+ }
18
+ }
19
+ /**
20
+ * Create Gist Plus middleware for Express
21
+ *
22
+ * This middleware handles:
23
+ * 1. Detecting requests without sessions (402 responses with Offers)
24
+ * 2. Validating session headers
25
+ * 3. Attaching session info to request
26
+ * 4. Generating and attaching Receipts to responses
27
+ *
28
+ * @param config - Provider configuration
29
+ * @returns Express middleware
30
+ */
31
+ export declare function gistMiddleware(config: GistProviderConfig): (req: Request, res: Response, next: NextFunction) => Promise<void>;
32
+ /**
33
+ * Middleware specifically for session creation endpoint
34
+ */
35
+ export declare function sessionCreationHandler(provider: GistProvider): (req: Request, res: Response) => Promise<void>;
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ /**
3
+ * Express middleware for Gist Plus protocol
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.gistMiddleware = gistMiddleware;
7
+ exports.sessionCreationHandler = sessionCreationHandler;
8
+ const core_1 = require("@gistplus/core");
9
+ const provider_1 = require("./provider");
10
+ /**
11
+ * Create Gist Plus middleware for Express
12
+ *
13
+ * This middleware handles:
14
+ * 1. Detecting requests without sessions (402 responses with Offers)
15
+ * 2. Validating session headers
16
+ * 3. Attaching session info to request
17
+ * 4. Generating and attaching Receipts to responses
18
+ *
19
+ * @param config - Provider configuration
20
+ * @returns Express middleware
21
+ */
22
+ function gistMiddleware(config) {
23
+ const provider = new provider_1.GistProvider(config);
24
+ return async (req, res, next) => {
25
+ req.gistRequestStartTime = Date.now();
26
+ try {
27
+ // Check for session header
28
+ const sessionId = req.headers[core_1.HEADERS.SESSION_ID.toLowerCase()];
29
+ if (sessionId) {
30
+ // Request has session - validate and attach
31
+ await handleSessionRequest(req, res, next, provider, sessionId);
32
+ }
33
+ else {
34
+ // No session - check for Intent and respond with Offer
35
+ await handleIntentRequest(req, res, next, provider);
36
+ }
37
+ }
38
+ catch (error) {
39
+ console.error('Gist Plus middleware error:', error);
40
+ res.status(500).json({ error: 'Internal server error' });
41
+ }
42
+ };
43
+ }
44
+ /**
45
+ * Handle request with Intent (no session yet)
46
+ */
47
+ async function handleIntentRequest(req, res, next, provider) {
48
+ // Check for Intent header
49
+ const intentHeader = req.headers[core_1.HEADERS.INTENT.toLowerCase()];
50
+ if (!intentHeader) {
51
+ // No Intent, no Session -> 402 Quote Required
52
+ res.status(core_1.HTTP_STATUS.QUOTE_REQUIRED).json({
53
+ error: 'Payment Required',
54
+ message: 'This endpoint requires Gist Plus payment. Send an Intent to negotiate.',
55
+ protocol: 'Gist Plus',
56
+ });
57
+ return;
58
+ }
59
+ try {
60
+ // Parse and validate Intent
61
+ const intent = JSON.parse(intentHeader);
62
+ (0, core_1.validateIntent)(intent);
63
+ // Create Offer
64
+ const offer = await provider.createOfferForIntent(intent);
65
+ // Respond with 402 and Offer in header
66
+ res.setHeader(core_1.HEADERS.OFFER, JSON.stringify(offer));
67
+ res.status(core_1.HTTP_STATUS.QUOTE_REQUIRED).json({
68
+ message: 'Offer provided',
69
+ offerId: offer.offerId,
70
+ pricePerRequest: offer.pricePerRequest,
71
+ token: offer.token,
72
+ });
73
+ }
74
+ catch (error) {
75
+ res.status(core_1.HTTP_STATUS.INVALID_OFFER).json({
76
+ error: 'Invalid Intent',
77
+ message: String(error),
78
+ });
79
+ }
80
+ }
81
+ /**
82
+ * Handle request with existing Session
83
+ */
84
+ async function handleSessionRequest(req, res, next, provider, sessionId) {
85
+ try {
86
+ // Get session
87
+ const session = provider.getSession(sessionId);
88
+ if (!session) {
89
+ res.status(core_1.HTTP_STATUS.SESSION_EXPIRED).json({
90
+ error: 'Session not found',
91
+ message: `Session ${sessionId} does not exist or has expired`,
92
+ });
93
+ return;
94
+ }
95
+ // Validate session is active
96
+ // (This would check expiration, balance, etc.)
97
+ // Attach session to request
98
+ req.gistSession = session;
99
+ // Create Receipt helper function
100
+ res.gistReceipt = (data) => {
101
+ // This will be called by the route handler
102
+ provider
103
+ .createReceiptForRequest(sessionId, req.body, data, req.gistRequestStartTime)
104
+ .then((receipt) => {
105
+ // Attach receipt to response header
106
+ res.setHeader(core_1.HEADERS.RECEIPT, JSON.stringify(receipt));
107
+ res.setHeader(core_1.HEADERS.SLA_STATUS, receipt.slaVerification.met ? 'met' : 'breached');
108
+ res.json(data);
109
+ })
110
+ .catch((error) => {
111
+ res.status(500).json({
112
+ error: 'Failed to generate receipt',
113
+ message: String(error),
114
+ });
115
+ });
116
+ return res;
117
+ };
118
+ // Continue to route handler
119
+ next();
120
+ }
121
+ catch (error) {
122
+ res.status(500).json({
123
+ error: 'Session error',
124
+ message: String(error),
125
+ });
126
+ }
127
+ }
128
+ /**
129
+ * Middleware specifically for session creation endpoint
130
+ */
131
+ function sessionCreationHandler(provider) {
132
+ return async (req, res) => {
133
+ try {
134
+ const { offerId, depositAmount, txSignature } = req.body;
135
+ if (!offerId || !depositAmount || !txSignature) {
136
+ res.status(400).json({
137
+ error: 'Missing required fields',
138
+ required: ['offerId', 'depositAmount', 'txSignature'],
139
+ });
140
+ return;
141
+ }
142
+ // For MVP, we'll need to reconstruct or store the offer
143
+ // In production, you'd store offers temporarily
144
+ // Create session (this includes payment verification)
145
+ const agentPubkey = req.body.agentPubkey; // Should be derived from tx
146
+ // This is a simplified version - in production you'd:
147
+ // 1. Retrieve the stored offer by offerId
148
+ // 2. Verify the offer hasn't expired
149
+ // 3. Create the session
150
+ res.status(core_1.HTTP_STATUS.SESSION_STARTED).json({
151
+ message: 'Session creation endpoint - implement offer storage',
152
+ });
153
+ }
154
+ catch (error) {
155
+ res.status(500).json({
156
+ error: 'Failed to create session',
157
+ message: String(error),
158
+ });
159
+ }
160
+ };
161
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Pricing Strategies for Gist Plus providers
3
+ */
4
+ import { Intent, SupportedToken } from '@gistplus/core';
5
+ /**
6
+ * PricingStrategy interface
7
+ *
8
+ * Allows providers to implement dynamic pricing based on:
9
+ * - Intent requirements
10
+ * - Current load
11
+ * - Token type
12
+ * - Time of day
13
+ * - Agent reputation
14
+ */
15
+ export interface PricingStrategy {
16
+ getPrice(intent: Intent): Promise<number> | number;
17
+ }
18
+ /**
19
+ * Static pricing - same price for all requests
20
+ */
21
+ export declare class StaticPricingStrategy implements PricingStrategy {
22
+ private basePrice;
23
+ private token;
24
+ constructor(basePrice: number, token: SupportedToken);
25
+ getPrice(intent: Intent): number;
26
+ }
27
+ /**
28
+ * Load-based pricing - price increases with load
29
+ */
30
+ export declare class LoadBasedPricingStrategy implements PricingStrategy {
31
+ private basePrice;
32
+ private token;
33
+ private getLoadFactor;
34
+ constructor(basePrice: number, token: SupportedToken, getLoadFactor: () => number);
35
+ getPrice(intent: Intent): number;
36
+ }
37
+ /**
38
+ * Time-based pricing - different prices for different times
39
+ */
40
+ export declare class TimeBasedPricingStrategy implements PricingStrategy {
41
+ private peakPrice;
42
+ private offPeakPrice;
43
+ private peakHours;
44
+ private token;
45
+ constructor(peakPrice: number, offPeakPrice: number, peakHours: {
46
+ start: number;
47
+ end: number;
48
+ }, // 0-23
49
+ token: SupportedToken);
50
+ getPrice(intent: Intent): number;
51
+ }
52
+ /**
53
+ * SLA-based pricing - higher price for stricter SLAs
54
+ */
55
+ export declare class SLABasedPricingStrategy implements PricingStrategy {
56
+ private basePrice;
57
+ private token;
58
+ constructor(basePrice: number, token: SupportedToken);
59
+ getPrice(intent: Intent): number;
60
+ }
61
+ /**
62
+ * Custom pricing strategy - combine multiple strategies
63
+ */
64
+ export declare class CompositePricingStrategy implements PricingStrategy {
65
+ private strategies;
66
+ constructor(strategies: PricingStrategy[]);
67
+ getPrice(intent: Intent): Promise<number>;
68
+ }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ /**
3
+ * Pricing Strategies for Gist Plus providers
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CompositePricingStrategy = exports.SLABasedPricingStrategy = exports.TimeBasedPricingStrategy = exports.LoadBasedPricingStrategy = exports.StaticPricingStrategy = void 0;
7
+ /**
8
+ * Static pricing - same price for all requests
9
+ */
10
+ class StaticPricingStrategy {
11
+ constructor(basePrice, token) {
12
+ this.basePrice = basePrice;
13
+ this.token = token;
14
+ }
15
+ getPrice(intent) {
16
+ return this.basePrice;
17
+ }
18
+ }
19
+ exports.StaticPricingStrategy = StaticPricingStrategy;
20
+ /**
21
+ * Load-based pricing - price increases with load
22
+ */
23
+ class LoadBasedPricingStrategy {
24
+ constructor(basePrice, token, getLoadFactor // Returns 0-1, multiplies price
25
+ ) {
26
+ this.basePrice = basePrice;
27
+ this.token = token;
28
+ this.getLoadFactor = getLoadFactor;
29
+ }
30
+ getPrice(intent) {
31
+ const loadFactor = this.getLoadFactor();
32
+ const multiplier = 1 + loadFactor;
33
+ return this.basePrice * multiplier;
34
+ }
35
+ }
36
+ exports.LoadBasedPricingStrategy = LoadBasedPricingStrategy;
37
+ /**
38
+ * Time-based pricing - different prices for different times
39
+ */
40
+ class TimeBasedPricingStrategy {
41
+ constructor(peakPrice, offPeakPrice, peakHours, // 0-23
42
+ token) {
43
+ this.peakPrice = peakPrice;
44
+ this.offPeakPrice = offPeakPrice;
45
+ this.peakHours = peakHours;
46
+ this.token = token;
47
+ }
48
+ getPrice(intent) {
49
+ const hour = new Date().getHours();
50
+ const isPeak = hour >= this.peakHours.start && hour <= this.peakHours.end;
51
+ return isPeak ? this.peakPrice : this.offPeakPrice;
52
+ }
53
+ }
54
+ exports.TimeBasedPricingStrategy = TimeBasedPricingStrategy;
55
+ /**
56
+ * SLA-based pricing - higher price for stricter SLAs
57
+ */
58
+ class SLABasedPricingStrategy {
59
+ constructor(basePrice, token) {
60
+ this.basePrice = basePrice;
61
+ this.token = token;
62
+ }
63
+ getPrice(intent) {
64
+ let price = this.basePrice;
65
+ // Charge more for stricter latency requirements
66
+ if (intent.sla?.maxLatencyMs) {
67
+ if (intent.sla.maxLatencyMs < 1000) {
68
+ price *= 1.5; // 50% premium for sub-second latency
69
+ }
70
+ else if (intent.sla.maxLatencyMs < 2000) {
71
+ price *= 1.25; // 25% premium for sub-2s latency
72
+ }
73
+ }
74
+ // Charge more for higher uptime guarantees
75
+ if (intent.sla?.minUptimePercent && intent.sla.minUptimePercent > 99.5) {
76
+ price *= 1.3; // 30% premium for 99.5%+ uptime
77
+ }
78
+ return price;
79
+ }
80
+ }
81
+ exports.SLABasedPricingStrategy = SLABasedPricingStrategy;
82
+ /**
83
+ * Custom pricing strategy - combine multiple strategies
84
+ */
85
+ class CompositePricingStrategy {
86
+ constructor(strategies) {
87
+ this.strategies = strategies;
88
+ }
89
+ async getPrice(intent) {
90
+ const prices = await Promise.all(this.strategies.map(strategy => strategy.getPrice(intent)));
91
+ // Return average, max, or custom combination
92
+ return Math.max(...prices);
93
+ }
94
+ }
95
+ exports.CompositePricingStrategy = CompositePricingStrategy;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * GistProvider - Main provider interface for Gist Plus server
3
+ */
4
+ import { Connection, Keypair } from '@solana/web3.js';
5
+ import { Intent, Offer, Session, Receipt, SLA, SupportedToken } from '@gistplus/core';
6
+ import { PricingStrategy } from './pricing';
7
+ export interface GistProviderConfig {
8
+ /** Solana connection */
9
+ connection: Connection;
10
+ /** Provider's keypair for signing Offers and Receipts */
11
+ wallet: Keypair;
12
+ /** Provider's API endpoint base URL */
13
+ endpoint: string;
14
+ /** Pricing strategy */
15
+ pricing: PricingStrategy | {
16
+ basePrice: number;
17
+ token: SupportedToken;
18
+ };
19
+ /** SLA guarantees */
20
+ sla: SLA;
21
+ /** Session duration in milliseconds */
22
+ sessionDurationMs?: number;
23
+ /** Offer expiration in milliseconds */
24
+ offerExpirationMs?: number;
25
+ }
26
+ /**
27
+ * GistProvider - Server-side provider implementation
28
+ *
29
+ * Enables API providers to:
30
+ * 1. Respond to Intents with Offers
31
+ * 2. Create and manage Sessions
32
+ * 3. Generate signed Receipts
33
+ * 4. Verify payments on Solana
34
+ */
35
+ export declare class GistProvider {
36
+ private connection;
37
+ private wallet;
38
+ private endpoint;
39
+ private pricing;
40
+ private sla;
41
+ private sessionDurationMs;
42
+ private offerExpirationMs;
43
+ private sessionStore;
44
+ constructor(config: GistProviderConfig);
45
+ /**
46
+ * Create an Offer in response to an Intent
47
+ *
48
+ * @param intent - Agent's Intent
49
+ * @returns Signed Offer
50
+ */
51
+ createOfferForIntent(intent: Intent): Promise<Offer>;
52
+ /**
53
+ * Create a Session from an accepted Offer
54
+ *
55
+ * @param offer - Accepted Offer
56
+ * @param agentPubkey - Agent's public key
57
+ * @param depositAmount - Amount deposited
58
+ * @param txSignature - Solana transaction signature for payment
59
+ * @returns Active Session
60
+ */
61
+ createSessionFromOffer(offer: Offer, agentPubkey: string, depositAmount: number, txSignature: string): Promise<Session>;
62
+ /**
63
+ * Get a session by ID
64
+ */
65
+ getSession(sessionId: string): Session | undefined;
66
+ /**
67
+ * Create a Receipt for a completed request
68
+ *
69
+ * @param sessionId - Session ID
70
+ * @param inputData - Request input data
71
+ * @param outputData - Request output data
72
+ * @param requestStartedAt - Request start timestamp
73
+ * @returns Signed Receipt
74
+ */
75
+ createReceiptForRequest(sessionId: string, inputData: any, outputData: any, requestStartedAt: number): Promise<Receipt>;
76
+ /**
77
+ * Close a session and process refund
78
+ */
79
+ closeSession(sessionId: string): Promise<{
80
+ refundAmount: number;
81
+ txSignature?: string;
82
+ }>;
83
+ /**
84
+ * Verify payment on Solana blockchain
85
+ */
86
+ private verifyPayment;
87
+ /**
88
+ * Get provider's public key
89
+ */
90
+ get publicKey(): import("@solana/web3.js").PublicKey;
91
+ }
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ /**
3
+ * GistProvider - Main provider interface for Gist Plus server
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GistProvider = void 0;
7
+ const core_1 = require("@gistplus/core");
8
+ const session_store_1 = require("./session-store");
9
+ const pricing_1 = require("./pricing");
10
+ /**
11
+ * GistProvider - Server-side provider implementation
12
+ *
13
+ * Enables API providers to:
14
+ * 1. Respond to Intents with Offers
15
+ * 2. Create and manage Sessions
16
+ * 3. Generate signed Receipts
17
+ * 4. Verify payments on Solana
18
+ */
19
+ class GistProvider {
20
+ constructor(config) {
21
+ this.connection = config.connection;
22
+ this.wallet = config.wallet;
23
+ this.endpoint = config.endpoint;
24
+ this.sla = config.sla;
25
+ this.sessionDurationMs = config.sessionDurationMs || 600000; // 10 minutes default
26
+ this.offerExpirationMs = config.offerExpirationMs || 60000; // 1 minute default
27
+ // Initialize pricing strategy
28
+ if ('getPrice' in config.pricing) {
29
+ this.pricing = config.pricing;
30
+ }
31
+ else {
32
+ this.pricing = new pricing_1.StaticPricingStrategy(config.pricing.basePrice, config.pricing.token);
33
+ }
34
+ // Initialize session store
35
+ this.sessionStore = new session_store_1.SessionStore(this.connection, this.wallet);
36
+ }
37
+ /**
38
+ * Create an Offer in response to an Intent
39
+ *
40
+ * @param intent - Agent's Intent
41
+ * @returns Signed Offer
42
+ */
43
+ async createOfferForIntent(intent) {
44
+ // Validate intent
45
+ (0, core_1.validateIntent)(intent);
46
+ // Get dynamic price based on intent
47
+ const price = await this.pricing.getPrice(intent);
48
+ // Create offer
49
+ const offer = (0, core_1.createOffer)({
50
+ intent,
51
+ providerPubkey: this.wallet.publicKey,
52
+ pricePerRequest: price,
53
+ sla: this.sla,
54
+ sessionDurationMs: intent.sessionDurationMs || this.sessionDurationMs,
55
+ endpoint: this.endpoint,
56
+ expirationMs: this.offerExpirationMs,
57
+ }, this.wallet);
58
+ return offer;
59
+ }
60
+ /**
61
+ * Create a Session from an accepted Offer
62
+ *
63
+ * @param offer - Accepted Offer
64
+ * @param agentPubkey - Agent's public key
65
+ * @param depositAmount - Amount deposited
66
+ * @param txSignature - Solana transaction signature for payment
67
+ * @returns Active Session
68
+ */
69
+ async createSessionFromOffer(offer, agentPubkey, depositAmount, txSignature) {
70
+ // Verify payment on-chain
71
+ await this.verifyPayment(txSignature, agentPubkey, depositAmount);
72
+ // Create session
73
+ const session = (0, core_1.createSession)({
74
+ offer,
75
+ agentPubkey,
76
+ depositAmount,
77
+ creationTxSignature: txSignature,
78
+ });
79
+ // Store session
80
+ this.sessionStore.saveSession(session);
81
+ return session;
82
+ }
83
+ /**
84
+ * Get a session by ID
85
+ */
86
+ getSession(sessionId) {
87
+ return this.sessionStore.getSession(sessionId);
88
+ }
89
+ /**
90
+ * Create a Receipt for a completed request
91
+ *
92
+ * @param sessionId - Session ID
93
+ * @param inputData - Request input data
94
+ * @param outputData - Request output data
95
+ * @param requestStartedAt - Request start timestamp
96
+ * @returns Signed Receipt
97
+ */
98
+ async createReceiptForRequest(sessionId, inputData, outputData, requestStartedAt) {
99
+ const session = this.sessionStore.getSession(sessionId);
100
+ if (!session) {
101
+ throw new Error(`Session ${sessionId} not found`);
102
+ }
103
+ const requestCompletedAt = Date.now();
104
+ // Create receipt
105
+ const receipt = (0, core_1.createReceipt)({
106
+ session,
107
+ requestNumber: session.requestCount + 1,
108
+ inputData,
109
+ outputData,
110
+ requestStartedAt,
111
+ requestCompletedAt,
112
+ amountCharged: session.pricePerRequest,
113
+ }, this.wallet);
114
+ // Update session
115
+ this.sessionStore.updateSessionAfterRequest(sessionId, receipt);
116
+ return receipt;
117
+ }
118
+ /**
119
+ * Close a session and process refund
120
+ */
121
+ async closeSession(sessionId) {
122
+ const session = this.sessionStore.getSession(sessionId);
123
+ if (!session) {
124
+ throw new Error(`Session ${sessionId} not found`);
125
+ }
126
+ const refundAmount = session.remainingBalance;
127
+ if (refundAmount > 0) {
128
+ // TODO: Process refund on-chain
129
+ // const txSignature = await this.processRefund(session.agentPubkey, refundAmount);
130
+ // return { refundAmount, txSignature };
131
+ }
132
+ // Mark session as closed
133
+ this.sessionStore.closeSession(sessionId);
134
+ return { refundAmount };
135
+ }
136
+ /**
137
+ * Verify payment on Solana blockchain
138
+ */
139
+ async verifyPayment(txSignature, expectedPayer, expectedAmount) {
140
+ try {
141
+ const transaction = await this.connection.getTransaction(txSignature, {
142
+ commitment: 'confirmed',
143
+ });
144
+ if (!transaction) {
145
+ throw new Error('Transaction not found');
146
+ }
147
+ // TODO: Verify transaction details match expected values
148
+ // This would check:
149
+ // 1. Payer matches expectedPayer
150
+ // 2. Recipient is this provider
151
+ // 3. Amount matches expectedAmount
152
+ // 4. Transaction is successful
153
+ }
154
+ catch (error) {
155
+ throw new Error(`Payment verification failed: ${error}`);
156
+ }
157
+ }
158
+ /**
159
+ * Get provider's public key
160
+ */
161
+ get publicKey() {
162
+ return this.wallet.publicKey;
163
+ }
164
+ }
165
+ exports.GistProvider = GistProvider;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * SessionStore - In-memory session storage for providers
3
+ */
4
+ import { Connection, Keypair } from '@solana/web3.js';
5
+ import { Session, Receipt } from '@gistplus/core';
6
+ /**
7
+ * SessionStore manages active sessions for a provider
8
+ *
9
+ * In production, this would be backed by Redis, PostgreSQL, or similar.
10
+ * For MVP, we use an in-memory store.
11
+ */
12
+ export declare class SessionStore {
13
+ private sessions;
14
+ private connection;
15
+ private wallet;
16
+ constructor(connection: Connection, wallet: Keypair);
17
+ /**
18
+ * Save a new session
19
+ */
20
+ saveSession(session: Session): void;
21
+ /**
22
+ * Get a session by ID
23
+ */
24
+ getSession(sessionId: string): Session | undefined;
25
+ /**
26
+ * Update session after a request
27
+ */
28
+ updateSessionAfterRequest(sessionId: string, receipt: Receipt): void;
29
+ /**
30
+ * Close a session
31
+ */
32
+ closeSession(sessionId: string): void;
33
+ /**
34
+ * Get all active sessions
35
+ */
36
+ getActiveSessions(): Session[];
37
+ /**
38
+ * Clean up expired sessions
39
+ */
40
+ private cleanup;
41
+ /**
42
+ * Start background cleanup task
43
+ */
44
+ private startCleanupTask;
45
+ /**
46
+ * Get session count
47
+ */
48
+ getSessionCount(): number;
49
+ }
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ /**
3
+ * SessionStore - In-memory session storage for providers
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SessionStore = void 0;
7
+ const core_1 = require("@gistplus/core");
8
+ /**
9
+ * SessionStore manages active sessions for a provider
10
+ *
11
+ * In production, this would be backed by Redis, PostgreSQL, or similar.
12
+ * For MVP, we use an in-memory store.
13
+ */
14
+ class SessionStore {
15
+ constructor(connection, wallet) {
16
+ this.sessions = new Map();
17
+ this.connection = connection;
18
+ this.wallet = wallet;
19
+ // Start background cleanup
20
+ this.startCleanupTask();
21
+ }
22
+ /**
23
+ * Save a new session
24
+ */
25
+ saveSession(session) {
26
+ this.sessions.set(session.sessionId, session);
27
+ }
28
+ /**
29
+ * Get a session by ID
30
+ */
31
+ getSession(sessionId) {
32
+ const session = this.sessions.get(sessionId);
33
+ if (!session) {
34
+ return undefined;
35
+ }
36
+ // Check if expired
37
+ if (!(0, core_1.isSessionActive)(session) && session.state === core_1.SessionState.ACTIVE) {
38
+ const expiredSession = (0, core_1.expireSession)(session);
39
+ this.sessions.set(sessionId, expiredSession);
40
+ return expiredSession;
41
+ }
42
+ return session;
43
+ }
44
+ /**
45
+ * Update session after a request
46
+ */
47
+ updateSessionAfterRequest(sessionId, receipt) {
48
+ const session = this.sessions.get(sessionId);
49
+ if (!session) {
50
+ throw new Error(`Session ${sessionId} not found`);
51
+ }
52
+ const updatedSession = (0, core_1.deductFromSession)(session, receipt.amountCharged);
53
+ this.sessions.set(sessionId, updatedSession);
54
+ }
55
+ /**
56
+ * Close a session
57
+ */
58
+ closeSession(sessionId) {
59
+ this.sessions.delete(sessionId);
60
+ }
61
+ /**
62
+ * Get all active sessions
63
+ */
64
+ getActiveSessions() {
65
+ return Array.from(this.sessions.values()).filter(core_1.isSessionActive);
66
+ }
67
+ /**
68
+ * Clean up expired sessions
69
+ */
70
+ cleanup() {
71
+ const now = Date.now();
72
+ const graceperiod = 3600000; // 1 hour
73
+ for (const [sessionId, session] of this.sessions.entries()) {
74
+ if (now > session.expiresAt + graceperiod) {
75
+ this.sessions.delete(sessionId);
76
+ }
77
+ }
78
+ }
79
+ /**
80
+ * Start background cleanup task
81
+ */
82
+ startCleanupTask() {
83
+ setInterval(() => {
84
+ this.cleanup();
85
+ }, 300000); // Every 5 minutes
86
+ }
87
+ /**
88
+ * Get session count
89
+ */
90
+ getSessionCount() {
91
+ return this.sessions.size;
92
+ }
93
+ }
94
+ exports.SessionStore = SessionStore;
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@gistplus/server",
3
+ "version": "0.1.0",
4
+ "description": "Server middleware for Gist Plus - enables API providers to monetize endpoints with automatic negotiation and payment verification",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "test": "jest",
17
+ "clean": "rimraf dist"
18
+ },
19
+ "keywords": [
20
+ "gistplus",
21
+ "gist",
22
+ "server",
23
+ "middleware",
24
+ "express",
25
+ "api-monetization"
26
+ ],
27
+ "author": "Gist Plus",
28
+ "license": "Apache-2.0",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/gistplus/gistplus"
32
+ },
33
+ "homepage": "https://github.com/gistplus/gistplus#readme",
34
+ "bugs": {
35
+ "url": "https://github.com/gistplus/gistplus/issues"
36
+ },
37
+ "dependencies": {
38
+ "@gistplus/core": "^0.1.0",
39
+ "@solana/web3.js": "^1.87.6",
40
+ "@solana/spl-token": "^0.3.9",
41
+ "express": "^4.18.2"
42
+ },
43
+ "devDependencies": {
44
+ "typescript": "^5.3.3",
45
+ "@types/node": "^20.10.6",
46
+ "@types/express": "^4.17.21",
47
+ "jest": "^29.7.0",
48
+ "@types/jest": "^29.5.11",
49
+ "rimraf": "^5.0.5"
50
+ },
51
+ "peerDependencies": {
52
+ "express": "^4.18.0"
53
+ }
54
+ }