@stelis/say-ur-intent 0.0.0 → 0.0.2

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 (234) hide show
  1. package/README.md +4 -39
  2. package/dist/adapters/adapterLifecycleValidators.js +7 -0
  3. package/dist/adapters/adapterPromptSurfaces.js +71 -0
  4. package/dist/adapters/deepbook/deepbookHumanReviewProducer.js +175 -0
  5. package/dist/adapters/deepbook/deepbookQuotePolicy.js +112 -0
  6. package/dist/adapters/deepbook/deepbookReviewEvidence.js +507 -0
  7. package/dist/adapters/deepbook/deepbookReviewLifecycle.js +85 -0
  8. package/dist/adapters/deepbook/deepbookSwapIntent.js +79 -0
  9. package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +269 -0
  10. package/dist/adapters/flowx/flowxSwapHumanReviewProducer.js +176 -0
  11. package/dist/adapters/flowx/flowxSwapIntent.js +79 -0
  12. package/dist/adapters/flowx/flowxSwapQuotePolicy.js +104 -0
  13. package/dist/adapters/flowx/flowxSwapReviewEvidence.js +468 -0
  14. package/dist/adapters/flowx/flowxSwapReviewLifecycle.js +85 -0
  15. package/dist/adapters/flowx/flowxSwapTransactionMaterialProducer.js +362 -0
  16. package/dist/adapters/intentPlanFactories.js +59 -0
  17. package/dist/adapters/reviewAdapters.js +81 -0
  18. package/dist/core/action/adapterLifecycleValidation.js +12 -0
  19. package/dist/core/action/forbiddenFields.js +43 -0
  20. package/dist/core/action/humanReadableReviewEvidence.js +203 -0
  21. package/dist/core/action/humanReadableReviewProjectionVerifier.js +29 -0
  22. package/dist/core/action/ptbVisualizationProducer.js +66 -0
  23. package/dist/core/action/reviewCheckResults.js +6 -0
  24. package/dist/core/action/reviewStateValidation.js +11 -0
  25. package/dist/core/action/reviewTimeSimulationEvidence.js +471 -0
  26. package/dist/core/action/schemas.js +529 -0
  27. package/dist/core/action/signableAdapterContract.js +993 -0
  28. package/dist/core/action/swapHumanReadableReviewProjection.js +124 -0
  29. package/dist/core/action/swapQuotePolicyEvidence.js +278 -0
  30. package/dist/core/action/transactionObjectOwnershipEvidence.js +247 -0
  31. package/dist/core/action/transactionObjectOwnershipProducer.js +329 -0
  32. package/dist/core/action/types.js +35 -0
  33. package/dist/core/action/walletReviewContractAssembler.js +282 -0
  34. package/dist/core/activity/activityStore.js +15 -0
  35. package/dist/core/activity/localDataService.js +258 -0
  36. package/dist/core/activity/localDataTypes.js +11 -0
  37. package/dist/core/activity/localDataValidation.js +396 -0
  38. package/dist/core/activity/schemaVersion.js +1 -0
  39. package/dist/core/activity/sqliteActivityStore.js +820 -0
  40. package/dist/core/activity/sqliteActivityStoreRows.js +430 -0
  41. package/dist/core/activity/sqliteActivityStoreSchema.js +258 -0
  42. package/dist/core/activity/sqliteActivityStoreTypes.js +5 -0
  43. package/dist/core/activity/suiFunctionTarget.js +43 -0
  44. package/dist/core/activity/transactionActivityAccountEffects.js +189 -0
  45. package/dist/core/activity/transactionActivityAnalysis.js +295 -0
  46. package/dist/core/activity/transactionActivityClassifier.js +306 -0
  47. package/dist/core/activity/transactionActivityDetails.js +229 -0
  48. package/dist/core/activity/transactionActivityProtocolRules.js +218 -0
  49. package/dist/core/activity/transactionActivityScanPolicy.js +170 -0
  50. package/dist/core/activity/transactionActivityService.js +379 -0
  51. package/dist/core/activity/transactionActivityTypes.js +18 -0
  52. package/dist/core/eventlog/sink.js +35 -0
  53. package/dist/core/evidence/settlementFamilies.js +87 -0
  54. package/dist/core/evidence/userAnswerUse.js +1 -0
  55. package/dist/core/numeric/rawU64.js +63 -0
  56. package/dist/core/preferences/preferencesStore.js +26 -0
  57. package/dist/core/preferences/sqlitePreferencesRepository.js +136 -0
  58. package/dist/core/proposal/externalProposalReview.js +347 -0
  59. package/dist/core/proposal/schemas.js +208 -0
  60. package/dist/core/proposal/types.js +35 -0
  61. package/dist/core/read/amounts.js +14 -0
  62. package/dist/core/read/coinMetadata.js +60 -0
  63. package/dist/core/read/deepbookRawQuoteClient.js +86 -0
  64. package/dist/core/read/deepbookReadHelpers.js +265 -0
  65. package/dist/core/read/deepbookRegistry.js +133 -0
  66. package/dist/core/read/flowxQuoteClient.js +117 -0
  67. package/dist/core/read/flowxReadHelpers.js +145 -0
  68. package/dist/core/read/flowxRegistry.js +174 -0
  69. package/dist/core/read/intentEvidenceResponseFormatting.js +228 -0
  70. package/dist/core/read/readResponseGuidance.js +451 -0
  71. package/dist/core/read/readService.js +1164 -0
  72. package/dist/core/read/readServiceTypes.js +59 -0
  73. package/dist/core/read/settlementParityFormatting.js +82 -0
  74. package/dist/core/read/walletReadHelpers.js +99 -0
  75. package/dist/core/review/reviewChecks.js +54 -0
  76. package/dist/core/review/reviewComputation.js +38 -0
  77. package/dist/core/review/reviewComputationResult.js +87 -0
  78. package/dist/core/session/localSession.js +31 -0
  79. package/dist/core/session/privateReviewArtifacts.js +73 -0
  80. package/dist/core/session/sessionErrors.js +9 -0
  81. package/dist/core/session/sessionStore.js +821 -0
  82. package/dist/core/session/settingsSession.js +1 -0
  83. package/dist/core/session/settingsSessions.js +43 -0
  84. package/dist/core/session/status.js +86 -0
  85. package/dist/core/session/transactionMaterialStore.js +205 -0
  86. package/dist/core/session/wait.js +102 -0
  87. package/dist/core/session/walletIdentity.js +103 -0
  88. package/dist/core/session/walletIdentitySessions.js +189 -0
  89. package/dist/core/suiAddress.js +18 -0
  90. package/dist/core/suiEndpoint.js +72 -0
  91. package/dist/mcp/activeAccountResponse.js +24 -0
  92. package/dist/mcp/prompts.js +146 -0
  93. package/dist/mcp/registerTool.js +19 -0
  94. package/dist/mcp/resources.js +72 -0
  95. package/dist/mcp/responseGuidance.js +381 -0
  96. package/dist/mcp/result.js +17 -0
  97. package/dist/mcp/schemas.js +8 -0
  98. package/dist/mcp/server.js +30 -0
  99. package/dist/mcp/serverInfo.js +123 -0
  100. package/dist/mcp/toolErrors.js +105 -0
  101. package/dist/mcp/toolNames.js +50 -0
  102. package/dist/mcp/tools/account/index.js +44 -0
  103. package/dist/mcp/tools/action/prepareSuiActionReview.js +120 -0
  104. package/dist/mcp/tools/read/commonSchemas.js +43 -0
  105. package/dist/mcp/tools/read/deepbookReadTools.js +453 -0
  106. package/dist/mcp/tools/read/flowxReadTools.js +135 -0
  107. package/dist/mcp/tools/read/index.js +16 -0
  108. package/dist/mcp/tools/read/readToolHelpers.js +68 -0
  109. package/dist/mcp/tools/read/reviewActivityTools.js +176 -0
  110. package/dist/mcp/tools/read/serverStatusTools.js +103 -0
  111. package/dist/mcp/tools/read/transactionActivityOutput.js +300 -0
  112. package/dist/mcp/tools/read/transactionActivityTools.js +544 -0
  113. package/dist/mcp/tools/read/walletReadTools.js +733 -0
  114. package/dist/mcp/tools/session/executionResultTools.js +92 -0
  115. package/dist/mcp/tools/session/index.js +8 -0
  116. package/dist/mcp/tools/session/shared.js +79 -0
  117. package/dist/mcp/tools/session/statusTools.js +134 -0
  118. package/dist/mcp/tools/session/walletIdentityTools.js +119 -0
  119. package/dist/mcp/tools/settings/index.js +64 -0
  120. package/dist/review-app/analysis.css +1 -0
  121. package/dist/review-app/analysis.js +1 -0
  122. package/dist/review-app/arc-BjIacwQm.js +1 -0
  123. package/dist/review-app/architecture-U656AL7Q-aSB9x1OK.js +1 -0
  124. package/dist/review-app/architectureDiagram-VXUJARFQ-C5W6re2I.js +36 -0
  125. package/dist/review-app/array-BmXUUrU6.js +1 -0
  126. package/dist/review-app/blockDiagram-VD42YOAC-20MLNcUm.js +122 -0
  127. package/dist/review-app/c4Diagram-YG6GDRKO-BZXRrcck.js +10 -0
  128. package/dist/review-app/channel-lk2p_CUu.js +1 -0
  129. package/dist/review-app/chunk-4BX2VUAB-BPITOdjX.js +1 -0
  130. package/dist/review-app/chunk-55IACEB6-Dz-pyw5k.js +1 -0
  131. package/dist/review-app/chunk-76Q3JFCE-cK_X1P_l.js +1 -0
  132. package/dist/review-app/chunk-ABZYJK2D-Dt4W53JI.js +81 -0
  133. package/dist/review-app/chunk-ATLVNIR6-fZHLXURb.js +1 -0
  134. package/dist/review-app/chunk-B4BG7PRW-BbgcjusC.js +165 -0
  135. package/dist/review-app/chunk-BJD4TVEz.js +1 -0
  136. package/dist/review-app/chunk-CVBHYZKI-CViawAKX.js +1 -0
  137. package/dist/review-app/chunk-DI55MBZ5-C5aoul-d.js +220 -0
  138. package/dist/review-app/chunk-FMBD7UC4-Chxmw62A.js +15 -0
  139. package/dist/review-app/chunk-FPAJGGOC-DDHjQ09H.js +80 -0
  140. package/dist/review-app/chunk-FWNWRKHM-CVVQUptk.js +1 -0
  141. package/dist/review-app/chunk-HN2XXSSU-yzNpjaSZ.js +1 -0
  142. package/dist/review-app/chunk-JA3XYJ7Z-C5ZJdU01.js +70 -0
  143. package/dist/review-app/chunk-JZLCHNYA-BBST4Cnk.js +54 -0
  144. package/dist/review-app/chunk-LBM3YZW2-CdwAPuHr.js +1 -0
  145. package/dist/review-app/chunk-LHMN2FUI-BtB5uDcp.js +1 -0
  146. package/dist/review-app/chunk-O7ZBX7Z2-pxdK4Sa3.js +1 -0
  147. package/dist/review-app/chunk-QN33PNHL-CbVv3uGK.js +1 -0
  148. package/dist/review-app/chunk-QXUST7PY-DKM2-t2c.js +7 -0
  149. package/dist/review-app/chunk-QZHKN3VN-C5ni2pN_.js +1 -0
  150. package/dist/review-app/chunk-S3R3BYOJ-BWvOhDs0.js +2 -0
  151. package/dist/review-app/chunk-S6J4BHB3-D9Fk0YeD.js +1 -0
  152. package/dist/review-app/chunk-T53DSG4Q-C1qEyzyV.js +1 -0
  153. package/dist/review-app/chunk-TZMSLE5B-B--7eU69.js +1 -0
  154. package/dist/review-app/classDiagram-2ON5EDUG-DlL1m2bp.js +1 -0
  155. package/dist/review-app/classDiagram-v2-WZHVMYZB-FXRskT1j.js +1 -0
  156. package/dist/review-app/clone-BZZb7gpZ.js +1 -0
  157. package/dist/review-app/cose-bilkent-S5V4N54A-CRIb8XEO.js +1 -0
  158. package/dist/review-app/cytoscape.esm-C7jYqDP5.js +321 -0
  159. package/dist/review-app/dagre-6UL2VRFP-FNCAXbdE.js +4 -0
  160. package/dist/review-app/dagre-Be46QtUd.js +1 -0
  161. package/dist/review-app/defaultLocale-BaWNtAUL.js +1 -0
  162. package/dist/review-app/diagram-PSM6KHXK-ylLWjiNM.js +24 -0
  163. package/dist/review-app/diagram-QEK2KX5R-BCDcESxs.js +43 -0
  164. package/dist/review-app/diagram-S2PKOQOG-Vdrc-vrO.js +24 -0
  165. package/dist/review-app/dist-WPc74x_f.js +1 -0
  166. package/dist/review-app/erDiagram-Q2GNP2WA-E5ZsUbDF.js +60 -0
  167. package/dist/review-app/flatten-DHf9IeNI.js +1 -0
  168. package/dist/review-app/flowDiagram-NV44I4VS-DBSQuj6x.js +162 -0
  169. package/dist/review-app/ganttDiagram-LVOFAZNH-CKUOsqwl.js +267 -0
  170. package/dist/review-app/gitGraph-F6HP7TQM-DsAD6qK1.js +1 -0
  171. package/dist/review-app/gitGraphDiagram-NY62KEGX-BCeIMWdl.js +65 -0
  172. package/dist/review-app/graphlib-CiX5CXxR.js +1 -0
  173. package/dist/review-app/http-DMvwuuFk.js +1 -0
  174. package/dist/review-app/identity-DY8PXc6t.js +1 -0
  175. package/dist/review-app/info-NVLQJR56-Dlx1nZic.js +1 -0
  176. package/dist/review-app/infoDiagram-F6ZHWCRC-CAuANIrz.js +2 -0
  177. package/dist/review-app/init-BvqephKz.js +1 -0
  178. package/dist/review-app/journeyDiagram-XKPGCS4Q-C-Z9phnx.js +139 -0
  179. package/dist/review-app/kanban-definition-3W4ZIXB7-DufgZABq.js +89 -0
  180. package/dist/review-app/katex-B-Z-NXXN.js +257 -0
  181. package/dist/review-app/line-DiIv3Jgw.js +1 -0
  182. package/dist/review-app/linear-Cv-UPvo1.js +1 -0
  183. package/dist/review-app/math-kmyYrkHL.js +1 -0
  184. package/dist/review-app/mermaid-parser.core-DkwUYTPl.js +4 -0
  185. package/dist/review-app/mindmap-definition-VGOIOE7T-TM_CqdmV.js +68 -0
  186. package/dist/review-app/ordinal-BliTlkoG.js +1 -0
  187. package/dist/review-app/packet-BFZMPI3H-DqbnU92v.js +1 -0
  188. package/dist/review-app/path-AEo9W6mQ.js +1 -0
  189. package/dist/review-app/pie-7BOR55EZ-LJzaLkgr.js +1 -0
  190. package/dist/review-app/pieDiagram-ADFJNKIX-BAs8OfRS.js +30 -0
  191. package/dist/review-app/quadrantDiagram-AYHSOK5B-CyUDZP5S.js +7 -0
  192. package/dist/review-app/radar-NHE76QYJ-DBpHc8_Y.js +1 -0
  193. package/dist/review-app/reduce-B-HuPpdd.js +1 -0
  194. package/dist/review-app/requirementDiagram-UZGBJVZJ-BEHix78P.js +64 -0
  195. package/dist/review-app/review.css +1 -0
  196. package/dist/review-app/review.js +43 -0
  197. package/dist/review-app/sankeyDiagram-TZEHDZUN-B2bKbmsm.js +10 -0
  198. package/dist/review-app/sequenceDiagram-WL72ISMW-DVLOORFJ.js +145 -0
  199. package/dist/review-app/settings.css +1 -0
  200. package/dist/review-app/settings.js +1 -0
  201. package/dist/review-app/src-Buml7cM5.js +1 -0
  202. package/dist/review-app/stateDiagram-FKZM4ZOC-sFGGp2kV.js +1 -0
  203. package/dist/review-app/stateDiagram-v2-4FDKWEC3-BHfCF4dX.js +1 -0
  204. package/dist/review-app/timeline-definition-IT6M3QCI-BESnBijC.js +61 -0
  205. package/dist/review-app/treemap-KMMF4GRG-wnVLBDeQ.js +1 -0
  206. package/dist/review-app/walletStatus-CcojOdGy.js +7 -0
  207. package/dist/review-app/xychartDiagram-PRI3JC2R-BGWVfCx4.js +7 -0
  208. package/dist/review-server/assets.js +48 -0
  209. package/dist/review-server/html.js +66 -0
  210. package/dist/review-server/http.js +47 -0
  211. package/dist/review-server/middleware/hostOrigin.js +48 -0
  212. package/dist/review-server/middleware/reviewToken.js +7 -0
  213. package/dist/review-server/reviewServerPolicy.js +10 -0
  214. package/dist/review-server/server.js +568 -0
  215. package/dist/review-server/settingsApi.js +182 -0
  216. package/dist/review-server/walletIdentityResponse.js +13 -0
  217. package/dist/runtime/config.js +103 -0
  218. package/dist/runtime/localSettingsService.js +198 -0
  219. package/dist/runtime/logger.js +50 -0
  220. package/dist/runtime/reviewServerAcquire.js +128 -0
  221. package/dist/runtime/smokeMainnetRead.js +529 -0
  222. package/dist/runtime/smokeMainnetReadAssertions.js +308 -0
  223. package/dist/runtime/start.js +295 -0
  224. package/dist/runtime/suiEndpoint.js +97 -0
  225. package/dist/runtime/suiTransactionGraphqlMapping.js +200 -0
  226. package/dist/runtime/suiTransactionGraphqlQueries.js +231 -0
  227. package/dist/runtime/suiTransactionGraphqlSource.js +148 -0
  228. package/docs/AGENT_BEHAVIOR.md +1 -1
  229. package/docs/AGENT_DEVELOPMENT_POLICY.md +20 -0
  230. package/docs/FRONTEND_POLICY.md +4 -3
  231. package/docs/MCP_SETUP.md +59 -7
  232. package/docs/MCP_TOOLS.md +1 -1
  233. package/docs/SDK_API.md +5 -1
  234. package/package.json +3 -2
