@agirails/sdk 3.3.0 → 3.4.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 (107) hide show
  1. package/dist/api/agirailsApp.d.ts +21 -1
  2. package/dist/api/agirailsApp.d.ts.map +1 -1
  3. package/dist/api/agirailsApp.js.map +1 -1
  4. package/dist/builders/CounterAcceptBuilder.d.ts +96 -0
  5. package/dist/builders/CounterAcceptBuilder.d.ts.map +1 -0
  6. package/dist/builders/CounterAcceptBuilder.js +226 -0
  7. package/dist/builders/CounterAcceptBuilder.js.map +1 -0
  8. package/dist/builders/CounterOfferBuilder.d.ts +143 -0
  9. package/dist/builders/CounterOfferBuilder.d.ts.map +1 -0
  10. package/dist/builders/CounterOfferBuilder.js +329 -0
  11. package/dist/builders/CounterOfferBuilder.js.map +1 -0
  12. package/dist/builders/QuoteBuilder.d.ts.map +1 -1
  13. package/dist/builders/QuoteBuilder.js +8 -3
  14. package/dist/builders/QuoteBuilder.js.map +1 -1
  15. package/dist/builders/index.d.ts +2 -0
  16. package/dist/builders/index.d.ts.map +1 -1
  17. package/dist/builders/index.js +7 -1
  18. package/dist/builders/index.js.map +1 -1
  19. package/dist/cli/agirails.js +22 -2
  20. package/dist/cli/agirails.js.map +1 -1
  21. package/dist/cli/commands/health.js +21 -5
  22. package/dist/cli/commands/health.js.map +1 -1
  23. package/dist/cli/commands/init.d.ts.map +1 -1
  24. package/dist/cli/commands/init.js +25 -5
  25. package/dist/cli/commands/init.js.map +1 -1
  26. package/dist/cli/commands/publish.d.ts +34 -0
  27. package/dist/cli/commands/publish.d.ts.map +1 -1
  28. package/dist/cli/commands/publish.js +256 -80
  29. package/dist/cli/commands/publish.js.map +1 -1
  30. package/dist/cli/commands/repair.d.ts +23 -0
  31. package/dist/cli/commands/repair.d.ts.map +1 -0
  32. package/dist/cli/commands/repair.js +210 -0
  33. package/dist/cli/commands/repair.js.map +1 -0
  34. package/dist/cli/commands/serve.d.ts +38 -0
  35. package/dist/cli/commands/serve.d.ts.map +1 -0
  36. package/dist/cli/commands/serve.js +308 -0
  37. package/dist/cli/commands/serve.js.map +1 -0
  38. package/dist/cli/commands/test.js +2 -2
  39. package/dist/cli/commands/test.js.map +1 -1
  40. package/dist/cli/index.js +6 -0
  41. package/dist/cli/index.js.map +1 -1
  42. package/dist/config/agirailsmdV4.d.ts +46 -1
  43. package/dist/config/agirailsmdV4.d.ts.map +1 -1
  44. package/dist/config/agirailsmdV4.js +65 -8
  45. package/dist/config/agirailsmdV4.js.map +1 -1
  46. package/dist/config/defaults.d.ts +10 -0
  47. package/dist/config/defaults.d.ts.map +1 -1
  48. package/dist/config/defaults.js +10 -0
  49. package/dist/config/defaults.js.map +1 -1
  50. package/dist/config/publishPipeline.d.ts +23 -1
  51. package/dist/config/publishPipeline.d.ts.map +1 -1
  52. package/dist/config/publishPipeline.js +70 -15
  53. package/dist/config/publishPipeline.js.map +1 -1
  54. package/dist/index.d.ts +20 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +29 -3
  57. package/dist/index.js.map +1 -1
  58. package/dist/level1/Agent.d.ts +27 -0
  59. package/dist/level1/Agent.d.ts.map +1 -1
  60. package/dist/level1/Agent.js +77 -6
  61. package/dist/level1/Agent.js.map +1 -1
  62. package/dist/negotiation/BuyerOrchestrator.d.ts +103 -1
  63. package/dist/negotiation/BuyerOrchestrator.d.ts.map +1 -1
  64. package/dist/negotiation/BuyerOrchestrator.js +499 -4
  65. package/dist/negotiation/BuyerOrchestrator.js.map +1 -1
  66. package/dist/negotiation/DecisionEngine.d.ts +69 -1
  67. package/dist/negotiation/DecisionEngine.d.ts.map +1 -1
  68. package/dist/negotiation/DecisionEngine.js +140 -1
  69. package/dist/negotiation/DecisionEngine.js.map +1 -1
  70. package/dist/negotiation/PolicyEngine.d.ts +32 -0
  71. package/dist/negotiation/PolicyEngine.d.ts.map +1 -1
  72. package/dist/negotiation/PolicyEngine.js.map +1 -1
  73. package/dist/negotiation/ProviderOrchestrator.d.ts +108 -0
  74. package/dist/negotiation/ProviderOrchestrator.d.ts.map +1 -0
  75. package/dist/negotiation/ProviderOrchestrator.js +136 -0
  76. package/dist/negotiation/ProviderOrchestrator.js.map +1 -0
  77. package/dist/negotiation/ProviderPolicy.d.ts +143 -0
  78. package/dist/negotiation/ProviderPolicy.d.ts.map +1 -0
  79. package/dist/negotiation/ProviderPolicy.js +207 -0
  80. package/dist/negotiation/ProviderPolicy.js.map +1 -0
  81. package/dist/negotiation/index.d.ts +8 -1
  82. package/dist/negotiation/index.d.ts.map +1 -1
  83. package/dist/negotiation/index.js +8 -1
  84. package/dist/negotiation/index.js.map +1 -1
  85. package/dist/negotiation/verifyQuoteOnChain.d.ts +58 -0
  86. package/dist/negotiation/verifyQuoteOnChain.d.ts.map +1 -0
  87. package/dist/negotiation/verifyQuoteOnChain.js +83 -0
  88. package/dist/negotiation/verifyQuoteOnChain.js.map +1 -0
  89. package/dist/runtime/BlockchainRuntime.d.ts +13 -0
  90. package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
  91. package/dist/runtime/BlockchainRuntime.js +23 -0
  92. package/dist/runtime/BlockchainRuntime.js.map +1 -1
  93. package/dist/runtime/IACTPRuntime.d.ts +35 -0
  94. package/dist/runtime/IACTPRuntime.d.ts.map +1 -1
  95. package/dist/runtime/MockRuntime.d.ts +11 -0
  96. package/dist/runtime/MockRuntime.d.ts.map +1 -1
  97. package/dist/runtime/MockRuntime.js +39 -0
  98. package/dist/runtime/MockRuntime.js.map +1 -1
  99. package/dist/runtime/types/MockState.d.ts +10 -0
  100. package/dist/runtime/types/MockState.d.ts.map +1 -1
  101. package/dist/runtime/types/MockState.js.map +1 -1
  102. package/dist/transport/QuoteChannel.d.ts +201 -0
  103. package/dist/transport/QuoteChannel.d.ts.map +1 -0
  104. package/dist/transport/QuoteChannel.js +358 -0
  105. package/dist/transport/QuoteChannel.js.map +1 -0
  106. package/dist/types/adapter.d.ts +24 -24
  107. package/package.json +16 -1
