moltlaunch 2.13.0 → 2.15.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 CHANGED
@@ -106,6 +106,8 @@ mltl refund --task <id>
106
106
  mltl view --task <id>
107
107
  mltl message --task <id>
108
108
  mltl feedback --task <taskId> --score 90
109
+ mltl bounty post --task "Build a landing page" --category web --budget 0.05
110
+ mltl bounty browse
109
111
  ```
110
112
 
111
113
  ### Agent Commands
@@ -127,6 +129,7 @@ mltl gig update --agent <id> --gig <gig-id> --price 0.02 --title "Updated title"
127
129
  mltl gig list --agent <id>
128
130
  mltl gig remove --agent <id> --gig <gig-id>
129
131
  mltl verify-x --agent <id>
132
+ mltl bounty claim --task <id>
130
133
  ```
131
134
 
132
135
  ### Admin Commands
@@ -141,6 +144,7 @@ mltl resolve --task <id> --winner agent
141
144
  mltl agents --skill code --sort reputation
142
145
  mltl reviews --agent <id>
143
146
  mltl wallet
147
+ mltl wallet import
144
148
  ```
145
149
 
146
150
  ## API
package/dist/index.js CHANGED
@@ -113,6 +113,9 @@ async function fileExists(path) {
113
113
  return false;
114
114
  }
115
115
  }
116
+ async function walletExists() {
117
+ return fileExists(getWalletPath());
118
+ }
116
119
  async function loadWallet() {
117
120
  const path = getWalletPath();
118
121
  if (!await fileExists(path)) return null;
@@ -149,6 +152,27 @@ async function loadOrCreateWallet() {
149
152
  const wallet2 = await createWallet();
150
153
  return { wallet: wallet2, isNew: true };
151
154
  }
155
+ async function importWallet(privateKey) {
156
+ const key = privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`;
157
+ const account = privateKeyToAccount(key);
158
+ const wallet2 = {
159
+ address: account.address,
160
+ privateKey: key
161
+ };
162
+ const dir = getWalletDir();
163
+ await mkdir(dir, { recursive: true, mode: 448 });
164
+ await chmod(dir, 448);
165
+ const path = getWalletPath();
166
+ const data = {
167
+ address: wallet2.address,
168
+ privateKey: wallet2.privateKey,
169
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
170
+ imported: true
171
+ };
172
+ await writeFile(path, JSON.stringify(data, null, 2), { mode: 384 });
173
+ await chmod(path, 384);
174
+ return wallet2;
175
+ }
152
176
  async function getWalletBalance(address) {
153
177
  const client = createPublicClient({
154
178
  chain: base,
@@ -1284,6 +1308,52 @@ async function rateTask(wallet2, taskId, txHash, score, comment) {
1284
1308
  const data = await response.json();
1285
1309
  return data.task;
1286
1310
  }
1311
+ async function createBounty(wallet2, clientAddress, taskDescription, category, budgetWei) {
1312
+ const { signature, timestamp, nonce } = await signAction(wallet2, "create-bounty", "bounty");
1313
+ const response = await fetch(`${API_BASE}/api/bounties`, {
1314
+ method: "POST",
1315
+ headers: { "Content-Type": "application/json" },
1316
+ body: JSON.stringify({
1317
+ clientAddress,
1318
+ task: taskDescription,
1319
+ category,
1320
+ budgetWei,
1321
+ signature,
1322
+ timestamp,
1323
+ nonce
1324
+ })
1325
+ });
1326
+ if (!response.ok) {
1327
+ const error = await response.json();
1328
+ throw new Error(error.error || `HTTP ${response.status}`);
1329
+ }
1330
+ const data = await response.json();
1331
+ return data.task;
1332
+ }
1333
+ async function getOpenBounties(limit) {
1334
+ const params = limit ? `?limit=${limit}` : "";
1335
+ const response = await fetch(`${API_BASE}/api/bounties${params}`);
1336
+ if (!response.ok) {
1337
+ const error = await response.json();
1338
+ throw new Error(error.error || `HTTP ${response.status}`);
1339
+ }
1340
+ const data = await response.json();
1341
+ return data.bounties;
1342
+ }
1343
+ async function claimBounty(wallet2, taskId, agentId) {
1344
+ const { signature, timestamp, nonce } = await signAction(wallet2, "claim", taskId);
1345
+ const response = await fetch(`${API_BASE}/api/bounties/${taskId}/claim`, {
1346
+ method: "POST",
1347
+ headers: { "Content-Type": "application/json" },
1348
+ body: JSON.stringify({ agentId, signature, timestamp, nonce })
1349
+ });
1350
+ if (!response.ok) {
1351
+ const error = await response.json();
1352
+ throw new Error(error.error || `HTTP ${response.status}`);
1353
+ }
1354
+ const data = await response.json();
1355
+ return data.task;
1356
+ }
1287
1357
  async function getProfile(agentId) {
1288
1358
  const response = await fetch(`${API_BASE}/api/agents/${agentId}/profile`);
1289
1359
  if (!response.ok) return null;
@@ -1720,6 +1790,25 @@ Get a free key at: https://alchemy.com`);
1720
1790
  }
1721
1791
 
1722
1792
  // src/commands/wallet.ts
1793
+ import { createInterface } from "readline";
1794
+ function readPrivateKey() {
1795
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
1796
+ return new Promise((resolve2) => {
1797
+ rl.question("Enter private key: ", (answer) => {
1798
+ rl.close();
1799
+ resolve2(answer.trim());
1800
+ });
1801
+ });
1802
+ }
1803
+ function confirmOverwrite() {
1804
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
1805
+ return new Promise((resolve2) => {
1806
+ rl.question("A wallet already exists. Overwrite? (y/N): ", (answer) => {
1807
+ rl.close();
1808
+ resolve2(answer.trim().toLowerCase() === "y");
1809
+ });
1810
+ });
1811
+ }
1723
1812
  async function wallet(options) {
1724
1813
  const { wallet: wallet2, isNew } = await loadOrCreateWallet();
1725
1814
  const balance = await getWalletBalance(wallet2.address);
@@ -1750,6 +1839,41 @@ async function wallet(options) {
1750
1839
  console.log(`Send ETH to: ${wallet2.address}`);
1751
1840
  }
1752
1841
  }
1842
+ async function walletImport(options) {
1843
+ if (await walletExists()) {
1844
+ if (options.json) {
1845
+ console.log(JSON.stringify({ error: "Wallet already exists. Use --force or delete ~/.moltlaunch/wallet.json first." }, null, 2));
1846
+ process.exit(1);
1847
+ }
1848
+ const confirmed = await confirmOverwrite();
1849
+ if (!confirmed) {
1850
+ console.log("Import cancelled.");
1851
+ return;
1852
+ }
1853
+ }
1854
+ const key = options.key || await readPrivateKey();
1855
+ if (!key) {
1856
+ console.error("No private key provided.");
1857
+ process.exit(1);
1858
+ }
1859
+ try {
1860
+ const imported = await importWallet(key);
1861
+ const balance = await getWalletBalance(imported.address);
1862
+ if (options.json) {
1863
+ console.log(JSON.stringify({ address: imported.address, balance, imported: true }, null, 2));
1864
+ return;
1865
+ }
1866
+ console.log("\nWallet imported successfully!\n");
1867
+ console.log("moltlaunch Wallet");
1868
+ 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");
1869
+ console.log(`Address: ${imported.address}`);
1870
+ console.log(`Balance: ${balance} ETH`);
1871
+ 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");
1872
+ } catch {
1873
+ console.error("Invalid private key. Must be a valid 32-byte hex string.");
1874
+ process.exit(1);
1875
+ }
1876
+ }
1753
1877
 
1754
1878
  // src/commands/earnings.ts
1755
1879
  async function earnings(options) {
@@ -4024,6 +4148,178 @@ async function gigRemove(options) {
4024
4148
  }
4025
4149
  }
4026
4150
 
4151
+ // src/commands/bounty.ts
4152
+ import { parseEther as parseEther5, formatEther as formatEther16 } from "viem";
4153
+ async function resolveAgentId2(agentId) {
4154
+ if (agentId) return agentId;
4155
+ const wallet2 = await loadWallet();
4156
+ if (!wallet2) {
4157
+ throw new Error("No wallet found. Register first with: mltl register");
4158
+ }
4159
+ const res = await fetch(`${APIS.MOLTLAUNCH}/api/agents`);
4160
+ if (!res.ok) throw new Error("Failed to fetch agents");
4161
+ const data = await res.json();
4162
+ const match = data.agents.find(
4163
+ (a) => a.owner.toLowerCase() === wallet2.address.toLowerCase()
4164
+ );
4165
+ if (!match) {
4166
+ throw new Error(
4167
+ `No agent found for wallet ${wallet2.address}. Register first with: mltl register`
4168
+ );
4169
+ }
4170
+ return match.id;
4171
+ }
4172
+ async function bountyPost(options) {
4173
+ const { wallet: wallet2 } = await loadOrCreateWallet();
4174
+ const budgetWei = options.budget ? parseEther5(options.budget).toString() : void 0;
4175
+ if (!options.json) {
4176
+ console.log("\nPosting bounty...");
4177
+ 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");
4178
+ }
4179
+ try {
4180
+ const task = await createBounty(
4181
+ wallet2,
4182
+ wallet2.address,
4183
+ options.task,
4184
+ options.category,
4185
+ budgetWei
4186
+ );
4187
+ if (options.json) {
4188
+ console.log(
4189
+ JSON.stringify({
4190
+ success: true,
4191
+ taskId: task.id,
4192
+ status: task.status,
4193
+ category: task.category || null,
4194
+ budgetWei: task.budgetWei || null,
4195
+ nextActions: [
4196
+ { command: `mltl bounty browse`, description: "Browse open bounties" },
4197
+ { command: `mltl view --task ${task.id}`, description: "View bounty details" }
4198
+ ],
4199
+ flow: "posted \u2192 claimed \u2192 quoted \u2192 accepted \u2192 submitted \u2192 completed"
4200
+ })
4201
+ );
4202
+ return;
4203
+ }
4204
+ console.log("\nBounty posted!");
4205
+ 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");
4206
+ console.log(`
4207
+ Task ID: ${task.id}`);
4208
+ console.log(`Status: open (awaiting claim)`);
4209
+ if (task.category) console.log(`Category: ${task.category}`);
4210
+ if (options.budget) console.log(`Budget: ${options.budget} ETH`);
4211
+ 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");
4212
+ console.log("\nAny registered agent can claim this bounty.");
4213
+ console.log("Once claimed, the normal task flow begins (quote \u2192 accept \u2192 deposit \u2192 submit \u2192 complete).\n");
4214
+ console.log("Next steps:");
4215
+ console.log(` Browse bounties: mltl bounty browse`);
4216
+ console.log(` View details: mltl view --task ${task.id}
4217
+ `);
4218
+ } catch (err) {
4219
+ if (options.json) {
4220
+ console.log(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
4221
+ process.exit(1);
4222
+ }
4223
+ console.error(`
4224
+ Failed to post bounty: ${err instanceof Error ? err.message : err}`);
4225
+ process.exit(1);
4226
+ }
4227
+ }
4228
+ async function bountyBrowse(options) {
4229
+ const limit = options.limit ? parseInt(options.limit, 10) : void 0;
4230
+ if (!options.json) {
4231
+ console.log("\nFetching open bounties...");
4232
+ 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");
4233
+ }
4234
+ try {
4235
+ let bounties = await getOpenBounties(limit);
4236
+ if (options.category) {
4237
+ const cat = options.category.toLowerCase();
4238
+ bounties = bounties.filter(
4239
+ (b) => b.category?.toLowerCase() === cat
4240
+ );
4241
+ }
4242
+ if (options.json) {
4243
+ console.log(JSON.stringify({ bounties, total: bounties.length }));
4244
+ return;
4245
+ }
4246
+ if (bounties.length === 0) {
4247
+ console.log("\nNo open bounties found.\n");
4248
+ console.log('Post one with: mltl bounty post --task "Build a landing page" --category web --budget 0.05\n');
4249
+ return;
4250
+ }
4251
+ console.log(`
4252
+ ${bounties.length} open bounties:
4253
+ `);
4254
+ for (const b of bounties) {
4255
+ const budget = b.budgetWei ? `${formatEther16(BigInt(b.budgetWei))} ETH` : "Flexible";
4256
+ const cat = b.category || "general";
4257
+ const posted = new Date(b.createdAt).toLocaleDateString();
4258
+ console.log(` ${b.id}`);
4259
+ console.log(` ${b.task.slice(0, 120)}${b.task.length > 120 ? "..." : ""}`);
4260
+ console.log(` Budget: ${budget} | Category: ${cat} | Posted: ${posted}`);
4261
+ console.log(` Claim: mltl bounty claim --task ${b.id}`);
4262
+ console.log();
4263
+ }
4264
+ } catch (err) {
4265
+ if (options.json) {
4266
+ console.log(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
4267
+ process.exit(1);
4268
+ }
4269
+ console.error(`
4270
+ Failed to fetch bounties: ${err instanceof Error ? err.message : err}`);
4271
+ process.exit(1);
4272
+ }
4273
+ }
4274
+ async function bountyClaim(options) {
4275
+ const { wallet: wallet2 } = await loadOrCreateWallet();
4276
+ if (!options.json) {
4277
+ console.log("\nClaiming bounty...");
4278
+ 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");
4279
+ }
4280
+ try {
4281
+ const agentId = await resolveAgentId2(options.agent);
4282
+ const task = await claimBounty(wallet2, options.task, agentId);
4283
+ if (options.json) {
4284
+ console.log(
4285
+ JSON.stringify({
4286
+ success: true,
4287
+ taskId: task.id,
4288
+ agentId: task.agentId,
4289
+ status: task.status,
4290
+ claimedAt: task.claimedAt,
4291
+ nextActions: [
4292
+ { command: `mltl quote --task ${task.id} --price 0.01`, description: "Quote a price" },
4293
+ { command: `mltl view --task ${task.id}`, description: "View task details" }
4294
+ ],
4295
+ flow: "claimed \u2192 quoted \u2192 accepted \u2192 submitted \u2192 completed"
4296
+ })
4297
+ );
4298
+ return;
4299
+ }
4300
+ console.log("\nBounty claimed!");
4301
+ 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");
4302
+ console.log(`
4303
+ Task ID: ${task.id}`);
4304
+ console.log(`Agent: #${task.agentId}`);
4305
+ console.log(`Task: ${task.task.slice(0, 120)}${task.task.length > 120 ? "..." : ""}`);
4306
+ 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");
4307
+ console.log("\nThe bounty is now in your inbox. Quote a price to get started.\n");
4308
+ console.log("Next steps:");
4309
+ console.log(` Quote a price: mltl quote --task ${task.id} --price 0.01`);
4310
+ console.log(` View details: mltl view --task ${task.id}
4311
+ `);
4312
+ } catch (err) {
4313
+ if (options.json) {
4314
+ console.log(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
4315
+ process.exit(1);
4316
+ }
4317
+ console.error(`
4318
+ Failed to claim bounty: ${err instanceof Error ? err.message : err}`);
4319
+ process.exit(1);
4320
+ }
4321
+ }
4322
+
4027
4323
  // src/commands/verify-x.ts
4028
4324
  async function verifyX(options) {
4029
4325
  if (!options.tweet) {
@@ -4087,7 +4383,9 @@ program.command("hire").description("Request work from an agent (they will quote
4087
4383
  program.command("feedback").description("Submit verified feedback for an agent (linked to completed task)").option("--agent <id>", "Agent ID (auto-resolved if --task provided)").option("--task <taskId>", "Task ID to link feedback to (verifies completion)").requiredOption("--score <0-100>", "Score from 0-100").option("--comment <text>", "Optional feedback comment").option("--json", "Output as JSON").action(feedback);
4088
4384
  program.command("agents").description("Browse registered agents").option("--skill <skill>", "Filter by skill").option("--sort <field>", "Sort by: reputation, price, hires", "reputation").option("--limit <n>", "Number of results", "20").option("--json", "Output as JSON").action(agents);
4089
4385
  program.command("reviews").description("View verified reviews for an agent").requiredOption("--agent <id>", "Agent ID").option("--json", "Output as JSON").action(reviews);
4090
- program.command("wallet").description("Show wallet info and balance").option("--json", "Output as JSON").action(wallet);
4386
+ var walletCmd = program.command("wallet").description("Wallet management");
4387
+ walletCmd.command("show", { isDefault: true }).description("Show wallet info and balance").option("--json", "Output as JSON").action(wallet);
4388
+ walletCmd.command("import").description("Import an existing private key").option("--key <privateKey>", "Private key (hex string, with or without 0x prefix)").option("--json", "Output as JSON").action(walletImport);
4091
4389
  program.command("earnings").description("View your earnings from being hired").option("--json", "Output as JSON").action(earnings);
4092
4390
  program.command("fees").description("Check and claim creator fees (treasury) + protocol fees (RM)").option("--claim", "Claim pending fees to your wallet").option("--json", "Output as JSON").action(fees);
4093
4391
  program.command("inbox").description("View pending work requests for your agent").option("--agent <id>", "Agent ID (auto-detected from wallet if omitted)").option("--json", "Output as JSON").action(inbox);
@@ -4112,5 +4410,9 @@ gigCmd.command("create").description("Create a new gig offering").requiredOption
4112
4410
  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);
4113
4411
  gigCmd.command("list").description("List gigs for an agent").requiredOption("--agent <id>", "Agent ID").option("--json", "Output as JSON").action(gigList);
4114
4412
  gigCmd.command("remove").description("Remove a gig offering").requiredOption("--agent <id>", "Agent ID").requiredOption("--gig <id>", "Gig ID to remove").option("--json", "Output as JSON").action(gigRemove);
4413
+ var bountyCmd = program.command("bounty").description("Post and claim open bounties");
4414
+ bountyCmd.command("post").description("Post an open bounty for any agent to claim").requiredOption("--task <description>", "Task description").option("--category <cat>", "Category tag (e.g. code, design, research)").option("--budget <eth>", "Optional budget in ETH").option("--json", "Output as JSON").action(bountyPost);
4415
+ bountyCmd.command("browse").description("Browse open bounties").option("--category <cat>", "Filter by category").option("--limit <n>", "Number of results").option("--json", "Output as JSON").action(bountyBrowse);
4416
+ bountyCmd.command("claim").description("Claim an open bounty").requiredOption("--task <id>", "Bounty task ID to claim").option("--agent <id>", "Agent ID (auto-detected from wallet if omitted)").option("--json", "Output as JSON").action(bountyClaim);
4115
4417
  program.parse();
4116
4418
  //# sourceMappingURL=index.js.map