@agentlayer.tech/wallet 0.1.100 → 0.1.102

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 (85) hide show
  1. package/.openclaw/extensions/agent-wallet/dist/index.js +46 -1
  2. package/.openclaw/extensions/agent-wallet/index.ts +46 -1
  3. package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +1 -1
  4. package/.openclaw/extensions/agent-wallet/package.json +1 -1
  5. package/README.md +18 -0
  6. package/VERSION +1 -1
  7. package/agent-wallet/agent_wallet/__init__.py +1 -1
  8. package/agent-wallet/agent_wallet/connector_cli.py +198 -0
  9. package/agent-wallet/agent_wallet/connectors/__init__.py +28 -0
  10. package/agent-wallet/agent_wallet/connectors/catalog.py +68 -0
  11. package/agent-wallet/agent_wallet/connectors/client.py +308 -0
  12. package/agent-wallet/agent_wallet/connectors/intent_policy.py +243 -0
  13. package/agent-wallet/agent_wallet/connectors/manifest.py +192 -0
  14. package/agent-wallet/agent_wallet/connectors/registry.py +341 -0
  15. package/agent-wallet/agent_wallet/openclaw_adapter.py +56 -5
  16. package/agent-wallet/agent_wallet/providers/solana_rpc.py +1 -1
  17. package/agent-wallet/agent_wallet/providers/wdk_btc_local.py +7 -4
  18. package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +7 -2
  19. package/agent-wallet/agent_wallet/wallet_layer/solana.py +1 -1
  20. package/agent-wallet/openclaw.plugin.json +1 -1
  21. package/agent-wallet/pyproject.toml +4 -2
  22. package/agent-wallet/scripts/build_release_bundle.py +1 -0
  23. package/agent-wallet/scripts/install_agent_wallet.py +1 -1
  24. package/bin/openclaw-agent-wallet.mjs +38 -0
  25. package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
  26. package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +1 -1
  27. package/codex/plugins/agent-wallet/server.py +17 -5
  28. package/connectors/AGENTS.md +37 -0
  29. package/connectors/DEVELOPER_GUIDE.md +131 -0
  30. package/connectors/README.md +139 -0
  31. package/connectors/READ_ONLY_BETA.md +54 -0
  32. package/connectors/RELEASING.md +48 -0
  33. package/connectors/conformance/README.md +31 -0
  34. package/connectors/conformance/package.json +54 -0
  35. package/connectors/conformance/scripts/sync-license.mjs +3 -0
  36. package/connectors/conformance/scripts/sync-spec.mjs +16 -0
  37. package/connectors/conformance/src/cli.ts +43 -0
  38. package/connectors/conformance/src/index.ts +7 -0
  39. package/connectors/conformance/src/runner.ts +314 -0
  40. package/connectors/conformance/src/types.ts +27 -0
  41. package/connectors/conformance/test/fixtures/invalid-response-identity.json +14 -0
  42. package/connectors/conformance/test/fixtures/missing-tool-fixture.json +4 -0
  43. package/connectors/conformance/test/runner.test.mjs +151 -0
  44. package/connectors/conformance/tsconfig.json +17 -0
  45. package/connectors/examples/reference-crypto-data/Dockerfile +21 -0
  46. package/connectors/examples/reference-crypto-data/README.md +51 -0
  47. package/connectors/examples/reference-crypto-data/conformance.json +11 -0
  48. package/connectors/examples/reference-crypto-data/connector.json +95 -0
  49. package/connectors/examples/reference-crypto-data/package.json +21 -0
  50. package/connectors/examples/reference-crypto-data/railway.toml +9 -0
  51. package/connectors/examples/reference-crypto-data/src/connector.ts +137 -0
  52. package/connectors/examples/reference-crypto-data/src/server.ts +13 -0
  53. package/connectors/examples/reference-crypto-data/test/connector.test.mjs +97 -0
  54. package/connectors/examples/reference-crypto-data/tsconfig.json +17 -0
  55. package/connectors/package-lock.json +561 -0
  56. package/connectors/package.json +19 -0
  57. package/connectors/scripts/smoke-packed-packages.mjs +121 -0
  58. package/connectors/sdk-typescript/README.md +47 -0
  59. package/connectors/sdk-typescript/package.json +49 -0
  60. package/connectors/sdk-typescript/scripts/sync-license.mjs +3 -0
  61. package/connectors/sdk-typescript/src/connector.ts +204 -0
  62. package/connectors/sdk-typescript/src/errors.ts +11 -0
  63. package/connectors/sdk-typescript/src/http.ts +116 -0
  64. package/connectors/sdk-typescript/src/index.ts +26 -0
  65. package/connectors/sdk-typescript/src/types.ts +101 -0
  66. package/connectors/sdk-typescript/test/sdk.test.mjs +181 -0
  67. package/connectors/sdk-typescript/tsconfig.json +21 -0
  68. package/connectors/spec/connector-manifest.schema.json +340 -0
  69. package/connectors/spec/connector-protocol.md +159 -0
  70. package/connectors/spec/transaction-intent.schema.json +281 -0
  71. package/connectors/templates/read-only/.env.example +1 -0
  72. package/connectors/templates/read-only/Dockerfile +11 -0
  73. package/connectors/templates/read-only/README.md +33 -0
  74. package/connectors/templates/read-only/conformance.json +11 -0
  75. package/connectors/templates/read-only/connector.json +64 -0
  76. package/connectors/templates/read-only/package.json +21 -0
  77. package/connectors/templates/read-only/railway.toml +9 -0
  78. package/connectors/templates/read-only/src/connector.ts +61 -0
  79. package/connectors/templates/read-only/src/server.ts +14 -0
  80. package/connectors/templates/read-only/test/connector.test.mjs +62 -0
  81. package/connectors/templates/read-only/tsconfig.json +17 -0
  82. package/hermes/plugins/agent_wallet/plugin.yaml +1 -1
  83. package/package.json +2 -1
  84. package/wdk-btc-wallet/package.json +1 -1
  85. package/wdk-evm-wallet/package.json +1 -1
