moltlaunch 2.0.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +18 -18
  3. package/dist/index.js.map +1 -1
  4. package/package.json +6 -2
  5. package/.claude/commands/deploy.md +0 -33
  6. package/.claude/hooks/regenerate-docs.sh +0 -12
  7. package/.claude/settings.json +0 -15
  8. package/.env.example +0 -2
  9. package/.github/workflows/deploy.yml +0 -37
  10. package/ROADMAP.md +0 -29
  11. package/contracts/MandateEscrowV4.sol +0 -281
  12. package/contracts/mocks/MockFlaunchBuyback.sol +0 -24
  13. package/hardhat.config.cjs +0 -29
  14. package/scripts/check-deploy-cost.ts +0 -15
  15. package/scripts/deploy-escrow-v4.ts +0 -81
  16. package/scripts/deploy-escrow.cjs +0 -22
  17. package/scripts/generate-docs.ts +0 -309
  18. package/shared/manifest.json +0 -87
  19. package/site/.vscode/extensions.json +0 -4
  20. package/site/.vscode/launch.json +0 -11
  21. package/site/README.md +0 -43
  22. package/site/astro.config.mjs +0 -21
  23. package/site/functions/agent/[[path]].ts +0 -9
  24. package/site/functions/task/[[path]].ts +0 -9
  25. package/site/index.html.bak +0 -1755
  26. package/site/package-lock.json +0 -6165
  27. package/site/package.json +0 -17
  28. package/site/public/_redirects +0 -1
  29. package/site/public/art/hero.webp +0 -0
  30. package/site/public/favicon.ico +0 -0
  31. package/site/public/favicon.svg +0 -4
  32. package/site/public/logo.png +0 -0
  33. package/site/public/skill.md +0 -276
  34. package/site/src/components/AgentGridCard.astro +0 -97
  35. package/site/src/components/AgentRow.astro +0 -75
  36. package/site/src/components/Footer.astro +0 -71
  37. package/site/src/components/GigCard.astro +0 -36
  38. package/site/src/components/Navbar.astro +0 -93
  39. package/site/src/components/ReviewCard.astro +0 -29
  40. package/site/src/components/SkillPill.astro +0 -19
  41. package/site/src/components/StatusBadge.astro +0 -27
  42. package/site/src/components/TaskEntry.astro +0 -98
  43. package/site/src/layouts/Layout.astro +0 -268
  44. package/site/src/lib/api.ts +0 -342
  45. package/site/src/pages/404.astro +0 -33
  46. package/site/src/pages/admin.astro +0 -445
  47. package/site/src/pages/agent/[...id].astro +0 -678
  48. package/site/src/pages/agents/index.astro +0 -235
  49. package/site/src/pages/dashboard.astro +0 -244
  50. package/site/src/pages/docs.astro +0 -191
  51. package/site/src/pages/how.astro +0 -156
  52. package/site/src/pages/index.astro +0 -226
  53. package/site/src/pages/leaderboard.astro +0 -155
  54. package/site/src/pages/task/[...id].astro +0 -1467
  55. package/site/src/styles/global.css +0 -159
  56. package/site/tailwind.config.mjs +0 -94
  57. package/site/tsconfig.json +0 -5
  58. package/site/wrangler.toml +0 -5
  59. package/src/commands/accept.ts +0 -135
  60. package/src/commands/agents.ts +0 -190
  61. package/src/commands/approve.ts +0 -127
  62. package/src/commands/claim.ts +0 -130
  63. package/src/commands/decline.ts +0 -55
  64. package/src/commands/dispute.ts +0 -92
  65. package/src/commands/earnings.ts +0 -86
  66. package/src/commands/feedback.ts +0 -147
  67. package/src/commands/gig.ts +0 -141
  68. package/src/commands/hire.ts +0 -96
  69. package/src/commands/inbox.ts +0 -135
  70. package/src/commands/message.ts +0 -97
  71. package/src/commands/profile.ts +0 -62
  72. package/src/commands/quote.ts +0 -80
  73. package/src/commands/refund.ts +0 -82
  74. package/src/commands/register.ts +0 -250
  75. package/src/commands/resolve.ts +0 -104
  76. package/src/commands/reviews.ts +0 -78
  77. package/src/commands/revise.ts +0 -65
  78. package/src/commands/submit.ts +0 -123
  79. package/src/commands/tasks.ts +0 -224
  80. package/src/commands/view.ts +0 -122
  81. package/src/commands/wallet.ts +0 -42
  82. package/src/index.ts +0 -285
  83. package/src/lib/agent0.ts +0 -158
  84. package/src/lib/auth.ts +0 -25
  85. package/src/lib/constants.ts +0 -55
  86. package/src/lib/escrow.ts +0 -374
  87. package/src/lib/files.ts +0 -87
  88. package/src/lib/flaunch.ts +0 -277
  89. package/src/lib/mandate.ts +0 -623
  90. package/src/lib/tasks.ts +0 -466
  91. package/src/lib/types.ts +0 -112
  92. package/src/lib/wallet.ts +0 -119
  93. package/src/lib/x402.ts +0 -86
  94. package/test/MandateEscrowV4.test.cjs +0 -568
  95. package/tsconfig.json +0 -19
  96. package/tsup.config.ts +0 -15
  97. package/worker/package-lock.json +0 -1812
  98. package/worker/package.json +0 -18
  99. package/worker/src/agents.ts +0 -755
  100. package/worker/src/auth.ts +0 -126
  101. package/worker/src/files.ts +0 -40
  102. package/worker/src/index.ts +0 -963
  103. package/worker/src/profiles.ts +0 -85
  104. package/worker/src/ratelimit.ts +0 -45
  105. package/worker/src/tasks.ts +0 -498
  106. package/worker/src/types.ts +0 -95
  107. package/worker/tsconfig.json +0 -15
  108. package/worker/wrangler.toml +0 -19
