@shreyassp002/pinionos-emulator 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 +171 -0
- package/dist/examples/basic-agent.js +22 -0
- package/dist/examples/custom-skill.js +24 -0
- package/dist/examples/yield-optimizer.js +31 -0
- package/dist/src/app.js +116 -0
- package/dist/src/cli.js +136 -0
- package/dist/src/client/MockPinionClient.js +69 -0
- package/dist/src/config.js +149 -0
- package/dist/src/emulator.js +23 -0
- package/dist/src/freeApis/binance.js +36 -0
- package/dist/src/freeApis/coingecko.js +129 -0
- package/dist/src/mcp/server.js +122 -0
- package/dist/src/middleware/apiKeyStore.js +4 -0
- package/dist/src/middleware/chaos.js +33 -0
- package/dist/src/middleware/paymentLogger.js +82 -0
- package/dist/src/middleware/recorder.js +67 -0
- package/dist/src/middleware/x402.js +137 -0
- package/dist/src/routes/balance.js +28 -0
- package/dist/src/routes/broadcast.js +38 -0
- package/dist/src/routes/chat.js +106 -0
- package/dist/src/routes/facilitator.js +48 -0
- package/dist/src/routes/fund.js +52 -0
- package/dist/src/routes/price.js +79 -0
- package/dist/src/routes/send.js +101 -0
- package/dist/src/routes/trade.js +119 -0
- package/dist/src/routes/tx.js +48 -0
- package/dist/src/routes/unlimited.js +54 -0
- package/dist/src/routes/wallet.js +35 -0
- package/dist/src/routes/x402service.js +90 -0
- package/dist/src/state/balances.js +90 -0
- package/dist/src/types.js +30 -0
- package/dist/src/ui/colors.js +15 -0
- package/dist/src/ui/dashboard.js +340 -0
- package/dist/src/ui/panels/feed.js +31 -0
- package/dist/src/ui/panels/header.js +56 -0
- package/dist/src/ui/panels/inspector.js +19 -0
- package/dist/src/ui/panels/prices.js +23 -0
- package/dist/src/ui/panels/wallet.js +25 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# PinionOS Emulator
|
|
4
|
+
|
|
5
|
+
**Local PinionOS-compatible backend for product development and testing**
|
|
6
|
+
|
|
7
|
+
Test your app with the same `pinion-os` SDK calls, without real USDC spend.
|
|
8
|
+
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
## What This Project Is
|
|
12
|
+
|
|
13
|
+
`pinionos-emulator` is a local server that mirrors Pinion skill APIs so teams can build and test products safely.
|
|
14
|
+
|
|
15
|
+
Use it when you want to:
|
|
16
|
+
- develop app flows without hitting production
|
|
17
|
+
- run deterministic integration tests in CI
|
|
18
|
+
- exercise x402/unlimited-key paths locally
|
|
19
|
+
- inspect request/response behavior in a terminal dashboard
|
|
20
|
+
|
|
21
|
+
## Pinion SDK Compatibility
|
|
22
|
+
|
|
23
|
+
This emulator is designed for `pinion-os` client usage by pointing the SDK to local URL:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { PinionClient } from 'pinion-os';
|
|
27
|
+
|
|
28
|
+
const client = new PinionClient({
|
|
29
|
+
privateKey: process.env.PRIVATE_KEY!,
|
|
30
|
+
apiUrl: 'http://localhost:4020'
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Supported SDK Skills
|
|
35
|
+
|
|
36
|
+
| Skill | SDK Method | Emulator Endpoint | Status |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| balance | `skills.balance(address)` | `GET /balance/:address` | Supported |
|
|
39
|
+
| tx | `skills.tx(hash)` | `GET /tx/:hash` | Supported |
|
|
40
|
+
| price | `skills.price(token)` | `GET /price/:token` | Supported |
|
|
41
|
+
| wallet | `skills.wallet()` | `GET /wallet/generate` | Supported |
|
|
42
|
+
| chat | `skills.chat(message)` | `POST /chat` | Supported |
|
|
43
|
+
| send | `skills.send(to, amount, token)` | `POST /send` | Supported |
|
|
44
|
+
| trade | `skills.trade(src, dst, amount, slippage)` | `POST /trade` | Supported |
|
|
45
|
+
| fund | `skills.fund(address)` | `GET /fund/:address` | Supported |
|
|
46
|
+
| broadcast | `skills.broadcast(tx)` | `POST /broadcast` | Supported |
|
|
47
|
+
| unlimited | `skills.unlimited()` | `POST /unlimited` | Supported |
|
|
48
|
+
| unlimited-verify | `skills.unlimitedVerify(key)` | `GET /unlimited/verify?key=...` | Supported |
|
|
49
|
+
|
|
50
|
+
## Supported MCP Tools
|
|
51
|
+
|
|
52
|
+
Your emulator MCP server currently exposes:
|
|
53
|
+
- `pinion_price`
|
|
54
|
+
- `pinion_balance`
|
|
55
|
+
- `pinion_wallet`
|
|
56
|
+
- `pinion_tx`
|
|
57
|
+
- `pinion_chat`
|
|
58
|
+
- `pinion_send`
|
|
59
|
+
- `pinion_trade`
|
|
60
|
+
- `pinion_fund`
|
|
61
|
+
- `pinion_broadcast`
|
|
62
|
+
- `pinion_unlimited`
|
|
63
|
+
- `pinion_unlimited_verify`
|
|
64
|
+
- `pinion_pay_service`
|
|
65
|
+
- `pinion_facilitator_verify`
|
|
66
|
+
|
|
67
|
+
## Emulator Features Beyond Core Skills
|
|
68
|
+
|
|
69
|
+
- x402 middleware mode (`--x402`) with 402 challenge flow
|
|
70
|
+
- unlimited API key issuance and verification
|
|
71
|
+
- generic x402 test service (`/x402/*`)
|
|
72
|
+
- mock facilitator endpoints (`/facilitator/verify`, `/facilitator/status`)
|
|
73
|
+
- request recording (`/recording/start`, `/recording/stop`, `/recording/status`)
|
|
74
|
+
- chaos/error injection via config (`errorSimulation`)
|
|
75
|
+
- mutable in-memory balances + reset (`POST /reset`)
|
|
76
|
+
- terminal dashboard (feed, prices, wallet, inspector)
|
|
77
|
+
|
|
78
|
+
## Quick Start
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm install
|
|
82
|
+
npm start
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Health check:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
curl -s http://localhost:4020/health | jq
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Headless mode:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npx pinionos-emulator --no-dashboard
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## CLI Commands
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pinionos-emulator start
|
|
101
|
+
pinionos-emulator mcp
|
|
102
|
+
pinionos-emulator init
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Useful options:
|
|
106
|
+
- `--port <n>`
|
|
107
|
+
- `--x402`
|
|
108
|
+
- `--network base|base-sepolia`
|
|
109
|
+
- `--no-dashboard`
|
|
110
|
+
- `--config <path>`
|
|
111
|
+
|
|
112
|
+
## Product Testing Workflow
|
|
113
|
+
|
|
114
|
+
1. Start emulator locally.
|
|
115
|
+
2. Configure your app's Pinion client with `apiUrl: 'http://localhost:4020'`.
|
|
116
|
+
3. Run your app and test suite.
|
|
117
|
+
4. Assert your product behavior (not just raw route responses).
|
|
118
|
+
|
|
119
|
+
CI example:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npx pinionos-emulator --no-dashboard > /tmp/pinionos-emulator.log 2>&1 & EMU_PID=$!
|
|
123
|
+
sleep 2
|
|
124
|
+
npm test
|
|
125
|
+
kill $EMU_PID
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Route Summary
|
|
129
|
+
|
|
130
|
+
Core routes:
|
|
131
|
+
- `GET /price/:token`
|
|
132
|
+
- `GET /balance/:address`
|
|
133
|
+
- `GET /wallet`
|
|
134
|
+
- `GET /wallet/generate`
|
|
135
|
+
- `GET /tx/:hash`
|
|
136
|
+
- `POST /send`
|
|
137
|
+
- `POST /trade`
|
|
138
|
+
- `GET /fund/:address`
|
|
139
|
+
- `POST /chat`
|
|
140
|
+
- `POST /broadcast`
|
|
141
|
+
- `GET /unlimited`
|
|
142
|
+
- `POST /unlimited`
|
|
143
|
+
- `GET /unlimited/verify?key=...`
|
|
144
|
+
- `GET /unlimited/verify/:key`
|
|
145
|
+
|
|
146
|
+
System routes:
|
|
147
|
+
- `GET /`
|
|
148
|
+
- `GET /health`
|
|
149
|
+
- `POST /reset`
|
|
150
|
+
- `POST /recording/start`
|
|
151
|
+
- `POST /recording/stop`
|
|
152
|
+
- `GET /recording/status`
|
|
153
|
+
- `POST /facilitator/verify`
|
|
154
|
+
- `GET /facilitator/status`
|
|
155
|
+
- `ALL /x402/*`
|
|
156
|
+
|
|
157
|
+
## Notes On Behavior
|
|
158
|
+
|
|
159
|
+
- Responses are mock/simulated with realistic structure.
|
|
160
|
+
- Success envelope includes `mock: true` and payment metadata.
|
|
161
|
+
- Price path uses config override -> CoinGecko -> Binance -> fallback.
|
|
162
|
+
- This is for development/testing, not production settlement.
|
|
163
|
+
|
|
164
|
+
## Documentation
|
|
165
|
+
|
|
166
|
+
- Detailed usage and testing guide: [user_guide.md](user_guide.md)
|
|
167
|
+
- Planning/spec docs: [`docs/`](docs)
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
MIT
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
process.env.PINION_API_URL = 'http://localhost:4020';
|
|
4
|
+
const pinion_os_1 = require("pinion-os");
|
|
5
|
+
async function run() {
|
|
6
|
+
const pinion = new pinion_os_1.PinionClient({
|
|
7
|
+
privateKey: '0x0000000000000000000000000000000000000000000000000000000000000001'
|
|
8
|
+
});
|
|
9
|
+
console.log('=== STEP 1: PRICE ===');
|
|
10
|
+
const price = await pinion.skills.price('ETH');
|
|
11
|
+
console.log(price);
|
|
12
|
+
console.log('\n=== STEP 2: BALANCE ===');
|
|
13
|
+
const balance = await pinion.skills.balance('0x123');
|
|
14
|
+
console.log(balance);
|
|
15
|
+
console.log('\n=== STEP 3: WALLET ===');
|
|
16
|
+
const wallet = await pinion.skills.wallet();
|
|
17
|
+
console.log(wallet);
|
|
18
|
+
}
|
|
19
|
+
run().catch((error) => {
|
|
20
|
+
console.error(error);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const server_1 = require("pinion-os/server");
|
|
4
|
+
const server = (0, server_1.createSkillServer)({
|
|
5
|
+
payTo: '0x000000000000000000000000000000000000dEaD',
|
|
6
|
+
network: 'base'
|
|
7
|
+
});
|
|
8
|
+
server.add({
|
|
9
|
+
name: 'yieldRecs',
|
|
10
|
+
price: 0.01,
|
|
11
|
+
endpoint: '/yield-recs',
|
|
12
|
+
handler: async (req, res) => {
|
|
13
|
+
res.json({
|
|
14
|
+
data: {
|
|
15
|
+
recommendation: 'Rotate 20% into stablecoins during high volatility.',
|
|
16
|
+
confidence: 'medium'
|
|
17
|
+
},
|
|
18
|
+
mock: true
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
server.listen(4500, () => {
|
|
23
|
+
console.log('Custom skill server listening on :4500');
|
|
24
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
process.env.PINION_API_URL = 'http://localhost:4020';
|
|
4
|
+
const pinion_os_1 = require("pinion-os");
|
|
5
|
+
const client = new pinion_os_1.PinionClient({
|
|
6
|
+
privateKey: '0x0000000000000000000000000000000000000000000000000000000000000001'
|
|
7
|
+
});
|
|
8
|
+
let iteration = 0;
|
|
9
|
+
async function runOnce() {
|
|
10
|
+
iteration += 1;
|
|
11
|
+
console.log('\n=== Yield Check', iteration, '===');
|
|
12
|
+
const priceResult = await client.skills.price('ETH');
|
|
13
|
+
const ethPrice = Number(priceResult.data?.usd ?? 0);
|
|
14
|
+
console.log('ETH Price:', ethPrice);
|
|
15
|
+
const decision = ethPrice < 3000 ? 'BUY ETH->USDC rebalance' : 'HOLD';
|
|
16
|
+
console.log('Decision:', decision);
|
|
17
|
+
if (decision.startsWith('BUY')) {
|
|
18
|
+
const tradeResult = await client.skills.trade('ETH', 'USDC', '0.01');
|
|
19
|
+
console.log('Trade Executed:', tradeResult.data?.toAmount ?? 'n/a', 'USDC');
|
|
20
|
+
console.log('Simulated Yield: +$0.02');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
runOnce().catch((error) => {
|
|
24
|
+
console.error(error);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
});
|
|
27
|
+
setInterval(() => {
|
|
28
|
+
runOnce().catch((error) => {
|
|
29
|
+
console.error('Loop error:', error);
|
|
30
|
+
});
|
|
31
|
+
}, 10000);
|
package/dist/src/app.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createApp = createApp;
|
|
7
|
+
const cors_1 = __importDefault(require("cors"));
|
|
8
|
+
const express_1 = __importDefault(require("express"));
|
|
9
|
+
const config_1 = require("./config");
|
|
10
|
+
const chaos_1 = require("./middleware/chaos");
|
|
11
|
+
const paymentLogger_1 = require("./middleware/paymentLogger");
|
|
12
|
+
const recorder_1 = require("./middleware/recorder");
|
|
13
|
+
const x402_1 = require("./middleware/x402");
|
|
14
|
+
const price_1 = require("./routes/price");
|
|
15
|
+
const wallet_1 = require("./routes/wallet");
|
|
16
|
+
const balance_1 = __importDefault(require("./routes/balance"));
|
|
17
|
+
const broadcast_1 = __importDefault(require("./routes/broadcast"));
|
|
18
|
+
const chat_1 = __importDefault(require("./routes/chat"));
|
|
19
|
+
const fund_1 = require("./routes/fund");
|
|
20
|
+
const send_1 = require("./routes/send");
|
|
21
|
+
const trade_1 = require("./routes/trade");
|
|
22
|
+
const facilitator_1 = require("./routes/facilitator");
|
|
23
|
+
const tx_1 = __importDefault(require("./routes/tx"));
|
|
24
|
+
const unlimited_1 = __importDefault(require("./routes/unlimited"));
|
|
25
|
+
const x402service_1 = require("./routes/x402service");
|
|
26
|
+
const balances_1 = require("./state/balances");
|
|
27
|
+
const apiKeyStore_1 = require("./middleware/apiKeyStore");
|
|
28
|
+
const types_1 = require("./types");
|
|
29
|
+
function createApp(opts = {}) {
|
|
30
|
+
const dashboard = opts.dashboard;
|
|
31
|
+
const noop = {
|
|
32
|
+
logSkillCall() { },
|
|
33
|
+
updatePrices() { },
|
|
34
|
+
logError() { },
|
|
35
|
+
setWalletInfo() { },
|
|
36
|
+
destroy() { },
|
|
37
|
+
};
|
|
38
|
+
const db = dashboard ?? noop;
|
|
39
|
+
const app = (0, express_1.default)();
|
|
40
|
+
const config = (0, config_1.loadConfig)();
|
|
41
|
+
(0, balances_1.initBalances)();
|
|
42
|
+
app.use((0, cors_1.default)());
|
|
43
|
+
app.use(express_1.default.json());
|
|
44
|
+
// Request recording
|
|
45
|
+
app.use((0, recorder_1.recorderMiddleware)());
|
|
46
|
+
if (config.recording) {
|
|
47
|
+
(0, recorder_1.startRecording)();
|
|
48
|
+
}
|
|
49
|
+
// Chaos error injection
|
|
50
|
+
app.use((0, chaos_1.chaosMiddleware)(db));
|
|
51
|
+
// Log X-API-KEY usage
|
|
52
|
+
app.use((req, _res, next) => {
|
|
53
|
+
const apiKey = req.headers['x-api-key'];
|
|
54
|
+
if (typeof apiKey === 'string' && apiKey.length > 0) {
|
|
55
|
+
const entry = apiKeyStore_1.issuedKeys.get(apiKey);
|
|
56
|
+
const label = entry ? `✓ key valid (${apiKey.slice(0, 12)}...)` : `✗ key unknown (${apiKey.slice(0, 12)}...)`;
|
|
57
|
+
db.logSkillCall('x-api-key', '', label);
|
|
58
|
+
}
|
|
59
|
+
next();
|
|
60
|
+
});
|
|
61
|
+
app.use((0, paymentLogger_1.paymentLogger)(db));
|
|
62
|
+
app.use((0, x402_1.x402Middleware)(db));
|
|
63
|
+
app.get('/', (_req, res) => {
|
|
64
|
+
res.json({ status: 'ok', emulator: true });
|
|
65
|
+
});
|
|
66
|
+
app.get('/health', (_req, res) => {
|
|
67
|
+
res.json({ status: 'ok', emulator: true, port: config.port });
|
|
68
|
+
});
|
|
69
|
+
app.use('/price', (0, price_1.createPriceRouter)(db));
|
|
70
|
+
app.use('/wallet', (0, wallet_1.createWalletRouter)(db));
|
|
71
|
+
app.use('/balance', balance_1.default);
|
|
72
|
+
app.use('/tx', tx_1.default);
|
|
73
|
+
app.use('/send', (0, send_1.createSendRouter)(db));
|
|
74
|
+
app.use('/trade', (0, trade_1.createTradeRouter)(db));
|
|
75
|
+
app.use('/fund', (0, fund_1.createFundRouter)(db));
|
|
76
|
+
app.use('/chat', chat_1.default);
|
|
77
|
+
app.use('/unlimited', unlimited_1.default);
|
|
78
|
+
app.use('/broadcast', broadcast_1.default);
|
|
79
|
+
app.use('/facilitator', (0, facilitator_1.createFacilitatorRouter)(db));
|
|
80
|
+
app.use('/x402', (0, x402service_1.createX402ServiceRouter)(db));
|
|
81
|
+
app.post('/reset', (_req, res) => {
|
|
82
|
+
(0, balances_1.resetBalances)();
|
|
83
|
+
apiKeyStore_1.issuedKeys.clear();
|
|
84
|
+
db.logSkillCall('SYSTEM', '', 'State reset to config defaults');
|
|
85
|
+
res.json((0, types_1.success)({ message: 'All balances and API keys reset to defaults' }));
|
|
86
|
+
});
|
|
87
|
+
app.post('/recording/start', (_req, res) => {
|
|
88
|
+
(0, recorder_1.startRecording)();
|
|
89
|
+
db.logSkillCall('SYSTEM', '', 'Request recording started');
|
|
90
|
+
res.json((0, types_1.success)({ recording: true }));
|
|
91
|
+
});
|
|
92
|
+
app.post('/recording/stop', (_req, res) => {
|
|
93
|
+
(0, recorder_1.stopRecording)();
|
|
94
|
+
db.logSkillCall('SYSTEM', '', 'Request recording stopped');
|
|
95
|
+
res.json((0, types_1.success)({ recording: false }));
|
|
96
|
+
});
|
|
97
|
+
app.get('/recording/status', (_req, res) => {
|
|
98
|
+
res.json((0, types_1.success)({ recording: (0, recorder_1.isRecording)() }));
|
|
99
|
+
});
|
|
100
|
+
app.use((err, _req, res, next) => {
|
|
101
|
+
if (res.headersSent) {
|
|
102
|
+
next(err);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (err instanceof SyntaxError) {
|
|
106
|
+
res.status(400).json((0, types_1.errorResponse)('invalid json body'));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
db.logError(`internal emulator error: ${String(err?.message ?? err)}`);
|
|
110
|
+
res.status(500).json((0, types_1.errorResponse)('internal emulator error'));
|
|
111
|
+
});
|
|
112
|
+
app.use((_req, res) => {
|
|
113
|
+
res.status(404).json((0, types_1.errorResponse)('route not found'));
|
|
114
|
+
});
|
|
115
|
+
return { app, config };
|
|
116
|
+
}
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const node_util_1 = require("node:util");
|
|
41
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
42
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
43
|
+
const HELP = `
|
|
44
|
+
pinionos-emulator — Local PinionOS emulator for agent development
|
|
45
|
+
|
|
46
|
+
Usage:
|
|
47
|
+
pinionos-emulator [command] [options]
|
|
48
|
+
|
|
49
|
+
Commands:
|
|
50
|
+
start Start the emulator (default)
|
|
51
|
+
mcp Start MCP stdio server (emulator must be running)
|
|
52
|
+
init Generate a starter config.json in the current directory
|
|
53
|
+
|
|
54
|
+
Options:
|
|
55
|
+
--port <n> Port to listen on (default: 4020)
|
|
56
|
+
--x402 Enable x402 payment simulation mode
|
|
57
|
+
--network <name> Network: "base" (default) or "base-sepolia"
|
|
58
|
+
--no-dashboard Run without the terminal dashboard UI
|
|
59
|
+
--config <path> Path to config.json (default: ./config.json)
|
|
60
|
+
--help, -h Show this help message
|
|
61
|
+
--version, -v Show version
|
|
62
|
+
|
|
63
|
+
Examples:
|
|
64
|
+
pinionos-emulator # Start with defaults
|
|
65
|
+
pinionos-emulator --port 3000 --x402 # Custom port + x402 mode
|
|
66
|
+
pinionos-emulator init # Create config.json
|
|
67
|
+
npx pinionos-emulator # Zero-install usage
|
|
68
|
+
`.trim();
|
|
69
|
+
async function main() {
|
|
70
|
+
const { values, positionals } = (0, node_util_1.parseArgs)({
|
|
71
|
+
args: process.argv.slice(2),
|
|
72
|
+
options: {
|
|
73
|
+
port: { type: 'string', short: 'p' },
|
|
74
|
+
x402: { type: 'boolean', default: false },
|
|
75
|
+
network: { type: 'string', short: 'n' },
|
|
76
|
+
'no-dashboard': { type: 'boolean', default: false },
|
|
77
|
+
config: { type: 'string', short: 'c' },
|
|
78
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
79
|
+
version: { type: 'boolean', short: 'v', default: false },
|
|
80
|
+
},
|
|
81
|
+
allowPositionals: true,
|
|
82
|
+
strict: true,
|
|
83
|
+
});
|
|
84
|
+
if (values.help) {
|
|
85
|
+
console.log(HELP);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (values.version) {
|
|
89
|
+
const pkg = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
90
|
+
console.log(pkg.version);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Apply config path override before command handling
|
|
94
|
+
if (values.config) {
|
|
95
|
+
const { setConfigPath } = await Promise.resolve().then(() => __importStar(require('./config')));
|
|
96
|
+
setConfigPath(values.config);
|
|
97
|
+
process.env.PINION_CONFIG_PATH = node_path_1.default.resolve(values.config);
|
|
98
|
+
}
|
|
99
|
+
const command = positionals[0] ?? 'start';
|
|
100
|
+
if (command === 'init') {
|
|
101
|
+
const { getDefaultConfig } = await Promise.resolve().then(() => __importStar(require('./config')));
|
|
102
|
+
const dest = node_path_1.default.resolve(process.cwd(), 'config.json');
|
|
103
|
+
if (node_fs_1.default.existsSync(dest)) {
|
|
104
|
+
console.error(`config.json already exists at ${dest}`);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
node_fs_1.default.writeFileSync(dest, JSON.stringify(getDefaultConfig(), null, 2) + '\n');
|
|
108
|
+
console.log(`Created config.json at ${dest}`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (command === 'mcp') {
|
|
112
|
+
await Promise.resolve().then(() => __importStar(require('./mcp/server')));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Apply CLI overrides
|
|
116
|
+
const overrides = {};
|
|
117
|
+
if (values.port)
|
|
118
|
+
overrides.port = parseInt(values.port, 10);
|
|
119
|
+
if (values.x402)
|
|
120
|
+
overrides.x402Mode = true;
|
|
121
|
+
if (values.network)
|
|
122
|
+
overrides.network = values.network;
|
|
123
|
+
if (Object.keys(overrides).length > 0) {
|
|
124
|
+
const { setCliOverrides } = await Promise.resolve().then(() => __importStar(require('./config')));
|
|
125
|
+
setCliOverrides(overrides);
|
|
126
|
+
}
|
|
127
|
+
// Set env flag for no-dashboard mode
|
|
128
|
+
if (values['no-dashboard']) {
|
|
129
|
+
process.env.PINION_NO_DASHBOARD = '1';
|
|
130
|
+
}
|
|
131
|
+
await Promise.resolve().then(() => __importStar(require('./emulator')));
|
|
132
|
+
}
|
|
133
|
+
main().catch((error) => {
|
|
134
|
+
console.error(error);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MockPinionClient = void 0;
|
|
7
|
+
exports.wrapAsSkillResponse = wrapAsSkillResponse;
|
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
function wrapAsSkillResponse(data) {
|
|
10
|
+
return {
|
|
11
|
+
status: 200,
|
|
12
|
+
data,
|
|
13
|
+
paidAmount: '0.01',
|
|
14
|
+
responseTimeMs: 0
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Drop-in replacement for PinionClient when the SDK cannot be redirected via apiUrl.
|
|
19
|
+
* All skill methods return the same SkillResponse<T> shape as the real SDK.
|
|
20
|
+
* Usage:
|
|
21
|
+
* const client = new MockPinionClient({ baseUrl: 'http://localhost:4020' });
|
|
22
|
+
* const result = await client.skills.price('ETH');
|
|
23
|
+
* console.log(result.data.priceUSD);
|
|
24
|
+
*/
|
|
25
|
+
class MockPinionClient {
|
|
26
|
+
constructor(options = {}) {
|
|
27
|
+
this.skills = {
|
|
28
|
+
price: (token) => this.get(`/price/${token}`),
|
|
29
|
+
balance: (address) => this.get(`/balance/${address}`),
|
|
30
|
+
wallet: () => this.get('/wallet/generate'),
|
|
31
|
+
tx: (hash) => this.get(`/tx/${hash}`),
|
|
32
|
+
fund: (address) => this.get(`/fund/${address ?? this.address}`),
|
|
33
|
+
chat: (message, history) => {
|
|
34
|
+
const messages = history
|
|
35
|
+
? [...history, { role: 'user', content: message }]
|
|
36
|
+
: [{ role: 'user', content: message }];
|
|
37
|
+
return this.post('/chat', { messages });
|
|
38
|
+
},
|
|
39
|
+
send: (to, amount, token) => this.post('/send', { to, amount, token }),
|
|
40
|
+
trade: (src, dst, amount, slippage) => this.post('/trade', { src, dst, amount, slippage: slippage ?? 1 }),
|
|
41
|
+
broadcast: (tx, privateKey) => this.post('/broadcast', { tx, privateKey }),
|
|
42
|
+
unlimited: () => this.post('/unlimited', {}),
|
|
43
|
+
unlimitedVerify: async (key) => {
|
|
44
|
+
const res = await this.http.get('/unlimited/verify', { params: { key } });
|
|
45
|
+
return res.data;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
this.http = axios_1.default.create({
|
|
49
|
+
baseURL: options.baseUrl ?? 'http://localhost:4020',
|
|
50
|
+
timeout: 10000
|
|
51
|
+
});
|
|
52
|
+
this.address = options.address ?? '';
|
|
53
|
+
}
|
|
54
|
+
async get(path) {
|
|
55
|
+
const start = Date.now();
|
|
56
|
+
const res = await this.http.get(path);
|
|
57
|
+
const body = res.data;
|
|
58
|
+
const data = body.data ?? body;
|
|
59
|
+
return { status: res.status, data, paidAmount: '0.01', responseTimeMs: Date.now() - start };
|
|
60
|
+
}
|
|
61
|
+
async post(path, payload) {
|
|
62
|
+
const start = Date.now();
|
|
63
|
+
const res = await this.http.post(path, payload);
|
|
64
|
+
const body = res.data;
|
|
65
|
+
const data = body.data ?? body;
|
|
66
|
+
return { status: res.status, data, paidAmount: '0.01', responseTimeMs: Date.now() - start };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.MockPinionClient = MockPinionClient;
|