@agentwonderland/mcp 0.1.25 → 0.1.27
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__/api-client.test.d.ts +1 -0
- package/dist/core/__tests__/api-client.test.js +51 -0
- package/dist/core/__tests__/formatters.test.js +10 -0
- package/dist/core/__tests__/passes-api.test.d.ts +1 -0
- package/dist/core/__tests__/passes-api.test.js +27 -0
- package/dist/core/__tests__/payments.test.js +10 -6
- package/dist/core/__tests__/principal.test.js +41 -4
- package/dist/core/__tests__/solana-charge.test.d.ts +1 -0
- package/dist/core/__tests__/solana-charge.test.js +50 -0
- package/dist/core/api-client.d.ts +1 -0
- package/dist/core/api-client.js +8 -3
- package/dist/core/balances.d.ts +1 -0
- package/dist/core/balances.js +56 -0
- package/dist/core/base-charge.js +13 -6
- package/dist/core/formatters.d.ts +3 -2
- package/dist/core/formatters.js +7 -1
- package/dist/core/passes.d.ts +1 -1
- package/dist/core/passes.js +5 -2
- package/dist/core/payments.d.ts +1 -0
- package/dist/core/payments.js +20 -7
- package/dist/core/principal.d.ts +3 -0
- package/dist/core/principal.js +29 -1
- package/dist/core/settings.d.ts +20 -0
- package/dist/core/settings.js +19 -0
- package/dist/core/solana-charge.d.ts +5 -0
- package/dist/core/solana-charge.js +29 -7
- package/dist/core/tempo-charge.d.ts +7 -0
- package/dist/core/tempo-charge.js +84 -0
- package/dist/index.js +5 -7
- package/dist/prompts/index.js +1 -1
- package/dist/tools/__tests__/jobs.test.d.ts +1 -0
- package/dist/tools/__tests__/jobs.test.js +71 -0
- package/dist/tools/__tests__/run.test.d.ts +1 -0
- package/dist/tools/__tests__/run.test.js +149 -0
- package/dist/tools/__tests__/solve.test.d.ts +1 -0
- package/dist/tools/__tests__/solve.test.js +158 -0
- package/dist/tools/__tests__/wallet.test.d.ts +1 -0
- package/dist/tools/__tests__/wallet.test.js +230 -0
- package/dist/tools/_payment-confirmation.js +1 -1
- package/dist/tools/agent-info.js +14 -28
- package/dist/tools/jobs.js +82 -16
- package/dist/tools/passes.js +30 -14
- package/dist/tools/run.js +35 -20
- package/dist/tools/search.js +9 -8
- package/dist/tools/solve.js +45 -25
- package/dist/tools/wallet.js +35 -15
- package/package.json +2 -2
- package/src/core/__tests__/api-client.test.ts +78 -0
- package/src/core/__tests__/formatters.test.ts +12 -0
- package/src/core/__tests__/passes-api.test.ts +33 -0
- package/src/core/__tests__/payments.test.ts +17 -6
- package/src/core/__tests__/principal.test.ts +49 -4
- package/src/core/__tests__/solana-charge.test.ts +59 -0
- package/src/core/api-client.ts +16 -3
- package/src/core/balances.ts +63 -0
- package/src/core/base-charge.ts +13 -6
- package/src/core/formatters.ts +10 -3
- package/src/core/passes.ts +5 -2
- package/src/core/payments.ts +22 -7
- package/src/core/principal.ts +42 -1
- package/src/core/settings.ts +36 -0
- package/src/core/solana-charge.ts +43 -9
- package/src/core/tempo-charge.ts +104 -0
- package/src/index.ts +5 -7
- package/src/prompts/index.ts +1 -1
- package/src/tools/__tests__/jobs.test.ts +89 -0
- package/src/tools/__tests__/run.test.ts +176 -0
- package/src/tools/__tests__/solve.test.ts +186 -0
- package/src/tools/__tests__/wallet.test.ts +289 -0
- package/src/tools/_payment-confirmation.ts +1 -1
- package/src/tools/agent-info.ts +15 -38
- package/src/tools/jobs.ts +79 -17
- package/src/tools/passes.ts +30 -14
- package/src/tools/run.ts +38 -20
- package/src/tools/search.ts +10 -9
- package/src/tools/solve.ts +48 -25
- package/src/tools/wallet.ts +33 -17
- package/src/tools/observability.ts +0 -43
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
const { mockGetApiUrl, mockGetApiKey, mockGetPaymentFetch, mockEnsureConsumerPrincipalForMethod, mockGetConsumerPrincipalForMethod, mockGetBaseRebatePrincipal, mockPaymentFetch, } = vi.hoisted(() => ({
|
|
3
|
+
mockGetApiUrl: vi.fn(),
|
|
4
|
+
mockGetApiKey: vi.fn(),
|
|
5
|
+
mockGetPaymentFetch: vi.fn(),
|
|
6
|
+
mockEnsureConsumerPrincipalForMethod: vi.fn(),
|
|
7
|
+
mockGetConsumerPrincipalForMethod: vi.fn(),
|
|
8
|
+
mockGetBaseRebatePrincipal: vi.fn(),
|
|
9
|
+
mockPaymentFetch: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
vi.mock("../config.js", () => ({
|
|
12
|
+
getApiUrl: () => mockGetApiUrl(),
|
|
13
|
+
getApiKey: () => mockGetApiKey(),
|
|
14
|
+
}));
|
|
15
|
+
vi.mock("../payments.js", () => ({
|
|
16
|
+
getPaymentFetch: (...args) => mockGetPaymentFetch(...args),
|
|
17
|
+
}));
|
|
18
|
+
vi.mock("../principal.js", () => ({
|
|
19
|
+
ensureConsumerPrincipal: vi.fn(),
|
|
20
|
+
ensureConsumerPrincipalForMethod: (...args) => mockEnsureConsumerPrincipalForMethod(...args),
|
|
21
|
+
getConsumerPrincipal: vi.fn(),
|
|
22
|
+
getConsumerPrincipalForMethod: (...args) => mockGetConsumerPrincipalForMethod(...args),
|
|
23
|
+
getBaseRebatePrincipal: (...args) => mockGetBaseRebatePrincipal(...args),
|
|
24
|
+
}));
|
|
25
|
+
describe("api-client headers", () => {
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
vi.clearAllMocks();
|
|
28
|
+
mockGetApiUrl.mockReturnValue("https://api.agentwonderland.test");
|
|
29
|
+
mockGetApiKey.mockReturnValue(null);
|
|
30
|
+
mockGetPaymentFetch.mockResolvedValue(mockPaymentFetch);
|
|
31
|
+
mockEnsureConsumerPrincipalForMethod.mockResolvedValue("did:pkh:solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:42W2HfLfveSm1T5et9WTLp2CZ2QXdF2EYCUvyJ2gPpxF");
|
|
32
|
+
mockGetConsumerPrincipalForMethod.mockResolvedValue("did:pkh:eip155:8453:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
|
33
|
+
mockGetBaseRebatePrincipal.mockResolvedValue("did:pkh:eip155:8453:0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
|
|
34
|
+
mockPaymentFetch.mockResolvedValue(new Response(JSON.stringify({ ok: true }), {
|
|
35
|
+
status: 200,
|
|
36
|
+
headers: { "content-type": "application/json" },
|
|
37
|
+
}));
|
|
38
|
+
});
|
|
39
|
+
it("includes the Base rebate principal alongside the method-specific consumer principal", async () => {
|
|
40
|
+
const { apiPostWithPayment } = await import("../api-client.js");
|
|
41
|
+
await apiPostWithPayment("/agents/agent-1/run", { input: { text: "hello" } }, "solana");
|
|
42
|
+
expect(mockPaymentFetch).toHaveBeenCalledWith("https://api.agentwonderland.test/agents/agent-1/run", expect.objectContaining({
|
|
43
|
+
headers: expect.objectContaining({
|
|
44
|
+
"Content-Type": "application/json",
|
|
45
|
+
Accept: "application/json",
|
|
46
|
+
"X-AW-Consumer-Principal": "did:pkh:solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:42W2HfLfveSm1T5et9WTLp2CZ2QXdF2EYCUvyJ2gPpxF",
|
|
47
|
+
"X-AW-Rebate-Principal": "did:pkh:eip155:8453:0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
48
|
+
}),
|
|
49
|
+
}));
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -12,4 +12,14 @@ describe("formatRunResult", () => {
|
|
|
12
12
|
expect(output).toContain("Covered by credit pack (pack-123)");
|
|
13
13
|
expect(output).not.toContain("Paid:");
|
|
14
14
|
});
|
|
15
|
+
it("formats string-valued settled amounts from job lookups", () => {
|
|
16
|
+
const output = formatRunResult({
|
|
17
|
+
agent_name: "Async Analyzer",
|
|
18
|
+
status: "completed",
|
|
19
|
+
settled_amount: "0.100000",
|
|
20
|
+
job_id: "job-123",
|
|
21
|
+
}, { paymentMethod: "card" });
|
|
22
|
+
expect(output).toContain("Paid: $0.10 via card");
|
|
23
|
+
expect(output).toContain("Job ID: job-123");
|
|
24
|
+
});
|
|
15
25
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
const state = vi.hoisted(() => ({
|
|
3
|
+
apiGet: vi.fn(),
|
|
4
|
+
}));
|
|
5
|
+
vi.mock("../api-client.js", () => ({
|
|
6
|
+
apiGet: (...args) => state.apiGet(...args),
|
|
7
|
+
}));
|
|
8
|
+
describe("getCreditPackInventory", () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
vi.resetModules();
|
|
11
|
+
state.apiGet.mockReset();
|
|
12
|
+
});
|
|
13
|
+
it("requests inventory for the selected payment method principal", async () => {
|
|
14
|
+
state.apiGet.mockResolvedValueOnce({
|
|
15
|
+
consumer_principal: "did:pkh:solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:42W2HfLfveSm1T5et9WTLp2CZ2QXdF2EYCUvyJ2gPpxF",
|
|
16
|
+
offers: [],
|
|
17
|
+
balances: [],
|
|
18
|
+
});
|
|
19
|
+
const { getCreditPackInventory } = await import("../passes.js");
|
|
20
|
+
const result = await getCreditPackInventory("agent-1", "solana");
|
|
21
|
+
expect(result?.consumer_principal).toContain("did:pkh:solana:");
|
|
22
|
+
expect(state.apiGet).toHaveBeenCalledWith("/agents/agent-1/credit-packs", {
|
|
23
|
+
ensureConsumerPrincipal: true,
|
|
24
|
+
principalMethod: "solana",
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -12,7 +12,7 @@ let currentResolvedMethod = null;
|
|
|
12
12
|
const createdFetches = [vi.fn(), vi.fn(), vi.fn(), vi.fn()];
|
|
13
13
|
const mockMppxCreate = vi.fn();
|
|
14
14
|
const mockStripe = vi.fn((opts) => opts);
|
|
15
|
-
const
|
|
15
|
+
const mockTempoChargeClient = vi.fn((..._args) => "tempo_method");
|
|
16
16
|
const mockBaseChargeClient = vi.fn((..._args) => "base_method");
|
|
17
17
|
vi.mock("../config.js", () => ({
|
|
18
18
|
getApiUrl: () => "http://api.test",
|
|
@@ -27,7 +27,9 @@ vi.mock("mppx/client", () => ({
|
|
|
27
27
|
create: (config) => mockMppxCreate(config),
|
|
28
28
|
},
|
|
29
29
|
stripe: (config) => mockStripe(config),
|
|
30
|
-
|
|
30
|
+
}));
|
|
31
|
+
vi.mock("../tempo-charge.js", () => ({
|
|
32
|
+
tempoChargeClient: (config) => mockTempoChargeClient(config),
|
|
31
33
|
}));
|
|
32
34
|
vi.mock("../base-charge.js", () => ({
|
|
33
35
|
baseChargeClient: (config) => mockBaseChargeClient(config),
|
|
@@ -63,6 +65,8 @@ describe("payment method initialization", () => {
|
|
|
63
65
|
const secondFetch = await getPaymentFetch("card");
|
|
64
66
|
expect(firstFetch).not.toBe(secondFetch);
|
|
65
67
|
expect(mockMppxCreate).toHaveBeenCalledTimes(2);
|
|
68
|
+
expect(mockMppxCreate).toHaveBeenNthCalledWith(1, expect.objectContaining({ polyfill: false }));
|
|
69
|
+
expect(mockMppxCreate).toHaveBeenNthCalledWith(2, expect.objectContaining({ polyfill: false }));
|
|
66
70
|
});
|
|
67
71
|
it("initializes only the Base method when base is requested", async () => {
|
|
68
72
|
const wallet = {
|
|
@@ -78,8 +82,8 @@ describe("payment method initialization", () => {
|
|
|
78
82
|
const { getPaymentFetch } = await import("../payments.js");
|
|
79
83
|
await getPaymentFetch("base");
|
|
80
84
|
expect(mockBaseChargeClient).toHaveBeenCalledTimes(1);
|
|
81
|
-
expect(
|
|
82
|
-
expect(mockMppxCreate).toHaveBeenCalledWith(expect.objectContaining({ methods: ["base_method"] }));
|
|
85
|
+
expect(mockTempoChargeClient).not.toHaveBeenCalled();
|
|
86
|
+
expect(mockMppxCreate).toHaveBeenCalledWith(expect.objectContaining({ methods: ["base_method"], polyfill: false }));
|
|
83
87
|
});
|
|
84
88
|
it("initializes only the Tempo method when tempo is requested", async () => {
|
|
85
89
|
const wallet = {
|
|
@@ -94,8 +98,8 @@ describe("payment method initialization", () => {
|
|
|
94
98
|
currentResolvedMethod = { wallet, chain: "tempo" };
|
|
95
99
|
const { getPaymentFetch } = await import("../payments.js");
|
|
96
100
|
await getPaymentFetch("tempo");
|
|
97
|
-
expect(
|
|
101
|
+
expect(mockTempoChargeClient).toHaveBeenCalledTimes(1);
|
|
98
102
|
expect(mockBaseChargeClient).not.toHaveBeenCalled();
|
|
99
|
-
expect(mockMppxCreate).toHaveBeenCalledWith(expect.objectContaining({ methods: ["tempo_method"] }));
|
|
103
|
+
expect(mockMppxCreate).toHaveBeenCalledWith(expect.objectContaining({ methods: ["tempo_method"], polyfill: false }));
|
|
100
104
|
});
|
|
101
105
|
});
|
|
@@ -13,13 +13,24 @@ vi.mock("../config.js", () => ({
|
|
|
13
13
|
},
|
|
14
14
|
getDefaultWallet: () => state.wallets[0],
|
|
15
15
|
getWallets: () => state.wallets,
|
|
16
|
+
resolveWalletAndChain: (method) => {
|
|
17
|
+
for (const wallet of state.wallets) {
|
|
18
|
+
if (wallet.id === method) {
|
|
19
|
+
return { wallet, chain: wallet.defaultChain ?? wallet.chains[0] };
|
|
20
|
+
}
|
|
21
|
+
if (wallet.chains.includes(method)) {
|
|
22
|
+
return { wallet, chain: method };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
},
|
|
16
27
|
}));
|
|
17
28
|
vi.mock("../payments.js", () => ({
|
|
18
|
-
getWalletAddress: async (
|
|
19
|
-
if (
|
|
20
|
-
return state.addresses[
|
|
29
|
+
getWalletAddress: async (method) => {
|
|
30
|
+
if (method && method in state.addresses) {
|
|
31
|
+
return state.addresses[method] ?? null;
|
|
21
32
|
}
|
|
22
|
-
const wallet = state.wallets.find((entry) => entry.id ===
|
|
33
|
+
const wallet = state.wallets.find((entry) => entry.id === method);
|
|
23
34
|
if (wallet?.key) {
|
|
24
35
|
const { privateKeyToAccount } = await import("viem/accounts");
|
|
25
36
|
return privateKeyToAccount(wallet.key).address;
|
|
@@ -64,4 +75,30 @@ describe("consumer principal helpers", () => {
|
|
|
64
75
|
expect(state.addedWallets).toHaveLength(1);
|
|
65
76
|
expect(state.addedWallets[0]?.chains).toEqual(["tempo", "base"]);
|
|
66
77
|
});
|
|
78
|
+
it("derives a Solana principal when the selected payment method is solana", async () => {
|
|
79
|
+
state.wallets = [
|
|
80
|
+
{ id: "aw-main", keyType: "ows", owsWalletId: "ows-main", chains: ["tempo", "base", "solana"], defaultChain: "tempo" },
|
|
81
|
+
];
|
|
82
|
+
state.addresses = {
|
|
83
|
+
solana: "42W2HfLfveSm1T5et9WTLp2CZ2QXdF2EYCUvyJ2gPpxF",
|
|
84
|
+
tempo: "0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
|
85
|
+
base: "0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
|
86
|
+
};
|
|
87
|
+
const { getConsumerPrincipalForMethod } = await import("../principal.js");
|
|
88
|
+
const principal = await getConsumerPrincipalForMethod("solana");
|
|
89
|
+
expect(principal).toBe("did:pkh:solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:42W2HfLfveSm1T5et9WTLp2CZ2QXdF2EYCUvyJ2gPpxF");
|
|
90
|
+
});
|
|
91
|
+
it("returns the Base rebate principal when an EVM wallet is available", async () => {
|
|
92
|
+
state.wallets = [
|
|
93
|
+
{ id: "aw-main", keyType: "ows", owsWalletId: "ows-main", chains: ["tempo", "base", "solana"], defaultChain: "tempo" },
|
|
94
|
+
];
|
|
95
|
+
state.addresses = {
|
|
96
|
+
solana: "42W2HfLfveSm1T5et9WTLp2CZ2QXdF2EYCUvyJ2gPpxF",
|
|
97
|
+
tempo: "0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
|
98
|
+
base: "0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
|
99
|
+
};
|
|
100
|
+
const { getBaseRebatePrincipal } = await import("../principal.js");
|
|
101
|
+
const principal = await getBaseRebatePrincipal();
|
|
102
|
+
expect(principal).toBe("did:pkh:eip155:8453:0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
|
|
103
|
+
});
|
|
67
104
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import { TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from "@solana/spl-token";
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { SOLANA_USDC_MINT } from "../solana-charge.js";
|
|
5
|
+
describe("resolveRecipientTokenAccount", () => {
|
|
6
|
+
it("uses the recipient directly when Stripe gives us a token account", async () => {
|
|
7
|
+
const { resolveRecipientTokenAccount } = await import("../solana-charge.js");
|
|
8
|
+
const connection = {
|
|
9
|
+
getParsedAccountInfo: vi.fn().mockResolvedValue({
|
|
10
|
+
value: { owner: TOKEN_PROGRAM_ID },
|
|
11
|
+
}),
|
|
12
|
+
};
|
|
13
|
+
const mint = new PublicKey(SOLANA_USDC_MINT);
|
|
14
|
+
const recipient = new PublicKey("42W2HfLfveSm1T5et9WTLp2CZ2QXdF2EYCUvyJ2gPpxF");
|
|
15
|
+
const resolved = await resolveRecipientTokenAccount(connection, mint, recipient);
|
|
16
|
+
expect(resolved.tokenAccount.toBase58()).toBe(recipient.toBase58());
|
|
17
|
+
expect(resolved.needsCreateAssociatedAccount).toBe(false);
|
|
18
|
+
expect(connection.getParsedAccountInfo).toHaveBeenCalledTimes(1);
|
|
19
|
+
});
|
|
20
|
+
it("reuses the associated token account when it already exists", async () => {
|
|
21
|
+
const { resolveRecipientTokenAccount } = await import("../solana-charge.js");
|
|
22
|
+
const connection = {
|
|
23
|
+
getParsedAccountInfo: vi.fn()
|
|
24
|
+
.mockResolvedValueOnce({ value: null })
|
|
25
|
+
.mockResolvedValueOnce({ value: { owner: TOKEN_PROGRAM_ID } }),
|
|
26
|
+
};
|
|
27
|
+
const mint = new PublicKey(SOLANA_USDC_MINT);
|
|
28
|
+
const recipient = new PublicKey("G7kW6ZPMAK9v11AaVkK9FHed2Gd4WYwvCbanXUmK9WtS");
|
|
29
|
+
const expectedAta = getAssociatedTokenAddressSync(mint, recipient, false, TOKEN_PROGRAM_ID);
|
|
30
|
+
const resolved = await resolveRecipientTokenAccount(connection, mint, recipient);
|
|
31
|
+
expect(resolved.tokenAccount.toBase58()).toBe(expectedAta.toBase58());
|
|
32
|
+
expect(resolved.needsCreateAssociatedAccount).toBe(false);
|
|
33
|
+
expect(connection.getParsedAccountInfo).toHaveBeenCalledTimes(2);
|
|
34
|
+
});
|
|
35
|
+
it("marks the associated token account for creation when neither account exists", async () => {
|
|
36
|
+
const { resolveRecipientTokenAccount } = await import("../solana-charge.js");
|
|
37
|
+
const connection = {
|
|
38
|
+
getParsedAccountInfo: vi.fn()
|
|
39
|
+
.mockResolvedValueOnce({ value: null })
|
|
40
|
+
.mockResolvedValueOnce({ value: null }),
|
|
41
|
+
};
|
|
42
|
+
const mint = new PublicKey(SOLANA_USDC_MINT);
|
|
43
|
+
const recipient = new PublicKey("G7kW6ZPMAK9v11AaVkK9FHed2Gd4WYwvCbanXUmK9WtS");
|
|
44
|
+
const expectedAta = getAssociatedTokenAddressSync(mint, recipient, false, TOKEN_PROGRAM_ID);
|
|
45
|
+
const resolved = await resolveRecipientTokenAccount(connection, mint, recipient);
|
|
46
|
+
expect(resolved.tokenAccount.toBase58()).toBe(expectedAta.toBase58());
|
|
47
|
+
expect(resolved.needsCreateAssociatedAccount).toBe(true);
|
|
48
|
+
expect(connection.getParsedAccountInfo).toHaveBeenCalledTimes(2);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -5,6 +5,7 @@ export declare class ApiError extends Error {
|
|
|
5
5
|
}
|
|
6
6
|
interface RequestOptions {
|
|
7
7
|
ensureConsumerPrincipal?: boolean;
|
|
8
|
+
principalMethod?: string;
|
|
8
9
|
extraHeaders?: Record<string, string>;
|
|
9
10
|
}
|
|
10
11
|
export declare function apiGet<T>(path: string, options?: RequestOptions): Promise<T>;
|
package/dist/core/api-client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getApiUrl, getApiKey } from "./config.js";
|
|
2
2
|
import { getPaymentFetch } from "./payments.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getBaseRebatePrincipal, ensureConsumerPrincipalForMethod, getConsumerPrincipalForMethod, } from "./principal.js";
|
|
4
4
|
// ── Error class ────────────────────────────────────────────────────
|
|
5
5
|
export class ApiError extends Error {
|
|
6
6
|
status;
|
|
@@ -22,11 +22,15 @@ async function buildHeaders(options) {
|
|
|
22
22
|
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
23
23
|
}
|
|
24
24
|
const principal = options?.ensureConsumerPrincipal
|
|
25
|
-
? await
|
|
26
|
-
: await
|
|
25
|
+
? await ensureConsumerPrincipalForMethod(options?.principalMethod)
|
|
26
|
+
: await getConsumerPrincipalForMethod(options?.principalMethod);
|
|
27
27
|
if (principal) {
|
|
28
28
|
headers["X-AW-Consumer-Principal"] = principal;
|
|
29
29
|
}
|
|
30
|
+
const rebatePrincipal = await getBaseRebatePrincipal();
|
|
31
|
+
if (rebatePrincipal) {
|
|
32
|
+
headers["X-AW-Rebate-Principal"] = rebatePrincipal;
|
|
33
|
+
}
|
|
30
34
|
if (options?.extraHeaders) {
|
|
31
35
|
Object.assign(headers, options.extraHeaders);
|
|
32
36
|
}
|
|
@@ -109,6 +113,7 @@ export async function apiPostWithPayment(path, body, payWith, options) {
|
|
|
109
113
|
method: "POST",
|
|
110
114
|
headers: await buildHeaders({
|
|
111
115
|
ensureConsumerPrincipal: true,
|
|
116
|
+
principalMethod: payWith,
|
|
112
117
|
...options,
|
|
113
118
|
}),
|
|
114
119
|
body: JSON.stringify(body),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function fetchUsdcBalance(chain: "tempo" | "base" | "solana", address: string): Promise<string | null>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { getSettings } from "./settings.js";
|
|
2
|
+
const ERC20_BALANCE_ABI = [
|
|
3
|
+
{
|
|
4
|
+
type: "function",
|
|
5
|
+
name: "balanceOf",
|
|
6
|
+
stateMutability: "view",
|
|
7
|
+
inputs: [{ name: "account", type: "address" }],
|
|
8
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
9
|
+
},
|
|
10
|
+
];
|
|
11
|
+
async function fetchEvmUsdcBalance(chain, address) {
|
|
12
|
+
try {
|
|
13
|
+
const { createPublicClient, http, formatUnits } = await import("viem");
|
|
14
|
+
const client = createPublicClient({ transport: http(chain.rpcUrl) });
|
|
15
|
+
const raw = await client.readContract({
|
|
16
|
+
address: chain.usdc,
|
|
17
|
+
abi: ERC20_BALANCE_ABI,
|
|
18
|
+
functionName: "balanceOf",
|
|
19
|
+
args: [address],
|
|
20
|
+
});
|
|
21
|
+
return formatUnits(raw, 6);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async function fetchSolanaUsdcBalance(chain, address) {
|
|
28
|
+
try {
|
|
29
|
+
const { Connection, PublicKey } = await import("@solana/web3.js");
|
|
30
|
+
const connection = new Connection(chain.rpcUrl, "confirmed");
|
|
31
|
+
const owner = new PublicKey(address);
|
|
32
|
+
const mint = new PublicKey(chain.usdc);
|
|
33
|
+
const accounts = await connection.getParsedTokenAccountsByOwner(owner, { mint });
|
|
34
|
+
let total = 0;
|
|
35
|
+
for (const { account } of accounts.value) {
|
|
36
|
+
const amount = account.data?.parsed?.info?.tokenAmount?.uiAmount;
|
|
37
|
+
if (typeof amount === "number")
|
|
38
|
+
total += amount;
|
|
39
|
+
}
|
|
40
|
+
return total.toString();
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export async function fetchUsdcBalance(chain, address) {
|
|
47
|
+
const settings = await getSettings();
|
|
48
|
+
if (!settings)
|
|
49
|
+
return null;
|
|
50
|
+
const chainConfig = settings.chains[chain];
|
|
51
|
+
if (!chainConfig)
|
|
52
|
+
return null;
|
|
53
|
+
if (chain === "solana")
|
|
54
|
+
return fetchSolanaUsdcBalance(chainConfig, address);
|
|
55
|
+
return fetchEvmUsdcBalance(chainConfig, address);
|
|
56
|
+
}
|
package/dist/core/base-charge.js
CHANGED
|
@@ -8,7 +8,9 @@ import { Method, Credential, z } from "mppx";
|
|
|
8
8
|
import { toAtomicAmount } from "./amount-utils.js";
|
|
9
9
|
// Base USDC (Circle native)
|
|
10
10
|
const BASE_USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
11
|
+
const BASE_SEPOLIA_USDC = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
|
|
11
12
|
const BASE_CHAIN_ID = 8453;
|
|
13
|
+
const BASE_SEPOLIA_CHAIN_ID = 84532;
|
|
12
14
|
// Standard ERC-20 transfer function ABI
|
|
13
15
|
const ERC20_ABI = [
|
|
14
16
|
{
|
|
@@ -53,18 +55,23 @@ export function baseChargeClient(config) {
|
|
|
53
55
|
const { request } = challenge;
|
|
54
56
|
const amount = toAtomicAmount(request.amount, request.decimals ?? 6);
|
|
55
57
|
const recipient = request.recipient;
|
|
56
|
-
const
|
|
58
|
+
const chainId = request.chainId ?? BASE_CHAIN_ID;
|
|
59
|
+
const currency = (request.currency
|
|
60
|
+
?? (chainId === BASE_SEPOLIA_CHAIN_ID ? BASE_SEPOLIA_USDC : BASE_USDC));
|
|
57
61
|
// Dynamic imports to keep the module lightweight
|
|
58
62
|
const { createWalletClient, createPublicClient, http } = await import("viem");
|
|
59
|
-
const { base } = await import("viem/chains");
|
|
60
|
-
const
|
|
63
|
+
const { base, baseSepolia } = await import("viem/chains");
|
|
64
|
+
const chain = chainId === BASE_SEPOLIA_CHAIN_ID ? baseSepolia : base;
|
|
65
|
+
const rpcUrl = config.rpcUrl ?? (chainId === BASE_SEPOLIA_CHAIN_ID
|
|
66
|
+
? "https://sepolia.base.org"
|
|
67
|
+
: "https://mainnet.base.org");
|
|
61
68
|
const walletClient = createWalletClient({
|
|
62
69
|
account: config.account,
|
|
63
|
-
chain
|
|
70
|
+
chain,
|
|
64
71
|
transport: http(rpcUrl),
|
|
65
72
|
});
|
|
66
73
|
const publicClient = createPublicClient({
|
|
67
|
-
chain
|
|
74
|
+
chain,
|
|
68
75
|
transport: http(rpcUrl),
|
|
69
76
|
});
|
|
70
77
|
// Send ERC-20 transfer
|
|
@@ -79,7 +86,7 @@ export function baseChargeClient(config) {
|
|
|
79
86
|
return Credential.serialize({
|
|
80
87
|
challenge,
|
|
81
88
|
payload: { hash, type: "hash" },
|
|
82
|
-
source: `did:pkh:eip155:${
|
|
89
|
+
source: `did:pkh:eip155:${chainId}:${config.account.address}`,
|
|
83
90
|
});
|
|
84
91
|
},
|
|
85
92
|
});
|
|
@@ -38,8 +38,9 @@ interface RunResultLike {
|
|
|
38
38
|
error_code?: string;
|
|
39
39
|
latency_ms?: number;
|
|
40
40
|
execution_latency_ms?: number;
|
|
41
|
-
cost?: number;
|
|
42
|
-
|
|
41
|
+
cost?: number | string;
|
|
42
|
+
settled_amount?: number | string;
|
|
43
|
+
estimated_cost?: number | string;
|
|
43
44
|
input_tokens?: number;
|
|
44
45
|
tags?: string[];
|
|
45
46
|
consumption_mode?: string;
|
package/dist/core/formatters.js
CHANGED
|
@@ -81,6 +81,12 @@ export function agentList(agents, query) {
|
|
|
81
81
|
const lines = agents.map((a) => ` ${agentLine(a)}`);
|
|
82
82
|
return [header, "", ...lines].join("\n");
|
|
83
83
|
}
|
|
84
|
+
function toDisplayCost(value) {
|
|
85
|
+
if (value == null)
|
|
86
|
+
return null;
|
|
87
|
+
const numeric = typeof value === "number" ? value : Number.parseFloat(value);
|
|
88
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
89
|
+
}
|
|
84
90
|
export function formatRunResult(result, opts) {
|
|
85
91
|
const lines = [];
|
|
86
92
|
// Output
|
|
@@ -107,7 +113,7 @@ export function formatRunResult(result, opts) {
|
|
|
107
113
|
lines.push("");
|
|
108
114
|
}
|
|
109
115
|
// Summary line
|
|
110
|
-
const cost = result.cost ?? result.estimated_cost;
|
|
116
|
+
const cost = toDisplayCost(result.cost ?? result.settled_amount ?? result.estimated_cost);
|
|
111
117
|
const usedCreditPack = result.consumption_mode === "credit_pack";
|
|
112
118
|
const status = result.status === "success" || result.status === "completed" ? "✓" : "✗";
|
|
113
119
|
const agent = result.agent_name ?? result.agent_id ?? "";
|
package/dist/core/passes.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export interface CreditPackInventory {
|
|
|
34
34
|
balances: CreditPackRecord[];
|
|
35
35
|
}
|
|
36
36
|
export declare function getCreditPackProgram(agent: AgentRecord): CreditPackProgramInfo | null;
|
|
37
|
-
export declare function getCreditPackInventory(agentId: string): Promise<CreditPackInventory | null>;
|
|
37
|
+
export declare function getCreditPackInventory(agentId: string, method?: string): Promise<CreditPackInventory | null>;
|
|
38
38
|
export declare function getActiveCreditPack(inventory: CreditPackInventory | null): CreditPackRecord | null;
|
|
39
39
|
export declare function formatCreditPackOffer(offer: CreditPackOffer): string;
|
|
40
40
|
export declare function formatCreditPack(pack: CreditPackRecord): string;
|
package/dist/core/passes.js
CHANGED
|
@@ -4,9 +4,12 @@ export function getCreditPackProgram(agent) {
|
|
|
4
4
|
const creditPacks = payment.credit_packs;
|
|
5
5
|
return creditPacks ?? null;
|
|
6
6
|
}
|
|
7
|
-
export async function getCreditPackInventory(agentId) {
|
|
7
|
+
export async function getCreditPackInventory(agentId, method) {
|
|
8
8
|
try {
|
|
9
|
-
return await apiGet(`/agents/${agentId}/credit-packs`, {
|
|
9
|
+
return await apiGet(`/agents/${agentId}/credit-packs`, {
|
|
10
|
+
ensureConsumerPrincipal: true,
|
|
11
|
+
principalMethod: method,
|
|
12
|
+
});
|
|
10
13
|
}
|
|
11
14
|
catch {
|
|
12
15
|
return null;
|
package/dist/core/payments.d.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* which to use per-request via `--pay-with <wallet-id|chain|card>`.
|
|
11
11
|
*/
|
|
12
12
|
import type { AgentRecord } from "./types.js";
|
|
13
|
+
export declare function isCardPaymentEnabled(): boolean;
|
|
13
14
|
/**
|
|
14
15
|
* Returns a payment-aware fetch for a specific method, or the best
|
|
15
16
|
* available method if none is specified.
|
package/dist/core/payments.js
CHANGED
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
* which to use per-request via `--pay-with <wallet-id|chain|card>`.
|
|
11
11
|
*/
|
|
12
12
|
import { getConfig, getWallets, getDefaultWallet, getCardConfig, resolveWalletAndChain, getApiUrl, } from "./config.js";
|
|
13
|
+
// Feature flag: disable card payment for launch. Flip to true once Stripe
|
|
14
|
+
// approves the live SPT issuer. Config/card state is still persisted so this
|
|
15
|
+
// can be re-enabled without reconfiguring wallets.
|
|
16
|
+
const ENABLE_CARD_PAYMENT = false;
|
|
17
|
+
export function isCardPaymentEnabled() {
|
|
18
|
+
return ENABLE_CARD_PAYMENT;
|
|
19
|
+
}
|
|
13
20
|
// Cache per wallet+chain combo to avoid re-initializing
|
|
14
21
|
const fetchCache = new Map();
|
|
15
22
|
const REGISTRY_METHOD_MAP = {
|
|
@@ -47,7 +54,7 @@ function clearStaleCardCache(activeKey) {
|
|
|
47
54
|
// ── Per-protocol initializers ───────────────────────────────────
|
|
48
55
|
async function initEvmMppForChain(wallet, chain) {
|
|
49
56
|
try {
|
|
50
|
-
const { Mppx
|
|
57
|
+
const { Mppx } = await import("mppx/client");
|
|
51
58
|
let account;
|
|
52
59
|
if (wallet.keyType === "ows" && wallet.owsWalletId) {
|
|
53
60
|
const { owsAccountFromWalletId } = await import("./ows-adapter.js");
|
|
@@ -62,13 +69,14 @@ async function initEvmMppForChain(wallet, chain) {
|
|
|
62
69
|
}
|
|
63
70
|
const methods = [];
|
|
64
71
|
if (chain === "tempo") {
|
|
65
|
-
|
|
72
|
+
const { tempoChargeClient } = await import("./tempo-charge.js");
|
|
73
|
+
methods.push(tempoChargeClient({ account }));
|
|
66
74
|
}
|
|
67
75
|
else {
|
|
68
76
|
const { baseChargeClient } = await import("./base-charge.js");
|
|
69
77
|
methods.push(baseChargeClient({ account }));
|
|
70
78
|
}
|
|
71
|
-
const mppx = Mppx.create({ methods: methods });
|
|
79
|
+
const mppx = Mppx.create({ methods: methods, polyfill: false });
|
|
72
80
|
return mppx.fetch.bind(mppx);
|
|
73
81
|
}
|
|
74
82
|
catch {
|
|
@@ -84,6 +92,7 @@ async function initSolanaMpp(wallet) {
|
|
|
84
92
|
const { solanaChargeClient } = await import("./solana-charge.js");
|
|
85
93
|
const mppx = Mppx.create({
|
|
86
94
|
methods: [solanaChargeClient({ wallet })],
|
|
95
|
+
polyfill: false,
|
|
87
96
|
});
|
|
88
97
|
return mppx.fetch.bind(mppx);
|
|
89
98
|
}
|
|
@@ -120,6 +129,7 @@ async function initCard() {
|
|
|
120
129
|
return spt;
|
|
121
130
|
},
|
|
122
131
|
})],
|
|
132
|
+
polyfill: false,
|
|
123
133
|
});
|
|
124
134
|
return mppx.fetch.bind(mppx);
|
|
125
135
|
}
|
|
@@ -149,6 +159,9 @@ async function initForChain(wallet, chain) {
|
|
|
149
159
|
export async function getPaymentFetch(method) {
|
|
150
160
|
// Card payment
|
|
151
161
|
if (method === "card") {
|
|
162
|
+
if (!ENABLE_CARD_PAYMENT) {
|
|
163
|
+
throw new Error('Card payments are temporarily unavailable. Use a crypto wallet (tempo, base, or solana).');
|
|
164
|
+
}
|
|
152
165
|
const ck = cardCacheKey();
|
|
153
166
|
clearStaleCardCache(ck ?? undefined);
|
|
154
167
|
if (!ck) {
|
|
@@ -257,13 +270,13 @@ export function getConfiguredMethods() {
|
|
|
257
270
|
methods.push(c);
|
|
258
271
|
}
|
|
259
272
|
}
|
|
260
|
-
// Add card if configured
|
|
261
|
-
if (getCardConfig()) {
|
|
273
|
+
// Add card if configured (gated for launch)
|
|
274
|
+
if (ENABLE_CARD_PAYMENT && getCardConfig()) {
|
|
262
275
|
methods.push("card");
|
|
263
276
|
}
|
|
264
|
-
// Respect defaultPaymentMethod — move it to front of list
|
|
277
|
+
// Respect defaultPaymentMethod — move it to front of list (ignore card when disabled)
|
|
265
278
|
const defaultMethod = getConfig().defaultPaymentMethod;
|
|
266
|
-
if (defaultMethod) {
|
|
279
|
+
if (defaultMethod && (defaultMethod !== "card" || ENABLE_CARD_PAYMENT)) {
|
|
267
280
|
const idx = methods.indexOf(defaultMethod);
|
|
268
281
|
if (idx > 0) {
|
|
269
282
|
methods.splice(idx, 1);
|
package/dist/core/principal.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
export declare function getConsumerPrincipal(): Promise<string | null>;
|
|
2
|
+
export declare function getBaseRebatePrincipal(): Promise<string | null>;
|
|
3
|
+
export declare function getConsumerPrincipalForMethod(method?: string): Promise<string | null>;
|
|
2
4
|
export declare function ensureConsumerPrincipal(): Promise<string>;
|
|
5
|
+
export declare function ensureConsumerPrincipalForMethod(method?: string): Promise<string>;
|
package/dist/core/principal.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { addWallet, getDefaultWallet, getWallets } from "./config.js";
|
|
1
|
+
import { addWallet, getDefaultWallet, getWallets, resolveWalletAndChain, } from "./config.js";
|
|
2
2
|
import { getWalletAddress } from "./payments.js";
|
|
3
3
|
import { createOwsWallet, isOwsAvailable, listOwsWallets, } from "./ows-adapter.js";
|
|
4
4
|
const BASE_CHAIN_ID = "8453";
|
|
@@ -27,6 +27,12 @@ async function walletPrincipal(wallet) {
|
|
|
27
27
|
}
|
|
28
28
|
return null;
|
|
29
29
|
}
|
|
30
|
+
async function principalForChain(chain) {
|
|
31
|
+
const address = await getWalletAddress(chain);
|
|
32
|
+
if (!address)
|
|
33
|
+
return null;
|
|
34
|
+
return principalFromAddress(address, chain === "solana" ? "solana" : "evm");
|
|
35
|
+
}
|
|
30
36
|
function preferredWallets() {
|
|
31
37
|
const wallets = getWallets();
|
|
32
38
|
const defaultWallet = getDefaultWallet();
|
|
@@ -96,6 +102,18 @@ export async function getConsumerPrincipal() {
|
|
|
96
102
|
}
|
|
97
103
|
return null;
|
|
98
104
|
}
|
|
105
|
+
export async function getBaseRebatePrincipal() {
|
|
106
|
+
return (await principalForChain("base")) ?? principalForChain("tempo");
|
|
107
|
+
}
|
|
108
|
+
export async function getConsumerPrincipalForMethod(method) {
|
|
109
|
+
if (!method || method === "card") {
|
|
110
|
+
return getConsumerPrincipal();
|
|
111
|
+
}
|
|
112
|
+
const resolved = resolveWalletAndChain(method);
|
|
113
|
+
if (!resolved)
|
|
114
|
+
return null;
|
|
115
|
+
return principalForChain(resolved.chain);
|
|
116
|
+
}
|
|
99
117
|
export async function ensureConsumerPrincipal() {
|
|
100
118
|
const existing = await getConsumerPrincipal();
|
|
101
119
|
if (existing)
|
|
@@ -107,3 +125,13 @@ export async function ensureConsumerPrincipal() {
|
|
|
107
125
|
}
|
|
108
126
|
return principal;
|
|
109
127
|
}
|
|
128
|
+
export async function ensureConsumerPrincipalForMethod(method) {
|
|
129
|
+
const existing = await getConsumerPrincipalForMethod(method);
|
|
130
|
+
if (existing)
|
|
131
|
+
return existing;
|
|
132
|
+
if (method && method !== "card") {
|
|
133
|
+
throw new Error(`Could not derive a consumer principal for payment method "${method}". ` +
|
|
134
|
+
"Check wallet_status and confirm that chain is configured for the active wallet.");
|
|
135
|
+
}
|
|
136
|
+
return ensureConsumerPrincipal();
|
|
137
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ChainSettings {
|
|
2
|
+
chainId: number | string;
|
|
3
|
+
usdc: string;
|
|
4
|
+
rpcUrl: string;
|
|
5
|
+
explorer?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface PublicSettings {
|
|
8
|
+
network: "testnet" | "mainnet";
|
|
9
|
+
stripe: {
|
|
10
|
+
mode: "test" | "live";
|
|
11
|
+
};
|
|
12
|
+
chains: {
|
|
13
|
+
tempo: ChainSettings;
|
|
14
|
+
base: ChainSettings;
|
|
15
|
+
solana: ChainSettings & {
|
|
16
|
+
cluster: "mainnet-beta" | "devnet";
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export declare function getSettings(): Promise<PublicSettings | null>;
|