@agentwonderland/mcp 0.1.23 → 0.1.25
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/dist/core/__tests__/amount-utils.test.d.ts +1 -0
- package/dist/core/__tests__/amount-utils.test.js +11 -0
- package/dist/core/__tests__/card-setup.test.d.ts +1 -0
- package/dist/core/__tests__/card-setup.test.js +99 -0
- package/dist/core/__tests__/formatters.test.d.ts +1 -0
- package/dist/core/__tests__/formatters.test.js +15 -0
- package/dist/core/__tests__/passes.test.d.ts +1 -0
- package/dist/core/__tests__/passes.test.js +82 -0
- package/dist/core/__tests__/payments.test.d.ts +1 -0
- package/dist/core/__tests__/payments.test.js +101 -0
- package/dist/core/__tests__/principal.test.d.ts +1 -0
- package/dist/core/__tests__/principal.test.js +67 -0
- package/dist/core/__tests__/spend-policy.test.d.ts +1 -0
- package/dist/core/__tests__/spend-policy.test.js +40 -0
- package/dist/core/amount-utils.d.ts +1 -0
- package/dist/core/amount-utils.js +4 -0
- package/dist/core/api-client.d.ts +9 -4
- package/dist/core/api-client.js +52 -22
- package/dist/core/base-charge.js +3 -2
- package/dist/core/card-setup.d.ts +20 -13
- package/dist/core/card-setup.js +85 -29
- package/dist/core/config.d.ts +22 -0
- package/dist/core/config.js +46 -2
- package/dist/core/formatters.d.ts +4 -3
- package/dist/core/formatters.js +10 -8
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +2 -0
- package/dist/core/ows-adapter.d.ts +10 -2
- package/dist/core/ows-adapter.js +54 -10
- package/dist/core/passes.d.ts +40 -0
- package/dist/core/passes.js +32 -0
- package/dist/core/payments.d.ts +8 -0
- package/dist/core/payments.js +111 -17
- package/dist/core/principal.d.ts +2 -0
- package/dist/core/principal.js +109 -0
- package/dist/core/solana-charge.d.ts +9 -0
- package/dist/core/solana-charge.js +96 -0
- package/dist/core/spend-policy.d.ts +12 -0
- package/dist/core/spend-policy.js +53 -0
- package/dist/core/types.d.ts +11 -2
- package/dist/index.js +11 -3
- package/dist/prompts/index.js +4 -2
- package/dist/resources/agents.js +1 -1
- package/dist/resources/wallet.js +8 -1
- package/dist/tools/__tests__/_payment-confirmation.test.d.ts +1 -0
- package/dist/tools/__tests__/_payment-confirmation.test.js +30 -0
- package/dist/tools/_payment-confirmation.d.ts +6 -0
- package/dist/tools/_payment-confirmation.js +28 -0
- package/dist/tools/agent-info.js +16 -2
- package/dist/tools/favorites.js +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +1 -0
- package/dist/tools/observability.d.ts +2 -0
- package/dist/tools/observability.js +20 -0
- package/dist/tools/passes.d.ts +2 -0
- package/dist/tools/passes.js +157 -0
- package/dist/tools/run.js +127 -53
- package/dist/tools/solve.js +115 -51
- package/dist/tools/wallet.js +110 -59
- package/package.json +3 -1
- package/src/core/__tests__/amount-utils.test.ts +13 -0
- package/src/core/__tests__/card-setup.test.ts +118 -0
- package/src/core/__tests__/formatters.test.ts +17 -0
- package/src/core/__tests__/passes.test.ts +94 -0
- package/src/core/__tests__/payments.test.ts +122 -0
- package/src/core/__tests__/principal.test.ts +87 -0
- package/src/core/__tests__/spend-policy.test.ts +58 -0
- package/src/core/amount-utils.ts +5 -0
- package/src/core/api-client.ts +70 -23
- package/src/core/base-charge.ts +3 -2
- package/src/core/card-setup.ts +109 -34
- package/src/core/config.ts +74 -3
- package/src/core/formatters.ts +13 -9
- package/src/core/index.ts +2 -0
- package/src/core/ows-adapter.ts +74 -8
- package/src/core/passes.ts +74 -0
- package/src/core/payments.ts +130 -17
- package/src/core/principal.ts +128 -0
- package/src/core/solana-charge.ts +150 -0
- package/src/core/spend-policy.ts +69 -0
- package/src/core/types.ts +11 -2
- package/src/index.ts +11 -3
- package/src/prompts/index.ts +4 -2
- package/src/resources/agents.ts +1 -1
- package/src/resources/wallet.ts +8 -1
- package/src/tools/__tests__/_payment-confirmation.test.ts +45 -0
- package/src/tools/_payment-confirmation.ts +52 -0
- package/src/tools/agent-info.ts +25 -2
- package/src/tools/favorites.ts +1 -4
- package/src/tools/index.ts +1 -0
- package/src/tools/observability.ts +43 -0
- package/src/tools/passes.ts +228 -0
- package/src/tools/run.ts +174 -57
- package/src/tools/solve.ts +147 -59
- package/src/tools/wallet.ts +132 -62
|
@@ -1,23 +1,30 @@
|
|
|
1
|
+
export interface CardSetupResult {
|
|
2
|
+
last4: string;
|
|
3
|
+
brand: string;
|
|
4
|
+
consumerToken: string;
|
|
5
|
+
}
|
|
6
|
+
export interface CardCapabilities {
|
|
7
|
+
spt_status: "enabled" | "unavailable" | "unknown";
|
|
8
|
+
message?: string;
|
|
9
|
+
}
|
|
1
10
|
/**
|
|
2
|
-
*
|
|
3
|
-
* QR code + short URL for the user to scan.
|
|
11
|
+
* Create a new card setup session or resume the pending one from config.
|
|
4
12
|
*/
|
|
5
|
-
export declare function
|
|
6
|
-
qr: string;
|
|
13
|
+
export declare function getOrCreatePendingCardSetup(): Promise<{
|
|
7
14
|
url: string;
|
|
8
15
|
token: string;
|
|
16
|
+
isNew: boolean;
|
|
9
17
|
}>;
|
|
10
18
|
/**
|
|
11
|
-
* Format the card setup prompt as
|
|
12
|
-
*
|
|
13
|
-
*
|
|
19
|
+
* Format the card setup prompt as a single text block.
|
|
20
|
+
*
|
|
21
|
+
* Some MCP clients only surface one text chunk or collapse multi-part tool
|
|
22
|
+
* output, which can hide the QR code entirely. Keeping everything in one
|
|
23
|
+
* message makes the browser handoff much more reliable.
|
|
14
24
|
*/
|
|
15
|
-
export declare function formatCardSetupBlocks(
|
|
25
|
+
export declare function formatCardSetupBlocks(url: string): string[];
|
|
16
26
|
/**
|
|
17
27
|
* Poll for card setup completion. Returns the card details or null on timeout.
|
|
18
28
|
*/
|
|
19
|
-
export declare function pollCardSetup(token: string, timeoutMs?: number): Promise<
|
|
20
|
-
|
|
21
|
-
brand: string;
|
|
22
|
-
consumerToken: string;
|
|
23
|
-
} | null>;
|
|
29
|
+
export declare function pollCardSetup(token: string, timeoutMs?: number): Promise<CardSetupResult | null>;
|
|
30
|
+
export declare function getCardCapabilities(): Promise<CardCapabilities>;
|
package/dist/core/card-setup.js
CHANGED
|
@@ -1,41 +1,50 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { apiPost, apiGet, ApiError } from "./api-client.js";
|
|
2
|
+
import { getApiUrl, setCardConfig, getPendingCardSetupToken, setPendingCardSetupToken, } from "./config.js";
|
|
3
|
+
let cachedCapabilities = null;
|
|
4
|
+
function buildCardSetupUrl(token) {
|
|
5
|
+
return `${getApiUrl()}/card/handoff/${token}`;
|
|
6
|
+
}
|
|
4
7
|
/**
|
|
5
|
-
*
|
|
6
|
-
* QR code + short URL for the user to scan.
|
|
8
|
+
* Create a new card setup session or resume the pending one from config.
|
|
7
9
|
*/
|
|
8
|
-
export async function
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
return {
|
|
10
|
+
export async function getOrCreatePendingCardSetup() {
|
|
11
|
+
const pendingToken = getPendingCardSetupToken();
|
|
12
|
+
if (pendingToken) {
|
|
13
|
+
const url = buildCardSetupUrl(pendingToken);
|
|
14
|
+
return {
|
|
15
|
+
url,
|
|
16
|
+
token: pendingToken,
|
|
17
|
+
isNew: false,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const { token } = await apiPost("/card/setup", {});
|
|
21
|
+
setPendingCardSetupToken(token);
|
|
22
|
+
const url = buildCardSetupUrl(token);
|
|
23
|
+
return {
|
|
24
|
+
url,
|
|
25
|
+
token,
|
|
26
|
+
isNew: true,
|
|
27
|
+
};
|
|
22
28
|
}
|
|
23
29
|
/**
|
|
24
|
-
* Format the card setup prompt as
|
|
25
|
-
*
|
|
26
|
-
*
|
|
30
|
+
* Format the card setup prompt as a single text block.
|
|
31
|
+
*
|
|
32
|
+
* Some MCP clients only surface one text chunk or collapse multi-part tool
|
|
33
|
+
* output, which can hide the QR code entirely. Keeping everything in one
|
|
34
|
+
* message makes the browser handoff much more reliable.
|
|
27
35
|
*/
|
|
28
|
-
export function formatCardSetupBlocks(
|
|
36
|
+
export function formatCardSetupBlocks(url) {
|
|
29
37
|
return [
|
|
30
|
-
"\n" + qr.trim(),
|
|
31
38
|
[
|
|
32
|
-
|
|
39
|
+
"Open this setup page to connect your card:",
|
|
33
40
|
"",
|
|
34
41
|
url,
|
|
35
42
|
"",
|
|
36
|
-
|
|
43
|
+
"The setup page will take you to Stripe to securely save the card.",
|
|
44
|
+
"",
|
|
45
|
+
`Tell the user: "Open this setup page to connect your card. Let me know when you're done."`,
|
|
37
46
|
`After they confirm, call wallet_setup({ action: "add-card" }) to complete setup.`,
|
|
38
|
-
`Crypto wallets (
|
|
47
|
+
`Crypto wallets (Tempo, Base, or Solana USDC) are also available via wallet_setup({ action: "create" }).`,
|
|
39
48
|
].join("\n"),
|
|
40
49
|
];
|
|
41
50
|
}
|
|
@@ -45,7 +54,6 @@ export function formatCardSetupBlocks(qr, url) {
|
|
|
45
54
|
export async function pollCardSetup(token, timeoutMs = 120_000) {
|
|
46
55
|
const deadline = Date.now() + timeoutMs;
|
|
47
56
|
while (Date.now() < deadline) {
|
|
48
|
-
await new Promise((r) => setTimeout(r, 3000));
|
|
49
57
|
try {
|
|
50
58
|
const status = await apiGet(`/card/status?token=${token}`);
|
|
51
59
|
if (status.status === "complete" && status.consumer_token) {
|
|
@@ -61,12 +69,60 @@ export async function pollCardSetup(token, timeoutMs = 120_000) {
|
|
|
61
69
|
last4: card.last4,
|
|
62
70
|
brand: card.brand,
|
|
63
71
|
});
|
|
72
|
+
setPendingCardSetupToken(null);
|
|
64
73
|
return card;
|
|
65
74
|
}
|
|
66
75
|
}
|
|
67
|
-
catch {
|
|
76
|
+
catch (err) {
|
|
77
|
+
if (err instanceof ApiError && err.status === 404) {
|
|
78
|
+
setPendingCardSetupToken(null);
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
68
81
|
// Keep polling
|
|
69
82
|
}
|
|
83
|
+
const remainingMs = deadline - Date.now();
|
|
84
|
+
if (remainingMs <= 0)
|
|
85
|
+
break;
|
|
86
|
+
await new Promise((r) => setTimeout(r, Math.min(3000, remainingMs)));
|
|
70
87
|
}
|
|
71
88
|
return null;
|
|
72
89
|
}
|
|
90
|
+
export async function getCardCapabilities() {
|
|
91
|
+
const apiUrl = getApiUrl();
|
|
92
|
+
if (cachedCapabilities && cachedCapabilities.apiUrl === apiUrl && cachedCapabilities.expiresAt > Date.now()) {
|
|
93
|
+
return cachedCapabilities.value;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const capabilities = await apiGet("/card/capabilities");
|
|
97
|
+
cachedCapabilities = {
|
|
98
|
+
value: capabilities,
|
|
99
|
+
apiUrl,
|
|
100
|
+
expiresAt: Date.now() + 30_000,
|
|
101
|
+
};
|
|
102
|
+
return capabilities;
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
if (err instanceof ApiError) {
|
|
106
|
+
const fallback = {
|
|
107
|
+
spt_status: "unknown",
|
|
108
|
+
message: err.message,
|
|
109
|
+
};
|
|
110
|
+
cachedCapabilities = {
|
|
111
|
+
value: fallback,
|
|
112
|
+
apiUrl,
|
|
113
|
+
expiresAt: Date.now() + 10_000,
|
|
114
|
+
};
|
|
115
|
+
return fallback;
|
|
116
|
+
}
|
|
117
|
+
const fallback = {
|
|
118
|
+
spt_status: "unknown",
|
|
119
|
+
message: "Could not determine card payment availability.",
|
|
120
|
+
};
|
|
121
|
+
cachedCapabilities = {
|
|
122
|
+
value: fallback,
|
|
123
|
+
apiUrl,
|
|
124
|
+
expiresAt: Date.now() + 10_000,
|
|
125
|
+
};
|
|
126
|
+
return fallback;
|
|
127
|
+
}
|
|
128
|
+
}
|
package/dist/core/config.d.ts
CHANGED
|
@@ -13,6 +13,17 @@ export interface CardConfig {
|
|
|
13
13
|
last4: string;
|
|
14
14
|
brand: string;
|
|
15
15
|
}
|
|
16
|
+
export interface SpendPolicy {
|
|
17
|
+
maxPerTxUsd?: number;
|
|
18
|
+
maxPerDayUsd?: number;
|
|
19
|
+
requireConfirmationAboveUsd?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface SpendLedgerEntry {
|
|
22
|
+
method: string;
|
|
23
|
+
amountUsd: number;
|
|
24
|
+
day: string;
|
|
25
|
+
timestamp: string;
|
|
26
|
+
}
|
|
16
27
|
export interface Config {
|
|
17
28
|
apiUrl: string;
|
|
18
29
|
apiKey: string | null;
|
|
@@ -21,11 +32,16 @@ export interface Config {
|
|
|
21
32
|
defaultWallet: string | null;
|
|
22
33
|
defaultPaymentMethod?: string;
|
|
23
34
|
card: CardConfig | null;
|
|
35
|
+
pendingCardSetupToken?: string | null;
|
|
24
36
|
favorites: string[];
|
|
25
37
|
/** Require user confirmation before spending. Default: true. Set false for headless/automated use. */
|
|
26
38
|
confirmBeforeSpend: boolean;
|
|
27
39
|
/** Auto-tip amount in USD for successful runs. Default: 0 (no auto-tip). */
|
|
28
40
|
defaultTipAmount: number;
|
|
41
|
+
/** Optional per-method spend policies enforced client-side by MCP tools. */
|
|
42
|
+
spendPolicies?: Record<string, SpendPolicy>;
|
|
43
|
+
/** Daily spend ledger used to enforce max-per-day limits. */
|
|
44
|
+
spendLedger?: SpendLedgerEntry[];
|
|
29
45
|
}
|
|
30
46
|
/** All supported chain identifiers. */
|
|
31
47
|
export declare const SUPPORTED_CHAINS: readonly ["tempo", "base", "solana"];
|
|
@@ -42,6 +58,10 @@ export declare function getApiKey(): string | null;
|
|
|
42
58
|
export declare function isAuthenticated(): boolean;
|
|
43
59
|
export declare function requiresSpendConfirmation(): boolean;
|
|
44
60
|
export declare function getDefaultTipAmount(): number;
|
|
61
|
+
export declare function getSpendPolicy(method: string): SpendPolicy | null;
|
|
62
|
+
export declare function setSpendPolicy(method: string, policy: SpendPolicy): void;
|
|
63
|
+
export declare function getSpendLedger(): SpendLedgerEntry[];
|
|
64
|
+
export declare function saveSpendLedger(entries: SpendLedgerEntry[]): void;
|
|
45
65
|
/**
|
|
46
66
|
* Get all wallets from config + env var synthetic wallets.
|
|
47
67
|
*/
|
|
@@ -73,6 +93,8 @@ export declare function getCardConfig(): CardConfig | null;
|
|
|
73
93
|
* Save card configuration after setup.
|
|
74
94
|
*/
|
|
75
95
|
export declare function setCardConfig(card: CardConfig | null): void;
|
|
96
|
+
export declare function getPendingCardSetupToken(): string | null;
|
|
97
|
+
export declare function setPendingCardSetupToken(token: string | null): void;
|
|
76
98
|
/**
|
|
77
99
|
* Resolve a payment method string to a wallet + chain.
|
|
78
100
|
* Accepts: wallet ID, chain name, or "card".
|
package/dist/core/config.js
CHANGED
|
@@ -34,10 +34,14 @@ function migrateIfNeeded(raw) {
|
|
|
34
34
|
userId: raw.userId ?? null,
|
|
35
35
|
wallets: raw.wallets,
|
|
36
36
|
defaultWallet: raw.defaultWallet ?? null,
|
|
37
|
+
defaultPaymentMethod: raw.defaultPaymentMethod ?? undefined,
|
|
37
38
|
card: raw.card ?? null,
|
|
39
|
+
pendingCardSetupToken: r.pendingCardSetupToken ?? null,
|
|
38
40
|
favorites: r.favorites ?? [],
|
|
39
41
|
confirmBeforeSpend: r.confirmBeforeSpend !== false,
|
|
40
42
|
defaultTipAmount: typeof r.defaultTipAmount === "number" ? r.defaultTipAmount : 0,
|
|
43
|
+
spendPolicies: r.spendPolicies,
|
|
44
|
+
spendLedger: r.spendLedger,
|
|
41
45
|
};
|
|
42
46
|
}
|
|
43
47
|
// Build wallets from legacy flat fields
|
|
@@ -103,10 +107,14 @@ function migrateIfNeeded(raw) {
|
|
|
103
107
|
userId: raw.userId ?? null,
|
|
104
108
|
wallets,
|
|
105
109
|
defaultWallet,
|
|
110
|
+
defaultPaymentMethod: raw.defaultPaymentMethod ?? undefined,
|
|
106
111
|
card,
|
|
112
|
+
pendingCardSetupToken: null,
|
|
107
113
|
favorites: [],
|
|
108
114
|
confirmBeforeSpend: true,
|
|
109
115
|
defaultTipAmount: 0,
|
|
116
|
+
spendPolicies: {},
|
|
117
|
+
spendLedger: [],
|
|
110
118
|
};
|
|
111
119
|
// Write migrated config (only if there was something to migrate)
|
|
112
120
|
if (raw.tempoPrivateKey || raw.evmPrivateKey || raw.stripeConsumerToken) {
|
|
@@ -124,9 +132,12 @@ export function getConfig() {
|
|
|
124
132
|
wallets: [],
|
|
125
133
|
defaultWallet: null,
|
|
126
134
|
card: null,
|
|
135
|
+
pendingCardSetupToken: null,
|
|
127
136
|
favorites: [],
|
|
128
137
|
confirmBeforeSpend: true,
|
|
129
138
|
defaultTipAmount: 0,
|
|
139
|
+
spendPolicies: {},
|
|
140
|
+
spendLedger: [],
|
|
130
141
|
};
|
|
131
142
|
if (!existsSync(CONFIG_FILE)) {
|
|
132
143
|
return defaults;
|
|
@@ -167,6 +178,22 @@ export function requiresSpendConfirmation() {
|
|
|
167
178
|
export function getDefaultTipAmount() {
|
|
168
179
|
return getConfig().defaultTipAmount;
|
|
169
180
|
}
|
|
181
|
+
export function getSpendPolicy(method) {
|
|
182
|
+
const policies = getConfig().spendPolicies ?? {};
|
|
183
|
+
return policies[method] ?? null;
|
|
184
|
+
}
|
|
185
|
+
export function setSpendPolicy(method, policy) {
|
|
186
|
+
const config = getConfig();
|
|
187
|
+
const policies = config.spendPolicies ?? {};
|
|
188
|
+
policies[method] = policy;
|
|
189
|
+
saveConfig({ spendPolicies: policies });
|
|
190
|
+
}
|
|
191
|
+
export function getSpendLedger() {
|
|
192
|
+
return getConfig().spendLedger ?? [];
|
|
193
|
+
}
|
|
194
|
+
export function saveSpendLedger(entries) {
|
|
195
|
+
saveConfig({ spendLedger: entries });
|
|
196
|
+
}
|
|
170
197
|
// ── Wallet helpers ─────────────────────────────────────────────────
|
|
171
198
|
/**
|
|
172
199
|
* Get all wallets from config + env var synthetic wallets.
|
|
@@ -263,13 +290,30 @@ export function getCardConfig() {
|
|
|
263
290
|
* Save card configuration after setup.
|
|
264
291
|
*/
|
|
265
292
|
export function setCardConfig(card) {
|
|
293
|
+
const current = getConfig();
|
|
266
294
|
if (card) {
|
|
267
|
-
saveConfig({
|
|
295
|
+
saveConfig({
|
|
296
|
+
card,
|
|
297
|
+
defaultPaymentMethod: "card",
|
|
298
|
+
pendingCardSetupToken: null,
|
|
299
|
+
});
|
|
268
300
|
}
|
|
269
301
|
else {
|
|
270
|
-
saveConfig({
|
|
302
|
+
saveConfig({
|
|
303
|
+
card,
|
|
304
|
+
pendingCardSetupToken: null,
|
|
305
|
+
defaultPaymentMethod: current.defaultPaymentMethod === "card"
|
|
306
|
+
? undefined
|
|
307
|
+
: current.defaultPaymentMethod,
|
|
308
|
+
});
|
|
271
309
|
}
|
|
272
310
|
}
|
|
311
|
+
export function getPendingCardSetupToken() {
|
|
312
|
+
return getConfig().pendingCardSetupToken ?? null;
|
|
313
|
+
}
|
|
314
|
+
export function setPendingCardSetupToken(token) {
|
|
315
|
+
saveConfig({ pendingCardSetupToken: token });
|
|
316
|
+
}
|
|
273
317
|
/**
|
|
274
318
|
* Resolve a payment method string to a wallet + chain.
|
|
275
319
|
* Accepts: wallet ID, chain name, or "card".
|
|
@@ -11,7 +11,7 @@ export declare function isFileOutput(output: unknown): output is {
|
|
|
11
11
|
mime_type?: string;
|
|
12
12
|
size_bytes?: number;
|
|
13
13
|
};
|
|
14
|
-
export declare function formatPrice(
|
|
14
|
+
export declare function formatPrice(pricePerRunUsd?: string | null): string;
|
|
15
15
|
interface AgentLike {
|
|
16
16
|
id?: string;
|
|
17
17
|
name?: string;
|
|
@@ -19,8 +19,7 @@ interface AgentLike {
|
|
|
19
19
|
avgRating?: number | null;
|
|
20
20
|
ratingCount?: number;
|
|
21
21
|
totalExecutions?: number;
|
|
22
|
-
|
|
23
|
-
pricingModel?: string;
|
|
22
|
+
pricePerRunUsd?: string;
|
|
24
23
|
stats?: {
|
|
25
24
|
completedJobs?: number;
|
|
26
25
|
avgRating?: number | null;
|
|
@@ -43,6 +42,8 @@ interface RunResultLike {
|
|
|
43
42
|
estimated_cost?: number;
|
|
44
43
|
input_tokens?: number;
|
|
45
44
|
tags?: string[];
|
|
45
|
+
consumption_mode?: string;
|
|
46
|
+
credit_pack_id?: string;
|
|
46
47
|
}
|
|
47
48
|
export declare function formatRunResult(result: RunResultLike, opts?: {
|
|
48
49
|
paymentMethod?: string;
|
package/dist/core/formatters.js
CHANGED
|
@@ -32,20 +32,18 @@ export function isFileOutput(output) {
|
|
|
32
32
|
return output != null && typeof output === "object" && output.type === "file" && !!output.url;
|
|
33
33
|
}
|
|
34
34
|
// ── Price formatting ─────────────────────────────────────────────
|
|
35
|
-
export function formatPrice(
|
|
36
|
-
if (!
|
|
35
|
+
export function formatPrice(pricePerRunUsd) {
|
|
36
|
+
if (!pricePerRunUsd)
|
|
37
37
|
return "free";
|
|
38
|
-
const p = parseFloat(
|
|
39
|
-
|
|
40
|
-
return `$${p.toFixed(2)}/req`;
|
|
41
|
-
return `$${p.toFixed(3)}/1k tokens`;
|
|
38
|
+
const p = parseFloat(pricePerRunUsd);
|
|
39
|
+
return `$${p.toFixed(2)}/req`;
|
|
42
40
|
}
|
|
43
41
|
export function agentLine(agent) {
|
|
44
42
|
const name = agent.name ?? "Unknown";
|
|
45
43
|
const slug = agent.slug ? ` (${agent.slug})` : "";
|
|
46
44
|
const rating = agent.avgRating ?? agent.stats?.avgRating ?? null;
|
|
47
45
|
const jobs = agent.stats?.completedJobs ?? agent.totalExecutions ?? 0;
|
|
48
|
-
const price = formatPrice(agent.
|
|
46
|
+
const price = formatPrice(agent.pricePerRunUsd);
|
|
49
47
|
const reliability = agent.successRate != null && Number(agent.successRate) < 1
|
|
50
48
|
? ` • ${(Number(agent.successRate) * 100).toFixed(0)}% reliable`
|
|
51
49
|
: "";
|
|
@@ -110,6 +108,7 @@ export function formatRunResult(result, opts) {
|
|
|
110
108
|
}
|
|
111
109
|
// Summary line
|
|
112
110
|
const cost = result.cost ?? result.estimated_cost;
|
|
111
|
+
const usedCreditPack = result.consumption_mode === "credit_pack";
|
|
113
112
|
const status = result.status === "success" || result.status === "completed" ? "✓" : "✗";
|
|
114
113
|
const agent = result.agent_name ?? result.agent_id ?? "";
|
|
115
114
|
const costStr = cost != null ? `$${cost.toFixed(cost < 0.01 ? 6 : 2)}` : "";
|
|
@@ -119,7 +118,10 @@ export function formatRunResult(result, opts) {
|
|
|
119
118
|
if (result.error_code) {
|
|
120
119
|
lines.push(`Error: ${result.error_code}`);
|
|
121
120
|
}
|
|
122
|
-
if (
|
|
121
|
+
if (usedCreditPack) {
|
|
122
|
+
lines.push(`Covered by credit pack${result.credit_pack_id ? ` (${result.credit_pack_id})` : ""}`);
|
|
123
|
+
}
|
|
124
|
+
if (costStr && !usedCreditPack) {
|
|
123
125
|
lines.push(`Paid: ${costStr}${method ? ` via ${method}` : ""}`);
|
|
124
126
|
}
|
|
125
127
|
if (result.job_id) {
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* ALL imports are dynamic so the MCP server works without OWS installed.
|
|
9
9
|
*/
|
|
10
10
|
import type { LocalAccount } from "viem/accounts";
|
|
11
|
+
import type { Keypair } from "@solana/web3.js";
|
|
11
12
|
/**
|
|
12
13
|
* Check whether the OWS native module can be loaded.
|
|
13
14
|
*/
|
|
@@ -19,17 +20,19 @@ export declare function isOwsAvailable(): Promise<boolean>;
|
|
|
19
20
|
* @param chain - OWS chain identifier (default "evm")
|
|
20
21
|
*/
|
|
21
22
|
export declare function owsAccountFromWalletId(walletId: string, _chain?: string): Promise<LocalAccount>;
|
|
23
|
+
export declare function owsSolanaKeypairFromWalletId(walletId: string): Promise<Keypair>;
|
|
24
|
+
export declare function getOwsWalletAddress(walletId: string, chain?: "evm" | "solana"): Promise<string>;
|
|
22
25
|
/**
|
|
23
26
|
* Create a new OWS wallet and return its ID + EVM address.
|
|
24
27
|
*/
|
|
25
|
-
export declare function createOwsWallet(name: string): Promise<{
|
|
28
|
+
export declare function createOwsWallet(name: string, chain?: "evm" | "solana"): Promise<{
|
|
26
29
|
walletId: string;
|
|
27
30
|
address: string;
|
|
28
31
|
}>;
|
|
29
32
|
/**
|
|
30
33
|
* Import an existing EVM private key into OWS.
|
|
31
34
|
*/
|
|
32
|
-
export declare function importKeyToOws(privateKey: string, name: string): Promise<{
|
|
35
|
+
export declare function importKeyToOws(privateKey: string, name: string, chain?: "evm" | "solana"): Promise<{
|
|
33
36
|
walletId: string;
|
|
34
37
|
address: string;
|
|
35
38
|
}>;
|
|
@@ -41,3 +44,8 @@ export declare function listOwsWallets(): Promise<Array<{
|
|
|
41
44
|
name: string;
|
|
42
45
|
address: string;
|
|
43
46
|
}>>;
|
|
47
|
+
export declare function listOwsWalletsByChain(chain?: "evm" | "solana"): Promise<Array<{
|
|
48
|
+
id: string;
|
|
49
|
+
name: string;
|
|
50
|
+
address: string;
|
|
51
|
+
}>>;
|
package/dist/core/ows-adapter.js
CHANGED
|
@@ -38,6 +38,26 @@ function findEvmAccount(wallet) {
|
|
|
38
38
|
}
|
|
39
39
|
return evm;
|
|
40
40
|
}
|
|
41
|
+
function findSolanaAccount(wallet) {
|
|
42
|
+
const solana = wallet.accounts.find((a) => a.chainId.startsWith("solana"));
|
|
43
|
+
if (!solana) {
|
|
44
|
+
throw new Error(`Wallet "${wallet.name}" (${wallet.id}) has no Solana account.`);
|
|
45
|
+
}
|
|
46
|
+
return solana;
|
|
47
|
+
}
|
|
48
|
+
function ed25519HexToBytes(privateKeyHex) {
|
|
49
|
+
return Uint8Array.from(Buffer.from(privateKeyHex, "hex"));
|
|
50
|
+
}
|
|
51
|
+
function keypairFromEd25519Hex(privateKeyHex, KeypairCtor) {
|
|
52
|
+
const bytes = ed25519HexToBytes(privateKeyHex);
|
|
53
|
+
if (bytes.length === 32) {
|
|
54
|
+
return KeypairCtor.fromSeed(bytes);
|
|
55
|
+
}
|
|
56
|
+
if (bytes.length === 64) {
|
|
57
|
+
return KeypairCtor.fromSecretKey(bytes);
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`Unsupported ed25519 key length: ${bytes.length} bytes.`);
|
|
60
|
+
}
|
|
41
61
|
// ── Public API ───────────────────────────────────────────────────
|
|
42
62
|
/**
|
|
43
63
|
* Check whether the OWS native module can be loaded.
|
|
@@ -73,38 +93,62 @@ export async function owsAccountFromWalletId(walletId, _chain) {
|
|
|
73
93
|
const { privateKeyToAccount } = await import("viem/accounts");
|
|
74
94
|
return privateKeyToAccount(`0x${keys.secp256k1}`);
|
|
75
95
|
}
|
|
96
|
+
export async function owsSolanaKeypairFromWalletId(walletId) {
|
|
97
|
+
const ows = await loadOws();
|
|
98
|
+
const wallet = ows.getWallet(walletId);
|
|
99
|
+
findSolanaAccount(wallet);
|
|
100
|
+
const exported = ows.exportWallet(walletId);
|
|
101
|
+
const keys = JSON.parse(exported);
|
|
102
|
+
if (!keys.ed25519) {
|
|
103
|
+
throw new Error(`Wallet "${wallet.name}" has no ed25519 key for Solana signing.`);
|
|
104
|
+
}
|
|
105
|
+
const { Keypair } = await import("@solana/web3.js");
|
|
106
|
+
return keypairFromEd25519Hex(keys.ed25519, Keypair);
|
|
107
|
+
}
|
|
108
|
+
export async function getOwsWalletAddress(walletId, chain = "evm") {
|
|
109
|
+
const ows = await loadOws();
|
|
110
|
+
const wallet = ows.getWallet(walletId);
|
|
111
|
+
return chain === "solana"
|
|
112
|
+
? findSolanaAccount(wallet).address
|
|
113
|
+
: findEvmAccount(wallet).address;
|
|
114
|
+
}
|
|
76
115
|
/**
|
|
77
116
|
* Create a new OWS wallet and return its ID + EVM address.
|
|
78
117
|
*/
|
|
79
|
-
export async function createOwsWallet(name) {
|
|
118
|
+
export async function createOwsWallet(name, chain = "evm") {
|
|
80
119
|
const ows = await loadOws();
|
|
81
120
|
const wallet = ows.createWallet(name);
|
|
82
|
-
const
|
|
83
|
-
return { walletId: wallet.id, address:
|
|
121
|
+
const account = chain === "solana" ? findSolanaAccount(wallet) : findEvmAccount(wallet);
|
|
122
|
+
return { walletId: wallet.id, address: account.address };
|
|
84
123
|
}
|
|
85
124
|
/**
|
|
86
125
|
* Import an existing EVM private key into OWS.
|
|
87
126
|
*/
|
|
88
|
-
export async function importKeyToOws(privateKey, name) {
|
|
127
|
+
export async function importKeyToOws(privateKey, name, chain = "evm") {
|
|
89
128
|
const ows = await loadOws();
|
|
90
129
|
const normalizedKey = privateKey.startsWith("0x")
|
|
91
130
|
? privateKey.slice(2)
|
|
92
131
|
: privateKey;
|
|
93
|
-
const wallet = ows.importWalletPrivateKey(name, normalizedKey, null, null,
|
|
94
|
-
const
|
|
95
|
-
return { walletId: wallet.id, address:
|
|
132
|
+
const wallet = ows.importWalletPrivateKey(name, normalizedKey, null, null, chain);
|
|
133
|
+
const account = chain === "solana" ? findSolanaAccount(wallet) : findEvmAccount(wallet);
|
|
134
|
+
return { walletId: wallet.id, address: account.address };
|
|
96
135
|
}
|
|
97
136
|
/**
|
|
98
137
|
* List all OWS wallets that have an EVM account.
|
|
99
138
|
*/
|
|
100
139
|
export async function listOwsWallets() {
|
|
140
|
+
return listOwsWalletsByChain("evm");
|
|
141
|
+
}
|
|
142
|
+
export async function listOwsWalletsByChain(chain = "evm") {
|
|
101
143
|
const ows = await loadOws();
|
|
102
144
|
const wallets = ows.listWallets();
|
|
103
145
|
const result = [];
|
|
104
146
|
for (const w of wallets) {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
147
|
+
const account = chain === "solana"
|
|
148
|
+
? w.accounts.find((a) => a.chainId.startsWith("solana"))
|
|
149
|
+
: w.accounts.find((a) => a.chainId.startsWith("eip155") || a.chainId.startsWith("evm"));
|
|
150
|
+
if (account) {
|
|
151
|
+
result.push({ id: w.id, name: w.name, address: account.address });
|
|
108
152
|
}
|
|
109
153
|
}
|
|
110
154
|
return result;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { AgentRecord } from "./types.js";
|
|
2
|
+
export interface CreditPackOffer {
|
|
3
|
+
pack_id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
included_units: number;
|
|
6
|
+
price_usd: string;
|
|
7
|
+
effective_price_per_unit_usd?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface CreditPackRecord {
|
|
10
|
+
id: string;
|
|
11
|
+
status: string;
|
|
12
|
+
unit_type: string;
|
|
13
|
+
included_units: number;
|
|
14
|
+
remaining_units: number;
|
|
15
|
+
price_usd: string;
|
|
16
|
+
purchased_at: string;
|
|
17
|
+
pack?: {
|
|
18
|
+
name?: string | null;
|
|
19
|
+
key?: string | null;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface CreditPackProgramInfo {
|
|
23
|
+
unit_type?: string;
|
|
24
|
+
packs?: Array<{
|
|
25
|
+
key?: string;
|
|
26
|
+
name?: string;
|
|
27
|
+
included_units?: number;
|
|
28
|
+
price_usd?: string;
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
31
|
+
export interface CreditPackInventory {
|
|
32
|
+
consumer_principal: string | null;
|
|
33
|
+
offers: CreditPackOffer[];
|
|
34
|
+
balances: CreditPackRecord[];
|
|
35
|
+
}
|
|
36
|
+
export declare function getCreditPackProgram(agent: AgentRecord): CreditPackProgramInfo | null;
|
|
37
|
+
export declare function getCreditPackInventory(agentId: string): Promise<CreditPackInventory | null>;
|
|
38
|
+
export declare function getActiveCreditPack(inventory: CreditPackInventory | null): CreditPackRecord | null;
|
|
39
|
+
export declare function formatCreditPackOffer(offer: CreditPackOffer): string;
|
|
40
|
+
export declare function formatCreditPack(pack: CreditPackRecord): string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { apiGet } from "./api-client.js";
|
|
2
|
+
export function getCreditPackProgram(agent) {
|
|
3
|
+
const payment = (agent.payment ?? {});
|
|
4
|
+
const creditPacks = payment.credit_packs;
|
|
5
|
+
return creditPacks ?? null;
|
|
6
|
+
}
|
|
7
|
+
export async function getCreditPackInventory(agentId) {
|
|
8
|
+
try {
|
|
9
|
+
return await apiGet(`/agents/${agentId}/credit-packs`, { ensureConsumerPrincipal: true });
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function getActiveCreditPack(inventory) {
|
|
16
|
+
if (!inventory)
|
|
17
|
+
return null;
|
|
18
|
+
return inventory.balances
|
|
19
|
+
.filter((pack) => pack.status === "active" && pack.remaining_units > 0)
|
|
20
|
+
.sort((a, b) => new Date(a.purchased_at).getTime() - new Date(b.purchased_at).getTime())[0] ?? null;
|
|
21
|
+
}
|
|
22
|
+
export function formatCreditPackOffer(offer) {
|
|
23
|
+
const unitPrice = offer.effective_price_per_unit_usd
|
|
24
|
+
? ` ($${Number(offer.effective_price_per_unit_usd).toFixed(2)}/unit)`
|
|
25
|
+
: "";
|
|
26
|
+
return `${offer.label} — ${offer.included_units} units for $${offer.price_usd}${unitPrice}`;
|
|
27
|
+
}
|
|
28
|
+
export function formatCreditPack(pack) {
|
|
29
|
+
const label = pack.pack?.name ?? pack.pack?.key ?? "Credit Pack";
|
|
30
|
+
const statusPrefix = pack.status === "active" ? "" : `${pack.status} • `;
|
|
31
|
+
return `${label}: ${statusPrefix}${pack.remaining_units}/${pack.included_units} ${pack.unit_type}s remaining`;
|
|
32
|
+
}
|