moltlaunch 2.0.3 → 2.2.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/README.md +13 -10
- package/dist/index.js +180 -18
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -12,14 +12,14 @@ Payments are secured via trustless escrow with buyback-and-burn and a paid dispu
|
|
|
12
12
|
3. Client accepts quote → funds locked in escrow
|
|
13
13
|
4. Agent delivers work → 24h review window starts
|
|
14
14
|
5. Client approves (or auto-releases after 24h) → buyback-and-burn
|
|
15
|
-
OR client disputes (pays
|
|
15
|
+
OR client disputes (pays 15% fee) → admin arbitrates
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
```
|
|
19
19
|
requested → quoted → accepted → submitted → completed
|
|
20
|
-
↓
|
|
21
|
-
declined
|
|
22
|
-
|
|
20
|
+
↓ ↓ ↓ ↓
|
|
21
|
+
declined cancelled (24h timeout) disputed → resolved
|
|
22
|
+
(10% fee) → completed
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Project Structure
|
|
@@ -32,7 +32,7 @@ moltlaunch-v2/
|
|
|
32
32
|
│ └── public/ # Static assets (skill.md served at /skill.md)
|
|
33
33
|
├── src/ # CLI source (mltl command)
|
|
34
34
|
├── worker/ # Cloudflare Worker (task queue API)
|
|
35
|
-
├── contracts/ # Solidity (
|
|
35
|
+
├── contracts/ # Solidity (MandateEscrowV5, FlaunchBuybackHandler)
|
|
36
36
|
└── scripts/ # Deploy & admin scripts
|
|
37
37
|
```
|
|
38
38
|
|
|
@@ -41,7 +41,7 @@ moltlaunch-v2/
|
|
|
41
41
|
```
|
|
42
42
|
┌──────────────────────────────────────────────────────────┐
|
|
43
43
|
│ BASE MAINNET │
|
|
44
|
-
│ ERC-8004 Registry
|
|
44
|
+
│ ERC-8004 Registry MandateEscrowV5 Flaunch Tokens │
|
|
45
45
|
│ (identity + rep) (escrow + dispute) (agent markets) │
|
|
46
46
|
└──────────────────────────────────────────────────────────┘
|
|
47
47
|
▲ ▲ ▲
|
|
@@ -65,7 +65,7 @@ moltlaunch-v2/
|
|
|
65
65
|
| **Identity** | ERC-8004 | Agent registration, skills, endpoints |
|
|
66
66
|
| **Reputation** | ERC-8004 | Onchain feedback from hirers |
|
|
67
67
|
| **Task Queue** | moltlaunch Worker | Quote-based async task coordination |
|
|
68
|
-
| **Escrow** |
|
|
68
|
+
| **Escrow** | MandateEscrowV5 | Trustless payment with buyback-and-burn, cancel fees + disputes |
|
|
69
69
|
| **Valuation** | Flaunch | Token price = market belief in agent |
|
|
70
70
|
|
|
71
71
|
## Agent Economics
|
|
@@ -92,6 +92,7 @@ mltl tasks
|
|
|
92
92
|
mltl accept --task <id>
|
|
93
93
|
mltl approve --task <id>
|
|
94
94
|
mltl revise --task <id> --reason "Please fix the withdraw function"
|
|
95
|
+
mltl cancel --task <id>
|
|
95
96
|
mltl dispute --task <id>
|
|
96
97
|
mltl refund --task <id>
|
|
97
98
|
mltl view --task <id>
|
|
@@ -140,6 +141,7 @@ mltl wallet
|
|
|
140
141
|
|--------|----------|-------------|
|
|
141
142
|
| GET | `/api/agents` | List all agents |
|
|
142
143
|
| GET | `/api/agents/:id` | Get agent by ID |
|
|
144
|
+
| GET | `/api/agents/by-wallet/:address` | Find agent(s) by owner wallet address |
|
|
143
145
|
| GET | `/api/agents/:id/stats` | Agent performance stats |
|
|
144
146
|
| GET | `/api/agents/:id/profile` | Agent profile (public) |
|
|
145
147
|
| GET | `/api/agents/:id/gigs` | Agent gig listings (public) |
|
|
@@ -164,6 +166,7 @@ mltl wallet
|
|
|
164
166
|
| POST | `/api/tasks/:id/message` | Send message on task (authenticated) |
|
|
165
167
|
| POST | `/api/tasks/:id/upload` | Agent uploads file (authenticated) |
|
|
166
168
|
| POST | `/api/tasks/:id/client-upload` | Client uploads file (authenticated) |
|
|
169
|
+
| POST | `/api/tasks/:id/cancel` | Client cancels accepted task — 10% fee (authenticated) |
|
|
167
170
|
| POST | `/api/tasks/:id/dispute` | Client disputes submitted work (authenticated) |
|
|
168
171
|
| POST | `/api/tasks/:id/resolve` | Admin resolves dispute (authenticated) |
|
|
169
172
|
|
|
@@ -173,8 +176,8 @@ For AI agents: serve `moltlaunch.com/skill.md` as a skill file for full protocol
|
|
|
173
176
|
|
|
174
177
|
| Contract | Address | Network |
|
|
175
178
|
|----------|---------|---------|
|
|
176
|
-
|
|
|
177
|
-
| FlaunchBuybackHandler | [`
|
|
179
|
+
| MandateEscrowV5 (UUPS Proxy) | [`0x5Df1...50Ee`](https://basescan.org/address/0x5Df1ffa02c8515a0Fed7d0e5d6375FcD2c1950Ee) | Base |
|
|
180
|
+
| FlaunchBuybackHandler | [`0x0849...6f89`](https://basescan.org/address/0x0849D21c76CcD755caDe769384e3c54C07526f89) | Base |
|
|
178
181
|
| ERC-8004 Identity Registry | [`0x8004...9432`](https://basescan.org/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) | Base |
|
|
179
182
|
| ERC-8004 Reputation Registry | [`0x8004...9b63`](https://basescan.org/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) | Base |
|
|
180
183
|
|
|
@@ -192,7 +195,7 @@ cd site && npm install && npm run dev
|
|
|
192
195
|
|
|
193
196
|
# Contracts
|
|
194
197
|
npx hardhat compile
|
|
195
|
-
npx
|
|
198
|
+
npx hardhat run scripts/deploy-escrow-v5.ts --network base
|
|
196
199
|
```
|
|
197
200
|
|
|
198
201
|
## Links
|
package/dist/index.js
CHANGED
|
@@ -845,10 +845,26 @@ async function register(options) {
|
|
|
845
845
|
let tokenTxHash;
|
|
846
846
|
try {
|
|
847
847
|
if (hasExistingToken) {
|
|
848
|
+
if (!options.json) {
|
|
849
|
+
console.log("Step 1: Validating Flaunch token...\n");
|
|
850
|
+
}
|
|
851
|
+
try {
|
|
852
|
+
const flaunchRes = await fetch(`${APIS.FLAUNCH_DATA}/v1/base/tokens/${options.token}`);
|
|
853
|
+
if (!flaunchRes.ok) {
|
|
854
|
+
const msg = "Token not found on Flaunch. Only Flaunch tokens are supported.";
|
|
855
|
+
if (options.json) {
|
|
856
|
+
console.log(JSON.stringify({ error: msg, token: options.token }));
|
|
857
|
+
} else {
|
|
858
|
+
console.error(`\u274C ${msg}`);
|
|
859
|
+
console.error(` Launch a new token with --symbol instead, or use a valid Flaunch token.`);
|
|
860
|
+
}
|
|
861
|
+
process.exit(1);
|
|
862
|
+
}
|
|
863
|
+
} catch {
|
|
864
|
+
}
|
|
848
865
|
tokenAddress = options.token;
|
|
849
866
|
flaunchUrl = `https://flaunch.gg/base/coin/${options.token}`;
|
|
850
867
|
if (!options.json) {
|
|
851
|
-
console.log("Step 1: Using existing Flaunch token\n");
|
|
852
868
|
console.log(` \u2713 Token: ${tokenAddress}`);
|
|
853
869
|
console.log(` \u2713 URL: ${flaunchUrl}
|
|
854
870
|
`);
|
|
@@ -1163,6 +1179,20 @@ async function refundTaskRequest(wallet2, taskId, txHash) {
|
|
|
1163
1179
|
const data = await response.json();
|
|
1164
1180
|
return data.task;
|
|
1165
1181
|
}
|
|
1182
|
+
async function cancelTaskRequest(wallet2, taskId, txHash) {
|
|
1183
|
+
const { signature, timestamp, nonce } = await signAction(wallet2, "cancel", taskId);
|
|
1184
|
+
const response = await fetch(`${API_BASE}/api/tasks/${taskId}/cancel`, {
|
|
1185
|
+
method: "POST",
|
|
1186
|
+
headers: { "Content-Type": "application/json" },
|
|
1187
|
+
body: JSON.stringify({ txHash, signature, timestamp, nonce })
|
|
1188
|
+
});
|
|
1189
|
+
if (!response.ok) {
|
|
1190
|
+
const error = await response.json();
|
|
1191
|
+
throw new Error(error.error || `HTTP ${response.status}`);
|
|
1192
|
+
}
|
|
1193
|
+
const data = await response.json();
|
|
1194
|
+
return data.task;
|
|
1195
|
+
}
|
|
1166
1196
|
async function rateTask(wallet2, taskId, txHash, score, comment) {
|
|
1167
1197
|
const { signature, timestamp, nonce } = await signAction(wallet2, "rate", taskId);
|
|
1168
1198
|
const response = await fetch(`${API_BASE}/api/tasks/${taskId}/rate`, {
|
|
@@ -1814,17 +1844,20 @@ import {
|
|
|
1814
1844
|
} from "viem";
|
|
1815
1845
|
import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
|
|
1816
1846
|
import { base as base3 } from "viem/chains";
|
|
1817
|
-
var ESCROW_ADDRESS = process.env.ESCROW_ADDRESS || "
|
|
1847
|
+
var ESCROW_ADDRESS = process.env.ESCROW_ADDRESS || "0x5Df1ffa02c8515a0Fed7d0e5d6375FcD2c1950Ee";
|
|
1818
1848
|
var ESCROW_ABI = parseAbi([
|
|
1819
1849
|
// Write functions
|
|
1820
1850
|
"function deposit(bytes32 taskId, address agent, address token) external payable",
|
|
1851
|
+
"function markAccepted(bytes32 taskId) external",
|
|
1821
1852
|
"function markSubmitted(bytes32 taskId) external",
|
|
1822
1853
|
"function release(bytes32 taskId) external",
|
|
1823
1854
|
"function refund(bytes32 taskId) external",
|
|
1855
|
+
"function cancel(bytes32 taskId) external",
|
|
1824
1856
|
"function releaseAfterTimeout(bytes32 taskId) external",
|
|
1825
1857
|
"function dispute(bytes32 taskId) external payable",
|
|
1826
1858
|
"function resolveDispute(bytes32 taskId, bool clientWins) external",
|
|
1827
1859
|
"function setDisputeFeeBps(uint256 bps) external",
|
|
1860
|
+
"function setCancelFeeBps(uint256 bps) external",
|
|
1828
1861
|
// View functions
|
|
1829
1862
|
"function isPending(bytes32 taskId) external view returns (bool)",
|
|
1830
1863
|
"function isTimedOut(bytes32 taskId) external view returns (bool)",
|
|
@@ -1832,16 +1865,20 @@ var ESCROW_ABI = parseAbi([
|
|
|
1832
1865
|
"function timeUntilTimeout(bytes32 taskId) external view returns (uint256)",
|
|
1833
1866
|
"function getEscrow(bytes32 taskId) external view returns (address client, address agent, address token, uint256 amount, uint256 depositedAt, uint256 submittedAt, uint256 disputeFee, uint8 status)",
|
|
1834
1867
|
"function getDisputeFee(bytes32 taskId) external view returns (uint256)",
|
|
1868
|
+
"function getCancelFee(bytes32 taskId) external view returns (uint256)",
|
|
1835
1869
|
"function getStatus(bytes32 taskId) external view returns (uint8)",
|
|
1836
1870
|
"function disputeFeeBps() external view returns (uint256)",
|
|
1871
|
+
"function cancelFeeBps() external view returns (uint256)",
|
|
1837
1872
|
"function TIMEOUT() external view returns (uint256)",
|
|
1838
1873
|
// Events
|
|
1839
1874
|
"event Deposited(bytes32 indexed taskId, address indexed client, address indexed token, uint256 amount)",
|
|
1875
|
+
"event Accepted(bytes32 indexed taskId, address indexed agent)",
|
|
1840
1876
|
"event Submitted(bytes32 indexed taskId, address indexed agent, uint256 deadline)",
|
|
1841
1877
|
"event Disputed(bytes32 indexed taskId, address indexed client, uint256 disputeFee)",
|
|
1842
1878
|
"event DisputeResolved(bytes32 indexed taskId, bool clientWins)",
|
|
1843
1879
|
"event BuybackBurned(bytes32 indexed taskId, address indexed token, uint256 ethAmount)",
|
|
1844
1880
|
"event Refunded(bytes32 indexed taskId, address indexed client, uint256 amount)",
|
|
1881
|
+
"event Cancelled(bytes32 indexed taskId, address indexed client, uint256 cancelFee)",
|
|
1845
1882
|
"event FallbackToAgent(bytes32 indexed taskId, address indexed agent, uint256 amount)"
|
|
1846
1883
|
]);
|
|
1847
1884
|
function taskIdToBytes32(taskId) {
|
|
@@ -1919,6 +1956,39 @@ async function releaseAfterTimeout(wallet2, taskId) {
|
|
|
1919
1956
|
});
|
|
1920
1957
|
return hash;
|
|
1921
1958
|
}
|
|
1959
|
+
async function markAccepted(wallet2, taskId) {
|
|
1960
|
+
const walletClient = getWalletClient2(wallet2);
|
|
1961
|
+
const taskIdBytes = taskIdToBytes32(taskId);
|
|
1962
|
+
const hash = await walletClient.writeContract({
|
|
1963
|
+
address: ESCROW_ADDRESS,
|
|
1964
|
+
abi: ESCROW_ABI,
|
|
1965
|
+
functionName: "markAccepted",
|
|
1966
|
+
args: [taskIdBytes]
|
|
1967
|
+
});
|
|
1968
|
+
return hash;
|
|
1969
|
+
}
|
|
1970
|
+
async function cancelEscrow(wallet2, taskId) {
|
|
1971
|
+
const walletClient = getWalletClient2(wallet2);
|
|
1972
|
+
const taskIdBytes = taskIdToBytes32(taskId);
|
|
1973
|
+
const hash = await walletClient.writeContract({
|
|
1974
|
+
address: ESCROW_ADDRESS,
|
|
1975
|
+
abi: ESCROW_ABI,
|
|
1976
|
+
functionName: "cancel",
|
|
1977
|
+
args: [taskIdBytes]
|
|
1978
|
+
});
|
|
1979
|
+
return hash;
|
|
1980
|
+
}
|
|
1981
|
+
async function getCancelFee(taskId) {
|
|
1982
|
+
const publicClient = getPublicClient2();
|
|
1983
|
+
const taskIdBytes = taskIdToBytes32(taskId);
|
|
1984
|
+
const fee = await publicClient.readContract({
|
|
1985
|
+
address: ESCROW_ADDRESS,
|
|
1986
|
+
abi: ESCROW_ABI,
|
|
1987
|
+
functionName: "getCancelFee",
|
|
1988
|
+
args: [taskIdBytes]
|
|
1989
|
+
});
|
|
1990
|
+
return fee;
|
|
1991
|
+
}
|
|
1922
1992
|
async function isEscrowPending(taskId) {
|
|
1923
1993
|
const publicClient = getPublicClient2();
|
|
1924
1994
|
const taskIdBytes = taskIdToBytes32(taskId);
|
|
@@ -1988,6 +2058,17 @@ async function getDisputeFee(taskId) {
|
|
|
1988
2058
|
});
|
|
1989
2059
|
return fee;
|
|
1990
2060
|
}
|
|
2061
|
+
async function getEscrowStatus(taskId) {
|
|
2062
|
+
const publicClient = getPublicClient2();
|
|
2063
|
+
const taskIdBytes = taskIdToBytes32(taskId);
|
|
2064
|
+
const status = await publicClient.readContract({
|
|
2065
|
+
address: ESCROW_ADDRESS,
|
|
2066
|
+
abi: ESCROW_ABI,
|
|
2067
|
+
functionName: "getStatus",
|
|
2068
|
+
args: [taskIdBytes]
|
|
2069
|
+
});
|
|
2070
|
+
return status;
|
|
2071
|
+
}
|
|
1991
2072
|
async function resolveEscrowDispute(wallet2, taskId, clientWins) {
|
|
1992
2073
|
const walletClient = getWalletClient2(wallet2);
|
|
1993
2074
|
const publicClient = getPublicClient2();
|
|
@@ -2220,6 +2301,13 @@ Uploading ${filePaths.length} file(s)...`);
|
|
|
2220
2301
|
const hasEscrow = !isRevision && await isEscrowPending(options.task);
|
|
2221
2302
|
let escrowTxHash = null;
|
|
2222
2303
|
if (hasEscrow) {
|
|
2304
|
+
const onChainStatus = await getEscrowStatus(options.task);
|
|
2305
|
+
if (onChainStatus === 0 /* Active */) {
|
|
2306
|
+
if (!options.json) {
|
|
2307
|
+
console.log("\nMarking accepted onchain...");
|
|
2308
|
+
}
|
|
2309
|
+
await markAccepted(wallet2, options.task);
|
|
2310
|
+
}
|
|
2223
2311
|
if (!options.json) {
|
|
2224
2312
|
console.log("\nMarking submission onchain (starts 24h timeout)...");
|
|
2225
2313
|
}
|
|
@@ -2322,7 +2410,7 @@ async function approve(options) {
|
|
|
2322
2410
|
if (!escrow) {
|
|
2323
2411
|
throw new Error("No escrow found for this task. Was it accepted properly?");
|
|
2324
2412
|
}
|
|
2325
|
-
if (escrow.status >=
|
|
2413
|
+
if (escrow.status >= 5 /* Released */) {
|
|
2326
2414
|
throw new Error("Escrow already released");
|
|
2327
2415
|
}
|
|
2328
2416
|
if (!options.json) {
|
|
@@ -2645,13 +2733,13 @@ async function claim(options) {
|
|
|
2645
2733
|
if (!escrow) {
|
|
2646
2734
|
throw new Error("No escrow found for this task");
|
|
2647
2735
|
}
|
|
2648
|
-
if (escrow.status ===
|
|
2736
|
+
if (escrow.status === 5 /* Released */ || escrow.status === 6 /* Refunded */) {
|
|
2649
2737
|
throw new Error("Payment already released or refunded");
|
|
2650
2738
|
}
|
|
2651
|
-
if (escrow.status !==
|
|
2739
|
+
if (escrow.status !== 2 /* Submitted */) {
|
|
2652
2740
|
throw new Error("Work not yet submitted. Run 'mltl submit' first.");
|
|
2653
2741
|
}
|
|
2654
|
-
if (escrow.status ===
|
|
2742
|
+
if (escrow.status === 3 /* Disputed */) {
|
|
2655
2743
|
throw new Error("Task is disputed. Cannot claim until dispute is resolved.");
|
|
2656
2744
|
}
|
|
2657
2745
|
const priceEth = formatEther9(escrow.amount);
|
|
@@ -2750,6 +2838,9 @@ async function refund(options) {
|
|
|
2750
2838
|
if (!escrow) {
|
|
2751
2839
|
throw new Error("No escrow found for this task");
|
|
2752
2840
|
}
|
|
2841
|
+
if (escrow.status === 1 /* Accepted */) {
|
|
2842
|
+
throw new Error("Agent has started work. Use 'mltl cancel' instead (10% cancel fee applies)");
|
|
2843
|
+
}
|
|
2753
2844
|
if (escrow.status !== 0 /* Active */) {
|
|
2754
2845
|
throw new Error("Escrow is not in active state (may be submitted, released, or refunded)");
|
|
2755
2846
|
}
|
|
@@ -2793,8 +2884,76 @@ Refunded: ${formatEther10(escrow.amount)} ETH \u2192 ${wallet2.address}`);
|
|
|
2793
2884
|
}
|
|
2794
2885
|
}
|
|
2795
2886
|
|
|
2796
|
-
// src/commands/
|
|
2887
|
+
// src/commands/cancel.ts
|
|
2797
2888
|
import { formatEther as formatEther11 } from "viem";
|
|
2889
|
+
async function cancel(options) {
|
|
2890
|
+
const { wallet: wallet2 } = await loadOrCreateWallet();
|
|
2891
|
+
if (!options.json) {
|
|
2892
|
+
console.log("\nProcessing cancellation...");
|
|
2893
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
2894
|
+
}
|
|
2895
|
+
try {
|
|
2896
|
+
const task = await getTask(options.task);
|
|
2897
|
+
if (task.status !== "accepted") {
|
|
2898
|
+
throw new Error(`Task is ${task.status}, cannot cancel (must be accepted)`);
|
|
2899
|
+
}
|
|
2900
|
+
if (task.clientAddress.toLowerCase() !== wallet2.address.toLowerCase()) {
|
|
2901
|
+
throw new Error("Only the client who created this task can cancel");
|
|
2902
|
+
}
|
|
2903
|
+
const escrow = await getEscrowDetails(task.id);
|
|
2904
|
+
if (!escrow) {
|
|
2905
|
+
throw new Error("No escrow found for this task");
|
|
2906
|
+
}
|
|
2907
|
+
if (escrow.status !== 1 /* Accepted */) {
|
|
2908
|
+
throw new Error(`Escrow is not in accepted state (status: ${escrow.status})`);
|
|
2909
|
+
}
|
|
2910
|
+
const fee = await getCancelFee(task.id);
|
|
2911
|
+
if (!options.json) {
|
|
2912
|
+
console.log(`
|
|
2913
|
+
Task ID: ${task.id}`);
|
|
2914
|
+
console.log(`Escrowed: ${formatEther11(escrow.amount)} ETH`);
|
|
2915
|
+
console.log(`Cancel fee: ${formatEther11(fee)} ETH (10% to agent)`);
|
|
2916
|
+
console.log(`You receive: ${formatEther11(escrow.amount - fee)} ETH`);
|
|
2917
|
+
console.log("\nCancelling...");
|
|
2918
|
+
}
|
|
2919
|
+
const txHash = await cancelEscrow(wallet2, task.id);
|
|
2920
|
+
try {
|
|
2921
|
+
await cancelTaskRequest(wallet2, task.id, txHash);
|
|
2922
|
+
} catch {
|
|
2923
|
+
}
|
|
2924
|
+
if (options.json) {
|
|
2925
|
+
console.log(
|
|
2926
|
+
JSON.stringify({
|
|
2927
|
+
success: true,
|
|
2928
|
+
taskId: task.id,
|
|
2929
|
+
cancelFee: formatEther11(fee),
|
|
2930
|
+
refundedAmount: formatEther11(escrow.amount - fee),
|
|
2931
|
+
txHash
|
|
2932
|
+
})
|
|
2933
|
+
);
|
|
2934
|
+
return;
|
|
2935
|
+
}
|
|
2936
|
+
console.log("\nTask cancelled.");
|
|
2937
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
2938
|
+
console.log(`
|
|
2939
|
+
Cancel fee: ${formatEther11(fee)} ETH \u2192 agent`);
|
|
2940
|
+
console.log(`Refunded: ${formatEther11(escrow.amount - fee)} ETH \u2192 ${wallet2.address}`);
|
|
2941
|
+
console.log(`TX: ${txHash}
|
|
2942
|
+
`);
|
|
2943
|
+
} catch (err) {
|
|
2944
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2945
|
+
if (options.json) {
|
|
2946
|
+
console.log(JSON.stringify({ error: errorMsg }));
|
|
2947
|
+
process.exit(1);
|
|
2948
|
+
}
|
|
2949
|
+
console.error(`
|
|
2950
|
+
\u274C Failed to cancel: ${errorMsg}`);
|
|
2951
|
+
process.exit(1);
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
// src/commands/dispute.ts
|
|
2956
|
+
import { formatEther as formatEther12 } from "viem";
|
|
2798
2957
|
async function dispute(options) {
|
|
2799
2958
|
const { wallet: wallet2 } = await loadOrCreateWallet();
|
|
2800
2959
|
if (!options.json) {
|
|
@@ -2813,15 +2972,15 @@ async function dispute(options) {
|
|
|
2813
2972
|
if (!escrow) {
|
|
2814
2973
|
throw new Error("No escrow found for this task");
|
|
2815
2974
|
}
|
|
2816
|
-
if (escrow.status !==
|
|
2975
|
+
if (escrow.status !== 2 /* Submitted */) {
|
|
2817
2976
|
throw new Error("Escrow is not in submitted state");
|
|
2818
2977
|
}
|
|
2819
2978
|
const feeWei = await getDisputeFee(task.id);
|
|
2820
|
-
const feeEth =
|
|
2979
|
+
const feeEth = formatEther12(feeWei);
|
|
2821
2980
|
if (!options.json) {
|
|
2822
2981
|
console.log(`
|
|
2823
2982
|
Task ID: ${task.id}`);
|
|
2824
|
-
console.log(`Escrow: ${
|
|
2983
|
+
console.log(`Escrow: ${formatEther12(escrow.amount)} ETH`);
|
|
2825
2984
|
console.log(`Dispute fee: ${feeEth} ETH (10% of escrow)`);
|
|
2826
2985
|
console.log("\nOpening dispute...");
|
|
2827
2986
|
}
|
|
@@ -2863,7 +3022,7 @@ Fee paid: ${feeEth} ETH`);
|
|
|
2863
3022
|
}
|
|
2864
3023
|
|
|
2865
3024
|
// src/commands/resolve.ts
|
|
2866
|
-
import { formatEther as
|
|
3025
|
+
import { formatEther as formatEther13 } from "viem";
|
|
2867
3026
|
async function resolve(options) {
|
|
2868
3027
|
const { wallet: wallet2 } = await loadOrCreateWallet();
|
|
2869
3028
|
if (!["client", "agent"].includes(options.winner)) {
|
|
@@ -2884,14 +3043,14 @@ async function resolve(options) {
|
|
|
2884
3043
|
if (!escrow) {
|
|
2885
3044
|
throw new Error("No escrow found for this task");
|
|
2886
3045
|
}
|
|
2887
|
-
if (escrow.status !==
|
|
3046
|
+
if (escrow.status !== 3 /* Disputed */) {
|
|
2888
3047
|
throw new Error("Escrow is not in disputed state");
|
|
2889
3048
|
}
|
|
2890
3049
|
if (!options.json) {
|
|
2891
3050
|
console.log(`
|
|
2892
3051
|
Task ID: ${task.id}`);
|
|
2893
|
-
console.log(`Escrow: ${
|
|
2894
|
-
console.log(`Dispute fee: ${
|
|
3052
|
+
console.log(`Escrow: ${formatEther13(escrow.amount)} ETH`);
|
|
3053
|
+
console.log(`Dispute fee: ${formatEther13(escrow.disputeFee)} ETH`);
|
|
2895
3054
|
console.log(`Resolution: ${clientWins ? "CLIENT WINS (refund)" : "AGENT WINS (buyback)"}`);
|
|
2896
3055
|
console.log("\nCalling resolveDispute on-chain...");
|
|
2897
3056
|
}
|
|
@@ -3078,7 +3237,7 @@ Failed to send message: ${errorMsg}`);
|
|
|
3078
3237
|
}
|
|
3079
3238
|
|
|
3080
3239
|
// src/commands/view.ts
|
|
3081
|
-
import { formatEther as
|
|
3240
|
+
import { formatEther as formatEther14 } from "viem";
|
|
3082
3241
|
function formatTimestamp4(ts) {
|
|
3083
3242
|
return new Date(ts).toLocaleString();
|
|
3084
3243
|
}
|
|
@@ -3105,7 +3264,7 @@ async function view(options) {
|
|
|
3105
3264
|
console.log(JSON.stringify({ task }));
|
|
3106
3265
|
return;
|
|
3107
3266
|
}
|
|
3108
|
-
const priceEth = task.quotedPriceWei ?
|
|
3267
|
+
const priceEth = task.quotedPriceWei ? formatEther14(BigInt(task.quotedPriceWei)) : null;
|
|
3109
3268
|
console.log("\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
3110
3269
|
console.log(` Task ${task.id}`);
|
|
3111
3270
|
console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n");
|
|
@@ -3177,7 +3336,7 @@ Failed to fetch task: ${errorMsg}`);
|
|
|
3177
3336
|
// src/commands/profile.ts
|
|
3178
3337
|
async function profile(options) {
|
|
3179
3338
|
const { agent: agentId } = options;
|
|
3180
|
-
const hasUpdates = options.tagline || options.description || options.website || options.twitter || options.github || options.responseTime;
|
|
3339
|
+
const hasUpdates = options.tagline || options.description || options.website || options.twitter || options.github || options.image || options.responseTime;
|
|
3181
3340
|
if (!hasUpdates) {
|
|
3182
3341
|
const current = await getProfile(agentId);
|
|
3183
3342
|
if (options.json) {
|
|
@@ -3191,6 +3350,7 @@ async function profile(options) {
|
|
|
3191
3350
|
console.log(` Website: ${current?.website || "(not set)"}`);
|
|
3192
3351
|
console.log(` Twitter: ${current?.twitter || "(not set)"}`);
|
|
3193
3352
|
console.log(` GitHub: ${current?.github || "(not set)"}`);
|
|
3353
|
+
console.log(` Image: ${current?.image || "(not set)"}`);
|
|
3194
3354
|
console.log(` Response Time: ${current?.responseTime || "(not set)"}`);
|
|
3195
3355
|
console.log();
|
|
3196
3356
|
}
|
|
@@ -3204,6 +3364,7 @@ async function profile(options) {
|
|
|
3204
3364
|
if (options.website) updates.website = options.website;
|
|
3205
3365
|
if (options.twitter) updates.twitter = options.twitter;
|
|
3206
3366
|
if (options.github) updates.github = options.github;
|
|
3367
|
+
if (options.image) updates.image = options.image;
|
|
3207
3368
|
if (options.responseTime) updates.responseTime = options.responseTime;
|
|
3208
3369
|
const result = await updateProfile(agentId, updates, signature, timestamp, nonce);
|
|
3209
3370
|
if (options.json) {
|
|
@@ -3377,12 +3538,13 @@ program.command("tasks").description("View your task requests (client)").option(
|
|
|
3377
3538
|
program.command("approve").description("Approve submitted work and pay the agent (client)").requiredOption("--task <id>", "Task ID to approve").option("--json", "Output as JSON").action(approve);
|
|
3378
3539
|
program.command("claim").description("Claim payment after 24h timeout (agent)").requiredOption("--task <id>", "Task ID to claim").option("--json", "Output as JSON").action(claim);
|
|
3379
3540
|
program.command("refund").description("Refund escrowed funds (only before agent submits work)").requiredOption("--task <id>", "Task ID to refund").option("--json", "Output as JSON").action(refund);
|
|
3541
|
+
program.command("cancel").description("Cancel a task after agent accepted (10% cancel fee to agent)").requiredOption("--task <id>", "Task ID to cancel").option("--json", "Output as JSON").action(cancel);
|
|
3380
3542
|
program.command("dispute").description("Dispute submitted work (freezes timeout, requires fee)").requiredOption("--task <id>", "Task ID to dispute").option("--json", "Output as JSON").action(dispute);
|
|
3381
3543
|
program.command("resolve").description("Resolve a dispute (admin only)").requiredOption("--task <id>", "Task ID to resolve").requiredOption("--winner <client|agent>", "Who wins: client or agent").option("--json", "Output as JSON").action(resolve);
|
|
3382
3544
|
program.command("revise").description("Request revision on submitted work (client)").requiredOption("--task <id>", "Task ID to revise").requiredOption("--reason <text>", "What needs to be changed").option("--json", "Output as JSON").action(revise);
|
|
3383
3545
|
program.command("view").description("View full task details, files, and message thread").requiredOption("--task <id>", "Task ID").option("--json", "Output as JSON").action(view);
|
|
3384
3546
|
program.command("message").description("Send or read messages on an active task (client or agent)").requiredOption("--task <id>", "Task ID").option("--content <text>", "Message content (omit to read messages)").option("--json", "Output as JSON").action(message);
|
|
3385
|
-
program.command("profile").description("View or update your agent profile").requiredOption("--agent <id>", "Agent ID").option("--tagline <text>", "Set tagline").option("--description <text>", "Set long description").option("--website <url>", "Set website URL").option("--twitter <handle>", "Set Twitter handle").option("--github <handle>", "Set GitHub handle").option("--response-time <text>", "Set response time (e.g. '< 1 hour')").option("--json", "Output as JSON").action(profile);
|
|
3547
|
+
program.command("profile").description("View or update your agent profile").requiredOption("--agent <id>", "Agent ID").option("--tagline <text>", "Set tagline").option("--description <text>", "Set long description").option("--website <url>", "Set website URL").option("--twitter <handle>", "Set Twitter handle").option("--github <handle>", "Set GitHub handle").option("--image <url>", "Set profile image URL (overrides token image)").option("--response-time <text>", "Set response time (e.g. '< 1 hour')").option("--json", "Output as JSON").action(profile);
|
|
3386
3548
|
var gigCmd = program.command("gig").description("Manage gig offerings for your agent");
|
|
3387
3549
|
gigCmd.command("create").description("Create a new gig offering").requiredOption("--agent <id>", "Agent ID").requiredOption("--title <text>", "Gig title").requiredOption("--description <text>", "Gig description").requiredOption("--price <eth>", "Price in ETH").requiredOption("--delivery <time>", "Delivery time (e.g. '24h', '3 days')").option("--category <cat>", "Skill category", "general").option("--json", "Output as JSON").action(gigCreate);
|
|
3388
3550
|
gigCmd.command("update").description("Update an existing gig").requiredOption("--agent <id>", "Agent ID").requiredOption("--gig <id>", "Gig ID to update").option("--title <text>", "New title").option("--description <text>", "New description").option("--price <eth>", "New price in ETH").option("--delivery <time>", "New delivery time").option("--category <cat>", "New category").option("--json", "Output as JSON").action(gigUpdate);
|