m2m-sentinel-sdk 1.0.2 → 1.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.
Files changed (42) hide show
  1. package/README.md +71 -43
  2. package/agent_adapter.js +30 -0
  3. package/eliza_plugin.js +84 -0
  4. package/index.d.ts +94 -0
  5. package/index.js +183 -26
  6. package/m2m_sentinel_sdk.js +1 -12
  7. package/mcp_server.js +223 -0
  8. package/package.json +24 -10
  9. package/typescript/index.ts +204 -49
  10. package/typescript/package.json +6 -5
  11. package/cli/m2m_cli.js +0 -25
  12. package/csharp/M2MSentinel.csproj +0 -9
  13. package/csharp/M2MSentinelClient.cs +0 -67
  14. package/go/client.go +0 -94
  15. package/go/go.mod +0 -3
  16. package/java/M2MSentinelClient.java +0 -67
  17. package/java/pom.xml +0 -20
  18. package/java/src/main/java/com/m2msentinel/M2MSentinelClient.java +0 -69
  19. package/kotlin/M2MSentinelClient.kt +0 -59
  20. package/kotlin/build.gradle.kts +0 -24
  21. package/m2m_sentinel/__init__.py +0 -3
  22. package/m2m_sentinel/client.py +0 -16
  23. package/m2m_sentinel_sdk.py +0 -9
  24. package/php/M2MSentinelClient.php +0 -52
  25. package/php/composer.json +0 -20
  26. package/python/dist/m2m_sentinel-0.1.0-py3-none-any.whl +0 -0
  27. package/python/dist/m2m_sentinel-0.1.0.tar.gz +0 -0
  28. package/python/dist/m2m_sentinel-0.1.1-py3-none-any.whl +0 -0
  29. package/python/dist/m2m_sentinel-0.1.1.tar.gz +0 -0
  30. package/python/m2m_sentinel/__pycache__/client.cpython-314.pyc +0 -0
  31. package/python/m2m_sentinel/client.py +0 -40
  32. package/python/m2m_sentinel.egg-info/PKG-INFO +0 -35
  33. package/python/m2m_sentinel.egg-info/SOURCES.txt +0 -5
  34. package/python/m2m_sentinel.egg-info/dependency_links.txt +0 -1
  35. package/python/m2m_sentinel.egg-info/top_level.txt +0 -1
  36. package/python/setup.py +0 -35
  37. package/ruby/m2m_sentinel.gemspec +0 -17
  38. package/ruby/m2m_sentinel.rb +0 -55
  39. package/rust/Cargo.toml +0 -11
  40. package/rust/src/lib.rs +0 -61
  41. package/swift/M2MSentinelClient.swift +0 -65
  42. package/swift/Package.swift +0 -23