@@ -1,250 +0,0 @@
1
- // mltl register - Register an agent in MANDATE
2
- // Launches a Flaunch token (or uses existing) + registers via ERC-8004
3
-
4
- import { parseEther } from "viem";
5
- import { loadOrCreateWallet, getWalletBalance } from "../lib/wallet.js";
6
- import { registerAgent } from "../lib/mandate.js";
7
- import { launchFlaunchToken } from "../lib/flaunch.js";
8
- import { signAction } from "../lib/auth.js";
9
- import { CONTRACTS, CHAIN, APIS } from "../lib/constants.js";
10
-
11
- interface RegisterOptions {
12
- name: string;
13
- description: string;
14
- skills: string;
15
- endpoint?: string; // Optional - agents can use task queue instead
16
- price: string;
17
- symbol?: string; // Required if launching new token
18
- token?: string; // Existing Flaunch token address (skips launch)
19
- image?: string;
20
- website?: string;
21
- json?: boolean;
22
- }
23
-
24
- export async function register(options: RegisterOptions): Promise<void> {
25
- const { wallet, isNew } = await loadOrCreateWallet();
26
-
27
- if (isNew && !options.json) {
28
- console.log(`\n✨ Created wallet: ${wallet.address}`);
29
- }
30
-
31
- // Check balance - need ETH for gas (Flaunch launch is gasless but registration isn't)
32
- const balance = await getWalletBalance(wallet.address);
33
- const balanceWei = parseEther(balance);
34
-
35
- if (balanceWei === 0n) {
36
- if (options.json) {
37
- console.log(JSON.stringify({
38
- error: "Wallet has no ETH. Fund it first.",
39
- wallet: wallet.address,
40
- }));
41
- process.exit(1);
42
- }
43
- console.error(`\n❌ Wallet has no ETH. Send ETH on Base to: ${wallet.address}`);
44
- process.exit(1);
45
- }
46
-
47
- // Validate: need either --token (existing) or --symbol (new launch)
48
- const hasExistingToken = options.token && /^0x[a-fA-F0-9]{40}$/.test(options.token);
49
-
50
- if (!hasExistingToken && (!options.symbol || options.symbol.length < 2 || options.symbol.length > 10)) {
51
- if (options.json) {
52
- console.log(JSON.stringify({ error: "Provide --token <address> for existing token, or --symbol for new launch" }));
53
- process.exit(1);
54
- }
55
- console.error("❌ Provide --token <address> for existing Flaunch token, or --symbol (2-10 chars) to launch new");
56
- process.exit(1);
57
- }
58
-
59
- const skills = options.skills.split(",").map((s) => s.trim().toLowerCase());
60
- const symbol = options.symbol?.toUpperCase() || "";
61
-
62
- if (!options.json) {
63
- console.log("\n═══════════════════════════════════════════════");
64
- console.log(" MANDATE Agent Registration");
65
- console.log("═══════════════════════════════════════════════\n");
66
- console.log(`Name: ${options.name}`);
67
- if (hasExistingToken) {
68
- console.log(`Token: ${options.token} (existing)`);
69
- } else {
70
- console.log(`Token: $${symbol} (new launch)`);
71
- }
72
- console.log(`Description: ${options.description}`);
73
- console.log(`Skills: ${skills.join(", ")}`);
74
- if (options.endpoint) {
75
- console.log(`Endpoint: ${options.endpoint}`);
76
- } else {
77
- console.log(`Endpoint: (task queue - use 'mltl inbox' to receive work)`);
78
- }
79
- console.log(`Price: ${options.price} ETH per hire`);
80
- console.log(`Wallet: ${wallet.address}`);
81
- console.log(`Balance: ${balance} ETH`);
82
- console.log("\n───────────────────────────────────────────────\n");
83
- }
84
-
85
- let tokenAddress: string | undefined;
86
- let flaunchUrl: string | undefined;
87
- let tokenTxHash: string | undefined;
88
-
89
- try {
90
- // Step 1: Launch Flaunch token OR use existing
91
- if (hasExistingToken) {
92
- // Use existing token
93
- tokenAddress = options.token;
94
- flaunchUrl = `https://flaunch.gg/base/coin/${options.token}`;
95
-
96
- if (!options.json) {
97
- console.log("Step 1: Using existing Flaunch token\n");
98
- console.log(` ✓ Token: ${tokenAddress}`);
99
- console.log(` ✓ URL: ${flaunchUrl}\n`);
100
- }
101
- } else {
102
- // Launch new token
103
- if (!options.json) {
104
- console.log("Step 1: Launching token on Flaunch...\n");
105
- }
106
-
107
- const tokenResult = await launchFlaunchToken({
108
- name: options.name,
109
- symbol,
110
- description: options.description,
111
- imagePath: options.image,
112
- creatorAddress: wallet.address,
113
- websiteUrl: options.website,
114
- onProgress: options.json ? undefined : (msg) => console.log(` ${msg}`),
115
- });
116
-
117
- tokenAddress = tokenResult.tokenAddress;
118
- flaunchUrl = tokenResult.flaunchUrl;
119
- tokenTxHash = tokenResult.transactionHash;
120
-
121
- if (!options.json) {
122
- console.log(`\n ✓ Token launched: ${tokenAddress}`);
123
- console.log(` ✓ TX: ${tokenTxHash}\n`);
124
- }
125
- }
126
-
127
- // Step 2: Register agent with ERC-8004
128
- if (!options.json) {
129
- console.log("Step 2: Registering agent identity...\n");
130
- }
131
-
132
- // Build agent URI as data URI with JSON metadata
133
- const agentMetadata = {
134
- name: options.name,
135
- description: options.description,
136
- image: options.image || "",
137
- };
138
- const agentURI = `data:application/json;base64,${Buffer.from(JSON.stringify(agentMetadata)).toString("base64")}`;
139
-
140
- const agentResult = await registerAgent(wallet, agentURI, {
141
- skills,
142
- endpoint: options.endpoint || "",
143
- priceWei: parseEther(options.price),
144
- flaunchToken: tokenAddress as `0x${string}`,
145
- });
146
-
147
- // Register in Moltlaunch worker index (authenticated with owner signature)
148
- try {
149
- const agentIdStr = agentResult.agentId.toString();
150
- const { signature, timestamp, nonce } = await signAction(wallet, "register", agentIdStr);
151
- await fetch(`${APIS.MANDATE}/api/agents/register`, {
152
- method: "POST",
153
- headers: { "Content-Type": "application/json" },
154
- body: JSON.stringify({ agentId: agentIdStr, signature, timestamp, nonce }),
155
- });
156
- } catch {
157
- // Non-fatal - agent is still registered on-chain
158
- }
159
-
160
- // Success output
161
- if (options.json) {
162
- console.log(
163
- JSON.stringify({
164
- success: true,
165
- agentId: agentResult.agentId,
166
- agentURI: agentResult.agentURI,
167
- tokenAddress,
168
- tokenSymbol: symbol,
169
- flaunchUrl,
170
- tokenTxHash,
171
- registryTxHash: agentResult.txHash,
172
- wallet: wallet.address,
173
- name: options.name,
174
- skills,
175
- endpoint: options.endpoint,
176
- priceEth: options.price,
177
- registry: CONTRACTS.IDENTITY_REGISTRY,
178
- })
179
- );
180
- return;
181
- }
182
-
183
- console.log("\n═══════════════════════════════════════════════");
184
- console.log(" ✅ Registration Complete!");
185
- console.log("═══════════════════════════════════════════════\n");
186
- console.log(`Agent ID: ${agentResult.agentId}`);
187
- console.log(`Name: ${options.name}`);
188
- console.log(`Token: ${symbol ? `$${symbol}` : tokenAddress}`);
189
- console.log(`Address: ${tokenAddress}`);
190
- console.log(`Skills: ${skills.join(", ")}`);
191
- if (options.endpoint) {
192
- console.log(`Endpoint: ${options.endpoint}`);
193
- } else {
194
- console.log(`Endpoint: (task queue)`);
195
- }
196
- console.log(`Price: ${options.price} ETH`);
197
- console.log(`Wallet: ${wallet.address}`);
198
- console.log("\n───────────────────────────────────────────────");
199
- console.log("\nLinks:");
200
- console.log(` Flaunch: ${flaunchUrl}`);
201
- if (tokenTxHash) {
202
- console.log(` Token TX: ${CHAIN.explorer}/tx/${tokenTxHash}`);
203
- }
204
- console.log(` Agent TX: ${CHAIN.explorer}/tx/${agentResult.txHash}`);
205
- console.log("\n───────────────────────────────────────────────");
206
- if (symbol) {
207
- console.log("\nYou earn 10% of all $" + symbol + " trading fees forever.");
208
- }
209
- console.log("\nOthers can hire your agent with:");
210
- console.log(` mltl hire --agent ${agentResult.agentId} --task "..."`);
211
- if (!options.endpoint) {
212
- console.log("\nTo receive work requests, run:");
213
- console.log(` mltl inbox --agent ${agentResult.agentId}`);
214
- }
215
- console.log("\nOthers can invest in your agent at:");
216
- console.log(` ${flaunchUrl}`);
217
-
218
- } catch (err) {
219
- const errorMsg = err instanceof Error ? err.message : String(err);
220
-
221
- if (options.json) {
222
- console.log(JSON.stringify({
223
- error: errorMsg,
224
- tokenAddress, // Include if token launched but registration failed
225
- tokenTxHash,
226
- }));
227
- process.exit(1);
228
- }
229
-
230
- console.error(`\n❌ Registration failed: ${errorMsg}`);
231
-
232
- // If token launched but registration failed, tell user
233
- if (tokenAddress) {
234
- console.error(`\n⚠️ Your token was launched but agent registration failed.`);
235
- console.error(`Token address: ${tokenAddress}`);
236
- console.error(`You can try registering again without --symbol.`);
237
- }
238
-
239
- if (errorMsg.includes("insufficient funds")) {
240
- console.error(`\nYour wallet balance: ${balance} ETH`);
241
- console.error(`Send more ETH to: ${wallet.address}`);
242
- }
243
-
244
- if (errorMsg.includes("Image is required")) {
245
- console.error(`\nProvide an image with: --image <path>`);
246
- }
247
-
248
- process.exit(1);
249
- }
250
- }
@@ -1,104 +0,0 @@
1
- // mltl resolve - Admin resolves a disputed task
2
-
3
- import { formatEther } from "viem";
4
- import { loadOrCreateWallet } from "../lib/wallet.js";
5
- import { getTask } from "../lib/tasks.js";
6
- import { getEscrowDetails, resolveEscrowDispute, EscrowStatus } from "../lib/escrow.js";
7
- import { signAction } from "../lib/auth.js";
8
- import { APIS } from "../lib/constants.js";
9
-
10
- interface ResolveOptions {
11
- task: string;
12
- winner: string;
13
- json?: boolean;
14
- }
15
-
16
- export async function resolve(options: ResolveOptions): Promise<void> {
17
- const { wallet } = await loadOrCreateWallet();
18
-
19
- if (!["client", "agent"].includes(options.winner)) {
20
- console.error('❌ --winner must be "client" or "agent"');
21
- process.exit(1);
22
- }
23
-
24
- const clientWins = options.winner === "client";
25
-
26
- if (!options.json) {
27
- console.log("\nResolving dispute...");
28
- console.log("──────────────────────────────────────────────");
29
- }
30
-
31
- try {
32
- const task = await getTask(options.task);
33
-
34
- if (task.status !== "disputed") {
35
- throw new Error(`Task is ${task.status}, cannot resolve (must be disputed)`);
36
- }
37
-
38
- const escrow = await getEscrowDetails(task.id);
39
- if (!escrow) {
40
- throw new Error("No escrow found for this task");
41
- }
42
- if (escrow.status !== EscrowStatus.Disputed) {
43
- throw new Error("Escrow is not in disputed state");
44
- }
45
-
46
- if (!options.json) {
47
- console.log(`\nTask ID: ${task.id}`);
48
- console.log(`Escrow: ${formatEther(escrow.amount)} ETH`);
49
- console.log(`Dispute fee: ${formatEther(escrow.disputeFee)} ETH`);
50
- console.log(`Resolution: ${clientWins ? "CLIENT WINS (refund)" : "AGENT WINS (buyback)"}`);
51
- console.log("\nCalling resolveDispute on-chain...");
52
- }
53
-
54
- const txHash = await resolveEscrowDispute(wallet, task.id, clientWins);
55
-
56
- // Sign and notify worker
57
- const { signature, timestamp, nonce } = await signAction(wallet, "resolve", task.id);
58
-
59
- await fetch(`${APIS.MANDATE}/api/tasks/${task.id}/resolve`, {
60
- method: "POST",
61
- headers: { "Content-Type": "application/json" },
62
- body: JSON.stringify({
63
- resolution: options.winner,
64
- txHash,
65
- signature,
66
- timestamp,
67
- nonce,
68
- }),
69
- });
70
-
71
- if (options.json) {
72
- console.log(
73
- JSON.stringify({
74
- success: true,
75
- taskId: task.id,
76
- resolution: options.winner,
77
- txHash,
78
- })
79
- );
80
- return;
81
- }
82
-
83
- console.log("\nDispute resolved!");
84
- console.log("──────────────────────────────────────────────");
85
- console.log(`\nWinner: ${options.winner}`);
86
- console.log(`TX: ${txHash}`);
87
- if (clientWins) {
88
- console.log("\nClient gets escrow + dispute fee refunded.");
89
- } else {
90
- console.log("\nAgent gets dispute fee. Escrow released via buyback-and-burn.");
91
- }
92
- console.log();
93
- } catch (err) {
94
- const errorMsg = err instanceof Error ? err.message : String(err);
95
-
96
- if (options.json) {
97
- console.log(JSON.stringify({ error: errorMsg }));
98
- process.exit(1);
99
- }
100
-
101
- console.error(`\n❌ Failed to resolve: ${errorMsg}`);
102
- process.exit(1);
103
- }
104
- }
@@ -1,78 +0,0 @@
1
- // mltl reviews - View verified reviews for an agent
2
-
3
- import { APIS } from "../lib/constants.js";
4
-
5
- interface ReviewsOptions {
6
- agent: string;
7
- json?: boolean;
8
- }
9
-
10
- interface Review {
11
- taskId: string;
12
- score: number | null;
13
- comment: string | null;
14
- reviewer: string;
15
- ratedAt: number;
16
- ratedTxHash: string;
17
- }
18
-
19
- interface ReviewsResponse {
20
- reviews: Review[];
21
- total: number;
22
- avgScore: number | null;
23
- reviewCount: number;
24
- }
25
-
26
- function formatTimestamp(ts: number): string {
27
- return new Date(ts).toLocaleString();
28
- }
29
-
30
- export async function reviews(options: ReviewsOptions): Promise<void> {
31
- try {
32
- const res = await fetch(`${APIS.MANDATE}/api/agents/${encodeURIComponent(options.agent)}/reviews`);
33
- if (!res.ok) {
34
- const err = (await res.json()) as { error: string };
35
- throw new Error(err.error || `HTTP ${res.status}`);
36
- }
37
-
38
- const data = (await res.json()) as ReviewsResponse;
39
-
40
- if (options.json) {
41
- console.log(JSON.stringify(data));
42
- return;
43
- }
44
-
45
- console.log("\n═══════════════════════════════════════════════");
46
- console.log(` Reviews for Agent #${options.agent}`);
47
- console.log("═══════════════════════════════════════════════\n");
48
-
49
- if (data.avgScore !== null) {
50
- console.log(`Score: ${data.avgScore}/100 (${data.reviewCount} verified review${data.reviewCount !== 1 ? "s" : ""})\n`);
51
- }
52
-
53
- if (data.reviews.length === 0) {
54
- console.log("No reviews yet.\n");
55
- return;
56
- }
57
-
58
- console.log("───────────────────────────────────────────────\n");
59
- for (const review of data.reviews) {
60
- const scoreStr = review.score !== null ? `${review.score}/100` : "No score";
61
- console.log(`${scoreStr} ${formatTimestamp(review.ratedAt)}`);
62
- console.log(`Task: ${review.taskId}`);
63
- console.log(`Reviewer: ${review.reviewer}`);
64
- if (review.comment) console.log(`Comment: ${review.comment}`);
65
- console.log(`TX: ${review.ratedTxHash}\n`);
66
- }
67
- } catch (err) {
68
- const errorMsg = err instanceof Error ? err.message : String(err);
69
-
70
- if (options.json) {
71
- console.log(JSON.stringify({ error: errorMsg }));
72
- process.exit(1);
73
- }
74
-
75
- console.error(`\nFailed to fetch reviews: ${errorMsg}`);
76
- process.exit(1);
77
- }
78
- }
@@ -1,65 +0,0 @@
1
- // mltl revise - Request revision on submitted work (client action)
2
-
3
- import { loadOrCreateWallet } from "../lib/wallet.js";
4
- import { requestRevision, getTask } from "../lib/tasks.js";
5
-
6
- interface ReviseOptions {
7
- task: string;
8
- reason: string;
9
- json?: boolean;
10
- }
11
-
12
- export async function revise(options: ReviseOptions): Promise<void> {
13
- const { wallet } = await loadOrCreateWallet();
14
-
15
- if (!options.json) {
16
- console.log("\nRequesting revision...");
17
- console.log("──────────────────────────────────────────────");
18
- }
19
-
20
- try {
21
- const taskBefore = await getTask(options.task);
22
-
23
- if (taskBefore.status !== "submitted") {
24
- throw new Error(`Task is ${taskBefore.status}, can only request revision on submitted work`);
25
- }
26
-
27
- const task = await requestRevision(wallet, options.task, options.reason);
28
-
29
- if (options.json) {
30
- console.log(
31
- JSON.stringify({
32
- success: true,
33
- task: {
34
- id: task.id,
35
- agentId: task.agentId,
36
- status: task.status,
37
- revisionCount: task.revisionCount,
38
- },
39
- }),
40
- );
41
- return;
42
- }
43
-
44
- console.log("\nRevision requested!");
45
- console.log("──────────────────────────────────────────────");
46
- console.log(`\nTask ID: ${task.id}`);
47
- console.log(`Status: ${task.status}`);
48
- console.log(`Revisions: ${task.revisionCount || 1}`);
49
- console.log(`\nYour feedback:\n${options.reason}\n`);
50
- console.log("──────────────────────────────────────────────");
51
- console.log("\nThe agent will revise and resubmit their work.");
52
- console.log("You can also send messages with:");
53
- console.log(` mltl message --task ${task.id} --content "additional details"\n`);
54
- } catch (err) {
55
- const errorMsg = err instanceof Error ? err.message : String(err);
56
-
57
- if (options.json) {
58
- console.log(JSON.stringify({ error: errorMsg }));
59
- process.exit(1);
60
- }
61
-
62
- console.error(`\nFailed to request revision: ${errorMsg}`);
63
- process.exit(1);
64
- }
65
- }
@@ -1,123 +0,0 @@
1
- // mltl submit - Submit completed work for a task (agent action)
2
- // Also marks submission onchain to start 24h timeout countdown
3
-
4
- import { formatEther } from "viem";
5
- import { loadOrCreateWallet } from "../lib/wallet.js";
6
- import { submitTask, getTask } from "../lib/tasks.js";
7
- import { markSubmitted, isEscrowPending } from "../lib/escrow.js";
8
- import { uploadFiles } from "../lib/files.js";
9
- import type { TaskFile } from "../lib/types.js";
10
-
11
- interface SubmitOptions {
12
- task: string;
13
- result: string;
14
- files?: string;
15
- json?: boolean;
16
- }
17
-
18
- export async function submit(options: SubmitOptions): Promise<void> {
19
- const { wallet } = await loadOrCreateWallet();
20
-
21
- if (!options.json) {
22
- console.log("\nSubmitting work...");
23
- console.log("──────────────────────────────────────────────");
24
- }
25
-
26
- let isRevision = false;
27
-
28
- try {
29
- // First verify task exists and is in accepted state
30
- const taskBefore = await getTask(options.task);
31
-
32
- if (taskBefore.status !== "accepted" && taskBefore.status !== "revision") {
33
- throw new Error(`Task is ${taskBefore.status}, cannot submit (must be accepted or in revision)`);
34
- }
35
-
36
- isRevision = taskBefore.status === "revision";
37
-
38
- // Upload files if provided
39
- let uploadedFiles: TaskFile[] | undefined;
40
- if (options.files) {
41
- const filePaths = options.files.split(",").map((f) => f.trim());
42
- if (!options.json) {
43
- console.log(`\nUploading ${filePaths.length} file(s)...`);
44
- }
45
- uploadedFiles = await uploadFiles(wallet, options.task, filePaths);
46
- if (!options.json) {
47
- for (const f of uploadedFiles) {
48
- console.log(` Uploaded: ${f.name} (${f.size} bytes)`);
49
- }
50
- }
51
- }
52
-
53
- // Submit the work to task queue (signed with wallet)
54
- const task = await submitTask(wallet, options.task, options.result, uploadedFiles);
55
-
56
- // Check if escrow exists for this task (skip on revision — already marked on first submit)
57
- const hasEscrow = !isRevision && await isEscrowPending(options.task);
58
- let escrowTxHash: string | null = null;
59
-
60
- if (hasEscrow) {
61
- if (!options.json) {
62
- console.log("\nMarking submission onchain (starts 24h timeout)...");
63
- }
64
-
65
- // Mark submitted on escrow contract (starts timeout countdown)
66
- escrowTxHash = await markSubmitted(wallet, options.task);
67
-
68
- if (!options.json) {
69
- console.log(`Escrow TX: ${escrowTxHash}`);
70
- }
71
- }
72
-
73
- if (options.json) {
74
- console.log(
75
- JSON.stringify({
76
- success: true,
77
- task: {
78
- id: task.id,
79
- agentId: task.agentId,
80
- status: task.status,
81
- quotedPriceWei: task.quotedPriceWei,
82
- result: task.result,
83
- },
84
- escrow: escrowTxHash ? { txHash: escrowTxHash, timeoutHours: 24 } : null,
85
- }),
86
- );
87
- return;
88
- }
89
-
90
- const priceEth = task.quotedPriceWei ? formatEther(BigInt(task.quotedPriceWei)) : "0";
91
-
92
- console.log(isRevision ? "\n✅ Revised work submitted!" : "\n✅ Work submitted!");
93
- console.log("──────────────────────────────────────────────");
94
- console.log(`\nTask ID: ${task.id}`);
95
- console.log(`Status: ${task.status}`);
96
- if (isRevision) {
97
- console.log(`Revision: #${taskBefore.revisionCount || 1}`);
98
- }
99
- console.log(`Price: ${priceEth} ETH`);
100
- if (escrowTxHash) {
101
- console.log(`\nTimeout: 24 hours (auto-release if client doesn't respond)`);
102
- }
103
- console.log(`\nYour result:\n${options.result}\n`);
104
- console.log("──────────────────────────────────────────────");
105
- console.log("\nThe client will review your work.");
106
- if (escrowTxHash) {
107
- console.log("If they don't respond within 24h, you can claim payment with:");
108
- console.log(` mltl claim --task ${task.id}\n`);
109
- } else {
110
- console.log("Once approved, payment will be sent to your wallet.\n");
111
- }
112
- } catch (err) {
113
- const errorMsg = err instanceof Error ? err.message : String(err);
114
-
115
- if (options.json) {
116
- console.log(JSON.stringify({ error: errorMsg }));
117
- process.exit(1);
118
- }
119
-
120
- console.error(`\n❌ Failed to submit work: ${errorMsg}`);
121
- process.exit(1);
122
- }
123
- }