@forestrie/mcp-register 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.
Files changed (139) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -2
  3. package/bin/mcp-register.mjs +2 -0
  4. package/dist/core/base64.d.ts +7 -0
  5. package/dist/core/base64.d.ts.map +1 -0
  6. package/dist/core/base64.js +20 -0
  7. package/dist/core/base64.js.map +1 -0
  8. package/dist/core/delegation.d.ts +56 -0
  9. package/dist/core/delegation.d.ts.map +1 -0
  10. package/dist/core/delegation.js +130 -0
  11. package/dist/core/delegation.js.map +1 -0
  12. package/dist/core/grant.d.ts +41 -0
  13. package/dist/core/grant.d.ts.map +1 -0
  14. package/dist/core/grant.js +78 -0
  15. package/dist/core/grant.js.map +1 -0
  16. package/dist/core/hex.d.ts +4 -0
  17. package/dist/core/hex.d.ts.map +1 -0
  18. package/dist/core/hex.js +18 -0
  19. package/dist/core/hex.js.map +1 -0
  20. package/dist/core/index.d.ts +28 -0
  21. package/dist/core/index.d.ts.map +1 -0
  22. package/dist/core/index.js +21 -0
  23. package/dist/core/index.js.map +1 -0
  24. package/dist/core/issue.d.ts +38 -0
  25. package/dist/core/issue.d.ts.map +1 -0
  26. package/dist/core/issue.js +135 -0
  27. package/dist/core/issue.js.map +1 -0
  28. package/dist/core/limits.d.ts +9 -0
  29. package/dist/core/limits.d.ts.map +1 -0
  30. package/dist/core/limits.js +9 -0
  31. package/dist/core/limits.js.map +1 -0
  32. package/dist/core/scrapi.d.ts +66 -0
  33. package/dist/core/scrapi.d.ts.map +1 -0
  34. package/dist/core/scrapi.js +174 -0
  35. package/dist/core/scrapi.js.map +1 -0
  36. package/dist/core/signer.d.ts +24 -0
  37. package/dist/core/signer.d.ts.map +1 -0
  38. package/dist/core/signer.js +2 -0
  39. package/dist/core/signer.js.map +1 -0
  40. package/dist/core/statement.d.ts +26 -0
  41. package/dist/core/statement.d.ts.map +1 -0
  42. package/dist/core/statement.js +70 -0
  43. package/dist/core/statement.js.map +1 -0
  44. package/dist/core/version.d.ts +23 -0
  45. package/dist/core/version.d.ts.map +1 -0
  46. package/dist/core/version.js +23 -0
  47. package/dist/core/version.js.map +1 -0
  48. package/dist/core/x402.d.ts +99 -0
  49. package/dist/core/x402.d.ts.map +1 -0
  50. package/dist/core/x402.js +202 -0
  51. package/dist/core/x402.js.map +1 -0
  52. package/dist/net/delegate.d.ts +56 -0
  53. package/dist/net/delegate.d.ts.map +1 -0
  54. package/dist/net/delegate.js +182 -0
  55. package/dist/net/delegate.js.map +1 -0
  56. package/dist/net/http.d.ts +17 -0
  57. package/dist/net/http.d.ts.map +1 -0
  58. package/dist/net/http.js +52 -0
  59. package/dist/net/http.js.map +1 -0
  60. package/dist/net/index.d.ts +14 -0
  61. package/dist/net/index.d.ts.map +1 -0
  62. package/dist/net/index.js +13 -0
  63. package/dist/net/index.js.map +1 -0
  64. package/dist/net/write.d.ts +55 -0
  65. package/dist/net/write.d.ts.map +1 -0
  66. package/dist/net/write.js +46 -0
  67. package/dist/net/write.js.map +1 -0
  68. package/dist/node/cli.d.ts +8 -0
  69. package/dist/node/cli.d.ts.map +1 -0
  70. package/dist/node/cli.js +143 -0
  71. package/dist/node/cli.js.map +1 -0
  72. package/dist/node/grant-store.d.ts +49 -0
  73. package/dist/node/grant-store.d.ts.map +1 -0
  74. package/dist/node/grant-store.js +95 -0
  75. package/dist/node/grant-store.js.map +1 -0
  76. package/dist/node/grant-tools.d.ts +29 -0
  77. package/dist/node/grant-tools.d.ts.map +1 -0
  78. package/dist/node/grant-tools.js +498 -0
  79. package/dist/node/grant-tools.js.map +1 -0
  80. package/dist/node/ledger.d.ts +26 -0
  81. package/dist/node/ledger.d.ts.map +1 -0
  82. package/dist/node/ledger.js +78 -0
  83. package/dist/node/ledger.js.map +1 -0
  84. package/dist/node/payer.d.ts +48 -0
  85. package/dist/node/payer.d.ts.map +1 -0
  86. package/dist/node/payer.js +170 -0
  87. package/dist/node/payer.js.map +1 -0
  88. package/dist/node/resolve-input.d.ts +19 -0
  89. package/dist/node/resolve-input.d.ts.map +1 -0
  90. package/dist/node/resolve-input.js +76 -0
  91. package/dist/node/resolve-input.js.map +1 -0
  92. package/dist/node/server.d.ts +47 -0
  93. package/dist/node/server.d.ts.map +1 -0
  94. package/dist/node/server.js +212 -0
  95. package/dist/node/server.js.map +1 -0
  96. package/dist/node/signer-file.d.ts +34 -0
  97. package/dist/node/signer-file.d.ts.map +1 -0
  98. package/dist/node/signer-file.js +183 -0
  99. package/dist/node/signer-file.js.map +1 -0
  100. package/dist/node/store.d.ts +40 -0
  101. package/dist/node/store.d.ts.map +1 -0
  102. package/dist/node/store.js +117 -0
  103. package/dist/node/store.js.map +1 -0
  104. package/dist/node/text.d.ts +29 -0
  105. package/dist/node/text.d.ts.map +1 -0
  106. package/dist/node/text.js +124 -0
  107. package/dist/node/text.js.map +1 -0
  108. package/dist/node/tools.d.ts +118 -0
  109. package/dist/node/tools.d.ts.map +1 -0
  110. package/dist/node/tools.js +548 -0
  111. package/dist/node/tools.js.map +1 -0
  112. package/package.json +108 -3
  113. package/src/core/base64.ts +18 -0
  114. package/src/core/delegation.ts +239 -0
  115. package/src/core/grant.ts +127 -0
  116. package/src/core/hex.ts +18 -0
  117. package/src/core/index.ts +84 -0
  118. package/src/core/issue.ts +209 -0
  119. package/src/core/limits.ts +8 -0
  120. package/src/core/scrapi.ts +236 -0
  121. package/src/core/signer.ts +23 -0
  122. package/src/core/statement.ts +133 -0
  123. package/src/core/version.ts +25 -0
  124. package/src/core/x402.ts +314 -0
  125. package/src/net/delegate.ts +274 -0
  126. package/src/net/http.ts +71 -0
  127. package/src/net/index.ts +26 -0
  128. package/src/net/write.ts +124 -0
  129. package/src/node/cli.ts +170 -0
  130. package/src/node/grant-store.ts +154 -0
  131. package/src/node/grant-tools.ts +707 -0
  132. package/src/node/ledger.ts +98 -0
  133. package/src/node/payer.ts +241 -0
  134. package/src/node/resolve-input.ts +96 -0
  135. package/src/node/server.ts +332 -0
  136. package/src/node/signer-file.ts +251 -0
  137. package/src/node/store.ts +175 -0
  138. package/src/node/text.ts +153 -0
  139. package/src/node/tools.ts +810 -0
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The one request helper the write path uses. Same contract as
3
+ * `@forestrie/mcp-resolve/net`'s readers, whose `NetError` and
4
+ * `RawResponse` it reuses: `redirect: "manual"` (a 303 is the answer, not
5
+ * something to follow), every non-2xx returned rather than thrown, and
6
+ * `NetError` only when no response was obtained.
7
+ */
8
+ import { NetError, } from "@forestrie/mcp-resolve/net";
9
+ export const DEFAULT_TIMEOUT_MS = 30_000;
10
+ export async function rawRequest(url, init, opts) {
11
+ const fetchImpl = opts?.fetchImpl ?? globalThis.fetch;
12
+ const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
13
+ const at = new Date().toISOString();
14
+ const controller = new AbortController();
15
+ let timer;
16
+ const timeout = new Promise((_resolve, reject) => {
17
+ timer = setTimeout(() => {
18
+ controller.abort();
19
+ reject(new NetError("timeout", `timed out after ${timeoutMs}ms`));
20
+ }, timeoutMs);
21
+ });
22
+ try {
23
+ const res = await Promise.race([
24
+ fetchImpl(url, {
25
+ method: init.method,
26
+ redirect: "manual",
27
+ signal: controller.signal,
28
+ ...(init.headers ? { headers: init.headers } : {}),
29
+ ...(init.body !== undefined ? { body: init.body } : {}),
30
+ }),
31
+ timeout,
32
+ ]);
33
+ const body = new Uint8Array(await Promise.race([res.arrayBuffer(), timeout]));
34
+ const headers = {};
35
+ res.headers.forEach((value, key) => {
36
+ headers[key.toLowerCase()] = value;
37
+ });
38
+ return { url, status: res.status, headers, body, at };
39
+ }
40
+ catch (err) {
41
+ if (err instanceof NetError)
42
+ throw err;
43
+ throw new NetError("network", `network error on ${init.method} ${url}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
44
+ }
45
+ finally {
46
+ clearTimeout(timer);
47
+ }
48
+ }
49
+ export function trimBase(url) {
50
+ return url.replace(/\/+$/, "");
51
+ }
52
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/net/http.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,QAAQ,GAGT,MAAM,4BAA4B,CAAC;AAEpC,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAQzC,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAW,EACX,IAAiB,EACjB,IAAmB;IAEnB,MAAM,SAAS,GAAG,IAAI,EAAE,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC;IACtD,MAAM,SAAS,GAAG,IAAI,EAAE,SAAS,IAAI,kBAAkB,CAAC;IACxD,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACpC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,IAAI,KAAgD,CAAC;IACrD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;QACtD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACtB,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,MAAM,CAAC,IAAI,QAAQ,CAAC,SAAS,EAAE,mBAAmB,SAAS,IAAI,CAAC,CAAC,CAAC;QACpE,CAAC,EAAE,SAAS,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;IACH,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YAC7B,SAAS,CAAC,GAAG,EAAE;gBACb,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,QAAQ,EAAE,QAAQ;gBAClB,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAClD,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACpE,CAAC;YACF,OAAO;SACR,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,UAAU,CACzB,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,OAAO,CAAC,CAAC,CACjD,CAAC;QACF,MAAM,OAAO,GAA2B,EAAE,CAAC;QAC3C,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACjC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC;QACrC,CAAC,CAAC,CAAC;QACH,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IACxD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,QAAQ;YAAE,MAAM,GAAG,CAAC;QACvC,MAAM,IAAI,QAAQ,CAChB,SAAS,EACT,oBAAoB,IAAI,CAAC,MAAM,IAAI,GAAG,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAC7F,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `@forestrie/mcp-register/net` — the ONLY place `fetch` is called.
3
+ *
4
+ * Every function here takes `opts.fetchImpl` (defaulting to
5
+ * `globalThis.fetch`) and never calls a bare `fetch`, so unit tests inject a
6
+ * fake under the forbidden-fetch global. Reads that mcp-resolve already
7
+ * does (receipts, genesis) are imported from `@forestrie/mcp-resolve/net`,
8
+ * not reimplemented.
9
+ */
10
+ export { DEFAULT_TIMEOUT_MS, rawRequest } from "./http.js";
11
+ export { getPendingDelegation, getRegistrationStatus, postDelegationCertificate, postGrant, postStatement, registerGrantUrl, prepareChildLog, } from "./write.js";
12
+ export { delegateSealing } from "./delegate.js";
13
+ export type { DelegateSealingInput, DelegateSealingOutcome, DelegateStage, } from "./delegate.js";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/net/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,YAAY,EACV,oBAAoB,EACpB,sBAAsB,EACtB,aAAa,GACd,MAAM,eAAe,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `@forestrie/mcp-register/net` — the ONLY place `fetch` is called.
3
+ *
4
+ * Every function here takes `opts.fetchImpl` (defaulting to
5
+ * `globalThis.fetch`) and never calls a bare `fetch`, so unit tests inject a
6
+ * fake under the forbidden-fetch global. Reads that mcp-resolve already
7
+ * does (receipts, genesis) are imported from `@forestrie/mcp-resolve/net`,
8
+ * not reimplemented.
9
+ */
10
+ export { DEFAULT_TIMEOUT_MS, rawRequest } from "./http.js";
11
+ export { getPendingDelegation, getRegistrationStatus, postDelegationCertificate, postGrant, postStatement, registerGrantUrl, prepareChildLog, } from "./write.js";
12
+ export { delegateSealing } from "./delegate.js";
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/net/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The write path's requests. Thin: each builds one request, returns the
3
+ * raw response, and leaves reading it to `src/core/scrapi.ts` or
4
+ * `src/core/delegation.ts`. Every function takes `fetchImpl` through
5
+ * `opts`.
6
+ *
7
+ * Endpoints, all public and none needing an operator token:
8
+ *
9
+ * - canopy `POST /register/{R}/entries`, the statement as
10
+ * `application/cose`, authorised by `Forestrie-Grant <base64>`;
11
+ * - canopy `POST /register/{R}/grants`, the child grant as
12
+ * `Forestrie-Grant <base64>`, the parent grant in a CBOR body, and an
13
+ * `X-PAYMENT` when paying canopy's 402;
14
+ * - canopy `POST /api/forest/{childLogId}/prepare`, no body, the same
15
+ * grant: publishes the child log's public root to the delegation
16
+ * coordinator when register-grant's auto-forward did not;
17
+ * - coordinator `GET /api/logs/{logId}/pending-delegation`;
18
+ * - coordinator `POST /api/delegations/certificate`, JSON.
19
+ */
20
+ import type { FetchOptions, RawResponse } from "@forestrie/mcp-resolve/net";
21
+ import type { CertificateSubmission } from "../core/delegation.js";
22
+ export declare function postStatement(input: {
23
+ baseUrl: string;
24
+ rootLogId: string;
25
+ statement: Uint8Array;
26
+ grantB64: string;
27
+ }, opts?: FetchOptions): Promise<RawResponse>;
28
+ /** The URL `postGrant` POSTs: also the x402 resource a payment is for. */
29
+ export declare function registerGrantUrl(baseUrl: string, rootLogId: string): string;
30
+ export declare function postGrant(input: {
31
+ baseUrl: string;
32
+ rootLogId: string;
33
+ grantB64: string;
34
+ /** `registerGrantBody(parentGrant)`: canonical CBOR `{parentGrant}`. */
35
+ body: Uint8Array;
36
+ /** The `X-PAYMENT` header value, when paying. */
37
+ payment?: string;
38
+ }, opts?: FetchOptions): Promise<RawResponse>;
39
+ export declare function getRegistrationStatus(input: {
40
+ statusUrl: string;
41
+ }, opts?: FetchOptions): Promise<RawResponse>;
42
+ export declare function prepareChildLog(input: {
43
+ baseUrl: string;
44
+ logId: string;
45
+ grantB64: string;
46
+ }, opts?: FetchOptions): Promise<RawResponse>;
47
+ export declare function getPendingDelegation(input: {
48
+ coordinatorUrl: string;
49
+ logId: string;
50
+ }, opts?: FetchOptions): Promise<RawResponse>;
51
+ export declare function postDelegationCertificate(input: {
52
+ coordinatorUrl: string;
53
+ submission: CertificateSubmission;
54
+ }, opts?: FetchOptions): Promise<RawResponse>;
55
+ //# sourceMappingURL=write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/net/write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAGnE,wBAAgB,aAAa,CAC3B,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,UAAU,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,EACD,IAAI,CAAC,EAAE,YAAY,GAClB,OAAO,CAAC,WAAW,CAAC,CAatB;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED,wBAAgB,SAAS,CACvB,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,IAAI,EAAE,UAAU,CAAC;IACjB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,EACD,IAAI,CAAC,EAAE,YAAY,GAClB,OAAO,CAAC,WAAW,CAAC,CActB;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,EAC5B,IAAI,CAAC,EAAE,YAAY,GAClB,OAAO,CAAC,WAAW,CAAC,CAEtB;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAC3D,IAAI,CAAC,EAAE,YAAY,GAClB,OAAO,CAAC,WAAW,CAAC,CAStB;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAChD,IAAI,CAAC,EAAE,YAAY,GAClB,OAAO,CAAC,WAAW,CAAC,CAMtB;AAED,wBAAgB,yBAAyB,CACvC,KAAK,EAAE;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,qBAAqB,CAAA;CAAE,EACpE,IAAI,CAAC,EAAE,YAAY,GAClB,OAAO,CAAC,WAAW,CAAC,CAUtB"}
@@ -0,0 +1,46 @@
1
+ import { rawRequest, trimBase } from "./http.js";
2
+ export function postStatement(input, opts) {
3
+ return rawRequest(`${trimBase(input.baseUrl)}/register/${encodeURIComponent(input.rootLogId)}/entries`, {
4
+ method: "POST",
5
+ headers: {
6
+ "content-type": "application/cose",
7
+ authorization: `Forestrie-Grant ${input.grantB64}`,
8
+ },
9
+ body: input.statement,
10
+ }, opts);
11
+ }
12
+ /** The URL `postGrant` POSTs: also the x402 resource a payment is for. */
13
+ export function registerGrantUrl(baseUrl, rootLogId) {
14
+ return `${trimBase(baseUrl)}/register/${encodeURIComponent(rootLogId)}/grants`;
15
+ }
16
+ export function postGrant(input, opts) {
17
+ return rawRequest(registerGrantUrl(input.baseUrl, input.rootLogId), {
18
+ method: "POST",
19
+ headers: {
20
+ "content-type": "application/cbor",
21
+ authorization: `Forestrie-Grant ${input.grantB64}`,
22
+ ...(input.payment !== undefined ? { "x-payment": input.payment } : {}),
23
+ },
24
+ body: input.body,
25
+ }, opts);
26
+ }
27
+ export function getRegistrationStatus(input, opts) {
28
+ return rawRequest(input.statusUrl, { method: "GET" }, opts);
29
+ }
30
+ export function prepareChildLog(input, opts) {
31
+ return rawRequest(`${trimBase(input.baseUrl)}/api/forest/${encodeURIComponent(input.logId)}/prepare`, {
32
+ method: "POST",
33
+ headers: { authorization: `Forestrie-Grant ${input.grantB64}` },
34
+ }, opts);
35
+ }
36
+ export function getPendingDelegation(input, opts) {
37
+ return rawRequest(`${trimBase(input.coordinatorUrl)}/api/logs/${encodeURIComponent(input.logId)}/pending-delegation`, { method: "GET", headers: { accept: "application/json" } }, opts);
38
+ }
39
+ export function postDelegationCertificate(input, opts) {
40
+ return rawRequest(`${trimBase(input.coordinatorUrl)}/api/delegations/certificate`, {
41
+ method: "POST",
42
+ headers: { "content-type": "application/json" },
43
+ body: JSON.stringify(input.submission),
44
+ }, opts);
45
+ }
46
+ //# sourceMappingURL=write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/net/write.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAEjD,MAAM,UAAU,aAAa,CAC3B,KAKC,EACD,IAAmB;IAEnB,OAAO,UAAU,CACf,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,kBAAkB,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,EACpF;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,mBAAmB,KAAK,CAAC,QAAQ,EAAE;SACnD;QACD,IAAI,EAAE,KAAK,CAAC,SAAS;KACtB,EACD,IAAI,CACL,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,gBAAgB,CAAC,OAAe,EAAE,SAAiB;IACjE,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,aAAa,kBAAkB,CAAC,SAAS,CAAC,SAAS,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,KAQC,EACD,IAAmB;IAEnB,OAAO,UAAU,CACf,gBAAgB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,EAChD;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,mBAAmB,KAAK,CAAC,QAAQ,EAAE;YAClD,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACvE;QACD,IAAI,EAAE,KAAK,CAAC,IAAI;KACjB,EACD,IAAI,CACL,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,KAA4B,EAC5B,IAAmB;IAEnB,OAAO,UAAU,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,KAA2D,EAC3D,IAAmB;IAEnB,OAAO,UAAU,CACf,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,eAAe,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAClF;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,aAAa,EAAE,mBAAmB,KAAK,CAAC,QAAQ,EAAE,EAAE;KAChE,EACD,IAAI,CACL,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,KAAgD,EAChD,IAAmB;IAEnB,OAAO,UAAU,CACf,GAAG,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,qBAAqB,EAClG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,EAC1D,IAAI,CACL,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,KAAoE,EACpE,IAAmB;IAEnB,OAAO,UAAU,CACf,GAAG,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,8BAA8B,EAC/D;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC;KACvC,EACD,IAAI,CACL,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ export declare const HELP: string;
2
+ export declare function main(argv: readonly string[]): Promise<number>;
3
+ /**
4
+ * Entry point. `process.exitCode` rather than `process.exit()`: node then
5
+ * drains stdout before exiting, which a hard exit would not.
6
+ */
7
+ export declare function run(): Promise<void>;
8
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/node/cli.ts"],"names":[],"mappings":"AA+CA,eAAO,MAAM,IAAI,QAgDhB,CAAC;AAOF,wBAAsB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAkDnE;AAED;;;GAGG;AACH,wBAAsB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CASzC"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * The bin entry, reached via `bin/mcp-register.mjs`.
3
+ *
4
+ * <no args> → StdioServerTransport; this is what
5
+ * `npx -y @forestrie/mcp-register` does.
6
+ * --help | --version → text on stdout, exit 0.
7
+ *
8
+ * TWO RULES, both silent-corruption bugs if broken:
9
+ *
10
+ * 1. **Nothing may write to stdout in stdio mode except the transport.** A
11
+ * stray `console.log` does not produce a warning — it produces a malformed
12
+ * JSON-RPC frame and an MCP client that mysteriously fails to initialise.
13
+ * All logging goes to stderr. `scripts/check-stdio-clean.mjs` proves it
14
+ * against a real spawned process on every CI run.
15
+ *
16
+ * 2. **`main` returns an exit code; it does not call `process.exit`.** Killing
17
+ * the process mid-write truncates whatever the stdio transport had
18
+ * buffered. The wrapper sets `process.exitCode` and lets node drain.
19
+ *
20
+ * Serves the tools `createServer()` (`server.ts`) registers. It is
21
+ * called with no `deps`, so it reads `process.env` and calls the real
22
+ * `globalThis.fetch` — exactly what `npx -y @forestrie/mcp-register` should do.
23
+ */
24
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
25
+ import { PACKAGE_VERSION, RESOLVE_VERSION, VERIFIER_VERSION, } from "../core/index.js";
26
+ import { createServer } from "./server.js";
27
+ import { PROMPT_NAMES, PROMPT_TITLES, TOOL_NAMES, TOOL_TITLES, } from "./text.js";
28
+ /** One line per tool, generated from the same list the server registers
29
+ * from — never a hand-typed block. */
30
+ const TOOLS_BLOCK = TOOL_NAMES.map((name) => ` ${name.padEnd(26)} ${TOOL_TITLES[name]}`).join("\n");
31
+ const PROMPTS_BLOCK = PROMPT_NAMES.map((name) => ` ${name.padEnd(26)} ${PROMPT_TITLES[name]}`).join("\n");
32
+ export const HELP = `forestrie-mcp-register ${PACKAGE_VERSION}
33
+
34
+ MCP server that signs statements with a log key, registers them on a
35
+ Forestrie transparency log under a grant you hold, delegates sealing for
36
+ your data log, and verifies the receipt by composing @forestrie/mcp-resolve
37
+ and @forestrie/mcp-verify. Signature bytes never cross the MCP boundary.
38
+
39
+ USAGE
40
+ forestrie-mcp-register start the MCP server on stdio (the default)
41
+ forestrie-mcp-register --help
42
+ forestrie-mcp-register --version
43
+
44
+ TOOLS
45
+ ${TOOLS_BLOCK}
46
+
47
+ PROMPTS
48
+ ${PROMPTS_BLOCK}
49
+
50
+ CONFIGURATION (each overridable per call)
51
+ FORESTRIE_BASE_URL canopy SCRAPI base URL
52
+ FORESTRIE_ROOT_LOG_ID the forest's root log id (R in /register/{R}/entries)
53
+ FORESTRIE_GRANT your Forestrie-Grant: base64, or a file path
54
+ FORESTRIE_AUTHORITY_GRANT your authority log's grant, for issue_grant and register_grant
55
+ FORESTRIE_COORDINATOR_URL delegation coordinator, for delegate_sealing
56
+ FORESTRIE_KNOWN_SEALER_KEY registrar voucher key, base64 x‖y, for delegate_sealing
57
+ MCP_REGISTER_HOME key files, grants, registrations, spend ledger
58
+ (default ~/.config/forestrie/mcp-register)
59
+ MCP_REGISTER_KEY_FILE the log key file (default $MCP_REGISTER_HOME/log-key.jwk)
60
+ MCP_REGISTER_AUTHORITY_KEY_FILE
61
+ the authority key file (default $MCP_REGISTER_HOME/authority-key.jwk)
62
+
63
+ PAYMENT (configuration only; never a tool argument)
64
+ MCP_REGISTER_PAYER_KEY_FILE EVM private key (0x + 64 hex, mode 600) that pays canopy's
65
+ x402 402; unset means relay mode
66
+ MCP_REGISTER_PAY_MAX_PER_CALL cap per payment, atomic units (required with a payer)
67
+ MCP_REGISTER_PAY_MAX_PER_DAY cap per UTC day, atomic units (required with a payer)
68
+ MCP_REGISTER_PAY_NETWORK the one network (default eip155:84532, Base Sepolia)
69
+ MCP_REGISTER_PAY_ASSET the one asset (default USDC on Base Sepolia)
70
+ MCP_REGISTER_PAY_TO optional: the only payee
71
+ MCP_REGISTER_PAY_ALLOW_MAINNET 1 to pay on a network that is not a testnet (off)
72
+
73
+ CLIENT CONFIG
74
+ {"mcpServers": {"forestrie-register":
75
+ {"command": "npx", "args": ["-y", "@forestrie/mcp-register"]}}}
76
+
77
+ Verifier: @forestrie/mcp-verify ${VERIFIER_VERSION}
78
+ Resolver: @forestrie/mcp-resolve ${RESOLVE_VERSION}
79
+ Docs: https://github.com/forestrie/mcp-register#readme
80
+ `;
81
+ /** stdout is the transport in stdio mode; everything else is stderr. */
82
+ const out = (line) => {
83
+ process.stdout.write(`${line}\n`);
84
+ };
85
+ export async function main(argv) {
86
+ const args = [...argv];
87
+ if (args.includes("--help") || args.includes("-h") || args[0] === "help") {
88
+ out(HELP);
89
+ return 0;
90
+ }
91
+ if (args.includes("--version") || args.includes("-v")) {
92
+ out(PACKAGE_VERSION);
93
+ return 0;
94
+ }
95
+ const verb = args[0];
96
+ if (verb !== undefined && !verb.startsWith("-")) {
97
+ process.stderr.write(`forestrie-mcp-register: unknown command '${verb}'. Try --help.\n`);
98
+ return 1;
99
+ }
100
+ // Default: speak MCP on stdio. From here on, stdout belongs to the
101
+ // transport and nothing else may touch it.
102
+ const server = createServer();
103
+ const transport = new StdioServerTransport();
104
+ await server.connect(transport);
105
+ // Wait for the client to go away (stdin ends, the transport closes) or
106
+ // for a signal. `server.server.onclose`, not `transport.onclose`: connect()
107
+ // installed the latter and overwriting it would skip the SDK's own
108
+ // cleanup. Listeners are removed afterwards so nothing here keeps the
109
+ // event loop alive.
110
+ let finish = () => { };
111
+ const ended = new Promise((resolve) => {
112
+ finish = resolve;
113
+ });
114
+ const onSignal = () => finish();
115
+ server.server.onclose = finish;
116
+ process.stdin.once("end", finish);
117
+ process.on("SIGINT", onSignal);
118
+ process.on("SIGTERM", onSignal);
119
+ await ended;
120
+ process.off("SIGINT", onSignal);
121
+ process.off("SIGTERM", onSignal);
122
+ process.stdin.off("end", finish);
123
+ // A signal leaves the transport open, and its stdin listener keeps the
124
+ // process alive for as long as the client holds the pipe. Close it so the
125
+ // event loop drains; the wrapper sets the exit code, never process.exit.
126
+ await server.close();
127
+ return 0;
128
+ }
129
+ /**
130
+ * Entry point. `process.exitCode` rather than `process.exit()`: node then
131
+ * drains stdout before exiting, which a hard exit would not.
132
+ */
133
+ export async function run() {
134
+ try {
135
+ process.exitCode = await main(process.argv.slice(2));
136
+ }
137
+ catch (err) {
138
+ process.stderr.write(`forestrie-mcp-register: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`);
139
+ process.exitCode = 1;
140
+ }
141
+ }
142
+ await run();
143
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/node/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { PaymentRequired, PaymentSummary } from "../core/x402.js";
2
+ export type GrantState =
3
+ /** Signed (or supplied), not yet accepted by canopy. */
4
+ "issued"
5
+ /** canopy answered 402; waiting for a payment (relay mode). */
6
+ | "payment_required"
7
+ /** canopy answered 303: enqueued for sequencing under the owner log. */
8
+ | "accepted"
9
+ /** Sealed, and completed with its receipt. */
10
+ | "complete";
11
+ export interface GrantRecord {
12
+ logId: string;
13
+ ownerLogId: string;
14
+ state: GrantState;
15
+ /** True when issue_grant signed it here. */
16
+ issuedHere: boolean;
17
+ /** The key the grant names, hex x‖y. */
18
+ granteeHex: string;
19
+ maxHeight?: number;
20
+ flagsHex: string;
21
+ /** Whether the parent grant carries GF_CHILD_PAYMENT_REQUIRED. */
22
+ parentRequiresPayment?: boolean;
23
+ issuedAt: string;
24
+ baseUrl?: string;
25
+ rootLogId?: string;
26
+ paymentRequired?: PaymentRequired;
27
+ payment?: PaymentSummary & {
28
+ via: "payer" | "relay";
29
+ at: string;
30
+ };
31
+ statusUrl?: string;
32
+ acceptedAt?: string;
33
+ firstPendingAt?: string;
34
+ entryId?: string;
35
+ massifHeight?: number;
36
+ receiptUrl?: string;
37
+ completedAt?: string;
38
+ }
39
+ export declare class GrantStore {
40
+ readonly root: string;
41
+ constructor(root: string);
42
+ dir(logId: string): string;
43
+ path(logId: string, file: string): string;
44
+ has(logId: string): boolean;
45
+ save(record: GrantRecord, files?: Record<string, Uint8Array>): void;
46
+ load(logId: string): GrantRecord;
47
+ read(logId: string, file: string): Uint8Array | undefined;
48
+ }
49
+ //# sourceMappingURL=grant-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant-store.d.ts","sourceRoot":"","sources":["../../src/node/grant-store.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGvE,MAAM,MAAM,UAAU;AACpB,wDAAwD;AACtD,QAAQ;AACV,+DAA+D;GAC7D,kBAAkB;AACpB,wEAAwE;GACtE,UAAU;AACZ,8CAA8C;GAC5C,UAAU,CAAC;AAEf,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;IAClB,4CAA4C;IAC5C,UAAU,EAAE,OAAO,CAAC;IACpB,wCAAwC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,OAAO,CAAC,EAAE,cAAc,GAAG;QAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAqBD,qBAAa,UAAU;IACT,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;IAEjC,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAS1B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAIzC,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAS3B,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAM,GAAG,IAAI;IAYvE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW;IAmChC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;CAW1D"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Where child grants are kept between issue_grant and register_grant:
3
+ * `<home>/grants/<logId>/` holding `record.json`, `grant.cose` (as signed),
4
+ * `parent.cose` (the requester's authority grant, when known),
5
+ * `receipt.cose` and `completed.cose` (the grant with its receipt: the
6
+ * credential).
7
+ *
8
+ * The record is keyed by the child log id, so register_grant resumes from
9
+ * whatever state the last call reached. Nothing here is secret.
10
+ */
11
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ import { StoreError } from "./store.js";
14
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
15
+ const STATES = [
16
+ "issued",
17
+ "payment_required",
18
+ "accepted",
19
+ "complete",
20
+ ];
21
+ function io(what, run) {
22
+ try {
23
+ return run();
24
+ }
25
+ catch (err) {
26
+ throw new StoreError("store_failed", `${what}: ${err instanceof Error ? err.message : String(err)}`);
27
+ }
28
+ }
29
+ export class GrantStore {
30
+ root;
31
+ constructor(root) {
32
+ this.root = root;
33
+ }
34
+ dir(logId) {
35
+ if (!UUID.test(logId))
36
+ throw new StoreError("unknown_registration", `logId must be a lowercase UUID (the child log id issue_grant returned); got ${logId}`);
37
+ return join(this.root, "grants", logId);
38
+ }
39
+ path(logId, file) {
40
+ return join(this.dir(logId), file);
41
+ }
42
+ has(logId) {
43
+ try {
44
+ readFileSync(this.path(logId, "record.json"));
45
+ return true;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
51
+ save(record, files = {}) {
52
+ io(`cannot save grant ${record.logId} under ${this.root}`, () => {
53
+ mkdirSync(this.dir(record.logId), { recursive: true, mode: 0o700 });
54
+ for (const [name, bytes] of Object.entries(files))
55
+ writeFileSync(this.path(record.logId, name), bytes);
56
+ writeFileSync(this.path(record.logId, "record.json"), JSON.stringify(record, null, 2) + "\n");
57
+ });
58
+ }
59
+ load(logId) {
60
+ const path = this.path(logId, "record.json");
61
+ let text;
62
+ try {
63
+ text = readFileSync(path, "utf8");
64
+ }
65
+ catch (err) {
66
+ if (err.code === "ENOENT")
67
+ throw new StoreError("unknown_registration", `no grant for log ${logId} in ${this.root}: issue_grant records each grant it signs there, or pass the grant itself`);
68
+ throw new StoreError("store_failed", `cannot read ${path}: ${err.message}`);
69
+ }
70
+ let record;
71
+ try {
72
+ record = JSON.parse(text);
73
+ }
74
+ catch {
75
+ record = undefined;
76
+ }
77
+ if (record === undefined ||
78
+ record.logId !== logId ||
79
+ typeof record.ownerLogId !== "string" ||
80
+ !STATES.includes(record.state))
81
+ throw new StoreError("corrupt_record", `${path} is not a grant record for ${logId}`);
82
+ return record;
83
+ }
84
+ read(logId, file) {
85
+ try {
86
+ return new Uint8Array(readFileSync(this.path(logId, file)));
87
+ }
88
+ catch (err) {
89
+ if (err.code === "ENOENT")
90
+ return undefined;
91
+ throw new StoreError("store_failed", `cannot read ${file} for grant ${logId}: ${err.message}`);
92
+ }
93
+ }
94
+ }
95
+ //# sourceMappingURL=grant-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant-store.js","sourceRoot":"","sources":["../../src/node/grant-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { z } from "zod";
2
+ import { type Args, type ResolvedDeps, type ToolResult } from "./tools.js";
3
+ export declare const issueGrantInputShape: {
4
+ maxHeight: z.ZodNumber;
5
+ parentGrant: z.ZodOptional<z.ZodString>;
6
+ grantee: z.ZodOptional<z.ZodString>;
7
+ logId: z.ZodOptional<z.ZodString>;
8
+ };
9
+ export declare const registerGrantInputShape: {
10
+ logId: z.ZodOptional<z.ZodString>;
11
+ grant: z.ZodOptional<z.ZodString>;
12
+ parentGrant: z.ZodOptional<z.ZodString>;
13
+ payment: z.ZodOptional<z.ZodString>;
14
+ baseUrl: z.ZodOptional<z.ZodString>;
15
+ rootLogId: z.ZodOptional<z.ZodString>;
16
+ };
17
+ export declare function makeIssueGrantTool(deps: ResolvedDeps): (args: Args<typeof issueGrantInputShape>) => Promise<ToolResult>;
18
+ /**
19
+ * No lock: paying twice for one grant is prevented by the nonce, not by
20
+ * serialising calls. Every authorisation this server signs for a grant
21
+ * carries `grantPaymentNonce(grant content, payer)`, so overlapping calls,
22
+ * a retry after a lost answer and other processes sharing the payer all
23
+ * sign the same nonce, and EIP-3009 settles at most one authorisation per
24
+ * (payer, nonce). The daily cap is approximate under concurrency: calls that
25
+ * pass the check before either records its payment can overshoot it by at
26
+ * most one per-call cap each.
27
+ */
28
+ export declare function makeRegisterGrantTool(deps: ResolvedDeps): (args: Args<typeof registerGrantInputShape>) => Promise<ToolResult>;
29
+ //# sourceMappingURL=grant-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant-tools.d.ts","sourceRoot":"","sources":["../../src/node/grant-tools.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA4CxB,OAAO,EAWL,KAAK,IAAI,EACT,KAAK,YAAY,EACjB,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AAcpB,eAAO,MAAM,oBAAoB;;;;;CAmBhC,CAAC;AAEF,eAAO,MAAM,uBAAuB;;;;;;;CAuBnC,CAAC;AAIF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,IAC3C,MAAM,IAAI,CAAC,OAAO,oBAAoB,CAAC,yBAgFhD;AAuGD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,IAC9C,MAAM,IAAI,CAAC,OAAO,uBAAuB,CAAC,yBAEnD"}