@paybond/kit 0.9.3 → 0.9.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 +41 -18
- package/dist/index.d.ts +66 -0
- package/dist/index.js +206 -2
- package/dist/init.js +173 -52
- package/dist/login.d.ts +35 -0
- package/dist/login.js +454 -0
- package/dist/mcp-server.js +154 -4
- package/package.json +2 -1
package/dist/init.js
CHANGED
|
@@ -11,6 +11,7 @@ const FRAMEWORKS = new Set([
|
|
|
11
11
|
"langgraph",
|
|
12
12
|
"mcp",
|
|
13
13
|
]);
|
|
14
|
+
const PRESETS = new Set(["paid-tool-guard"]);
|
|
14
15
|
const FRAMEWORK_NOTES = {
|
|
15
16
|
generic: "Wrap the returned function around any side-effecting tool handler.",
|
|
16
17
|
"provider-agnostic": "Use the guarded handler with OpenAI, Gemini, Claude/Anthropic, local models, or any custom runtime.",
|
|
@@ -25,26 +26,36 @@ const FRAMEWORK_NOTES = {
|
|
|
25
26
|
};
|
|
26
27
|
function usage() {
|
|
27
28
|
return [
|
|
28
|
-
"Usage: paybond-init [--framework generic|provider-agnostic|openai|claude|anthropic|gemini|google-ai|vercel-ai|langgraph|mcp] [--out paybond-
|
|
29
|
+
"Usage: paybond-init [--preset paid-tool-guard] [--framework generic|provider-agnostic|openai|claude|anthropic|gemini|google-ai|vercel-ai|langgraph|mcp] [--out paybond-paid-tool-guard.ts] [--force]",
|
|
29
30
|
"",
|
|
30
|
-
"Scaffolds a Paybond
|
|
31
|
+
"Scaffolds a production-shaped Paybond guardrail integration helper.",
|
|
31
32
|
].join("\n");
|
|
32
33
|
}
|
|
33
34
|
function parseArgs(argv) {
|
|
34
|
-
let
|
|
35
|
-
let
|
|
35
|
+
let preset = "paid-tool-guard";
|
|
36
|
+
let framework = "provider-agnostic";
|
|
37
|
+
let out = "paybond-paid-tool-guard.ts";
|
|
36
38
|
let force = false;
|
|
37
39
|
for (let i = 0; i < argv.length; i += 1) {
|
|
38
40
|
const arg = argv[i];
|
|
39
41
|
if (arg === "--help" || arg === "-h") {
|
|
40
42
|
process.stdout.write(`${usage()}\n`);
|
|
41
43
|
process.exitCode = 0;
|
|
42
|
-
return { framework, out, force };
|
|
44
|
+
return { preset, framework, out, force };
|
|
43
45
|
}
|
|
44
46
|
if (arg === "--force") {
|
|
45
47
|
force = true;
|
|
46
48
|
continue;
|
|
47
49
|
}
|
|
50
|
+
if (arg === "--preset") {
|
|
51
|
+
const raw = argv[i + 1];
|
|
52
|
+
i += 1;
|
|
53
|
+
if (!raw || !PRESETS.has(raw)) {
|
|
54
|
+
throw new Error("invalid --preset");
|
|
55
|
+
}
|
|
56
|
+
preset = raw;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
48
59
|
if (arg === "--framework") {
|
|
49
60
|
const raw = argv[i + 1];
|
|
50
61
|
i += 1;
|
|
@@ -65,71 +76,166 @@ function parseArgs(argv) {
|
|
|
65
76
|
}
|
|
66
77
|
throw new Error(`unknown argument: ${arg}`);
|
|
67
78
|
}
|
|
68
|
-
return { framework, out, force };
|
|
79
|
+
return { preset, framework, out, force };
|
|
69
80
|
}
|
|
70
81
|
function template(framework) {
|
|
71
|
-
return `import {
|
|
82
|
+
return `import {
|
|
83
|
+
Paybond,
|
|
84
|
+
type SandboxGuardrailBootstrapResult,
|
|
85
|
+
type SandboxGuardrailEvidenceResult,
|
|
86
|
+
} from "@paybond/kit";
|
|
72
87
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
maxPriceCents: number;
|
|
88
|
+
declare const process: {
|
|
89
|
+
env: Record<string, string | undefined>;
|
|
76
90
|
};
|
|
77
91
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
92
|
+
// Production integration helpers only. Add your paid-tool handler in
|
|
93
|
+
// application code and pass it to wrapPaidTool(...).
|
|
94
|
+
const DEFAULT_OPERATION = "paid_tool.operation";
|
|
95
|
+
const DEFAULT_REQUESTED_SPEND_CENTS = 500;
|
|
96
|
+
|
|
97
|
+
export type PaidToolHandler<TInput, TResult> = (input: TInput) => TResult | Promise<TResult>;
|
|
98
|
+
|
|
99
|
+
export type SandboxGuardrailIntentOptions = {
|
|
100
|
+
operation?: string;
|
|
101
|
+
requestedSpendCents?: number;
|
|
102
|
+
currency?: string;
|
|
103
|
+
evidenceSchema?: Record<string, unknown>;
|
|
104
|
+
metadata?: Record<string, unknown>;
|
|
105
|
+
idempotencyKey?: string;
|
|
81
106
|
};
|
|
82
107
|
|
|
83
|
-
export
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
108
|
+
export type SubmitSandboxEvidenceOptions = {
|
|
109
|
+
operation?: string;
|
|
110
|
+
requestedSpendCents?: number;
|
|
111
|
+
metadata?: Record<string, unknown>;
|
|
112
|
+
artifacts?: string[];
|
|
113
|
+
idempotencyKey?: string;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export type OpenPaybondFromEnvOptions = {
|
|
117
|
+
/**
|
|
118
|
+
* Load PAYBOND_API_KEY from this local env file when the process environment
|
|
119
|
+
* does not already provide it. Pass false when your agent host injects secrets.
|
|
120
|
+
*/
|
|
121
|
+
envFile?: string | false;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
function readEnvValue(body: string, key: string): string | undefined {
|
|
125
|
+
const pattern = new RegExp("^\\\\s*(?:export\\\\s+)?" + key + "\\\\s*=\\\\s*(.*)$", "m");
|
|
126
|
+
const match = body.match(pattern);
|
|
127
|
+
if (!match) return undefined;
|
|
128
|
+
let value = (match[1] ?? "").trim();
|
|
129
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
130
|
+
try {
|
|
131
|
+
value = JSON.parse(value);
|
|
132
|
+
} catch {
|
|
133
|
+
value = value.slice(1, -1);
|
|
134
|
+
}
|
|
135
|
+
} else if (value.startsWith("'") && value.endsWith("'")) {
|
|
136
|
+
value = value.slice(1, -1);
|
|
137
|
+
}
|
|
138
|
+
return value.trim() || undefined;
|
|
88
139
|
}
|
|
89
140
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
141
|
+
async function readTextFile(envFile: string): Promise<string | undefined> {
|
|
142
|
+
// @ts-ignore Node builtins are available in agent and CLI Node runtimes.
|
|
143
|
+
const fs: { readFile(path: string, encoding: "utf8"): Promise<string> } = await import("node:fs/promises");
|
|
144
|
+
try {
|
|
145
|
+
return await fs.readFile(envFile, "utf8");
|
|
146
|
+
} catch (err) {
|
|
147
|
+
if ((err as { code?: unknown })?.code === "ENOENT") return undefined;
|
|
148
|
+
throw err;
|
|
95
149
|
}
|
|
96
|
-
|
|
97
|
-
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export async function loadPaybondEnvFile(envFile = ".env.local"): Promise<void> {
|
|
153
|
+
if (process.env.PAYBOND_API_KEY?.trim()) return;
|
|
154
|
+
const body = await readTextFile(envFile);
|
|
155
|
+
if (body === undefined) return;
|
|
156
|
+
const apiKey = readEnvValue(body, "PAYBOND_API_KEY");
|
|
157
|
+
if (apiKey) {
|
|
158
|
+
process.env.PAYBOND_API_KEY = apiKey;
|
|
98
159
|
}
|
|
99
|
-
return { intentId, capabilityToken };
|
|
100
160
|
}
|
|
101
161
|
|
|
102
|
-
export function
|
|
103
|
-
if (
|
|
104
|
-
|
|
162
|
+
export async function openPaybondFromEnv(options: OpenPaybondFromEnvOptions = {}): Promise<Paybond> {
|
|
163
|
+
if (options.envFile !== false) {
|
|
164
|
+
await loadPaybondEnvFile(options.envFile ?? ".env.local");
|
|
105
165
|
}
|
|
106
|
-
|
|
166
|
+
const apiKey = process.env.PAYBOND_API_KEY?.trim();
|
|
167
|
+
if (!apiKey) {
|
|
168
|
+
throw new Error("PAYBOND_API_KEY is required; run paybond login or configure your agent host to pass it");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return Paybond.open({
|
|
172
|
+
apiKey,
|
|
173
|
+
gatewayBaseUrl: process.env.PAYBOND_GATEWAY_BASE_URL,
|
|
174
|
+
expectedEnvironment: "sandbox",
|
|
175
|
+
});
|
|
107
176
|
}
|
|
108
177
|
|
|
109
|
-
async function
|
|
110
|
-
|
|
111
|
-
|
|
178
|
+
export async function bootstrapSandboxGuardrailIntent(
|
|
179
|
+
paybond: Paybond,
|
|
180
|
+
options: SandboxGuardrailIntentOptions = {},
|
|
181
|
+
): Promise<SandboxGuardrailBootstrapResult> {
|
|
182
|
+
return paybond.guardrails.bootstrapSandbox({
|
|
183
|
+
operation: options.operation ?? DEFAULT_OPERATION,
|
|
184
|
+
requestedSpendCents: options.requestedSpendCents ?? DEFAULT_REQUESTED_SPEND_CENTS,
|
|
185
|
+
currency: options.currency ?? "usd",
|
|
186
|
+
evidenceSchema: options.evidenceSchema ?? {
|
|
187
|
+
type: "object",
|
|
188
|
+
required: ["confirmation_id", "charged_cents"],
|
|
189
|
+
properties: {
|
|
190
|
+
confirmation_id: { type: "string" },
|
|
191
|
+
charged_cents: { type: "integer" },
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
metadata: options.metadata,
|
|
195
|
+
idempotencyKey: options.idempotencyKey,
|
|
196
|
+
});
|
|
112
197
|
}
|
|
113
198
|
|
|
114
|
-
export
|
|
115
|
-
paybond: Paybond
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
199
|
+
export function wrapPaidTool<TInput, TResult>(
|
|
200
|
+
paybond: Paybond,
|
|
201
|
+
guardrail: Pick<
|
|
202
|
+
SandboxGuardrailBootstrapResult,
|
|
203
|
+
"intent_id" | "capability_token" | "operation" | "requested_spend_cents"
|
|
204
|
+
>,
|
|
205
|
+
handler: PaidToolHandler<TInput, TResult>,
|
|
206
|
+
): (input: TInput) => Promise<Awaited<TResult>> {
|
|
207
|
+
if (!guardrail.capability_token.trim()) {
|
|
208
|
+
throw new Error("sandbox guardrail bootstrap did not return a capability token");
|
|
121
209
|
}
|
|
122
|
-
|
|
210
|
+
|
|
211
|
+
const guard = paybond.spendGuard(guardrail.intent_id, guardrail.capability_token);
|
|
123
212
|
|
|
124
213
|
// ${FRAMEWORK_NOTES[framework]}
|
|
125
214
|
return guard.guardTool(
|
|
126
215
|
{
|
|
127
|
-
operation:
|
|
128
|
-
requestedSpendCents:
|
|
216
|
+
operation: guardrail.operation,
|
|
217
|
+
requestedSpendCents: guardrail.requested_spend_cents,
|
|
129
218
|
},
|
|
130
|
-
|
|
219
|
+
handler,
|
|
131
220
|
);
|
|
132
221
|
}
|
|
222
|
+
|
|
223
|
+
export async function submitSandboxEvidence(
|
|
224
|
+
paybond: Paybond,
|
|
225
|
+
guardrail: Pick<SandboxGuardrailBootstrapResult, "intent_id" | "operation" | "requested_spend_cents">,
|
|
226
|
+
payload: Record<string, unknown>,
|
|
227
|
+
options: SubmitSandboxEvidenceOptions = {},
|
|
228
|
+
): Promise<SandboxGuardrailEvidenceResult> {
|
|
229
|
+
return paybond.guardrails.submitSandboxEvidence({
|
|
230
|
+
intentId: guardrail.intent_id,
|
|
231
|
+
payload,
|
|
232
|
+
artifacts: options.artifacts,
|
|
233
|
+
operation: options.operation ?? guardrail.operation,
|
|
234
|
+
requestedSpendCents: options.requestedSpendCents ?? guardrail.requested_spend_cents,
|
|
235
|
+
metadata: options.metadata,
|
|
236
|
+
idempotencyKey: options.idempotencyKey,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
133
239
|
`;
|
|
134
240
|
}
|
|
135
241
|
async function writeScaffold(out, body, force) {
|
|
@@ -162,13 +268,28 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
162
268
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
163
269
|
return 0;
|
|
164
270
|
}
|
|
165
|
-
|
|
166
|
-
|
|
271
|
+
try {
|
|
272
|
+
await writeScaffold(parsed.out, template(parsed.framework), parsed.force);
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
276
|
+
return 1;
|
|
277
|
+
}
|
|
278
|
+
process.stdout.write(`Created Paybond guardrail integration: ${parsed.out}\n`);
|
|
167
279
|
return 0;
|
|
168
280
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
process.
|
|
174
|
-
|
|
281
|
+
function normalizeFileURL(url) {
|
|
282
|
+
return url.startsWith("file:///var/") ? url.replace("file:///var/", "file:///private/var/") : url;
|
|
283
|
+
}
|
|
284
|
+
function invokedFromCLI() {
|
|
285
|
+
const invokedPath = process.argv[1] ? normalizeFileURL(new URL("file://" + process.argv[1]).href) : "";
|
|
286
|
+
return Boolean(invokedPath && normalizeFileURL(import.meta.url) === invokedPath);
|
|
287
|
+
}
|
|
288
|
+
if (invokedFromCLI()) {
|
|
289
|
+
main().then((code) => {
|
|
290
|
+
process.exitCode = code;
|
|
291
|
+
}, (err) => {
|
|
292
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
293
|
+
process.exitCode = 1;
|
|
294
|
+
});
|
|
295
|
+
}
|
package/dist/login.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
type Writable = {
|
|
3
|
+
write(chunk: string): boolean;
|
|
4
|
+
};
|
|
5
|
+
export type DeviceEnvironment = "sandbox";
|
|
6
|
+
export type LoginOptions = {
|
|
7
|
+
envFile: string;
|
|
8
|
+
gateway: string;
|
|
9
|
+
environment: DeviceEnvironment;
|
|
10
|
+
noOpen: boolean;
|
|
11
|
+
force: boolean;
|
|
12
|
+
};
|
|
13
|
+
type FetchInput = string | URL;
|
|
14
|
+
type FetchInit = {
|
|
15
|
+
method?: string;
|
|
16
|
+
headers?: Record<string, string>;
|
|
17
|
+
body?: string;
|
|
18
|
+
};
|
|
19
|
+
type FetchLike = (input: FetchInput, init?: FetchInit) => Promise<Response>;
|
|
20
|
+
export type LoginDependencies = {
|
|
21
|
+
cwd?: string;
|
|
22
|
+
fetch?: FetchLike;
|
|
23
|
+
sleep?: (ms: number) => Promise<void>;
|
|
24
|
+
openBrowser?: (url: string) => Promise<boolean>;
|
|
25
|
+
stdout?: Writable;
|
|
26
|
+
stderr?: Writable;
|
|
27
|
+
now?: () => number;
|
|
28
|
+
};
|
|
29
|
+
export declare function parseArgs(argv: string[]): LoginOptions | "help";
|
|
30
|
+
export declare function assertGitIgnored(envPath: string, cwd: string): Promise<void>;
|
|
31
|
+
export declare function assertCanWriteEnvFile(envPath: string, force: boolean): Promise<void>;
|
|
32
|
+
export declare function writeEnvFile(envPath: string, rawKey: string, force: boolean): Promise<void>;
|
|
33
|
+
export declare function runLogin(options: LoginOptions, deps?: LoginDependencies): Promise<number>;
|
|
34
|
+
export declare function main(argv?: string[], deps?: LoginDependencies): Promise<number>;
|
|
35
|
+
export {};
|