package/README.md CHANGED
@@ -1,43 +1,71 @@
1
- # m2m-sentinel-sdk
2
-
3
- > Official Client SDK for M2M Sentinel: Autonomous Machine-to-Machine Web3 Security & AI Intelligence Engine.
4
-
5
- [![npm version](https://img.shields.io/npm/v/m2m-sentinel-sdk.svg)](https://www.npmjs.com/package/m2m-sentinel-sdk)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
-
8
- ## Installation
9
-
10
- ```bash
11
- npm install m2m-sentinel-sdk
12
- ```
13
-
14
- ## Quickstart
15
-
16
- ```javascript
17
- const { M2MSentinelClient } = require('m2m-sentinel-sdk');
18
-
19
- const client = new M2MSentinelClient();
20
-
21
- async function run() {
22
- // Audit a Smart Contract
23
- const audit = await client.auditContract('0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913');
24
- console.log('Contract Risk Audit:', audit);
25
-
26
- // Fetch Real-Time Whale Transfer Alerts
27
- const whales = await client.getWhaleSignals();
28
- console.log('Whale Signals:', whales);
29
- }
30
-
31
- run();
32
- ```
33
-
34
- ## Features
35
- - **Smart Contract Risk Auditor** (`/v1/audit/:address`)
36
- - **Real-Time Whale Signals** (`/v1/whales/signals`)
37
- - **DEX Liquidity Depth & Spreads** (`/v1/dex/metrics`)
38
- - **AI Web Search Intelligence** (`/v1/search`)
39
-
40
- ## Documentation & Portals
41
- - **Live Portal**: [https://m2msentinel.vercel.app](https://m2msentinel.vercel.app)
42
- - **API Docs**: [https://m2msentinel.vercel.app/docs.html](https://m2msentinel.vercel.app/docs.html)
43
- - **OpenAPI Spec**: [https://m2msentinel.vercel.app/openapi.json](https://m2msentinel.vercel.app/openapi.json)
1
+ # M2M Sentinel SDKs
2
+
3
+ Base URL: `https://m2msentinel.vercel.app`.
4
+
5
+ M2M Sentinel reports selected static bytecode capabilities, common proxy
6
+ structures, evidence quality, and sourced Base market observations. It does not
7
+ classify a contract as safe, malicious, or exploitable. Transaction middleware
8
+ therefore requires a caller-defined policy and has no built-in allow threshold.
9
+
10
+ ## Support status
11
+
12
+ Supported here: JavaScript/Node (`index.js`), TypeScript source, Python source,
13
+ and the MCP stdio server. Other language folders are community previews and
14
+ must be checked against `/openapi.json` before production use.
15
+
16
+ ## Install this source build
17
+
18
+ ```bash
19
+ node -e "const { M2MSentinelClient } = require('./public/sdk')"
20
+ python -m pip install ./public/sdk/python
21
+ ```
22
+
23
+ Registry publishing is intentionally separate from the production API.
24
+ The repository does not claim that these source-installable packages are available from every language registry.
25
+
26
+ ## Authentication and x402
27
+
28
+ Send API keys only in `x-api-key` (or `Authorization: Bearer`). Query-string
29
+ credentials are rejected. Payable routes also support x402 v2. An unpaid call
30
+ returns a base64 `PAYMENT-REQUIRED` challenge when the facilitator is ready; a
31
+ successful settlement returns `PAYMENT-RESPONSE`.
32
+
33
+ ## Public routes
34
+
35
+ - `GET /v1/status`
36
+ - `GET /v1/stats` (public counter-free privacy envelope; exact aggregate
37
+ commercial counters are operator-only)
38
+ - `GET /v1/plans`
39
+ - `GET /v1/demo/audit/:address` for the published sample allowlist
40
+ - free-key and subscription onboarding routes
41
+
42
+ The JavaScript, TypeScript, and Python clients expose multi-period
43
+ purchase/renewal and wallet recovery without requiring callers to hand-build
44
+ requests:
45
+
46
+ ```js
47
+ await client.createSubscriptionIntent('GROWTH', wallet, {
48
+ durationDays: 90,
49
+ renewExistingKey: true,
50
+ apiKey: existingPaidKey
51
+ });
52
+ const challenge = await client.createRecoveryChallenge(wallet, { txHash });
53
+ await client.claimRecoveredKey(challenge.intent.id, walletSignature);
54
+ ```
55
+
56
+ `getPublicStats()` returns the counter-free privacy envelope. Operator-only
57
+ detail has a deliberately separate `getOperatorAggregateStats(days, token)`
58
+ method and sends the operator token only as `Authorization: Bearer`. This is
59
+ for private server/operator tooling only: never embed or bundle the operator
60
+ token in browser, mobile, SDK-distribution, or customer code.
61
+
62
+ ## Protected routes
63
+
64
+ - `GET /v1/audit/:address`
65
+ - `GET /v1/security/score/:address` (legacy URL; returns a capability coverage index)
66
+ - Base gas, DEX, token-price, and large-transfer observation routes
67
+ - key self-service routes
68
+
69
+ All live-data responses preserve provenance. Capability responses include
70
+ `notASafetyGuarantee: true`, a limitations array, `capabilityRating`, and an
71
+ evidence-grade flag. Do not replace `UNVERIFIED` or `null` with a default.
@@ -0,0 +1,30 @@
1
+ // UNSUPPORTED / COMMUNITY PREVIEW — not covered by the supported-SDK guarantee; verify against /openapi.json before production use.
2
+
3
+ const { M2MSentinelClient } = require('./index.js');
4
+
5
+ class M2MSentinelAgentTool {
6
+ constructor(options = {}) {
7
+ this.name = 'm2m_sentinel_capability_analysis';
8
+ this.description = 'Reports selected static bytecode capabilities, common proxy structures, provenance, and limitations for Base contracts.';
9
+ this.client = new M2MSentinelClient({
10
+ apiKey: options.apiKey || process.env.M2M_SENTINEL_API_KEY,
11
+ baseUrl: options.baseUrl
12
+ });
13
+ }
14
+
15
+ async _call(address) {
16
+ try {
17
+ return await this.client.auditContract(address);
18
+ } catch (err) {
19
+ return {
20
+ error: err.name || 'M2MSentinelError',
21
+ message: err.message,
22
+ status: err.status,
23
+ paymentRequired: err.paymentRequired || null,
24
+ retryAfter: err.retryAfter || null
25
+ };
26
+ }
27
+ }
28
+ }
29
+
30
+ module.exports = { M2MSentinelAgentTool };
@@ -0,0 +1,84 @@
1
+ // UNSUPPORTED / COMMUNITY PREVIEW — not covered by the supported-SDK guarantee; verify against /openapi.json before production use.
2
+
3
+ const { M2MSentinelClient } = require('./index.js');
4
+
5
+ function clientFor(runtime) {
6
+ const apiKey = runtime?.getSetting?.('M2M_SENTINEL_API_KEY') || process.env.M2M_SENTINEL_API_KEY;
7
+ return new M2MSentinelClient({ apiKey });
8
+ }
9
+
10
+ function errorText(err) {
11
+ if (err.status === 402) return 'M2M Sentinel payment required. Configure M2M_SENTINEL_API_KEY or settle the x402 challenge.';
12
+ if (err.status === 503 && err.body?.error === 'DATA_SOURCE_UNAVAILABLE') return 'M2M Sentinel data source unavailable; no estimated fallback value is returned.';
13
+ if (err.status === 429) return 'M2M Sentinel rate limited. Retry-After: ' + (err.retryAfter || 'not provided');
14
+ return 'M2M Sentinel API request failed: ' + err.message;
15
+ }
16
+
17
+ const m2mSentinelPlugin = {
18
+ name: '@elizaos/plugin-m2m-sentinel',
19
+ description: 'Preview ElizaOS actions for M2M Sentinel protected Web3 API routes.',
20
+ actions: [
21
+ {
22
+ name: 'AUDIT_CONTRACT',
23
+ description: 'Reports selected static capability and proxy observations for a Base contract.',
24
+ handler: async (runtime, message) => {
25
+ const address = message.content.text.match(/0x[a-fA-F0-9]{40}/)?.[0];
26
+ if (!address) return { text: 'Please provide a valid 42-character EVM contract address.' };
27
+ try {
28
+ const data = await clientFor(runtime).auditContract(address);
29
+ const audit = data.audit || {};
30
+ const proxy = audit.proxyResolution || {};
31
+ return { text: `M2M Sentinel capability analysis for ${address}: ${audit.capabilityRating || 'UNVERIFIED'}; proxy=${proxy.isProxy ? proxy.proxyType : 'NO'}; notASafetyGuarantee=true; provenance=${JSON.stringify(audit.provenance || data.provenance || null)}` };
32
+ } catch (err) { return { text: errorText(err) }; }
33
+ }
34
+ },
35
+ {
36
+ name: 'GET_CAPABILITY_SCORE',
37
+ description: 'Returns the static capability coverage index for a Base contract. The index is not a safety score.',
38
+ handler: async (runtime, message) => {
39
+ const address = message.content.text.match(/0x[a-fA-F0-9]{40}/)?.[0];
40
+ if (!address) return { text: 'Please provide a valid EVM contract address.' };
41
+ try {
42
+ const data = await clientFor(runtime).getCapabilityScore(address);
43
+ const score = data.capabilityScore === null ? 'unverified' : `${data.capabilityScore}/100 capability coverage`;
44
+ return { text: `M2M Sentinel static index for ${address}: ${score}; not a safety score. Provenance: ${JSON.stringify(data.provenance || null)}` };
45
+ } catch (err) { return { text: errorText(err) }; }
46
+ }
47
+ },
48
+ {
49
+ name: 'GET_DEX_METRICS',
50
+ description: 'Fetches Base DEX metrics.',
51
+ handler: async (runtime) => {
52
+ try { return { text: JSON.stringify(await clientFor(runtime).getDexMetrics()) }; }
53
+ catch (err) { return { text: errorText(err) }; }
54
+ }
55
+ },
56
+ {
57
+ name: 'GET_TOKEN_PRICE',
58
+ description: 'Queries a tracked Base token price.',
59
+ handler: async (runtime, message) => {
60
+ const symbol = (message.content.text.match(/price of ([a-zA-Z0-9]+)/i)?.[1] || 'USDC').toUpperCase();
61
+ try { return { text: JSON.stringify(await clientFor(runtime).getTokenPrice(symbol)) }; }
62
+ catch (err) { return { text: errorText(err) }; }
63
+ }
64
+ },
65
+ {
66
+ name: 'GET_GAS_FEES',
67
+ description: 'Fetches live Base gas fees.',
68
+ handler: async (runtime) => {
69
+ try { return { text: JSON.stringify(await clientFor(runtime).getGasFees()) }; }
70
+ catch (err) { return { text: errorText(err) }; }
71
+ }
72
+ },
73
+ {
74
+ name: 'GET_WHALE_SIGNALS',
75
+ description: 'Fetches whale signals.',
76
+ handler: async (runtime) => {
77
+ try { return { text: JSON.stringify(await clientFor(runtime).getWhaleSignals()) }; }
78
+ catch (err) { return { text: errorText(err) }; }
79
+ }
80
+ }
81
+ ]
82
+ };
83
+
84
+ module.exports = { m2mSentinelPlugin };
package/index.d.ts ADDED
@@ -0,0 +1,94 @@
1
+ export interface M2MSentinelClientOptions {
2
+ apiKey?: string;
3
+ baseUrl?: string;
4
+ timeoutMs?: number;
5
+ paymentSignature?: string;
6
+ }
7
+
8
+ export interface CreateSubscriptionIntentOptions extends M2MSentinelClientOptions {
9
+ durationDays?: 31 | 90 | 365;
10
+ renewExistingKey?: boolean;
11
+ }
12
+
13
+ export interface RecoveryChallengeOptions {
14
+ txHash?: string;
15
+ }
16
+
17
+ export interface M2MSentinelErrorOptions {
18
+ status?: number;
19
+ body?: unknown;
20
+ retryAfter?: string | null;
21
+ paymentRequired?: unknown;
22
+ paymentResponse?: unknown;
23
+ }
24
+
25
+ export class M2MSentinelError extends Error {
26
+ readonly status?: number;
27
+ readonly body?: unknown;
28
+ readonly retryAfter?: string | null;
29
+ readonly paymentRequired?: unknown;
30
+ readonly paymentResponse?: unknown;
31
+ constructor(message: string, options?: M2MSentinelErrorOptions);
32
+ }
33
+
34
+ export class PaymentRequiredError extends M2MSentinelError {}
35
+ export class RateLimitedError extends M2MSentinelError {}
36
+ export class DataSourceUnavailableError extends M2MSentinelError {}
37
+
38
+ export class M2MSentinelClient {
39
+ constructor(options?: M2MSentinelClientOptions);
40
+ constructor(apiKey?: string, baseUrl?: string);
41
+ request(method: string, path: string, body?: unknown, options?: M2MSentinelClientOptions): Promise<any>;
42
+ getStatus(): Promise<any>;
43
+ getPublicStats(days?: number): Promise<any>;
44
+ /** Backward-compatible alias for getPublicStats; customer keys never reveal operator counters. */
45
+ getAggregateStats(days?: number): Promise<any>;
46
+ getOperatorAggregateStats(days: number, operatorToken: string): Promise<any>;
47
+ getPlans(): Promise<any>;
48
+ demoAudit(address: string): Promise<any>;
49
+ createFreeChallenge(userWallet: string): Promise<any>;
50
+ claimFreeTier(intentId: string, signature: string): Promise<any>;
51
+ createSubscriptionIntent(tier: string, userWallet: string, options?: CreateSubscriptionIntentOptions): Promise<any>;
52
+ claimSubscription(intentId: string, signature: string, txHash: string): Promise<any>;
53
+ createRecoveryChallenge(userWallet: string, options?: RecoveryChallengeOptions): Promise<any>;
54
+ claimRecoveredKey(intentId: string, signature: string): Promise<any>;
55
+ auditContract(address: string, options?: M2MSentinelClientOptions): Promise<any>;
56
+ getCapabilityScore(address: string, options?: M2MSentinelClientOptions): Promise<any>;
57
+ /** Legacy alias. The response is a capability coverage index, not a safety score. */
58
+ getSecurityScore(address: string, options?: M2MSentinelClientOptions): Promise<any>;
59
+ getGasFees(options?: M2MSentinelClientOptions): Promise<any>;
60
+ getDexMetrics(options?: M2MSentinelClientOptions): Promise<any>;
61
+ getTokenPrice(symbol: string, options?: M2MSentinelClientOptions): Promise<any>;
62
+ getWhaleSignals(options?: M2MSentinelClientOptions): Promise<any>;
63
+ getKeySelf(): Promise<any>;
64
+ revokeKey(confirm?: boolean): Promise<any>;
65
+ }
66
+
67
+ export type SentinelPolicy = (
68
+ analysis: any,
69
+ context: { targetAddress: string; integration: string }
70
+ ) => boolean | { allow: boolean; reason?: string } | Promise<boolean | { allow: boolean; reason?: string }>;
71
+
72
+ export function enforceCallerPolicy(
73
+ policy: SentinelPolicy | undefined,
74
+ analysis: any,
75
+ context: { targetAddress: string; integration: string }
76
+ ): Promise<void>;
77
+
78
+ export function createEthersSentinelMiddleware(
79
+ apiKey?: string,
80
+ baseUrl?: string,
81
+ policy?: SentinelPolicy
82
+ ): {
83
+ client: M2MSentinelClient;
84
+ verifyContractBeforeTx(targetAddress: string, policyOverride?: SentinelPolicy): Promise<any>;
85
+ };
86
+
87
+ export function createViemSentinelInterceptor(
88
+ apiKey?: string,
89
+ baseUrl?: string,
90
+ policy?: SentinelPolicy
91
+ ): {
92
+ client: M2MSentinelClient;
93
+ inspectSwapTarget(address: string, policyOverride?: SentinelPolicy): Promise<any>;
94
+ };
package/index.js CHANGED
@@ -1,26 +1,183 @@
1
- const axios = require('axios');
2
-
3
- class M2MSentinelClient {
4
- constructor(apiKey, baseUrl = 'https://m2msentinel.vercel.app') {
5
- this.apiKey = apiKey;
6
- this.baseUrl = baseUrl.replace(/\/+$/, '');
7
- this.client = axios.create({
8
- baseURL: this.baseUrl,
9
- headers: {
10
- 'Authorization': `Bearer ${this.apiKey}`,
11
- 'Content-Type': 'application/json'
12
- }
13
- });
14
- }
15
-
16
- async getStatus() {
17
- try {
18
- const response = await this.client.get('/v1/status');
19
- return response.data;
20
- } catch (error) {
21
- throw error;
22
- }
23
- }
24
- }
25
-
26
- module.exports = { M2MSentinelClient };
1
+ const DEFAULT_BASE_URL = 'https://m2msentinel.vercel.app';
2
+ const DEFAULT_TIMEOUT_MS = 30000;
3
+
4
+ class M2MSentinelError extends Error {
5
+ constructor(message, options = {}) {
6
+ super(message);
7
+ this.name = this.constructor.name;
8
+ this.status = options.status;
9
+ this.body = options.body;
10
+ this.retryAfter = options.retryAfter || null;
11
+ this.paymentRequired = options.paymentRequired || null;
12
+ this.paymentResponse = options.paymentResponse || null;
13
+ }
14
+ }
15
+
16
+ class PaymentRequiredError extends M2MSentinelError {}
17
+ class RateLimitedError extends M2MSentinelError {}
18
+ class DataSourceUnavailableError extends M2MSentinelError {}
19
+
20
+ function parseX402Header(value) {
21
+ if (!value) return null;
22
+ // x402 v2 transports protocol objects as base64 JSON. Retain raw-JSON
23
+ // compatibility for older M2M Sentinel deployments during upgrades.
24
+ try { return JSON.parse(value); } catch (_) { /* try v2 encoding */ }
25
+ try {
26
+ const normalized = String(value).replace(/-/g, '+').replace(/_/g, '/');
27
+ const padded = normalized + '='.repeat((4 - normalized.length % 4) % 4);
28
+ return JSON.parse(Buffer.from(padded, 'base64').toString('utf8'));
29
+ } catch (_) {
30
+ return null;
31
+ }
32
+ }
33
+
34
+ function normalizeOptions(optionsOrApiKey, baseUrl) {
35
+ if (optionsOrApiKey && typeof optionsOrApiKey === 'object') return { ...optionsOrApiKey };
36
+ return { apiKey: optionsOrApiKey || undefined, baseUrl: baseUrl || DEFAULT_BASE_URL };
37
+ }
38
+
39
+ class M2MSentinelClient {
40
+ constructor(optionsOrApiKey, baseUrl) {
41
+ const options = normalizeOptions(optionsOrApiKey, baseUrl);
42
+ this.baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, '');
43
+ this.apiKey = options.apiKey || undefined;
44
+ this.timeoutMs = options.timeoutMs || DEFAULT_TIMEOUT_MS;
45
+ this.paymentSignature = options.paymentSignature || undefined;
46
+ }
47
+
48
+ async request(method, path, body, options = {}) {
49
+ const headers = {
50
+ 'Accept': 'application/json'
51
+ };
52
+ if (body !== undefined) headers['Content-Type'] = 'application/json';
53
+ const apiKey = options.apiKey || this.apiKey;
54
+ if (apiKey) headers['x-api-key'] = apiKey;
55
+ if (options.operatorToken) headers.Authorization = 'Bearer ' + options.operatorToken;
56
+ const paymentSignature = options.paymentSignature || this.paymentSignature;
57
+ if (paymentSignature) headers['PAYMENT-SIGNATURE'] = paymentSignature;
58
+
59
+ const controller = new AbortController();
60
+ const timeout = setTimeout(() => controller.abort(), options.timeoutMs || this.timeoutMs);
61
+ let response;
62
+ try {
63
+ response = await fetch(this.baseUrl + path, {
64
+ method,
65
+ headers,
66
+ body: body === undefined ? undefined : JSON.stringify(body),
67
+ signal: controller.signal
68
+ });
69
+ } catch (err) {
70
+ if (err && err.name === 'AbortError') throw new M2MSentinelError('M2M Sentinel request timed out', { status: 0 });
71
+ throw err;
72
+ } finally {
73
+ clearTimeout(timeout);
74
+ }
75
+
76
+ const text = await response.text();
77
+ let data = null;
78
+ if (text) {
79
+ try { data = JSON.parse(text); } catch (_) { data = { raw: text }; }
80
+ }
81
+
82
+ const paymentRequired = parseX402Header(response.headers.get('PAYMENT-REQUIRED'));
83
+ const paymentResponse = parseX402Header(response.headers.get('PAYMENT-RESPONSE')) || response.headers.get('PAYMENT-RESPONSE');
84
+ if (response.ok) return data;
85
+
86
+ const details = {
87
+ status: response.status,
88
+ body: data,
89
+ retryAfter: response.headers.get('Retry-After'),
90
+ paymentRequired,
91
+ paymentResponse
92
+ };
93
+ const message = data && data.message ? data.message : 'M2M Sentinel HTTP ' + response.status;
94
+ if (response.status === 402) throw new PaymentRequiredError(message, details);
95
+ if (response.status === 429) throw new RateLimitedError(message, details);
96
+ if (response.status === 503 && data && data.error === 'DATA_SOURCE_UNAVAILABLE') throw new DataSourceUnavailableError(message, details);
97
+ throw new M2MSentinelError(message, details);
98
+ }
99
+
100
+ getStatus() { return this.request('GET', '/v1/status'); }
101
+ getPublicStats(days = 30) { return this.request('GET', '/v1/stats?days=' + encodeURIComponent(days)); }
102
+ // Backward-compatible alias. Ordinary/customer credentials still receive
103
+ // only the counter-free public privacy envelope.
104
+ getAggregateStats(days = 30) { return this.getPublicStats(days); }
105
+ getOperatorAggregateStats(days, operatorToken) {
106
+ return this.request('GET', '/v1/stats?days=' + encodeURIComponent(days || 30), undefined, { operatorToken });
107
+ }
108
+ getPlans() { return this.request('GET', '/v1/plans'); }
109
+ demoAudit(address) { return this.request('GET', '/v1/demo/audit/' + encodeURIComponent(address)); }
110
+ createFreeChallenge(userWallet) { return this.request('POST', '/v1/subscribe/free/challenge', { userWallet }); }
111
+ claimFreeTier(intentId, signature) { return this.request('POST', '/v1/subscribe/free/claim', { intentId, signature }); }
112
+ createSubscriptionIntent(tier, userWallet, options = {}) {
113
+ const body = { tier, userWallet };
114
+ if (options.durationDays !== undefined) body.durationDays = options.durationDays;
115
+ if (options.renewExistingKey !== undefined) body.renewExistingKey = options.renewExistingKey;
116
+ return this.request('POST', '/v1/subscribe/intents', body, options);
117
+ }
118
+ claimSubscription(intentId, signature, txHash) { return this.request('POST', '/v1/subscribe/crypto', { intentId, signature, txHash }); }
119
+ createRecoveryChallenge(userWallet, options = {}) {
120
+ const body = { userWallet };
121
+ if (options.txHash !== undefined) body.txHash = options.txHash;
122
+ return this.request('POST', '/v1/keys/recovery/challenge', body);
123
+ }
124
+ claimRecoveredKey(intentId, signature) {
125
+ return this.request('POST', '/v1/keys/recovery/claim', { intentId, signature });
126
+ }
127
+ auditContract(address, options) { return this.request('GET', '/v1/audit/' + encodeURIComponent(address), undefined, options); }
128
+ getCapabilityScore(address, options) { return this.request('GET', '/v1/security/score/' + encodeURIComponent(address), undefined, options); }
129
+ // Legacy method name; the response is a capability coverage index, not a safety score.
130
+ getSecurityScore(address, options) { return this.request('GET', '/v1/security/score/' + encodeURIComponent(address), undefined, options); }
131
+ getGasFees(options) { return this.request('GET', '/v1/gas/fees', undefined, options); }
132
+ getDexMetrics(options) { return this.request('GET', '/v1/dex/metrics', undefined, options); }
133
+ getTokenPrice(symbol, options) { return this.request('GET', '/v1/token/price/' + encodeURIComponent(symbol), undefined, options); }
134
+ getWhaleSignals(options) { return this.request('GET', '/v1/whales/signals', undefined, options); }
135
+ getKeySelf() { return this.request('GET', '/v1/keys/self'); }
136
+ revokeKey(confirm = true) { return this.request('POST', '/v1/keys/revoke', { confirm }); }
137
+ }
138
+
139
+ async function enforceCallerPolicy(policy, analysis, context) {
140
+ if (typeof policy !== 'function') {
141
+ throw new Error('[M2M Sentinel] A caller-defined policy function is required. Static capability observations are not a safety decision.');
142
+ }
143
+ const result = await policy(analysis, context);
144
+ if (result === false || (result && result.allow === false)) {
145
+ const reason = result && result.reason ? result.reason : 'caller-defined policy rejected the transaction';
146
+ throw new Error('[M2M Sentinel] Transaction blocked: ' + reason + '.');
147
+ }
148
+ }
149
+
150
+ function createEthersSentinelMiddleware(apiKey, baseUrl, policy) {
151
+ const client = new M2MSentinelClient({ apiKey, baseUrl });
152
+ return {
153
+ client,
154
+ verifyContractBeforeTx: async (targetAddress, policyOverride) => {
155
+ const audit = await client.auditContract(targetAddress);
156
+ await enforceCallerPolicy(policyOverride || policy, audit, { targetAddress, integration: 'ethers' });
157
+ return audit;
158
+ }
159
+ };
160
+ }
161
+
162
+ function createViemSentinelInterceptor(apiKey, baseUrl, policy) {
163
+ const client = new M2MSentinelClient({ apiKey, baseUrl });
164
+ return {
165
+ client,
166
+ inspectSwapTarget: async (address, policyOverride) => {
167
+ const analysis = await client.getCapabilityScore(address);
168
+ await enforceCallerPolicy(policyOverride || policy, analysis, { targetAddress: address, integration: 'viem' });
169
+ return analysis;
170
+ }
171
+ };
172
+ }
173
+
174
+ module.exports = {
175
+ M2MSentinelClient,
176
+ M2MSentinelError,
177
+ PaymentRequiredError,
178
+ RateLimitedError,
179
+ DataSourceUnavailableError,
180
+ enforceCallerPolicy,
181
+ createEthersSentinelMiddleware,
182
+ createViemSentinelInterceptor
183
+ };
@@ -1,12 +1 @@
1
- class M2MSentinelSDK {
2
- constructor(apiKey) {
3
- this.apiKey = apiKey;
4
- this.baseUrl = 'https://m2msentinel.vercel.app/v1';
5
- }
6
-
7
- async getTokenPrice() {
8
- const response = await fetch(`${this.baseUrl}/token/price`);
9
- return response.json();
10
- }
11
- }
12
- module.exports = M2MSentinelSDK;
1
+ module.exports = require('./index.js');