@apiosk/mcp 1.7.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -612
- package/assets/brand/apiosk-a-20260921.png +0 -0
- package/assets/brand/apple-touch-icon.png +0 -0
- package/assets/brand/favicon-dark.ico +0 -0
- package/assets/brand/favicon-light.ico +0 -0
- package/assets/brand/favicon.ico +0 -0
- package/assets/brand/icon-192.png +0 -0
- package/assets/brand/icon-512.png +0 -0
- package/assets/brand/icon-maskable-512.png +0 -0
- package/assets/brand/inter-OFL.txt +93 -0
- package/assets/brand/inter-latin-400-normal.woff2 +0 -0
- package/assets/brand/inter-latin-500-normal.woff2 +0 -0
- package/assets/brand/inter-latin-600-normal.woff2 +0 -0
- package/assets/brand/mark-20260905-transparent.svg +1 -0
- package/assets/brand/mark-20260918.svg +1 -0
- package/assets/brand/mark-black-20260918.svg +1 -0
- package/assets/brand/mark-dark-20260905-transparent.png +0 -0
- package/assets/brand/mark-dark-20260918.png +0 -0
- package/assets/brand/mark-dark-96.png +0 -0
- package/assets/brand/mark-light-20260905-transparent.png +0 -0
- package/assets/brand/mark-light-20260918.png +0 -0
- package/assets/brand/mark-light-96.png +0 -0
- package/assets/brand/wordmark-black-320.png +0 -0
- package/assets/brand/wordmark-white-320.png +0 -0
- package/docs/branding.md +11 -0
- package/docs/marketplace-submission-2026-04-08.md +4 -0
- package/docs/marketplace-submission-2026-08-20.md +157 -0
- package/docs/openai-plugin-submission-2026-09-05.md +143 -0
- package/dxt.json +26 -22
- package/index.mjs +3 -1
- package/logo-optimized-light.png +0 -0
- package/package.json +14 -14
- package/plugin/apiosk/.codex-plugin/plugin.json +45 -0
- package/plugin/apiosk/.mcp.json +8 -0
- package/plugin/apiosk/assets/icon-dark.png +0 -0
- package/plugin/apiosk/assets/icon.png +0 -0
- package/plugin/apiosk/assets/icon.svg +1 -0
- package/plugin/apiosk/assets/logo.png +0 -0
- package/plugin/apiosk/skills/apiosk/SKILL.md +33 -0
- package/plugin/apiosk/skills/apiosk/agents/openai.yaml +12 -0
- package/server.json +58 -24
- package/server.mjs +136 -151
- package/src/approval-feedback.mjs +21 -0
- package/src/brand-routes.mjs +28 -0
- package/src/create-server.mjs +160 -46
- package/src/display-money.mjs +20 -0
- package/src/display-text.mjs +67 -0
- package/src/gateway-client.mjs +42 -0
- package/src/gateway-v2-ask.mjs +50 -0
- package/src/gateway-v2-card-account.mjs +23 -0
- package/src/gateway-v2-card-actions.mjs +67 -0
- package/src/gateway-v2-card-answer-text.mjs +130 -0
- package/src/gateway-v2-card-answer.mjs +68 -0
- package/src/gateway-v2-card-blocks.mjs +166 -0
- package/src/gateway-v2-card-body.mjs +259 -0
- package/src/gateway-v2-card-budget.mjs +21 -0
- package/src/gateway-v2-card-cbs.mjs +56 -0
- package/src/gateway-v2-card-choices.mjs +4 -0
- package/src/gateway-v2-card-clarification.mjs +25 -0
- package/src/gateway-v2-card-compact.mjs +34 -0
- package/src/gateway-v2-card-events.mjs +41 -0
- package/src/gateway-v2-card-presentation.mjs +125 -0
- package/src/gateway-v2-card-research.mjs +63 -0
- package/src/gateway-v2-card-result.mjs +32 -0
- package/src/gateway-v2-card-search.mjs +39 -0
- package/src/gateway-v2-card-sources.mjs +32 -0
- package/src/gateway-v2-card-style.mjs +76 -0
- package/src/gateway-v2-card-verdict.mjs +66 -0
- package/src/gateway-v2-card.mjs +91 -0
- package/src/gateway-v2-contracts.json +66 -0
- package/src/gateway-v2-instructions.md +113 -0
- package/src/gateway-v2-recovery.mjs +18 -0
- package/src/gateway-v2-report-links.mjs +14 -0
- package/src/gateway-v2-workflows.mjs +17 -0
- package/src/gateway-v2.mjs +179 -0
- package/src/oauth.mjs +402 -1949
- package/src/observability.mjs +19 -3
- package/src/result-presentation.mjs +9 -0
- package/src/runtime.mjs +24 -3670
- package/src/source-groups.mjs +91 -0
- package/src/source-value-format.mjs +25 -0
- package/src/tool-result.mjs +20 -0
- package/src/ui-bridge.mjs +185 -0
- package/src/well-known-routes.mjs +132 -0
- package/src/assets/wallet-accounts.mjs +0 -20513
- package/src/assets/walletconnect-provider.mjs +0 -6319
- package/src/discovery.mjs +0 -929
- package/src/external-fetch.mjs +0 -203
- package/src/funding-options.mjs +0 -255
- package/src/gateway-management.mjs +0 -107
- package/src/hosted-payment.mjs +0 -552
- package/src/hosted-wallets.mjs +0 -530
- package/src/listing-metadata.mjs +0 -287
- package/src/local-config.mjs +0 -256
- package/src/payment-guidance.mjs +0 -298
- package/src/publisher.mjs +0 -1288
- package/src/result-canvas.mjs +0 -16
- package/src/source-registry.mjs +0 -215
- package/src/wallet-store.mjs +0 -476
- package/src/x402-inspect.mjs +0 -361
package/src/listing-metadata.mjs
DELETED
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
function isObject(value) {
|
|
2
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
function cloneJson(value, fallback) {
|
|
6
|
-
if (value === undefined) return fallback;
|
|
7
|
-
return JSON.parse(JSON.stringify(value));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function normalizePath(pathValue) {
|
|
11
|
-
const value = String(pathValue || "/").trim();
|
|
12
|
-
if (!value || value === "/") return "/";
|
|
13
|
-
return value.startsWith("/") ? value : `/${value}`;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function normalizeTags(tags = []) {
|
|
17
|
-
return Array.from(
|
|
18
|
-
new Set(
|
|
19
|
-
tags
|
|
20
|
-
.map((tag) => String(tag || "").trim().toLowerCase())
|
|
21
|
-
.filter(Boolean)
|
|
22
|
-
)
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function defaultExecuteInputSchema() {
|
|
27
|
-
return {
|
|
28
|
-
type: "object",
|
|
29
|
-
additionalProperties: false,
|
|
30
|
-
properties: {
|
|
31
|
-
operation: {
|
|
32
|
-
type: "string",
|
|
33
|
-
description: "Optional operation selector. Accepts an operation id, a path, or a METHOD /path key.",
|
|
34
|
-
},
|
|
35
|
-
input: {
|
|
36
|
-
description: "JSON payload forwarded to the selected operation.",
|
|
37
|
-
},
|
|
38
|
-
query: {
|
|
39
|
-
type: "object",
|
|
40
|
-
additionalProperties: true,
|
|
41
|
-
description: "Optional query parameter override for GET-style operations.",
|
|
42
|
-
},
|
|
43
|
-
path_params: {
|
|
44
|
-
type: "object",
|
|
45
|
-
additionalProperties: true,
|
|
46
|
-
description: "Optional path parameter override for routes that include placeholders.",
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function defaultExecuteOutputSchema() {
|
|
53
|
-
return {
|
|
54
|
-
type: "object",
|
|
55
|
-
required: ["status", "result", "cost", "latency", "operation", "api", "upstream_status"],
|
|
56
|
-
properties: {
|
|
57
|
-
status: { type: "string", enum: ["success", "error"] },
|
|
58
|
-
result: {},
|
|
59
|
-
cost: { type: "number" },
|
|
60
|
-
latency: { type: "integer" },
|
|
61
|
-
operation: { type: "string" },
|
|
62
|
-
api: { type: "string" },
|
|
63
|
-
upstream_status: { type: "integer" },
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function defaultOperations(priceUsd) {
|
|
69
|
-
return [
|
|
70
|
-
{
|
|
71
|
-
id: "/",
|
|
72
|
-
key: "GET /",
|
|
73
|
-
method: "GET",
|
|
74
|
-
path: "/",
|
|
75
|
-
price_usd: priceUsd,
|
|
76
|
-
payment_required: true,
|
|
77
|
-
description: "Forward a GET request to the API root.",
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
id: "/",
|
|
81
|
-
key: "POST /",
|
|
82
|
-
method: "POST",
|
|
83
|
-
path: "/",
|
|
84
|
-
price_usd: priceUsd,
|
|
85
|
-
payment_required: true,
|
|
86
|
-
description: "Forward a POST request to the API root.",
|
|
87
|
-
},
|
|
88
|
-
];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function normalizeOperation(operation, priceUsd) {
|
|
92
|
-
const method = String(operation?.method || "POST").trim().toUpperCase();
|
|
93
|
-
const path = normalizePath(operation?.path || operation?.id || "/");
|
|
94
|
-
return {
|
|
95
|
-
...cloneJson(operation, {}),
|
|
96
|
-
id: String(operation?.id || path),
|
|
97
|
-
key: String(operation?.key || `${method} ${path}`),
|
|
98
|
-
method,
|
|
99
|
-
path,
|
|
100
|
-
price_usd:
|
|
101
|
-
typeof operation?.price_usd === "number" && Number.isFinite(operation.price_usd)
|
|
102
|
-
? operation.price_usd
|
|
103
|
-
: priceUsd,
|
|
104
|
-
payment_required:
|
|
105
|
-
typeof operation?.payment_required === "boolean" ? operation.payment_required : true,
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function isDiagnosticOperationPath(path = "/") {
|
|
110
|
-
const normalized = normalizePath(path);
|
|
111
|
-
if (normalized === "/") return true;
|
|
112
|
-
return normalized.split("/").filter(Boolean).some((segment) =>
|
|
113
|
-
/^(health|status|metadata|docs?|openapi|swagger|ping|readyz?|livez?)$/i.test(segment)
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function defaultOperationRank(operation = {}) {
|
|
118
|
-
const path = normalizePath(operation.path || operation.id || "/");
|
|
119
|
-
const trimmed = path.replace(/^\/+|\/+$/g, "");
|
|
120
|
-
const segments = trimmed ? trimmed.split("/").filter(Boolean).length : 0;
|
|
121
|
-
const method = String(operation.method || "POST").trim().toUpperCase();
|
|
122
|
-
const methodRank =
|
|
123
|
-
method === "POST"
|
|
124
|
-
? 0
|
|
125
|
-
: method === "PUT"
|
|
126
|
-
? 1
|
|
127
|
-
: method === "PATCH"
|
|
128
|
-
? 2
|
|
129
|
-
: method === "DELETE"
|
|
130
|
-
? 3
|
|
131
|
-
: method === "GET"
|
|
132
|
-
? 4
|
|
133
|
-
: method === "HEAD"
|
|
134
|
-
? 5
|
|
135
|
-
: 6;
|
|
136
|
-
return [
|
|
137
|
-
isDiagnosticOperationPath(path) ? 1 : 0,
|
|
138
|
-
/batch/i.test(path) ? 1 : 0,
|
|
139
|
-
segments,
|
|
140
|
-
path.length,
|
|
141
|
-
methodRank,
|
|
142
|
-
operation.payment_required === false ? 1 : 0,
|
|
143
|
-
`${method} ${path}`,
|
|
144
|
-
];
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function inferDefaultOperation(operations = []) {
|
|
148
|
-
const ranked = [...operations].sort((left, right) => {
|
|
149
|
-
const leftRank = defaultOperationRank(left);
|
|
150
|
-
const rightRank = defaultOperationRank(right);
|
|
151
|
-
for (let index = 0; index < leftRank.length; index += 1) {
|
|
152
|
-
if (leftRank[index] < rightRank[index]) return -1;
|
|
153
|
-
if (leftRank[index] > rightRank[index]) return 1;
|
|
154
|
-
}
|
|
155
|
-
return 0;
|
|
156
|
-
});
|
|
157
|
-
return ranked[0]?.id || operations[0]?.id || "/";
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function inferReadOnlyHint(operations = [], slug = "", name = "", description = "") {
|
|
161
|
-
const allReadOnly =
|
|
162
|
-
operations.length > 0 &&
|
|
163
|
-
operations.every((operation) => operation.method === "GET" || operation.method === "HEAD");
|
|
164
|
-
|
|
165
|
-
if (allReadOnly) return true;
|
|
166
|
-
|
|
167
|
-
return /extract|lookup|watch|map|research|digest|diff|monitor|verify|metadata|status/i.test(
|
|
168
|
-
`${slug} ${name} ${description}`
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function inferIdempotentHint(operations = []) {
|
|
173
|
-
return (
|
|
174
|
-
operations.length > 0 &&
|
|
175
|
-
operations.every((operation) => operation.method === "GET" || operation.method === "HEAD")
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function normalizeListingGroup(listingGroup) {
|
|
180
|
-
const value = String(listingGroup || "").trim().toLowerCase();
|
|
181
|
-
return value || null;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export function resolveCategory(category, listingGroup) {
|
|
185
|
-
const explicit = String(category || "").trim().toLowerCase();
|
|
186
|
-
if (explicit) return explicit;
|
|
187
|
-
|
|
188
|
-
const normalizedGroup = normalizeListingGroup(listingGroup);
|
|
189
|
-
if (normalizedGroup === "datasets") return "dataset";
|
|
190
|
-
if (normalizedGroup === "compute") return "compute";
|
|
191
|
-
return "data";
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function resolveProviderListingType(category, listingGroup, baseProvider = null) {
|
|
195
|
-
const explicit = String(baseProvider?.listing_type || "").trim().toLowerCase();
|
|
196
|
-
if (explicit) return explicit;
|
|
197
|
-
|
|
198
|
-
const normalizedGroup = normalizeListingGroup(listingGroup);
|
|
199
|
-
if (normalizedGroup === "datasets" || category === "dataset") return "dataset";
|
|
200
|
-
if (["service", "connector", "skill", "product"].includes(normalizedGroup || "")) {
|
|
201
|
-
return normalizedGroup;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return "api";
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export function buildListingMetadata({
|
|
208
|
-
name,
|
|
209
|
-
slug,
|
|
210
|
-
description,
|
|
211
|
-
endpoint_url,
|
|
212
|
-
price_usd,
|
|
213
|
-
category,
|
|
214
|
-
listing_group,
|
|
215
|
-
listing_metadata = {},
|
|
216
|
-
} = {}) {
|
|
217
|
-
const safeSlug = String(slug || "").trim();
|
|
218
|
-
const safeName = String(name || safeSlug || "Apiosk API").trim();
|
|
219
|
-
const safeDescription = String(description || "").trim();
|
|
220
|
-
const safeEndpointUrl = String(endpoint_url || "").trim();
|
|
221
|
-
const safePrice = Number(price_usd);
|
|
222
|
-
const normalizedCategory = resolveCategory(category, listing_group);
|
|
223
|
-
const base = isObject(listing_metadata) ? cloneJson(listing_metadata, {}) : {};
|
|
224
|
-
const operationsInput = Array.isArray(base.operations) ? base.operations : defaultOperations(safePrice);
|
|
225
|
-
const operations = operationsInput.map((operation) => normalizeOperation(operation, safePrice));
|
|
226
|
-
const inputSchema = cloneJson(
|
|
227
|
-
base.input_schema || base.mcp_tool?.inputSchema,
|
|
228
|
-
defaultExecuteInputSchema()
|
|
229
|
-
);
|
|
230
|
-
const outputSchema = cloneJson(
|
|
231
|
-
base.output_schema || base.mcp_tool?.outputSchema,
|
|
232
|
-
defaultExecuteOutputSchema()
|
|
233
|
-
);
|
|
234
|
-
const provider = {
|
|
235
|
-
type: "http_api",
|
|
236
|
-
origin_url: safeEndpointUrl,
|
|
237
|
-
...cloneJson(base.provider, {}),
|
|
238
|
-
origin_url: safeEndpointUrl,
|
|
239
|
-
listing_type: resolveProviderListingType(normalizedCategory, listing_group, base.provider),
|
|
240
|
-
};
|
|
241
|
-
const mergedTags = normalizeTags([
|
|
242
|
-
...(Array.isArray(base.tags) ? base.tags : []),
|
|
243
|
-
normalizedCategory,
|
|
244
|
-
"api",
|
|
245
|
-
"agent-ready",
|
|
246
|
-
"mcp-native",
|
|
247
|
-
]);
|
|
248
|
-
const defaultOperation = String(base.default_operation || inferDefaultOperation(operations));
|
|
249
|
-
const readOnlyHint =
|
|
250
|
-
typeof base.mcp_tool?.annotations?.readOnlyHint === "boolean"
|
|
251
|
-
? base.mcp_tool.annotations.readOnlyHint
|
|
252
|
-
: inferReadOnlyHint(operations, safeSlug, safeName, safeDescription);
|
|
253
|
-
const idempotentHint =
|
|
254
|
-
typeof base.mcp_tool?.annotations?.idempotentHint === "boolean"
|
|
255
|
-
? base.mcp_tool.annotations.idempotentHint
|
|
256
|
-
: inferIdempotentHint(operations);
|
|
257
|
-
|
|
258
|
-
return {
|
|
259
|
-
...base,
|
|
260
|
-
agent_native: true,
|
|
261
|
-
mcp_native: true,
|
|
262
|
-
metadata_path: String(base.metadata_path || "/metadata"),
|
|
263
|
-
execute_path: String(base.execute_path || "/execute"),
|
|
264
|
-
default_operation: defaultOperation,
|
|
265
|
-
cost_per_call: safePrice,
|
|
266
|
-
input_schema: inputSchema,
|
|
267
|
-
output_schema: outputSchema,
|
|
268
|
-
operations,
|
|
269
|
-
provider,
|
|
270
|
-
tags: mergedTags,
|
|
271
|
-
mcp_tool: {
|
|
272
|
-
...cloneJson(base.mcp_tool, {}),
|
|
273
|
-
name: String(base.mcp_tool?.name || safeSlug),
|
|
274
|
-
title: String(base.mcp_tool?.title || safeName),
|
|
275
|
-
description: String(base.mcp_tool?.description || safeDescription || safeName),
|
|
276
|
-
inputSchema: inputSchema,
|
|
277
|
-
outputSchema: outputSchema,
|
|
278
|
-
annotations: {
|
|
279
|
-
readOnlyHint,
|
|
280
|
-
idempotentHint,
|
|
281
|
-
destructiveHint: false,
|
|
282
|
-
openWorldHint: false,
|
|
283
|
-
...cloneJson(base.mcp_tool?.annotations, {}),
|
|
284
|
-
},
|
|
285
|
-
},
|
|
286
|
-
};
|
|
287
|
-
}
|
package/src/local-config.mjs
DELETED
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
import os from "node:os";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
4
|
-
|
|
5
|
-
const DEFAULT_CONNECT_HEADER_NAME = "X-Apiosk-Connect-Token";
|
|
6
|
-
const DEFAULT_GATEWAY_URL = "https://gateway.apiosk.com";
|
|
7
|
-
const DEFAULT_DASHBOARD_URL = "https://apiosk.com";
|
|
8
|
-
const DEFAULT_CONTROL_PLANE_URL = "https://mcp.apiosk.com";
|
|
9
|
-
const DEFAULT_CHAIN_ID = 8453;
|
|
10
|
-
const DEFAULT_RPC_URL = "https://mainnet.base.org";
|
|
11
|
-
const DEFAULT_USDC_CONTRACT = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
12
|
-
|
|
13
|
-
function trimString(value) {
|
|
14
|
-
return String(value || "").trim();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function defaultApioskDir(env = process.env) {
|
|
18
|
-
const configured = trimString(env.APIOSK_HOME);
|
|
19
|
-
if (configured) return configured;
|
|
20
|
-
|
|
21
|
-
const home = trimString(env.HOME) || os.homedir();
|
|
22
|
-
return path.join(home, ".apiosk");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function normalizeWalletAddress(value, { optional = false } = {}) {
|
|
26
|
-
const normalized = trimString(value).toLowerCase();
|
|
27
|
-
if (!normalized && optional) return "";
|
|
28
|
-
if (!/^0x[a-f0-9]{40}$/.test(normalized)) {
|
|
29
|
-
throw new Error("Wallet address must use a 0x-prefixed 40-byte hex address.");
|
|
30
|
-
}
|
|
31
|
-
return normalized;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function normalizeConnectToken(value, { optional = false } = {}) {
|
|
35
|
-
const normalized = trimString(value);
|
|
36
|
-
if (!normalized && optional) return "";
|
|
37
|
-
if (!/^aw_[A-Za-z0-9_:-]+$/.test(normalized)) {
|
|
38
|
-
throw new Error("Connect token must match the Apiosk managed-token format.");
|
|
39
|
-
}
|
|
40
|
-
return normalized;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function safeChmod(filePath, mode) {
|
|
44
|
-
try {
|
|
45
|
-
await chmod(filePath, mode);
|
|
46
|
-
} catch {
|
|
47
|
-
// Best effort only; not all environments support chmod.
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async function writeAtomic(filePath, value) {
|
|
52
|
-
const dir = path.dirname(filePath);
|
|
53
|
-
const tempFile = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
54
|
-
|
|
55
|
-
await mkdir(dir, { recursive: true });
|
|
56
|
-
await writeFile(tempFile, value, "utf8");
|
|
57
|
-
await safeChmod(tempFile, 0o600);
|
|
58
|
-
await rename(tempFile, filePath);
|
|
59
|
-
await safeChmod(filePath, 0o600);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function createApioskLocalConfigPaths(env = process.env) {
|
|
63
|
-
const apioskDir = defaultApioskDir(env);
|
|
64
|
-
return {
|
|
65
|
-
apioskDir,
|
|
66
|
-
configFile: path.join(apioskDir, "config.json"),
|
|
67
|
-
connectEnvFile: path.join(apioskDir, "connect.env"),
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export async function readLocalApioskConfig(env = process.env) {
|
|
72
|
-
const { configFile } = createApioskLocalConfigPaths(env);
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
const raw = await readFile(configFile, "utf8");
|
|
76
|
-
const parsed = JSON.parse(raw);
|
|
77
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
return parsed;
|
|
81
|
-
} catch (error) {
|
|
82
|
-
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
throw new Error(
|
|
86
|
-
`Could not read the local Apiosk config: ${
|
|
87
|
-
error instanceof Error ? error.message : String(error)
|
|
88
|
-
}`
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export async function saveLocalApioskConfig(input = {}, env = process.env) {
|
|
94
|
-
const paths = createApioskLocalConfigPaths(env);
|
|
95
|
-
const existing = (await readLocalApioskConfig(env)) || {};
|
|
96
|
-
const gatewayUrl = trimString(input.gateway_url || DEFAULT_GATEWAY_URL).replace(/\/+$/, "");
|
|
97
|
-
const chainId = Number.parseInt(String(input.chain_id || DEFAULT_CHAIN_ID), 10) || DEFAULT_CHAIN_ID;
|
|
98
|
-
const walletAddress = normalizeWalletAddress(input.agent_wallet_address || input.wallet_address, {
|
|
99
|
-
optional: true,
|
|
100
|
-
});
|
|
101
|
-
const connectToken = normalizeConnectToken(input.connect_token, { optional: true });
|
|
102
|
-
const connectAuthorization = trimString(
|
|
103
|
-
input.connect_authorization || (connectToken ? `Bearer ${connectToken}` : "")
|
|
104
|
-
);
|
|
105
|
-
const connectHeaderName = trimString(input.connect_header_name || DEFAULT_CONNECT_HEADER_NAME);
|
|
106
|
-
const dailyLimitUsdc = Number.isFinite(Number(input.daily_limit_usdc))
|
|
107
|
-
? Number(input.daily_limit_usdc)
|
|
108
|
-
: 100;
|
|
109
|
-
const perRequestLimitUsdc = Number.isFinite(Number(input.per_request_limit_usdc))
|
|
110
|
-
? Number(input.per_request_limit_usdc)
|
|
111
|
-
: 1;
|
|
112
|
-
|
|
113
|
-
if (!connectToken && !connectAuthorization) {
|
|
114
|
-
throw new Error("A connect token or connect authorization value is required.");
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const config = {
|
|
118
|
-
rpc_url: DEFAULT_RPC_URL,
|
|
119
|
-
chain_id: chainId,
|
|
120
|
-
usdc_contract: DEFAULT_USDC_CONTRACT,
|
|
121
|
-
gateway_url: gatewayUrl,
|
|
122
|
-
agent_wallet_address: walletAddress || null,
|
|
123
|
-
daily_limit_usdc: dailyLimitUsdc,
|
|
124
|
-
per_request_limit_usdc: perRequestLimitUsdc,
|
|
125
|
-
connect_token: connectToken || null,
|
|
126
|
-
connect_authorization: connectAuthorization || null,
|
|
127
|
-
connect_header_name: connectHeaderName || DEFAULT_CONNECT_HEADER_NAME,
|
|
128
|
-
control_plane_url:
|
|
129
|
-
trimString(existing.control_plane_url || env.APIOSK_CONTROL_PLANE_URL || DEFAULT_CONTROL_PLANE_URL).replace(
|
|
130
|
-
/\/+$/,
|
|
131
|
-
""
|
|
132
|
-
) || null,
|
|
133
|
-
dashboard_url:
|
|
134
|
-
trimString(existing.dashboard_url || env.APIOSK_DASHBOARD_URL || DEFAULT_DASHBOARD_URL).replace(/\/+$/, "") ||
|
|
135
|
-
null,
|
|
136
|
-
dashboard_session_token: trimString(existing.dashboard_session_token) || null,
|
|
137
|
-
dashboard_session_email: trimString(existing.dashboard_session_email) || null,
|
|
138
|
-
dashboard_session_expires_at: Number.isFinite(Number(existing.dashboard_session_expires_at))
|
|
139
|
-
? Number(existing.dashboard_session_expires_at)
|
|
140
|
-
: null,
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const connectEnv = [
|
|
144
|
-
`APIO_GATEWAY_URL=${gatewayUrl}`,
|
|
145
|
-
`APIO_CHAIN_ID=${chainId}`,
|
|
146
|
-
`APIO_AGENT_WALLET_ADDRESS=${walletAddress}`,
|
|
147
|
-
`APIO_CONNECT_TOKEN=${connectToken}`,
|
|
148
|
-
`APIO_CONNECT_AUTHORIZATION=${connectAuthorization}`,
|
|
149
|
-
`APIO_CONNECT_HEADER_NAME=${connectHeaderName || DEFAULT_CONNECT_HEADER_NAME}`,
|
|
150
|
-
`APIO_WALLET_DAILY_LIMIT_USDC=${dailyLimitUsdc}`,
|
|
151
|
-
`APIO_WALLET_PER_TX_LIMIT_USDC=${perRequestLimitUsdc}`,
|
|
152
|
-
].join("\n");
|
|
153
|
-
|
|
154
|
-
await writeAtomic(paths.configFile, `${JSON.stringify(config, null, 2)}\n`);
|
|
155
|
-
await writeAtomic(paths.connectEnvFile, `${connectEnv}\n`);
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
paths,
|
|
159
|
-
config,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export async function saveLocalApioskDashboardSession(input = {}, env = process.env) {
|
|
164
|
-
const paths = createApioskLocalConfigPaths(env);
|
|
165
|
-
const existing = (await readLocalApioskConfig(env)) || {};
|
|
166
|
-
const sessionToken = trimString(input.dashboard_session_token || input.session_token);
|
|
167
|
-
|
|
168
|
-
if (!sessionToken) {
|
|
169
|
-
throw new Error("A dashboard session token is required.");
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const expiresAtRaw = input.dashboard_session_expires_at ?? input.expires_at;
|
|
173
|
-
const expiresAt = Number(expiresAtRaw);
|
|
174
|
-
const config = {
|
|
175
|
-
...existing,
|
|
176
|
-
control_plane_url:
|
|
177
|
-
trimString(
|
|
178
|
-
input.control_plane_url || existing.control_plane_url || env.APIOSK_CONTROL_PLANE_URL || DEFAULT_CONTROL_PLANE_URL
|
|
179
|
-
).replace(/\/+$/, "") || null,
|
|
180
|
-
dashboard_url:
|
|
181
|
-
trimString(input.dashboard_url || existing.dashboard_url || env.APIOSK_DASHBOARD_URL || DEFAULT_DASHBOARD_URL).replace(/\/+$/, "") ||
|
|
182
|
-
null,
|
|
183
|
-
dashboard_session_token: sessionToken,
|
|
184
|
-
dashboard_session_email:
|
|
185
|
-
trimString(input.dashboard_session_email || input.email || existing.dashboard_session_email) || null,
|
|
186
|
-
dashboard_session_expires_at: Number.isFinite(expiresAt)
|
|
187
|
-
? expiresAt
|
|
188
|
-
: Number.isFinite(Number(existing.dashboard_session_expires_at))
|
|
189
|
-
? Number(existing.dashboard_session_expires_at)
|
|
190
|
-
: null,
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
await writeAtomic(paths.configFile, `${JSON.stringify(config, null, 2)}\n`);
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
paths,
|
|
197
|
-
config,
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export function parseConnectString(raw) {
|
|
202
|
-
const lines = String(raw || "").split(/\r?\n/);
|
|
203
|
-
const parsed = {
|
|
204
|
-
gateway_url: "",
|
|
205
|
-
chain_id: DEFAULT_CHAIN_ID,
|
|
206
|
-
wallet_address: "",
|
|
207
|
-
connect_token: "",
|
|
208
|
-
connect_authorization: "",
|
|
209
|
-
connect_header_name: DEFAULT_CONNECT_HEADER_NAME,
|
|
210
|
-
daily_limit_usdc: 100,
|
|
211
|
-
per_request_limit_usdc: 1,
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
for (const line of lines) {
|
|
215
|
-
const trimmed = line.trim();
|
|
216
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
217
|
-
|
|
218
|
-
const normalizedLine = trimmed.startsWith("export ") ? trimmed.slice(7) : trimmed;
|
|
219
|
-
const separatorIndex = normalizedLine.indexOf("=");
|
|
220
|
-
if (separatorIndex === -1) continue;
|
|
221
|
-
|
|
222
|
-
const key = normalizedLine.slice(0, separatorIndex).trim();
|
|
223
|
-
let value = normalizedLine.slice(separatorIndex + 1).trim();
|
|
224
|
-
if (
|
|
225
|
-
(value.startsWith('"') && value.endsWith('"')) ||
|
|
226
|
-
(value.startsWith("'") && value.endsWith("'"))
|
|
227
|
-
) {
|
|
228
|
-
value = value.slice(1, -1);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (key === "APIO_GATEWAY_URL") parsed.gateway_url = value;
|
|
232
|
-
if (key === "APIO_CHAIN_ID") parsed.chain_id = Number.parseInt(value, 10) || DEFAULT_CHAIN_ID;
|
|
233
|
-
if (key === "APIO_AGENT_WALLET_ADDRESS") parsed.wallet_address = value;
|
|
234
|
-
if (key === "APIO_CONNECT_TOKEN") parsed.connect_token = value;
|
|
235
|
-
if (key === "APIO_CONNECT_AUTHORIZATION") parsed.connect_authorization = value;
|
|
236
|
-
if (key === "APIO_CONNECT_HEADER_NAME") parsed.connect_header_name = value || DEFAULT_CONNECT_HEADER_NAME;
|
|
237
|
-
if (key === "APIO_WALLET_DAILY_LIMIT_USDC") parsed.daily_limit_usdc = Number(value) || 100;
|
|
238
|
-
if (key === "APIO_WALLET_PER_TX_LIMIT_USDC") parsed.per_request_limit_usdc = Number(value) || 1;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (!parsed.gateway_url || !parsed.wallet_address || !parsed.connect_token) {
|
|
242
|
-
throw new Error("Connect string is missing one of: APIO_GATEWAY_URL, APIO_AGENT_WALLET_ADDRESS, APIO_CONNECT_TOKEN.");
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return {
|
|
246
|
-
gateway_url: trimString(parsed.gateway_url).replace(/\/+$/, ""),
|
|
247
|
-
chain_id: parsed.chain_id || DEFAULT_CHAIN_ID,
|
|
248
|
-
wallet_address: normalizeWalletAddress(parsed.wallet_address),
|
|
249
|
-
connect_token: normalizeConnectToken(parsed.connect_token),
|
|
250
|
-
connect_authorization:
|
|
251
|
-
trimString(parsed.connect_authorization) || `Bearer ${normalizeConnectToken(parsed.connect_token)}`,
|
|
252
|
-
connect_header_name: trimString(parsed.connect_header_name) || DEFAULT_CONNECT_HEADER_NAME,
|
|
253
|
-
daily_limit_usdc: parsed.daily_limit_usdc,
|
|
254
|
-
per_request_limit_usdc: parsed.per_request_limit_usdc,
|
|
255
|
-
};
|
|
256
|
-
}
|