@@ -0,0 +1,568 @@
1
+ import { createServer } from "node:http";
2
+ import { FAILURE_REASONS } from "../core/action/types.js";
3
+ import { computeReviewStateWithPrivateArtifacts } from "../core/review/reviewComputation.js";
4
+ import { SessionStoreError } from "../core/session/sessionStore.js";
5
+ import { getExecutionPollingStatus } from "../core/session/status.js";
6
+ import { walletIdentityResultInputSchema, walletIdentityPollingHint } from "../core/session/walletIdentity.js";
7
+ import { parseSuiAddress } from "../core/suiAddress.js";
8
+ import { validateHostOrigin } from "./middleware/hostOrigin.js";
9
+ import { readReviewToken } from "./middleware/reviewToken.js";
10
+ import { defaultReviewAssetsDir, serveReviewAsset } from "./assets.js";
11
+ import { analysisHtml, reviewHtml, settingsHtml } from "./html.js";
12
+ import { HttpError, readJsonBody, sendHtml, sendJson } from "./http.js";
13
+ import { ALLOWED_HOSTNAMES, SUI_BROWSER_EXECUTION_ORIGIN } from "./reviewServerPolicy.js";
14
+ import { routeSettingsApi } from "./settingsApi.js";
15
+ import { walletIdentitySessionResponse } from "./walletIdentityResponse.js";
16
+ const REVIEW_WALLET_IDENTITY_STATUSES = new Set([
17
+ "awaiting_wallet",
18
+ "wallet_connected",
19
+ "ready_for_wallet_review",
20
+ "refresh_required",
21
+ "blocked"
22
+ ]);
23
+ export function createReviewHttpServer(options) {
24
+ const server = createServer(async (request, response) => {
25
+ try {
26
+ await routeRequest(request, response, options);
27
+ }
28
+ catch (error) {
29
+ if (error instanceof HttpError) {
30
+ sendJson(response, error.status, { error: error.code });
31
+ return;
32
+ }
33
+ options.logger.error("review server request failed", {
34
+ error: error instanceof Error ? error.message : String(error)
35
+ });
36
+ sendJson(response, 500, { error: "internal_error" });
37
+ }
38
+ });
39
+ return {
40
+ start(port) {
41
+ return new Promise((resolve, reject) => {
42
+ server.once("error", reject);
43
+ server.listen(port, options.host, () => {
44
+ const address = server.address();
45
+ resolve({
46
+ host: options.host,
47
+ port: address.port,
48
+ close: () => new Promise((closeResolve, closeReject) => {
49
+ server.close((error) => (error ? closeReject(error) : closeResolve()));
50
+ })
51
+ });
52
+ });
53
+ });
54
+ }
55
+ };
56
+ }
57
+ async function requireWalletIdentityToken(store, sessionId, request, response) {
58
+ const token = readReviewToken(request.headers);
59
+ if (!token || !(await store.validateWalletIdentityToken(sessionId, token))) {
60
+ sendJson(response, 401, { error: "invalid_wallet_token" });
61
+ return undefined;
62
+ }
63
+ return token;
64
+ }
65
+ async function requireReviewSessionToken(store, sessionId, request, response) {
66
+ const token = readReviewToken(request.headers);
67
+ if (!token || !(await store.validateReviewToken(sessionId, token))) {
68
+ sendJson(response, 401, { error: "invalid_review_token" });
69
+ return undefined;
70
+ }
71
+ return token;
72
+ }
73
+ async function routeRequest(request, response, options) {
74
+ // Structural guard: every request to this server passes the Host/Origin
75
+ // policy once, so a new route cannot accidentally skip it.
76
+ const hostOrigin = validateHostOrigin(request, { allowedHostnames: ALLOWED_HOSTNAMES });
77
+ if (!hostOrigin.ok) {
78
+ sendJson(response, hostOrigin.status, { error: hostOrigin.reason });
79
+ return;
80
+ }
81
+ const url = new URL(request.url ?? "/", "http://localhost");
82
+ // Loopback identity probe used by a newer instance to confirm this port is
83
+ // held by our own review server before taking it over. It exposes only the
84
+ // service name, role, version, and pid — no addresses, tokens, or session
85
+ // data — and is reachable only on the loopback host this server binds.
86
+ if (request.method === "GET" && url.pathname === "/__identity") {
87
+ sendJson(response, 200, {
88
+ service: options.serverInfo?.name ?? "say-ur-intent",
89
+ role: "review-server",
90
+ version: options.serverInfo?.version,
91
+ pid: process.pid
92
+ });
93
+ return;
94
+ }
95
+ const reviewMatch = /^\/review\/([^/]+)$/.exec(url.pathname);
96
+ const apiReviewMatch = /^\/api\/review\/([^/]+)$/.exec(url.pathname);
97
+ const apiReviewWalletIdentityMatch = /^\/api\/review\/([^/]+)\/wallet-identity$/.exec(url.pathname);
98
+ const apiReviewOpenedMatch = /^\/api\/review\/([^/]+)\/opened$/.exec(url.pathname);
99
+ const apiReviewStateMatch = /^\/api\/review\/([^/]+)\/state$/.exec(url.pathname);
100
+ const apiReviewResultMatch = /^\/api\/review\/([^/]+)\/result$/.exec(url.pathname);
101
+ const apiResultMatch = /^\/api\/result\/([^/]+)$/.exec(url.pathname);
102
+ const walletMatch = /^\/analysis\/([^/]+)$/.exec(url.pathname);
103
+ const apiReviewHandoffMatch = /^\/api\/review\/([^/]+)\/handoff$/.exec(url.pathname);
104
+ const apiReviewHandoffCancelMatch = /^\/api\/review\/([^/]+)\/handoff\/cancel$/.exec(url.pathname);
105
+ const analysisAssetsMatch = /^\/api\/analysis\/([^/]+)\/assets$/.exec(url.pathname);
106
+ const analysisActivityMatch = /^\/api\/analysis\/([^/]+)\/review-activity$/.exec(url.pathname);
107
+ const apiWalletOpenedMatch = /^\/api\/wallet\/([^/]+)\/opened$/.exec(url.pathname);
108
+ const apiWalletConnectingMatch = /^\/api\/wallet\/([^/]+)\/connecting$/.exec(url.pathname);
109
+ const apiWalletResultMatch = /^\/api\/wallet\/([^/]+)\/result$/.exec(url.pathname);
110
+ const settingsMatch = /^\/settings\/([^/]+)$/.exec(url.pathname);
111
+ const apiSettingsMatch = /^\/api\/settings\/([^/]+)$/.exec(url.pathname);
112
+ const apiSettingsWalletIdentityMatch = /^\/api\/settings\/([^/]+)\/wallet-identity$/.exec(url.pathname);
113
+ const apiSettingsClearActiveAccountMatch = /^\/api\/settings\/([^/]+)\/clear-active-account$/.exec(url.pathname);
114
+ const apiSettingsSuiGrpcUrlMatch = /^\/api\/settings\/([^/]+)\/sui-grpc-url$/.exec(url.pathname);
115
+ const apiSettingsSuiGrpcUrlDefaultMatch = /^\/api\/settings\/([^/]+)\/sui-grpc-url\/restore-default$/.exec(url.pathname);
116
+ const apiSettingsSuiGraphqlUrlMatch = /^\/api\/settings\/([^/]+)\/sui-graphql-url$/.exec(url.pathname);
117
+ const apiSettingsSuiGraphqlUrlDefaultMatch = /^\/api\/settings\/([^/]+)\/sui-graphql-url\/restore-default$/.exec(url.pathname);
118
+ const apiSettingsLocalDataExportMatch = /^\/api\/settings\/([^/]+)\/local-data\/export$/.exec(url.pathname);
119
+ const apiSettingsLocalDataPreviewMatch = /^\/api\/settings\/([^/]+)\/local-data\/import\/preview$/.exec(url.pathname);
120
+ const apiSettingsLocalDataImportMatch = /^\/api\/settings\/([^/]+)\/local-data\/import$/.exec(url.pathname);
121
+ const apiSettingsLocalDataResetMatch = /^\/api\/settings\/([^/]+)\/local-data\/reset$/.exec(url.pathname);
122
+ const reviewAssetMatch = /^\/review-assets\/(.+)$/.exec(url.pathname);
123
+ if (request.method === "GET" && reviewMatch?.[1]) {
124
+ sendHtml(response, reviewHtml(reviewMatch[1]), {
125
+ "content-security-policy": [
126
+ "default-src 'none'",
127
+ "base-uri 'none'",
128
+ // 'self' for review-server APIs; the Sui fullnode origin for the
129
+ // browser-side signed-transaction submission (see reviewServerPolicy).
130
+ `connect-src 'self' ${SUI_BROWSER_EXECUTION_ORIGIN}`,
131
+ "script-src 'self'",
132
+ // Inline styles are allowed for mermaid's SVG styling; scripts stay 'self'-only.
133
+ "style-src 'self' 'unsafe-inline'",
134
+ "img-src 'self' data:",
135
+ "form-action 'none'"
136
+ ].join("; ")
137
+ });
138
+ return;
139
+ }
140
+ if (request.method === "GET" && walletMatch?.[1]) {
141
+ sendHtml(response, analysisHtml(walletMatch[1]), {
142
+ "content-security-policy": [
143
+ "default-src 'none'",
144
+ "base-uri 'none'",
145
+ // 'self' for review-server APIs; the Sui fullnode origin so the dapp-kit
146
+ // chain client can read mainnet state during wallet connect.
147
+ `connect-src 'self' ${SUI_BROWSER_EXECUTION_ORIGIN}`,
148
+ "script-src 'self'",
149
+ // Inline styles are allowed for mermaid's SVG styling; scripts stay 'self'-only.
150
+ "style-src 'self' 'unsafe-inline'",
151
+ "img-src 'self' data:",
152
+ "form-action 'none'"
153
+ ].join("; ")
154
+ });
155
+ return;
156
+ }
157
+ if (request.method === "POST" && apiReviewHandoffCancelMatch?.[1]) {
158
+ const sessionId = apiReviewHandoffCancelMatch[1];
159
+ const token = await requireReviewSessionToken(options.store, sessionId, request, response);
160
+ if (!token) {
161
+ return;
162
+ }
163
+ try {
164
+ await options.store.cancelWalletHandoff(sessionId);
165
+ sendJson(response, 200, { cancelled: true });
166
+ }
167
+ catch (error) {
168
+ if (error instanceof SessionStoreError) {
169
+ sendJson(response, statusForStoreError(error), { error: error.code });
170
+ return;
171
+ }
172
+ throw error;
173
+ }
174
+ return;
175
+ }
176
+ if (request.method === "POST" && apiReviewHandoffMatch?.[1]) {
177
+ const sessionId = apiReviewHandoffMatch[1];
178
+ const token = await requireReviewSessionToken(options.store, sessionId, request, response);
179
+ if (!token) {
180
+ return;
181
+ }
182
+ const body = await readJsonBody(request);
183
+ const planId = typeof body.planId === "string" ? body.planId : "";
184
+ const account = typeof body.account === "string" ? body.account : "";
185
+ if (!planId || !account) {
186
+ sendJson(response, 400, { error: "input_invalid" });
187
+ return;
188
+ }
189
+ try {
190
+ const handoff = await options.store.prepareWalletHandoff(sessionId, planId, account);
191
+ sendJson(response, 200, handoff);
192
+ }
193
+ catch (error) {
194
+ if (error instanceof SessionStoreError) {
195
+ sendJson(response, statusForStoreError(error), { error: error.code });
196
+ return;
197
+ }
198
+ throw error;
199
+ }
200
+ return;
201
+ }
202
+ if (request.method === "GET" && (analysisAssetsMatch?.[1] || analysisActivityMatch?.[1])) {
203
+ const sessionId = (analysisAssetsMatch?.[1] ?? analysisActivityMatch?.[1]);
204
+ const token = await requireWalletIdentityToken(options.store, sessionId, request, response);
205
+ if (!token) {
206
+ return;
207
+ }
208
+ const walletSession = await options.store.getWalletIdentitySession(sessionId);
209
+ const connectedAccount = walletSession && walletSession.status === "connected" ? walletSession.account : undefined;
210
+ const active = options.activityStore ? await options.activityStore.getActiveAccount() : undefined;
211
+ const account = connectedAccount ?? active?.address;
212
+ if (!account) {
213
+ sendJson(response, 409, { error: "no_active_account" });
214
+ return;
215
+ }
216
+ if (analysisAssetsMatch?.[1]) {
217
+ if (!options.readService) {
218
+ sendJson(response, 503, { error: "analysis_data_unavailable" });
219
+ return;
220
+ }
221
+ try {
222
+ const summary = await options.readService.summarizeWalletAssets({ account });
223
+ sendJson(response, 200, summary);
224
+ }
225
+ catch {
226
+ sendJson(response, 502, { error: "wallet_read_failed" });
227
+ }
228
+ return;
229
+ }
230
+ if (!options.activityStore) {
231
+ sendJson(response, 503, { error: "analysis_data_unavailable" });
232
+ return;
233
+ }
234
+ try {
235
+ const funnel = await options.activityStore.summarizeReviewFunnel({ account });
236
+ sendJson(response, 200, funnel);
237
+ }
238
+ catch {
239
+ sendJson(response, 502, { error: "review_activity_read_failed" });
240
+ }
241
+ return;
242
+ }
243
+ if (request.method === "GET" && settingsMatch?.[1]) {
244
+ if (url.searchParams.has("token")) {
245
+ sendJson(response, 400, { error: "token_query_not_supported" });
246
+ return;
247
+ }
248
+ sendHtml(response, settingsHtml(settingsMatch[1]), {
249
+ "content-security-policy": [
250
+ "default-src 'none'",
251
+ "base-uri 'none'",
252
+ "connect-src 'self'",
253
+ "script-src 'self'",
254
+ // Inline styles are allowed for mermaid's SVG styling; scripts stay 'self'-only.
255
+ "style-src 'self' 'unsafe-inline'",
256
+ "img-src 'self' data:",
257
+ "form-action 'none'"
258
+ ].join("; ")
259
+ });
260
+ return;
261
+ }
262
+ if (request.method === "GET" && reviewAssetMatch?.[1]) {
263
+ await serveReviewAsset(response, options.reviewAssetsDir ?? defaultReviewAssetsDir(), reviewAssetMatch[1]);
264
+ return;
265
+ }
266
+ if (apiSettingsMatch?.[1] ||
267
+ apiSettingsWalletIdentityMatch?.[1] ||
268
+ apiSettingsClearActiveAccountMatch?.[1] ||
269
+ apiSettingsSuiGrpcUrlMatch?.[1] ||
270
+ apiSettingsSuiGrpcUrlDefaultMatch?.[1] ||
271
+ apiSettingsSuiGraphqlUrlMatch?.[1] ||
272
+ apiSettingsSuiGraphqlUrlDefaultMatch?.[1] ||
273
+ apiSettingsLocalDataExportMatch?.[1] ||
274
+ apiSettingsLocalDataPreviewMatch?.[1] ||
275
+ apiSettingsLocalDataImportMatch?.[1] ||
276
+ apiSettingsLocalDataResetMatch?.[1]) {
277
+ await routeSettingsApi(request, response, options, url, {
278
+ status: apiSettingsMatch?.[1],
279
+ walletIdentity: apiSettingsWalletIdentityMatch?.[1],
280
+ clearActiveAccount: apiSettingsClearActiveAccountMatch?.[1],
281
+ setSuiGrpcUrl: apiSettingsSuiGrpcUrlMatch?.[1],
282
+ restoreDefaultSuiGrpcUrl: apiSettingsSuiGrpcUrlDefaultMatch?.[1],
283
+ setSuiGraphqlUrl: apiSettingsSuiGraphqlUrlMatch?.[1],
284
+ restoreDefaultSuiGraphqlUrl: apiSettingsSuiGraphqlUrlDefaultMatch?.[1],
285
+ exportLocalData: apiSettingsLocalDataExportMatch?.[1],
286
+ previewImport: apiSettingsLocalDataPreviewMatch?.[1],
287
+ importLocalData: apiSettingsLocalDataImportMatch?.[1],
288
+ resetLocalData: apiSettingsLocalDataResetMatch?.[1]
289
+ });
290
+ return;
291
+ }
292
+ if (request.method === "GET" && apiReviewMatch?.[1]) {
293
+ const sessionId = apiReviewMatch[1];
294
+ const token = await requireReviewSessionToken(options.store, sessionId, request, response);
295
+ if (!token) {
296
+ return;
297
+ }
298
+ const session = await options.store.getReviewSession(sessionId);
299
+ if (!session) {
300
+ sendJson(response, 404, { error: "session_not_found" });
301
+ return;
302
+ }
303
+ const activeAccount = options.activityStore ? await options.activityStore.getActiveAccount() : undefined;
304
+ sendJson(response, 200, {
305
+ reviewSessionId: session.id,
306
+ internalStatus: session.status,
307
+ pollingStatus: getExecutionPollingStatus(session),
308
+ lastActivityAt: session.lastActivityAt,
309
+ ...(session.executionResult ? { executionResult: session.executionResult } : {}),
310
+ signingInProgress: session.pendingHandoffDigest !== undefined,
311
+ activeAccount: activeAccount
312
+ ? {
313
+ account: activeAccount.address,
314
+ source: activeAccount.source,
315
+ setAt: activeAccount.setAt,
316
+ ...(activeAccount.walletName ? { walletName: activeAccount.walletName } : {}),
317
+ ...(activeAccount.walletId ? { walletId: activeAccount.walletId } : {})
318
+ }
319
+ : undefined,
320
+ reviewState: session.reviewState,
321
+ plans: session.plans
322
+ });
323
+ return;
324
+ }
325
+ if (request.method === "POST" && apiReviewWalletIdentityMatch?.[1]) {
326
+ const sessionId = apiReviewWalletIdentityMatch[1];
327
+ const token = await requireReviewSessionToken(options.store, sessionId, request, response);
328
+ if (!token) {
329
+ return;
330
+ }
331
+ await readJsonBody(request);
332
+ const session = await options.store.getReviewSession(sessionId);
333
+ if (!session) {
334
+ sendJson(response, 404, { error: "session_not_found" });
335
+ return;
336
+ }
337
+ if (session.status === "expired") {
338
+ sendJson(response, 410, { error: "session_expired" });
339
+ return;
340
+ }
341
+ if (!REVIEW_WALLET_IDENTITY_STATUSES.has(session.status)) {
342
+ sendJson(response, 409, { error: "invalid_session_transition" });
343
+ return;
344
+ }
345
+ const wallet = await options.store.createWalletIdentitySession();
346
+ sendJson(response, 200, walletIdentitySessionResponse(wallet, requestBaseUrl(request)));
347
+ return;
348
+ }
349
+ if (request.method === "POST" &&
350
+ (apiWalletOpenedMatch?.[1] || apiWalletConnectingMatch?.[1] || apiWalletResultMatch?.[1])) {
351
+ const sessionId = apiWalletOpenedMatch?.[1] ?? apiWalletConnectingMatch?.[1] ?? apiWalletResultMatch?.[1];
352
+ if (!sessionId) {
353
+ throw new HttpError(404, "not_found");
354
+ }
355
+ const token = await requireWalletIdentityToken(options.store, sessionId, request, response);
356
+ if (!token) {
357
+ return;
358
+ }
359
+ let session;
360
+ if (apiWalletOpenedMatch?.[1]) {
361
+ session = await mapStoreError(() => options.store.recordWalletIdentityOpened(sessionId));
362
+ }
363
+ else if (apiWalletConnectingMatch?.[1]) {
364
+ session = await mapStoreError(() => options.store.recordWalletIdentityConnecting(sessionId));
365
+ }
366
+ else {
367
+ const body = await readJsonBody(request);
368
+ const parsed = walletIdentityResultInputSchema.safeParse(body);
369
+ if (!parsed.success) {
370
+ sendJson(response, 400, { error: "input_invalid" });
371
+ return;
372
+ }
373
+ session = await mapStoreError(() => options.store.recordWalletIdentityResult(sessionId, parsed.data));
374
+ }
375
+ sendJson(response, 200, publicWalletIdentitySession(session));
376
+ return;
377
+ }
378
+ if (request.method === "POST" && apiReviewOpenedMatch?.[1]) {
379
+ const sessionId = apiReviewOpenedMatch[1];
380
+ const token = await requireReviewSessionToken(options.store, sessionId, request, response);
381
+ if (!token) {
382
+ return;
383
+ }
384
+ const session = await mapStoreError(() => options.store.recordReviewPageOpened(sessionId));
385
+ sendJson(response, 200, {
386
+ reviewSessionId: session.id,
387
+ internalStatus: session.status,
388
+ pollingStatus: getExecutionPollingStatus(session),
389
+ lastActivityAt: session.lastActivityAt
390
+ });
391
+ return;
392
+ }
393
+ if (request.method === "POST" && apiReviewStateMatch?.[1]) {
394
+ const sessionId = apiReviewStateMatch[1];
395
+ const token = await requireReviewSessionToken(options.store, sessionId, request, response);
396
+ if (!token) {
397
+ return;
398
+ }
399
+ const body = await readJsonBody(request);
400
+ const account = typeof body.account === "string" ? body.account : undefined;
401
+ const planId = typeof body.planId === "string" ? body.planId : undefined;
402
+ if (!account || !planId) {
403
+ sendJson(response, 400, { error: "input_invalid" });
404
+ return;
405
+ }
406
+ const normalizedAccount = parseSuiAddress(account);
407
+ if (!normalizedAccount) {
408
+ sendJson(response, 400, { error: "input_invalid" });
409
+ return;
410
+ }
411
+ const session = await options.store.getReviewSession(sessionId);
412
+ if (!session) {
413
+ sendJson(response, 404, { error: "session_not_found" });
414
+ return;
415
+ }
416
+ const plan = session.plans.find((candidate) => candidate.id === planId);
417
+ if (!plan) {
418
+ sendJson(response, 400, { error: "plan_not_in_session" });
419
+ return;
420
+ }
421
+ await mapStoreError(() => options.store.recordWalletConnected(sessionId, normalizedAccount));
422
+ const computed = await computeReviewStateWithPrivateArtifacts({
423
+ reviewSessionId: sessionId,
424
+ plan,
425
+ account: normalizedAccount
426
+ }, options.reviewComputationDeps);
427
+ const updatedSession = await mapStoreError(() => options.store.recordReviewStateWithArtifacts(sessionId, computed.state, computed.privateArtifacts));
428
+ sendJson(response, 200, { reviewState: updatedSession.reviewState });
429
+ return;
430
+ }
431
+ if (request.method === "POST" && apiReviewResultMatch?.[1]) {
432
+ const sessionId = apiReviewResultMatch[1];
433
+ const token = await requireReviewSessionToken(options.store, sessionId, request, response);
434
+ if (!token) {
435
+ return;
436
+ }
437
+ const body = await readJsonBody(request);
438
+ const planId = typeof body.planId === "string" ? body.planId : undefined;
439
+ const status = body.status === "signed_pending_result" ||
440
+ body.status === "success" ||
441
+ body.status === "failure"
442
+ ? body.status
443
+ : undefined;
444
+ if (!planId || !status) {
445
+ sendJson(response, 400, { error: "input_invalid" });
446
+ return;
447
+ }
448
+ const failureReason = typeof body.failureReason === "string" ? body.failureReason : undefined;
449
+ const txDigest = typeof body.txDigest === "string" ? body.txDigest : undefined;
450
+ const resultBase = {
451
+ reviewSessionId: sessionId,
452
+ planId,
453
+ recordedAt: new Date().toISOString()
454
+ };
455
+ let result;
456
+ if (status === "failure") {
457
+ if (!isFailureReason(failureReason)) {
458
+ sendJson(response, 400, { error: "input_invalid" });
459
+ return;
460
+ }
461
+ result = { ...resultBase, status, failureReason };
462
+ }
463
+ else {
464
+ if (failureReason !== undefined) {
465
+ sendJson(response, 400, { error: "input_invalid" });
466
+ return;
467
+ }
468
+ if (!txDigest) {
469
+ sendJson(response, 400, { error: "input_invalid" });
470
+ return;
471
+ }
472
+ result = { ...resultBase, status, txDigest };
473
+ }
474
+ if (status === "failure" && txDigest) {
475
+ result.txDigest = txDigest;
476
+ }
477
+ const updatedSession = await mapStoreError(() => options.store.recordExecutionResult(sessionId, result));
478
+ sendJson(response, 200, { executionResult: updatedSession.executionResult });
479
+ return;
480
+ }
481
+ if (request.method === "GET" && apiResultMatch?.[1]) {
482
+ const sessionId = apiResultMatch[1];
483
+ const token = await requireReviewSessionToken(options.store, sessionId, request, response);
484
+ if (!token) {
485
+ return;
486
+ }
487
+ const session = await options.store.getReviewSession(sessionId);
488
+ if (!session) {
489
+ sendJson(response, 404, { error: "session_not_found" });
490
+ return;
491
+ }
492
+ sendJson(response, 200, {
493
+ reviewSessionId: session.id,
494
+ status: getExecutionPollingStatus(session),
495
+ lastActivityAt: session.lastActivityAt,
496
+ executionResult: session.executionResult
497
+ });
498
+ return;
499
+ }
500
+ sendJson(response, 404, { error: "not_found" });
501
+ }
502
+ async function mapStoreError(operation) {
503
+ try {
504
+ return await operation();
505
+ }
506
+ catch (error) {
507
+ if (error instanceof SessionStoreError) {
508
+ throw new HttpError(statusForStoreError(error), error.code);
509
+ }
510
+ throw error;
511
+ }
512
+ }
513
+ function statusForStoreError(error) {
514
+ switch (error.code) {
515
+ case "active_account_not_set":
516
+ return 409;
517
+ case "input_invalid":
518
+ return 400;
519
+ case "invalid_session_transition":
520
+ case "execution_result_finalized":
521
+ case "signed_pending_result_conflict":
522
+ return 409;
523
+ case "plan_not_in_session":
524
+ case "session_mismatch":
525
+ return 400;
526
+ case "handoff_unavailable":
527
+ case "handoff_commitment_mismatch":
528
+ return 409;
529
+ case "session_expired":
530
+ return 410;
531
+ case "session_not_found":
532
+ return 404;
533
+ default:
534
+ return assertNever(error.code);
535
+ }
536
+ }
537
+ function assertNever(value) {
538
+ throw new Error(`Unhandled store error code: ${String(value)}`);
539
+ }
540
+ function isFailureReason(value) {
541
+ return value !== undefined && FAILURE_REASONS.includes(value);
542
+ }
543
+ function requestBaseUrl(request) {
544
+ const host = request.headers.host;
545
+ if (!host) {
546
+ throw new HttpError(400, "host_required");
547
+ }
548
+ return `http://${host}`;
549
+ }
550
+ function publicWalletIdentitySession(session) {
551
+ return {
552
+ walletSessionId: session.id,
553
+ status: session.status,
554
+ account: session.status === "connected" ? session.account : undefined,
555
+ chain: session.status === "connected" ? session.chain : undefined,
556
+ walletName: session.status === "connected" || session.status === "rejected" || session.status === "failed"
557
+ ? session.walletName
558
+ : undefined,
559
+ walletId: session.status === "connected" || session.status === "rejected" || session.status === "failed"
560
+ ? session.walletId
561
+ : undefined,
562
+ failureReason: session.status === "rejected" || session.status === "failed" ? session.failureReason : undefined,
563
+ failureDetail: session.status === "rejected" || session.status === "failed" ? session.failureDetail : undefined,
564
+ expiresAt: session.expiresAt,
565
+ lastActivityAt: session.lastActivityAt,
566
+ pollingHint: walletIdentityPollingHint()
567
+ };
568
+ }