@agirails/sdk 3.2.0 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +12 -14
- package/dist/ACTPClient.d.ts +8 -11
- package/dist/ACTPClient.d.ts.map +1 -1
- package/dist/ACTPClient.js +79 -20
- package/dist/ACTPClient.js.map +1 -1
- package/dist/__tests__/helpers/mockX402Server.d.ts +67 -0
- package/dist/__tests__/helpers/mockX402Server.d.ts.map +1 -0
- package/dist/__tests__/helpers/mockX402Server.js +121 -0
- package/dist/__tests__/helpers/mockX402Server.js.map +1 -0
- package/dist/adapters/BaseAdapter.d.ts +7 -1
- package/dist/adapters/BaseAdapter.d.ts.map +1 -1
- package/dist/adapters/BaseAdapter.js +11 -6
- package/dist/adapters/BaseAdapter.js.map +1 -1
- package/dist/adapters/BasicAdapter.d.ts.map +1 -1
- package/dist/adapters/BasicAdapter.js +12 -2
- package/dist/adapters/BasicAdapter.js.map +1 -1
- package/dist/adapters/StandardAdapter.d.ts.map +1 -1
- package/dist/adapters/StandardAdapter.js +12 -2
- package/dist/adapters/StandardAdapter.js.map +1 -1
- package/dist/adapters/X402Adapter.d.ts +161 -199
- package/dist/adapters/X402Adapter.d.ts.map +1 -1
- package/dist/adapters/X402Adapter.js +603 -414
- package/dist/adapters/X402Adapter.js.map +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/api/agirailsApp.d.ts +21 -1
- package/dist/api/agirailsApp.d.ts.map +1 -1
- package/dist/api/agirailsApp.js.map +1 -1
- package/dist/builders/CounterAcceptBuilder.d.ts +96 -0
- package/dist/builders/CounterAcceptBuilder.d.ts.map +1 -0
- package/dist/builders/CounterAcceptBuilder.js +226 -0
- package/dist/builders/CounterAcceptBuilder.js.map +1 -0
- package/dist/builders/CounterOfferBuilder.d.ts +143 -0
- package/dist/builders/CounterOfferBuilder.d.ts.map +1 -0
- package/dist/builders/CounterOfferBuilder.js +329 -0
- package/dist/builders/CounterOfferBuilder.js.map +1 -0
- package/dist/builders/DeliveryProofBuilder.d.ts.map +1 -1
- package/dist/builders/DeliveryProofBuilder.js +3 -2
- package/dist/builders/DeliveryProofBuilder.js.map +1 -1
- package/dist/builders/QuoteBuilder.d.ts.map +1 -1
- package/dist/builders/QuoteBuilder.js +8 -3
- package/dist/builders/QuoteBuilder.js.map +1 -1
- package/dist/builders/index.d.ts +2 -0
- package/dist/builders/index.d.ts.map +1 -1
- package/dist/builders/index.js +7 -1
- package/dist/builders/index.js.map +1 -1
- package/dist/cli/agirails.js +34 -6
- package/dist/cli/agirails.js.map +1 -1
- package/dist/cli/commands/autopublish.js +9 -1
- package/dist/cli/commands/autopublish.js.map +1 -1
- package/dist/cli/commands/config.js +1 -12
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/deploy-env.js +1 -1
- package/dist/cli/commands/deploy-env.js.map +1 -1
- package/dist/cli/commands/diff.js +38 -4
- package/dist/cli/commands/diff.js.map +1 -1
- package/dist/cli/commands/health.js +24 -6
- package/dist/cli/commands/health.js.map +1 -1
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +100 -7
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/pay.d.ts.map +1 -1
- package/dist/cli/commands/pay.js +23 -0
- package/dist/cli/commands/pay.js.map +1 -1
- package/dist/cli/commands/publish.d.ts +34 -0
- package/dist/cli/commands/publish.d.ts.map +1 -1
- package/dist/cli/commands/publish.js +266 -83
- package/dist/cli/commands/publish.js.map +1 -1
- package/dist/cli/commands/pull.js +3 -1
- package/dist/cli/commands/pull.js.map +1 -1
- package/dist/cli/commands/receipt.d.ts +17 -3
- package/dist/cli/commands/receipt.d.ts.map +1 -1
- package/dist/cli/commands/receipt.js +95 -33
- package/dist/cli/commands/receipt.js.map +1 -1
- package/dist/cli/commands/repair.d.ts +23 -0
- package/dist/cli/commands/repair.d.ts.map +1 -0
- package/dist/cli/commands/repair.js +210 -0
- package/dist/cli/commands/repair.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +38 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +308 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/test.d.ts.map +1 -1
- package/dist/cli/commands/test.js +222 -60
- package/dist/cli/commands/test.js.map +1 -1
- package/dist/cli/commands/tx.js +13 -0
- package/dist/cli/commands/tx.js.map +1 -1
- package/dist/cli/index.js +9 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/receiptUpload.d.ts +52 -0
- package/dist/cli/receiptUpload.d.ts.map +1 -0
- package/dist/cli/receiptUpload.js +134 -0
- package/dist/cli/receiptUpload.js.map +1 -0
- package/dist/cli/utils/banner.d.ts +31 -0
- package/dist/cli/utils/banner.d.ts.map +1 -0
- package/dist/cli/utils/banner.js +92 -0
- package/dist/cli/utils/banner.js.map +1 -0
- package/dist/cli/utils/config.d.ts +0 -2
- package/dist/cli/utils/config.d.ts.map +1 -1
- package/dist/cli/utils/config.js +40 -25
- package/dist/cli/utils/config.js.map +1 -1
- package/dist/cli/utils/output.d.ts +2 -0
- package/dist/cli/utils/output.d.ts.map +1 -1
- package/dist/cli/utils/output.js +7 -1
- package/dist/cli/utils/output.js.map +1 -1
- package/dist/cli/utils/share.d.ts +51 -0
- package/dist/cli/utils/share.d.ts.map +1 -0
- package/dist/cli/utils/share.js +133 -0
- package/dist/cli/utils/share.js.map +1 -0
- package/dist/config/agirailsmd.d.ts.map +1 -1
- package/dist/config/agirailsmd.js +2 -1
- package/dist/config/agirailsmd.js.map +1 -1
- package/dist/config/agirailsmdV4.d.ts +46 -1
- package/dist/config/agirailsmdV4.d.ts.map +1 -1
- package/dist/config/agirailsmdV4.js +65 -8
- package/dist/config/agirailsmdV4.js.map +1 -1
- package/dist/config/defaults.d.ts +12 -2
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +19 -3
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/networks.d.ts +7 -0
- package/dist/config/networks.d.ts.map +1 -1
- package/dist/config/networks.js +20 -11
- package/dist/config/networks.js.map +1 -1
- package/dist/config/pendingPublish.d.ts.map +1 -1
- package/dist/config/pendingPublish.js +10 -3
- package/dist/config/pendingPublish.js.map +1 -1
- package/dist/config/publishPipeline.d.ts +23 -1
- package/dist/config/publishPipeline.d.ts.map +1 -1
- package/dist/config/publishPipeline.js +70 -15
- package/dist/config/publishPipeline.js.map +1 -1
- package/dist/config/syncOperations.d.ts.map +1 -1
- package/dist/config/syncOperations.js +4 -2
- package/dist/config/syncOperations.js.map +1 -1
- package/dist/erc8004/ERC8004Bridge.d.ts.map +1 -1
- package/dist/erc8004/ERC8004Bridge.js +0 -1
- package/dist/erc8004/ERC8004Bridge.js.map +1 -1
- package/dist/errors/ACTPError.d.ts +24 -0
- package/dist/errors/ACTPError.d.ts.map +1 -0
- package/dist/errors/ACTPError.js +35 -0
- package/dist/errors/ACTPError.js.map +1 -0
- package/dist/errors/X402Errors.d.ts +106 -0
- package/dist/errors/X402Errors.d.ts.map +1 -0
- package/dist/errors/X402Errors.js +160 -0
- package/dist/errors/X402Errors.js.map +1 -0
- package/dist/errors/index.d.ts +3 -9
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +38 -33
- package/dist/errors/index.js.map +1 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +41 -3
- package/dist/index.js.map +1 -1
- package/dist/level0/Provider.d.ts +5 -0
- package/dist/level0/Provider.d.ts.map +1 -1
- package/dist/level0/ServiceDirectory.d.ts.map +1 -1
- package/dist/level0/ServiceDirectory.js +3 -2
- package/dist/level0/ServiceDirectory.js.map +1 -1
- package/dist/level0/provide.d.ts.map +1 -1
- package/dist/level0/provide.js +11 -8
- package/dist/level0/provide.js.map +1 -1
- package/dist/level0/request.d.ts.map +1 -1
- package/dist/level0/request.js +14 -6
- package/dist/level0/request.js.map +1 -1
- package/dist/level1/Agent.d.ts +28 -1
- package/dist/level1/Agent.d.ts.map +1 -1
- package/dist/level1/Agent.js +89 -12
- package/dist/level1/Agent.js.map +1 -1
- package/dist/level1/pricing/PriceCalculator.d.ts.map +1 -1
- package/dist/level1/pricing/PriceCalculator.js +4 -12
- package/dist/level1/pricing/PriceCalculator.js.map +1 -1
- package/dist/negotiation/BuyerOrchestrator.d.ts +103 -1
- package/dist/negotiation/BuyerOrchestrator.d.ts.map +1 -1
- package/dist/negotiation/BuyerOrchestrator.js +499 -4
- package/dist/negotiation/BuyerOrchestrator.js.map +1 -1
- package/dist/negotiation/DecisionEngine.d.ts +69 -1
- package/dist/negotiation/DecisionEngine.d.ts.map +1 -1
- package/dist/negotiation/DecisionEngine.js +140 -1
- package/dist/negotiation/DecisionEngine.js.map +1 -1
- package/dist/negotiation/PolicyEngine.d.ts +32 -0
- package/dist/negotiation/PolicyEngine.d.ts.map +1 -1
- package/dist/negotiation/PolicyEngine.js.map +1 -1
- package/dist/negotiation/ProviderOrchestrator.d.ts +108 -0
- package/dist/negotiation/ProviderOrchestrator.d.ts.map +1 -0
- package/dist/negotiation/ProviderOrchestrator.js +136 -0
- package/dist/negotiation/ProviderOrchestrator.js.map +1 -0
- package/dist/negotiation/ProviderPolicy.d.ts +143 -0
- package/dist/negotiation/ProviderPolicy.d.ts.map +1 -0
- package/dist/negotiation/ProviderPolicy.js +207 -0
- package/dist/negotiation/ProviderPolicy.js.map +1 -0
- package/dist/negotiation/index.d.ts +8 -1
- package/dist/negotiation/index.d.ts.map +1 -1
- package/dist/negotiation/index.js +8 -1
- package/dist/negotiation/index.js.map +1 -1
- package/dist/negotiation/verifyQuoteOnChain.d.ts +58 -0
- package/dist/negotiation/verifyQuoteOnChain.d.ts.map +1 -0
- package/dist/negotiation/verifyQuoteOnChain.js +83 -0
- package/dist/negotiation/verifyQuoteOnChain.js.map +1 -0
- package/dist/protocol/ACTPKernel.d.ts +4 -1
- package/dist/protocol/ACTPKernel.d.ts.map +1 -1
- package/dist/protocol/ACTPKernel.js +2 -1
- package/dist/protocol/ACTPKernel.js.map +1 -1
- package/dist/protocol/EventMonitor.d.ts +27 -1
- package/dist/protocol/EventMonitor.d.ts.map +1 -1
- package/dist/protocol/EventMonitor.js +11 -9
- package/dist/protocol/EventMonitor.js.map +1 -1
- package/dist/protocol/ProofGenerator.d.ts.map +1 -1
- package/dist/protocol/ProofGenerator.js +3 -2
- package/dist/protocol/ProofGenerator.js.map +1 -1
- package/dist/runtime/BlockchainRuntime.d.ts +15 -0
- package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
- package/dist/runtime/BlockchainRuntime.js +42 -6
- package/dist/runtime/BlockchainRuntime.js.map +1 -1
- package/dist/runtime/IACTPRuntime.d.ts +35 -0
- package/dist/runtime/IACTPRuntime.d.ts.map +1 -1
- package/dist/runtime/MockRuntime.d.ts +14 -2
- package/dist/runtime/MockRuntime.d.ts.map +1 -1
- package/dist/runtime/MockRuntime.js +55 -22
- package/dist/runtime/MockRuntime.js.map +1 -1
- package/dist/runtime/types/MockState.d.ts +14 -0
- package/dist/runtime/types/MockState.d.ts.map +1 -1
- package/dist/runtime/types/MockState.js.map +1 -1
- package/dist/server/buildX402Server.d.ts +131 -0
- package/dist/server/buildX402Server.d.ts.map +1 -0
- package/dist/server/buildX402Server.js +151 -0
- package/dist/server/buildX402Server.js.map +1 -0
- package/dist/server/index.d.ts +33 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +36 -0
- package/dist/server/index.js.map +1 -0
- package/dist/transport/QuoteChannel.d.ts +201 -0
- package/dist/transport/QuoteChannel.d.ts.map +1 -0
- package/dist/transport/QuoteChannel.js +358 -0
- package/dist/transport/QuoteChannel.js.map +1 -0
- package/dist/types/adapter.d.ts +64 -34
- package/dist/types/adapter.d.ts.map +1 -1
- package/dist/types/adapter.js +6 -1
- package/dist/types/adapter.js.map +1 -1
- package/dist/types/eip712.d.ts +20 -0
- package/dist/types/eip712.d.ts.map +1 -1
- package/dist/types/x402.d.ts +8 -8
- package/dist/utils/security.d.ts.map +1 -1
- package/dist/utils/security.js +4 -6
- package/dist/utils/security.js.map +1 -1
- package/dist/wallet/AutoWalletProvider.d.ts +45 -1
- package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
- package/dist/wallet/AutoWalletProvider.js +154 -1
- package/dist/wallet/AutoWalletProvider.js.map +1 -1
- package/dist/wallet/EOAWalletProvider.d.ts +13 -1
- package/dist/wallet/EOAWalletProvider.d.ts.map +1 -1
- package/dist/wallet/EOAWalletProvider.js +24 -0
- package/dist/wallet/EOAWalletProvider.js.map +1 -1
- package/dist/wallet/IWalletProvider.d.ts +34 -0
- package/dist/wallet/IWalletProvider.d.ts.map +1 -1
- package/dist/wallet/SmartWalletRouter.d.ts.map +1 -1
- package/dist/wallet/SmartWalletRouter.js +3 -1
- package/dist/wallet/SmartWalletRouter.js.map +1 -1
- package/dist/wallet/aa/BundlerClient.js +8 -4
- package/dist/wallet/aa/BundlerClient.js.map +1 -1
- package/dist/wallet/aa/DualNonceManager.d.ts +4 -1
- package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
- package/dist/wallet/aa/DualNonceManager.js +3 -0
- package/dist/wallet/aa/DualNonceManager.js.map +1 -1
- package/dist/wallet/keystore.d.ts.map +1 -1
- package/dist/wallet/keystore.js +6 -4
- package/dist/wallet/keystore.js.map +1 -1
- package/package.json +31 -3
- package/dist/adapters/BeginnerAdapter.d.ts +0 -152
- package/dist/adapters/BeginnerAdapter.d.ts.map +0 -1
- package/dist/adapters/BeginnerAdapter.js +0 -168
- package/dist/adapters/BeginnerAdapter.js.map +0 -1
- package/dist/adapters/IntermediateAdapter.d.ts +0 -211
- package/dist/adapters/IntermediateAdapter.d.ts.map +0 -1
- package/dist/adapters/IntermediateAdapter.js +0 -260
- package/dist/adapters/IntermediateAdapter.js.map +0 -1
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `actp serve` — long-running provider daemon.
|
|
4
|
+
*
|
|
5
|
+
* Loads a ProviderPolicy JSON, constructs a ProviderOrchestrator, opens
|
|
6
|
+
* an HTTP server on the configured port that exposes the AIP-2.1 quote
|
|
7
|
+
* channel surface (`POST /quote-channel/{chainId}/{txId}` per §8.2),
|
|
8
|
+
* and routes incoming buyer counter-offers through
|
|
9
|
+
* orchestrator.evaluateCounter().
|
|
10
|
+
*
|
|
11
|
+
* Scope (v1):
|
|
12
|
+
* - accept + verify incoming counter-offers via QuoteChannelHandler
|
|
13
|
+
* - log the policy verdict (accept / reject) per round
|
|
14
|
+
* - emit a one-line health response on `GET /`
|
|
15
|
+
*
|
|
16
|
+
* Out of scope for v1 (Phase 5):
|
|
17
|
+
* - on-chain event listening (no automatic submitQuote on incoming
|
|
18
|
+
* INITIATED txs — caller still drives via Agent.ts or manual code)
|
|
19
|
+
* - sending CounterAcceptMessage back to buyer (no reverse-endpoint
|
|
20
|
+
* discovery yet — print the verdict, operator handles delivery)
|
|
21
|
+
*
|
|
22
|
+
* @module cli/commands/serve
|
|
23
|
+
*/
|
|
24
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
27
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
28
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
29
|
+
}
|
|
30
|
+
Object.defineProperty(o, k2, desc);
|
|
31
|
+
}) : (function(o, m, k, k2) {
|
|
32
|
+
if (k2 === undefined) k2 = k;
|
|
33
|
+
o[k2] = m[k];
|
|
34
|
+
}));
|
|
35
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
36
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
37
|
+
}) : function(o, v) {
|
|
38
|
+
o["default"] = v;
|
|
39
|
+
});
|
|
40
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.readBody = exports.createServeCommand = void 0;
|
|
49
|
+
const commander_1 = require("commander");
|
|
50
|
+
const http_1 = require("http");
|
|
51
|
+
const fs_1 = require("fs");
|
|
52
|
+
const ethers_1 = require("ethers");
|
|
53
|
+
const output_1 = require("../utils/output");
|
|
54
|
+
const client_1 = require("../utils/client");
|
|
55
|
+
const networks_1 = require("../../config/networks");
|
|
56
|
+
const BlockchainRuntime_1 = require("../../runtime/BlockchainRuntime");
|
|
57
|
+
const MockRuntime_1 = require("../../runtime/MockRuntime");
|
|
58
|
+
const MockStateManager_1 = require("../../runtime/MockStateManager");
|
|
59
|
+
const ProviderOrchestrator_1 = require("../../negotiation/ProviderOrchestrator");
|
|
60
|
+
const QuoteChannel_1 = require("../../transport/QuoteChannel");
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Command
|
|
63
|
+
// ============================================================================
|
|
64
|
+
function createServeCommand() {
|
|
65
|
+
const cmd = new commander_1.Command('serve')
|
|
66
|
+
.description('Run a long-running provider daemon (AIP-2.1 quote channel)')
|
|
67
|
+
.requiredOption('--policy <path>', 'Path to ProviderPolicy JSON file')
|
|
68
|
+
.option('--port <num>', 'HTTP port to listen on', '8787')
|
|
69
|
+
.option('--network <name>', 'Network — base-sepolia | base-mainnet | mock', 'base-sepolia')
|
|
70
|
+
.option('--rpc <url>', 'Custom RPC URL override (testnet/mainnet only)')
|
|
71
|
+
.option('--mock', 'Run with MockRuntime instead of BlockchainRuntime (for local testing)')
|
|
72
|
+
.action(async (options) => {
|
|
73
|
+
const output = new output_1.Output('human');
|
|
74
|
+
try {
|
|
75
|
+
await runServe(options, output);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
const structured = (0, client_1.mapError)(error);
|
|
79
|
+
output.errorResult({
|
|
80
|
+
code: structured.code,
|
|
81
|
+
message: structured.message,
|
|
82
|
+
details: structured.details,
|
|
83
|
+
});
|
|
84
|
+
process.exit(output_1.ExitCode.ERROR);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
return cmd;
|
|
88
|
+
}
|
|
89
|
+
exports.createServeCommand = createServeCommand;
|
|
90
|
+
async function runServe(options, output) {
|
|
91
|
+
// 1. Load + validate policy.
|
|
92
|
+
if (!(0, fs_1.existsSync)(options.policy)) {
|
|
93
|
+
throw new Error(`Policy file not found: ${options.policy}`);
|
|
94
|
+
}
|
|
95
|
+
const policy = JSON.parse((0, fs_1.readFileSync)(options.policy, 'utf-8'));
|
|
96
|
+
// 2. Build runtime AND keep a direct signer reference for the
|
|
97
|
+
// orchestrator. BlockchainRuntime makes its signer private, so we
|
|
98
|
+
// hold ours alongside.
|
|
99
|
+
let runtime;
|
|
100
|
+
let kernelAddress;
|
|
101
|
+
let chainId;
|
|
102
|
+
let signerAddress;
|
|
103
|
+
let orchestratorSigner;
|
|
104
|
+
if (options.mock) {
|
|
105
|
+
const stateMgr = new MockStateManager_1.MockStateManager(process.cwd() + '/.actp-serve');
|
|
106
|
+
runtime = new MockRuntime_1.MockRuntime(stateMgr);
|
|
107
|
+
kernelAddress = '0x' + '0'.repeat(40);
|
|
108
|
+
chainId = 84532;
|
|
109
|
+
const mockWallet = ethers_1.Wallet.createRandom();
|
|
110
|
+
orchestratorSigner = mockWallet;
|
|
111
|
+
signerAddress = mockWallet.address;
|
|
112
|
+
output.info('Running in MOCK mode — no real on-chain interaction.');
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
const networkCfg = (0, networks_1.getNetwork)(options.network);
|
|
116
|
+
if (options.rpc)
|
|
117
|
+
networkCfg.rpcUrl = options.rpc;
|
|
118
|
+
const { resolvePrivateKey } = await Promise.resolve().then(() => __importStar(require('../../wallet/keystore')));
|
|
119
|
+
const tier = options.network.includes('mainnet') ? 'mainnet'
|
|
120
|
+
: options.network.includes('sepolia') ? 'testnet' : 'mock';
|
|
121
|
+
const privateKey = await resolvePrivateKey(process.cwd(), {
|
|
122
|
+
network: tier,
|
|
123
|
+
});
|
|
124
|
+
if (!privateKey) {
|
|
125
|
+
throw new Error('No wallet found. Run `actp init` or set ACTP_KEYSTORE_BASE64.');
|
|
126
|
+
}
|
|
127
|
+
const provider = new ethers_1.JsonRpcProvider(networkCfg.rpcUrl);
|
|
128
|
+
const signer = new ethers_1.Wallet(privateKey, provider);
|
|
129
|
+
orchestratorSigner = signer;
|
|
130
|
+
runtime = new BlockchainRuntime_1.BlockchainRuntime({
|
|
131
|
+
network: options.network,
|
|
132
|
+
signer,
|
|
133
|
+
provider,
|
|
134
|
+
});
|
|
135
|
+
await runtime.initialize();
|
|
136
|
+
kernelAddress = networkCfg.contracts.actpKernel;
|
|
137
|
+
chainId = networkCfg.chainId;
|
|
138
|
+
signerAddress = await signer.getAddress();
|
|
139
|
+
}
|
|
140
|
+
const orchestrator = new ProviderOrchestrator_1.ProviderOrchestrator({
|
|
141
|
+
policy,
|
|
142
|
+
runtime,
|
|
143
|
+
signer: orchestratorSigner,
|
|
144
|
+
kernelAddress,
|
|
145
|
+
chainId,
|
|
146
|
+
});
|
|
147
|
+
// 4. Build channel handler with kernel address per chain.
|
|
148
|
+
const channelHandler = new QuoteChannel_1.QuoteChannelHandler({
|
|
149
|
+
kernelAddressByChainId: {
|
|
150
|
+
[chainId]: kernelAddress,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
// 5. Start HTTP server.
|
|
154
|
+
const port = Number(options.port);
|
|
155
|
+
if (!Number.isFinite(port) || port < 1 || port > 65535) {
|
|
156
|
+
throw new Error(`Invalid port: ${options.port}`);
|
|
157
|
+
}
|
|
158
|
+
const server = (0, http_1.createServer)(async (req, res) => {
|
|
159
|
+
try {
|
|
160
|
+
await routeRequest(req, res, {
|
|
161
|
+
channelHandler,
|
|
162
|
+
orchestrator,
|
|
163
|
+
chainId,
|
|
164
|
+
signerAddress,
|
|
165
|
+
output,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
output.error(`Request handler crashed: ${err instanceof Error ? err.message : String(err)}`);
|
|
170
|
+
res.statusCode = 500;
|
|
171
|
+
res.end('{"error":"internal"}');
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
// Slow-loris hardening: bound the time a client can hold a socket open
|
|
175
|
+
// dribbling out a request body. Without this, `readBody` will sit in
|
|
176
|
+
// its `data` listener until the OS-level keepalive ages out.
|
|
177
|
+
// 10s for headers, 15s for the full request — generous for normal
|
|
178
|
+
// peers (a counter-offer body is well under 4 KiB), tight enough to
|
|
179
|
+
// shed slow-trickle attackers in seconds.
|
|
180
|
+
server.headersTimeout = 10000;
|
|
181
|
+
server.requestTimeout = 15000;
|
|
182
|
+
server.listen(port, () => {
|
|
183
|
+
output.success(`actp serve listening on http://0.0.0.0:${port}`);
|
|
184
|
+
output.print(` Network: ${options.network}${options.mock ? ' (mock)' : ''}`);
|
|
185
|
+
output.print(` Provider: ${signerAddress}`);
|
|
186
|
+
output.print(` Channel base: ${(0, QuoteChannel_1.buildChannelPath)(chainId, '<txId>')}`);
|
|
187
|
+
output.print(` Health: GET /`);
|
|
188
|
+
output.print('');
|
|
189
|
+
output.print('Counter-offers POSTed to /quote-channel/{chainId}/{txId} are verified + evaluated.');
|
|
190
|
+
output.print('Verdicts are logged here; v1 does NOT auto-deliver CounterAcceptMessage back to');
|
|
191
|
+
output.print('the buyer — operator handles that out-of-band (see AIP-2.1 §5.3).');
|
|
192
|
+
output.print('');
|
|
193
|
+
});
|
|
194
|
+
// Graceful shutdown.
|
|
195
|
+
const shutdown = (signal) => {
|
|
196
|
+
output.info(`\n${signal} received — shutting down…`);
|
|
197
|
+
server.close(() => {
|
|
198
|
+
output.success('Stopped.');
|
|
199
|
+
process.exit(0);
|
|
200
|
+
});
|
|
201
|
+
// Force-exit after 5s if connections hang.
|
|
202
|
+
setTimeout(() => process.exit(0), 5000).unref();
|
|
203
|
+
};
|
|
204
|
+
process.once('SIGINT', () => shutdown('SIGINT'));
|
|
205
|
+
process.once('SIGTERM', () => shutdown('SIGTERM'));
|
|
206
|
+
}
|
|
207
|
+
async function routeRequest(req, res, ctx) {
|
|
208
|
+
const url = req.url ?? '/';
|
|
209
|
+
// Health check.
|
|
210
|
+
if (req.method === 'GET' && url === '/') {
|
|
211
|
+
res.statusCode = 200;
|
|
212
|
+
res.setHeader('Content-Type', 'application/json');
|
|
213
|
+
res.end(JSON.stringify({
|
|
214
|
+
status: 'ok',
|
|
215
|
+
provider: ctx.signerAddress,
|
|
216
|
+
chainId: ctx.chainId,
|
|
217
|
+
service: 'actp-serve',
|
|
218
|
+
}));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
// /quote-channel/{chainId}/{txId}.
|
|
222
|
+
const channelMatch = url.match(/^\/quote-channel\/(\d+)\/(0x[a-fA-F0-9]{64})\/?$/);
|
|
223
|
+
if (req.method === 'POST' && channelMatch) {
|
|
224
|
+
const [, chainIdStr, txId] = channelMatch;
|
|
225
|
+
const pathChainId = Number(chainIdStr);
|
|
226
|
+
const body = await readBody(req);
|
|
227
|
+
let payload;
|
|
228
|
+
try {
|
|
229
|
+
payload = JSON.parse(body);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
res.statusCode = 400;
|
|
233
|
+
res.end(JSON.stringify({ accepted: false, reason: 'Invalid JSON' }));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const result = await ctx.channelHandler.handle(payload, { pathChainId, pathTxId: txId });
|
|
237
|
+
// For counter-offers that pass the channel handler, run the
|
|
238
|
+
// orchestrator's policy verdict and log it.
|
|
239
|
+
if (result.status === 201 || result.status === 200) {
|
|
240
|
+
const p = payload;
|
|
241
|
+
if (p?.type === 'agirails.counteroffer.v1' && p.message) {
|
|
242
|
+
try {
|
|
243
|
+
const verdict = await ctx.orchestrator.evaluateCounter(p.message);
|
|
244
|
+
ctx.output.info(`[counter] tx=${txId.slice(0, 12)}… counter=${p.message.counterAmount} → ${verdict.action}: ${verdict.reason}`);
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
ctx.output.warning(`[counter] tx=${txId.slice(0, 12)}… verification failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
res.statusCode = result.status;
|
|
252
|
+
res.setHeader('Content-Type', 'application/json');
|
|
253
|
+
res.end(JSON.stringify(result.body));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
res.statusCode = 404;
|
|
257
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Read a request body with two hard limits — defense in depth alongside
|
|
261
|
+
* the server-level `headersTimeout` / `requestTimeout`:
|
|
262
|
+
* - byte cap (64 KiB) to bound memory
|
|
263
|
+
* - wall-clock deadline (10s) to bound time
|
|
264
|
+
*
|
|
265
|
+
* Both must be enforced here too because a caller could construct the
|
|
266
|
+
* server without our timeouts (e.g. testing with a raw http.Server) and
|
|
267
|
+
* the body cap alone won't shed a peer that sends 1 byte/sec forever.
|
|
268
|
+
*/
|
|
269
|
+
function readBody(req) {
|
|
270
|
+
return new Promise((resolve, reject) => {
|
|
271
|
+
const chunks = [];
|
|
272
|
+
let total = 0;
|
|
273
|
+
let settled = false;
|
|
274
|
+
const MAX_BYTES = 64 * 1024;
|
|
275
|
+
const MAX_MS = 10000;
|
|
276
|
+
const finish = (err, body) => {
|
|
277
|
+
if (settled)
|
|
278
|
+
return;
|
|
279
|
+
settled = true;
|
|
280
|
+
clearTimeout(deadline);
|
|
281
|
+
if (err) {
|
|
282
|
+
try {
|
|
283
|
+
req.destroy();
|
|
284
|
+
}
|
|
285
|
+
catch { /* */ }
|
|
286
|
+
reject(err);
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
resolve(body ?? '');
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
const deadline = setTimeout(() => {
|
|
293
|
+
finish(new Error('Body read timeout'));
|
|
294
|
+
}, MAX_MS);
|
|
295
|
+
req.on('data', (chunk) => {
|
|
296
|
+
total += chunk.length;
|
|
297
|
+
if (total > MAX_BYTES) {
|
|
298
|
+
finish(new Error('Body too large'));
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
chunks.push(chunk);
|
|
302
|
+
});
|
|
303
|
+
req.on('end', () => finish(null, Buffer.concat(chunks).toString('utf-8')));
|
|
304
|
+
req.on('error', (err) => finish(err));
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
exports.readBody = readBody;
|
|
308
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../../src/cli/commands/serve.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,MAAM,EAAiB,MAAM,iBAAiB,CAAC;AAexD,wBAAgB,iBAAiB,IAAI,OAAO,CAwB3C;AA6ED,iBAAe,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0JpD;AAkGD,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
|
@@ -43,6 +43,9 @@ const agirailsmdV4_1 = require("../../config/agirailsmdV4");
|
|
|
43
43
|
const testjobs_1 = require("../testjobs");
|
|
44
44
|
const receipt_1 = require("./receipt");
|
|
45
45
|
const MockRuntime_1 = require("../../runtime/MockRuntime");
|
|
46
|
+
const banner_1 = require("../utils/banner");
|
|
47
|
+
const receiptUpload_1 = require("../receiptUpload");
|
|
48
|
+
const defaults_1 = require("../../config/defaults");
|
|
46
49
|
// ============================================================================
|
|
47
50
|
// Command Definition
|
|
48
51
|
// ============================================================================
|
|
@@ -72,45 +75,104 @@ exports.createTestCommand = createTestCommand;
|
|
|
72
75
|
// ============================================================================
|
|
73
76
|
// Implementation
|
|
74
77
|
// ============================================================================
|
|
78
|
+
/** Sleep helper */
|
|
79
|
+
function sleep(ms) {
|
|
80
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
81
|
+
}
|
|
82
|
+
/** Spinner frames (rotating Unicode circle) */
|
|
83
|
+
const SPINNER_FRAMES = ['◐', '◓', '◑', '◒'];
|
|
84
|
+
/**
|
|
85
|
+
* Run a state transition with rotating spinner animation.
|
|
86
|
+
*
|
|
87
|
+
* On TTY (human mode): prints a spinning line, awaits the work, enforces a
|
|
88
|
+
* minimum visible duration, then rewrites the line with the final icon + timing.
|
|
89
|
+
*
|
|
90
|
+
* On non-TTY / CI: executes the work without animation, emits a single line
|
|
91
|
+
* with actual elapsed time.
|
|
92
|
+
*/
|
|
93
|
+
async function animateState(output, label, message, work, settled = false, minDurationMs = 450) {
|
|
94
|
+
const labelPad = label.padEnd(14);
|
|
95
|
+
const msgPad = message.padEnd(40);
|
|
96
|
+
if (output.mode !== 'human') {
|
|
97
|
+
// Non-human (json/quiet): execute silently, no output
|
|
98
|
+
await work();
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
if (!process.stdout.isTTY) {
|
|
102
|
+
// Non-TTY: no animation, single line after work completes
|
|
103
|
+
const start = performance.now();
|
|
104
|
+
await work();
|
|
105
|
+
const elapsed = Math.round(performance.now() - start);
|
|
106
|
+
const icon = settled ? output_1.fmt.green('✓') : output_1.fmt.cyan('·');
|
|
107
|
+
const lbl = settled ? output_1.fmt.green(output_1.fmt.bold(labelPad)) : output_1.fmt.bold(labelPad);
|
|
108
|
+
console.log(` ${icon} ${lbl} ${msgPad} ${output_1.fmt.dim(`[${elapsed}ms]`)}`);
|
|
109
|
+
return elapsed;
|
|
110
|
+
}
|
|
111
|
+
// TTY: rotating spinner with min duration for visibility
|
|
112
|
+
let frameIdx = 0;
|
|
113
|
+
process.stdout.write(` ${output_1.fmt.cyan(SPINNER_FRAMES[0])} ${output_1.fmt.bold(labelPad)} ${msgPad}`);
|
|
114
|
+
const interval = setInterval(() => {
|
|
115
|
+
frameIdx = (frameIdx + 1) % SPINNER_FRAMES.length;
|
|
116
|
+
process.stdout.write(`\r ${output_1.fmt.cyan(SPINNER_FRAMES[frameIdx])} ${output_1.fmt.bold(labelPad)} ${msgPad}`);
|
|
117
|
+
}, 90);
|
|
118
|
+
const start = performance.now();
|
|
119
|
+
await Promise.all([
|
|
120
|
+
work(),
|
|
121
|
+
sleep(minDurationMs),
|
|
122
|
+
]);
|
|
123
|
+
const elapsed = Math.round(performance.now() - start);
|
|
124
|
+
clearInterval(interval);
|
|
125
|
+
const icon = settled ? output_1.fmt.green('✓') : output_1.fmt.cyan('·');
|
|
126
|
+
const lbl = settled ? output_1.fmt.green(output_1.fmt.bold(labelPad)) : output_1.fmt.bold(labelPad);
|
|
127
|
+
process.stdout.write(`\r ${icon} ${lbl} ${msgPad} ${output_1.fmt.dim(`[${elapsed}ms]`)}\n`);
|
|
128
|
+
return elapsed;
|
|
129
|
+
}
|
|
130
|
+
/** Demo amount for first-TX experience: $10 USDC (fee $0.10, net $9.90) */
|
|
131
|
+
const TEST_TX_AMOUNT_WEI = 10000000n;
|
|
75
132
|
async function runTest(output) {
|
|
76
133
|
// Step 1: Resolve identity file
|
|
77
134
|
const identityPath = (0, config_1.resolveIdentityPath)();
|
|
78
135
|
if (!identityPath) {
|
|
79
|
-
throw new Error('No agent identity file found.\n' +
|
|
80
|
-
'Create a
|
|
81
|
-
'Or
|
|
136
|
+
throw new Error('No agent identity file ({slug}.md) found in this directory.\n' +
|
|
137
|
+
'Create one with a valid AGIRAILS.md v4 frontmatter (name, services, pricing).\n' +
|
|
138
|
+
'Or let an AI assistant generate one: curl -sLO https://www.agirails.app/protocol/AGIRAILS.md');
|
|
82
139
|
}
|
|
83
|
-
|
|
140
|
+
// Parse identity
|
|
141
|
+
const content = fs.readFileSync(identityPath, 'utf-8');
|
|
142
|
+
const config = (0, agirailsmdV4_1.parseAgirailsMdV4)(content);
|
|
143
|
+
const testJob = (0, testjobs_1.selectTestJob)(config.services.map(s => s.type));
|
|
144
|
+
// Render banner + section header (human mode only)
|
|
145
|
+
if (output.mode === 'human') {
|
|
146
|
+
output.print('');
|
|
147
|
+
output.print((0, banner_1.inlineBanner)('ACTP Transaction Lifecycle'));
|
|
148
|
+
output.print('');
|
|
149
|
+
}
|
|
150
|
+
const isTTY = process.stdout.isTTY && output.mode === 'human';
|
|
151
|
+
const totalStart = performance.now();
|
|
152
|
+
const runtime = new MockRuntime_1.MockRuntime();
|
|
153
|
+
// Create synthetic addresses
|
|
154
|
+
const requesterWallet = ethers_1.ethers.Wallet.createRandom();
|
|
155
|
+
let providerAddress;
|
|
84
156
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// Amount in USDC wei (6 decimals)
|
|
105
|
-
const amountWei = BigInt(Math.round(config.pricing.base * 1000000));
|
|
106
|
-
const amountStr = amountWei.toString();
|
|
107
|
-
// Mint balance for requester
|
|
108
|
-
await runtime.mintTokens(requesterWallet.address, amountStr);
|
|
109
|
-
// Step 4a: createTransaction (INITIATED)
|
|
110
|
-
const deadline = runtime.time.now() + 86400; // +24h
|
|
111
|
-
const disputeWindow = parseDuration(config.sla.dispute_window);
|
|
112
|
-
const escrowStart = performance.now();
|
|
113
|
-
const txId = await runtime.createTransaction({
|
|
157
|
+
providerAddress = (0, config_1.loadConfig)().address || ethers_1.ethers.Wallet.createRandom().address;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
providerAddress = ethers_1.ethers.Wallet.createRandom().address;
|
|
161
|
+
}
|
|
162
|
+
// Hardcoded $10 demo amount — demonstrates fee math (fee $0.10, net $9.90)
|
|
163
|
+
const amountWei = TEST_TX_AMOUNT_WEI;
|
|
164
|
+
const amountStr = amountWei.toString();
|
|
165
|
+
await runtime.mintTokens(requesterWallet.address, amountStr);
|
|
166
|
+
const deadline = runtime.time.now() + 86400;
|
|
167
|
+
const disputeWindow = parseDuration(config.sla.dispute_window);
|
|
168
|
+
// Shared txId/escrowId across state closures
|
|
169
|
+
let txId = '';
|
|
170
|
+
let escrowId = '';
|
|
171
|
+
let escrowLockMs = 0;
|
|
172
|
+
let settlementMs = 0;
|
|
173
|
+
// === INITIATED ===
|
|
174
|
+
await animateState(output, 'INITIATED', 'Requester created transaction', async () => {
|
|
175
|
+
txId = await runtime.createTransaction({
|
|
114
176
|
provider: providerAddress,
|
|
115
177
|
requester: requesterWallet.address,
|
|
116
178
|
amount: amountStr,
|
|
@@ -118,42 +180,142 @@ async function runTest(output) {
|
|
|
118
180
|
disputeWindow,
|
|
119
181
|
serviceDescription: testJob.title,
|
|
120
182
|
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
183
|
+
});
|
|
184
|
+
// === QUOTED === (educational: demonstrates full state machine)
|
|
185
|
+
await animateState(output, 'QUOTED', `${config.name} quoted $10.00 USDC`, async () => {
|
|
186
|
+
await runtime.transitionState(txId, 'QUOTED');
|
|
187
|
+
});
|
|
188
|
+
// === COMMITTED ===
|
|
189
|
+
await animateState(output, 'COMMITTED', 'Escrow funded — $10.00 locked', async () => {
|
|
190
|
+
const commitStart = performance.now();
|
|
191
|
+
escrowId = await runtime.linkEscrow(txId, amountStr);
|
|
192
|
+
escrowLockMs = performance.now() - commitStart;
|
|
193
|
+
});
|
|
194
|
+
// === IN_PROGRESS ===
|
|
195
|
+
await animateState(output, 'IN_PROGRESS', `${config.name} working...`, async () => {
|
|
125
196
|
await runtime.transitionState(txId, 'IN_PROGRESS');
|
|
126
|
-
|
|
197
|
+
}, false, 700); // longer delay — simulates "doing work"
|
|
198
|
+
// === DELIVERED ===
|
|
199
|
+
await animateState(output, 'DELIVERED', 'Delivery proof submitted', async () => {
|
|
127
200
|
await runtime.transitionState(txId, 'DELIVERED');
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
201
|
+
});
|
|
202
|
+
// Advance time past dispute window (silent — not a real state transition)
|
|
203
|
+
await runtime.time.advanceTime(disputeWindow + 1);
|
|
204
|
+
// === SETTLED ===
|
|
205
|
+
await animateState(output, 'SETTLED', `Escrow released → ${config.name}`, async () => {
|
|
131
206
|
const settlementStart = performance.now();
|
|
132
207
|
await runtime.releaseEscrow(escrowId);
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
208
|
+
settlementMs = performance.now() - settlementStart;
|
|
209
|
+
}, true);
|
|
210
|
+
const totalMs = performance.now() - totalStart;
|
|
211
|
+
// Summary line
|
|
212
|
+
if (output.mode === 'human') {
|
|
213
|
+
output.print('');
|
|
214
|
+
output.print(output_1.fmt.dim(` ─── ${Math.round(totalMs)}ms total ${'─'.repeat(Math.max(0, 40 - String(Math.round(totalMs)).length))}`));
|
|
215
|
+
output.print('');
|
|
216
|
+
}
|
|
217
|
+
// Receipt
|
|
218
|
+
(0, receipt_1.renderReceipt)({
|
|
219
|
+
agent: config.name,
|
|
220
|
+
service: config.services[0].type,
|
|
221
|
+
amountWei,
|
|
222
|
+
network: 'mock',
|
|
223
|
+
txId,
|
|
224
|
+
timing: {
|
|
225
|
+
totalMs: Math.round(totalMs),
|
|
226
|
+
escrowLockMs: Math.round(escrowLockMs),
|
|
227
|
+
settlementMs: Math.round(settlementMs),
|
|
228
|
+
},
|
|
229
|
+
}, output);
|
|
230
|
+
// Best-effort publish to agirails.app/r/<id> — mock auth requires API key.
|
|
231
|
+
// On failure, fall through silently; the CLI already printed a local receipt.
|
|
232
|
+
// Fee math from canonical SDK helper (config/defaults.ts) — kept in sync
|
|
233
|
+
// with the web copy at lib/receipts/fees.ts via the parity test on web.
|
|
234
|
+
const feeWei = (0, defaults_1.computeDisplayFee)(amountWei);
|
|
235
|
+
const netWei = amountWei - feeWei;
|
|
236
|
+
const upload = await (0, receiptUpload_1.uploadReceipt)({
|
|
237
|
+
agentAddress: providerAddress,
|
|
238
|
+
service: testJob.title,
|
|
239
|
+
amountWei: amountStr,
|
|
240
|
+
feeWei: feeWei.toString(),
|
|
241
|
+
netWei: netWei.toString(),
|
|
242
|
+
txId,
|
|
243
|
+
network: 'mock',
|
|
244
|
+
requesterAddress: requesterWallet.address,
|
|
245
|
+
durationMs: Math.round(totalMs),
|
|
246
|
+
});
|
|
247
|
+
if (output.mode === 'human' && upload.ok) {
|
|
248
|
+
output.print('');
|
|
249
|
+
output.print(` ${output_1.fmt.green('→')} Shareable receipt: ${output_1.fmt.bold(upload.url)}`);
|
|
250
|
+
if (upload.milestone) {
|
|
251
|
+
output.print(` ${output_1.fmt.yellow('★')} Milestone: ${output_1.fmt.bold(upload.milestone)}`);
|
|
252
|
+
}
|
|
253
|
+
output.print('');
|
|
149
254
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
255
|
+
// Share prompt (TTY + human mode only — never in CI/piped/json/quiet)
|
|
256
|
+
if (isTTY) {
|
|
257
|
+
await promptShare(amountWei, 'mock', undefined, upload.ok ? upload.url : undefined);
|
|
153
258
|
}
|
|
154
259
|
}
|
|
155
260
|
exports.runTest = runTest;
|
|
156
261
|
// ============================================================================
|
|
262
|
+
// Share prompt
|
|
263
|
+
// ============================================================================
|
|
264
|
+
async function promptShare(amountWei, network, ethTxHash, receiptUrl) {
|
|
265
|
+
const readline = await Promise.resolve().then(() => __importStar(require('readline')));
|
|
266
|
+
const { copyToClipboardOSC52, buildTwitterIntentUrl, openUrl, buildMockTweet, buildTestnetTweet, } = await Promise.resolve().then(() => __importStar(require('../utils/share')));
|
|
267
|
+
// Compute net for tweet text
|
|
268
|
+
const { computeDisplayFee } = await Promise.resolve().then(() => __importStar(require('../../config/defaults')));
|
|
269
|
+
const fee = computeDisplayFee(amountWei);
|
|
270
|
+
const net = amountWei - fee;
|
|
271
|
+
const netUsd = `$${(Number(net) / 1000000).toFixed(2)}`;
|
|
272
|
+
const baseTweet = network === 'testnet' && ethTxHash
|
|
273
|
+
? buildTestnetTweet(netUsd, ethTxHash)
|
|
274
|
+
: buildMockTweet(netUsd);
|
|
275
|
+
const tweetText = receiptUrl ? `${baseTweet}\n\n${receiptUrl}` : baseTweet;
|
|
276
|
+
console.log('');
|
|
277
|
+
console.log(output_1.fmt.bold('Share your first transaction?'));
|
|
278
|
+
console.log('');
|
|
279
|
+
console.log(` ${output_1.fmt.cyan('1)')} Copy tweet text to clipboard`);
|
|
280
|
+
console.log(` ${output_1.fmt.cyan('2)')} Open Twitter with pre-filled tweet`);
|
|
281
|
+
console.log(` ${output_1.fmt.cyan('3)')} Skip`);
|
|
282
|
+
console.log('');
|
|
283
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
284
|
+
const answer = await new Promise((resolve) => {
|
|
285
|
+
rl.question('Choose [1-3, default 3]: ', resolve);
|
|
286
|
+
});
|
|
287
|
+
rl.close();
|
|
288
|
+
const choice = answer.trim() || '3';
|
|
289
|
+
if (choice === '1') {
|
|
290
|
+
const copied = copyToClipboardOSC52(tweetText);
|
|
291
|
+
console.log('');
|
|
292
|
+
if (copied) {
|
|
293
|
+
console.log(output_1.fmt.green('✓ Tweet copied to clipboard.'));
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
console.log(output_1.fmt.yellow('Clipboard not available — copy manually:'));
|
|
297
|
+
console.log('');
|
|
298
|
+
console.log(output_1.fmt.dim(tweetText));
|
|
299
|
+
}
|
|
300
|
+
console.log('');
|
|
301
|
+
}
|
|
302
|
+
else if (choice === '2') {
|
|
303
|
+
const url = buildTwitterIntentUrl(tweetText);
|
|
304
|
+
const opened = openUrl(url);
|
|
305
|
+
console.log('');
|
|
306
|
+
if (opened) {
|
|
307
|
+
console.log(output_1.fmt.green('✓ Opening Twitter...'));
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
console.log(output_1.fmt.yellow('Could not open browser. Copy this URL:'));
|
|
311
|
+
console.log('');
|
|
312
|
+
console.log(output_1.fmt.dim(url));
|
|
313
|
+
}
|
|
314
|
+
console.log('');
|
|
315
|
+
}
|
|
316
|
+
// choice === '3' or anything else: silent skip
|
|
317
|
+
}
|
|
318
|
+
// ============================================================================
|
|
157
319
|
// Helpers
|
|
158
320
|
// ============================================================================
|
|
159
321
|
/**
|