@@ -0,0 +1,201 @@
1
+ /**
2
+ * QuoteChannel — HTTPS transport for AIP-2.1 quote + counter-offer messages.
3
+ *
4
+ * Split into three responsibilities so the SDK is framework-agnostic:
5
+ *
6
+ * 1. `QuoteChannelClient` — sends a signed message to a peer's endpoint.
7
+ * Used by buyers (posting counter-offers to the provider) and by
8
+ * providers (posting quotes to the buyer). Plain fetch + timeout.
9
+ *
10
+ * 2. `QuoteChannelHandler` — framework-agnostic receive-side handler.
11
+ * Callers wire it into whatever HTTP framework they use (Express,
12
+ * Next.js route handler, Fastify, etc). Enforces the security model
13
+ * from AIP-2.1-DRAFT §8:
14
+ * - URL path binding: `/quote-channel/{chainId}/{txId}` must
15
+ * match message.chainId / message.txId (closes T2 + T5).
16
+ * - EIP-712 signature verification (closes "anyone can POST").
17
+ * - TTL + grace window (closes T3).
18
+ * - Nonce LRU dedup (closes T1, idempotent replay).
19
+ * Rate limiting is intentionally out of scope — framework-level
20
+ * concern (Next.js middleware, Express rate-limit, nginx, etc).
21
+ *
22
+ * 3. `DedupStore` — swappable backing for the nonce LRU. In-memory
23
+ * default for single-process use; callers can plug Redis etc. for
24
+ * multi-worker production.
25
+ *
26
+ * @module transport/QuoteChannel
27
+ * @see Protocol/aips/AIP-2.1-DRAFT.md §8 (threat model + mitigations)
28
+ */
29
+ import { QuoteMessage } from '../builders/QuoteBuilder';
30
+ import { CounterOfferMessage } from '../builders/CounterOfferBuilder';
31
+ /** Path pattern builders use / handlers expect. */
32
+ export declare function buildChannelPath(chainId: number, txId: string): string;
33
+ export declare const TTL_GRACE_SECONDS = 30;
34
+ export declare const DEDUP_TTL_SECONDS = 90000;
35
+ /**
36
+ * Wire payload posted by the client and parsed by the handler.
37
+ * Discriminated by `type` so the same endpoint serves both directions.
38
+ */
39
+ export type ChannelPayload = {
40
+ type: 'agirails.quote.v1';
41
+ message: QuoteMessage;
42
+ } | {
43
+ type: 'agirails.counteroffer.v1';
44
+ message: CounterOfferMessage;
45
+ };
46
+ export interface DedupStore {
47
+ /**
48
+ * Atomic "record if absent". Returns:
49
+ * 'recorded' — the key was not present (or had expired) and has now
50
+ * been recorded with the given TTL. Caller treats this
51
+ * POST as fresh and performs side effects.
52
+ * 'duplicate' — the key was already present and unexpired. Caller
53
+ * must return an idempotent cached response without
54
+ * performing side effects.
55
+ *
56
+ * MUST be atomic at the backend level. Single-process JS gets this
57
+ * trivially (no real concurrency); Redis implementations use
58
+ * `SET key 1 NX PX ttlMs` which is natively atomic and correctly
59
+ * handles multiple concurrent workers competing on the same key.
60
+ *
61
+ * Separate check+record would open a TOCTOU window where two
62
+ * workers both observe 'fresh' and both commit — that's the P2
63
+ * audit finding this interface closes.
64
+ */
65
+ recordOnce(key: string, ttlMs: number): Promise<'recorded' | 'duplicate'>;
66
+ }
67
+ /**
68
+ * Single-process in-memory LRU. Callers replace this in production
69
+ * with a distributed store (Redis SET NX EX, DynamoDB conditional put,
70
+ * Postgres INSERT ... ON CONFLICT DO NOTHING, etc).
71
+ *
72
+ * Atomicity here is free because JavaScript event-loop execution is
73
+ * single-threaded — a `recordOnce` call cannot be interrupted mid-way.
74
+ * Multi-worker deployments MUST use a real distributed store or will
75
+ * see duplicate 'recorded' returns across workers.
76
+ */
77
+ export declare class InMemoryDedupStore implements DedupStore {
78
+ private readonly entries;
79
+ private readonly maxSize;
80
+ constructor(maxSize?: number);
81
+ recordOnce(key: string, ttlMs: number): Promise<'recorded' | 'duplicate'>;
82
+ /** Drop entries whose TTL has elapsed. O(n) — fine for our sizes. */
83
+ private dropExpired;
84
+ /** Bound the map at `maxSize` by evicting oldest-inserted keys. */
85
+ private trimToSize;
86
+ }
87
+ export interface QuoteChannelClientConfig {
88
+ /** Per-request timeout in ms. Default 10s. */
89
+ timeoutMs?: number;
90
+ /** Override fetch for tests. Defaults to global fetch. */
91
+ fetchImpl?: typeof fetch;
92
+ /**
93
+ * Allow insecure targets (http://, localhost, RFC1918, link-local).
94
+ * Default false (production hardening). Set to true ONLY for local
95
+ * dev or integration tests running against a mock server.
96
+ *
97
+ * Note: this is a URL-string check only. DNS rebinding can still bypass
98
+ * it — production deployments that are extra paranoid should put the
99
+ * client behind an HTTP proxy that enforces the same rules against the
100
+ * resolved IP at request time.
101
+ */
102
+ allowInsecureTargets?: boolean;
103
+ }
104
+ export declare class QuoteChannelClient {
105
+ private readonly timeoutMs;
106
+ private readonly fetchImpl;
107
+ private readonly allowInsecureTargets;
108
+ constructor(cfg?: QuoteChannelClientConfig);
109
+ /** POST a provider quote to the buyer's endpoint. */
110
+ sendQuote(peerEndpoint: string, quote: QuoteMessage): Promise<void>;
111
+ /** POST a buyer counter-offer to the provider's endpoint. */
112
+ sendCounter(peerEndpoint: string, counter: CounterOfferMessage): Promise<void>;
113
+ private post;
114
+ }
115
+ export interface QuoteChannelHandlerConfig {
116
+ /** Kernel address per chainId — used for EIP-712 domain when verifying. */
117
+ kernelAddressByChainId: Record<number, string>;
118
+ /** Dedup store. Defaults to in-memory (single process). */
119
+ dedupStore?: DedupStore;
120
+ /** TTL grace window in seconds for `expiresAt` check. Defaults to 30s. */
121
+ ttlGraceSeconds?: number;
122
+ }
123
+ export interface HandlerContext {
124
+ /** Chain ID parsed from the URL path. */
125
+ pathChainId: number;
126
+ /** txId parsed from the URL path (0x-prefixed 64-hex). */
127
+ pathTxId: string;
128
+ }
129
+ export type HandlerResult = {
130
+ status: 201;
131
+ body: {
132
+ accepted: true;
133
+ duplicate: false;
134
+ };
135
+ } | {
136
+ status: 200;
137
+ body: {
138
+ accepted: true;
139
+ duplicate: true;
140
+ };
141
+ } | {
142
+ status: 400;
143
+ body: {
144
+ accepted: false;
145
+ reason: string;
146
+ };
147
+ } | {
148
+ status: 401;
149
+ body: {
150
+ accepted: false;
151
+ reason: string;
152
+ };
153
+ } | {
154
+ status: 410;
155
+ body: {
156
+ accepted: false;
157
+ reason: string;
158
+ };
159
+ } | {
160
+ status: 422;
161
+ body: {
162
+ accepted: false;
163
+ reason: string;
164
+ };
165
+ };
166
+ export declare class QuoteChannelHandler {
167
+ private readonly kernelAddressByChainId;
168
+ private readonly dedupStore;
169
+ private readonly ttlGraceSeconds;
170
+ private readonly quoteVerifier;
171
+ private readonly counterVerifier;
172
+ constructor(cfg: QuoteChannelHandlerConfig);
173
+ /**
174
+ * Validate + dedup an incoming POST.
175
+ * Caller is responsible for: parsing URL path into `pathChainId` /
176
+ * `pathTxId`, parsing request body into `ChannelPayload`, and rate
177
+ * limiting the endpoint at the framework level.
178
+ */
179
+ handle(payload: unknown, ctx: HandlerContext): Promise<HandlerResult>;
180
+ }
181
+ /**
182
+ * Reject peer URLs that could SSRF into local / internal infrastructure.
183
+ *
184
+ * Rules (default, `allowInsecureTargets=false`):
185
+ * - scheme MUST be https
186
+ * - hostname MUST NOT be `localhost`
187
+ * - hostname MUST NOT be a literal loopback IP (127.x.x.x, ::1)
188
+ * - hostname MUST NOT be a literal link-local IP (169.254.x.x, fe80::/10)
189
+ * — this also covers AWS metadata at 169.254.169.254
190
+ * - hostname MUST NOT be a literal RFC1918 private IP (10.x, 172.16-31.x,
191
+ * 192.168.x) or IPv6 ULA (fc00::/7)
192
+ *
193
+ * Dev mode (`allowInsecureTargets=true`): no restrictions, callers
194
+ * opting in are responsible for their own network security.
195
+ *
196
+ * @throws Error if the URL fails the checks. Error message is deliberately
197
+ * specific so test fixtures and diagnostics can assert on it.
198
+ * @internal Exported for unit tests.
199
+ */
200
+ export declare function assertSafePeerUrl(url: string, allowInsecureTargets: boolean): void;
201
+ //# sourceMappingURL=QuoteChannel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuoteChannel.d.ts","sourceRoot":"","sources":["../../src/transport/QuoteChannel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,358 @@
1
+ "use strict";
2
+ /**
3
+ * QuoteChannel — HTTPS transport for AIP-2.1 quote + counter-offer messages.
4
+ *
5
+ * Split into three responsibilities so the SDK is framework-agnostic:
6
+ *
7
+ * 1. `QuoteChannelClient` — sends a signed message to a peer's endpoint.
8
+ * Used by buyers (posting counter-offers to the provider) and by
9
+ * providers (posting quotes to the buyer). Plain fetch + timeout.
10
+ *
11
+ * 2. `QuoteChannelHandler` — framework-agnostic receive-side handler.
12
+ * Callers wire it into whatever HTTP framework they use (Express,
13
+ * Next.js route handler, Fastify, etc). Enforces the security model
14
+ * from AIP-2.1-DRAFT §8:
15
+ * - URL path binding: `/quote-channel/{chainId}/{txId}` must
16
+ * match message.chainId / message.txId (closes T2 + T5).
17
+ * - EIP-712 signature verification (closes "anyone can POST").
18
+ * - TTL + grace window (closes T3).
19
+ * - Nonce LRU dedup (closes T1, idempotent replay).
20
+ * Rate limiting is intentionally out of scope — framework-level
21
+ * concern (Next.js middleware, Express rate-limit, nginx, etc).
22
+ *
23
+ * 3. `DedupStore` — swappable backing for the nonce LRU. In-memory
24
+ * default for single-process use; callers can plug Redis etc. for
25
+ * multi-worker production.
26
+ *
27
+ * @module transport/QuoteChannel
28
+ * @see Protocol/aips/AIP-2.1-DRAFT.md §8 (threat model + mitigations)
29
+ */
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.assertSafePeerUrl = exports.QuoteChannelHandler = exports.QuoteChannelClient = exports.InMemoryDedupStore = exports.DEDUP_TTL_SECONDS = exports.TTL_GRACE_SECONDS = exports.buildChannelPath = void 0;
32
+ const QuoteBuilder_1 = require("../builders/QuoteBuilder");
33
+ const CounterOfferBuilder_1 = require("../builders/CounterOfferBuilder");
34
+ const NonceManager_1 = require("../utils/NonceManager");
35
+ const ethers_1 = require("ethers");
36
+ // ============================================================================
37
+ // Constants (exported for tests + callers that want to align)
38
+ // ============================================================================
39
+ /** Path pattern builders use / handlers expect. */
40
+ function buildChannelPath(chainId, txId) {
41
+ return `/quote-channel/${chainId}/${txId}`;
42
+ }
43
+ exports.buildChannelPath = buildChannelPath;
44
+ exports.TTL_GRACE_SECONDS = 30;
45
+ exports.DEDUP_TTL_SECONDS = 90000; // 25h (covers max quote TTL + grace)
46
+ /**
47
+ * Single-process in-memory LRU. Callers replace this in production
48
+ * with a distributed store (Redis SET NX EX, DynamoDB conditional put,
49
+ * Postgres INSERT ... ON CONFLICT DO NOTHING, etc).
50
+ *
51
+ * Atomicity here is free because JavaScript event-loop execution is
52
+ * single-threaded — a `recordOnce` call cannot be interrupted mid-way.
53
+ * Multi-worker deployments MUST use a real distributed store or will
54
+ * see duplicate 'recorded' returns across workers.
55
+ */
56
+ class InMemoryDedupStore {
57
+ constructor(maxSize = 10000) {
58
+ this.entries = new Map(); // key → expires_at_ms
59
+ this.maxSize = maxSize;
60
+ }
61
+ async recordOnce(key, ttlMs) {
62
+ this.dropExpired();
63
+ const now = Date.now();
64
+ const exp = this.entries.get(key);
65
+ if (exp !== undefined && exp > now) {
66
+ return 'duplicate';
67
+ }
68
+ // Atomic in single-threaded JS — the check above and this set happen
69
+ // without any await in between, so no other task can interleave.
70
+ this.entries.set(key, now + ttlMs);
71
+ // Trim AFTER the set so `size <= maxSize` is an invariant. Trimming
72
+ // before the set would still leave the map at maxSize+1 after set.
73
+ this.trimToSize();
74
+ return 'recorded';
75
+ }
76
+ /** Drop entries whose TTL has elapsed. O(n) — fine for our sizes. */
77
+ dropExpired() {
78
+ const now = Date.now();
79
+ for (const [k, exp] of this.entries) {
80
+ if (exp <= now)
81
+ this.entries.delete(k);
82
+ }
83
+ }
84
+ /** Bound the map at `maxSize` by evicting oldest-inserted keys. */
85
+ trimToSize() {
86
+ while (this.entries.size > this.maxSize) {
87
+ const firstKey = this.entries.keys().next().value;
88
+ if (firstKey === undefined)
89
+ break;
90
+ this.entries.delete(firstKey);
91
+ }
92
+ }
93
+ }
94
+ exports.InMemoryDedupStore = InMemoryDedupStore;
95
+ class QuoteChannelClient {
96
+ constructor(cfg = {}) {
97
+ this.timeoutMs = cfg.timeoutMs ?? 10000;
98
+ this.fetchImpl = cfg.fetchImpl ?? fetch;
99
+ this.allowInsecureTargets = cfg.allowInsecureTargets ?? false;
100
+ }
101
+ /** POST a provider quote to the buyer's endpoint. */
102
+ async sendQuote(peerEndpoint, quote) {
103
+ await this.post(peerEndpoint, quote.chainId, quote.txId, {
104
+ type: 'agirails.quote.v1',
105
+ message: quote,
106
+ });
107
+ }
108
+ /** POST a buyer counter-offer to the provider's endpoint. */
109
+ async sendCounter(peerEndpoint, counter) {
110
+ await this.post(peerEndpoint, counter.chainId, counter.txId, {
111
+ type: 'agirails.counteroffer.v1',
112
+ message: counter,
113
+ });
114
+ }
115
+ async post(peerEndpoint, chainId, txId, payload) {
116
+ const url = `${stripTrailingSlash(peerEndpoint)}${buildChannelPath(chainId, txId)}`;
117
+ // SSRF guard. Peer endpoints come from on-chain AgentRegistry / the
118
+ // agirails.app DB — both technically writable by an adversary. A
119
+ // malicious endpoint pointing at http://169.254.169.254/ (AWS metadata),
120
+ // http://localhost:8080 (internal service), or http://10.x.x.x (RFC1918
121
+ // internal) would have the client leak signed payloads inside the
122
+ // deployer's infrastructure. Fail fast in `new URL()` + string checks.
123
+ assertSafePeerUrl(url, this.allowInsecureTargets);
124
+ const controller = new AbortController();
125
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
126
+ try {
127
+ const res = await this.fetchImpl(url, {
128
+ method: 'POST',
129
+ headers: { 'Content-Type': 'application/json' },
130
+ body: JSON.stringify(payload),
131
+ signal: controller.signal,
132
+ });
133
+ if (!res.ok) {
134
+ const text = await res.text().catch(() => '');
135
+ throw new Error(`Quote channel POST failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : ''}`);
136
+ }
137
+ }
138
+ finally {
139
+ clearTimeout(timer);
140
+ }
141
+ }
142
+ }
143
+ exports.QuoteChannelClient = QuoteChannelClient;
144
+ class QuoteChannelHandler {
145
+ constructor(cfg) {
146
+ this.kernelAddressByChainId = cfg.kernelAddressByChainId;
147
+ this.dedupStore = cfg.dedupStore ?? new InMemoryDedupStore();
148
+ this.ttlGraceSeconds = cfg.ttlGraceSeconds ?? exports.TTL_GRACE_SECONDS;
149
+ const throwawayWallet = ethers_1.Wallet.createRandom();
150
+ const throwawayNonces = new NonceManager_1.InMemoryNonceManager();
151
+ this.quoteVerifier = new QuoteBuilder_1.QuoteBuilder(throwawayWallet, throwawayNonces);
152
+ this.counterVerifier = new CounterOfferBuilder_1.CounterOfferBuilder(throwawayWallet, throwawayNonces);
153
+ }
154
+ /**
155
+ * Validate + dedup an incoming POST.
156
+ * Caller is responsible for: parsing URL path into `pathChainId` /
157
+ * `pathTxId`, parsing request body into `ChannelPayload`, and rate
158
+ * limiting the endpoint at the framework level.
159
+ */
160
+ async handle(payload, ctx) {
161
+ // 1. Shape check on the payload wrapper.
162
+ if (!isChannelPayload(payload)) {
163
+ return {
164
+ status: 400,
165
+ body: { accepted: false, reason: 'Invalid payload shape' },
166
+ };
167
+ }
168
+ // 2. Path binding — the URL path chainId/txId MUST match the inner message.
169
+ if (payload.message.chainId !== ctx.pathChainId) {
170
+ return {
171
+ status: 400,
172
+ body: { accepted: false, reason: 'chainId mismatch between URL and message' },
173
+ };
174
+ }
175
+ if (payload.message.txId.toLowerCase() !== ctx.pathTxId.toLowerCase()) {
176
+ return {
177
+ status: 400,
178
+ body: { accepted: false, reason: 'txId mismatch between URL and message' },
179
+ };
180
+ }
181
+ // 3. Kernel address must be configured for this chain.
182
+ const kernelAddress = this.kernelAddressByChainId[payload.message.chainId];
183
+ if (!kernelAddress) {
184
+ return {
185
+ status: 400,
186
+ body: { accepted: false, reason: `Unsupported chainId: ${payload.message.chainId}` },
187
+ };
188
+ }
189
+ // 4. TTL + grace. Check expiry BEFORE signature to fast-reject stale traffic
190
+ // cheaply; signature verification is the expensive step.
191
+ const now = Math.floor(Date.now() / 1000);
192
+ if (payload.message.expiresAt + this.ttlGraceSeconds < now) {
193
+ return {
194
+ status: 410,
195
+ body: { accepted: false, reason: 'Message expired' },
196
+ };
197
+ }
198
+ // 5. Signature verification + business rules (delegated to the builder).
199
+ try {
200
+ if (payload.type === 'agirails.quote.v1') {
201
+ await this.quoteVerifier.verify(payload.message, kernelAddress);
202
+ }
203
+ else {
204
+ await this.counterVerifier.verify(payload.message, kernelAddress);
205
+ }
206
+ }
207
+ catch (err) {
208
+ const reason = err instanceof Error ? err.message : String(err);
209
+ // Signature failures vs schema/band failures are both client errors;
210
+ // distinguish with 401 (auth) vs 422 (validation) for better diagnostics.
211
+ const isAuth = /signature|Invalid signature|recovered/i.test(reason);
212
+ return {
213
+ status: isAuth ? 401 : 422,
214
+ body: { accepted: false, reason },
215
+ };
216
+ }
217
+ // 6. Dedup via nonce LRU. Key is (type, signerDID, nonce) — uniquely
218
+ // identifies a signed message within its issuing agent's nonce space.
219
+ // Single atomic recordOnce() call (not check-then-record) so concurrent
220
+ // workers competing on the same key see exactly one 'recorded'; the
221
+ // rest see 'duplicate' and return the idempotent cached response.
222
+ const signerDID = payload.type === 'agirails.quote.v1'
223
+ ? payload.message.provider
224
+ : payload.message.consumer;
225
+ const dedupKey = `${payload.type}:${signerDID}:${payload.message.nonce}`;
226
+ const outcome = await this.dedupStore.recordOnce(dedupKey, exports.DEDUP_TTL_SECONDS * 1000);
227
+ if (outcome === 'duplicate') {
228
+ return { status: 200, body: { accepted: true, duplicate: true } };
229
+ }
230
+ return { status: 201, body: { accepted: true, duplicate: false } };
231
+ }
232
+ }
233
+ exports.QuoteChannelHandler = QuoteChannelHandler;
234
+ // ============================================================================
235
+ // Helpers
236
+ // ============================================================================
237
+ function stripTrailingSlash(url) {
238
+ return url.endsWith('/') ? url.slice(0, -1) : url;
239
+ }
240
+ /**
241
+ * Reject peer URLs that could SSRF into local / internal infrastructure.
242
+ *
243
+ * Rules (default, `allowInsecureTargets=false`):
244
+ * - scheme MUST be https
245
+ * - hostname MUST NOT be `localhost`
246
+ * - hostname MUST NOT be a literal loopback IP (127.x.x.x, ::1)
247
+ * - hostname MUST NOT be a literal link-local IP (169.254.x.x, fe80::/10)
248
+ * — this also covers AWS metadata at 169.254.169.254
249
+ * - hostname MUST NOT be a literal RFC1918 private IP (10.x, 172.16-31.x,
250
+ * 192.168.x) or IPv6 ULA (fc00::/7)
251
+ *
252
+ * Dev mode (`allowInsecureTargets=true`): no restrictions, callers
253
+ * opting in are responsible for their own network security.
254
+ *
255
+ * @throws Error if the URL fails the checks. Error message is deliberately
256
+ * specific so test fixtures and diagnostics can assert on it.
257
+ * @internal Exported for unit tests.
258
+ */
259
+ function assertSafePeerUrl(url, allowInsecureTargets) {
260
+ let parsed;
261
+ try {
262
+ parsed = new URL(url);
263
+ }
264
+ catch {
265
+ throw new Error(`Invalid peer URL: ${url}`);
266
+ }
267
+ if (allowInsecureTargets)
268
+ return;
269
+ if (parsed.protocol !== 'https:') {
270
+ throw new Error(`Peer URL must use https:// (got ${parsed.protocol}//). ` +
271
+ `Set allowInsecureTargets=true on the QuoteChannelClient for dev/test only.`);
272
+ }
273
+ // Node's URL() keeps brackets around IPv6 hosts. Strip them so the
274
+ // downstream string checks work uniformly for IPv4 and IPv6 literals.
275
+ const rawHost = parsed.hostname.toLowerCase();
276
+ const stripped = rawHost.startsWith('[') && rawHost.endsWith(']')
277
+ ? rawHost.slice(1, -1)
278
+ : rawHost;
279
+ // IPv4-mapped IPv6 — the OS resolves this to the corresponding IPv4
280
+ // address, so the same loopback / RFC1918 / link-local rules MUST apply.
281
+ // Two normalized shapes can come out of Node's URL():
282
+ // 1. dotted-quad: `::ffff:127.0.0.1` (rare, some Node versions)
283
+ // 2. hex pair: `::ffff:7f00:1` (Node default — folds the v4 octets)
284
+ // Without this re-extraction, an attacker crafts `[::ffff:127.0.0.1]`
285
+ // or `[::ffff:169.254.169.254]` and bypasses the IPv4 checks below
286
+ // (which only match dotted-quad).
287
+ let host = stripped;
288
+ const mappedDotted = stripped.match(/^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/);
289
+ const mappedHex = stripped.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
290
+ if (mappedDotted) {
291
+ host = mappedDotted[1];
292
+ }
293
+ else if (mappedHex) {
294
+ const hi = parseInt(mappedHex[1], 16);
295
+ const lo = parseInt(mappedHex[2], 16);
296
+ host = `${(hi >> 8) & 0xff}.${hi & 0xff}.${(lo >> 8) & 0xff}.${lo & 0xff}`;
297
+ }
298
+ if (host === 'localhost' || host.endsWith('.localhost')) {
299
+ throw new Error(`Peer URL points at localhost (${host}) — refusing (SSRF guard)`);
300
+ }
301
+ // IPv4 literals
302
+ const ipv4 = host.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
303
+ if (ipv4) {
304
+ const [, a, b] = ipv4.map(Number);
305
+ if (a === 127) {
306
+ throw new Error(`Peer URL points at loopback IP (${host}) — refusing (SSRF guard)`);
307
+ }
308
+ if (a === 169 && b === 254) {
309
+ throw new Error(`Peer URL points at link-local / cloud-metadata IP (${host}) — refusing (SSRF guard)`);
310
+ }
311
+ if (a === 10) {
312
+ throw new Error(`Peer URL points at RFC1918 10.x.x.x (${host}) — refusing (SSRF guard)`);
313
+ }
314
+ if (a === 192 && b === 168) {
315
+ throw new Error(`Peer URL points at RFC1918 192.168.x.x (${host}) — refusing (SSRF guard)`);
316
+ }
317
+ if (a === 172 && b >= 16 && b <= 31) {
318
+ throw new Error(`Peer URL points at RFC1918 172.16-31.x (${host}) — refusing (SSRF guard)`);
319
+ }
320
+ }
321
+ // IPv6 literals — URL() wraps with brackets; the hostname getter strips them.
322
+ if (host === '::1') {
323
+ throw new Error(`Peer URL points at IPv6 loopback (${host}) — refusing (SSRF guard)`);
324
+ }
325
+ if (host.startsWith('fe80:') || host.startsWith('fe80::')) {
326
+ throw new Error(`Peer URL points at IPv6 link-local (${host}) — refusing (SSRF guard)`);
327
+ }
328
+ // IPv6 ULA fc00::/7 → high byte starts with 0xfc or 0xfd.
329
+ if (host.startsWith('fc') || host.startsWith('fd')) {
330
+ // Narrow to the fc00::/7 pattern: fc?? or fd?? as the first group.
331
+ if (/^(fc|fd)[0-9a-f]{0,2}:/.test(host)) {
332
+ throw new Error(`Peer URL points at IPv6 ULA (${host}) — refusing (SSRF guard)`);
333
+ }
334
+ }
335
+ }
336
+ exports.assertSafePeerUrl = assertSafePeerUrl;
337
+ function isChannelPayload(x) {
338
+ if (!x || typeof x !== 'object')
339
+ return false;
340
+ const p = x;
341
+ if (p.type !== 'agirails.quote.v1' && p.type !== 'agirails.counteroffer.v1')
342
+ return false;
343
+ if (!p.message || typeof p.message !== 'object')
344
+ return false;
345
+ const msg = p.message;
346
+ if (typeof msg.chainId !== 'number')
347
+ return false;
348
+ if (typeof msg.txId !== 'string')
349
+ return false;
350
+ if (typeof msg.nonce !== 'number')
351
+ return false;
352
+ if (typeof msg.expiresAt !== 'number')
353
+ return false;
354
+ if (typeof msg.signature !== 'string')
355
+ return false;
356
+ return true;
357
+ }
358
+ //# sourceMappingURL=QuoteChannel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuoteChannel.js","sourceRoot":"","sources":["../../src/transport/QuoteChannel.ts"],"names":[],"mappings":"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"}