@stellar-agent-services/provider-kit 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +506 -2
- package/dist/approve/assets/index-BazIJAI2.css +1 -0
- package/dist/approve/assets/index-CL28cSJy.js +420 -0
- package/dist/approve/favicon.svg +1 -0
- package/dist/approve/index.html +18 -0
- package/dist/cli/approval-view.d.ts +80 -0
- package/dist/cli/approval-view.d.ts.map +1 -0
- package/dist/cli/approval-view.js +5 -0
- package/dist/cli/approval-view.js.map +1 -0
- package/dist/cli/approval.d.ts +132 -0
- package/dist/cli/approval.d.ts.map +1 -0
- package/dist/cli/approval.js +542 -0
- package/dist/cli/approval.js.map +1 -0
- package/dist/cli/check.d.ts +3 -0
- package/dist/cli/check.d.ts.map +1 -0
- package/dist/cli/check.js +137 -0
- package/dist/cli/check.js.map +1 -0
- package/dist/cli/dev-account.d.ts +28 -0
- package/dist/cli/dev-account.d.ts.map +1 -0
- package/dist/cli/dev-account.js +62 -0
- package/dist/cli/dev-account.js.map +1 -0
- package/dist/cli/dev.d.ts +27 -0
- package/dist/cli/dev.d.ts.map +1 -0
- package/dist/cli/dev.js +226 -0
- package/dist/cli/dev.js.map +1 -0
- package/dist/cli/discovery.d.ts +20 -0
- package/dist/cli/discovery.d.ts.map +1 -0
- package/dist/cli/discovery.js +202 -0
- package/dist/cli/discovery.js.map +1 -0
- package/dist/cli/env-file.d.ts +16 -0
- package/dist/cli/env-file.d.ts.map +1 -0
- package/dist/cli/env-file.js +77 -0
- package/dist/cli/env-file.js.map +1 -0
- package/dist/cli/fly.d.ts +28 -0
- package/dist/cli/fly.d.ts.map +1 -0
- package/dist/cli/fly.js +92 -0
- package/dist/cli/fly.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +11 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +170 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/launch.d.ts +21 -0
- package/dist/cli/launch.d.ts.map +1 -0
- package/dist/cli/launch.js +627 -0
- package/dist/cli/launch.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +158 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/owner-chain.d.ts +88 -0
- package/dist/cli/owner-chain.d.ts.map +1 -0
- package/dist/cli/owner-chain.js +284 -0
- package/dist/cli/owner-chain.js.map +1 -0
- package/dist/cli/owner-steps.d.ts +33 -0
- package/dist/cli/owner-steps.d.ts.map +1 -0
- package/dist/cli/owner-steps.js +121 -0
- package/dist/cli/owner-steps.js.map +1 -0
- package/dist/cli/processes.d.ts +47 -0
- package/dist/cli/processes.d.ts.map +1 -0
- package/dist/cli/processes.js +129 -0
- package/dist/cli/processes.js.map +1 -0
- package/dist/cli/reconcile.d.ts +3 -0
- package/dist/cli/reconcile.d.ts.map +1 -0
- package/dist/cli/reconcile.js +76 -0
- package/dist/cli/reconcile.js.map +1 -0
- package/dist/cli/register.d.ts +9 -0
- package/dist/cli/register.d.ts.map +1 -0
- package/dist/cli/register.js +209 -0
- package/dist/cli/register.js.map +1 -0
- package/dist/cli/request-validation.d.ts +5 -0
- package/dist/cli/request-validation.d.ts.map +1 -0
- package/dist/cli/request-validation.js +144 -0
- package/dist/cli/request-validation.js.map +1 -0
- package/dist/cli/serve.d.ts +25 -0
- package/dist/cli/serve.d.ts.map +1 -0
- package/dist/cli/serve.js +73 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/cli/shared.d.ts +142 -0
- package/dist/cli/shared.d.ts.map +1 -0
- package/dist/cli/shared.js +110 -0
- package/dist/cli/shared.js.map +1 -0
- package/dist/cli/status.d.ts +32 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +248 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/target.d.ts +14 -0
- package/dist/cli/target.d.ts.map +1 -0
- package/dist/cli/target.js +43 -0
- package/dist/cli/target.js.map +1 -0
- package/dist/cli/trustline.d.ts +3 -0
- package/dist/cli/trustline.d.ts.map +1 -0
- package/dist/cli/trustline.js +114 -0
- package/dist/cli/trustline.js.map +1 -0
- package/dist/cli/try.d.ts +54 -0
- package/dist/cli/try.d.ts.map +1 -0
- package/dist/cli/try.js +448 -0
- package/dist/cli/try.js.map +1 -0
- package/dist/cli/tunnel.d.ts +13 -0
- package/dist/cli/tunnel.d.ts.map +1 -0
- package/dist/cli/tunnel.js +43 -0
- package/dist/cli/tunnel.js.map +1 -0
- package/dist/cli/validators.d.ts +6 -0
- package/dist/cli/validators.d.ts.map +1 -0
- package/dist/cli/validators.js +14 -0
- package/dist/cli/validators.js.map +1 -0
- package/dist/define-service.d.ts +71 -0
- package/dist/define-service.d.ts.map +1 -0
- package/dist/define-service.js +86 -0
- package/dist/define-service.js.map +1 -0
- package/dist/gateway/config.d.ts +88 -0
- package/dist/gateway/config.d.ts.map +1 -0
- package/dist/gateway/config.js +201 -0
- package/dist/gateway/config.js.map +1 -0
- package/dist/gateway/openapi.d.ts +69 -0
- package/dist/gateway/openapi.d.ts.map +1 -0
- package/dist/gateway/openapi.js +677 -0
- package/dist/gateway/openapi.js.map +1 -0
- package/dist/gateway/upstream.d.ts +15 -0
- package/dist/gateway/upstream.d.ts.map +1 -0
- package/dist/gateway/upstream.js +95 -0
- package/dist/gateway/upstream.js.map +1 -0
- package/dist/http/bounded.d.ts +3 -0
- package/dist/http/bounded.d.ts.map +1 -0
- package/dist/http/bounded.js +23 -0
- package/dist/http/bounded.js.map +1 -0
- package/dist/http/node-server.d.ts +15 -0
- package/dist/http/node-server.d.ts.map +1 -0
- package/dist/http/node-server.js +120 -0
- package/dist/http/node-server.js.map +1 -0
- package/dist/http/rate-limit.d.ts +11 -0
- package/dist/http/rate-limit.d.ts.map +1 -0
- package/dist/http/rate-limit.js +35 -0
- package/dist/http/rate-limit.js.map +1 -0
- package/dist/http/router.d.ts +29 -0
- package/dist/http/router.d.ts.map +1 -0
- package/dist/http/router.js +329 -0
- package/dist/http/router.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/jobs/memory-store.d.ts +3 -0
- package/dist/jobs/memory-store.d.ts.map +1 -0
- package/dist/jobs/memory-store.js +121 -0
- package/dist/jobs/memory-store.js.map +1 -0
- package/dist/jobs/reconcile.d.ts +22 -0
- package/dist/jobs/reconcile.d.ts.map +1 -0
- package/dist/jobs/reconcile.js +82 -0
- package/dist/jobs/reconcile.js.map +1 -0
- package/dist/jobs/recovery.d.ts +19 -0
- package/dist/jobs/recovery.d.ts.map +1 -0
- package/dist/jobs/recovery.js +65 -0
- package/dist/jobs/recovery.js.map +1 -0
- package/dist/jobs/runner.d.ts +38 -0
- package/dist/jobs/runner.d.ts.map +1 -0
- package/dist/jobs/runner.js +194 -0
- package/dist/jobs/runner.js.map +1 -0
- package/dist/jobs/scheduler.d.ts +66 -0
- package/dist/jobs/scheduler.d.ts.map +1 -0
- package/dist/jobs/scheduler.js +197 -0
- package/dist/jobs/scheduler.js.map +1 -0
- package/dist/jobs/sqlite-store.d.ts +8 -0
- package/dist/jobs/sqlite-store.d.ts.map +1 -0
- package/dist/jobs/sqlite-store.js +231 -0
- package/dist/jobs/sqlite-store.js.map +1 -0
- package/dist/jobs/store.d.ts +118 -0
- package/dist/jobs/store.d.ts.map +1 -0
- package/dist/jobs/store.js +36 -0
- package/dist/jobs/store.js.map +1 -0
- package/dist/manifest.d.ts +11 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +99 -0
- package/dist/manifest.js.map +1 -0
- package/dist/payments/chain.d.ts +23 -0
- package/dist/payments/chain.d.ts.map +1 -0
- package/dist/payments/chain.js +108 -0
- package/dist/payments/chain.js.map +1 -0
- package/dist/payments/facilitator.d.ts +44 -0
- package/dist/payments/facilitator.d.ts.map +1 -0
- package/dist/payments/facilitator.js +125 -0
- package/dist/payments/facilitator.js.map +1 -0
- package/dist/payments/onchain.d.ts +36 -0
- package/dist/payments/onchain.d.ts.map +1 -0
- package/dist/payments/onchain.js +191 -0
- package/dist/payments/onchain.js.map +1 -0
- package/dist/payments/payload-checks.d.ts +22 -0
- package/dist/payments/payload-checks.d.ts.map +1 -0
- package/dist/payments/payload-checks.js +113 -0
- package/dist/payments/payload-checks.js.map +1 -0
- package/dist/payments/settlement.d.ts +22 -0
- package/dist/payments/settlement.d.ts.map +1 -0
- package/dist/payments/settlement.js +65 -0
- package/dist/payments/settlement.js.map +1 -0
- package/dist/payments/x402-server.d.ts +8 -0
- package/dist/payments/x402-server.d.ts.map +1 -0
- package/dist/payments/x402-server.js +74 -0
- package/dist/payments/x402-server.js.map +1 -0
- package/dist/provider.d.ts +63 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +177 -0
- package/dist/provider.js.map +1 -0
- package/dist/registration.d.ts +86 -0
- package/dist/registration.d.ts.map +1 -0
- package/dist/registration.js +162 -0
- package/dist/registration.js.map +1 -0
- package/dist/testing.d.ts +13 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +48 -0
- package/dist/testing.js.map +1 -0
- package/dist/validation-request.d.ts +36 -0
- package/dist/validation-request.d.ts.map +1 -0
- package/dist/validation-request.js +101 -0
- package/dist/validation-request.js.map +1 -0
- package/package.json +55 -4
- package/schemas/gateway-config.schema.json +140 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"target.js","sourceRoot":"","sources":["../../src/cli/target.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trustline.d.ts","sourceRoot":"","sources":["../../src/cli/trustline.ts"],"names":[],"mappings":"AAaA,OAAO,EAA4C,KAAK,QAAQ,EAAE,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAGpG,wBAAsB,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,GAAE,OAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAgE9F"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// provider-kit trustline: lets the payout account (payTo) receive USDC [P2], approved in the wallet that
|
|
2
|
+
// holds it, on a local approval page (cli/approval.ts). Clients pay in USDC, and a G... account can't
|
|
3
|
+
// receive it without a trustline.
|
|
4
|
+
//
|
|
5
|
+
// With --config or --manifest it sets up that provider's payTo. On its own it sets up whichever account the
|
|
6
|
+
// wallet connects, on --network (testnet by default), and prints it as PAY_TO. A new testnet account gets
|
|
7
|
+
// test XLM from Friendbot first. Nothing is signed when the trustline is already there, and a contract
|
|
8
|
+
// (C...) payTo needs none. With --source, a key in the Stellar CLI keychain signs instead, for scripts and CI.
|
|
9
|
+
import { stellarAddressKind, USDC_ISSUER } from "@stellar-agent-services/protocol";
|
|
10
|
+
import { ApprovalError, describeOutcome } from "./approval.js";
|
|
11
|
+
import { explorerUrl, networkName, shortAddress } from "./owner-chain.js";
|
|
12
|
+
import { accountSteps } from "./owner-steps.js";
|
|
13
|
+
import { ownerIo, parseNetwork, quote, UsageError } from "./shared.js";
|
|
14
|
+
import { loadTarget } from "./target.js";
|
|
15
|
+
export async function trustline(flags, io = ownerIo(flags)) {
|
|
16
|
+
let network;
|
|
17
|
+
let payTo;
|
|
18
|
+
if (flags.config !== undefined || flags.manifest !== undefined) {
|
|
19
|
+
if (flags.network !== undefined)
|
|
20
|
+
throw new UsageError("--network comes from the provider's manifest: leave it out with --config or --manifest");
|
|
21
|
+
const target = await loadTarget(flags);
|
|
22
|
+
if ("problems" in target) {
|
|
23
|
+
console.error(`The provider would not pass Provider Standard v1 checks:\n${target.problems.map((problem) => ` ✗ ${problem}`).join("\n")}`);
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
({ network, payTo } = target.manifest.payment.accepts[0]);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
network = parseNetwork(flags.network ?? "testnet");
|
|
30
|
+
}
|
|
31
|
+
if (payTo !== undefined && stellarAddressKind(payTo) === "contract") {
|
|
32
|
+
console.log(`payTo ${payTo} is a contract: it holds USDC without a trustline. Nothing to sign.`);
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
const chain = io.chain(network, flags.rpc);
|
|
36
|
+
if (payTo !== undefined && (await chain.account(payTo)).usdcTrustline) {
|
|
37
|
+
console.log(`✓ ${payTo} already has a USDC trustline on ${network}. Nothing to sign.`);
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
if (flags.source !== undefined)
|
|
41
|
+
return withStellarCli(flags, io, chain, flags.source, payTo);
|
|
42
|
+
if (flags["dry-run"]) {
|
|
43
|
+
console.log(`Dry run. Without --dry-run, a page opens where your wallet adds a USDC trustline on ${network}. Nothing was signed.`);
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
let account = payTo ?? "";
|
|
47
|
+
let added = false;
|
|
48
|
+
const plan = {
|
|
49
|
+
command: "provider-kit trustline",
|
|
50
|
+
title: "Get ready to receive USDC",
|
|
51
|
+
intro: "Clients pay your service in USDC. The account they pay needs a USDC trustline to receive it.",
|
|
52
|
+
network,
|
|
53
|
+
...(payTo === undefined ? {} : { account: { address: payTo, label: "Payout account", reason: "The account the provider's manifest names as payTo: clients pay it." } }),
|
|
54
|
+
outline: [{ id: "trustline", title: "Add USDC" }],
|
|
55
|
+
async steps(address) {
|
|
56
|
+
if (payTo !== undefined && address !== payTo) {
|
|
57
|
+
throw new ApprovalError("Connect your payout account", `Your wallet connected ${shortAddress(address)}. Switch it to ${shortAddress(payTo)}, the manifest's payTo.`, "connect");
|
|
58
|
+
}
|
|
59
|
+
account = address;
|
|
60
|
+
const steps = await accountSteps(chain, address, { trustline: true });
|
|
61
|
+
added = steps.some((step) => step.id === "trustline");
|
|
62
|
+
return steps;
|
|
63
|
+
},
|
|
64
|
+
result: () => ({
|
|
65
|
+
title: "Ready to receive USDC",
|
|
66
|
+
message: added
|
|
67
|
+
? `This account can now be paid in USDC on ${networkName(network)}.`
|
|
68
|
+
: `This account already had a USDC trustline on ${networkName(network)}, so nothing was signed.`,
|
|
69
|
+
details: [{ label: "Account", value: account, mono: true, href: explorerUrl(network, "account", account) }],
|
|
70
|
+
}),
|
|
71
|
+
};
|
|
72
|
+
const outcome = await io.approve(plan, chain);
|
|
73
|
+
if (outcome.status !== "done") {
|
|
74
|
+
console.log(describeOutcome(outcome));
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
console.log(added ? `${account} can now receive USDC on ${network}.` : `${account} already had a USDC trustline on ${network}.`);
|
|
78
|
+
if (payTo === undefined)
|
|
79
|
+
console.log(`Use it as your payout address: PAY_TO=${account}`);
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
/** --source: the Stellar CLI signs change_trust with a key from its keychain. */
|
|
83
|
+
async function withStellarCli(flags, io, chain, source, payTo) {
|
|
84
|
+
const { network } = chain;
|
|
85
|
+
const cliNetwork = network === "stellar:pubnet" ? "mainnet" : "testnet";
|
|
86
|
+
const args = ["tx", "new", "change-trust", "--source-account", source, "--network", cliNetwork, "--line", `USDC:${USDC_ISSUER[network]}`];
|
|
87
|
+
const stellar = await io.stellar();
|
|
88
|
+
if (flags["dry-run"] || stellar === undefined) {
|
|
89
|
+
console.log(stellar === undefined ? "The Stellar CLI (`stellar`) is not installed: https://developers.stellar.org/docs/tools/cli. Run this once it is:" : "Dry run. This would run:");
|
|
90
|
+
console.log(` stellar ${args.map(quote).join(" ")}`);
|
|
91
|
+
return stellar === undefined ? 1 : 0;
|
|
92
|
+
}
|
|
93
|
+
const address = (await stellar(["keys", "address", source])).trim();
|
|
94
|
+
if (payTo !== undefined && address !== payTo) {
|
|
95
|
+
console.error(`Key "${source}" is ${address}, not the manifest's payTo ${payTo}. Nothing was signed.`);
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
const state = await chain.account(address);
|
|
99
|
+
if (state.usdcTrustline) {
|
|
100
|
+
console.log(`✓ ${address} already has a USDC trustline on ${network}. Nothing to sign.`);
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
103
|
+
if (!state.exists) {
|
|
104
|
+
console.error(`${address} doesn't exist on ${network} yet: ${network === "stellar:testnet" ? `stellar keys fund ${source} --network testnet` : "send it some XLM"} first.`);
|
|
105
|
+
return 1;
|
|
106
|
+
}
|
|
107
|
+
console.log(`Signing with "${source}" (${address}) on ${cliNetwork}...`);
|
|
108
|
+
await stellar(args);
|
|
109
|
+
console.log(`${address} can now receive USDC on ${network}.`);
|
|
110
|
+
if (payTo === undefined)
|
|
111
|
+
console.log(`Use it as your payout address: PAY_TO=${address}`);
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=trustline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trustline.js","sourceRoot":"","sources":["../../src/cli/trustline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type PaymentClientOptions } from "@stellar-agent-services/payment-client";
|
|
2
|
+
import { type JsonObject, type StellarNetwork } from "@stellar-agent-services/protocol";
|
|
3
|
+
import { Keypair } from "@stellar/stellar-sdk";
|
|
4
|
+
import { type OwnerChain } from "./owner-chain.js";
|
|
5
|
+
import { type CliFlags } from "./shared.js";
|
|
6
|
+
/** Where `try` reaches the outside world. Tests replace it. */
|
|
7
|
+
export interface TryIo {
|
|
8
|
+
/** Prints a line. */
|
|
9
|
+
log(line: string): void;
|
|
10
|
+
/** Shows the step under way until the next line replaces it. Terminals only. "" clears it. */
|
|
11
|
+
status(text: string): void;
|
|
12
|
+
/** Whether someone is there to answer a question. */
|
|
13
|
+
interactive: boolean;
|
|
14
|
+
/** Asks yes or no. Enter means yes. */
|
|
15
|
+
confirm(question: string): Promise<boolean>;
|
|
16
|
+
chain(network: StellarNetwork, rpcUrl?: string): OwnerChain;
|
|
17
|
+
/** The throwaway payer's key: a new one each time. */
|
|
18
|
+
payer(): Keypair;
|
|
19
|
+
/** How the payment client reaches providers and pays: fetch and x402 `exact` unless a test says otherwise. */
|
|
20
|
+
client: Pick<PaymentClientOptions, "fetch" | "methods" | "pollMs">;
|
|
21
|
+
/**
|
|
22
|
+
* Starts the provider for this call when nothing answers at `manifestUrl`: a gateway config in this process, or a
|
|
23
|
+
* command. Without it, try only says to start the provider.
|
|
24
|
+
*/
|
|
25
|
+
startProvider?(how: {
|
|
26
|
+
config: string;
|
|
27
|
+
} | {
|
|
28
|
+
command: string;
|
|
29
|
+
}, manifestUrl: string): Promise<{
|
|
30
|
+
stop(): Promise<void>;
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
/** What `try --json` prints: how the call went. */
|
|
34
|
+
export interface TrySummary {
|
|
35
|
+
ok: boolean;
|
|
36
|
+
manifest: string;
|
|
37
|
+
service?: string;
|
|
38
|
+
jobId?: string;
|
|
39
|
+
status?: string;
|
|
40
|
+
charged?: boolean;
|
|
41
|
+
payment?: {
|
|
42
|
+
amount: string;
|
|
43
|
+
payTo: string;
|
|
44
|
+
transaction: string;
|
|
45
|
+
url: string;
|
|
46
|
+
confirmed: boolean;
|
|
47
|
+
};
|
|
48
|
+
output?: JsonObject;
|
|
49
|
+
error?: string;
|
|
50
|
+
}
|
|
51
|
+
export declare function tryIo(): TryIo;
|
|
52
|
+
/** provider-kit try [<service>] ['<input JSON>' | @<file>] */
|
|
53
|
+
export declare function tryCall(flags: CliFlags, args: string[], io?: TryIo): Promise<number>;
|
|
54
|
+
//# sourceMappingURL=try.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"try.d.ts","sourceRoot":"","sources":["../../src/cli/try.ts"],"names":[],"mappings":"AAsBA,OAAO,EAaL,KAAK,oBAAoB,EAG1B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAyB,KAAK,UAAU,EAAiD,KAAK,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAC9J,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAI/C,OAAO,EAAoF,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGrI,OAAO,EAA0D,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIpG,+DAA+D;AAC/D,MAAM,WAAW,KAAK;IACpB,qBAAqB;IACrB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,8FAA8F;IAC9F,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,qDAAqD;IACrD,WAAW,EAAE,OAAO,CAAC;IACrB,uCAAuC;IACvC,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,KAAK,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAC5D,sDAAsD;IACtD,KAAK,IAAI,OAAO,CAAC;IACjB,8GAA8G;IAC9G,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC,CAAC;IACnE;;;OAGG;IACH,aAAa,CAAC,CAAC,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC,CAAC;CACxH;AAED,mDAAmD;AACnD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAClG,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,KAAK,IAAI,KAAK,CAwC7B;AAsBD,8DAA8D;AAC9D,wBAAsB,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBnG"}
|
package/dist/cli/try.js
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
// provider-kit try: one paid call to a service on testnet, made the way any client makes it, from a throwaway payer
|
|
2
|
+
// (docs/provider-setup-guide.md §6). It shows the whole flow working before anyone else pays.
|
|
3
|
+
//
|
|
4
|
+
// 1. The provider: --manifest, or the baseUrl of --config. Without either, ./provider.config.json when it exists,
|
|
5
|
+
// else a provider on this computer's default port (http://localhost:8787).
|
|
6
|
+
// 2. The quote: an unpaid create-job and its 402 challenge, checked against the manifest [P7]. Nothing is signed.
|
|
7
|
+
// 3. Approval: the exact price, payee, asset and network, shown before anything is paid. --yes skips the question,
|
|
8
|
+
// as in scripts.
|
|
9
|
+
// 4. The payer: a new key, held in memory only, funded with test XLM by Friendbot and with the job's price in test
|
|
10
|
+
// USDC, bought on the testnet DEX. It is never written anywhere and is gone when the command ends.
|
|
11
|
+
// 5. The payment client pays once, follows the job and confirms the transfer on chain [J16]. Its spending policy
|
|
12
|
+
// allows this one payment only: the manifest's price, to the manifest's payTo.
|
|
13
|
+
//
|
|
14
|
+
// Testnet only: a provider on Mainnet is refused before anything is signed.
|
|
15
|
+
//
|
|
16
|
+
// When nothing answers, try starts the provider for this one call: a gateway config in this process (with a throwaway
|
|
17
|
+
// payTo when PAY_TO is empty, as `provider-kit dev` makes one), or --start's command, such as 'npm run dev'. --json
|
|
18
|
+
// prints the outcome as JSON on standard output, and the steps on standard error.
|
|
19
|
+
import { existsSync } from "node:fs";
|
|
20
|
+
import { readFile } from "node:fs/promises";
|
|
21
|
+
import { createInterface } from "node:readline/promises";
|
|
22
|
+
import { defaultPolicy, Journal, loadManifest, localKeySigner, ManifestError, memoryJournalStorage, memorySecretStore, PaymentClient, PaymentRefusedError, PaymentUncertainError, ProviderBusyError, ProviderError, } from "@stellar-agent-services/payment-client";
|
|
23
|
+
import { DEFAULT_MANIFEST_PATH } from "@stellar-agent-services/protocol";
|
|
24
|
+
import { Keypair } from "@stellar/stellar-sdk";
|
|
25
|
+
import { DEFAULT_PORT, gatewayManifestUrl } from "../gateway/config.js";
|
|
26
|
+
import { devPayTo } from "./dev-account.js";
|
|
27
|
+
import { ENV_FILE, loadEnvFile } from "./env-file.js";
|
|
28
|
+
import { ApprovalError, explorerUrl, formatUsdc, networkName, rpcOwnerChain, shortAddress } from "./owner-chain.js";
|
|
29
|
+
import { startCommand, waitForManifest } from "./processes.js";
|
|
30
|
+
import { serveGateway } from "./serve.js";
|
|
31
|
+
import { DEFAULT_CONFIG, plain, quote as shellQuote, UsageError } from "./shared.js";
|
|
32
|
+
/** The most of a job's output printed, in characters. */
|
|
33
|
+
const OUTPUT_LIMIT = 10_000;
|
|
34
|
+
export function tryIo() {
|
|
35
|
+
const live = process.stdout.isTTY === true;
|
|
36
|
+
let showing = false;
|
|
37
|
+
const clear = () => {
|
|
38
|
+
if (showing)
|
|
39
|
+
process.stdout.write("\r\x1b[K");
|
|
40
|
+
showing = false;
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
log(line) {
|
|
44
|
+
clear();
|
|
45
|
+
console.log(line);
|
|
46
|
+
},
|
|
47
|
+
status(text) {
|
|
48
|
+
if (!live)
|
|
49
|
+
return;
|
|
50
|
+
clear();
|
|
51
|
+
if (text === "")
|
|
52
|
+
return;
|
|
53
|
+
process.stdout.write(text);
|
|
54
|
+
showing = true;
|
|
55
|
+
},
|
|
56
|
+
interactive: process.stdin.isTTY === true,
|
|
57
|
+
async confirm(question) {
|
|
58
|
+
clear();
|
|
59
|
+
const readline = createInterface({ input: process.stdin, output: process.stdout });
|
|
60
|
+
try {
|
|
61
|
+
// Ctrl+C and Ctrl+D answer no.
|
|
62
|
+
const answer = await new Promise((resolve) => {
|
|
63
|
+
readline.once("SIGINT", () => resolve(undefined));
|
|
64
|
+
readline.question(question).then(resolve, () => resolve(undefined));
|
|
65
|
+
});
|
|
66
|
+
if (answer === undefined)
|
|
67
|
+
process.stdout.write("\n");
|
|
68
|
+
return answer !== undefined && /^(y|yes)?$/i.test(answer.trim());
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
readline.close();
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
chain: rpcOwnerChain,
|
|
75
|
+
payer: () => Keypair.random(),
|
|
76
|
+
client: {},
|
|
77
|
+
startProvider: startForTry,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** The provider, started for one call: a gateway in this process, or a command in the background until its manifest answers. */
|
|
81
|
+
async function startForTry(how, manifestUrl) {
|
|
82
|
+
if ("command" in how) {
|
|
83
|
+
const started = startCommand(how.command);
|
|
84
|
+
try {
|
|
85
|
+
await waitForManifest(manifestUrl, { exited: started.exited, timeoutMs: 120_000 });
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
await started.stop();
|
|
89
|
+
const output = started.output().trim().split("\n").slice(-12).join("\n");
|
|
90
|
+
throw new Error(`${how.command} didn't start the provider: ${error.message}${output === "" ? "" : `\nIts output:\n${output}`}`);
|
|
91
|
+
}
|
|
92
|
+
return started;
|
|
93
|
+
}
|
|
94
|
+
await loadEnvFile(ENV_FILE);
|
|
95
|
+
const network = JSON.parse(await readFile(how.config, "utf8")).network ?? "stellar:testnet";
|
|
96
|
+
await devPayTo({ env: process.env, network, chain: () => rpcOwnerChain(network), envFile: ENV_FILE });
|
|
97
|
+
const { server } = await serveGateway(how.config, { dev: true, log: () => { } });
|
|
98
|
+
return { stop: () => server.close() };
|
|
99
|
+
}
|
|
100
|
+
/** provider-kit try [<service>] ['<input JSON>' | @<file>] */
|
|
101
|
+
export async function tryCall(flags, args, io = tryIo()) {
|
|
102
|
+
if (args.length > 2)
|
|
103
|
+
throw new UsageError("try takes a service and its input: provider-kit try <service> ['<input JSON>' | @<file>]");
|
|
104
|
+
if (!flags.yes && !io.interactive)
|
|
105
|
+
throw new UsageError("try asks before it pays, and nobody can answer here: add --yes to pay without asking");
|
|
106
|
+
if (flags.start !== undefined && flags.manifest !== undefined)
|
|
107
|
+
throw new UsageError("--start runs a provider on this computer: leave out --manifest, or start it yourself");
|
|
108
|
+
const summary = { ok: false, manifest: "" };
|
|
109
|
+
// With --json, standard output holds only the JSON: the steps go to standard error.
|
|
110
|
+
const steps = flags.json ? { ...io, log: (line) => void process.stderr.write(`${line}\n`), status: () => { } } : io;
|
|
111
|
+
let code;
|
|
112
|
+
try {
|
|
113
|
+
code = await payOnce(flags, args, steps, summary);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (!flags.json)
|
|
117
|
+
throw error;
|
|
118
|
+
summary.error = plain(error instanceof Error ? error.message : String(error), 400);
|
|
119
|
+
code = error instanceof UsageError ? 2 : 1;
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
io.status("");
|
|
123
|
+
}
|
|
124
|
+
if (flags.json)
|
|
125
|
+
console.log(JSON.stringify({ ...summary, ok: code === 0 }, null, 2));
|
|
126
|
+
return code;
|
|
127
|
+
}
|
|
128
|
+
async function payOnce(flags, args, io, summary) {
|
|
129
|
+
const manifestUrl = await providerManifestUrl(flags);
|
|
130
|
+
summary.manifest = manifestUrl;
|
|
131
|
+
const fetcher = io.client.fetch ?? fetch;
|
|
132
|
+
let started;
|
|
133
|
+
let manifest;
|
|
134
|
+
try {
|
|
135
|
+
manifest = await readManifest(manifestUrl, fetcher);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
const config = flags.manifest === undefined ? (flags.config ?? (existsSync(DEFAULT_CONFIG) ? DEFAULT_CONFIG : undefined)) : undefined;
|
|
139
|
+
const how = flags.start !== undefined ? { command: flags.start } : config !== undefined ? { config } : undefined;
|
|
140
|
+
if (!(error instanceof NothingAnswers) || how === undefined || io.startProvider === undefined)
|
|
141
|
+
throw error;
|
|
142
|
+
started = await step(io, "command" in how ? `Started ${how.command} for this call` : `Started the gateway from ${how.config} for this call`, () => io.startProvider(how, manifestUrl));
|
|
143
|
+
manifest = await readManifest(manifestUrl, fetcher);
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
return await payStarted(flags, args, io, summary, manifestUrl, manifest);
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
await started?.stop();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function payStarted(flags, args, io, summary, manifestUrl, manifest) {
|
|
153
|
+
const network = manifest.payment.accepts[0].network;
|
|
154
|
+
if (network !== "stellar:testnet") {
|
|
155
|
+
throw new Error(`${plain(manifest.provider.name)} is on ${networkName(network)}. try pays from a throwaway testnet account, so it runs on testnet only. Nothing was signed.`);
|
|
156
|
+
}
|
|
157
|
+
const service = pickService(manifest, args[0], flags, io);
|
|
158
|
+
if (service === undefined) {
|
|
159
|
+
summary.error = `name a service: ${manifest.services.map((each) => each.name).join(", ")}`;
|
|
160
|
+
return 2;
|
|
161
|
+
}
|
|
162
|
+
summary.service = service.name;
|
|
163
|
+
const { input, example } = await readInput(service, args[1]);
|
|
164
|
+
const payer = io.payer();
|
|
165
|
+
const price = service.price.amount;
|
|
166
|
+
const journal = await Journal.open(memoryJournalStorage());
|
|
167
|
+
const client = new PaymentClient({
|
|
168
|
+
signer: localKeySigner(payer.secret(), network),
|
|
169
|
+
// This one payment and no other: the manifest's price, to the manifest's payTo.
|
|
170
|
+
policy: {
|
|
171
|
+
...defaultPolicy(payer.publicKey(), network),
|
|
172
|
+
maxValuePerTx: price,
|
|
173
|
+
maxValuePerDay: price,
|
|
174
|
+
stellar: { network, payees: { allow: [manifest.payment.accepts[0].payTo], block: [] }, autoApproveUpTo: "0" },
|
|
175
|
+
},
|
|
176
|
+
journal,
|
|
177
|
+
secrets: memorySecretStore(),
|
|
178
|
+
// A development tool on testnet: an http://localhost provider is fine [M6].
|
|
179
|
+
allowLocalhost: true,
|
|
180
|
+
followSeconds: Math.max(120, (service.expectedDurationSeconds ?? 0) * 2 + 30),
|
|
181
|
+
...(flags.rpc === undefined ? {} : { rpcUrl: flags.rpc }),
|
|
182
|
+
...io.client,
|
|
183
|
+
});
|
|
184
|
+
io.status(` … Asking ${service.name} for its price`);
|
|
185
|
+
let offer;
|
|
186
|
+
try {
|
|
187
|
+
offer = await client.quote(manifestUrl, service.name, input);
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
throw new Error(`${service.name} didn't quote a price: ${problem(error)}. Nothing was signed.`);
|
|
191
|
+
}
|
|
192
|
+
showTerms(io, offer, network, payer.publicKey(), example);
|
|
193
|
+
summary.jobId = offer.jobId;
|
|
194
|
+
if (!client.payable(offer)) {
|
|
195
|
+
io.log("Nothing was signed or paid.");
|
|
196
|
+
summary.error = "the challenge or the spending policy refused this payment; nothing was signed";
|
|
197
|
+
return 1;
|
|
198
|
+
}
|
|
199
|
+
if (!flags.yes) {
|
|
200
|
+
if (!(await io.confirm(`Pay ${formatUsdc(offer.requirements.amount)} in test money? [Y/n] `))) {
|
|
201
|
+
await client.decline(offer);
|
|
202
|
+
io.log("Not paid. Nothing was signed.");
|
|
203
|
+
summary.error = "declined; nothing was signed";
|
|
204
|
+
return 1;
|
|
205
|
+
}
|
|
206
|
+
io.log("");
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
await fundPayer(io, io.chain(network, flags.rpc), payer, offer.requirements.amount);
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
throw new Error(`${problem(error)}. Nothing was paid to ${service.name}.`);
|
|
213
|
+
}
|
|
214
|
+
io.status(` … Paying ${service.name} and waiting for its result`);
|
|
215
|
+
const started = Date.now();
|
|
216
|
+
let result;
|
|
217
|
+
try {
|
|
218
|
+
result = await client.pay(offer, { approved: true });
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
summary.error = problem(error);
|
|
222
|
+
return notPaid(io, error, offer, journal);
|
|
223
|
+
}
|
|
224
|
+
const { job, confirmed } = result;
|
|
225
|
+
Object.assign(summary, {
|
|
226
|
+
status: job.status,
|
|
227
|
+
...(job.charged === undefined ? {} : { charged: job.charged }),
|
|
228
|
+
...(job.payment === undefined
|
|
229
|
+
? {}
|
|
230
|
+
: {
|
|
231
|
+
payment: {
|
|
232
|
+
amount: formatUsdc(job.payment.amount),
|
|
233
|
+
payTo: offer.requirements.payTo,
|
|
234
|
+
transaction: job.payment.transaction,
|
|
235
|
+
url: explorerUrl(job.payment.network, "tx", job.payment.transaction),
|
|
236
|
+
confirmed,
|
|
237
|
+
},
|
|
238
|
+
}),
|
|
239
|
+
...(job.output === undefined ? {} : { output: job.output }),
|
|
240
|
+
...(job.error === undefined ? {} : { error: plain(`${job.error.code}${job.error.detail === undefined ? "" : `: ${job.error.detail}`}`, 400) }),
|
|
241
|
+
});
|
|
242
|
+
return report(io, offer, result, (Date.now() - started) / 1000);
|
|
243
|
+
}
|
|
244
|
+
/** The manifest URL of the provider to try: --manifest, the baseUrl of --config or ./provider.config.json, or this computer's default port. */
|
|
245
|
+
async function providerManifestUrl(flags) {
|
|
246
|
+
if (flags.config !== undefined && flags.manifest !== undefined)
|
|
247
|
+
throw new UsageError("pass --config <file> or --manifest <url>, not both");
|
|
248
|
+
if (flags.manifest !== undefined)
|
|
249
|
+
return flags.manifest;
|
|
250
|
+
const config = flags.config ?? (existsSync(DEFAULT_CONFIG) ? DEFAULT_CONFIG : undefined);
|
|
251
|
+
if (config === undefined)
|
|
252
|
+
return new URL(DEFAULT_MANIFEST_PATH, `http://localhost:${DEFAULT_PORT}`).toString();
|
|
253
|
+
try {
|
|
254
|
+
return await gatewayManifestUrl(config);
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
throw new Error(`${config}: ${error.message}. Set it as the provider has it, or pass --manifest <url>.`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/** The manifest, read as a client reads it: from the running provider, and checked against the standard. */
|
|
261
|
+
async function readManifest(url, fetcher) {
|
|
262
|
+
try {
|
|
263
|
+
return await loadManifest(url, { fetch: fetcher, allowLocalhost: true });
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
if (error instanceof ManifestError)
|
|
267
|
+
throw new Error(`the manifest at ${url} isn't valid: ${error.message}. provider-kit check lists every problem.`);
|
|
268
|
+
if (unreachable(error))
|
|
269
|
+
throw new NothingAnswers(`nothing answers at ${url}. Start the provider first, such as with provider-kit dev, or pass --start '<command>'.`);
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/** No provider listens at the manifest's address. */
|
|
274
|
+
class NothingAnswers extends Error {
|
|
275
|
+
name = "NothingAnswers";
|
|
276
|
+
}
|
|
277
|
+
/** The service named on the command line, or the only one there is. Lists them when there are several to pick from. */
|
|
278
|
+
function pickService(manifest, name, flags, io) {
|
|
279
|
+
const names = manifest.services.map((service) => service.name);
|
|
280
|
+
if (name !== undefined) {
|
|
281
|
+
const service = manifest.services.find((candidate) => candidate.name === name);
|
|
282
|
+
if (service === undefined)
|
|
283
|
+
throw new Error(`${plain(manifest.provider.name)} has no service "${name}". It has ${names.join(", ")}.`);
|
|
284
|
+
return service;
|
|
285
|
+
}
|
|
286
|
+
if (manifest.services.length === 1)
|
|
287
|
+
return manifest.services[0];
|
|
288
|
+
const target = flags.manifest !== undefined ? ` --manifest ${shellQuote(flags.manifest)}` : flags.config !== undefined ? ` --config ${shellQuote(flags.config)}` : "";
|
|
289
|
+
const width = Math.max(...names.map((candidate) => candidate.length));
|
|
290
|
+
io.log(`${plain(manifest.provider.name)} has ${names.length} services. Name the one to try:`);
|
|
291
|
+
for (const service of manifest.services) {
|
|
292
|
+
io.log(` provider-kit try ${service.name.padEnd(width)}${target} ${formatUsdc(service.price.amount)}`);
|
|
293
|
+
}
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
/** The job's input: the argument as JSON, the JSON file it names with @, or the service's first example. */
|
|
297
|
+
async function readInput(service, arg) {
|
|
298
|
+
if (arg === undefined) {
|
|
299
|
+
const example = service.examples?.[0]?.input;
|
|
300
|
+
if (example === undefined)
|
|
301
|
+
throw new UsageError(`${service.name} has no example input, so pass one: provider-kit try ${service.name} '<input JSON>'`);
|
|
302
|
+
return { input: example, example: true };
|
|
303
|
+
}
|
|
304
|
+
const file = arg.startsWith("@") ? arg.slice(1) : undefined;
|
|
305
|
+
let text = arg;
|
|
306
|
+
if (file !== undefined) {
|
|
307
|
+
try {
|
|
308
|
+
text = await readFile(file, "utf8");
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
throw new UsageError(`the input file can't be read: ${error.message}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
let input;
|
|
315
|
+
try {
|
|
316
|
+
input = JSON.parse(text);
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
input = undefined;
|
|
320
|
+
}
|
|
321
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) {
|
|
322
|
+
throw new UsageError(`the input must be a JSON object, such as '{"text":"Hello"}'${file === undefined ? "" : `, and ${file} doesn't hold one`}`);
|
|
323
|
+
}
|
|
324
|
+
return { input: input, example: false };
|
|
325
|
+
}
|
|
326
|
+
/** The terms of the payment, as the challenge states them: what the developer approves. */
|
|
327
|
+
function showTerms(io, offer, network, payer, example) {
|
|
328
|
+
const { manifest, service, requirements } = offer;
|
|
329
|
+
const charge = service.charge === "on-success" ? "charged only if the work succeeds" : "charged up front, before the work starts";
|
|
330
|
+
io.log(`Pay for one job at ${plain(manifest.provider.name)} (${new URL(manifest.baseUrl).host})`);
|
|
331
|
+
io.log("");
|
|
332
|
+
const rows = [
|
|
333
|
+
["Service", `${service.name}: ${plain(service.description, 70)}`],
|
|
334
|
+
["Input", `${plain(JSON.stringify(offer.input), 70)}${example ? " (its example)" : ""}`],
|
|
335
|
+
["Price", `${formatUsdc(requirements.amount)}, ${charge}`],
|
|
336
|
+
["Pay to", requirements.payTo],
|
|
337
|
+
["From", `${payer}, a throwaway test account`],
|
|
338
|
+
["Network", `${networkName(network)}: test money only`],
|
|
339
|
+
];
|
|
340
|
+
for (const [label, value] of rows)
|
|
341
|
+
io.log(` ${label.padEnd(8)} ${value}`);
|
|
342
|
+
for (const violation of offer.challengeViolations)
|
|
343
|
+
io.log(` ✗ ${violation}`);
|
|
344
|
+
for (const violation of offer.policyViolations)
|
|
345
|
+
io.log(` ✗ ${violation.field}: ${violation.message}`);
|
|
346
|
+
io.log("");
|
|
347
|
+
}
|
|
348
|
+
/** Funds the throwaway payer: test XLM from Friendbot, then the job's price in test USDC from the testnet DEX. */
|
|
349
|
+
async function fundPayer(io, chain, payer, amount) {
|
|
350
|
+
const address = payer.publicKey();
|
|
351
|
+
await step(io, "Test XLM from Friendbot", () => chain.fund(address));
|
|
352
|
+
await step(io, `Test USDC from the testnet DEX: ${formatUsdc(amount)}`, async () => {
|
|
353
|
+
const purchase = await chain.buyTestUsdc(address, amount);
|
|
354
|
+
purchase.sign(payer);
|
|
355
|
+
const sent = await chain.send(purchase);
|
|
356
|
+
if (sent.status === "failed") {
|
|
357
|
+
throw new Error(`Buying test USDC failed: ${sent.reason} Try again in a minute, or pay with stellar-pay from a test wallet of your own, funded at https://faucet.circle.com (docs/provider-setup-guide.md §6).`);
|
|
358
|
+
}
|
|
359
|
+
if (sent.status === "unknown")
|
|
360
|
+
throw new Error(`Buying test USDC may not have gone through: ${sent.reason}.`);
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
/** Runs one step: " … label" while it runs, on a terminal, then " ✓ label" or " ✗ label". */
|
|
364
|
+
async function step(io, label, run) {
|
|
365
|
+
io.status(` … ${label}`);
|
|
366
|
+
try {
|
|
367
|
+
const value = await run();
|
|
368
|
+
io.log(` ✓ ${label}`);
|
|
369
|
+
return value;
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
io.log(` ✗ ${label}`);
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* pay() threw. Before the paid request went out, a refusal or a busy answer to it charged nothing. Anything else
|
|
378
|
+
* after it went out may have been paid.
|
|
379
|
+
*/
|
|
380
|
+
function notPaid(io, error, offer, journal) {
|
|
381
|
+
const sent = journal.forJob(offer.jobId).some((entry) => entry.event === "sending");
|
|
382
|
+
if (!sent || error instanceof PaymentRefusedError || error instanceof ProviderBusyError) {
|
|
383
|
+
io.log(` ✗ Not paid: ${problem(error)}. Nothing was charged.`);
|
|
384
|
+
return 1;
|
|
385
|
+
}
|
|
386
|
+
const reason = error instanceof PaymentUncertainError ? error.reason : problem(error);
|
|
387
|
+
io.log(` ! The payment may have gone through, but the answer was lost: ${reason}.`);
|
|
388
|
+
io.log(` Don't pay again. The provider decides job ${offer.jobId} from the chain [J13]; provider-kit reconcile --dry-run shows where it stands.`);
|
|
389
|
+
return 1;
|
|
390
|
+
}
|
|
391
|
+
/** What became of the job and its payment. 0 only when the job succeeded and its payment is confirmed on chain [J16]. */
|
|
392
|
+
function report(io, offer, { job, confirmed, unconfirmedReason }, seconds) {
|
|
393
|
+
const name = offer.service.name;
|
|
394
|
+
const took = `${seconds.toFixed(1)} s`;
|
|
395
|
+
if (job.status === "succeeded")
|
|
396
|
+
io.log(` ✓ ${name} succeeded: ${took} from payment to result`);
|
|
397
|
+
else if (job.status === "failed")
|
|
398
|
+
io.log(` ✗ ${name} failed: ${job.error === undefined ? "no reason given" : plain(`${job.error.code}${job.error.detail === undefined ? "" : `: ${job.error.detail}`}`, 200)}`);
|
|
399
|
+
else if (job.status === "payment_uncertain")
|
|
400
|
+
io.log(` ! ${name} can't tell yet whether it was paid. The provider decides job ${job.jobId} from the chain [J13]; don't pay again.`);
|
|
401
|
+
else
|
|
402
|
+
io.log(` ! ${name} is still ${job.status} after ${took}. Its job is ${job.jobId}.`);
|
|
403
|
+
if (job.payment !== undefined) {
|
|
404
|
+
const link = explorerUrl(job.payment.network, "tx", job.payment.transaction);
|
|
405
|
+
if (confirmed)
|
|
406
|
+
io.log(` ✓ Paid ${formatUsdc(job.payment.amount)}, confirmed on chain: ${link}`);
|
|
407
|
+
else
|
|
408
|
+
io.log(` ! The provider says it was paid, but that isn't confirmed on chain: ${unconfirmedReason ?? "no reason given"}. ${link}`);
|
|
409
|
+
}
|
|
410
|
+
else if (job.charged === false) {
|
|
411
|
+
io.log(" ✓ Not charged");
|
|
412
|
+
}
|
|
413
|
+
if (job.output !== undefined)
|
|
414
|
+
io.log(`\n${printable(job.output)}`);
|
|
415
|
+
const ok = job.status === "succeeded" && confirmed;
|
|
416
|
+
if (ok)
|
|
417
|
+
io.log(`\n${shortAddress(offer.requirements.payTo)} (payTo) received ${formatUsdc(offer.requirements.amount)}.`);
|
|
418
|
+
return ok ? 0 : 1;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* An error in words, to go before ". ": an approval error's title and message, or the error's own message, which
|
|
422
|
+
* may carry the provider's text.
|
|
423
|
+
*/
|
|
424
|
+
function problem(error) {
|
|
425
|
+
let text;
|
|
426
|
+
if (error instanceof ApprovalError)
|
|
427
|
+
text = `${error.title}: ${error.message}`;
|
|
428
|
+
else if (unreachable(error))
|
|
429
|
+
text = "nothing answers at the provider's address";
|
|
430
|
+
else
|
|
431
|
+
text = error instanceof Error ? error.message : String(error);
|
|
432
|
+
// Which part of the input the service refused, and why.
|
|
433
|
+
const errors = error instanceof ProviderError ? (error.problem?.errors ?? []) : [];
|
|
434
|
+
if (errors.length > 0)
|
|
435
|
+
text += `: ${errors.map((entry) => `${entry.pointer || "/"} ${entry.message}`).join("; ")}`;
|
|
436
|
+
return plain(text, 400).replace(/\.$/, "");
|
|
437
|
+
}
|
|
438
|
+
/** fetch() found nothing listening, or no such host. */
|
|
439
|
+
function unreachable(error) {
|
|
440
|
+
return error instanceof TypeError && error.message === "fetch failed";
|
|
441
|
+
}
|
|
442
|
+
/** A job's output for the terminal: indented JSON, clipped when it is long. */
|
|
443
|
+
function printable(output) {
|
|
444
|
+
// JSON escapes the C0 controls in strings, but not C1 (U+0080–U+009F), which some terminals take as commands.
|
|
445
|
+
const text = JSON.stringify(output, null, 2).replace(/[\u007f-\u009f]/g, (char) => `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
446
|
+
return text.length > OUTPUT_LIMIT ? `${text.slice(0, OUTPUT_LIMIT)}\n… and ${text.length - OUTPUT_LIMIT} more characters` : text;
|
|
447
|
+
}
|
|
448
|
+
//# sourceMappingURL=try.js.map
|