@agentwonderland/mcp 0.1.22 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/core/__tests__/card-setup.test.d.ts +1 -0
  2. package/dist/core/__tests__/card-setup.test.js +99 -0
  3. package/dist/core/__tests__/formatters.test.d.ts +1 -0
  4. package/dist/core/__tests__/formatters.test.js +15 -0
  5. package/dist/core/__tests__/passes.test.d.ts +1 -0
  6. package/dist/core/__tests__/passes.test.js +82 -0
  7. package/dist/core/__tests__/payments.test.d.ts +1 -0
  8. package/dist/core/__tests__/payments.test.js +52 -0
  9. package/dist/core/__tests__/principal.test.d.ts +1 -0
  10. package/dist/core/__tests__/principal.test.js +67 -0
  11. package/dist/core/api-client.d.ts +9 -4
  12. package/dist/core/api-client.js +52 -22
  13. package/dist/core/card-setup.d.ts +20 -13
  14. package/dist/core/card-setup.js +87 -30
  15. package/dist/core/config.d.ts +4 -0
  16. package/dist/core/config.js +28 -1
  17. package/dist/core/formatters.d.ts +2 -0
  18. package/dist/core/formatters.js +5 -1
  19. package/dist/core/index.d.ts +2 -0
  20. package/dist/core/index.js +2 -0
  21. package/dist/core/ows-adapter.d.ts +10 -2
  22. package/dist/core/ows-adapter.js +54 -10
  23. package/dist/core/passes.d.ts +40 -0
  24. package/dist/core/passes.js +32 -0
  25. package/dist/core/payments.d.ts +8 -0
  26. package/dist/core/payments.js +121 -16
  27. package/dist/core/principal.d.ts +2 -0
  28. package/dist/core/principal.js +109 -0
  29. package/dist/core/solana-charge.d.ts +9 -0
  30. package/dist/core/solana-charge.js +95 -0
  31. package/dist/core/types.d.ts +10 -0
  32. package/dist/index.js +13 -4
  33. package/dist/prompts/index.js +1 -1
  34. package/dist/resources/wallet.js +8 -1
  35. package/dist/tools/__tests__/_payment-confirmation.test.d.ts +1 -0
  36. package/dist/tools/__tests__/_payment-confirmation.test.js +30 -0
  37. package/dist/tools/_payment-confirmation.d.ts +6 -0
  38. package/dist/tools/_payment-confirmation.js +28 -0
  39. package/dist/tools/agent-info.js +14 -0
  40. package/dist/tools/index.d.ts +1 -0
  41. package/dist/tools/index.js +1 -0
  42. package/dist/tools/passes.d.ts +2 -0
  43. package/dist/tools/passes.js +157 -0
  44. package/dist/tools/run.js +116 -49
  45. package/dist/tools/solve.js +102 -44
  46. package/dist/tools/wallet.js +85 -50
  47. package/package.json +3 -1
  48. package/src/core/__tests__/card-setup.test.ts +118 -0
  49. package/src/core/__tests__/formatters.test.ts +17 -0
  50. package/src/core/__tests__/passes.test.ts +94 -0
  51. package/src/core/__tests__/payments.test.ts +60 -0
  52. package/src/core/__tests__/principal.test.ts +87 -0
  53. package/src/core/api-client.ts +70 -23
  54. package/src/core/card-setup.ts +112 -35
  55. package/src/core/config.ts +33 -2
  56. package/src/core/formatters.ts +7 -1
  57. package/src/core/index.ts +2 -0
  58. package/src/core/ows-adapter.ts +74 -8
  59. package/src/core/passes.ts +74 -0
  60. package/src/core/payments.ts +140 -15
  61. package/src/core/principal.ts +128 -0
  62. package/src/core/solana-charge.ts +149 -0
  63. package/src/core/types.ts +10 -0
  64. package/src/index.ts +13 -4
  65. package/src/prompts/index.ts +1 -1
  66. package/src/resources/wallet.ts +8 -1
  67. package/src/tools/__tests__/_payment-confirmation.test.ts +45 -0
  68. package/src/tools/_payment-confirmation.ts +52 -0
  69. package/src/tools/agent-info.ts +23 -0
  70. package/src/tools/index.ts +1 -0
  71. package/src/tools/passes.ts +234 -0
  72. package/src/tools/run.ts +174 -53
  73. package/src/tools/solve.ts +149 -56
  74. package/src/tools/wallet.ts +102 -52
