@agentxv2/gateway 0.1.1

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.
Files changed (175) hide show
  1. package/.env.example +53 -0
  2. package/db/migrations/001_init.sql +117 -0
  3. package/db/migrations/002_agents.sql +20 -0
  4. package/db/migrations/003_a2a_results.sql +23 -0
  5. package/deploy/.env.deploy.example +23 -0
  6. package/deploy/add_swap_build.py +45 -0
  7. package/deploy/build_and_deploy.py +110 -0
  8. package/deploy/build_deploy_a2a.py +190 -0
  9. package/deploy/build_no_lint.py +52 -0
  10. package/deploy/build_on_fe.py +86 -0
  11. package/deploy/build_on_gw.py +98 -0
  12. package/deploy/build_prod.py +54 -0
  13. package/deploy/check_bal.py +31 -0
  14. package/deploy/check_code.py +47 -0
  15. package/deploy/check_deploy.sh +20 -0
  16. package/deploy/check_docs.py +16 -0
  17. package/deploy/check_frontend.py +42 -0
  18. package/deploy/check_frontend2.py +35 -0
  19. package/deploy/check_pks.py +34 -0
  20. package/deploy/check_status.py +32 -0
  21. package/deploy/check_v2.py +8 -0
  22. package/deploy/clean_install.py +28 -0
  23. package/deploy/deploy_a2a.py +58 -0
  24. package/deploy/deploy_a2a.sh +33 -0
  25. package/deploy/deploy_a2a_bg.sh +40 -0
  26. package/deploy/deploy_a2a_v2.py +151 -0
  27. package/deploy/deploy_a2a_v2.sh +36 -0
  28. package/deploy/deploy_bin.py +64 -0
  29. package/deploy/deploy_config.py +34 -0
  30. package/deploy/deploy_final.py +42 -0
  31. package/deploy/deploy_frontend.py +108 -0
  32. package/deploy/deploy_mcp.py +108 -0
  33. package/deploy/deploy_now.py +49 -0
  34. package/deploy/deploy_push_all.py +128 -0
  35. package/deploy/deploy_script.py +64 -0
  36. package/deploy/deploy_sep_a2a.sh +18 -0
  37. package/deploy/dev_mode.py +51 -0
  38. package/deploy/diag.py +12 -0
  39. package/deploy/diff_test.py +22 -0
  40. package/deploy/final_check.py +33 -0
  41. package/deploy/final_deploy.py +50 -0
  42. package/deploy/final_fix.py +54 -0
  43. package/deploy/final_push.py +114 -0
  44. package/deploy/find_frontend.py +38 -0
  45. package/deploy/fix_a2a.py +43 -0
  46. package/deploy/fix_and_build.py +77 -0
  47. package/deploy/fix_and_verify.py +56 -0
  48. package/deploy/fix_build.py +51 -0
  49. package/deploy/forge_deploy.py +67 -0
  50. package/deploy/full_push.py +101 -0
  51. package/deploy/full_push2.py +106 -0
  52. package/deploy/go_prod.py +59 -0
  53. package/deploy/gw_deploy.py +133 -0
  54. package/deploy/gw_final.py +206 -0
  55. package/deploy/gw_restart.py +28 -0
  56. package/deploy/gw_status.py +18 -0
  57. package/deploy/inline_build.py +99 -0
  58. package/deploy/one_shot.py +84 -0
  59. package/deploy/ox_deploy.py +32 -0
  60. package/deploy/pg_setup.py +36 -0
  61. package/deploy/prod_deploy.py +126 -0
  62. package/deploy/prod_final.py +140 -0
  63. package/deploy/prod_go.py +61 -0
  64. package/deploy/prod_one_shot.py +139 -0
  65. package/deploy/prod_setup.sh +62 -0
  66. package/deploy/prod_v2.py +91 -0
  67. package/deploy/prod_v3.py +103 -0
  68. package/deploy/push_docs.py +93 -0
  69. package/deploy/push_mcp.py +66 -0
  70. package/deploy/push_mcp_doc.py +28 -0
  71. package/deploy/push_v063.py +80 -0
  72. package/deploy/push_v2.py +71 -0
  73. package/deploy/qcheck.py +13 -0
  74. package/deploy/qcheck2.py +18 -0
  75. package/deploy/quick_check.py +8 -0
  76. package/deploy/rebuild.py +47 -0
  77. package/deploy/rebuild_frontend.py +44 -0
  78. package/deploy/restart.py +28 -0
  79. package/deploy/restore_frontend.py +42 -0
  80. package/deploy/restore_frontend2.py +41 -0
  81. package/deploy/restore_gateway.py +34 -0
  82. package/deploy/restore_quick.py +33 -0
  83. package/deploy/run_bash.py +19 -0
  84. package/deploy/run_sep.py +20 -0
  85. package/deploy/sdk_upgrade.py +50 -0
  86. package/deploy/server_prepare.py +41 -0
  87. package/deploy/server_setup.py +41 -0
  88. package/deploy/ssh_check.py +14 -0
  89. package/deploy/ssh_push.py +24 -0
  90. package/deploy/start_deploy.py +35 -0
  91. package/deploy/start_frontend.py +12 -0
  92. package/deploy/start_v2.py +29 -0
  93. package/deploy/stat.py +18 -0
  94. package/deploy/tail_log.py +11 -0
  95. package/deploy/update_env.py +32 -0
  96. package/deploy/upload_and_start.py +107 -0
  97. package/deploy/verify_a2a.py +13 -0
  98. package/deploy/verify_mcp.py +37 -0
  99. package/deploy/verify_sdk.py +21 -0
  100. package/dist/config.d.ts +26 -0
  101. package/dist/config.js +41 -0
  102. package/dist/config.js.map +1 -0
  103. package/dist/index.d.ts +1 -0
  104. package/dist/index.js +149 -0
  105. package/dist/index.js.map +1 -0
  106. package/dist/lib/crypto.d.ts +2 -0
  107. package/dist/lib/crypto.js +43 -0
  108. package/dist/lib/crypto.js.map +1 -0
  109. package/dist/lib/db.d.ts +3 -0
  110. package/dist/lib/db.js +26 -0
  111. package/dist/lib/db.js.map +1 -0
  112. package/dist/lib/ethers-utils.d.ts +60 -0
  113. package/dist/lib/ethers-utils.js +186 -0
  114. package/dist/lib/ethers-utils.js.map +1 -0
  115. package/dist/middleware/adminAuth.d.ts +2 -0
  116. package/dist/middleware/adminAuth.js +31 -0
  117. package/dist/middleware/adminAuth.js.map +1 -0
  118. package/dist/middleware/auth.d.ts +22 -0
  119. package/dist/middleware/auth.js +192 -0
  120. package/dist/middleware/auth.js.map +1 -0
  121. package/dist/middleware/error-handler.d.ts +36 -0
  122. package/dist/middleware/error-handler.js +100 -0
  123. package/dist/middleware/error-handler.js.map +1 -0
  124. package/dist/middleware/rate-limiter.d.ts +5 -0
  125. package/dist/middleware/rate-limiter.js +107 -0
  126. package/dist/middleware/rate-limiter.js.map +1 -0
  127. package/dist/routes/a2a.d.ts +2 -0
  128. package/dist/routes/a2a.js +84 -0
  129. package/dist/routes/a2a.js.map +1 -0
  130. package/dist/routes/admin.d.ts +2 -0
  131. package/dist/routes/admin.js +180 -0
  132. package/dist/routes/admin.js.map +1 -0
  133. package/dist/routes/agents.d.ts +2 -0
  134. package/dist/routes/agents.js +43 -0
  135. package/dist/routes/agents.js.map +1 -0
  136. package/dist/routes/chat.d.ts +2 -0
  137. package/dist/routes/chat.js +183 -0
  138. package/dist/routes/chat.js.map +1 -0
  139. package/dist/routes/history.d.ts +2 -0
  140. package/dist/routes/history.js +92 -0
  141. package/dist/routes/history.js.map +1 -0
  142. package/dist/routes/mcp.d.ts +13 -0
  143. package/dist/routes/mcp.js +513 -0
  144. package/dist/routes/mcp.js.map +1 -0
  145. package/dist/routes/tenant.d.ts +2 -0
  146. package/dist/routes/tenant.js +154 -0
  147. package/dist/routes/tenant.js.map +1 -0
  148. package/dist/services/a2a-worker.d.ts +7 -0
  149. package/dist/services/a2a-worker.js +406 -0
  150. package/dist/services/a2a-worker.js.map +1 -0
  151. package/dist/services/agent-indexer.d.ts +4 -0
  152. package/dist/services/agent-indexer.js +124 -0
  153. package/dist/services/agent-indexer.js.map +1 -0
  154. package/e2e_wallet.js +175 -0
  155. package/ecosystem.config.js +17 -0
  156. package/package.json +33 -0
  157. package/src/config.ts +47 -0
  158. package/src/index.ts +143 -0
  159. package/src/lib/crypto.ts +44 -0
  160. package/src/lib/db.ts +25 -0
  161. package/src/lib/ethers-utils.ts +229 -0
  162. package/src/middleware/adminAuth.ts +34 -0
  163. package/src/middleware/auth.ts +244 -0
  164. package/src/middleware/error-handler.ts +109 -0
  165. package/src/middleware/rate-limiter.ts +115 -0
  166. package/src/routes/a2a.ts +97 -0
  167. package/src/routes/admin.ts +219 -0
  168. package/src/routes/agents.ts +52 -0
  169. package/src/routes/chat.ts +219 -0
  170. package/src/routes/history.ts +114 -0
  171. package/src/routes/mcp.ts +567 -0
  172. package/src/routes/tenant.ts +196 -0
  173. package/src/services/a2a-worker.ts +486 -0
  174. package/src/services/agent-indexer.ts +127 -0
  175. package/tsconfig.json +18 -0
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // AgentX Gateway — Agent Indexer (reads chain → stores in PostgreSQL)
4
+ // ---------------------------------------------------------------------------
5
+ // Syncs agent metadata from IdentityRegistry (ERC-721) into the agents table.
6
+ // Handles: IPFS CIDs, base64 data URIs, and malformed tokenURIs.
7
+ // ---------------------------------------------------------------------------
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.syncAgents = syncAgents;
10
+ const ethers_1 = require("ethers");
11
+ const db_1 = require("../lib/db");
12
+ const config_1 = require("../config");
13
+ const IDENTITY_ABI = [
14
+ 'function tokenURI(uint256 tokenId) view returns (string)',
15
+ 'function ownerOf(uint256 tokenId) view returns (address)',
16
+ ];
17
+ // Parse base64 data URI tokenURI → JSON metadata object
18
+ function parseTokenURIToJSON(tokenURI) {
19
+ if (!tokenURI)
20
+ return null;
21
+ if (tokenURI.startsWith('ipfs://'))
22
+ return null;
23
+ const match = tokenURI.match(/^data:application\/json;base64,(.+)$/i);
24
+ if (!match)
25
+ return null;
26
+ // Clean up malformed base64: trim everything after the last "==" padding
27
+ let b64 = match[1];
28
+ const lastDoubleEq = b64.lastIndexOf('==');
29
+ if (lastDoubleEq > 0 && lastDoubleEq < b64.length - 2) {
30
+ b64 = b64.substring(0, lastDoubleEq + 2);
31
+ }
32
+ try {
33
+ const decoded = Buffer.from(b64, 'base64').toString('utf-8');
34
+ // Try JSON parse first
35
+ try {
36
+ return JSON.parse(decoded);
37
+ }
38
+ catch {
39
+ // Unterminated JSON (contract bug): append missing closing quotes/braces
40
+ let fixed = decoded;
41
+ // Count unclosed quotes
42
+ const quoteCount = (fixed.match(/"/g) || []).length;
43
+ if (quoteCount % 2 !== 0)
44
+ fixed += '"';
45
+ // Count unclosed braces
46
+ const openBraces = (fixed.match(/\{/g) || []).length;
47
+ const closeBraces = (fixed.match(/\}/g) || []).length;
48
+ for (let i = closeBraces; i < openBraces; i++)
49
+ fixed += '}';
50
+ try {
51
+ return JSON.parse(fixed);
52
+ }
53
+ catch { /* ok */ }
54
+ }
55
+ // Regex fallback
56
+ const nameM = decoded.match(/"name"\s*:\s*"([^"]*)/);
57
+ if (nameM)
58
+ return { name: nameM[1] };
59
+ return null;
60
+ }
61
+ catch {
62
+ return null;
63
+ }
64
+ }
65
+ // Extract CID from IPFS tokenURI
66
+ function extractCID(tokenURI) {
67
+ if (!tokenURI)
68
+ return null;
69
+ if (tokenURI.startsWith('ipfs://'))
70
+ return tokenURI.replace('ipfs://', '').split('/')[0];
71
+ const match = tokenURI.match(/\/ipfs\/([^/?#]+)/);
72
+ if (match)
73
+ return match[1];
74
+ return null;
75
+ }
76
+ async function syncAgents() {
77
+ const provider = new ethers_1.ethers.JsonRpcProvider(config_1.config.rpcUrlOxaChain);
78
+ const contract = new ethers_1.ethers.Contract(config_1.config.identityRegistryOxaChain, IDENTITY_ABI, provider);
79
+ const pool = (0, db_1.getPool)();
80
+ let synced = 0;
81
+ let consecutiveMisses = 0;
82
+ const MAX_MISSES = 8;
83
+ const BATCH_SIZE = 10;
84
+ for (let batchStart = 1; consecutiveMisses < MAX_MISSES; batchStart += BATCH_SIZE) {
85
+ const batchIds = [];
86
+ for (let i = batchStart; i < batchStart + BATCH_SIZE && consecutiveMisses < MAX_MISSES; i++) {
87
+ batchIds.push(i);
88
+ }
89
+ const results = await Promise.allSettled(batchIds.map(async (id) => {
90
+ const [tokenURI, owner] = await Promise.all([
91
+ contract.tokenURI(id).catch(() => null),
92
+ contract.ownerOf(id).catch(() => null),
93
+ ]);
94
+ if (!tokenURI) {
95
+ consecutiveMisses++;
96
+ return null;
97
+ }
98
+ consecutiveMisses = 0;
99
+ const parsed = parseTokenURIToJSON(tokenURI);
100
+ const name = parsed?.name || `Agent ${id}`;
101
+ const description = parsed?.description || '';
102
+ const tags = Array.isArray(parsed?.tags) ? parsed.tags.map(String) : [];
103
+ const capabilities = Array.isArray(parsed?.capabilities) ? parsed.capabilities.map(String) : [];
104
+ await pool.query(`INSERT INTO agents (id, owner, name, description, tags, capabilities, token_uri, metadata_json, synced_at, updated_at)
105
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,NOW(),NOW())
106
+ ON CONFLICT (id) DO UPDATE SET
107
+ owner = EXCLUDED.owner,
108
+ name = EXCLUDED.name,
109
+ description = EXCLUDED.description,
110
+ tags = EXCLUDED.tags,
111
+ capabilities = EXCLUDED.capabilities,
112
+ token_uri = EXCLUDED.token_uri,
113
+ metadata_json = EXCLUDED.metadata_json,
114
+ synced_at = NOW(),
115
+ updated_at = NOW()`, [id, owner || '', name, description, tags, capabilities, tokenURI, JSON.stringify(parsed || {})]);
116
+ return { id, name, owner };
117
+ }));
118
+ synced += results.filter(r => r.status === 'fulfilled' && r.value !== null).length;
119
+ if (consecutiveMisses >= MAX_MISSES)
120
+ break;
121
+ }
122
+ return { synced, total: synced };
123
+ }
124
+ //# sourceMappingURL=agent-indexer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-indexer.js","sourceRoot":"","sources":["../../src/services/agent-indexer.ts"],"names":[],"mappings":"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"}
package/e2e_wallet.js ADDED
@@ -0,0 +1,175 @@
1
+ const { ethers } = require('ethers');
2
+
3
+ const RPC_URL = 'http://43.156.99.215:18545';
4
+ const PK = '0x5a74fd11c022e5e1fdc099e9971ebcdbad8b813f91fa99e6861bc0bcac99e094';
5
+ const GW = 'http://localhost:3090';
6
+
7
+ const contracts = {
8
+ identity: '0xbf5F9db266c8c97E3334466C88597Eb758AfE212',
9
+ sub: '0x019AC9d945467478Dd371CDbD70cb2f325800E6B',
10
+ rep: '0x6a18C2664E1b42063860d864b6448b824d7B843F',
11
+ cfg: '0x07280674ccc2898Fd038A9e3C22005CA83ffD2F8',
12
+ ep: '0xB361d04F49000013FC131D3C59C41c8486C64f8c',
13
+ };
14
+
15
+ const ID_ABI = [
16
+ 'function register(string tokenURI) payable returns (uint256)',
17
+ 'function agentExists(uint256) view returns (bool)',
18
+ 'function getCurrentAgentId() view returns (uint256)',
19
+ 'function getAgentsByOwner(address) view returns (uint256[])',
20
+ 'function tokenURI(uint256) view returns (string)',
21
+ ];
22
+ const CFG_ABI = [
23
+ 'function setConfig(uint256,string,string,string) returns (bool)',
24
+ 'function getAgentConfigs(uint256) view returns (tuple(uint256,uint256,string,string,string,string,bool,uint256,uint256,address)[])',
25
+ ];
26
+ const REP_ABI = [
27
+ 'function setFeedback(uint256,uint8) returns (bool)',
28
+ 'function getReputationSummary(uint256,address[],bytes32,bytes32) view returns (uint64,uint8)',
29
+ ];
30
+
31
+ let results = [];
32
+ let p = 0, f = 0;
33
+
34
+ function log(t, s, d) {
35
+ const icon = s === 'PASS' ? '✅' : s === 'SKIP' ? '⚠️' : '❌';
36
+ if (s === 'PASS') p++; else if (s === 'FAIL') f++;
37
+ results.push({ test: t, status: s, detail: d });
38
+ console.log(icon + ' [' + s + '] ' + t + (d ? ': ' + d.substring(0, 100) : ''));
39
+ }
40
+
41
+ async function mcp(name, args) {
42
+ const r = await fetch(GW + '/mcp', {
43
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
44
+ body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name, arguments: { chain: 'oxachain', ...args } } }),
45
+ });
46
+ return r.json();
47
+ }
48
+
49
+ async function main() {
50
+ console.log('=== AgentX E2E Wallet Injection Test ===\n');
51
+ const provider = new ethers.JsonRpcProvider(RPC_URL);
52
+ const wallet = new ethers.Wallet(PK, provider);
53
+ const bal = await provider.getBalance(wallet.address);
54
+ console.log('Wallet: ' + wallet.address + '\nBalance: ' + ethers.formatEther(bal) + ' T0x\n');
55
+
56
+ const id = new ethers.Contract(contracts.identity, ID_ABI, wallet);
57
+ const cfg = new ethers.Contract(contracts.cfg, CFG_ABI, wallet);
58
+ const rep = new ethers.Contract(contracts.rep, REP_ABI, wallet);
59
+
60
+ let currentId = Number(await id.getCurrentAgentId());
61
+ let agentIds = [];
62
+ console.log('Current total agents: ' + currentId + '\n');
63
+
64
+ // TEST 1: Register 3 Agents
65
+ console.log('--- [1] Register 3 Agents ---');
66
+ const names = ['E2E-Alpha', 'E2E-Beta', 'E2E-Gamma'];
67
+ for (let i = 0; i < 3; i++) {
68
+ try {
69
+ const tx = await id.register('ipfs://QmE2E' + names[i], { gasLimit: 300000 });
70
+ const r = await tx.wait();
71
+ agentIds.push(currentId + i + 1);
72
+ log('Register ' + names[i], 'PASS', 'agentId=' + agentIds[i] + ' gas=' + r.gasUsed);
73
+ } catch (e) { log('Register ' + names[i], 'FAIL', e.reason || e.message?.substring(0, 80)); }
74
+ }
75
+
76
+ // TEST 2: Verify Agents
77
+ console.log('\n--- [2] Verify Agents ---');
78
+ for (const aid of agentIds) {
79
+ try {
80
+ const ex = await id.agentExists(aid);
81
+ const uri = await id.tokenURI(aid);
82
+ log('Agent ' + aid + ' exists', 'PASS', 'exists=' + ex + ' uri=' + uri);
83
+ } catch (e) { log('Agent ' + aid, 'FAIL', e.message?.substring(0, 80)); }
84
+ }
85
+
86
+ // TEST 3: List owned
87
+ console.log('\n--- [3] List Owned Agents ---');
88
+ try {
89
+ const owned = await id.getAgentsByOwner(wallet.address);
90
+ log('getAgentsByOwner', 'PASS', 'ids=[' + owned.map(n => Number(n)).join(',') + ']');
91
+ } catch (e) { log('getAgentsByOwner', 'FAIL', e.message?.substring(0, 80)); }
92
+
93
+ // TEST 4: Configure
94
+ console.log('\n--- [4] Configure Agents ---');
95
+ for (let i = 0; i < agentIds.length; i++) {
96
+ try {
97
+ const tx = await cfg.setConfig(agentIds[i], 'greeting', 'Hello from ' + names[i] + '!', 'string', { gasLimit: 200000 });
98
+ await tx.wait();
99
+ log('Config Agent ' + agentIds[i], 'PASS', 'greeting set');
100
+ } catch (e) { log('Config Agent ' + agentIds[i], 'FAIL', e.reason || e.message?.substring(0, 80)); }
101
+ }
102
+
103
+ // TEST 5: Rate
104
+ console.log('\n--- [5] Rate Agents ---');
105
+ const scores = [5, 4, 5];
106
+ for (let i = 0; i < agentIds.length; i++) {
107
+ try {
108
+ const tx = await rep.setFeedback(agentIds[i], scores[i], { gasLimit: 200000 });
109
+ await tx.wait();
110
+ log('Rate Agent ' + agentIds[i], 'PASS', 'score=' + scores[i]);
111
+ } catch (e) { log('Rate Agent ' + agentIds[i], 'FAIL', e.reason || e.message?.substring(0, 80)); }
112
+ }
113
+
114
+ // TEST 6: MCP Integration
115
+ console.log('\n--- [6] MCP Integration ---');
116
+ try {
117
+ const r = await mcp('agentx_identity_list', { ownerAddress: wallet.address });
118
+ const t = JSON.parse(r?.result?.content?.[0]?.text || '{}');
119
+ log('MCP identity_list', 'PASS', 'found ' + (t.agentIds?.length || 0) + ' agents');
120
+ } catch (e) { log('MCP identity_list', 'FAIL', e.message?.substring(0, 80)); }
121
+
122
+ for (const aid of agentIds) {
123
+ try {
124
+ const r = await mcp('agentx_identity_exists', { agentId: aid });
125
+ const t = JSON.parse(r?.result?.content?.[0]?.text || '{}');
126
+ log('MCP exists(' + aid + ')', t.exists ? 'PASS' : 'FAIL', 'exists=' + t.exists);
127
+ } catch (e) { log('MCP exists(' + aid + ')', 'FAIL', e.message?.substring(0, 80)); }
128
+ }
129
+
130
+ for (const aid of agentIds) {
131
+ try {
132
+ const r = await mcp('agentx_reputation_get', { agentId: aid });
133
+ const t = JSON.parse(r?.result?.content?.[0]?.text || '{}');
134
+ log('MCP reputation(' + aid + ')', 'PASS', 'avg=' + t.averageScore + ' count=' + t.reviewCount);
135
+ } catch (e) { log('MCP reputation(' + aid + ')', 'FAIL', e.message?.substring(0, 80)); }
136
+ }
137
+
138
+ // TEST 7: Subscription
139
+ console.log('\n--- [7] Subscription ---');
140
+ try {
141
+ const r = await mcp('agentx_subscription_check', { subscriberAddress: wallet.address, agentId: agentIds[0] });
142
+ const t = JSON.parse(r?.result?.content?.[0]?.text || '{}');
143
+ log('MCP sub_check', 'PASS', 'active=' + t.active);
144
+ } catch (e) { log('MCP sub_check', 'FAIL', e.message?.substring(0, 80)); }
145
+
146
+ try {
147
+ const r = await mcp('agentx_subscription_plans', { planId: 1 });
148
+ const t = JSON.parse(r?.result?.content?.[0]?.text || '{}');
149
+ log('MCP plans', t.note ? 'SKIP' : 'PASS', t.note || JSON.stringify(t).substring(0, 60));
150
+ } catch (e) { log('MCP plans', 'FAIL', e.message?.substring(0, 80)); }
151
+
152
+ // TEST 8: Auth
153
+ console.log('\n--- [8] Gateway Auth ---');
154
+ try {
155
+ const cRes = await fetch(GW + '/api/v1/auth/challenge?address=' + wallet.address);
156
+ const c = await cRes.json();
157
+ if (c.challenge) {
158
+ log('Auth challenge', 'PASS');
159
+ const sig = await wallet.signMessage(c.challenge);
160
+ const vRes = await fetch(GW + '/api/v1/auth/verify', {
161
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
162
+ body: JSON.stringify({ address: wallet.address, signature: sig }),
163
+ });
164
+ const v = await vRes.json();
165
+ log('Auth verify', v.token ? 'PASS' : 'FAIL', v.token ? 'JWT issued' : JSON.stringify(v).substring(0, 60));
166
+ } else { log('Auth challenge', 'FAIL', JSON.stringify(c).substring(0, 80)); }
167
+ } catch (e) { log('Auth', 'FAIL', e.message?.substring(0, 80)); }
168
+
169
+ // SUMMARY
170
+ const fb = await provider.getBalance(wallet.address);
171
+ console.log('\n=== SUMMARY: ' + p + ' PASS, ' + f + ' FAIL, ' + (results.length - p - f) + ' SKIP ===');
172
+ console.log('Gas used: ' + ethers.formatEther(bal - fb) + ' T0x');
173
+ console.log('Final balance: ' + ethers.formatEther(fb) + ' T0x');
174
+ }
175
+ main().catch(e => { console.error('FATAL:', e); process.exit(1); });
@@ -0,0 +1,17 @@
1
+ module.exports = {
2
+ apps: [{
3
+ name: 'agentx-gateway',
4
+ script: 'dist/index.js',
5
+ instances: 2,
6
+ exec_mode: 'cluster',
7
+ env: {
8
+ NODE_ENV: 'production',
9
+ PORT: 3090,
10
+ },
11
+ error_file: './logs/err.log',
12
+ out_file: './logs/out.log',
13
+ merge_logs: true,
14
+ max_memory_restart: '512M',
15
+ node_args: '--max-old-space-size=512',
16
+ }],
17
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@agentxv2/gateway",
3
+ "version": "0.1.1",
4
+ "description": "AgentX Gateway — Multi-tenant LLM proxy with wallet auth, rate limiting, and API key management",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "build": "tsc",
8
+ "start": "node dist/index.js",
9
+ "dev": "tsx watch src/index.ts",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "cors": "^2.8.5",
14
+ "dotenv": "^16.4.5",
15
+ "ethers": "^6.13.0",
16
+ "express": "^4.21.0",
17
+ "express-rate-limit": "^7.4.0",
18
+ "helmet": "^8.0.0",
19
+ "jsonwebtoken": "^9.0.2",
20
+ "pg": "^8.13.0",
21
+ "ioredis": "^5.4.1"
22
+ },
23
+ "devDependencies": {
24
+ "@types/cors": "^2.8.17",
25
+ "@types/express": "^4.17.21",
26
+ "@types/jsonwebtoken": "^9.0.7",
27
+ "@types/node": "^22.0.0",
28
+ "@types/pg": "^8.11.10",
29
+ "tsx": "^4.19.0",
30
+ "typescript": "^5.5.0"
31
+ },
32
+ "license": "MIT"
33
+ }
package/src/config.ts ADDED
@@ -0,0 +1,47 @@
1
+ // ---------------------------------------------------------------------------
2
+ // AgentX Gateway — Configuration
3
+ // ---------------------------------------------------------------------------
4
+
5
+ import dotenv from 'dotenv'
6
+ dotenv.config()
7
+
8
+ export const config = {
9
+ port: parseInt(process.env.PORT || '3090', 10),
10
+ nodeEnv: process.env.NODE_ENV || 'development',
11
+
12
+ databaseUrl: process.env.DATABASE_URL || 'postgresql://localhost:5432/agentx_gateway',
13
+ redisUrl: process.env.REDIS_URL || 'redis://localhost:6379',
14
+
15
+ jwtSecret: process.env.JWT_SECRET || 'dev-secret-change-me',
16
+ masterEncryptionKey: process.env.MASTER_ENCRYPTION_KEY || '',
17
+
18
+ sessionTtlSec: parseInt(process.env.SESSION_TTL_SEC || '86400', 10),
19
+
20
+ // MCP / On-chain read — Dual-chain
21
+ // Sepolia
22
+ rpcUrl: process.env.RPC_URL || 'https://ethereum-sepolia-rpc.publicnode.com',
23
+ rpcUrlOxaChain: process.env.RPC_URL_OXACHAIN || 'https://rpc-oxa.0xainet.top',
24
+ chainId: parseInt(process.env.CHAIN_ID || '11155111', 10),
25
+ chainIdOxaChain: parseInt(process.env.CHAIN_ID_OXACHAIN || '19505', 10),
26
+
27
+ identityRegistry: process.env.IDENTITY_REGISTRY || '0xe94ad380d3F8d08a7590eda0C84f354a93F96e5F',
28
+ identityRegistryOxaChain: process.env.IDENTITY_REGISTRY_OXACHAIN || '0xbf5F9db266c8c97E3334466C88597Eb758AfE212',
29
+
30
+ subscriptionManager: process.env.SUBSCRIPTION_MANAGER || '0xC15fE80b9d800abb72121F353a6ae6d6E9077E63',
31
+ subscriptionManagerOxaChain: process.env.SUBSCRIPTION_MANAGER_OXACHAIN || '0x019AC9d945467478Dd371CDbD70cb2f325800E6B',
32
+
33
+ a2aProtocol: process.env.A2A_PROTOCOL || '0x309C7447d89f3087A9924BB686d88df020F7e9cB',
34
+ a2aProtocolOxaChain: process.env.A2A_PROTOCOL_OXACHAIN || '0x7F42a7dC4A0F3C107664C3750bE1B5B6fa6BEb86',
35
+
36
+ reputationRegistry: process.env.REPUTATION_REGISTRY || '0xeb6B410ea71b8d9dA0c96f6A91d35027CE143DC9',
37
+ reputationRegistryOxaChain: process.env.REPUTATION_REGISTRY_OXACHAIN || '0x6a18C2664E1b42063860d864b6448b824d7B843F',
38
+
39
+ configurationRegistry: process.env.CONFIGURATION_REGISTRY || '0x68DcE00e4C9077c94BC68016cD14B09557faEA6c',
40
+ configurationRegistryOxaChain: process.env.CONFIGURATION_REGISTRY_OXACHAIN || '0x07280674ccc2898Fd038A9e3C22005CA83ffD2F8',
41
+
42
+ multiEndpoint: process.env.MULTI_ENDPOINT || '0xEB5e866f186d4B73F97aa0d70B86f2C6e2e21Cb7',
43
+ multiEndpointOxaChain: process.env.MULTI_ENDPOINT_OXACHAIN || '0xB361d04F49000013FC131D3C59C41c8486C64f8c',
44
+
45
+ // A2A Worker wallet (for creating sub-tasks on-chain during multi-agent orchestration)
46
+ a2aWorkerPrivateKey: process.env.A2A_WORKER_PRIVATE_KEY || '',
47
+ }
package/src/index.ts ADDED
@@ -0,0 +1,143 @@
1
+ // ---------------------------------------------------------------------------
2
+ // AgentX Gateway — Entry Point
3
+ // ---------------------------------------------------------------------------
4
+
5
+ import express from 'express'
6
+ import cors from 'cors'
7
+ import helmet from 'helmet'
8
+ import rateLimit from 'express-rate-limit'
9
+ import { config } from './config'
10
+ import { getChallenge, verifyChallenge, authMiddleware } from './middleware/auth'
11
+ import { tenantRateLimiter } from './middleware/rate-limiter'
12
+ import { globalErrorHandler } from './middleware/error-handler'
13
+ import chatRouter from './routes/chat'
14
+ import tenantRouter from './routes/tenant'
15
+ import historyRouter from './routes/history'
16
+ import mcpRouter from './routes/mcp'
17
+ import agentsRouter from './routes/agents'
18
+ import a2aRouter from './routes/a2a'
19
+ import adminRouter from './routes/admin'
20
+
21
+ const app = express()
22
+
23
+ // ── Security ──────────────────────────────────────────────────────────────
24
+
25
+ app.use(helmet({
26
+ contentSecurityPolicy: false,
27
+ crossOriginEmbedderPolicy: false,
28
+ }))
29
+ app.use(cors({
30
+ origin: process.env.CORS_ORIGIN || '*',
31
+ credentials: true,
32
+ }))
33
+
34
+ // ── Global rate limit ─────────────────────────────────────────────────────
35
+
36
+ app.use(rateLimit({
37
+ windowMs: 60_000,
38
+ max: 1000,
39
+ standardHeaders: true,
40
+ legacyHeaders: false,
41
+ message: { error: 'Too many requests, please try again later.' },
42
+ }))
43
+
44
+ // ── Body parsing ──────────────────────────────────────────────────────────
45
+
46
+ app.use(express.json({ limit: '1mb' }))
47
+
48
+ // Handle malformed JSON (return 400 instead of 500)
49
+ app.use((err: any, _req: express.Request, res: express.Response, next: express.NextFunction) => {
50
+ if (err && err.type === 'entity.parse.failed') {
51
+ return res.status(400).json({ error: 'Invalid JSON in request body' })
52
+ }
53
+ next(err)
54
+ })
55
+
56
+ // ── Health check ──────────────────────────────────────────────────────────
57
+
58
+ app.get('/api/v1/health', async (_req, res) => {
59
+ res.json({ status: 'ok', time: new Date().toISOString() })
60
+ })
61
+
62
+ // ── MCP endpoint (public JSON-RPC 2.0) ────────────────────────────────────
63
+
64
+ app.use('/mcp', mcpRouter)
65
+
66
+ // ── Admin API (protected by admin key, not wallet auth) ──────────────────
67
+
68
+ app.use('/api/v1/admin', adminRouter)
69
+
70
+ // ── Auth routes (public) ──────────────────────────────────────────────────
71
+
72
+ app.get('/api/v1/auth/challenge', getChallenge)
73
+ app.post('/api/v1/auth/verify', verifyChallenge)
74
+
75
+ // ── Agents API (public, no auth needed) ────────────────────────────────────
76
+
77
+ app.use('/api/v1/agents', agentsRouter)
78
+
79
+ // ── A2A Task Results API (public, SDK daemon queries this) ────────────────
80
+
81
+ app.use('/api/v1/a2a', a2aRouter)
82
+
83
+ // Agent sync (public, for cron)
84
+ app.post('/api/v1/agents-sync', async (_req, res, next) => {
85
+ try {
86
+ const { syncAgents } = await import('./services/agent-indexer')
87
+ const result = await syncAgents()
88
+ res.json({ success: true, ...result })
89
+ } catch (err: any) {
90
+ next(err)
91
+ }
92
+ })
93
+
94
+ // ── Protected routes (auth + rate-limit only on known paths) ─────────────
95
+
96
+ // Known protected API path prefixes (anything else under /api/v1 returns 404)
97
+ const PROTECTED_PREFIXES = ['/chat/completions', '/chat/history', '/tenant/']
98
+
99
+ app.use('/api/v1', (req, _res, next) => {
100
+ if (PROTECTED_PREFIXES.some(p => req.path.startsWith(p))) {
101
+ next()
102
+ } else {
103
+ // Path is not a known API route → 404, skip auth middleware
104
+ _res.status(404).json({ error: 'Not found' })
105
+ }
106
+ })
107
+
108
+ const api = express.Router()
109
+ api.use(authMiddleware)
110
+ api.use(tenantRateLimiter)
111
+
112
+ api.use(chatRouter)
113
+ api.use('/tenant', tenantRouter)
114
+ api.use('/chat', historyRouter)
115
+
116
+ app.use('/api/v1', api)
117
+
118
+ // ── 404 handler ───────────────────────────────────────────────────────────
119
+
120
+ app.use((_req, res) => {
121
+ if (!res.headersSent) {
122
+ res.status(404).json({ error: 'Not found' })
123
+ }
124
+ })
125
+
126
+ // ── Error handler ─────────────────────────────────────────────────────────
127
+
128
+ app.use(globalErrorHandler)
129
+
130
+ // ── Start ─────────────────────────────────────────────────────────────────
131
+
132
+ app.listen(config.port, () => {
133
+ console.log(`[AgentX Gateway] Running on port ${config.port}`)
134
+ console.log(`[AgentX Gateway] Mode: ${config.nodeEnv}`)
135
+
136
+ // Start A2A background worker for multi-agent task processing
137
+ import('./services/a2a-worker').then(({ startA2AWorker }) => {
138
+ startA2AWorker()
139
+ console.log('[AgentX Gateway] A2A Worker started')
140
+ }).catch(err => {
141
+ console.error('[AgentX Gateway] Failed to start A2A Worker:', err.message)
142
+ })
143
+ })
@@ -0,0 +1,44 @@
1
+ // ---------------------------------------------------------------------------
2
+ // AgentX Gateway — Crypto Utilities
3
+ // ---------------------------------------------------------------------------
4
+ // AES-256-GCM encrypt/decrypt for tenant API keys at rest.
5
+ // ---------------------------------------------------------------------------
6
+
7
+ import crypto from 'crypto'
8
+
9
+ const ALGORITHM = 'aes-256-gcm'
10
+ const IV_LENGTH = 12
11
+ const AUTH_TAG_LENGTH = 16
12
+
13
+ export function encryptApiKey(plaintext: string, masterKeyHex: string): string {
14
+ const key = Buffer.from(masterKeyHex, 'hex')
15
+ const iv = crypto.randomBytes(IV_LENGTH)
16
+ const cipher = crypto.createCipheriv(ALGORITHM, key, iv)
17
+
18
+ const encrypted = Buffer.concat([
19
+ cipher.update(plaintext, 'utf8'),
20
+ cipher.final(),
21
+ ])
22
+ const authTag = cipher.getAuthTag()
23
+
24
+ const combined = Buffer.concat([iv, authTag, encrypted])
25
+ return combined.toString('base64')
26
+ }
27
+
28
+ export function decryptApiKey(ciphertext: string, masterKeyHex: string): string {
29
+ const key = Buffer.from(masterKeyHex, 'hex')
30
+ const combined = Buffer.from(ciphertext, 'base64')
31
+
32
+ const iv = combined.subarray(0, IV_LENGTH)
33
+ const authTag = combined.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH)
34
+ const encrypted = combined.subarray(IV_LENGTH + AUTH_TAG_LENGTH)
35
+
36
+ const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv)
37
+ decipher.setAuthTag(authTag)
38
+
39
+ const decrypted = Buffer.concat([
40
+ decipher.update(encrypted),
41
+ decipher.final(),
42
+ ])
43
+ return decrypted.toString('utf8')
44
+ }
package/src/lib/db.ts ADDED
@@ -0,0 +1,25 @@
1
+ // ---------------------------------------------------------------------------
2
+ // AgentX Gateway — Database Pool
3
+ // ---------------------------------------------------------------------------
4
+
5
+ import { Pool } from 'pg'
6
+
7
+ let pool: Pool | null = null
8
+
9
+ export function getPool(): Pool {
10
+ if (!pool) {
11
+ pool = new Pool({
12
+ connectionString: process.env.DATABASE_URL,
13
+ max: 20,
14
+ idleTimeoutMillis: 30_000,
15
+ })
16
+ }
17
+ return pool
18
+ }
19
+
20
+ export async function closePool(): Promise<void> {
21
+ if (pool) {
22
+ await pool.end()
23
+ pool = null
24
+ }
25
+ }