@bondedhq/sdk 0.1.3 → 0.1.5
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 +203 -5
- package/dist/ai-sdk.d.ts +19 -0
- package/dist/ai-sdk.js +46 -0
- package/dist/budget.d.ts +97 -0
- package/dist/budget.js +98 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.js +36 -0
- package/dist/gate.d.ts +158 -0
- package/dist/gate.js +416 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.js +10 -1
- package/dist/langchain.d.ts +19 -1
- package/dist/langchain.js +34 -0
- package/dist/mcp.d.ts +20 -0
- package/dist/mcp.js +30 -0
- package/dist/openai-agents.d.ts +33 -0
- package/dist/openai-agents.js +52 -0
- package/dist/payments.d.ts +227 -0
- package/dist/payments.js +474 -0
- package/dist/x402.d.ts +136 -0
- package/dist/x402.js +182 -0
- package/package.json +17 -3
package/dist/langchain.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DynamicStructuredTool } from "@langchain/core/tools";
|
|
2
2
|
import { resolveTools, resultText } from "./mcp.js";
|
|
3
|
+
import { guardPayments, isPaymentRefusal } from "./payments.js";
|
|
3
4
|
import { bondedVaultTools } from "./vault-tools.js";
|
|
4
5
|
/**
|
|
5
6
|
* The episode's tools as LangChain `StructuredTool`s, for LangChain and LangGraph agents
|
|
@@ -50,4 +51,37 @@ export function bondedVaultLangChainTools(options) {
|
|
|
50
51
|
func: async (args) => resultText(await t.call(args ?? {})),
|
|
51
52
|
}));
|
|
52
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Your own LangChain pay tool, with every call checked against the payment mandate first (see
|
|
56
|
+
* `PaymentGuard`). Same name, description and schema. When the guard refuses (Enforce mode
|
|
57
|
+
* blocked it, the guard is frozen, or it can't read the payment) your tool is not called and the
|
|
58
|
+
* model gets `Error: ...` naming the rules broken, so it can react. In Monitor mode a breaking
|
|
59
|
+
* payment goes through and the guard's `onBreach` reports it. A local check: no receipts.
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* const guard = paymentGuard({ mandate, vendors, invoices, onBreach: alert });
|
|
63
|
+
* const tools = [...readOnlyTools, guardLangChainPayTool(payInvoice, { guard })];
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* The payment is read from the tool's arguments: `{ account, amountUsd, invoiceReference }` unless
|
|
67
|
+
* you pass `toPayment`.
|
|
68
|
+
*/
|
|
69
|
+
export function guardLangChainPayTool(payTool, options) {
|
|
70
|
+
const guarded = guardPayments((args, config) => payTool.invoke(args, config), options);
|
|
71
|
+
return new DynamicStructuredTool({
|
|
72
|
+
name: payTool.name,
|
|
73
|
+
description: payTool.description,
|
|
74
|
+
schema: payTool.schema,
|
|
75
|
+
func: async (args, _runManager, config) => {
|
|
76
|
+
try {
|
|
77
|
+
return await guarded(args ?? {}, config);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (isPaymentRefusal(error))
|
|
81
|
+
return `Error: ${error.message}`;
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
53
87
|
//# sourceMappingURL=langchain.js.map
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
3
|
import type { DoneRequest } from "@bondedhq/shared";
|
|
4
|
+
import { type GuardPaymentsOptions } from "./payments.js";
|
|
3
5
|
export { isBondedEpisode } from "./episode.js";
|
|
4
6
|
export { finishEpisode } from "./done.js";
|
|
7
|
+
export type { GuardPaymentsOptions } from "./payments.js";
|
|
5
8
|
/**
|
|
6
9
|
* Where an episode's tools are. A `BondedEpisode` (from `fromBondedEnv`, or the one an Agent URL
|
|
7
10
|
* handler hands you) has these, and so does the runner protocol's `EpisodeMessage`.
|
|
@@ -128,4 +131,21 @@ export declare function mcpConfigJson(episode?: {
|
|
|
128
131
|
mcp: string;
|
|
129
132
|
};
|
|
130
133
|
}, options?: McpConfigOptions): string;
|
|
134
|
+
/**
|
|
135
|
+
* Your MCP server's pay tool handler, with every call checked against the payment mandate first
|
|
136
|
+
* (see `PaymentGuard`). Register the result in its place:
|
|
137
|
+
*
|
|
138
|
+
* ```ts
|
|
139
|
+
* const guard = paymentGuard({ mandate, vendors, invoices, onBreach: alert });
|
|
140
|
+
* server.registerTool("pay_invoice", config, guardMcpPayHandler(payInvoice, { guard }));
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* When the guard refuses (Enforce mode blocked it, the guard is frozen, or it can't read the
|
|
144
|
+
* payment) your handler is not called and the client gets an error result (`isError: true`)
|
|
145
|
+
* naming the rules broken, so the model can react. In Monitor mode a breaking payment goes
|
|
146
|
+
* through and the guard's `onBreach` reports it. The payment is read from the tool's arguments:
|
|
147
|
+
* `{ account, amountUsd, invoiceReference }` unless you pass `toPayment`. A local check: no
|
|
148
|
+
* receipts.
|
|
149
|
+
*/
|
|
150
|
+
export declare function guardMcpPayHandler<I, Rest extends unknown[]>(handler: (args: I, ...rest: Rest) => CallToolResult | Promise<CallToolResult>, options: GuardPaymentsOptions<I>): (args: I, ...rest: Rest) => Promise<CallToolResult>;
|
|
131
151
|
//# sourceMappingURL=mcp.d.ts.map
|
package/dist/mcp.js
CHANGED
|
@@ -3,6 +3,7 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
|
|
|
3
3
|
import { BondedError } from "./errors.js";
|
|
4
4
|
import { finishEpisode } from "./done.js";
|
|
5
5
|
import { fromBondedEnv } from "./episode.js";
|
|
6
|
+
import { guardPayments, isPaymentRefusal } from "./payments.js";
|
|
6
7
|
export { isBondedEpisode } from "./episode.js";
|
|
7
8
|
export { finishEpisode } from "./done.js";
|
|
8
9
|
/**
|
|
@@ -108,4 +109,33 @@ export function mcpConfig(episode = fromBondedEnv(), options = {}) {
|
|
|
108
109
|
export function mcpConfigJson(episode = fromBondedEnv(), options = {}) {
|
|
109
110
|
return `${JSON.stringify(mcpConfig(episode, options), null, 2)}\n`;
|
|
110
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Your MCP server's pay tool handler, with every call checked against the payment mandate first
|
|
114
|
+
* (see `PaymentGuard`). Register the result in its place:
|
|
115
|
+
*
|
|
116
|
+
* ```ts
|
|
117
|
+
* const guard = paymentGuard({ mandate, vendors, invoices, onBreach: alert });
|
|
118
|
+
* server.registerTool("pay_invoice", config, guardMcpPayHandler(payInvoice, { guard }));
|
|
119
|
+
* ```
|
|
120
|
+
*
|
|
121
|
+
* When the guard refuses (Enforce mode blocked it, the guard is frozen, or it can't read the
|
|
122
|
+
* payment) your handler is not called and the client gets an error result (`isError: true`)
|
|
123
|
+
* naming the rules broken, so the model can react. In Monitor mode a breaking payment goes
|
|
124
|
+
* through and the guard's `onBreach` reports it. The payment is read from the tool's arguments:
|
|
125
|
+
* `{ account, amountUsd, invoiceReference }` unless you pass `toPayment`. A local check: no
|
|
126
|
+
* receipts.
|
|
127
|
+
*/
|
|
128
|
+
export function guardMcpPayHandler(handler, options) {
|
|
129
|
+
const guarded = guardPayments(handler, options);
|
|
130
|
+
return async (args, ...rest) => {
|
|
131
|
+
try {
|
|
132
|
+
return await guarded(args, ...rest);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (isPaymentRefusal(error))
|
|
136
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
111
141
|
//# sourceMappingURL=mcp.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type GuardPaymentsOptions } from "./payments.js";
|
|
2
|
+
export type { GuardPaymentsOptions } from "./payments.js";
|
|
3
|
+
/**
|
|
4
|
+
* The part of an OpenAI Agents SDK function tool (`tool()` from `@openai/agents`) the guard needs.
|
|
5
|
+
* The SDK calls `invoke` with the run context and the model's arguments as a JSON string.
|
|
6
|
+
*/
|
|
7
|
+
export interface OpenAiAgentsFunctionTool {
|
|
8
|
+
type: "function";
|
|
9
|
+
name: string;
|
|
10
|
+
invoke: (runContext: never, input: string, details?: never) => Promise<unknown>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Your own OpenAI Agents SDK pay tool, with every call checked against the payment mandate first
|
|
14
|
+
* (see `PaymentGuard`, or pass the hosted `PaymentGate` as `guard`). Same name, description,
|
|
15
|
+
* parameters, approval setting and guardrails. When the guard refuses (Enforce mode blocked it,
|
|
16
|
+
* the guard is frozen, the gate couldn't be reached, or it can't read the payment) your tool is not
|
|
17
|
+
* called and the model gets `Error: ...` naming the rules broken, so it can react. In Monitor mode
|
|
18
|
+
* a breaking payment goes through and the guard's `onBreach` reports it.
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { Agent, tool } from "@openai/agents";
|
|
22
|
+
* import { guardOpenAiAgentsPayTool } from "@bondedhq/sdk/openai-agents";
|
|
23
|
+
*
|
|
24
|
+
* const guard = paymentGuard({ mandate, vendors, invoices, onBreach: alert });
|
|
25
|
+
* const agent = new Agent({ name: "AP clerk", tools: [...readOnlyTools, guardOpenAiAgentsPayTool(payInvoice, { guard })] });
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* The payment is read from the tool's arguments: `{ account, amountUsd, invoiceReference }` unless
|
|
29
|
+
* you pass `toPayment`. If the tool needs a person's approval, the run stops for it before the
|
|
30
|
+
* guard is asked, so an approved payment can still be blocked.
|
|
31
|
+
*/
|
|
32
|
+
export declare function guardOpenAiAgentsPayTool<T extends OpenAiAgentsFunctionTool>(payTool: T, options: GuardPaymentsOptions<Record<string, unknown>>): T;
|
|
33
|
+
//# sourceMappingURL=openai-agents.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { BondedError } from "./errors.js";
|
|
2
|
+
import { guardPayments, isPaymentRefusal } from "./payments.js";
|
|
3
|
+
/**
|
|
4
|
+
* Your own OpenAI Agents SDK pay tool, with every call checked against the payment mandate first
|
|
5
|
+
* (see `PaymentGuard`, or pass the hosted `PaymentGate` as `guard`). Same name, description,
|
|
6
|
+
* parameters, approval setting and guardrails. When the guard refuses (Enforce mode blocked it,
|
|
7
|
+
* the guard is frozen, the gate couldn't be reached, or it can't read the payment) your tool is not
|
|
8
|
+
* called and the model gets `Error: ...` naming the rules broken, so it can react. In Monitor mode
|
|
9
|
+
* a breaking payment goes through and the guard's `onBreach` reports it.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { Agent, tool } from "@openai/agents";
|
|
13
|
+
* import { guardOpenAiAgentsPayTool } from "@bondedhq/sdk/openai-agents";
|
|
14
|
+
*
|
|
15
|
+
* const guard = paymentGuard({ mandate, vendors, invoices, onBreach: alert });
|
|
16
|
+
* const agent = new Agent({ name: "AP clerk", tools: [...readOnlyTools, guardOpenAiAgentsPayTool(payInvoice, { guard })] });
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* The payment is read from the tool's arguments: `{ account, amountUsd, invoiceReference }` unless
|
|
20
|
+
* you pass `toPayment`. If the tool needs a person's approval, the run stops for it before the
|
|
21
|
+
* guard is asked, so an approved payment can still be blocked.
|
|
22
|
+
*/
|
|
23
|
+
export function guardOpenAiAgentsPayTool(payTool, options) {
|
|
24
|
+
if (payTool?.type !== "function" || typeof payTool.invoke !== "function")
|
|
25
|
+
throw new BondedError("CONFIG", "guardOpenAiAgentsPayTool needs a function tool");
|
|
26
|
+
const invoke = payTool.invoke.bind(payTool);
|
|
27
|
+
const guarded = guardPayments((_args, runContext, input, details) => invoke(runContext, input, details), options);
|
|
28
|
+
return {
|
|
29
|
+
...payTool,
|
|
30
|
+
invoke: (async (runContext, input, details) => {
|
|
31
|
+
let args;
|
|
32
|
+
try {
|
|
33
|
+
args = JSON.parse(input || "{}");
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return "Error: the payment's arguments are not valid JSON; no payment was made";
|
|
37
|
+
}
|
|
38
|
+
// A custom toPayment reads fields: null or an array would throw before the guard answers.
|
|
39
|
+
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
40
|
+
return "Error: the payment's arguments must be an object; no payment was made";
|
|
41
|
+
try {
|
|
42
|
+
return await guarded(args, runContext, input, details);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (isPaymentRefusal(error))
|
|
46
|
+
return `Error: ${error.message}`;
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=openai-agents.js.map
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { type BudgetTerms, type GateRule, type PaidPayment, type PayableInvoice, type PayableRefund, type PaymentAttempt, type PaymentLimits, type SignedGateReceipt, type VerifiedChange } from "@bondedhq/shared";
|
|
2
|
+
import { type BudgetRequest, type PaymentBudget } from "./budget.js";
|
|
3
|
+
import { BondedError, PaymentBlockedError, PaymentFrozenError, PaymentGateError } from "./errors.js";
|
|
4
|
+
/** The off-chain payment mandate an agent's payments are checked against. */
|
|
5
|
+
export interface PaymentMandate extends PaymentLimits {
|
|
6
|
+
/** Monitor lets a breaking payment through and reports it; Enforce blocks it. */
|
|
7
|
+
mode: "monitor" | "enforce";
|
|
8
|
+
/** After the first breach, refuse every payment until a person unfreezes the guard. */
|
|
9
|
+
freezeOnBreach: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* False when your payments have no invoices: a payment needs a vendor on the list and to stay
|
|
12
|
+
* within the limits, and its reference is optional. Refunds still need an approved refund.
|
|
13
|
+
*/
|
|
14
|
+
invoiceMatching?: boolean;
|
|
15
|
+
/** Payments allowed in any hour, with invoice matching off. No limit unless set. */
|
|
16
|
+
maxPaymentsPerHour?: number;
|
|
17
|
+
}
|
|
18
|
+
/** A supplier as your vendor master holds it. Its account is one the agent may pay. */
|
|
19
|
+
export interface PaymentVendor {
|
|
20
|
+
id: string;
|
|
21
|
+
/** Bank account (an IBAN or account number). Compared without spaces, case-insensitively. */
|
|
22
|
+
account: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
}
|
|
25
|
+
/** A list the guard reads on every check: fixed, or read from your own system each time. */
|
|
26
|
+
export type PaymentSource<T> = readonly T[] | (() => readonly T[] | Promise<readonly T[]>);
|
|
27
|
+
/** What the guard remembers between payments. Persist it to keep it across restarts. */
|
|
28
|
+
export interface PaymentGuardState {
|
|
29
|
+
/** Payments made through the guard (and any made before it, if you restore them), oldest first. */
|
|
30
|
+
payments: PaidPayment[];
|
|
31
|
+
/** Frozen after a breach: every payment is refused until `unfreeze()`. */
|
|
32
|
+
frozen: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Bank changes made without a verified ticket (let through in Monitor mode). The vendor master
|
|
35
|
+
* may now show these accounts, but they never count as the vendor's: paying one is
|
|
36
|
+
* `unknown_payee`.
|
|
37
|
+
*/
|
|
38
|
+
unverifiedChanges: VerifiedChange[];
|
|
39
|
+
/**
|
|
40
|
+
* Open budgets (`budget()`): their terms and what was counted against them. Each one's whole
|
|
41
|
+
* amount is in `payments` until it closes. Absent in state saved before budgets existed.
|
|
42
|
+
*/
|
|
43
|
+
budgets?: LocalBudgetState[];
|
|
44
|
+
/** When budget payments were made in the last hour (Unix milliseconds), for the hourly limit. */
|
|
45
|
+
budgetSpends?: number[];
|
|
46
|
+
}
|
|
47
|
+
/** An open local budget, as the guard's state keeps it. */
|
|
48
|
+
export interface LocalBudgetState {
|
|
49
|
+
id: string;
|
|
50
|
+
terms: BudgetTerms;
|
|
51
|
+
spentMicros: number;
|
|
52
|
+
payments: number;
|
|
53
|
+
}
|
|
54
|
+
/** One payment (or bank change) that broke the mandate, as the guard reports it. */
|
|
55
|
+
export interface PaymentBreach {
|
|
56
|
+
kind: "invoice" | "refund" | "vendor_change" | "budget";
|
|
57
|
+
/** Every rule broken, most serious first. */
|
|
58
|
+
rules: GateRule[];
|
|
59
|
+
/** Each rule in plain words (`GATE_RULE_WORDS`), in the same order. */
|
|
60
|
+
reasons: string[];
|
|
61
|
+
/** Enforce mode stopped it: the pay function was not called. */
|
|
62
|
+
blocked: boolean;
|
|
63
|
+
account: string;
|
|
64
|
+
/** The invoice or refund reference; empty for a bank change or a budget. */
|
|
65
|
+
reference: string;
|
|
66
|
+
/** The vendor whose verified account this is (or, for a bank change, the vendor changed). */
|
|
67
|
+
vendorId: string | null;
|
|
68
|
+
amountUsd: number;
|
|
69
|
+
/** If it went out: paid to an account outside the vendor master, gone for good. */
|
|
70
|
+
lossUsd: number;
|
|
71
|
+
/** If it went out: paid to a known payee beyond what it was owed, claimable back. */
|
|
72
|
+
overpaidUsd: number;
|
|
73
|
+
/** UTC day it counts against, YYYY-MM-DD. */
|
|
74
|
+
day: string;
|
|
75
|
+
/** From the hosted gate (`PaymentGate`): the decision's id in its log. */
|
|
76
|
+
decisionId?: string;
|
|
77
|
+
/** From the hosted gate: Bonded's signed receipt for the decision. */
|
|
78
|
+
receipt?: SignedGateReceipt;
|
|
79
|
+
}
|
|
80
|
+
/** The rule verdict for a payment, without making it. */
|
|
81
|
+
export interface PaymentVerdict {
|
|
82
|
+
/** Every rule the payment breaks, most serious first. Empty when it is allowed. */
|
|
83
|
+
rules: GateRule[];
|
|
84
|
+
reasons: string[];
|
|
85
|
+
amountUsd: number;
|
|
86
|
+
reference: string;
|
|
87
|
+
vendorId: string | null;
|
|
88
|
+
lossUsd: number;
|
|
89
|
+
overpaidUsd: number;
|
|
90
|
+
/** The guard is frozen: the payment would be refused whatever the rules say. */
|
|
91
|
+
frozen: boolean;
|
|
92
|
+
}
|
|
93
|
+
export interface PaymentGuardOptions {
|
|
94
|
+
mandate: PaymentMandate;
|
|
95
|
+
/** The vendor master: each vendor's account on file. */
|
|
96
|
+
vendors: PaymentSource<PaymentVendor>;
|
|
97
|
+
/** Invoices the business approved for payment. */
|
|
98
|
+
invoices?: PaymentSource<PayableInvoice>;
|
|
99
|
+
/** Customer refunds finance approved. */
|
|
100
|
+
refunds?: PaymentSource<PayableRefund>;
|
|
101
|
+
/**
|
|
102
|
+
* Bank changes finance verified out of band (a call to the number on file). A ticket's account
|
|
103
|
+
* is a known payee for its vendor, and the only way a vendor's account may change.
|
|
104
|
+
*/
|
|
105
|
+
changes?: PaymentSource<VerifiedChange>;
|
|
106
|
+
/** State to start from, as `onStateChange` last saved it (or `state()` returned). */
|
|
107
|
+
state?: PaymentGuardState;
|
|
108
|
+
/** Called with the new state after every change, inside the guard's lock. Save it here. */
|
|
109
|
+
onStateChange?: (state: PaymentGuardState) => void | Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Called with every breach before the guard acts on it: blocked in Enforce mode, let through
|
|
112
|
+
* in Monitor mode. If it throws, the payment is not made.
|
|
113
|
+
*/
|
|
114
|
+
onBreach?: (breach: PaymentBreach) => void | Promise<void>;
|
|
115
|
+
now?: () => Date;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The payment mandate, checked inside your own process: the same rules (`@bondedhq/shared`'s
|
|
119
|
+
* payment mandate) the Arena rates agents against, with no network and no model. You give it the
|
|
120
|
+
* mandate and your vendor master, approved invoices and refunds, and verified bank changes; it
|
|
121
|
+
* keeps the payments made and the frozen flag, and hands them to `onStateChange` to persist.
|
|
122
|
+
*
|
|
123
|
+
* Wrap your pay function with `guardPayments` (or a framework helper) so every payment is checked
|
|
124
|
+
* first. In Enforce mode a payment that breaks a rule throws `PaymentBlockedError` and your pay
|
|
125
|
+
* function is never called; in Monitor mode it goes through and `onBreach` reports it. A frozen
|
|
126
|
+
* guard refuses everything (`PaymentFrozenError`).
|
|
127
|
+
*
|
|
128
|
+
* It is a local check: the agent's code can still call the pay function without it, and it makes
|
|
129
|
+
* no receipts. Payments are checked and made one at a time, so two at once can't both pass the
|
|
130
|
+
* duplicate or daily checks.
|
|
131
|
+
*/
|
|
132
|
+
export declare class PaymentGuard {
|
|
133
|
+
#private;
|
|
134
|
+
readonly mandate: Readonly<PaymentMandate>;
|
|
135
|
+
constructor(options: PaymentGuardOptions);
|
|
136
|
+
get frozen(): boolean;
|
|
137
|
+
/** A copy of what the guard remembers, to persist or inspect. */
|
|
138
|
+
state(): PaymentGuardState;
|
|
139
|
+
/** Lets payments through again after a freeze. For a person who reviewed the breach. */
|
|
140
|
+
unfreeze(): Promise<void>;
|
|
141
|
+
/** The rule verdict for a payment, without making or recording it. */
|
|
142
|
+
check(attempt: PaymentAttempt, kind?: "invoice" | "refund"): Promise<PaymentVerdict>;
|
|
143
|
+
/**
|
|
144
|
+
* Checks a payment, then calls `pay` if the mandate allows it (or Monitor mode lets it through)
|
|
145
|
+
* and records it once `pay` returns. Throws `PaymentBlockedError` (Enforce), `PaymentFrozenError`
|
|
146
|
+
* or `BondedError` `INVALID_PAYMENT` without calling `pay`. A `pay` that throws records nothing.
|
|
147
|
+
*/
|
|
148
|
+
pay<R>(attempt: PaymentAttempt, pay: () => R | Promise<R>, kind?: "invoice" | "refund"): Promise<R>;
|
|
149
|
+
/**
|
|
150
|
+
* Checks a vendor bank change, then calls `update` if a verified ticket covers that vendor and
|
|
151
|
+
* account (or Monitor mode lets it through). An unverified change breaks
|
|
152
|
+
* `unverified_vendor_change`; let through in Monitor mode, its account still never counts as
|
|
153
|
+
* the vendor's.
|
|
154
|
+
*/
|
|
155
|
+
changeVendorAccount<R>(request: {
|
|
156
|
+
vendorId: string;
|
|
157
|
+
account: string;
|
|
158
|
+
}, update: () => R | Promise<R>): Promise<R>;
|
|
159
|
+
/**
|
|
160
|
+
* Opens a budget: one check for many small payments to one payee (see `PaymentBudget`). Needs
|
|
161
|
+
* `invoiceMatching: false`. The payee must be a vendor's account (`unknown_payee`), the whole
|
|
162
|
+
* budget must fit in what's left of the day's limit (`over_daily_cap`), and with
|
|
163
|
+
* `maxPaymentsPerHour` there must be room for a payment (`over_hourly_limit`); the budget then
|
|
164
|
+
* covers what's left of the hour. Enforce mode throws `PaymentBlockedError`; Monitor mode opens it
|
|
165
|
+
* and reports the breach. The whole budget counts toward the day until `close()`, when what was
|
|
166
|
+
* spent (rounded up to the cent) counts instead. A budget past its end is closed the next time
|
|
167
|
+
* one is opened.
|
|
168
|
+
*/
|
|
169
|
+
budget(request: BudgetRequest): Promise<PaymentBudget>;
|
|
170
|
+
}
|
|
171
|
+
/** A payment guard: see `PaymentGuard`. */
|
|
172
|
+
export declare function paymentGuard(options: PaymentGuardOptions): PaymentGuard;
|
|
173
|
+
export interface GuardPaymentsOptions<I> {
|
|
174
|
+
/**
|
|
175
|
+
* A `PaymentGuard`, or the hosted `PaymentGate`. Share one between your invoice and refund
|
|
176
|
+
* functions, so limits and freezes are shared.
|
|
177
|
+
*/
|
|
178
|
+
guard: Pick<PaymentGuard, "pay">;
|
|
179
|
+
/** `invoice` (the default) pays approved invoices; `refund` pays approved customer refunds. */
|
|
180
|
+
kind?: "invoice" | "refund";
|
|
181
|
+
/**
|
|
182
|
+
* Reads the payment from your function's first argument. Default: `{ account, amountUsd,
|
|
183
|
+
* invoiceReference }` (or `reference`), the Arena's `pay_invoice` and `issue_refund` arguments.
|
|
184
|
+
*/
|
|
185
|
+
toPayment?: (input: I) => PaymentAttempt;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Wraps your own pay function so every call is checked against the payment mandate first. The
|
|
189
|
+
* wrapped function takes the same arguments and returns what yours returns. In Enforce mode a
|
|
190
|
+
* payment that breaks a rule throws `PaymentBlockedError` and your function is not called; in
|
|
191
|
+
* Monitor mode it is called and the guard's `onBreach` reports the breach. A frozen guard throws
|
|
192
|
+
* `PaymentFrozenError`.
|
|
193
|
+
*
|
|
194
|
+
* ```ts
|
|
195
|
+
* const guard = paymentGuard({ mandate, vendors, invoices, onBreach: alert });
|
|
196
|
+
* const pay = guardPayments(bank.pay, { guard });
|
|
197
|
+
* await pay({ account: "DE89 3704 0044 0532 0130 00", amountUsd: 1840.5, invoiceReference: "ACM-2291" });
|
|
198
|
+
* ```
|
|
199
|
+
*
|
|
200
|
+
* A local check: it makes no receipts, and code that calls your function directly skips it.
|
|
201
|
+
*/
|
|
202
|
+
export declare function guardPayments<I, Rest extends unknown[], R>(pay: (input: I, ...rest: Rest) => R | Promise<R>, options: GuardPaymentsOptions<I>): (input: I, ...rest: Rest) => Promise<R>;
|
|
203
|
+
export interface GuardVendorChangesOptions<I> {
|
|
204
|
+
/** A `PaymentGuard`, or the hosted `PaymentGate`. */
|
|
205
|
+
guard: Pick<PaymentGuard, "changeVendorAccount">;
|
|
206
|
+
/**
|
|
207
|
+
* Reads the change from your function's first argument. Default: `{ vendorId, account }`, and
|
|
208
|
+
* `ticket` when there is one (the hosted gate checks it).
|
|
209
|
+
*/
|
|
210
|
+
toChange?: (input: I) => {
|
|
211
|
+
vendorId: string;
|
|
212
|
+
account: string;
|
|
213
|
+
ticket?: string;
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Wraps your own "change a vendor's bank account" function: a change no verified ticket covers
|
|
218
|
+
* breaks `unverified_vendor_change`. Enforce mode throws `PaymentBlockedError` without calling
|
|
219
|
+
* it; Monitor mode calls it, reports the breach, and never treats the new account as the vendor's.
|
|
220
|
+
*/
|
|
221
|
+
export declare function guardVendorChanges<I, Rest extends unknown[], R>(update: (input: I, ...rest: Rest) => R | Promise<R>, options: GuardVendorChangesOptions<I>): (input: I, ...rest: Rest) => Promise<R>;
|
|
222
|
+
/**
|
|
223
|
+
* The payment guard or gate refused (blocked, frozen, arguments it can't check, or the gate gave no
|
|
224
|
+
* answer to act on): no money moved.
|
|
225
|
+
*/
|
|
226
|
+
export declare function isPaymentRefusal(error: unknown): error is PaymentBlockedError | PaymentFrozenError | PaymentGateError | BondedError;
|
|
227
|
+
//# sourceMappingURL=payments.d.ts.map
|