Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
"lint:fix": "eslint . --ext .js,.mjs,.cjs --fix",
"format": "prettier --write \"**/*.{js,md,html,yaml,yml}\"",
"format:check": "prettier --check \"**/*.{js,md,html,yaml,yml}\"",
"test": "node src/agents/settlement-header.test.js",
"test": "node src/agents/settlement-header.test.js && node tests/premium-endpoints.integration.test.js",
"test:parser": "node src/agents/settlement-header.test.js",
"test:premium": "node tests/premium-endpoints.integration.test.js",
"test:demo": "node src/demo.js"
},
"keywords": [
Expand Down
67 changes: 67 additions & 0 deletions src/routes/premium-routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
export function registerPremiumRoutes(app, deps) {
const {
pricingConfig,
broadcast,
runResearch,
runSummary,
runAnalysis,
runCode,
MODEL_LABELS,
} = deps;

app.get('/api/premium/research', async (req, res, next) => {
try {
const topic = req.query.topic || 'AI and blockchain payments';
const priceInfo = pricingConfig.getEndpointInfo('GET /api/premium/research');
const cost = priceInfo.price.slice(1);
broadcast({ type: 'agent_call', agent: `${priceInfo.emoji} Research Agent`, agentId: 'research-bot', input: topic, cost, timestamp: new Date().toISOString() });
const result = await runResearch(topic);
broadcast({ type: 'agent_response', agent: `${priceInfo.emoji} Research Agent`, agentId: 'research-bot', resultPreview: result.substring(0, 150), cost, timestamp: new Date().toISOString() });
res.json({ agent: 'research-bot', topic, result, model: MODEL_LABELS.research, cost: `${cost} USDC`, paidVia: 'x402' });
} catch (err) {
next(err);
}
});

app.get('/api/premium/summarize', async (req, res, next) => {
try {
const text = req.query.text || 'Please provide text to summarize via ?text= parameter';
const priceInfo = pricingConfig.getEndpointInfo('GET /api/premium/summarize');
const cost = priceInfo.price.slice(1);
broadcast({ type: 'agent_call', agent: `${priceInfo.emoji} Summary Agent`, agentId: 'summary-bot', input: text.substring(0, 100), cost, timestamp: new Date().toISOString() });
const result = await runSummary(text);
broadcast({ type: 'agent_response', agent: `${priceInfo.emoji} Summary Agent`, agentId: 'summary-bot', resultPreview: result.substring(0, 150), cost, timestamp: new Date().toISOString() });
res.json({ agent: 'summary-bot', result, model: MODEL_LABELS.summary, cost: `${cost} USDC`, paidVia: 'x402' });
} catch (err) {
next(err);
}
});

app.get('/api/premium/analyze', async (req, res, next) => {
try {
const topic = req.query.topic || 'AI agent economies';
const priceInfo = pricingConfig.getEndpointInfo('GET /api/premium/analyze');
const cost = priceInfo.price.slice(1);
broadcast({ type: 'agent_call', agent: `${priceInfo.emoji} Analysis Agent`, agentId: 'analyst-bot', input: topic, cost, timestamp: new Date().toISOString() });
const result = await runAnalysis(topic);
broadcast({ type: 'agent_response', agent: `${priceInfo.emoji} Analysis Agent`, agentId: 'analyst-bot', resultPreview: result.substring(0, 150), cost, timestamp: new Date().toISOString() });
res.json({ agent: 'analyst-bot', topic, result, model: MODEL_LABELS.analysis, cost: `${cost} USDC`, paidVia: 'x402' });
} catch (err) {
next(err);
}
});

app.get('/api/premium/code', async (req, res, next) => {
try {
const prompt = req.query.prompt || 'Write a hello world function';
const priceInfo = pricingConfig.getEndpointInfo('GET /api/premium/code');
const cost = priceInfo.price.slice(1);
broadcast({ type: 'agent_call', agent: `${priceInfo.emoji} Code Agent`, agentId: 'code-bot', input: prompt.substring(0, 100), cost, timestamp: new Date().toISOString() });
const result = await runCode(prompt);
broadcast({ type: 'agent_response', agent: `${priceInfo.emoji} Code Agent`, agentId: 'code-bot', resultPreview: result.substring(0, 150), cost, timestamp: new Date().toISOString() });
res.json({ agent: 'code-bot', prompt, result, model: MODEL_LABELS.code, cost: `${cost} USDC`, paidVia: 'x402' });
} catch (err) {
next(err);
}
});
}
146 changes: 9 additions & 137 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { requestId, requestLogger, errorHandler } from './middleware/errorHandle
import { orchestrateLimiter, apikeyLimiter } from './middleware/rateLimiter.js'
import { logger } from './logger.js'
import { adminAuth } from './middleware/auth.js'
import { registerPremiumRoutes } from './routes/premium-routes.js'

// x402 imports
import { paymentMiddlewareFromConfig } from '@x402/express'
Expand Down Expand Up @@ -159,143 +160,14 @@ if (config.serverAddress) {
}

// ─── Premium x402-Protected Endpoints ────────────────────────
app.get('/api/premium/research', async (req, res, next) => {
try {
const topic = req.query.topic || 'AI and blockchain payments'
const priceInfo = pricingConfig.getEndpointInfo('GET /api/premium/research')
const cost = priceInfo.price.slice(1) // Remove '$' for display
broadcast({
type: 'agent_call',
agent: `${priceInfo.emoji} Research Agent`,
agentId: 'research-bot',
input: topic,
cost,
timestamp: new Date().toISOString(),
})
const result = await runResearch(topic)
broadcast({
type: 'agent_response',
agent: `${priceInfo.emoji} Research Agent`,
agentId: 'research-bot',
resultPreview: result.substring(0, 150),
cost,
timestamp: new Date().toISOString(),
})
res.json({
agent: 'research-bot',
topic,
result,
model: MODEL_LABELS.research,
cost: `${cost} USDC`,
paidVia: 'x402',
})
} catch (err) {
next(err)
}
})

app.get('/api/premium/summarize', async (req, res, next) => {
try {
const text = req.query.text || 'Please provide text to summarize via ?text= parameter'
const priceInfo = pricingConfig.getEndpointInfo('GET /api/premium/summarize')
const cost = priceInfo.price.slice(1)
broadcast({
type: 'agent_call',
agent: `${priceInfo.emoji} Summary Agent`,
agentId: 'summary-bot',
input: text.substring(0, 100),
cost,
timestamp: new Date().toISOString(),
})
const result = await runSummary(text)
broadcast({
type: 'agent_response',
agent: `${priceInfo.emoji} Summary Agent`,
agentId: 'summary-bot',
resultPreview: result.substring(0, 150),
cost,
timestamp: new Date().toISOString(),
})
res.json({
agent: 'summary-bot',
result,
model: MODEL_LABELS.summary,
cost: `${cost} USDC`,
paidVia: 'x402',
})
} catch (err) {
next(err)
}
})

app.get('/api/premium/analyze', async (req, res, next) => {
try {
const topic = req.query.topic || 'AI agent economies'
const priceInfo = pricingConfig.getEndpointInfo('GET /api/premium/analyze')
const cost = priceInfo.price.slice(1)
broadcast({
type: 'agent_call',
agent: `${priceInfo.emoji} Analysis Agent`,
agentId: 'analyst-bot',
input: topic,
cost,
timestamp: new Date().toISOString(),
})
const result = await runAnalysis(topic)
broadcast({
type: 'agent_response',
agent: `${priceInfo.emoji} Analysis Agent`,
agentId: 'analyst-bot',
resultPreview: result.substring(0, 150),
cost,
timestamp: new Date().toISOString(),
})
res.json({
agent: 'analyst-bot',
topic,
result,
model: MODEL_LABELS.analysis,
cost: `${cost} USDC`,
paidVia: 'x402',
})
} catch (err) {
next(err)
}
})

app.get('/api/premium/code', async (req, res, next) => {
try {
const prompt = req.query.prompt || 'Write a hello world function'
const priceInfo = pricingConfig.getEndpointInfo('GET /api/premium/code')
const cost = priceInfo.price.slice(1)
broadcast({
type: 'agent_call',
agent: `${priceInfo.emoji} Code Agent`,
agentId: 'code-bot',
input: prompt.substring(0, 100),
cost,
timestamp: new Date().toISOString(),
})
const result = await runCode(prompt)
broadcast({
type: 'agent_response',
agent: `${priceInfo.emoji} Code Agent`,
agentId: 'code-bot',
resultPreview: result.substring(0, 150),
cost,
timestamp: new Date().toISOString(),
})
res.json({
agent: 'code-bot',
prompt,
result,
model: MODEL_LABELS.code,
cost: `${cost} USDC`,
paidVia: 'x402',
})
} catch (err) {
next(err)
}
registerPremiumRoutes(app, {
pricingConfig,
broadcast,
runResearch,
runSummary,
runAnalysis,
runCode,
MODEL_LABELS,
})

// ─── Free Agent Endpoints (for internal orchestrator use) ────
Expand Down
140 changes: 140 additions & 0 deletions tests/premium-endpoints.integration.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import assert from 'node:assert';
import express from 'express';
import { pricingConfig } from '../src/pricing.config.js';
import { registerPremiumRoutes } from '../src/routes/premium-routes.js';

function createTestApp({ withPaymentHeaderRequired = true } = {}) {
const app = express();

// Mock payment middleware: block premium endpoints unless payment header exists.
app.use((req, res, next) => {
if (!req.path.startsWith('/api/premium/')) return next();
if (!withPaymentHeaderRequired) return next();

if (!req.header('x-payment-context')) {
return res.status(402).json({
error: 'payment_required',
message: 'Missing payment context',
});
}
return next();
});

const calls = {
research: 0,
summary: 0,
analysis: 0,
code: 0,
};

registerPremiumRoutes(app, {
pricingConfig,
broadcast: () => {},
runResearch: async (topic) => {
calls.research += 1;
return `research:${topic}`;
},
runSummary: async (text) => {
calls.summary += 1;
return `summary:${text}`;
},
runAnalysis: async (topic) => {
calls.analysis += 1;
return `analysis:${topic}`;
},
runCode: async (prompt) => {
calls.code += 1;
return `code:${prompt}`;
},
MODEL_LABELS: {
research: 'test-research-model',
summary: 'test-summary-model',
analysis: 'test-analysis-model',
code: 'test-code-model',
},
});

return { app, calls };
}

async function requestJson(baseUrl, endpoint, payment = false) {
const headers = payment ? { 'x-payment-context': 'paid' } : {};
const res = await fetch(`${baseUrl}${endpoint}`, { headers });
const body = await res.json();
return { status: res.status, body };
}

async function withServer(app, fn) {
const server = await new Promise((resolve) => {
const s = app.listen(0, () => resolve(s));
});
const { port } = server.address();
const baseUrl = `http://127.0.0.1:${port}`;
try {
await fn(baseUrl);
} finally {
await new Promise((resolve, reject) => server.close((err) => (err ? reject(err) : resolve())));
}
}

async function run() {
const endpoints = [
'/api/premium/research?topic=test-topic',
'/api/premium/summarize?text=test-text',
'/api/premium/analyze?topic=test-topic',
'/api/premium/code?prompt=test-prompt',
];

// Without payment context => blocked by middleware
{
const { app, calls } = createTestApp();
await withServer(app, async (baseUrl) => {
for (const endpoint of endpoints) {
const { status, body } = await requestJson(baseUrl, endpoint, false);
assert.strictEqual(status, 402, `Expected 402 for ${endpoint} without payment context`);
assert.strictEqual(body.error, 'payment_required', `Expected payment_required payload for ${endpoint}`);
}
});
assert.deepStrictEqual(calls, { research: 0, summary: 0, analysis: 0, code: 0 }, 'Agent handlers must not execute on 402');
}

// With payment context => premium handlers succeed
{
const { app, calls } = createTestApp();
await withServer(app, async (baseUrl) => {
const research = await requestJson(baseUrl, '/api/premium/research?topic=my-topic', true);
assert.strictEqual(research.status, 200);
assert.strictEqual(research.body.agent, 'research-bot');
assert.strictEqual(research.body.paidVia, 'x402');
assert.strictEqual(research.body.cost, '0.01 USDC');

const summarize = await requestJson(baseUrl, '/api/premium/summarize?text=my-text', true);
assert.strictEqual(summarize.status, 200);
assert.strictEqual(summarize.body.agent, 'summary-bot');
assert.strictEqual(summarize.body.paidVia, 'x402');
assert.strictEqual(summarize.body.cost, '0.01 USDC');

const analyze = await requestJson(baseUrl, '/api/premium/analyze?topic=my-topic', true);
assert.strictEqual(analyze.status, 200);
assert.strictEqual(analyze.body.agent, 'analyst-bot');
assert.strictEqual(analyze.body.paidVia, 'x402');
assert.strictEqual(analyze.body.cost, '0.05 USDC');

const code = await requestJson(baseUrl, '/api/premium/code?prompt=my-prompt', true);
assert.strictEqual(code.status, 200);
assert.strictEqual(code.body.agent, 'code-bot');
assert.strictEqual(code.body.paidVia, 'x402');
assert.strictEqual(code.body.cost, '0.03 USDC');
});

assert.deepStrictEqual(calls, { research: 1, summary: 1, analysis: 1, code: 1 }, 'Each premium handler should run exactly once with payment');
}

console.log('✅ premium endpoint integration tests passed');
}

run().catch((err) => {
console.error('❌ premium endpoint integration tests failed');
console.error(err);
process.exit(1);
});
Loading