@@ -0,0 +1,118 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ const mockApiGet = vi.fn();
4
+ const mockApiPost = vi.fn();
5
+
6
+ class MockApiError extends Error {
7
+ constructor(public status: number, message: string) {
8
+ super(message);
9
+ this.name = "ApiError";
10
+ }
11
+ }
12
+
13
+ let pendingToken: string | null = null;
14
+ const savedCards: unknown[] = [];
15
+
16
+ vi.mock("../api-client.js", () => ({
17
+ ApiError: MockApiError,
18
+ apiGet: (...args: unknown[]) => mockApiGet(...args),
19
+ apiPost: (...args: unknown[]) => mockApiPost(...args),
20
+ }));
21
+
22
+ vi.mock("../config.js", () => ({
23
+ getApiUrl: () => "http://api.test",
24
+ getPendingCardSetupToken: () => pendingToken,
25
+ setCardConfig: (card: unknown) => {
26
+ savedCards.push(card);
27
+ },
28
+ setPendingCardSetupToken: (token: string | null) => {
29
+ pendingToken = token;
30
+ },
31
+ }));
32
+
33
+ describe("card setup helpers", () => {
34
+ beforeEach(() => {
35
+ vi.clearAllMocks();
36
+ pendingToken = null;
37
+ savedCards.length = 0;
38
+ });
39
+
40
+ it("reuses the existing pending setup token", async () => {
41
+ pendingToken = "tok_existing";
42
+ const { getOrCreatePendingCardSetup } = await import("../card-setup.js");
43
+
44
+ const result = await getOrCreatePendingCardSetup();
45
+
46
+ expect(mockApiPost).not.toHaveBeenCalled();
47
+ expect(result).toMatchObject({
48
+ token: "tok_existing",
49
+ url: "http://api.test/card/handoff/tok_existing",
50
+ isNew: false,
51
+ });
52
+ });
53
+
54
+ it("stores a newly created setup token for later resume", async () => {
55
+ mockApiPost.mockResolvedValueOnce({ token: "tok_new" });
56
+ const { getOrCreatePendingCardSetup } = await import("../card-setup.js");
57
+
58
+ const result = await getOrCreatePendingCardSetup();
59
+
60
+ expect(mockApiPost).toHaveBeenCalledWith("/card/setup", {});
61
+ expect(pendingToken).toBe("tok_new");
62
+ expect(result).toMatchObject({
63
+ token: "tok_new",
64
+ url: "http://api.test/card/handoff/tok_new",
65
+ isNew: true,
66
+ });
67
+ });
68
+
69
+ it("persists the card and clears pending setup on successful completion", async () => {
70
+ pendingToken = "tok_ready";
71
+ mockApiGet.mockResolvedValueOnce({
72
+ status: "complete",
73
+ card_last4: "4242",
74
+ card_brand: "visa",
75
+ consumer_token: "consumer_123",
76
+ payment_method_id: "pm_123",
77
+ });
78
+ const { pollCardSetup } = await import("../card-setup.js");
79
+
80
+ const result = await pollCardSetup("tok_ready", 50);
81
+
82
+ expect(result).toEqual({
83
+ last4: "4242",
84
+ brand: "visa",
85
+ consumerToken: "consumer_123",
86
+ });
87
+ expect(savedCards).toEqual([{
88
+ consumerToken: "consumer_123",
89
+ paymentMethodId: "pm_123",
90
+ last4: "4242",
91
+ brand: "visa",
92
+ }]);
93
+ expect(pendingToken).toBeNull();
94
+ });
95
+
96
+ it("clears expired pending setup tokens", async () => {
97
+ pendingToken = "tok_expired";
98
+ mockApiGet.mockRejectedValueOnce(new MockApiError(404, "Setup not found"));
99
+ const { pollCardSetup } = await import("../card-setup.js");
100
+
101
+ const result = await pollCardSetup("tok_expired", 50);
102
+
103
+ expect(result).toBeNull();
104
+ expect(pendingToken).toBeNull();
105
+ });
106
+
107
+ it("formats card setup as a single visible message with the setup page URL", async () => {
108
+ const { formatCardSetupBlocks } = await import("../card-setup.js");
109
+
110
+ const blocks = formatCardSetupBlocks("http://api.test/card/handoff/tok");
111
+
112
+ expect(blocks).toHaveLength(1);
113
+ expect(blocks[0]).toContain("Open this setup page to connect your card:");
114
+ expect(blocks[0]).toContain("http://api.test/card/handoff/tok");
115
+ expect(blocks[0]).toContain("securely save the card");
116
+ expect(blocks[0]).not.toContain("QR code above");
117
+ });
118
+ });
@@ -0,0 +1,17 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { formatRunResult } from "../formatters.js";
3
+
4
+ describe("formatRunResult", () => {
5
+ it("does not show a paid line for credit-pack-backed runs with zero cost", () => {
6
+ const output = formatRunResult({
7
+ agent_name: "Credit Pack Agent",
8
+ status: "success",
9
+ cost: 0,
10
+ consumption_mode: "credit_pack",
11
+ credit_pack_id: "pack-123",
12
+ }, { paymentMethod: "card" });
13
+
14
+ expect(output).toContain("Covered by credit pack (pack-123)");
15
+ expect(output).not.toContain("Paid:");
16
+ });
17
+ });
@@ -0,0 +1,94 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ formatCreditPack,
4
+ formatCreditPackOffer,
5
+ getActiveCreditPack,
6
+ getCreditPackProgram,
7
+ } from "../passes.js";
8
+ import type { AgentRecord } from "../types.js";
9
+
10
+ const baseAgent: AgentRecord = {
11
+ id: "agent-1",
12
+ name: "Test Agent",
13
+ payment: {
14
+ credit_packs: {
15
+ unit_type: "run",
16
+ packs: [
17
+ { key: "starter", name: "Starter Pack", included_units: 5, price_usd: "1.00" },
18
+ { key: "growth", name: "Growth Pack", included_units: 20, price_usd: "3.00" },
19
+ ],
20
+ },
21
+ },
22
+ };
23
+
24
+ describe("getCreditPackProgram", () => {
25
+ it("returns the configured credit-pack program from agent metadata", () => {
26
+ expect(getCreditPackProgram(baseAgent)).toEqual({
27
+ unit_type: "run",
28
+ packs: [
29
+ { key: "starter", name: "Starter Pack", included_units: 5, price_usd: "1.00" },
30
+ { key: "growth", name: "Growth Pack", included_units: 20, price_usd: "3.00" },
31
+ ],
32
+ });
33
+ });
34
+ });
35
+
36
+ describe("formatCreditPackOffer", () => {
37
+ it("shows units and per-unit price", () => {
38
+ expect(formatCreditPackOffer({
39
+ pack_id: "starter",
40
+ label: "Starter Pack",
41
+ included_units: 5,
42
+ price_usd: "1.00",
43
+ effective_price_per_unit_usd: "0.200000",
44
+ })).toContain("Starter Pack");
45
+ });
46
+ });
47
+
48
+ describe("formatCreditPack", () => {
49
+ it("includes the pack status when it is no longer active", () => {
50
+ expect(formatCreditPack({
51
+ id: "pack-1",
52
+ status: "depleted",
53
+ unit_type: "run",
54
+ included_units: 5,
55
+ remaining_units: 0,
56
+ price_usd: "1.00",
57
+ purchased_at: "2026-05-04T15:08:28.700Z",
58
+ pack: { key: "starter", name: "Starter Pack" },
59
+ })).toContain("depleted");
60
+ });
61
+ });
62
+
63
+ describe("getActiveCreditPack", () => {
64
+ it("returns the oldest active balance with remaining units", () => {
65
+ const pack = getActiveCreditPack({
66
+ consumer_principal: "did:pkh:eip155:8453:0x1111111111111111111111111111111111111111",
67
+ offers: [],
68
+ balances: [
69
+ {
70
+ id: "newer",
71
+ status: "active",
72
+ unit_type: "run",
73
+ included_units: 20,
74
+ remaining_units: 20,
75
+ price_usd: "3.00",
76
+ purchased_at: "2026-04-10T00:00:00Z",
77
+ pack: { key: "growth", name: "Growth Pack" },
78
+ },
79
+ {
80
+ id: "older",
81
+ status: "active",
82
+ unit_type: "run",
83
+ included_units: 5,
84
+ remaining_units: 2,
85
+ price_usd: "1.00",
86
+ purchased_at: "2026-04-01T00:00:00Z",
87
+ pack: { key: "starter", name: "Starter Pack" },
88
+ },
89
+ ],
90
+ });
91
+
92
+ expect(pack?.id).toBe("older");
93
+ });
94
+ });
@@ -0,0 +1,60 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ let currentCard = {
4
+ consumerToken: "consumer_one",
5
+ paymentMethodId: "pm_one",
6
+ last4: "1111",
7
+ brand: "visa",
8
+ };
9
+
10
+ const createdFetches = [vi.fn(), vi.fn(), vi.fn()];
11
+ const mockMppxCreate = vi.fn();
12
+ const mockStripe = vi.fn((opts: unknown) => opts);
13
+
14
+ vi.mock("../config.js", () => ({
15
+ getApiUrl: () => "http://api.test",
16
+ getCardConfig: () => currentCard,
17
+ getConfig: () => ({ defaultPaymentMethod: "card" }),
18
+ getDefaultWallet: () => undefined,
19
+ getWallets: () => [],
20
+ resolveWalletAndChain: () => null,
21
+ }));
22
+
23
+ vi.mock("mppx/client", () => ({
24
+ Mppx: {
25
+ create: (config: unknown) => mockMppxCreate(config),
26
+ },
27
+ stripe: (config: unknown) => mockStripe(config),
28
+ }));
29
+
30
+ describe("card payment fetch cache", () => {
31
+ beforeEach(() => {
32
+ vi.clearAllMocks();
33
+ currentCard = {
34
+ consumerToken: "consumer_one",
35
+ paymentMethodId: "pm_one",
36
+ last4: "1111",
37
+ brand: "visa",
38
+ };
39
+ mockMppxCreate
40
+ .mockReturnValueOnce({ fetch: createdFetches[0] })
41
+ .mockReturnValueOnce({ fetch: createdFetches[1] })
42
+ .mockReturnValueOnce({ fetch: createdFetches[2] });
43
+ });
44
+
45
+ it("rebuilds the cached card fetch when the card config changes", async () => {
46
+ const { getPaymentFetch } = await import("../payments.js");
47
+
48
+ const firstFetch = await getPaymentFetch("card");
49
+ currentCard = {
50
+ consumerToken: "consumer_two",
51
+ paymentMethodId: "pm_two",
52
+ last4: "2222",
53
+ brand: "mastercard",
54
+ };
55
+ const secondFetch = await getPaymentFetch("card");
56
+
57
+ expect(firstFetch).not.toBe(secondFetch);
58
+ expect(mockMppxCreate).toHaveBeenCalledTimes(2);
59
+ });
60
+ });
@@ -0,0 +1,87 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ const state = vi.hoisted(() => ({
4
+ wallets: [] as Array<{
5
+ id: string;
6
+ keyType: "evm" | "ows";
7
+ key?: string;
8
+ owsWalletId?: string;
9
+ chains: string[];
10
+ defaultChain?: string;
11
+ label?: string;
12
+ }>,
13
+ addresses: {} as Record<string, string | null>,
14
+ addedWallets: [] as Array<Record<string, unknown>>,
15
+ owsAvailable: false,
16
+ owsWallets: [] as Array<{ id: string; name: string; address: string }>,
17
+ }));
18
+
19
+ vi.mock("../config.js", () => ({
20
+ addWallet: (wallet: Record<string, unknown>) => {
21
+ state.addedWallets.push(wallet);
22
+ state.wallets.push(wallet as any);
23
+ },
24
+ getDefaultWallet: () => state.wallets[0],
25
+ getWallets: () => state.wallets,
26
+ }));
27
+
28
+ vi.mock("../payments.js", () => ({
29
+ getWalletAddress: async (walletId: string) => {
30
+ if (walletId in state.addresses) {
31
+ return state.addresses[walletId] ?? null;
32
+ }
33
+
34
+ const wallet = state.wallets.find((entry) => entry.id === walletId);
35
+ if (wallet?.key) {
36
+ const { privateKeyToAccount } = await import("viem/accounts");
37
+ return privateKeyToAccount(wallet.key as `0x${string}`).address;
38
+ }
39
+
40
+ return null;
41
+ },
42
+ }));
43
+
44
+ vi.mock("../ows-adapter.js", () => ({
45
+ createOwsWallet: async (name: string) => ({
46
+ walletId: `ows-${name}`,
47
+ address: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
48
+ }),
49
+ isOwsAvailable: async () => state.owsAvailable,
50
+ listOwsWallets: async () => state.owsWallets,
51
+ }));
52
+
53
+ describe("consumer principal helpers", () => {
54
+ beforeEach(() => {
55
+ vi.resetModules();
56
+ state.wallets = [];
57
+ state.addresses = {};
58
+ state.addedWallets = [];
59
+ state.owsAvailable = false;
60
+ state.owsWallets = [];
61
+ });
62
+
63
+ it("prefers an EVM-backed principal when both EVM and Solana wallets exist", async () => {
64
+ state.wallets = [
65
+ { id: "sol-wallet", keyType: "ows", owsWalletId: "ows-sol", chains: ["solana"], defaultChain: "solana" },
66
+ { id: "evm-wallet", keyType: "evm", key: "0x1234", chains: ["tempo", "base"], defaultChain: "base" },
67
+ ];
68
+ state.addresses = {
69
+ "sol-wallet": "So1ana11111111111111111111111111111111111111",
70
+ "evm-wallet": "0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
71
+ };
72
+
73
+ const { getConsumerPrincipal } = await import("../principal.js");
74
+ const principal = await getConsumerPrincipal();
75
+
76
+ expect(principal).toBe("did:pkh:eip155:8453:0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
77
+ });
78
+
79
+ it("creates a fallback EVM identity wallet when none exists", async () => {
80
+ const { ensureConsumerPrincipal } = await import("../principal.js");
81
+ const principal = await ensureConsumerPrincipal();
82
+
83
+ expect(principal).toMatch(/^did:pkh:eip155:8453:0x[a-f0-9]{40}$/);
84
+ expect(state.addedWallets).toHaveLength(1);
85
+ expect(state.addedWallets[0]?.chains).toEqual(["tempo", "base"]);
86
+ });
87
+ });
@@ -1,5 +1,6 @@
1
1
  import { getApiUrl, getApiKey } from "./config.js";
2
2
  import { getPaymentFetch } from "./payments.js";
3
+ import { ensureConsumerPrincipal, getConsumerPrincipal } from "./principal.js";
3
4
 
4
5
  // ── Error class ────────────────────────────────────────────────────
5
6
 
@@ -16,7 +17,12 @@ export class ApiError extends Error {
16
17
 
17
18
  // ── Internal helpers ───────────────────────────────────────────────
18
19
 
19
- function buildHeaders(): Record<string, string> {
20
+ interface RequestOptions {
21
+ ensureConsumerPrincipal?: boolean;
22
+ extraHeaders?: Record<string, string>;
23
+ }
24
+
25
+ async function buildHeaders(options?: RequestOptions): Promise<Record<string, string>> {
20
26
  const headers: Record<string, string> = {
21
27
  "Content-Type": "application/json",
22
28
  Accept: "application/json",
@@ -27,6 +33,17 @@ function buildHeaders(): Record<string, string> {
27
33
  headers["Authorization"] = `Bearer ${apiKey}`;
28
34
  }
29
35
 
36
+ const principal = options?.ensureConsumerPrincipal
37
+ ? await ensureConsumerPrincipal()
38
+ : await getConsumerPrincipal();
39
+ if (principal) {
40
+ headers["X-AW-Consumer-Principal"] = principal;
41
+ }
42
+
43
+ if (options?.extraHeaders) {
44
+ Object.assign(headers, options.extraHeaders);
45
+ }
46
+
30
47
  return headers;
31
48
  }
32
49
 
@@ -55,25 +72,56 @@ async function handleResponse<T>(response: Response): Promise<T> {
55
72
  return body as T;
56
73
  }
57
74
 
75
+ function attachResponseMetadata<T>(result: T, response: Response): T {
76
+ if (!result || typeof result !== "object") {
77
+ return result;
78
+ }
79
+
80
+ const record = result as Record<string, unknown>;
81
+ const headerToken = response.headers.get("x-feedback-token");
82
+ if (headerToken && !("feedback_token" in record)) {
83
+ record.feedback_token = headerToken;
84
+ }
85
+
86
+ const consumptionMode = response.headers.get("x-aw-consumption-mode");
87
+ if (consumptionMode) {
88
+ record.consumption_mode = consumptionMode;
89
+ }
90
+
91
+ const creditPackId = response.headers.get("x-aw-credit-pack-id");
92
+ if (creditPackId) {
93
+ record.credit_pack_id = creditPackId;
94
+ }
95
+
96
+ const principal = response.headers.get("x-aw-consumer-principal");
97
+ if (principal) {
98
+ record.consumer_principal = principal;
99
+ }
100
+
101
+ return result;
102
+ }
103
+
58
104
  // ── Public API ─────────────────────────────────────────────────────
59
105
 
60
- export async function apiGet<T>(path: string): Promise<T> {
106
+ export async function apiGet<T>(path: string, options?: RequestOptions): Promise<T> {
61
107
  const url = `${getApiUrl()}${path}`;
62
108
  const response = await fetch(url, {
63
109
  method: "GET",
64
- headers: buildHeaders(),
110
+ headers: await buildHeaders(options),
65
111
  });
66
- return handleResponse<T>(response);
112
+ const result = await handleResponse<T>(response);
113
+ return attachResponseMetadata(result, response);
67
114
  }
68
115
 
69
- export async function apiPost<T>(path: string, body: unknown): Promise<T> {
116
+ export async function apiPost<T>(path: string, body: unknown, options?: RequestOptions): Promise<T> {
70
117
  const url = `${getApiUrl()}${path}`;
71
118
  const response = await fetch(url, {
72
119
  method: "POST",
73
- headers: buildHeaders(),
120
+ headers: await buildHeaders(options),
74
121
  body: JSON.stringify(body),
75
122
  });
76
- return handleResponse<T>(response);
123
+ const result = await handleResponse<T>(response);
124
+ return attachResponseMetadata(result, response);
77
125
  }
78
126
 
79
127
  /**
@@ -81,34 +129,33 @@ export async function apiPost<T>(path: string, body: unknown): Promise<T> {
81
129
  * auto-handle 402 → sign → retry for paid endpoints.
82
130
  * Pass `payWith` to specify a method, or omit for auto-detection.
83
131
  */
84
- export async function apiPostWithPayment<T>(path: string, body: unknown, payWith?: string): Promise<T> {
132
+ export async function apiPostWithPayment<T>(
133
+ path: string,
134
+ body: unknown,
135
+ payWith?: string,
136
+ options?: RequestOptions,
137
+ ): Promise<T> {
85
138
  const url = `${getApiUrl()}${path}`;
86
139
  const paymentFetch = await getPaymentFetch(payWith);
87
140
  const response = await paymentFetch(url, {
88
141
  method: "POST",
89
- headers: buildHeaders(),
142
+ headers: await buildHeaders({
143
+ ensureConsumerPrincipal: true,
144
+ ...options,
145
+ }),
90
146
  body: JSON.stringify(body),
91
147
  });
92
148
  const result = await handleResponse<T>(response);
93
-
94
- // mppx may strip extra fields from the response body during receipt processing.
95
- // The gateway also sends feedback_token as a header to survive this.
96
- if (result && typeof result === "object" && !("feedback_token" in result)) {
97
- const headerToken = response.headers.get("x-feedback-token");
98
- if (headerToken) {
99
- (result as Record<string, unknown>).feedback_token = headerToken;
100
- }
101
- }
102
-
103
- return result;
149
+ return attachResponseMetadata(result, response);
104
150
  }
105
151
 
106
- export async function apiPut<T>(path: string, body: unknown): Promise<T> {
152
+ export async function apiPut<T>(path: string, body: unknown, options?: RequestOptions): Promise<T> {
107
153
  const url = `${getApiUrl()}${path}`;
108
154
  const response = await fetch(url, {
109
155
  method: "PUT",
110
- headers: buildHeaders(),
156
+ headers: await buildHeaders(options),
111
157
  body: JSON.stringify(body),
112
158
  });
113
- return handleResponse<T>(response);
159
+ const result = await handleResponse<T>(response);
160
+ return attachResponseMetadata(result, response);
114
161
  }