@@ -0,0 +1,21 @@
1
+ FROM node:24-alpine AS build
2
+
3
+ WORKDIR /app/connectors
4
+ COPY connectors/package.json connectors/package-lock.json ./
5
+ COPY connectors/sdk-typescript ./sdk-typescript
6
+ COPY connectors/conformance ./conformance
7
+ COPY connectors/templates/read-only ./templates/read-only
8
+ COPY connectors/examples/reference-crypto-data ./examples/reference-crypto-data
9
+ RUN npm ci
10
+ RUN npm run build --workspace @agentlayer.tech/connector-sdk
11
+ RUN npm run build --workspace @agentlayer.tech/reference-crypto-data-connector
12
+
13
+ FROM node:24-alpine
14
+
15
+ ENV NODE_ENV=production
16
+ WORKDIR /app/connectors
17
+ COPY --from=build --chown=node:node /app/connectors /app/connectors
18
+ USER node
19
+ EXPOSE 3000
20
+
21
+ CMD ["npm", "run", "start", "--workspace", "@agentlayer.tech/reference-crypto-data-connector"]
@@ -0,0 +1,51 @@
1
+ # AgentLayer crypto-data reference connector
2
+
3
+ This is a neutral, read-only reference implementation for Connector Protocol
4
+ v1. It exposes public spot-price and asset-metadata tools. It does not receive a
5
+ wallet address, construct transactions, make payments, sign, or broadcast.
6
+
7
+ The upstream is Coinbase Exchange's public market-data API. The reference uses
8
+ the documented public ticker and currency endpoints and requires no API key:
9
+
10
+ - https://docs.cdp.coinbase.com/api-reference/exchange-api/rest-api/products/get-product-ticker
11
+ - https://docs.cdp.coinbase.com/api-reference/exchange-api/rest-api/currencies/get-a-currency
12
+
13
+ Run locally:
14
+
15
+ ```bash
16
+ npm ci --prefix connectors
17
+ npm run build --workspace @agentlayer.tech/reference-crypto-data-connector --prefix connectors
18
+ npm run start --workspace @agentlayer.tech/reference-crypto-data-connector --prefix connectors
19
+ ```
20
+
21
+ The reviewed beta deployment is available at
22
+ `https://reference-crypto-data-production.up.railway.app`. Its manifest is
23
+ already pinned to that exact HTTPS endpoint. Re-run its live conformance check
24
+ after any immutable version release:
25
+
26
+ ```bash
27
+ cd connectors
28
+ node conformance/dist/cli.js \
29
+ --manifest examples/reference-crypto-data/connector.json \
30
+ --fixture examples/reference-crypto-data/conformance.json \
31
+ --endpoint https://reference-crypto-data-production.up.railway.app
32
+ ```
33
+
34
+ ## Railway deployment
35
+
36
+ Deploy this directory as one stateless service in a new, isolated Railway
37
+ project. Keep the repository root as the Docker build context and point the
38
+ service Dockerfile at
39
+ `connectors/examples/reference-crypto-data/Dockerfile` (for a root deployment,
40
+ set Railway's `RAILWAY_DOCKERFILE_PATH` service variable to that value).
41
+ Configure the Railway deployment health-check path as `/healthz` with a
42
+ 10-second timeout and an `ON_FAILURE` restart policy. The service needs no
43
+ application variables, volume, database, wallet material, or connection to the
44
+ AgentLayer provider gateway.
45
+
46
+ After Railway generates the public domain:
47
+
48
+ 1. update `connector.json` to that exact HTTPS URL;
49
+ 2. redeploy the immutable manifest version;
50
+ 3. run both live conformance and wallet invocation tests;
51
+ 4. install the reviewed manifest with `wallet connectors install ... --enable --yes`.
@@ -0,0 +1,11 @@
1
+ {
2
+ "schema_version": 1,
3
+ "tools": {
4
+ "get_spot_price": {
5
+ "arguments": { "base": "BTC", "quote": "USD" }
6
+ },
7
+ "get_asset_metadata": {
8
+ "arguments": { "asset": "BTC" }
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,95 @@
1
+ {
2
+ "schema_version": 1,
3
+ "id": "tech.agentlayer.crypto-data",
4
+ "name": "AgentLayer Crypto Data Reference",
5
+ "version": "0.1.0-beta.1",
6
+ "publisher": {
7
+ "id": "agentlayer",
8
+ "name": "AgentLayer",
9
+ "url": "https://agentlayer.tech"
10
+ },
11
+ "agentlayer": {
12
+ "protocol_version": 1,
13
+ "runtime_range": ">=0.1.101"
14
+ },
15
+ "trust": "community_read_only",
16
+ "transport": {
17
+ "type": "https",
18
+ "url": "https://reference-crypto-data-production.up.railway.app",
19
+ "timeout_ms": 10000
20
+ },
21
+ "permissions": {
22
+ "wallet_address": false,
23
+ "transaction_intents": false,
24
+ "network_hosts": ["api.exchange.coinbase.com"]
25
+ },
26
+ "tools": [
27
+ {
28
+ "name": "get_spot_price",
29
+ "description": "Get a public spot ticker snapshot for a crypto/fiat or crypto/crypto product.",
30
+ "read_only": true,
31
+ "risk_level": "low",
32
+ "input_schema": {
33
+ "type": "object",
34
+ "properties": {
35
+ "base": { "type": "string", "pattern": "^[A-Za-z0-9]{2,12}$" },
36
+ "quote": { "type": "string", "pattern": "^[A-Za-z0-9]{2,12}$" }
37
+ },
38
+ "required": ["base", "quote"],
39
+ "additionalProperties": false
40
+ },
41
+ "output_schema": {
42
+ "type": "object",
43
+ "properties": {
44
+ "product_id": { "type": "string", "minLength": 5, "maxLength": 25 },
45
+ "base_currency": { "type": "string", "minLength": 2, "maxLength": 12 },
46
+ "quote_currency": { "type": "string", "minLength": 2, "maxLength": 12 },
47
+ "price": { "type": "string", "minLength": 1, "maxLength": 80 },
48
+ "bid": { "type": "string", "minLength": 1, "maxLength": 80 },
49
+ "ask": { "type": "string", "minLength": 1, "maxLength": 80 },
50
+ "last_size": { "type": "string", "minLength": 1, "maxLength": 80 },
51
+ "volume_24h": { "type": "string", "minLength": 1, "maxLength": 80 },
52
+ "observed_at": { "type": "string", "minLength": 10, "maxLength": 64 },
53
+ "source": { "const": "coinbase_exchange" }
54
+ },
55
+ "required": [
56
+ "product_id", "base_currency", "quote_currency", "price", "bid", "ask",
57
+ "last_size", "volume_24h", "observed_at", "source"
58
+ ],
59
+ "additionalProperties": false
60
+ }
61
+ },
62
+ {
63
+ "name": "get_asset_metadata",
64
+ "description": "Get public metadata and network status for a currency code.",
65
+ "read_only": true,
66
+ "risk_level": "low",
67
+ "input_schema": {
68
+ "type": "object",
69
+ "properties": {
70
+ "asset": { "type": "string", "pattern": "^[A-Za-z0-9]{2,12}$" }
71
+ },
72
+ "required": ["asset"],
73
+ "additionalProperties": false
74
+ },
75
+ "output_schema": {
76
+ "type": "object",
77
+ "properties": {
78
+ "asset": { "type": "string", "minLength": 2, "maxLength": 12 },
79
+ "name": { "type": "string", "minLength": 1, "maxLength": 120 },
80
+ "status": { "type": "string", "minLength": 1, "maxLength": 40 },
81
+ "type": { "type": ["string", "null"], "maxLength": 40 },
82
+ "min_size": { "type": "string", "minLength": 1, "maxLength": 80 },
83
+ "max_precision": { "type": "string", "minLength": 1, "maxLength": 80 },
84
+ "default_network": { "type": ["string", "null"], "maxLength": 80 },
85
+ "source": { "const": "coinbase_exchange" }
86
+ },
87
+ "required": [
88
+ "asset", "name", "status", "type", "min_size", "max_precision",
89
+ "default_network", "source"
90
+ ],
91
+ "additionalProperties": false
92
+ }
93
+ }
94
+ ]
95
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@agentlayer.tech/reference-crypto-data-connector",
3
+ "version": "0.1.0-beta.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=24"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc -p tsconfig.json",
11
+ "start": "node dist/server.js",
12
+ "check": "npm run build && node --test test/*.test.mjs"
13
+ },
14
+ "dependencies": {
15
+ "@agentlayer.tech/connector-sdk": "0.1.0-beta.1"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^24.0.0",
19
+ "typescript": "^7.0.2"
20
+ }
21
+ }
@@ -0,0 +1,9 @@
1
+ [build]
2
+ builder = "DOCKERFILE"
3
+ dockerfilePath = "connectors/examples/reference-crypto-data/Dockerfile"
4
+
5
+ [deploy]
6
+ healthcheckPath = "/healthz"
7
+ healthcheckTimeout = 10
8
+ restartPolicyType = "ON_FAILURE"
9
+ restartPolicyMaxRetries = 3
@@ -0,0 +1,137 @@
1
+ import { readFileSync } from "node:fs";
2
+
3
+ import {
4
+ ConnectorSdkError,
5
+ defineReadOnlyConnector,
6
+ type ConnectorManifest,
7
+ type JsonObject,
8
+ } from "@agentlayer.tech/connector-sdk";
9
+
10
+ const manifest = JSON.parse(
11
+ readFileSync(new URL("../connector.json", import.meta.url), "utf8")
12
+ ) as ConnectorManifest;
13
+ const UPSTREAM_ORIGIN = "https://api.exchange.coinbase.com";
14
+ const MAX_UPSTREAM_BYTES = 256 * 1024;
15
+ const ASSET_PATTERN = /^[A-Z0-9]{2,12}$/;
16
+
17
+ function assetCode(value: unknown, field: string): string {
18
+ const normalized = String(value ?? "").trim().toUpperCase();
19
+ if (!ASSET_PATTERN.test(normalized)) {
20
+ throw new ConnectorSdkError("invalid_arguments", `${field} must be a 2-12 character asset code.`);
21
+ }
22
+ return normalized;
23
+ }
24
+
25
+ function requiredString(value: unknown, field: string, maxLength = 120): string {
26
+ if (typeof value !== "string" || value.length === 0 || value.length > maxLength) {
27
+ throw new ConnectorSdkError("invalid_upstream", `Upstream ${field} is invalid.`, 502);
28
+ }
29
+ return value;
30
+ }
31
+
32
+ function optionalString(value: unknown, maxLength = 80): string | null {
33
+ return typeof value === "string" && value.length > 0 && value.length <= maxLength ? value : null;
34
+ }
35
+
36
+ async function readObject(response: Response): Promise<Record<string, unknown>> {
37
+ const contentLength = Number(response.headers.get("content-length") ?? 0);
38
+ if (Number.isFinite(contentLength) && contentLength > MAX_UPSTREAM_BYTES) {
39
+ throw new ConnectorSdkError("upstream_too_large", "Upstream response exceeds the size limit.", 502);
40
+ }
41
+ if (!response.body) {
42
+ throw new ConnectorSdkError("invalid_upstream", "Upstream response body is missing.", 502);
43
+ }
44
+ const reader = response.body.getReader();
45
+ const chunks: Uint8Array[] = [];
46
+ let size = 0;
47
+ while (true) {
48
+ const { done, value } = await reader.read();
49
+ if (done) break;
50
+ size += value.byteLength;
51
+ if (size > MAX_UPSTREAM_BYTES) {
52
+ await reader.cancel();
53
+ throw new ConnectorSdkError("upstream_too_large", "Upstream response exceeds the size limit.", 502);
54
+ }
55
+ chunks.push(value);
56
+ }
57
+ let payload: unknown;
58
+ try {
59
+ payload = JSON.parse(Buffer.concat(chunks).toString("utf8"));
60
+ } catch {
61
+ throw new ConnectorSdkError("invalid_upstream", "Upstream response is not valid JSON.", 502);
62
+ }
63
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
64
+ throw new ConnectorSdkError("invalid_upstream", "Upstream response root is invalid.", 502);
65
+ }
66
+ return payload as Record<string, unknown>;
67
+ }
68
+
69
+ async function fetchObject(
70
+ path: string,
71
+ fetchImpl: typeof fetch
72
+ ): Promise<Record<string, unknown>> {
73
+ const url = new URL(path, UPSTREAM_ORIGIN);
74
+ if (url.origin !== UPSTREAM_ORIGIN || !manifest.permissions.network_hosts.includes(url.hostname)) {
75
+ throw new ConnectorSdkError("upstream_not_allowed", "Upstream host is not allowlisted.", 500);
76
+ }
77
+ const response = await fetchImpl(url, {
78
+ headers: {
79
+ accept: "application/json",
80
+ "cache-control": "no-cache",
81
+ "user-agent": "AgentLayer-Reference-Connector/0.1",
82
+ },
83
+ redirect: "error",
84
+ signal: AbortSignal.timeout(5000),
85
+ });
86
+ if (!response.ok) {
87
+ throw new ConnectorSdkError("upstream_http_error", `Upstream returned HTTP ${response.status}.`, 502);
88
+ }
89
+ return readObject(response);
90
+ }
91
+
92
+ export function createCryptoDataConnector(fetchImpl: typeof fetch = fetch) {
93
+ return defineReadOnlyConnector({
94
+ manifest,
95
+ responseTtlSeconds: 30,
96
+ handlers: {
97
+ async get_spot_price(arguments_: JsonObject): Promise<JsonObject> {
98
+ const base = assetCode(arguments_.base, "base");
99
+ const quote = assetCode(arguments_.quote, "quote");
100
+ const productId = `${base}-${quote}`;
101
+ const ticker = await fetchObject(`/products/${encodeURIComponent(productId)}/ticker`, fetchImpl);
102
+ return {
103
+ product_id: productId,
104
+ base_currency: base,
105
+ quote_currency: quote,
106
+ price: requiredString(ticker.price, "price", 80),
107
+ bid: requiredString(ticker.bid, "bid", 80),
108
+ ask: requiredString(ticker.ask, "ask", 80),
109
+ last_size: requiredString(ticker.size, "size", 80),
110
+ volume_24h: requiredString(ticker.volume, "volume", 80),
111
+ observed_at: requiredString(ticker.time, "time", 64),
112
+ source: "coinbase_exchange",
113
+ };
114
+ },
115
+ async get_asset_metadata(arguments_: JsonObject): Promise<JsonObject> {
116
+ const asset = assetCode(arguments_.asset, "asset");
117
+ const currency = await fetchObject(`/currencies/${encodeURIComponent(asset)}`, fetchImpl);
118
+ const details =
119
+ currency.details && typeof currency.details === "object" && !Array.isArray(currency.details)
120
+ ? (currency.details as Record<string, unknown>)
121
+ : {};
122
+ return {
123
+ asset: requiredString(currency.id, "id", 12),
124
+ name: requiredString(currency.name, "name"),
125
+ status: requiredString(currency.status, "status", 40),
126
+ type: optionalString(details.type, 40),
127
+ min_size: requiredString(currency.min_size, "min_size", 80),
128
+ max_precision: requiredString(currency.max_precision, "max_precision", 80),
129
+ default_network: optionalString(currency.default_network, 80),
130
+ source: "coinbase_exchange",
131
+ };
132
+ },
133
+ },
134
+ });
135
+ }
136
+
137
+ export const connector = createCryptoDataConnector();
@@ -0,0 +1,13 @@
1
+ import { startConnectorServer } from "@agentlayer.tech/connector-sdk";
2
+
3
+ import { connector } from "./connector.js";
4
+
5
+ const server = await startConnectorServer(connector);
6
+ console.log(
7
+ JSON.stringify({
8
+ event: "connector_started",
9
+ connector_id: connector.manifest.id,
10
+ connector_version: connector.manifest.version,
11
+ address: server.address(),
12
+ })
13
+ );
@@ -0,0 +1,97 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { createCryptoDataConnector } from "../dist/connector.js";
5
+
6
+ function request(connector, tool, arguments_) {
7
+ return {
8
+ protocol_version: 1,
9
+ request_id: `reference-${tool}`,
10
+ connector: { id: connector.manifest.id, version: connector.manifest.version },
11
+ tool,
12
+ arguments: arguments_,
13
+ context: {},
14
+ };
15
+ }
16
+
17
+ test("normalizes public ticker data without wallet context", async () => {
18
+ const connector = createCryptoDataConnector(async (url, options) => {
19
+ assert.equal(url.href, "https://api.exchange.coinbase.com/products/BTC-USD/ticker");
20
+ assert.equal(options.redirect, "error");
21
+ assert.equal(options.headers["cache-control"], "no-cache");
22
+ return new Response(
23
+ JSON.stringify({
24
+ price: "78000.12",
25
+ bid: "78000.11",
26
+ ask: "78000.13",
27
+ size: "0.01",
28
+ volume: "1234.5",
29
+ time: "2026-08-27T00:00:00Z",
30
+ }),
31
+ { status: 200, headers: { "content-type": "application/json" } }
32
+ );
33
+ });
34
+ const response = await connector.invoke(request(connector, "get_spot_price", {
35
+ base: "btc",
36
+ quote: "usd",
37
+ }));
38
+ assert.deepEqual(response.result, {
39
+ product_id: "BTC-USD",
40
+ base_currency: "BTC",
41
+ quote_currency: "USD",
42
+ price: "78000.12",
43
+ bid: "78000.11",
44
+ ask: "78000.13",
45
+ last_size: "0.01",
46
+ volume_24h: "1234.5",
47
+ observed_at: "2026-08-27T00:00:00Z",
48
+ source: "coinbase_exchange",
49
+ });
50
+ });
51
+
52
+ test("normalizes public asset metadata", async () => {
53
+ const connector = createCryptoDataConnector(async (url) => {
54
+ assert.equal(url.href, "https://api.exchange.coinbase.com/currencies/BTC");
55
+ return new Response(
56
+ JSON.stringify({
57
+ id: "BTC",
58
+ name: "Bitcoin",
59
+ min_size: "0.00000001",
60
+ max_precision: "0.00000001",
61
+ status: "online",
62
+ default_network: "bitcoin",
63
+ details: { type: "crypto" },
64
+ }),
65
+ { status: 200, headers: { "content-type": "application/json" } }
66
+ );
67
+ });
68
+ const response = await connector.invoke(request(connector, "get_asset_metadata", { asset: "btc" }));
69
+ assert.deepEqual(response.result, {
70
+ asset: "BTC",
71
+ name: "Bitcoin",
72
+ status: "online",
73
+ type: "crypto",
74
+ min_size: "0.00000001",
75
+ max_precision: "0.00000001",
76
+ default_network: "bitcoin",
77
+ source: "coinbase_exchange",
78
+ });
79
+ });
80
+
81
+ test("rejects oversized and malformed upstream responses", async () => {
82
+ const oversized = createCryptoDataConnector(async () =>
83
+ new Response("{}", { status: 200, headers: { "content-length": "999999" } })
84
+ );
85
+ await assert.rejects(
86
+ oversized.invoke(request(oversized, "get_asset_metadata", { asset: "BTC" })),
87
+ /size limit/
88
+ );
89
+
90
+ const malformed = createCryptoDataConnector(async () =>
91
+ new Response("not-json", { status: 200, headers: { "content-type": "application/json" } })
92
+ );
93
+ await assert.rejects(
94
+ malformed.invoke(request(malformed, "get_asset_metadata", { asset: "BTC" })),
95
+ /not valid JSON/
96
+ );
97
+ });
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2023",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "rootDir": "src",
7
+ "outDir": "dist",
8
+ "declaration": true,
9
+ "strict": true,
10
+ "noUncheckedIndexedAccess": true,
11
+ "exactOptionalPropertyTypes": true,
12
+ "verbatimModuleSyntax": true,
13
+ "types": ["node"],
14
+ "skipLibCheck": true
15
+ },
16
+ "include": ["src/**/*.ts"]
17
+ }