safehands-pharos 1.5.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/policies/default.json +15 -0
- package/.env.example +81 -64
- package/README.md +301 -444
- package/SECURITY.md +112 -0
- package/SKILL.md +59 -0
- package/contracts/SafeHandsAttestation.sol +182 -0
- package/contracts/SafeHandsRegistry.sol +149 -0
- package/dist/agent/SafeHandsGuardianAgent.d.ts +39 -0
- package/dist/agent/SafeHandsGuardianAgent.d.ts.map +1 -0
- package/dist/agent/SafeHandsGuardianAgent.js +98 -0
- package/dist/agent/SafeHandsGuardianAgent.js.map +1 -0
- package/dist/agent/agentDecisionFormatter.d.ts +43 -0
- package/dist/agent/agentDecisionFormatter.d.ts.map +1 -0
- package/dist/agent/agentDecisionFormatter.js +96 -0
- package/dist/agent/agentDecisionFormatter.js.map +1 -0
- package/dist/agent/agentEnrich.d.ts +28 -0
- package/dist/agent/agentEnrich.d.ts.map +1 -0
- package/dist/agent/agentEnrich.js +99 -0
- package/dist/agent/agentEnrich.js.map +1 -0
- package/dist/agent/agentIntentClassifier.d.ts +44 -0
- package/dist/agent/agentIntentClassifier.d.ts.map +1 -0
- package/dist/agent/agentIntentClassifier.js +77 -0
- package/dist/agent/agentIntentClassifier.js.map +1 -0
- package/dist/agent/agentPolicyResolver.d.ts +44 -0
- package/dist/agent/agentPolicyResolver.d.ts.map +1 -0
- package/dist/agent/agentPolicyResolver.js +94 -0
- package/dist/agent/agentPolicyResolver.js.map +1 -0
- package/dist/agent/agentRuntime.d.ts +12 -0
- package/dist/agent/agentRuntime.d.ts.map +1 -0
- package/dist/agent/agentRuntime.js +31 -0
- package/dist/agent/agentRuntime.js.map +1 -0
- package/dist/agent/agentToolRouter.d.ts +24 -0
- package/dist/agent/agentToolRouter.d.ts.map +1 -0
- package/dist/agent/agentToolRouter.js +89 -0
- package/dist/agent/agentToolRouter.js.map +1 -0
- package/dist/agent/guardianOperator.d.ts +35 -0
- package/dist/agent/guardianOperator.d.ts.map +1 -0
- package/dist/agent/guardianOperator.js +65 -0
- package/dist/agent/guardianOperator.js.map +1 -0
- package/dist/agent/index.d.ts +8 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +14 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/api/activityRoutes.d.ts +70 -0
- package/dist/api/activityRoutes.d.ts.map +1 -0
- package/dist/api/activityRoutes.js +100 -0
- package/dist/api/activityRoutes.js.map +1 -0
- package/dist/api/agentRoutes.d.ts +16 -0
- package/dist/api/agentRoutes.d.ts.map +1 -0
- package/dist/api/agentRoutes.js +37 -0
- package/dist/api/agentRoutes.js.map +1 -0
- package/dist/api/broadcastRoutes.d.ts +34 -0
- package/dist/api/broadcastRoutes.d.ts.map +1 -0
- package/dist/api/broadcastRoutes.js +147 -0
- package/dist/api/broadcastRoutes.js.map +1 -0
- package/dist/api/consolePage.d.ts +2 -0
- package/dist/api/consolePage.d.ts.map +1 -0
- package/dist/api/consolePage.js +206 -0
- package/dist/api/consolePage.js.map +1 -0
- package/dist/api/httpHardening.d.ts +75 -0
- package/dist/api/httpHardening.d.ts.map +1 -0
- package/dist/api/httpHardening.js +122 -0
- package/dist/api/httpHardening.js.map +1 -0
- package/dist/api/indexerRoutes.d.ts +46 -0
- package/dist/api/indexerRoutes.d.ts.map +1 -0
- package/dist/api/indexerRoutes.js +75 -0
- package/dist/api/indexerRoutes.js.map +1 -0
- package/dist/api/paidRoutes.d.ts +8 -0
- package/dist/api/paidRoutes.d.ts.map +1 -0
- package/dist/api/paidRoutes.js +175 -0
- package/dist/api/paidRoutes.js.map +1 -0
- package/dist/api/prepareRoutes.d.ts +58 -0
- package/dist/api/prepareRoutes.d.ts.map +1 -0
- package/dist/api/prepareRoutes.js +90 -0
- package/dist/api/prepareRoutes.js.map +1 -0
- package/dist/api/response.d.ts +39 -0
- package/dist/api/response.d.ts.map +1 -0
- package/dist/api/response.js +53 -0
- package/dist/api/response.js.map +1 -0
- package/dist/api/routes.d.ts +175 -0
- package/dist/api/routes.d.ts.map +1 -0
- package/dist/api/routes.js +276 -0
- package/dist/api/routes.js.map +1 -0
- package/dist/api/schemas.d.ts +147 -0
- package/dist/api/schemas.d.ts.map +1 -0
- package/dist/api/schemas.js +81 -0
- package/dist/api/schemas.js.map +1 -0
- package/dist/api/server.d.ts +11 -0
- package/dist/api/server.d.ts.map +1 -0
- package/dist/api/server.js +505 -0
- package/dist/api/server.js.map +1 -0
- package/dist/api/toolRoutes.d.ts +14 -0
- package/dist/api/toolRoutes.d.ts.map +1 -0
- package/dist/api/toolRoutes.js +138 -0
- package/dist/api/toolRoutes.js.map +1 -0
- package/dist/api/walletPrepareRoutes.d.ts +74 -0
- package/dist/api/walletPrepareRoutes.d.ts.map +1 -0
- package/dist/api/walletPrepareRoutes.js +127 -0
- package/dist/api/walletPrepareRoutes.js.map +1 -0
- package/dist/api/x402Gate.d.ts +51 -0
- package/dist/api/x402Gate.d.ts.map +1 -0
- package/dist/api/x402Gate.js +184 -0
- package/dist/api/x402Gate.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +29 -1
- package/dist/cli.js.map +1 -1
- package/dist/data/ecosystemRegistry.data.d.ts +3 -0
- package/dist/data/ecosystemRegistry.data.d.ts.map +1 -0
- package/dist/data/ecosystemRegistry.data.js +582 -0
- package/dist/data/ecosystemRegistry.data.js.map +1 -0
- package/dist/demo.d.ts +0 -0
- package/dist/demo.d.ts.map +1 -1
- package/dist/demo.js +43 -8
- package/dist/demo.js.map +1 -1
- package/dist/handsafe.zip +0 -0
- package/dist/index.d.ts +0 -0
- package/dist/index.d.ts.map +0 -0
- package/dist/index.js +212 -135
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +0 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +72 -10
- package/dist/init.js.map +1 -1
- package/dist/lib/analysis/abiWords.d.ts +17 -0
- package/dist/lib/analysis/abiWords.d.ts.map +1 -0
- package/dist/lib/analysis/abiWords.js +48 -0
- package/dist/lib/analysis/abiWords.js.map +1 -0
- package/dist/lib/analysis/approval.d.ts +29 -0
- package/dist/lib/analysis/approval.d.ts.map +1 -0
- package/dist/lib/analysis/approval.js +189 -0
- package/dist/lib/analysis/approval.js.map +1 -0
- package/dist/lib/analysis/calldata.d.ts +60 -0
- package/dist/lib/analysis/calldata.d.ts.map +1 -0
- package/dist/lib/analysis/calldata.js +464 -0
- package/dist/lib/analysis/calldata.js.map +1 -0
- package/dist/lib/analysis/contractIntel.d.ts +65 -0
- package/dist/lib/analysis/contractIntel.d.ts.map +1 -0
- package/dist/lib/analysis/contractIntel.js +320 -0
- package/dist/lib/analysis/contractIntel.js.map +1 -0
- package/dist/lib/analysis/evm.d.ts +42 -0
- package/dist/lib/analysis/evm.d.ts.map +1 -0
- package/dist/lib/analysis/evm.js +185 -0
- package/dist/lib/analysis/evm.js.map +1 -0
- package/dist/lib/analysis/gas.d.ts +4 -0
- package/dist/lib/analysis/gas.d.ts.map +1 -0
- package/dist/lib/analysis/gas.js +13 -0
- package/dist/lib/analysis/gas.js.map +1 -0
- package/dist/lib/analysis/index.d.ts +11 -0
- package/dist/lib/analysis/index.d.ts.map +1 -0
- package/dist/lib/analysis/index.js +16 -0
- package/dist/lib/analysis/index.js.map +1 -0
- package/dist/lib/analysis/pharosTokens.d.ts +31 -0
- package/dist/lib/analysis/pharosTokens.d.ts.map +1 -0
- package/dist/lib/analysis/pharosTokens.js +78 -0
- package/dist/lib/analysis/pharosTokens.js.map +1 -0
- package/dist/lib/analysis/safeTx.d.ts +27 -0
- package/dist/lib/analysis/safeTx.d.ts.map +1 -0
- package/dist/lib/analysis/safeTx.js +118 -0
- package/dist/lib/analysis/safeTx.js.map +1 -0
- package/dist/lib/analysis/token.d.ts +21 -0
- package/dist/lib/analysis/token.d.ts.map +1 -0
- package/dist/lib/analysis/token.js +151 -0
- package/dist/lib/analysis/token.js.map +1 -0
- package/dist/lib/analysis/types.d.ts +49 -0
- package/dist/lib/analysis/types.d.ts.map +1 -0
- package/dist/lib/analysis/types.js +10 -0
- package/dist/lib/analysis/types.js.map +1 -0
- package/dist/lib/analysis/x402.d.ts +24 -0
- package/dist/lib/analysis/x402.d.ts.map +1 -0
- package/dist/lib/analysis/x402.js +60 -0
- package/dist/lib/analysis/x402.js.map +1 -0
- package/dist/lib/auditLog.d.ts +1 -0
- package/dist/lib/auditLog.d.ts.map +1 -1
- package/dist/lib/auditLog.js +0 -0
- package/dist/lib/auditLog.js.map +1 -1
- package/dist/lib/config.d.ts +77 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +120 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/constants.d.ts +246 -97
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +334 -127
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/dodoApi.d.ts +26 -3
- package/dist/lib/dodoApi.d.ts.map +1 -1
- package/dist/lib/dodoApi.js +80 -17
- package/dist/lib/dodoApi.js.map +1 -1
- package/dist/lib/ecosystemRegistry.d.ts +126 -0
- package/dist/lib/ecosystemRegistry.d.ts.map +1 -0
- package/dist/lib/ecosystemRegistry.js +178 -0
- package/dist/lib/ecosystemRegistry.js.map +1 -0
- package/dist/lib/envLoader.d.ts +0 -0
- package/dist/lib/envLoader.d.ts.map +0 -0
- package/dist/lib/envLoader.js +39 -18
- package/dist/lib/envLoader.js.map +1 -1
- package/dist/lib/goldsky.d.ts +58 -0
- package/dist/lib/goldsky.d.ts.map +1 -0
- package/dist/lib/goldsky.js +275 -0
- package/dist/lib/goldsky.js.map +1 -0
- package/dist/lib/guardian/decision.d.ts +23 -0
- package/dist/lib/guardian/decision.d.ts.map +1 -0
- package/dist/lib/guardian/decision.js +40 -0
- package/dist/lib/guardian/decision.js.map +1 -0
- package/dist/lib/http.d.ts +0 -1
- package/dist/lib/http.d.ts.map +1 -1
- package/dist/lib/http.js +297 -32
- package/dist/lib/http.js.map +1 -1
- package/dist/lib/managedExecution.d.ts +19 -0
- package/dist/lib/managedExecution.d.ts.map +1 -0
- package/dist/lib/managedExecution.js +64 -0
- package/dist/lib/managedExecution.js.map +1 -0
- package/dist/lib/merkleBatcher.d.ts +23 -0
- package/dist/lib/merkleBatcher.d.ts.map +1 -0
- package/dist/lib/merkleBatcher.js +62 -0
- package/dist/lib/merkleBatcher.js.map +1 -0
- package/dist/lib/networks.d.ts +48 -0
- package/dist/lib/networks.d.ts.map +1 -0
- package/dist/lib/networks.js +103 -0
- package/dist/lib/networks.js.map +1 -0
- package/dist/lib/observability/accessContext.d.ts +29 -0
- package/dist/lib/observability/accessContext.d.ts.map +1 -0
- package/dist/lib/observability/accessContext.js +50 -0
- package/dist/lib/observability/accessContext.js.map +1 -0
- package/dist/lib/observability/activityStore.d.ts +70 -0
- package/dist/lib/observability/activityStore.d.ts.map +1 -0
- package/dist/lib/observability/activityStore.js +149 -0
- package/dist/lib/observability/activityStore.js.map +1 -0
- package/dist/lib/observability/apiKey.d.ts +55 -0
- package/dist/lib/observability/apiKey.d.ts.map +1 -0
- package/dist/lib/observability/apiKey.js +141 -0
- package/dist/lib/observability/apiKey.js.map +1 -0
- package/dist/lib/observability/logger.d.ts +30 -0
- package/dist/lib/observability/logger.d.ts.map +1 -0
- package/dist/lib/observability/logger.js +30 -0
- package/dist/lib/observability/logger.js.map +1 -0
- package/dist/lib/observability/quota.d.ts +33 -0
- package/dist/lib/observability/quota.d.ts.map +1 -0
- package/dist/lib/observability/quota.js +49 -0
- package/dist/lib/observability/quota.js.map +1 -0
- package/dist/lib/observability/requestId.d.ts +8 -0
- package/dist/lib/observability/requestId.d.ts.map +1 -0
- package/dist/lib/observability/requestId.js +24 -0
- package/dist/lib/observability/requestId.js.map +1 -0
- package/dist/lib/observability/sanitize.d.ts +77 -0
- package/dist/lib/observability/sanitize.d.ts.map +1 -0
- package/dist/lib/observability/sanitize.js +228 -0
- package/dist/lib/observability/sanitize.js.map +1 -0
- package/dist/lib/observability/scopes.d.ts +20 -0
- package/dist/lib/observability/scopes.d.ts.map +1 -0
- package/dist/lib/observability/scopes.js +77 -0
- package/dist/lib/observability/scopes.js.map +1 -0
- package/dist/lib/okxDexApi.d.ts +106 -0
- package/dist/lib/okxDexApi.d.ts.map +1 -0
- package/dist/lib/okxDexApi.js +221 -0
- package/dist/lib/okxDexApi.js.map +1 -0
- package/dist/lib/persistentJsonStore.d.ts +5 -0
- package/dist/lib/persistentJsonStore.d.ts.map +1 -0
- package/dist/lib/persistentJsonStore.js +39 -0
- package/dist/lib/persistentJsonStore.js.map +1 -0
- package/dist/lib/pharos/attestationPublisher.d.ts +50 -0
- package/dist/lib/pharos/attestationPublisher.d.ts.map +1 -0
- package/dist/lib/pharos/attestationPublisher.js +262 -0
- package/dist/lib/pharos/attestationPublisher.js.map +1 -0
- package/dist/lib/pharos/ecosystem.d.ts +52 -0
- package/dist/lib/pharos/ecosystem.d.ts.map +1 -0
- package/dist/lib/pharos/ecosystem.js +431 -0
- package/dist/lib/pharos/ecosystem.js.map +1 -0
- package/dist/lib/pharos/ecosystemEvidence.d.ts +53 -0
- package/dist/lib/pharos/ecosystemEvidence.d.ts.map +1 -0
- package/dist/lib/pharos/ecosystemEvidence.js +133 -0
- package/dist/lib/pharos/ecosystemEvidence.js.map +1 -0
- package/dist/lib/pharos/rpc.d.ts +44 -0
- package/dist/lib/pharos/rpc.d.ts.map +1 -0
- package/dist/lib/pharos/rpc.js +90 -0
- package/dist/lib/pharos/rpc.js.map +1 -0
- package/dist/lib/pharos/rpcEvidence.d.ts +49 -0
- package/dist/lib/pharos/rpcEvidence.d.ts.map +1 -0
- package/dist/lib/pharos/rpcEvidence.js +46 -0
- package/dist/lib/pharos/rpcEvidence.js.map +1 -0
- package/dist/lib/pharos/rpcMethods.d.ts +46 -0
- package/dist/lib/pharos/rpcMethods.d.ts.map +1 -0
- package/dist/lib/pharos/rpcMethods.js +176 -0
- package/dist/lib/pharos/rpcMethods.js.map +1 -0
- package/dist/lib/pharos/spvVerifier.d.ts +47 -0
- package/dist/lib/pharos/spvVerifier.d.ts.map +1 -0
- package/dist/lib/pharos/spvVerifier.js +105 -0
- package/dist/lib/pharos/spvVerifier.js.map +1 -0
- package/dist/lib/pharos/userSignedBroadcaster.d.ts +10 -0
- package/dist/lib/pharos/userSignedBroadcaster.d.ts.map +1 -0
- package/dist/lib/pharos/userSignedBroadcaster.js +50 -0
- package/dist/lib/pharos/userSignedBroadcaster.js.map +1 -0
- package/dist/lib/pharosClient.d.ts +5 -10
- package/dist/lib/pharosClient.d.ts.map +1 -1
- package/dist/lib/pharosClient.js +9 -16
- package/dist/lib/pharosClient.js.map +1 -1
- package/dist/lib/policy/actionPolicyEngine.d.ts +37 -1
- package/dist/lib/policy/actionPolicyEngine.d.ts.map +1 -1
- package/dist/lib/policy/actionPolicyEngine.js +213 -52
- package/dist/lib/policy/actionPolicyEngine.js.map +1 -1
- package/dist/lib/policy/agentPolicy.d.ts +29 -0
- package/dist/lib/policy/agentPolicy.d.ts.map +1 -0
- package/dist/lib/policy/agentPolicy.js +154 -0
- package/dist/lib/policy/agentPolicy.js.map +1 -0
- package/dist/lib/policy/policyPresets.d.ts +30 -0
- package/dist/lib/policy/policyPresets.d.ts.map +1 -0
- package/dist/lib/policy/policyPresets.js +124 -0
- package/dist/lib/policy/policyPresets.js.map +1 -0
- package/dist/lib/policy/writeExecutionGate.d.ts +23 -0
- package/dist/lib/policy/writeExecutionGate.d.ts.map +1 -0
- package/dist/lib/policy/writeExecutionGate.js +65 -0
- package/dist/lib/policy/writeExecutionGate.js.map +1 -0
- package/dist/lib/preparedTxStore.d.ts +21 -0
- package/dist/lib/preparedTxStore.d.ts.map +1 -0
- package/dist/lib/preparedTxStore.js +90 -0
- package/dist/lib/preparedTxStore.js.map +1 -0
- package/dist/lib/price/chainlinkPushProvider.d.ts +34 -0
- package/dist/lib/price/chainlinkPushProvider.d.ts.map +1 -0
- package/dist/lib/price/chainlinkPushProvider.js +36 -0
- package/dist/lib/price/chainlinkPushProvider.js.map +1 -0
- package/dist/lib/price/feedRegistry.d.ts +26 -0
- package/dist/lib/price/feedRegistry.d.ts.map +1 -0
- package/dist/lib/price/feedRegistry.js +67 -0
- package/dist/lib/price/feedRegistry.js.map +1 -0
- package/dist/lib/price/priceResolver.d.ts +35 -0
- package/dist/lib/price/priceResolver.d.ts.map +1 -0
- package/dist/lib/price/priceResolver.js +246 -0
- package/dist/lib/price/priceResolver.js.map +1 -0
- package/dist/lib/price/supraProvider.d.ts +9 -0
- package/dist/lib/price/supraProvider.d.ts.map +1 -0
- package/dist/lib/price/supraProvider.js +17 -0
- package/dist/lib/price/supraProvider.js.map +1 -0
- package/dist/lib/price/types.d.ts +67 -0
- package/dist/lib/price/types.d.ts.map +1 -0
- package/dist/lib/price/types.js +7 -0
- package/dist/lib/price/types.js.map +1 -0
- package/dist/lib/productionGuards.d.ts +16 -0
- package/dist/lib/productionGuards.d.ts.map +1 -0
- package/dist/lib/productionGuards.js +93 -0
- package/dist/lib/productionGuards.js.map +1 -0
- package/dist/lib/recipientSafety.d.ts +5 -0
- package/dist/lib/recipientSafety.d.ts.map +1 -0
- package/dist/lib/recipientSafety.js +28 -0
- package/dist/lib/recipientSafety.js.map +1 -0
- package/dist/lib/riskEngine.d.ts +19 -0
- package/dist/lib/riskEngine.d.ts.map +1 -1
- package/dist/lib/riskEngine.js +107 -38
- package/dist/lib/riskEngine.js.map +1 -1
- package/dist/lib/riskInclusion.d.ts +72 -0
- package/dist/lib/riskInclusion.d.ts.map +1 -0
- package/dist/lib/riskInclusion.js +252 -0
- package/dist/lib/riskInclusion.js.map +1 -0
- package/dist/lib/safeHandsRegistry.d.ts +32 -0
- package/dist/lib/safeHandsRegistry.d.ts.map +1 -0
- package/dist/lib/safeHandsRegistry.js +151 -0
- package/dist/lib/safeHandsRegistry.js.map +1 -0
- package/dist/lib/signer/index.d.ts +3 -3
- package/dist/lib/signer/index.d.ts.map +1 -1
- package/dist/lib/signer/index.js +33 -11
- package/dist/lib/signer/index.js.map +1 -1
- package/dist/lib/spendAccumulator.d.ts +24 -1
- package/dist/lib/spendAccumulator.d.ts.map +1 -1
- package/dist/lib/spendAccumulator.js +76 -6
- package/dist/lib/spendAccumulator.js.map +1 -1
- package/dist/lib/toolResponse.d.ts +6 -1
- package/dist/lib/toolResponse.d.ts.map +1 -1
- package/dist/lib/toolResponse.js +13 -9
- package/dist/lib/toolResponse.js.map +1 -1
- package/dist/lib/validation.d.ts +7 -0
- package/dist/lib/validation.d.ts.map +1 -0
- package/dist/lib/validation.js +62 -0
- package/dist/lib/validation.js.map +1 -0
- package/dist/lib/wallet/index.d.ts +8 -12
- package/dist/lib/wallet/index.d.ts.map +1 -1
- package/dist/lib/wallet/index.js +48 -35
- package/dist/lib/wallet/index.js.map +1 -1
- package/dist/lib/x402ReplayStore.d.ts +6 -0
- package/dist/lib/x402ReplayStore.d.ts.map +1 -0
- package/dist/lib/x402ReplayStore.js +79 -0
- package/dist/lib/x402ReplayStore.js.map +1 -0
- package/dist/safehands.zip +0 -0
- package/dist/sdk.d.ts +14 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/sdk.js +25 -0
- package/dist/sdk.js.map +1 -0
- package/dist/tools/approveToken.d.ts +31 -10
- package/dist/tools/approveToken.d.ts.map +1 -1
- package/dist/tools/approveToken.js +91 -24
- package/dist/tools/approveToken.js.map +1 -1
- package/dist/tools/assessRisk.d.ts +13 -26
- package/dist/tools/assessRisk.d.ts.map +1 -1
- package/dist/tools/assessRisk.js +34 -75
- package/dist/tools/assessRisk.js.map +1 -1
- package/dist/tools/checkAllowance.d.ts +9 -3
- package/dist/tools/checkAllowance.d.ts.map +1 -1
- package/dist/tools/checkAllowance.js +27 -5
- package/dist/tools/checkAllowance.js.map +1 -1
- package/dist/tools/checkTokenSecurity.d.ts +62 -5
- package/dist/tools/checkTokenSecurity.d.ts.map +1 -1
- package/dist/tools/checkTokenSecurity.js +166 -21
- package/dist/tools/checkTokenSecurity.js.map +1 -1
- package/dist/tools/createAgentWallet.d.ts +4 -2
- package/dist/tools/createAgentWallet.d.ts.map +1 -1
- package/dist/tools/createAgentWallet.js +36 -31
- package/dist/tools/createAgentWallet.js.map +1 -1
- package/dist/tools/estimateGas.d.ts +7 -2
- package/dist/tools/estimateGas.d.ts.map +1 -1
- package/dist/tools/estimateGas.js +16 -10
- package/dist/tools/estimateGas.js.map +1 -1
- package/dist/tools/executeSwap.d.ts +56 -8
- package/dist/tools/executeSwap.d.ts.map +1 -1
- package/dist/tools/executeSwap.js +194 -46
- package/dist/tools/executeSwap.js.map +1 -1
- package/dist/tools/explainRisk.d.ts +9 -9
- package/dist/tools/explainRisk.d.ts.map +1 -1
- package/dist/tools/explainRisk.js +5 -4
- package/dist/tools/explainRisk.js.map +1 -1
- package/dist/tools/getAgentPolicy.d.ts +15 -0
- package/dist/tools/getAgentPolicy.d.ts.map +1 -0
- package/dist/tools/getAgentPolicy.js +16 -0
- package/dist/tools/getAgentPolicy.js.map +1 -0
- package/dist/tools/getAgentReputation.d.ts +31 -0
- package/dist/tools/getAgentReputation.d.ts.map +1 -0
- package/dist/tools/getAgentReputation.js +44 -0
- package/dist/tools/getAgentReputation.js.map +1 -0
- package/dist/tools/getAgentWallet.d.ts +1 -1
- package/dist/tools/getAgentWallet.d.ts.map +1 -1
- package/dist/tools/getAgentWallet.js +11 -1
- package/dist/tools/getAgentWallet.js.map +1 -1
- package/dist/tools/getAgentWalletBalance.d.ts +1 -1
- package/dist/tools/getAgentWalletBalance.d.ts.map +1 -1
- package/dist/tools/getAgentWalletBalance.js +58 -26
- package/dist/tools/getAgentWalletBalance.js.map +1 -1
- package/dist/tools/getExecutionHistory.d.ts +1 -1
- package/dist/tools/getExecutionHistory.d.ts.map +1 -1
- package/dist/tools/getExecutionHistory.js +13 -19
- package/dist/tools/getExecutionHistory.js.map +1 -1
- package/dist/tools/getGasPrice.d.ts +3 -3
- package/dist/tools/getGasPrice.d.ts.map +0 -0
- package/dist/tools/getGasPrice.js +7 -7
- package/dist/tools/getGasPrice.js.map +1 -1
- package/dist/tools/getPoolInfo.d.ts +17 -17
- package/dist/tools/getPoolInfo.d.ts.map +1 -1
- package/dist/tools/getPoolInfo.js +14 -9
- package/dist/tools/getPoolInfo.js.map +1 -1
- package/dist/tools/getSpvProof.d.ts +24 -0
- package/dist/tools/getSpvProof.d.ts.map +1 -0
- package/dist/tools/getSpvProof.js +64 -0
- package/dist/tools/getSpvProof.js.map +1 -0
- package/dist/tools/getTokenPrice.d.ts +28 -85
- package/dist/tools/getTokenPrice.d.ts.map +1 -1
- package/dist/tools/getTokenPrice.js +87 -94
- package/dist/tools/getTokenPrice.js.map +1 -1
- package/dist/tools/getTransactionStatus.d.ts +2 -2
- package/dist/tools/getTransactionStatus.d.ts.map +0 -0
- package/dist/tools/getTransactionStatus.js +0 -0
- package/dist/tools/getTransactionStatus.js.map +0 -0
- package/dist/tools/getWalletBalance.d.ts +38 -3
- package/dist/tools/getWalletBalance.d.ts.map +1 -1
- package/dist/tools/getWalletBalance.js +92 -52
- package/dist/tools/getWalletBalance.js.map +1 -1
- package/dist/tools/publishRiskScore.d.ts +36 -14
- package/dist/tools/publishRiskScore.d.ts.map +1 -1
- package/dist/tools/publishRiskScore.js +80 -32
- package/dist/tools/publishRiskScore.js.map +1 -1
- package/dist/tools/queryGoldsky.d.ts +20 -0
- package/dist/tools/queryGoldsky.d.ts.map +1 -0
- package/dist/tools/queryGoldsky.js +29 -0
- package/dist/tools/queryGoldsky.js.map +1 -0
- package/dist/tools/queryRiskRegistry.d.ts +14 -14
- package/dist/tools/queryRiskRegistry.d.ts.map +1 -1
- package/dist/tools/queryRiskRegistry.js +34 -30
- package/dist/tools/queryRiskRegistry.js.map +1 -1
- package/dist/tools/safehandsPreflightCheck.d.ts +29 -14
- package/dist/tools/safehandsPreflightCheck.d.ts.map +1 -1
- package/dist/tools/safehandsPreflightCheck.js +133 -6
- package/dist/tools/safehandsPreflightCheck.js.map +1 -1
- package/dist/tools/safehandsRiskReport.d.ts +29 -14
- package/dist/tools/safehandsRiskReport.d.ts.map +1 -1
- package/dist/tools/safehandsRiskReport.js +23 -3
- package/dist/tools/safehandsRiskReport.js.map +1 -1
- package/dist/tools/safehandsSafeExecute.d.ts +1 -1
- package/dist/tools/safehandsSafeExecute.d.ts.map +1 -1
- package/dist/tools/safehandsSafeExecute.js +100 -17
- package/dist/tools/safehandsSafeExecute.js.map +1 -1
- package/dist/tools/safehandsWalletHealth.d.ts +15 -1
- package/dist/tools/safehandsWalletHealth.d.ts.map +1 -1
- package/dist/tools/safehandsWalletHealth.js +67 -25
- package/dist/tools/safehandsWalletHealth.js.map +1 -1
- package/dist/tools/safehandsX402Preflight.d.ts +3 -3
- package/dist/tools/safehandsX402Preflight.d.ts.map +1 -1
- package/dist/tools/safehandsX402Preflight.js +24 -3
- package/dist/tools/safehandsX402Preflight.js.map +1 -1
- package/dist/tools/sendPayment.d.ts +10 -4
- package/dist/tools/sendPayment.d.ts.map +1 -1
- package/dist/tools/sendPayment.js +63 -36
- package/dist/tools/sendPayment.js.map +1 -1
- package/dist/tools/setAgentPolicy.d.ts +75 -0
- package/dist/tools/setAgentPolicy.d.ts.map +1 -0
- package/dist/tools/setAgentPolicy.js +80 -0
- package/dist/tools/setAgentPolicy.js.map +1 -0
- package/dist/tools/simulateTransaction.d.ts +7 -3
- package/dist/tools/simulateTransaction.d.ts.map +1 -1
- package/dist/tools/simulateTransaction.js +42 -9
- package/dist/tools/simulateTransaction.js.map +1 -1
- package/dist/tools/tokenRegistryStatus.d.ts +25 -5
- package/dist/tools/tokenRegistryStatus.d.ts.map +1 -1
- package/dist/tools/tokenRegistryStatus.js +61 -31
- package/dist/tools/tokenRegistryStatus.js.map +1 -1
- package/dist/tools/verifyRiskInclusion.d.ts +59 -0
- package/dist/tools/verifyRiskInclusion.d.ts.map +1 -0
- package/dist/tools/verifyRiskInclusion.js +40 -0
- package/dist/tools/verifyRiskInclusion.js.map +1 -0
- package/dist/tools/x402PayAndFetch.d.ts +15 -9
- package/dist/tools/x402PayAndFetch.d.ts.map +1 -1
- package/dist/tools/x402PayAndFetch.js +163 -20
- package/dist/tools/x402PayAndFetch.js.map +1 -1
- package/dist/worker.d.ts +3 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +39 -0
- package/dist/worker.js.map +1 -0
- package/dist/x402Server.d.ts +1 -1
- package/dist/x402Server.d.ts.map +1 -1
- package/dist/x402Server.js +144 -35
- package/dist/x402Server.js.map +1 -1
- package/docs/REVIEWER_QUICKSTART.md +113 -0
- package/docs/SAFEHANDS_REVIEWER_DEMO_SCRIPT.md +228 -0
- package/package.json +131 -73
- package/contracts/RiskRegistry.json +0 -223
- package/contracts/RiskRegistry.sol +0 -58
- package/dist/lib/testDodoLive.d.ts +0 -2
- package/dist/lib/testDodoLive.d.ts.map +0 -1
- package/dist/lib/testDodoLive.js +0 -105
- package/dist/lib/testDodoLive.js.map +0 -1
- package/dist/lib/testLiveSafehands.d.ts +0 -2
- package/dist/lib/testLiveSafehands.d.ts.map +0 -1
- package/dist/lib/testLiveSafehands.js +0 -93
- package/dist/lib/testLiveSafehands.js.map +0 -1
- package/dist/lib/testRpc.d.ts +0 -2
- package/dist/lib/testRpc.d.ts.map +0 -1
- package/dist/lib/testRpc.js +0 -30
- package/dist/lib/testRpc.js.map +0 -1
- package/dist/lib/testRpcLive.d.ts +0 -2
- package/dist/lib/testRpcLive.d.ts.map +0 -1
- package/dist/lib/testRpcLive.js +0 -89
- package/dist/lib/testRpcLive.js.map +0 -1
- package/dist/lib/testTools.d.ts +0 -2
- package/dist/lib/testTools.d.ts.map +0 -1
- package/dist/lib/testTools.js +0 -397
- package/dist/lib/testTools.js.map +0 -1
- package/dist/lib/testX402Live.d.ts +0 -2
- package/dist/lib/testX402Live.d.ts.map +0 -1
- package/dist/lib/testX402Live.js +0 -160
- package/dist/lib/testX402Live.js.map +0 -1
- package/dist/scripts/checkDeploy.d.ts +0 -2
- package/dist/scripts/checkDeploy.d.ts.map +0 -1
- package/dist/scripts/checkDeploy.js +0 -25
- package/dist/scripts/checkDeploy.js.map +0 -1
- package/dist/scripts/deployRegistry.d.ts +0 -2
- package/dist/scripts/deployRegistry.d.ts.map +0 -1
- package/dist/scripts/deployRegistry.js +0 -101
- package/dist/scripts/deployRegistry.js.map +0 -1
- package/dist/scripts/testRegistry.d.ts +0 -2
- package/dist/scripts/testRegistry.d.ts.map +0 -1
- package/dist/scripts/testRegistry.js +0 -44
- package/dist/scripts/testRegistry.js.map +0 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { broadcastSignedSchema } from "./schemas.js";
|
|
2
|
+
import { getPreparedTx, markPreparedTxUsed } from "../lib/preparedTxStore.js";
|
|
3
|
+
import { parseTransaction, recoverTransactionAddress, keccak256, isAddressEqual } from "viem";
|
|
4
|
+
import { broadcastSignedTransaction } from "../lib/pharos/userSignedBroadcaster.js";
|
|
5
|
+
import { checkAttestationConfig, publishAttestation, getAttestationRecord } from "../lib/pharos/attestationPublisher.js";
|
|
6
|
+
import { lookupAttestationInGoldsky, lookupAttestationOnchain } from "../lib/goldsky.js";
|
|
7
|
+
import { auditLog } from "../lib/auditLog.js";
|
|
8
|
+
import { getActiveNetworkName } from "../lib/networks.js";
|
|
9
|
+
import { readOnlyMeta } from "./response.js";
|
|
10
|
+
export async function handleBroadcastSigned(raw) {
|
|
11
|
+
const input = broadcastSignedSchema.parse(raw);
|
|
12
|
+
// 1. Load prepared record
|
|
13
|
+
const record = getPreparedTx(input.preparedTransactionId);
|
|
14
|
+
if (!record) {
|
|
15
|
+
throw new Error("Prepared transaction not found or expired.");
|
|
16
|
+
}
|
|
17
|
+
if (record.hash !== input.preparedTransactionHash) {
|
|
18
|
+
throw new Error("Prepared transaction hash mismatch.");
|
|
19
|
+
}
|
|
20
|
+
if (record.usedAt) {
|
|
21
|
+
throw new Error("Prepared transaction has already been broadcast.");
|
|
22
|
+
}
|
|
23
|
+
if (record.expiresAt < Date.now()) {
|
|
24
|
+
throw new Error("Prepared transaction has expired.");
|
|
25
|
+
}
|
|
26
|
+
if (record.decision === "BLOCK") {
|
|
27
|
+
throw new Error("Action is blocked by SafeHands policy.");
|
|
28
|
+
}
|
|
29
|
+
// Deliberate stance split vs the MCP write gate (see docs/DECISION_CONTRACT.md):
|
|
30
|
+
// the MCP tools accept caller `confirm=true` because the MCP host / human operator
|
|
31
|
+
// relaying the call is the trust anchor attesting the review happened. This HTTP
|
|
32
|
+
// relay has NO such trusted channel — any client can set a boolean — so a
|
|
33
|
+
// REQUIRE_CONFIRMATION record is refused outright rather than self-confirmed.
|
|
34
|
+
if (record.decision === "REQUIRE_CONFIRMATION") {
|
|
35
|
+
throw new Error("Action requires confirmation. No trusted confirmation-satisfied mechanism is currently implemented.");
|
|
36
|
+
}
|
|
37
|
+
// 2. Decode and verify the raw signed transaction
|
|
38
|
+
const parsedTx = parseTransaction(input.rawSignedTransaction);
|
|
39
|
+
// Recover the sender from the signature
|
|
40
|
+
const recoveredAddress = await recoverTransactionAddress({
|
|
41
|
+
serializedTransaction: input.rawSignedTransaction,
|
|
42
|
+
});
|
|
43
|
+
if (!isAddressEqual(recoveredAddress, record.from)) {
|
|
44
|
+
throw new Error(`Signer mismatch: expected ${record.from}, got ${recoveredAddress}`);
|
|
45
|
+
}
|
|
46
|
+
if (parsedTx.to && !isAddressEqual(parsedTx.to, record.to)) {
|
|
47
|
+
throw new Error(`To address mismatch: expected ${record.to}, got ${parsedTx.to}`);
|
|
48
|
+
}
|
|
49
|
+
const expectedValue = record.value ? BigInt(record.value) : 0n;
|
|
50
|
+
const actualValue = parsedTx.value || 0n;
|
|
51
|
+
if (expectedValue !== actualValue) {
|
|
52
|
+
throw new Error(`Value mismatch: expected ${expectedValue}, got ${actualValue}`);
|
|
53
|
+
}
|
|
54
|
+
const actualData = parsedTx.data || "0x";
|
|
55
|
+
const actualDataHash = keccak256(actualData);
|
|
56
|
+
if (actualDataHash !== record.dataHash) {
|
|
57
|
+
throw new Error("Transaction data mismatch.");
|
|
58
|
+
}
|
|
59
|
+
if (parsedTx.chainId !== record.chainId) {
|
|
60
|
+
throw new Error(`ChainId mismatch: expected ${record.chainId}, got ${parsedTx.chainId}`);
|
|
61
|
+
}
|
|
62
|
+
// 3. Check for Live Broadcast capability
|
|
63
|
+
const isBroadcastEnabled = process.env.SAFEHANDS_USER_SIGNED_BROADCAST_ENABLED === "true";
|
|
64
|
+
let txHash;
|
|
65
|
+
let broadcastStatus = "verify_only_passed";
|
|
66
|
+
let attestationStatus = "skipped";
|
|
67
|
+
// Deliberately static: this relay returns right after eth_sendRawTransaction and
|
|
68
|
+
// never waits for the receipt, so the on-chain outcome is genuinely unknown here.
|
|
69
|
+
// Callers poll get_transaction_status with the returned txHash for the mined result.
|
|
70
|
+
const executionStatus = "UNKNOWN";
|
|
71
|
+
let message = "Transaction signature and fields verified successfully. Broadcast is disabled in verify-only mode.";
|
|
72
|
+
if (isBroadcastEnabled) {
|
|
73
|
+
if (record.chainId !== 1672) {
|
|
74
|
+
throw new Error("Live broadcast is only supported on Pharos Pacific Mainnet (chainId 1672).");
|
|
75
|
+
}
|
|
76
|
+
// 3.5 Check attestation config before broadcast
|
|
77
|
+
checkAttestationConfig();
|
|
78
|
+
// 4. Mark used BEFORE broadcast to eliminate async race window
|
|
79
|
+
markPreparedTxUsed(record.id);
|
|
80
|
+
// 5. Broadcast
|
|
81
|
+
txHash = await broadcastSignedTransaction(input.rawSignedTransaction, record.chainId);
|
|
82
|
+
// 6. Attest. In production required/sync mode, wait for the first attempt so
|
|
83
|
+
// the response reflects durable state; retries continue from the persistent queue.
|
|
84
|
+
attestationStatus = "pending_retry";
|
|
85
|
+
const shouldSyncAttestation = process.env.SAFEHANDS_ATTESTATION_REQUIRED === "true" || process.env.SAFEHANDS_ATTESTATION_SYNC === "true";
|
|
86
|
+
if (shouldSyncAttestation) {
|
|
87
|
+
attestationStatus = await publishAttestation(txHash, parsedTx, record);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
publishAttestation(txHash, parsedTx, record).catch(console.error);
|
|
91
|
+
}
|
|
92
|
+
auditLog({
|
|
93
|
+
ts: new Date().toISOString(),
|
|
94
|
+
tool: "broadcast_signed",
|
|
95
|
+
success: true,
|
|
96
|
+
txHash,
|
|
97
|
+
preparedTransactionId: record.id,
|
|
98
|
+
preparedTransactionHash: record.hash,
|
|
99
|
+
attestationStatus,
|
|
100
|
+
chainId: record.chainId,
|
|
101
|
+
});
|
|
102
|
+
broadcastStatus = "success";
|
|
103
|
+
message = "Transaction verified and broadcast. The execution outcome is not awaited; poll get_transaction_status with the returned txHash for the mined receipt.";
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
broadcastStatus,
|
|
107
|
+
attestationStatus,
|
|
108
|
+
executionStatus,
|
|
109
|
+
message,
|
|
110
|
+
txHash,
|
|
111
|
+
preparedTransactionId: record.id,
|
|
112
|
+
preparedTransactionHash: record.hash,
|
|
113
|
+
decision: record.decision,
|
|
114
|
+
network: getActiveNetworkName(),
|
|
115
|
+
chainId: record.chainId,
|
|
116
|
+
...readOnlyMeta(),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export async function handleGetBroadcastAttestationStatus(txHash) {
|
|
120
|
+
const localRecord = getAttestationRecord(txHash);
|
|
121
|
+
const goldsky = await lookupAttestationInGoldsky(txHash);
|
|
122
|
+
const onchain = goldsky.indexed
|
|
123
|
+
? { source: "contract", skipped: true, reason: "Goldsky already indexed the on-chain attestation event." }
|
|
124
|
+
: await lookupAttestationOnchain(txHash);
|
|
125
|
+
const attestationStatus = localRecord?.status || (goldsky.indexed ? "indexed" : ("skipped"));
|
|
126
|
+
return {
|
|
127
|
+
txHash,
|
|
128
|
+
status: goldsky.indexed || ("onchainVerified" in onchain && onchain.onchainVerified)
|
|
129
|
+
? "attested"
|
|
130
|
+
: localRecord?.status === "pending_retry"
|
|
131
|
+
? "pending_retry"
|
|
132
|
+
: localRecord?.status === "failed"
|
|
133
|
+
? "failed"
|
|
134
|
+
: "not_found",
|
|
135
|
+
attestationStatus,
|
|
136
|
+
local: {
|
|
137
|
+
found: Boolean(localRecord),
|
|
138
|
+
record: localRecord,
|
|
139
|
+
},
|
|
140
|
+
goldsky,
|
|
141
|
+
onchain,
|
|
142
|
+
lookupOrder: ["local", "goldsky", "contract_fallback"],
|
|
143
|
+
network: getActiveNetworkName(),
|
|
144
|
+
...readOnlyMeta(),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=broadcastRoutes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"broadcastRoutes.js","sourceRoot":"","sources":["../../src/api/broadcastRoutes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const DEMO_PAGE_HTML = "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>SafeHands: live policy demo \u00B7 Pharos Pacific Mainnet</title>\n<meta name=\"description\" content=\"Paste an agent intent, get a real ALLOW / BLOCK decision from the live SafeHands policy engine on Pharos Pacific Mainnet (1672).\">\n<style>\n :root{\n --bg0:#0d1117; --bg1:#151c2e; --panel:#161d2f; --line:#232c44;\n --text:#f0f6fc; --muted:#9da7b3; --dim:#6e7781;\n --violet:#7c3aed; --cyan:#0891b2;\n --allow:#16a34a; --block:#dc2626; --confirm:#d97706; --prepare:#0891b2;\n }\n *{box-sizing:border-box}\n body{margin:0;background:linear-gradient(160deg,var(--bg0),var(--bg1));min-height:100vh;\n color:var(--text);font:15px/1.55 ui-sans-serif,system-ui,-apple-system,\"Segoe UI\",Roboto,sans-serif;}\n .wrap{max-width:880px;margin:0 auto;padding:40px 20px 64px}\n header{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap}\n h1{font-size:26px;margin:0;letter-spacing:-.02em}\n h1 .shield{background:linear-gradient(90deg,var(--violet),var(--cyan));\n -webkit-background-clip:text;background-clip:text;color:transparent}\n .chip{font:12px/1 ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--muted);\n border:1px solid var(--line);border-radius:999px;padding:5px 10px;white-space:nowrap}\n .sub{color:var(--muted);margin:10px 0 0;max-width:64ch}\n .sub b{color:var(--text);font-weight:600}\n .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:10px;margin:28px 0 14px}\n .preset{appearance:none;text-align:left;cursor:pointer;border:1px solid var(--line);\n background:var(--panel);color:var(--text);border-radius:10px;padding:12px 14px;\n transition:border-color .15s,transform .15s}\n .preset:hover{border-color:var(--cyan);transform:translateY(-1px)}\n .preset small{display:block;color:var(--dim);margin-top:3px}\n .preset .tag{float:right;font:11px/1 ui-monospace,monospace;padding:3px 7px;border-radius:6px;margin-left:8px}\n .tag.allow{color:var(--allow);border:1px solid var(--allow)}\n .tag.prepare{color:var(--prepare);border:1px solid var(--prepare)}\n .tag.block{color:var(--block);border:1px solid var(--block)}\n .tag.confirm{color:var(--confirm);border:1px solid var(--confirm)}\n label{display:block;color:var(--muted);font-size:13px;margin:18px 0 6px}\n textarea{width:100%;min-height:150px;resize:vertical;background:#0b101d;color:var(--text);\n border:1px solid var(--line);border-radius:10px;padding:12px 14px;\n font:13px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace}\n textarea:focus{outline:none;border-color:var(--violet)}\n .run{margin-top:12px;cursor:pointer;border:none;border-radius:10px;padding:12px 22px;\n font-weight:600;font-size:15px;color:#fff;\n background:linear-gradient(90deg,var(--violet),var(--cyan))}\n .run:disabled{opacity:.55;cursor:wait}\n #result{margin-top:26px}\n .card{border-radius:12px;border:1px solid var(--line);background:var(--panel);overflow:hidden}\n .verdict{display:flex;align-items:center;gap:12px;padding:16px 18px;font-size:18px;font-weight:700;\n border-left:6px solid var(--dim)}\n .verdict .lvl{font:12px/1 ui-monospace,monospace;font-weight:400;color:var(--muted)}\n .v-ALLOW{border-left-color:var(--allow);color:var(--allow)}\n .v-BLOCK{border-left-color:var(--block);color:var(--block)}\n .v-REQUIRE_CONFIRMATION{border-left-color:var(--confirm);color:var(--confirm)}\n .v-PREPARE_ONLY{border-left-color:var(--prepare);color:var(--prepare)}\n .v-ERROR{border-left-color:var(--block);color:var(--block)}\n .body{padding:2px 18px 16px;color:var(--muted)}\n .body ul{margin:8px 0;padding-left:20px}\n details{border-top:1px solid var(--line);padding:10px 18px}\n summary{cursor:pointer;color:var(--dim);font-size:13px}\n pre{overflow:auto;font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--muted);margin:10px 0 4px}\n footer{margin-top:44px;color:var(--dim);font-size:13px;border-top:1px solid var(--line);padding-top:16px}\n footer a{color:var(--muted)}\n .spin{display:inline-block;width:14px;height:14px;border:2px solid #fff5;border-top-color:#fff;\n border-radius:50%;animation:r .7s linear infinite;vertical-align:-2px;margin-right:8px}\n @keyframes r{to{transform:rotate(360deg)}}\n</style>\n</head>\n<body>\n<div class=\"wrap\">\n <header>\n <h1><span class=\"shield\">\uD83D\uDEE1 SafeHands</span> live policy demo</h1>\n <span class=\"chip\">Pharos Pacific Mainnet \u00B7 1672</span>\n <span class=\"chip\">zero-custody \u00B7 read-only</span>\n </header>\n <p class=\"sub\">Every button below sends a real agent intent to the <b>live deterministic policy engine</b> running behind this page (<code>POST /tools/safehands_preflight_check</code>). No wallet, no keys, no transaction: this is the check an AI agent runs <b>before</b> it signs anything on Pharos.</p>\n\n <div class=\"grid\" id=\"presets\"></div>\n\n <label for=\"intent\">Agent intent (editable JSON)</label>\n <textarea id=\"intent\" spellcheck=\"false\"></textarea>\n <button class=\"run\" id=\"run\">Run preflight</button>\n\n <div id=\"result\"></div>\n\n <footer>\n SafeHands never holds keys and never signs for you. \u00B7\n <a href=\"https://github.com/SZtch/safehands-pharos\" rel=\"noopener\">GitHub</a> \u00B7\n <a href=\"https://www.pharosscan.xyz/address/0x428e02bf85412e7242d991cd6725ec59e8b06c8d\" rel=\"noopener\">Registry contract</a> \u00B7\n <a href=\"https://www.pharosscan.xyz/address/0x71a7a87b3b1ab6d86204cad691bb32fd75b4588c\" rel=\"noopener\">Attestation contract</a>\n </footer>\n</div>\n<script>\n(function(){\n \"use strict\";\n var PRESETS = [\n { name: \"Unlimited approval\", hint: \"agent grants a spender forever\", tag: \"BLOCK\", cls: \"block\",\n intent: { actionType: \"approve_token\", chainId: 1672, approvalToken: \"USDC\",\n spender: \"0x000000000000000000000000000000000000dEaD\", approvalAmount: \"max\" } },\n { name: \"Wrong chain\", hint: \"agent targets Ethereum, not Pharos\", tag: \"BLOCK\", cls: \"block\",\n intent: { actionType: \"send_payment\", chainId: 1, amount: \"0.5\", amountUnit: \"PROS\",\n recipient: \"0x0000000000000000000000000000000000000001\" } },\n { name: \"Safe small payment\", hint: \"0.001 PROS within policy: safe; sign it yourself\", tag: \"PREPARE\", cls: \"prepare\",\n intent: { actionType: \"send_payment\", chainId: 1672, isMainnet: true, amount: \"0.001\",\n amountUnit: \"PROS\", recipient: \"0x0000000000000000000000000000000000000001\" } },\n { name: \"Unregistered RWA token\", hint: \"tokenized asset not in registry\", tag: \"CONFIRM\", cls: \"confirm\",\n intent: { actionType: \"approve_token\", chainId: 1672, isMainnet: true, approvalAmount: \"25\",\n tokenAddress: \"0x00000000000000000000000000000000000000aa\",\n spender: \"0x0000000000000000000000000000000000000001\", spenderVerified: false } },\n { name: \"Malicious x402 endpoint\", hint: \"payment to an internal IP\", tag: \"BLOCK\", cls: \"block\",\n intent: { actionType: \"x402_pay_and_fetch\", chainId: 1672,\n url: \"http://169.254.169.254/latest/meta-data\", paymentAmountUsdc: \"0.01\" } },\n { name: \"Oversized settlement\", hint: \"5 USDC over the x402 policy cap\", tag: \"BLOCK\", cls: \"block\",\n intent: { actionType: \"x402_pay_and_fetch\", chainId: 1672, isMainnet: true,\n url: \"https://rpc.pharos.xyz/\", paymentAmountUsdc: \"5\" } }\n ];\n\n var box = document.getElementById(\"intent\");\n var out = document.getElementById(\"result\");\n var run = document.getElementById(\"run\");\n var grid = document.getElementById(\"presets\");\n\n PRESETS.forEach(function (p) {\n var b = document.createElement(\"button\");\n b.className = \"preset\";\n b.innerHTML = \"<span class='tag \" + p.cls + \"'>\" + p.tag + \"</span><strong>\" + p.name +\n \"</strong><small>\" + p.hint + \"</small>\";\n b.addEventListener(\"click\", function () {\n box.value = JSON.stringify(p.intent, null, 2);\n submit();\n });\n grid.appendChild(b);\n });\n\n box.value = JSON.stringify(PRESETS[0].intent, null, 2);\n\n function esc(s) {\n return String(s).replace(/[&<>\"]/g, function (c) {\n return { \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\" }[c];\n });\n }\n\n function list(title, items) {\n if (!items || !items.length) return \"\";\n return \"<div><b>\" + title + \"</b><ul>\" + items.map(function (r) {\n return \"<li>\" + esc(r) + \"</li>\";\n }).join(\"\") + \"</ul></div>\";\n }\n\n function render(payload) {\n var html;\n if (payload && payload.success && payload.data) {\n var d = payload.data;\n var decision = d.decision || d.guardianDecision || \"UNKNOWN\";\n html =\n \"<div class='card'>\" +\n \"<div class='verdict v-\" + esc(decision) + \"'>\" + esc(decision) +\n (d.riskLevel ? \" <span class='lvl'>risk: \" + esc(d.riskLevel) + \"</span>\" : \"\") + \"</div>\" +\n \"<div class='body'>\" + list(\"Why\", d.reasons) + list(\"Required actions\", d.requiredActions) +\n (!d.reasons || !d.reasons.length ? \"<div>All policy checks passed for this intent.</div>\" : \"\") +\n \"</div>\" +\n \"<details><summary>Raw response</summary><pre>\" + esc(JSON.stringify(payload, null, 2)) + \"</pre></details>\" +\n \"</div>\";\n } else {\n var err = (payload && payload.error) || {};\n html =\n \"<div class='card'>\" +\n \"<div class='verdict v-ERROR'>\" + esc(err.code || \"ERROR\") + \"</div>\" +\n \"<div class='body'>\" + esc(err.message || \"Request failed.\") + \"</div>\" +\n \"<details><summary>Raw response</summary><pre>\" + esc(JSON.stringify(payload, null, 2)) + \"</pre></details>\" +\n \"</div>\";\n }\n out.innerHTML = html;\n }\n\n function submit() {\n var body;\n try { body = JSON.parse(box.value); }\n catch (e) { render({ success: false, error: { code: \"INVALID_JSON\", message: \"The intent is not valid JSON: \" + e.message } }); return; }\n run.disabled = true;\n run.innerHTML = \"<span class='spin'></span>Checking\u2026\";\n fetch(\"/tools/safehands_preflight_check\", {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body)\n })\n .then(function (r) { return r.json(); })\n .then(render)\n .catch(function (e) { render({ success: false, error: { code: \"NETWORK_ERROR\", message: String(e) } }); })\n .then(function () { run.disabled = false; run.textContent = \"Run preflight\"; });\n }\n\n run.addEventListener(\"click\", submit);\n})();\n</script>\n</body>\n</html>\n";
|
|
2
|
+
//# sourceMappingURL=consolePage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consolePage.d.ts","sourceRoot":"","sources":["../../src/api/consolePage.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,cAAc,urVAqM1B,CAAC"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// ─── SafeHands Interactive Demo Page (GET /demo) ────────────────────────
|
|
2
|
+
// A single self-contained HTML page (no build tooling, no external assets,
|
|
3
|
+
// no dependencies) served by the read-only API. It calls the same-origin
|
|
4
|
+
// hosted tool gateway (POST /tools/safehands_preflight_check) so visitors
|
|
5
|
+
// get REAL decisions from the live deterministic policy engine.
|
|
6
|
+
// Zero-custody: the page never asks for keys and never signs anything.
|
|
7
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
8
|
+
export const DEMO_PAGE_HTML = `<!doctype html>
|
|
9
|
+
<html lang="en">
|
|
10
|
+
<head>
|
|
11
|
+
<meta charset="utf-8">
|
|
12
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
13
|
+
<title>SafeHands: live policy demo · Pharos Pacific Mainnet</title>
|
|
14
|
+
<meta name="description" content="Paste an agent intent, get a real ALLOW / BLOCK decision from the live SafeHands policy engine on Pharos Pacific Mainnet (1672).">
|
|
15
|
+
<style>
|
|
16
|
+
:root{
|
|
17
|
+
--bg0:#0d1117; --bg1:#151c2e; --panel:#161d2f; --line:#232c44;
|
|
18
|
+
--text:#f0f6fc; --muted:#9da7b3; --dim:#6e7781;
|
|
19
|
+
--violet:#7c3aed; --cyan:#0891b2;
|
|
20
|
+
--allow:#16a34a; --block:#dc2626; --confirm:#d97706; --prepare:#0891b2;
|
|
21
|
+
}
|
|
22
|
+
*{box-sizing:border-box}
|
|
23
|
+
body{margin:0;background:linear-gradient(160deg,var(--bg0),var(--bg1));min-height:100vh;
|
|
24
|
+
color:var(--text);font:15px/1.55 ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;}
|
|
25
|
+
.wrap{max-width:880px;margin:0 auto;padding:40px 20px 64px}
|
|
26
|
+
header{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap}
|
|
27
|
+
h1{font-size:26px;margin:0;letter-spacing:-.02em}
|
|
28
|
+
h1 .shield{background:linear-gradient(90deg,var(--violet),var(--cyan));
|
|
29
|
+
-webkit-background-clip:text;background-clip:text;color:transparent}
|
|
30
|
+
.chip{font:12px/1 ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--muted);
|
|
31
|
+
border:1px solid var(--line);border-radius:999px;padding:5px 10px;white-space:nowrap}
|
|
32
|
+
.sub{color:var(--muted);margin:10px 0 0;max-width:64ch}
|
|
33
|
+
.sub b{color:var(--text);font-weight:600}
|
|
34
|
+
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:10px;margin:28px 0 14px}
|
|
35
|
+
.preset{appearance:none;text-align:left;cursor:pointer;border:1px solid var(--line);
|
|
36
|
+
background:var(--panel);color:var(--text);border-radius:10px;padding:12px 14px;
|
|
37
|
+
transition:border-color .15s,transform .15s}
|
|
38
|
+
.preset:hover{border-color:var(--cyan);transform:translateY(-1px)}
|
|
39
|
+
.preset small{display:block;color:var(--dim);margin-top:3px}
|
|
40
|
+
.preset .tag{float:right;font:11px/1 ui-monospace,monospace;padding:3px 7px;border-radius:6px;margin-left:8px}
|
|
41
|
+
.tag.allow{color:var(--allow);border:1px solid var(--allow)}
|
|
42
|
+
.tag.prepare{color:var(--prepare);border:1px solid var(--prepare)}
|
|
43
|
+
.tag.block{color:var(--block);border:1px solid var(--block)}
|
|
44
|
+
.tag.confirm{color:var(--confirm);border:1px solid var(--confirm)}
|
|
45
|
+
label{display:block;color:var(--muted);font-size:13px;margin:18px 0 6px}
|
|
46
|
+
textarea{width:100%;min-height:150px;resize:vertical;background:#0b101d;color:var(--text);
|
|
47
|
+
border:1px solid var(--line);border-radius:10px;padding:12px 14px;
|
|
48
|
+
font:13px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace}
|
|
49
|
+
textarea:focus{outline:none;border-color:var(--violet)}
|
|
50
|
+
.run{margin-top:12px;cursor:pointer;border:none;border-radius:10px;padding:12px 22px;
|
|
51
|
+
font-weight:600;font-size:15px;color:#fff;
|
|
52
|
+
background:linear-gradient(90deg,var(--violet),var(--cyan))}
|
|
53
|
+
.run:disabled{opacity:.55;cursor:wait}
|
|
54
|
+
#result{margin-top:26px}
|
|
55
|
+
.card{border-radius:12px;border:1px solid var(--line);background:var(--panel);overflow:hidden}
|
|
56
|
+
.verdict{display:flex;align-items:center;gap:12px;padding:16px 18px;font-size:18px;font-weight:700;
|
|
57
|
+
border-left:6px solid var(--dim)}
|
|
58
|
+
.verdict .lvl{font:12px/1 ui-monospace,monospace;font-weight:400;color:var(--muted)}
|
|
59
|
+
.v-ALLOW{border-left-color:var(--allow);color:var(--allow)}
|
|
60
|
+
.v-BLOCK{border-left-color:var(--block);color:var(--block)}
|
|
61
|
+
.v-REQUIRE_CONFIRMATION{border-left-color:var(--confirm);color:var(--confirm)}
|
|
62
|
+
.v-PREPARE_ONLY{border-left-color:var(--prepare);color:var(--prepare)}
|
|
63
|
+
.v-ERROR{border-left-color:var(--block);color:var(--block)}
|
|
64
|
+
.body{padding:2px 18px 16px;color:var(--muted)}
|
|
65
|
+
.body ul{margin:8px 0;padding-left:20px}
|
|
66
|
+
details{border-top:1px solid var(--line);padding:10px 18px}
|
|
67
|
+
summary{cursor:pointer;color:var(--dim);font-size:13px}
|
|
68
|
+
pre{overflow:auto;font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--muted);margin:10px 0 4px}
|
|
69
|
+
footer{margin-top:44px;color:var(--dim);font-size:13px;border-top:1px solid var(--line);padding-top:16px}
|
|
70
|
+
footer a{color:var(--muted)}
|
|
71
|
+
.spin{display:inline-block;width:14px;height:14px;border:2px solid #fff5;border-top-color:#fff;
|
|
72
|
+
border-radius:50%;animation:r .7s linear infinite;vertical-align:-2px;margin-right:8px}
|
|
73
|
+
@keyframes r{to{transform:rotate(360deg)}}
|
|
74
|
+
</style>
|
|
75
|
+
</head>
|
|
76
|
+
<body>
|
|
77
|
+
<div class="wrap">
|
|
78
|
+
<header>
|
|
79
|
+
<h1><span class="shield">🛡 SafeHands</span> live policy demo</h1>
|
|
80
|
+
<span class="chip">Pharos Pacific Mainnet · 1672</span>
|
|
81
|
+
<span class="chip">zero-custody · read-only</span>
|
|
82
|
+
</header>
|
|
83
|
+
<p class="sub">Every button below sends a real agent intent to the <b>live deterministic policy engine</b> running behind this page (<code>POST /tools/safehands_preflight_check</code>). No wallet, no keys, no transaction: this is the check an AI agent runs <b>before</b> it signs anything on Pharos.</p>
|
|
84
|
+
|
|
85
|
+
<div class="grid" id="presets"></div>
|
|
86
|
+
|
|
87
|
+
<label for="intent">Agent intent (editable JSON)</label>
|
|
88
|
+
<textarea id="intent" spellcheck="false"></textarea>
|
|
89
|
+
<button class="run" id="run">Run preflight</button>
|
|
90
|
+
|
|
91
|
+
<div id="result"></div>
|
|
92
|
+
|
|
93
|
+
<footer>
|
|
94
|
+
SafeHands never holds keys and never signs for you. ·
|
|
95
|
+
<a href="https://github.com/SZtch/safehands-pharos" rel="noopener">GitHub</a> ·
|
|
96
|
+
<a href="https://www.pharosscan.xyz/address/0x428e02bf85412e7242d991cd6725ec59e8b06c8d" rel="noopener">Registry contract</a> ·
|
|
97
|
+
<a href="https://www.pharosscan.xyz/address/0x71a7a87b3b1ab6d86204cad691bb32fd75b4588c" rel="noopener">Attestation contract</a>
|
|
98
|
+
</footer>
|
|
99
|
+
</div>
|
|
100
|
+
<script>
|
|
101
|
+
(function(){
|
|
102
|
+
"use strict";
|
|
103
|
+
var PRESETS = [
|
|
104
|
+
{ name: "Unlimited approval", hint: "agent grants a spender forever", tag: "BLOCK", cls: "block",
|
|
105
|
+
intent: { actionType: "approve_token", chainId: 1672, approvalToken: "USDC",
|
|
106
|
+
spender: "0x000000000000000000000000000000000000dEaD", approvalAmount: "max" } },
|
|
107
|
+
{ name: "Wrong chain", hint: "agent targets Ethereum, not Pharos", tag: "BLOCK", cls: "block",
|
|
108
|
+
intent: { actionType: "send_payment", chainId: 1, amount: "0.5", amountUnit: "PROS",
|
|
109
|
+
recipient: "0x0000000000000000000000000000000000000001" } },
|
|
110
|
+
{ name: "Safe small payment", hint: "0.001 PROS within policy: safe; sign it yourself", tag: "PREPARE", cls: "prepare",
|
|
111
|
+
intent: { actionType: "send_payment", chainId: 1672, isMainnet: true, amount: "0.001",
|
|
112
|
+
amountUnit: "PROS", recipient: "0x0000000000000000000000000000000000000001" } },
|
|
113
|
+
{ name: "Unregistered RWA token", hint: "tokenized asset not in registry", tag: "CONFIRM", cls: "confirm",
|
|
114
|
+
intent: { actionType: "approve_token", chainId: 1672, isMainnet: true, approvalAmount: "25",
|
|
115
|
+
tokenAddress: "0x00000000000000000000000000000000000000aa",
|
|
116
|
+
spender: "0x0000000000000000000000000000000000000001", spenderVerified: false } },
|
|
117
|
+
{ name: "Malicious x402 endpoint", hint: "payment to an internal IP", tag: "BLOCK", cls: "block",
|
|
118
|
+
intent: { actionType: "x402_pay_and_fetch", chainId: 1672,
|
|
119
|
+
url: "http://169.254.169.254/latest/meta-data", paymentAmountUsdc: "0.01" } },
|
|
120
|
+
{ name: "Oversized settlement", hint: "5 USDC over the x402 policy cap", tag: "BLOCK", cls: "block",
|
|
121
|
+
intent: { actionType: "x402_pay_and_fetch", chainId: 1672, isMainnet: true,
|
|
122
|
+
url: "https://rpc.pharos.xyz/", paymentAmountUsdc: "5" } }
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
var box = document.getElementById("intent");
|
|
126
|
+
var out = document.getElementById("result");
|
|
127
|
+
var run = document.getElementById("run");
|
|
128
|
+
var grid = document.getElementById("presets");
|
|
129
|
+
|
|
130
|
+
PRESETS.forEach(function (p) {
|
|
131
|
+
var b = document.createElement("button");
|
|
132
|
+
b.className = "preset";
|
|
133
|
+
b.innerHTML = "<span class='tag " + p.cls + "'>" + p.tag + "</span><strong>" + p.name +
|
|
134
|
+
"</strong><small>" + p.hint + "</small>";
|
|
135
|
+
b.addEventListener("click", function () {
|
|
136
|
+
box.value = JSON.stringify(p.intent, null, 2);
|
|
137
|
+
submit();
|
|
138
|
+
});
|
|
139
|
+
grid.appendChild(b);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
box.value = JSON.stringify(PRESETS[0].intent, null, 2);
|
|
143
|
+
|
|
144
|
+
function esc(s) {
|
|
145
|
+
return String(s).replace(/[&<>"]/g, function (c) {
|
|
146
|
+
return { "&": "&", "<": "<", ">": ">", '"': """ }[c];
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function list(title, items) {
|
|
151
|
+
if (!items || !items.length) return "";
|
|
152
|
+
return "<div><b>" + title + "</b><ul>" + items.map(function (r) {
|
|
153
|
+
return "<li>" + esc(r) + "</li>";
|
|
154
|
+
}).join("") + "</ul></div>";
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function render(payload) {
|
|
158
|
+
var html;
|
|
159
|
+
if (payload && payload.success && payload.data) {
|
|
160
|
+
var d = payload.data;
|
|
161
|
+
var decision = d.decision || d.guardianDecision || "UNKNOWN";
|
|
162
|
+
html =
|
|
163
|
+
"<div class='card'>" +
|
|
164
|
+
"<div class='verdict v-" + esc(decision) + "'>" + esc(decision) +
|
|
165
|
+
(d.riskLevel ? " <span class='lvl'>risk: " + esc(d.riskLevel) + "</span>" : "") + "</div>" +
|
|
166
|
+
"<div class='body'>" + list("Why", d.reasons) + list("Required actions", d.requiredActions) +
|
|
167
|
+
(!d.reasons || !d.reasons.length ? "<div>All policy checks passed for this intent.</div>" : "") +
|
|
168
|
+
"</div>" +
|
|
169
|
+
"<details><summary>Raw response</summary><pre>" + esc(JSON.stringify(payload, null, 2)) + "</pre></details>" +
|
|
170
|
+
"</div>";
|
|
171
|
+
} else {
|
|
172
|
+
var err = (payload && payload.error) || {};
|
|
173
|
+
html =
|
|
174
|
+
"<div class='card'>" +
|
|
175
|
+
"<div class='verdict v-ERROR'>" + esc(err.code || "ERROR") + "</div>" +
|
|
176
|
+
"<div class='body'>" + esc(err.message || "Request failed.") + "</div>" +
|
|
177
|
+
"<details><summary>Raw response</summary><pre>" + esc(JSON.stringify(payload, null, 2)) + "</pre></details>" +
|
|
178
|
+
"</div>";
|
|
179
|
+
}
|
|
180
|
+
out.innerHTML = html;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function submit() {
|
|
184
|
+
var body;
|
|
185
|
+
try { body = JSON.parse(box.value); }
|
|
186
|
+
catch (e) { render({ success: false, error: { code: "INVALID_JSON", message: "The intent is not valid JSON: " + e.message } }); return; }
|
|
187
|
+
run.disabled = true;
|
|
188
|
+
run.innerHTML = "<span class='spin'></span>Checking…";
|
|
189
|
+
fetch("/tools/safehands_preflight_check", {
|
|
190
|
+
method: "POST",
|
|
191
|
+
headers: { "content-type": "application/json" },
|
|
192
|
+
body: JSON.stringify(body)
|
|
193
|
+
})
|
|
194
|
+
.then(function (r) { return r.json(); })
|
|
195
|
+
.then(render)
|
|
196
|
+
.catch(function (e) { render({ success: false, error: { code: "NETWORK_ERROR", message: String(e) } }); })
|
|
197
|
+
.then(function () { run.disabled = false; run.textContent = "Run preflight"; });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
run.addEventListener("click", submit);
|
|
201
|
+
})();
|
|
202
|
+
</script>
|
|
203
|
+
</body>
|
|
204
|
+
</html>
|
|
205
|
+
`;
|
|
206
|
+
//# sourceMappingURL=consolePage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consolePage.js","sourceRoot":"","sources":["../../src/api/consolePage.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,2EAA2E;AAC3E,yEAAyE;AACzE,0EAA0E;AAC1E,gEAAgE;AAChE,uEAAuE;AACvE,2EAA2E;AAE3E,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqM7B,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type ToolFailure } from "../lib/toolResponse.js";
|
|
2
|
+
import type { QuotaConfig } from "../lib/observability/quota.js";
|
|
3
|
+
/** Railway/containers require binding all interfaces. */
|
|
4
|
+
export declare const BIND_HOST = "0.0.0.0";
|
|
5
|
+
/** Default API port when neither PORT nor GUARDIAN_API_PORT is set. */
|
|
6
|
+
export declare const DEFAULT_PORT = 4022;
|
|
7
|
+
type Env = Record<string, string | undefined>;
|
|
8
|
+
/** Resolve the listen port. Railway injects PORT; never hardcode a fixed port. */
|
|
9
|
+
export declare function resolvePort(env?: Env): number;
|
|
10
|
+
/** True only when NODE_ENV is exactly "production". */
|
|
11
|
+
export declare function isProduction(env?: Env): boolean;
|
|
12
|
+
export interface CorsConfig {
|
|
13
|
+
/** "wildcard" ⇒ allow any origin (safe for a public, read-only API). */
|
|
14
|
+
mode: "wildcard" | "allowlist";
|
|
15
|
+
/** Explicit allowed origins (only when mode === "allowlist"). */
|
|
16
|
+
origins: string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolve CORS from env. `CORS_ORIGIN` or `SAFEHANDS_ALLOWED_ORIGINS` (comma-separated)
|
|
20
|
+
* define an allowlist; either set to `*` (or unset) ⇒ wildcard. The default is wildcard
|
|
21
|
+
* because the hosted API is public and read-only — we never hardcode a private domain.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveCorsConfig(env?: Env): CorsConfig;
|
|
24
|
+
/**
|
|
25
|
+
* The value to send for `Access-Control-Allow-Origin` given the request `Origin`:
|
|
26
|
+
* - wildcard ⇒ "*"
|
|
27
|
+
* - allowlist + origin present + allowed ⇒ the reflected origin
|
|
28
|
+
* - allowlist + no/Origin not allowed ⇒ the first allowlisted origin (never a secret;
|
|
29
|
+
* never wide-open). Returns null only when there is genuinely nothing to advertise.
|
|
30
|
+
*/
|
|
31
|
+
export declare function corsOriginValue(requestOrigin: string | undefined, cfg: CorsConfig): string | null;
|
|
32
|
+
/** Express body-parser limit; small by default (read-only payloads are tiny). */
|
|
33
|
+
export declare function resolveJsonLimit(env?: Env): string;
|
|
34
|
+
export interface RateLimitConfig {
|
|
35
|
+
enabled: boolean;
|
|
36
|
+
windowMs: number;
|
|
37
|
+
max: number;
|
|
38
|
+
}
|
|
39
|
+
/** Lightweight in-memory rate-limit config. Disable with SAFEHANDS_RATE_LIMIT_ENABLED=false. */
|
|
40
|
+
export declare function resolveRateLimitConfig(env?: Env): RateLimitConfig;
|
|
41
|
+
export interface ActivityConfig {
|
|
42
|
+
enabled: boolean;
|
|
43
|
+
capacity: number;
|
|
44
|
+
}
|
|
45
|
+
/** In-memory activity ring config. Enabled by default; capacity clamped 10–5000. */
|
|
46
|
+
export declare function resolveActivityConfig(env?: Env): ActivityConfig;
|
|
47
|
+
export interface RequestLoggingConfig {
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
format: "json";
|
|
50
|
+
}
|
|
51
|
+
/** Structured JSON request log to stdout. Enabled by default; silence via env. */
|
|
52
|
+
export declare function resolveLoggingConfig(env?: Env): RequestLoggingConfig;
|
|
53
|
+
/**
|
|
54
|
+
* Per-tier in-memory quota config. Shares the rate-limit enable/window defaults so
|
|
55
|
+
* existing behavior is preserved: the `anonymous` tier defaults to the base
|
|
56
|
+
* rate-limit max (120/60s). Generous defaults keep smoke tests + reviewer demos green.
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolveQuotaConfig(env?: Env): QuotaConfig;
|
|
59
|
+
export interface ErrorResponse {
|
|
60
|
+
status: number;
|
|
61
|
+
body: ToolFailure;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Map any thrown value to a safe HTTP error response. ZodError ⇒ 400 with field
|
|
65
|
+
* issues. Handler guard messages (requires/invalid/must/unknown) ⇒ 400. Everything
|
|
66
|
+
* else ⇒ 500. **Stack traces are never included**, and in production the 500 message
|
|
67
|
+
* is generic so internal details never leak.
|
|
68
|
+
*/
|
|
69
|
+
export declare function errorResponseBody(err: unknown, opts: {
|
|
70
|
+
production: boolean;
|
|
71
|
+
}): ErrorResponse;
|
|
72
|
+
/** Consistent 404 body for unmatched routes. */
|
|
73
|
+
export declare function notFoundBody(method: string, path: string): ToolFailure;
|
|
74
|
+
export {};
|
|
75
|
+
//# sourceMappingURL=httpHardening.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpHardening.d.ts","sourceRoot":"","sources":["../../src/api/httpHardening.ts"],"names":[],"mappings":"AAUA,OAAO,EAAQ,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAIjE,yDAAyD;AACzD,eAAO,MAAM,SAAS,YAAY,CAAC;AAEnC,uEAAuE;AACvE,eAAO,MAAM,YAAY,OAAO,CAAC;AAEjC,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE9C,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,GAAG,GAAE,GAAiB,GAAG,MAAM,CAI1D;AAED,uDAAuD;AACvD,wBAAgB,YAAY,CAAC,GAAG,GAAE,GAAiB,GAAG,OAAO,CAE5D;AAID,MAAM,WAAW,UAAU;IACzB,wEAAwE;IACxE,IAAI,EAAE,UAAU,GAAG,WAAW,CAAC;IAC/B,iEAAiE;IACjE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,GAAiB,GAAG,UAAU,CAMpE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAIjG;AAID,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,GAAiB,GAAG,MAAM,CAG/D;AAID,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,gGAAgG;AAChG,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,GAAiB,GAAG,eAAe,CAK9E;AAUD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,oFAAoF;AACpF,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,GAAiB,GAAG,cAAc,CAK5E;AAID,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,GAAiB,GAAG,oBAAoB,CAEjF;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,GAAiB,GAAG,WAAW,CAQtE;AAID,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,GAAG,aAAa,CAW5F;AAED,gDAAgD;AAChD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAEtE"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// ─── SafeHands API — Production HTTP Hardening (read-only) ─────
|
|
2
|
+
// Pure, deterministic, offline-testable configuration helpers for the production
|
|
3
|
+
// Express server: PORT/host resolution, env-driven CORS allowlist, JSON body limit,
|
|
4
|
+
// in-memory rate-limit config, a consistent error-response shape (no stack traces in
|
|
5
|
+
// production), and a 404 body. NOTHING here signs, sends, holds keys, or calls the
|
|
6
|
+
// network — it only shapes HTTP behaviour. All inputs come from `env` so tests can
|
|
7
|
+
// pass a synthetic environment.
|
|
8
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
9
|
+
import { ZodError } from "zod";
|
|
10
|
+
import { fail } from "../lib/toolResponse.js";
|
|
11
|
+
const SOURCE = "guardian_api";
|
|
12
|
+
/** Railway/containers require binding all interfaces. */
|
|
13
|
+
export const BIND_HOST = "0.0.0.0";
|
|
14
|
+
/** Default API port when neither PORT nor GUARDIAN_API_PORT is set. */
|
|
15
|
+
export const DEFAULT_PORT = 4022;
|
|
16
|
+
/** Resolve the listen port. Railway injects PORT; never hardcode a fixed port. */
|
|
17
|
+
export function resolvePort(env = process.env) {
|
|
18
|
+
const raw = env.PORT || env.GUARDIAN_API_PORT;
|
|
19
|
+
const n = raw ? parseInt(raw, 10) : DEFAULT_PORT;
|
|
20
|
+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_PORT;
|
|
21
|
+
}
|
|
22
|
+
/** True only when NODE_ENV is exactly "production". */
|
|
23
|
+
export function isProduction(env = process.env) {
|
|
24
|
+
return env.NODE_ENV === "production";
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve CORS from env. `CORS_ORIGIN` or `SAFEHANDS_ALLOWED_ORIGINS` (comma-separated)
|
|
28
|
+
* define an allowlist; either set to `*` (or unset) ⇒ wildcard. The default is wildcard
|
|
29
|
+
* because the hosted API is public and read-only — we never hardcode a private domain.
|
|
30
|
+
*/
|
|
31
|
+
export function resolveCorsConfig(env = process.env) {
|
|
32
|
+
const raw = (env.CORS_ORIGIN ?? env.SAFEHANDS_ALLOWED_ORIGINS ?? "").trim();
|
|
33
|
+
if (raw === "" || raw === "*")
|
|
34
|
+
return { mode: "wildcard", origins: [] };
|
|
35
|
+
const origins = raw.split(",").map((o) => o.trim()).filter(Boolean);
|
|
36
|
+
if (origins.length === 0 || origins.includes("*"))
|
|
37
|
+
return { mode: "wildcard", origins: [] };
|
|
38
|
+
return { mode: "allowlist", origins };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The value to send for `Access-Control-Allow-Origin` given the request `Origin`:
|
|
42
|
+
* - wildcard ⇒ "*"
|
|
43
|
+
* - allowlist + origin present + allowed ⇒ the reflected origin
|
|
44
|
+
* - allowlist + no/Origin not allowed ⇒ the first allowlisted origin (never a secret;
|
|
45
|
+
* never wide-open). Returns null only when there is genuinely nothing to advertise.
|
|
46
|
+
*/
|
|
47
|
+
export function corsOriginValue(requestOrigin, cfg) {
|
|
48
|
+
if (cfg.mode === "wildcard")
|
|
49
|
+
return "*";
|
|
50
|
+
if (requestOrigin && cfg.origins.includes(requestOrigin))
|
|
51
|
+
return requestOrigin;
|
|
52
|
+
return cfg.origins[0] ?? null;
|
|
53
|
+
}
|
|
54
|
+
// ── JSON body limit ───────────────────────────────────────────────────────
|
|
55
|
+
/** Express body-parser limit; small by default (read-only payloads are tiny). */
|
|
56
|
+
export function resolveJsonLimit(env = process.env) {
|
|
57
|
+
const raw = (env.SAFEHANDS_JSON_LIMIT ?? "").trim();
|
|
58
|
+
return raw || "64kb";
|
|
59
|
+
}
|
|
60
|
+
/** Lightweight in-memory rate-limit config. Disable with SAFEHANDS_RATE_LIMIT_ENABLED=false. */
|
|
61
|
+
export function resolveRateLimitConfig(env = process.env) {
|
|
62
|
+
const enabled = env.SAFEHANDS_RATE_LIMIT_ENABLED !== "false";
|
|
63
|
+
const windowMs = toPositiveInt(env.SAFEHANDS_RATE_LIMIT_WINDOW_MS, 60_000);
|
|
64
|
+
const max = toPositiveInt(env.SAFEHANDS_RATE_LIMIT_MAX, 120);
|
|
65
|
+
return { enabled, windowMs, max };
|
|
66
|
+
}
|
|
67
|
+
function toPositiveInt(raw, fallback) {
|
|
68
|
+
if (!raw)
|
|
69
|
+
return fallback;
|
|
70
|
+
const n = parseInt(raw, 10);
|
|
71
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
72
|
+
}
|
|
73
|
+
/** In-memory activity ring config. Enabled by default; capacity clamped 10–5000. */
|
|
74
|
+
export function resolveActivityConfig(env = process.env) {
|
|
75
|
+
const enabled = env.SAFEHANDS_ACTIVITY_ENABLED !== "false";
|
|
76
|
+
const raw = toPositiveInt(env.SAFEHANDS_ACTIVITY_CAPACITY, 500);
|
|
77
|
+
const capacity = Math.min(5000, Math.max(10, raw));
|
|
78
|
+
return { enabled, capacity };
|
|
79
|
+
}
|
|
80
|
+
/** Structured JSON request log to stdout. Enabled by default; silence via env. */
|
|
81
|
+
export function resolveLoggingConfig(env = process.env) {
|
|
82
|
+
return { enabled: env.SAFEHANDS_REQUEST_LOG_ENABLED !== "false", format: "json" };
|
|
83
|
+
}
|
|
84
|
+
// ── Tiered quota (Phase 8B) ────────────────────────────────────────────────
|
|
85
|
+
/**
|
|
86
|
+
* Per-tier in-memory quota config. Shares the rate-limit enable/window defaults so
|
|
87
|
+
* existing behavior is preserved: the `anonymous` tier defaults to the base
|
|
88
|
+
* rate-limit max (120/60s). Generous defaults keep smoke tests + reviewer demos green.
|
|
89
|
+
*/
|
|
90
|
+
export function resolveQuotaConfig(env = process.env) {
|
|
91
|
+
const enabled = env.SAFEHANDS_RATE_LIMIT_ENABLED !== "false";
|
|
92
|
+
const windowMs = toPositiveInt(env.SAFEHANDS_QUOTA_WINDOW_MS ?? env.SAFEHANDS_RATE_LIMIT_WINDOW_MS, 60_000);
|
|
93
|
+
const anonymous = toPositiveInt(env.SAFEHANDS_QUOTA_ANON_MAX ?? env.SAFEHANDS_RATE_LIMIT_MAX, 120);
|
|
94
|
+
const apiKey = toPositiveInt(env.SAFEHANDS_QUOTA_KEY_MAX, 600);
|
|
95
|
+
const agent = toPositiveInt(env.SAFEHANDS_QUOTA_AGENT_MAX, 600);
|
|
96
|
+
const a2a = toPositiveInt(env.SAFEHANDS_QUOTA_A2A_MAX, agent);
|
|
97
|
+
return { enabled, windowMs, max: { anonymous, "api-key": apiKey, agent, a2a } };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Map any thrown value to a safe HTTP error response. ZodError ⇒ 400 with field
|
|
101
|
+
* issues. Handler guard messages (requires/invalid/must/unknown) ⇒ 400. Everything
|
|
102
|
+
* else ⇒ 500. **Stack traces are never included**, and in production the 500 message
|
|
103
|
+
* is generic so internal details never leak.
|
|
104
|
+
*/
|
|
105
|
+
export function errorResponseBody(err, opts) {
|
|
106
|
+
if (err instanceof ZodError) {
|
|
107
|
+
const message = err.issues.map((i) => `${i.path.join(".") || "input"}: ${i.message}`).join("; ");
|
|
108
|
+
return { status: 400, body: fail("VALIDATION_ERROR", message, false, SOURCE) };
|
|
109
|
+
}
|
|
110
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
111
|
+
const isClient = /requires|invalid|must|unknown/i.test(message);
|
|
112
|
+
if (isClient)
|
|
113
|
+
return { status: 400, body: fail("BAD_REQUEST", message, false, SOURCE) };
|
|
114
|
+
// 500-class: never echo internal error text in production.
|
|
115
|
+
const safeMessage = opts.production ? "Internal server error." : message;
|
|
116
|
+
return { status: 500, body: fail("INTERNAL_ERROR", safeMessage, false, SOURCE) };
|
|
117
|
+
}
|
|
118
|
+
/** Consistent 404 body for unmatched routes. */
|
|
119
|
+
export function notFoundBody(method, path) {
|
|
120
|
+
return fail("NOT_FOUND", `No route for ${method} ${path}.`, false, SOURCE);
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=httpHardening.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpHardening.js","sourceRoot":"","sources":["../../src/api/httpHardening.ts"],"names":[],"mappings":"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"}
|