@stellar-agent-services/provider-kit 0.0.0-stage → 0.1.1

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 (220) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +508 -2
  3. package/dist/approve/assets/index-BazIJAI2.css +1 -0
  4. package/dist/approve/assets/index-CL28cSJy.js +420 -0
  5. package/dist/approve/favicon.svg +1 -0
  6. package/dist/approve/index.html +18 -0
  7. package/dist/cli/approval-view.d.ts +80 -0
  8. package/dist/cli/approval-view.d.ts.map +1 -0
  9. package/dist/cli/approval-view.js +5 -0
  10. package/dist/cli/approval-view.js.map +1 -0
  11. package/dist/cli/approval.d.ts +132 -0
  12. package/dist/cli/approval.d.ts.map +1 -0
  13. package/dist/cli/approval.js +542 -0
  14. package/dist/cli/approval.js.map +1 -0
  15. package/dist/cli/check.d.ts +3 -0
  16. package/dist/cli/check.d.ts.map +1 -0
  17. package/dist/cli/check.js +137 -0
  18. package/dist/cli/check.js.map +1 -0
  19. package/dist/cli/dev-account.d.ts +28 -0
  20. package/dist/cli/dev-account.d.ts.map +1 -0
  21. package/dist/cli/dev-account.js +62 -0
  22. package/dist/cli/dev-account.js.map +1 -0
  23. package/dist/cli/dev.d.ts +27 -0
  24. package/dist/cli/dev.d.ts.map +1 -0
  25. package/dist/cli/dev.js +226 -0
  26. package/dist/cli/dev.js.map +1 -0
  27. package/dist/cli/discovery.d.ts +20 -0
  28. package/dist/cli/discovery.d.ts.map +1 -0
  29. package/dist/cli/discovery.js +202 -0
  30. package/dist/cli/discovery.js.map +1 -0
  31. package/dist/cli/env-file.d.ts +16 -0
  32. package/dist/cli/env-file.d.ts.map +1 -0
  33. package/dist/cli/env-file.js +77 -0
  34. package/dist/cli/env-file.js.map +1 -0
  35. package/dist/cli/fly.d.ts +28 -0
  36. package/dist/cli/fly.d.ts.map +1 -0
  37. package/dist/cli/fly.js +92 -0
  38. package/dist/cli/fly.js.map +1 -0
  39. package/dist/cli/index.d.ts +3 -0
  40. package/dist/cli/index.d.ts.map +1 -0
  41. package/dist/cli/index.js +11 -0
  42. package/dist/cli/index.js.map +1 -0
  43. package/dist/cli/init.d.ts +3 -0
  44. package/dist/cli/init.d.ts.map +1 -0
  45. package/dist/cli/init.js +170 -0
  46. package/dist/cli/init.js.map +1 -0
  47. package/dist/cli/launch.d.ts +21 -0
  48. package/dist/cli/launch.d.ts.map +1 -0
  49. package/dist/cli/launch.js +627 -0
  50. package/dist/cli/launch.js.map +1 -0
  51. package/dist/cli/main.d.ts +2 -0
  52. package/dist/cli/main.d.ts.map +1 -0
  53. package/dist/cli/main.js +158 -0
  54. package/dist/cli/main.js.map +1 -0
  55. package/dist/cli/owner-chain.d.ts +88 -0
  56. package/dist/cli/owner-chain.d.ts.map +1 -0
  57. package/dist/cli/owner-chain.js +284 -0
  58. package/dist/cli/owner-chain.js.map +1 -0
  59. package/dist/cli/owner-steps.d.ts +33 -0
  60. package/dist/cli/owner-steps.d.ts.map +1 -0
  61. package/dist/cli/owner-steps.js +121 -0
  62. package/dist/cli/owner-steps.js.map +1 -0
  63. package/dist/cli/processes.d.ts +47 -0
  64. package/dist/cli/processes.d.ts.map +1 -0
  65. package/dist/cli/processes.js +129 -0
  66. package/dist/cli/processes.js.map +1 -0
  67. package/dist/cli/reconcile.d.ts +3 -0
  68. package/dist/cli/reconcile.d.ts.map +1 -0
  69. package/dist/cli/reconcile.js +76 -0
  70. package/dist/cli/reconcile.js.map +1 -0
  71. package/dist/cli/register.d.ts +9 -0
  72. package/dist/cli/register.d.ts.map +1 -0
  73. package/dist/cli/register.js +209 -0
  74. package/dist/cli/register.js.map +1 -0
  75. package/dist/cli/request-validation.d.ts +5 -0
  76. package/dist/cli/request-validation.d.ts.map +1 -0
  77. package/dist/cli/request-validation.js +144 -0
  78. package/dist/cli/request-validation.js.map +1 -0
  79. package/dist/cli/serve.d.ts +25 -0
  80. package/dist/cli/serve.d.ts.map +1 -0
  81. package/dist/cli/serve.js +73 -0
  82. package/dist/cli/serve.js.map +1 -0
  83. package/dist/cli/shared.d.ts +142 -0
  84. package/dist/cli/shared.d.ts.map +1 -0
  85. package/dist/cli/shared.js +110 -0
  86. package/dist/cli/shared.js.map +1 -0
  87. package/dist/cli/status.d.ts +32 -0
  88. package/dist/cli/status.d.ts.map +1 -0
  89. package/dist/cli/status.js +248 -0
  90. package/dist/cli/status.js.map +1 -0
  91. package/dist/cli/target.d.ts +14 -0
  92. package/dist/cli/target.d.ts.map +1 -0
  93. package/dist/cli/target.js +43 -0
  94. package/dist/cli/target.js.map +1 -0
  95. package/dist/cli/trustline.d.ts +3 -0
  96. package/dist/cli/trustline.d.ts.map +1 -0
  97. package/dist/cli/trustline.js +114 -0
  98. package/dist/cli/trustline.js.map +1 -0
  99. package/dist/cli/try.d.ts +54 -0
  100. package/dist/cli/try.d.ts.map +1 -0
  101. package/dist/cli/try.js +450 -0
  102. package/dist/cli/try.js.map +1 -0
  103. package/dist/cli/tunnel.d.ts +13 -0
  104. package/dist/cli/tunnel.d.ts.map +1 -0
  105. package/dist/cli/tunnel.js +43 -0
  106. package/dist/cli/tunnel.js.map +1 -0
  107. package/dist/cli/validators.d.ts +6 -0
  108. package/dist/cli/validators.d.ts.map +1 -0
  109. package/dist/cli/validators.js +14 -0
  110. package/dist/cli/validators.js.map +1 -0
  111. package/dist/define-service.d.ts +71 -0
  112. package/dist/define-service.d.ts.map +1 -0
  113. package/dist/define-service.js +86 -0
  114. package/dist/define-service.js.map +1 -0
  115. package/dist/gateway/config.d.ts +88 -0
  116. package/dist/gateway/config.d.ts.map +1 -0
  117. package/dist/gateway/config.js +201 -0
  118. package/dist/gateway/config.js.map +1 -0
  119. package/dist/gateway/openapi.d.ts +69 -0
  120. package/dist/gateway/openapi.d.ts.map +1 -0
  121. package/dist/gateway/openapi.js +677 -0
  122. package/dist/gateway/openapi.js.map +1 -0
  123. package/dist/gateway/upstream.d.ts +15 -0
  124. package/dist/gateway/upstream.d.ts.map +1 -0
  125. package/dist/gateway/upstream.js +95 -0
  126. package/dist/gateway/upstream.js.map +1 -0
  127. package/dist/http/bounded.d.ts +3 -0
  128. package/dist/http/bounded.d.ts.map +1 -0
  129. package/dist/http/bounded.js +23 -0
  130. package/dist/http/bounded.js.map +1 -0
  131. package/dist/http/node-server.d.ts +15 -0
  132. package/dist/http/node-server.d.ts.map +1 -0
  133. package/dist/http/node-server.js +120 -0
  134. package/dist/http/node-server.js.map +1 -0
  135. package/dist/http/rate-limit.d.ts +11 -0
  136. package/dist/http/rate-limit.d.ts.map +1 -0
  137. package/dist/http/rate-limit.js +35 -0
  138. package/dist/http/rate-limit.js.map +1 -0
  139. package/dist/http/router.d.ts +29 -0
  140. package/dist/http/router.d.ts.map +1 -0
  141. package/dist/http/router.js +329 -0
  142. package/dist/http/router.js.map +1 -0
  143. package/dist/index.d.ts +20 -0
  144. package/dist/index.d.ts.map +1 -0
  145. package/dist/index.js +31 -0
  146. package/dist/index.js.map +1 -0
  147. package/dist/jobs/memory-store.d.ts +3 -0
  148. package/dist/jobs/memory-store.d.ts.map +1 -0
  149. package/dist/jobs/memory-store.js +121 -0
  150. package/dist/jobs/memory-store.js.map +1 -0
  151. package/dist/jobs/reconcile.d.ts +22 -0
  152. package/dist/jobs/reconcile.d.ts.map +1 -0
  153. package/dist/jobs/reconcile.js +82 -0
  154. package/dist/jobs/reconcile.js.map +1 -0
  155. package/dist/jobs/recovery.d.ts +19 -0
  156. package/dist/jobs/recovery.d.ts.map +1 -0
  157. package/dist/jobs/recovery.js +65 -0
  158. package/dist/jobs/recovery.js.map +1 -0
  159. package/dist/jobs/runner.d.ts +38 -0
  160. package/dist/jobs/runner.d.ts.map +1 -0
  161. package/dist/jobs/runner.js +194 -0
  162. package/dist/jobs/runner.js.map +1 -0
  163. package/dist/jobs/scheduler.d.ts +66 -0
  164. package/dist/jobs/scheduler.d.ts.map +1 -0
  165. package/dist/jobs/scheduler.js +197 -0
  166. package/dist/jobs/scheduler.js.map +1 -0
  167. package/dist/jobs/sqlite-store.d.ts +8 -0
  168. package/dist/jobs/sqlite-store.d.ts.map +1 -0
  169. package/dist/jobs/sqlite-store.js +231 -0
  170. package/dist/jobs/sqlite-store.js.map +1 -0
  171. package/dist/jobs/store.d.ts +118 -0
  172. package/dist/jobs/store.d.ts.map +1 -0
  173. package/dist/jobs/store.js +36 -0
  174. package/dist/jobs/store.js.map +1 -0
  175. package/dist/manifest.d.ts +11 -0
  176. package/dist/manifest.d.ts.map +1 -0
  177. package/dist/manifest.js +99 -0
  178. package/dist/manifest.js.map +1 -0
  179. package/dist/payments/chain.d.ts +23 -0
  180. package/dist/payments/chain.d.ts.map +1 -0
  181. package/dist/payments/chain.js +108 -0
  182. package/dist/payments/chain.js.map +1 -0
  183. package/dist/payments/facilitator.d.ts +44 -0
  184. package/dist/payments/facilitator.d.ts.map +1 -0
  185. package/dist/payments/facilitator.js +125 -0
  186. package/dist/payments/facilitator.js.map +1 -0
  187. package/dist/payments/onchain.d.ts +36 -0
  188. package/dist/payments/onchain.d.ts.map +1 -0
  189. package/dist/payments/onchain.js +191 -0
  190. package/dist/payments/onchain.js.map +1 -0
  191. package/dist/payments/payload-checks.d.ts +22 -0
  192. package/dist/payments/payload-checks.d.ts.map +1 -0
  193. package/dist/payments/payload-checks.js +113 -0
  194. package/dist/payments/payload-checks.js.map +1 -0
  195. package/dist/payments/settlement.d.ts +22 -0
  196. package/dist/payments/settlement.d.ts.map +1 -0
  197. package/dist/payments/settlement.js +65 -0
  198. package/dist/payments/settlement.js.map +1 -0
  199. package/dist/payments/x402-server.d.ts +8 -0
  200. package/dist/payments/x402-server.d.ts.map +1 -0
  201. package/dist/payments/x402-server.js +74 -0
  202. package/dist/payments/x402-server.js.map +1 -0
  203. package/dist/provider.d.ts +63 -0
  204. package/dist/provider.d.ts.map +1 -0
  205. package/dist/provider.js +177 -0
  206. package/dist/provider.js.map +1 -0
  207. package/dist/registration.d.ts +86 -0
  208. package/dist/registration.d.ts.map +1 -0
  209. package/dist/registration.js +162 -0
  210. package/dist/registration.js.map +1 -0
  211. package/dist/testing.d.ts +13 -0
  212. package/dist/testing.d.ts.map +1 -0
  213. package/dist/testing.js +48 -0
  214. package/dist/testing.js.map +1 -0
  215. package/dist/validation-request.d.ts +36 -0
  216. package/dist/validation-request.d.ts.map +1 -0
  217. package/dist/validation-request.js +101 -0
  218. package/dist/validation-request.js.map +1 -0
  219. package/package.json +55 -4
  220. package/schemas/gateway-config.schema.json +140 -0
@@ -0,0 +1,82 @@
1
+ // Reconciliation: deciding payment_uncertain jobs from on-chain data [J13].
2
+ //
3
+ // A job is payment_uncertain when its settlement was attempted and no usable answer came
4
+ // (payments/settlement.ts, jobs/recovery.ts). It is never charged again. The chain says what happened
5
+ // (payments/onchain.ts), and the job moves on as §5.6 allows:
6
+ //
7
+ // settled upfront: queued, with the receipt, and a worker runs it.
8
+ // on-success: succeeded, with the receipt. The output was stored before settling [J14].
9
+ // not settled failed with payment_failed, charged: false.
10
+ // undecided stays payment_uncertain, and the next run looks again.
11
+ //
12
+ // The provider runs this after every recovery sweep when it has a Soroban RPC, and operators run it
13
+ // with `provider-kit reconcile`. Each update is compare-and-set from payment_uncertain, so a late
14
+ // settlement answer, another process or a second run deciding the same job is harmless: they all read
15
+ // the same chain, and the first decision stands.
16
+ import { jobError } from "@stellar-agent-services/protocol";
17
+ import { findPayment } from "../payments/onchain.js";
18
+ const DEFAULT_LIMIT = 500;
19
+ /** Decides the store's payment_uncertain jobs that the chain can decide. */
20
+ export async function reconcile(store, options) {
21
+ const jobs = await store.list({
22
+ statuses: ["payment_uncertain"],
23
+ createdBefore: new Date(Date.now() + 1000).toISOString(),
24
+ limit: options.limit ?? DEFAULT_LIMIT,
25
+ });
26
+ if (jobs.length === 0)
27
+ return [];
28
+ // One read of the RPC's ledger range serves every job of the run.
29
+ let health;
30
+ try {
31
+ health = await options.chain.getHealth();
32
+ }
33
+ catch (error) {
34
+ const reason = `the chain could not be read: ${error.message}`;
35
+ return jobs.map((job) => ({ jobId: job.jobId, service: job.service, outcome: "undecided", status: job.status, reason }));
36
+ }
37
+ const results = [];
38
+ for (const job of jobs)
39
+ results.push(await reconcileJob(store, job, options, health));
40
+ return results;
41
+ }
42
+ async function reconcileJob(store, job, options, health) {
43
+ // Every ledger the payment could be in is gone from this RPC's history: nothing to ask it, so a
44
+ // backlog of such jobs costs no calls. One with more history (provider-kit reconcile --rpc) may tell.
45
+ const lookup = job.claim.expirationLedger < health.oldestLedger
46
+ ? { outcome: "undecided", reason: `the payment's ledgers are older than the RPC's history (it starts at ledger ${health.oldestLedger}); use an RPC with more history` }
47
+ : await findPayment(job.claim, job.settlement?.transaction, options, health);
48
+ const result = (status, extra = {}) => ({
49
+ jobId: job.jobId,
50
+ service: job.service,
51
+ outcome: lookup.outcome,
52
+ status,
53
+ ...extra,
54
+ });
55
+ let patch;
56
+ let extra;
57
+ switch (lookup.outcome) {
58
+ case "undecided":
59
+ return result(job.status, { reason: lookup.reason });
60
+ case "settled":
61
+ extra = { transaction: lookup.receipt.transaction };
62
+ if (job.charge === "upfront")
63
+ patch = { status: "queued", payment: lookup.receipt };
64
+ // The runner stores the output before it settles, so this means a broken store: never report a paid job as unpaid.
65
+ else if (job.output === undefined)
66
+ return { ...result(job.status, extra), outcome: "undecided", reason: "the payment settled, but the job's output is missing" };
67
+ else
68
+ patch = { status: "succeeded", payment: lookup.receipt };
69
+ break;
70
+ case "not_settled":
71
+ extra = { reason: lookup.reason };
72
+ patch = { status: "failed", error: jobError("payment_failed", `the payment was not settled (${lookup.reason}); nothing was charged`) };
73
+ break;
74
+ }
75
+ if (options.dryRun)
76
+ return result(patch.status, extra);
77
+ const updated = await store.update(job.jobId, "payment_uncertain", patch);
78
+ // Decided meanwhile, by a late settlement answer or another process: that decision stands.
79
+ const status = updated?.status ?? (await store.get(job.jobId))?.status ?? job.status;
80
+ return result(status, extra);
81
+ }
82
+ //# sourceMappingURL=reconcile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../src/jobs/reconcile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { LedgerClock } from "../payments/chain.js";
2
+ import type { JobStore } from "./store.js";
3
+ /** How far past its last deadline a job must be before a sweep decides that its process is gone. */
4
+ export declare const ORPHAN_MARGIN_SECONDS = 60;
5
+ export interface SweepOptions {
6
+ maxTimeoutSeconds: number;
7
+ /** How long a settle call may take (payments/facilitator.ts). */
8
+ settleTimeoutSeconds: number;
9
+ retentionSeconds: number;
10
+ /** Tells that a payment expired from its ledger, before the wall-clock cutoff. */
11
+ clock?: LedgerClock | undefined;
12
+ }
13
+ export declare function sweep(store: JobStore, options: SweepOptions, now?: number): Promise<void>;
14
+ /**
15
+ * Paid jobs, by service, of services not in `offered`: queued or running when the provider stopped
16
+ * offering them. They wait, so restoring the service runs them, and check-status keeps answering.
17
+ */
18
+ export declare function strandedJobs(store: JobStore, offered: ReadonlyMap<string, unknown>): Promise<Map<string, number>>;
19
+ //# sourceMappingURL=recovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../src/jobs/recovery.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAa,MAAM,YAAY,CAAC;AAEtD,oGAAoG;AACpG,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAGxC,MAAM,WAAW,YAAY;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iEAAiE;IACjE,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC;IACzB,kFAAkF;IAClF,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CACjC;AAED,wBAAsB,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,SAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BnG;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAMvH"}
@@ -0,0 +1,65 @@
1
+ // Recovery sweeps: decide what a crash, a lost worker or the passage of time left behind. Run at
2
+ // startup and then periodically, by every process sharing a store (the steps are compare-and-set, so
3
+ // running them twice is harmless).
4
+ //
5
+ // - A settlement that never answered (the process died, or settle outlived every deadline) becomes
6
+ // payment_uncertain, to be decided from on-chain data (jobs/reconcile.ts) [J13]. Nothing is charged
7
+ // again.
8
+ // - An on-success job that never started settling before its payment expired can never be charged:
9
+ // it fails with `timeout`, uncharged [S2], [Q4]. Expired means the ledger clock is past the payment's
10
+ // expiration ledger, or, whether or not the clock answers, maxTimeoutSeconds have passed since the
11
+ // claim.
12
+ // - An upfront job whose worker died is not handled here: workers lease it again (store.lease()).
13
+ // - Final jobs past jobs.retentionSeconds are deleted [J15].
14
+ import { jobError } from "@stellar-agent-services/protocol";
15
+ /** How far past its last deadline a job must be before a sweep decides that its process is gone. */
16
+ export const ORPHAN_MARGIN_SECONDS = 60;
17
+ const BATCH = 500;
18
+ export async function sweep(store, options, now = Date.now()) {
19
+ const settleCutoff = now - (options.settleTimeoutSeconds + ORPHAN_MARGIN_SECONDS) * 1000;
20
+ for (const job of await all(store, ["pending", "running"], settleCutoff)) {
21
+ // A running upfront job was settled before it queued; only on-success jobs settle while running.
22
+ if (job.status === "running" && job.charge !== "on-success")
23
+ continue;
24
+ if (job.settlingSince === undefined || Date.parse(job.settlingSince) >= settleCutoff)
25
+ continue;
26
+ console.error(`[provider-kit] job ${job.jobId}: its settlement never answered; it is payment_uncertain until reconciled`);
27
+ await store.update(job.jobId, job.status, {
28
+ status: "payment_uncertain",
29
+ settlement: { reason: "the settlement did not answer before the provider stopped" },
30
+ });
31
+ }
32
+ // An on-success job settles before its payment expires or not at all. Past its expiration ledger the
33
+ // signature is void, so even a live process can no longer charge for the job.
34
+ const paymentCutoff = now - (options.maxTimeoutSeconds + ORPHAN_MARGIN_SECONDS) * 1000;
35
+ const latestLedger = await options.clock?.latest().catch(() => undefined);
36
+ for (const job of await all(store, ["queued", "running"], latestLedger === undefined ? paymentCutoff : now + 1000)) {
37
+ if (job.charge !== "on-success" || job.settlingSince !== undefined)
38
+ continue;
39
+ const expired = Date.parse(job.createdAt) < paymentCutoff || (latestLedger !== undefined && latestLedger > job.claim.expirationLedger);
40
+ if (!expired)
41
+ continue;
42
+ const detail = "the job did not finish before its payment expired; nothing was charged";
43
+ if (job.status === "queued" && (await store.update(job.jobId, "queued", { status: "running" })) === undefined)
44
+ continue;
45
+ await store.update(job.jobId, "running", { status: "failed", error: jobError("timeout", detail) });
46
+ }
47
+ await store.deleteFinishedBefore(new Date(now - options.retentionSeconds * 1000).toISOString());
48
+ }
49
+ /**
50
+ * Paid jobs, by service, of services not in `offered`: queued or running when the provider stopped
51
+ * offering them. They wait, so restoring the service runs them, and check-status keeps answering.
52
+ */
53
+ export async function strandedJobs(store, offered) {
54
+ const stranded = new Map();
55
+ for (const job of await all(store, ["queued", "running"], Date.now() + 1000)) {
56
+ if (!offered.has(job.service))
57
+ stranded.set(job.service, (stranded.get(job.service) ?? 0) + 1);
58
+ }
59
+ return stranded;
60
+ }
61
+ /** Up to BATCH jobs per status group and sweep: the next sweep takes the rest. */
62
+ function all(store, statuses, before) {
63
+ return store.list({ statuses, createdBefore: new Date(before).toISOString(), limit: BATCH });
64
+ }
65
+ //# sourceMappingURL=recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../src/jobs/recovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { type JsonObject } from "@stellar-agent-services/protocol";
2
+ import type { PaymentPayload, PaymentRequirements, SettleResponse } from "@x402/core/types";
3
+ import type { Service } from "../define-service.js";
4
+ import type { LedgerClock } from "../payments/chain.js";
5
+ import type { Facilitator } from "../payments/facilitator.js";
6
+ import type { CheckedPayment } from "../payments/payload-checks.js";
7
+ import { type Scheduler } from "./scheduler.js";
8
+ import type { JobStore, StoredJob } from "./store.js";
9
+ /** Starts of one upfront job before it fails: a handler that keeps killing its worker is not retried forever. */
10
+ export declare const MAX_ATTEMPTS = 3;
11
+ export interface PaidRun {
12
+ store: JobStore;
13
+ facilitator: Facilitator;
14
+ scheduler: Scheduler;
15
+ service: Service;
16
+ input: JsonObject;
17
+ payload: PaymentPayload;
18
+ requirements: PaymentRequirements;
19
+ payment: CheckedPayment;
20
+ /** How long settlement may take before the job is payment_uncertain. */
21
+ settleTimeoutMs: number;
22
+ /** Times an on-success job by its payment's expiration ledger. */
23
+ clock?: LedgerClock | undefined;
24
+ }
25
+ /** on-success: resolves when the job is final or payment_uncertain, with the settle response if settled. */
26
+ export declare function runOnSuccess(job: StoredJob, run: PaidRun): Promise<SettleResponse | undefined>;
27
+ /** upfront: settles, then queues the job for a worker. Resolves once it is queued, failed or payment_uncertain. */
28
+ export declare function settleUpfront(job: StoredJob, run: PaidRun): Promise<SettleResponse | undefined>;
29
+ export interface WorkerContext {
30
+ store: JobStore;
31
+ services: ReadonlyMap<string, Service>;
32
+ scheduler: Scheduler;
33
+ owner: string;
34
+ leaseSeconds: number;
35
+ }
36
+ /** Runs a leased upfront job to its end, renewing the lease while the handler works. */
37
+ export declare function runLeased(job: StoredJob, context: WorkerContext): Promise<void>;
38
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/jobs/runner.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAiC,KAAK,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAClG,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAEpE,OAAO,EAAyB,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEtD,iHAAiH;AACjH,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,QAAQ,CAAC;IAChB,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,cAAc,CAAC;IACxB,YAAY,EAAE,mBAAmB,CAAC;IAClC,OAAO,EAAE,cAAc,CAAC;IACxB,wEAAwE;IACxE,eAAe,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CACjC;AAED,4GAA4G;AAC5G,wBAAsB,YAAY,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAgCpG;AAYD,mHAAmH;AACnH,wBAAgB,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAE/F;AAyDD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,QAAQ,CAAC;IAChB,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,SAAS,EAAE,SAAS,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wFAAwF;AACxF,wBAAsB,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCrF"}
@@ -0,0 +1,194 @@
1
+ // Runs claimed jobs under their charge policy (docs/provider-standard.md §3.2, §5.6).
2
+ //
3
+ // - "on-success": claimed as queued -> its turn (scheduler) -> running -> handler -> output checked ->
4
+ // output stored, settlement started -> settle -> succeeded | failed | payment_uncertain. A handler
5
+ // failure or an invalid output settles nothing [S1]. A job that can't start, or finish, before its
6
+ // payment expires fails with `timeout`, also uncharged [S2].
7
+ // - "upfront": claimed as pending -> settle -> queued (or failed | payment_uncertain). A worker in any
8
+ // process sharing the store then leases it: running -> handler -> succeeded | failed. Paid before it
9
+ // waits, so its place survives a restart [Q4]: a job whose worker died is leased again, up to
10
+ // MAX_ATTEMPTS starts, so the handler may run more than once for one job.
11
+ // Every state change is stored before the next step, so a lost response is always recoverable, and a
12
+ // process that dies mid-settlement leaves a trail recovery.ts can decide from.
13
+ //
14
+ // Settlement has a deadline (settleTimeoutMs). Past it the job is payment_uncertain, but the answer
15
+ // still counts if it comes: a late or raced result is recorded from payment_uncertain, which the spec
16
+ // allows to become queued, succeeded or failed (§5.6). A charged payment is never dropped.
17
+ //
18
+ // An on-success job's deadline is when its payment expires, less room to settle: the close of its
19
+ // expiration ledger by the ledger clock (payments/chain.ts), so a payment the client resent long after
20
+ // signing [Q8] is not worked on past its life. The claim time plus maxTimeoutSeconds bounds it too,
21
+ // and is the deadline when the clock can't answer.
22
+ import { jobError } from "@stellar-agent-services/protocol";
23
+ import { settle } from "../payments/settlement.js";
24
+ import { SETTLE_MARGIN_SECONDS } from "./scheduler.js";
25
+ /** Starts of one upfront job before it fails: a handler that keeps killing its worker is not retried forever. */
26
+ export const MAX_ATTEMPTS = 3;
27
+ /** on-success: resolves when the job is final or payment_uncertain, with the settle response if settled. */
28
+ export async function runOnSuccess(job, run) {
29
+ const { store, scheduler, service } = run;
30
+ const deadline = await paymentDeadline(job, run);
31
+ try {
32
+ const started = await scheduler.waitForTurn(job, deadline);
33
+ if (started === undefined) {
34
+ // Its turn never came, or came just as it gave up: either way it is failed from running.
35
+ await store.update(job.jobId, "queued", { status: "running" });
36
+ await fail(store, job.jobId, "timeout", "the job could not start before its payment expired; nothing was charged");
37
+ return undefined;
38
+ }
39
+ const budgetMs = Math.min(deadline - Date.now(), service.timeoutSeconds * 1000);
40
+ if (budgetMs <= 0) {
41
+ await fail(store, job.jobId, "timeout", "the job could not start before its payment expired; nothing was charged");
42
+ return undefined;
43
+ }
44
+ const startedAt = Date.now();
45
+ const work = await doWork(service, job.jobId, run.input, budgetMs);
46
+ scheduler.record(service.name, Date.now() - startedAt);
47
+ if ("failure" in work) {
48
+ await fail(store, job.jobId, work.failure, work.detail);
49
+ return undefined;
50
+ }
51
+ // Stored before settling: if this process dies mid-settlement, recovery finds the output and
52
+ // marks the job payment_uncertain rather than losing a payment that may have landed [J14].
53
+ const settling = await store.update(job.jobId, "running", { status: "running", output: work.output, settlingSince: new Date().toISOString() });
54
+ if (settling === undefined)
55
+ return undefined;
56
+ return await settleAndRecord(job, run, "running", (receipt) => ({ status: "succeeded", payment: receipt }));
57
+ }
58
+ finally {
59
+ scheduler.wake(service.name);
60
+ }
61
+ }
62
+ /**
63
+ * When an on-success job's work must be done, leaving room to settle before its payment expires [S2]:
64
+ * the close of its expiration ledger, but no later than maxTimeoutSeconds after the claim.
65
+ */
66
+ async function paymentDeadline(job, run) {
67
+ const byClaim = Date.parse(job.createdAt) + Math.max(1, run.requirements.maxTimeoutSeconds - SETTLE_MARGIN_SECONDS) * 1000;
68
+ const expires = await run.clock?.closesAt(job.claim.expirationLedger).catch(() => undefined);
69
+ return expires === undefined ? byClaim : Math.min(byClaim, expires - SETTLE_MARGIN_SECONDS * 1000);
70
+ }
71
+ /** upfront: settles, then queues the job for a worker. Resolves once it is queued, failed or payment_uncertain. */
72
+ export function settleUpfront(job, run) {
73
+ return settleAndRecord(job, run, "pending", (receipt) => ({ status: "queued", payment: receipt }));
74
+ }
75
+ async function settleAndRecord(job, run, from, settled) {
76
+ const { store, facilitator, payload, requirements, payment, settleTimeoutMs } = run;
77
+ const attempt = settle(facilitator, payload, requirements, payment.payer);
78
+ let timer;
79
+ const late = new Promise((resolve) => (timer = setTimeout(resolve, settleTimeoutMs, undefined)));
80
+ const settlement = await Promise.race([attempt, late]);
81
+ clearTimeout(timer);
82
+ if (settlement === undefined) {
83
+ const seconds = Math.round(settleTimeoutMs / 1000);
84
+ await record(job, run, from, { outcome: "uncertain", reason: `the settlement did not answer in ${seconds} s` }, settled);
85
+ void attempt.then((answer) => (answer.outcome === "uncertain" ? undefined : record(job, run, "payment_uncertain", answer, settled)));
86
+ return undefined;
87
+ }
88
+ return record(job, run, from, settlement, settled);
89
+ }
90
+ /**
91
+ * Stores a settlement's outcome. If the job is no longer in `from` because recovery already marked it
92
+ * payment_uncertain, the outcome is recorded from there instead [J13].
93
+ */
94
+ async function record(job, run, from, settlement, settled) {
95
+ const { store } = run;
96
+ const update = async (patch) => (await store.update(job.jobId, from, patch)) ?? (from === "payment_uncertain" ? undefined : await store.update(job.jobId, "payment_uncertain", patch));
97
+ switch (settlement.outcome) {
98
+ case "settled": {
99
+ const stored = await update(settled(settlement.receipt));
100
+ if (stored?.status === "queued")
101
+ run.scheduler.wake(run.service.name);
102
+ return settlement.response;
103
+ }
104
+ case "failed":
105
+ await update(finished({ status: "failed", error: jobError("payment_failed", `the payment was not settled (${settlement.reason}); nothing was charged`) }));
106
+ return undefined;
107
+ case "uncertain": {
108
+ // Decided later from on-chain data, never by charging again [J13]. The output stays withheld [J14].
109
+ const detail = settlement.transaction === undefined ? { reason: settlement.reason } : { reason: settlement.reason, transaction: settlement.transaction };
110
+ console.error(`[provider-kit] job ${job.jobId}: settlement uncertain (${settlement.reason}); run reconcile`);
111
+ await update({ status: "payment_uncertain", settlement: detail });
112
+ return undefined;
113
+ }
114
+ }
115
+ }
116
+ /** Runs a leased upfront job to its end, renewing the lease while the handler works. */
117
+ export async function runLeased(job, context) {
118
+ const { store, owner, leaseSeconds } = context;
119
+ const finish = (patch) => store.update(job.jobId, "running", patch, owner);
120
+ const service = context.services.get(job.service);
121
+ if (service === undefined) {
122
+ // Workers lease only the services they run, so this is a bug: leave the paid job for its lease to expire.
123
+ console.error(`[provider-kit] job ${job.jobId}: leased for "${job.service}", which this process does not run`);
124
+ return;
125
+ }
126
+ if (job.input === undefined) {
127
+ await finish(finished({ status: "failed", error: jobError("service_failed", "the job's input was lost") }));
128
+ return;
129
+ }
130
+ if ((job.attempts ?? 1) > MAX_ATTEMPTS) {
131
+ await finish(finished({ status: "failed", error: jobError("service_failed", `the job was interrupted ${MAX_ATTEMPTS} times`) }));
132
+ return;
133
+ }
134
+ const budgetMs = service.timeoutSeconds * 1000;
135
+ const lost = new AbortController();
136
+ const renew = setInterval(() => {
137
+ store.renew(job.jobId, owner, leaseSeconds).then((held) => held || lost.abort(new Error("the lease was lost")), (error) => console.error(`[provider-kit] job ${job.jobId}: the lease could not be renewed:`, error));
138
+ }, (leaseSeconds * 1000) / 3);
139
+ renew.unref();
140
+ try {
141
+ const startedAt = Date.now();
142
+ const work = await doWork(service, job.jobId, job.input, budgetMs, lost.signal);
143
+ context.scheduler.record(service.name, Date.now() - startedAt);
144
+ // Another worker took the job over: its result counts, this one is dropped.
145
+ if (lost.signal.aborted)
146
+ return;
147
+ if ("failure" in work)
148
+ await finish(finished({ status: "failed", error: jobError(work.failure, work.detail) }));
149
+ else
150
+ await finish(finished({ status: "succeeded", output: work.output }));
151
+ }
152
+ finally {
153
+ clearInterval(renew);
154
+ }
155
+ }
156
+ /** Runs the handler for at most budgetMs, or until `stop` aborts. The handler's signal aborts with it. */
157
+ async function doWork(service, jobId, input, budgetMs, stop) {
158
+ const timeout = new AbortController();
159
+ // A plain timer rather than AbortSignal.timeout(), so tests can drive it with fake timers.
160
+ const timer = setTimeout(() => timeout.abort(new DOMException("the work took too long", "TimeoutError")), budgetMs);
161
+ const signal = stop === undefined ? timeout.signal : AbortSignal.any([timeout.signal, stop]);
162
+ let output;
163
+ try {
164
+ output = await Promise.race([
165
+ service.handler(structuredClone(input), { signal, jobId }),
166
+ new Promise((_, reject) => {
167
+ if (signal.aborted)
168
+ reject(signal.reason);
169
+ signal.addEventListener("abort", () => reject(signal.reason), { once: true });
170
+ }),
171
+ ]);
172
+ }
173
+ catch (error) {
174
+ if (signal.aborted)
175
+ return { failure: "timeout", detail: `the work took longer than ${Math.round(budgetMs / 1000)} s` };
176
+ // The handler's message may contain anything, so it is not passed on [E2].
177
+ console.error(`[provider-kit] ${service.name} failed:`, error);
178
+ return { failure: "service_failed", detail: "the service handler failed" };
179
+ }
180
+ finally {
181
+ clearTimeout(timer);
182
+ }
183
+ if (!service.validateOutput(output))
184
+ return { failure: "output_invalid", detail: "the output did not match outputSchema" };
185
+ return { output };
186
+ }
187
+ /** running -> failed. */
188
+ function fail(store, jobId, code, detail) {
189
+ return store.update(jobId, "running", finished({ status: "failed", error: jobError(code, detail) }));
190
+ }
191
+ function finished(patch) {
192
+ return { ...patch, finishedAt: new Date().toISOString() };
193
+ }
194
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/jobs/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import type { Charge, QueuePosition } from "@stellar-agent-services/protocol";
2
+ import type { Service } from "../define-service.js";
3
+ import type { JobStore, StoredJob } from "./store.js";
4
+ export interface LaneConfig {
5
+ service: string;
6
+ charge: Charge;
7
+ concurrency: number;
8
+ maxQueued: number;
9
+ /** Longest a new job may expect to wait before it is turned away [Q2]. */
10
+ maxWaitSeconds: number;
11
+ /** Seeds the average job duration until jobs finish. */
12
+ expectedSeconds: number;
13
+ }
14
+ export type Admission = {
15
+ accepted: true;
16
+ reservation: Reservation;
17
+ } | {
18
+ accepted: false;
19
+ retryAfterSeconds: number;
20
+ };
21
+ /** A place held for a request between admission and its claim. */
22
+ export interface Reservation {
23
+ /** Safe to call more than once. */
24
+ release(): void;
25
+ }
26
+ export interface SchedulerOptions {
27
+ store: JobStore;
28
+ lanes: readonly LaneConfig[];
29
+ /** Runs a leased upfront job to its end. Without it, this process runs no upfront jobs (HTTP only). */
30
+ work?: (job: StoredJob) => Promise<void>;
31
+ /** This process's worker id, written on its leases. */
32
+ owner: string;
33
+ leaseSeconds: number;
34
+ /** How often to look for work other processes queued or left behind. */
35
+ pollMs: number;
36
+ }
37
+ export interface Scheduler {
38
+ admit(service: string): Promise<Admission>;
39
+ /** Runs fn under the lane's admission lock. The claim runs here, so admission never counts it twice. */
40
+ exclusive<T>(service: string, fn: () => Promise<T>): Promise<T>;
41
+ /**
42
+ * Waits until it is a queued on-success job's turn and starts it (queued → running). Resolves with
43
+ * the running job, or undefined if the turn did not come before `deadline` (epoch ms).
44
+ */
45
+ waitForTurn(job: StoredJob, deadline: number): Promise<StoredJob | undefined>;
46
+ /** Something changed in the lane (a job queued or finished): start whatever can start now. */
47
+ wake(service: string): void;
48
+ /** Resolves the next time a lane wakes or one of this process's jobs finishes: a cue to look again. */
49
+ changed(): Promise<void>;
50
+ /** Where a queued job stands [Q5], or undefined once it is no longer queued. */
51
+ position(job: StoredJob): Promise<QueuePosition | undefined>;
52
+ /** Feeds a finished job's duration into the wait estimates. */
53
+ record(service: string, durationMs: number): void;
54
+ /** Starts the poll that picks up work queued or left behind by other processes. */
55
+ start(): void;
56
+ stop(): void;
57
+ }
58
+ /** Room left, after an "on-success" job's work, to settle before the signed payment expires [S2]. */
59
+ export declare const SETTLE_MARGIN_SECONDS = 10;
60
+ /**
61
+ * The lane for a service. An "on-success" job is settled after its work, before the payment expires
62
+ * [S2], so it may wait only as long as leaves room for the work and the settlement.
63
+ */
64
+ export declare function laneFor(service: Service, maxTimeoutSeconds: number): LaneConfig;
65
+ export declare function scheduler(options: SchedulerOptions): Scheduler;
66
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/jobs/scheduler.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAC9E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,WAAW,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtH,kEAAkE;AAClE,MAAM,WAAW,WAAW;IAC1B,mCAAmC;IACnC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B,uGAAuG;IACvG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAC3C,wGAAwG;IACxG,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAChE;;;OAGG;IACH,WAAW,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;IAC9E,8FAA8F;IAC9F,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,uGAAuG;IACvG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,gFAAgF;IAChF,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IAC7D,+DAA+D;IAC/D,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAClD,mFAAmF;IACnF,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,IAAI,IAAI,CAAC;CACd;AASD,qGAAqG;AACrG,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC;;;GAGG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,GAAG,UAAU,CAQ/E;AAmBD,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAyJ9D"}