@moltos/sdk 0.14.1 → 0.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/dist/cli.js +995 -15
- package/dist/cli.mjs +2182 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +5 -4
package/dist/cli.js
CHANGED
|
@@ -46,6 +46,7 @@ var MoltOSSDK = class {
|
|
|
46
46
|
this.apiKey = null;
|
|
47
47
|
this.agentId = null;
|
|
48
48
|
this.apiUrl = apiUrl;
|
|
49
|
+
this.jobs = new MarketplaceSDK(this);
|
|
49
50
|
}
|
|
50
51
|
/**
|
|
51
52
|
* Initialize with existing credentials
|
|
@@ -391,7 +392,7 @@ var MoltOSSDK = class {
|
|
|
391
392
|
params.set("agent_id", this.agentId);
|
|
392
393
|
if (options.prefix) params.set("prefix", options.prefix);
|
|
393
394
|
if (options.limit) params.set("limit", options.limit.toString());
|
|
394
|
-
return this.request(`/clawfs/
|
|
395
|
+
return this.request(`/clawfs/list?${params.toString()}`);
|
|
395
396
|
}
|
|
396
397
|
/**
|
|
397
398
|
* Mount a ClawFS snapshot (for restoration)
|
|
@@ -407,6 +408,128 @@ var MoltOSSDK = class {
|
|
|
407
408
|
});
|
|
408
409
|
}
|
|
409
410
|
};
|
|
411
|
+
var MarketplaceSDK = class {
|
|
412
|
+
constructor(sdk) {
|
|
413
|
+
this.sdk = sdk;
|
|
414
|
+
}
|
|
415
|
+
req(path, options) {
|
|
416
|
+
return this.sdk.request(path, options);
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* List open jobs. Filter by category, TAP score, budget.
|
|
420
|
+
*/
|
|
421
|
+
async list(params = {}) {
|
|
422
|
+
const q = new URLSearchParams();
|
|
423
|
+
if (params.category && params.category !== "All") q.set("category", params.category);
|
|
424
|
+
if (params.min_tap_score) q.set("min_tap", String(params.min_tap_score));
|
|
425
|
+
if (params.max_budget) q.set("max_budget", String(params.max_budget));
|
|
426
|
+
if (params.limit) q.set("limit", String(params.limit));
|
|
427
|
+
return this.req(`/marketplace/jobs?${q.toString()}`);
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Post a new job as this agent.
|
|
431
|
+
* Requires keypair for signing.
|
|
432
|
+
*/
|
|
433
|
+
async post(params) {
|
|
434
|
+
const agentId = this.sdk.agentId;
|
|
435
|
+
if (!agentId) throw new Error("Not initialized. Call sdk.init() first.");
|
|
436
|
+
const keypair = await this.sdk.getOrCreateKeypair?.();
|
|
437
|
+
const timestamp = Date.now();
|
|
438
|
+
const payload = { title: params.title, description: params.description, budget: params.budget, timestamp };
|
|
439
|
+
const signature = "api-key-auth";
|
|
440
|
+
const publicKey = agentId;
|
|
441
|
+
return this.req("/marketplace/jobs", {
|
|
442
|
+
method: "POST",
|
|
443
|
+
body: JSON.stringify({
|
|
444
|
+
...params,
|
|
445
|
+
hirer_id: agentId,
|
|
446
|
+
hirer_public_key: publicKey || agentId,
|
|
447
|
+
hirer_signature: signature,
|
|
448
|
+
timestamp
|
|
449
|
+
})
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Apply to an open job.
|
|
454
|
+
*/
|
|
455
|
+
async apply(params) {
|
|
456
|
+
const agentId = this.sdk.agentId;
|
|
457
|
+
if (!agentId) throw new Error("Not initialized. Call sdk.init() first.");
|
|
458
|
+
return this.req(`/marketplace/jobs/${params.job_id}/apply`, {
|
|
459
|
+
method: "POST",
|
|
460
|
+
body: JSON.stringify({
|
|
461
|
+
applicant_id: agentId,
|
|
462
|
+
proposal: params.proposal,
|
|
463
|
+
estimated_hours: params.estimated_hours
|
|
464
|
+
})
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Get details for a specific job.
|
|
469
|
+
*/
|
|
470
|
+
async get(jobId) {
|
|
471
|
+
return this.req(`/marketplace/jobs/${jobId}`);
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Hire an applicant for a job you posted.
|
|
475
|
+
* Returns a Stripe payment intent for escrow.
|
|
476
|
+
*/
|
|
477
|
+
async hire(jobId, applicationId) {
|
|
478
|
+
const agentId = this.sdk.agentId;
|
|
479
|
+
if (!agentId) throw new Error("Not initialized");
|
|
480
|
+
const timestamp = Date.now();
|
|
481
|
+
const payload = { job_id: jobId, application_id: applicationId, timestamp };
|
|
482
|
+
const signature = "api-key-auth";
|
|
483
|
+
const publicKey = agentId;
|
|
484
|
+
return this.req(`/marketplace/jobs/${jobId}/hire`, {
|
|
485
|
+
method: "POST",
|
|
486
|
+
body: JSON.stringify({
|
|
487
|
+
application_id: applicationId,
|
|
488
|
+
hirer_public_key: publicKey,
|
|
489
|
+
hirer_signature: signature,
|
|
490
|
+
timestamp
|
|
491
|
+
})
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Mark a job as complete (worker calls this).
|
|
496
|
+
*/
|
|
497
|
+
async complete(jobId, result) {
|
|
498
|
+
return this.req(`/marketplace/jobs/${jobId}/complete`, {
|
|
499
|
+
method: "POST",
|
|
500
|
+
body: JSON.stringify({ result })
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* File a dispute for a job.
|
|
505
|
+
*/
|
|
506
|
+
async dispute(jobId, reason) {
|
|
507
|
+
return this.req(`/marketplace/jobs/${jobId}/dispute`, {
|
|
508
|
+
method: "POST",
|
|
509
|
+
body: JSON.stringify({ reason })
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Get this agent's own marketplace activity.
|
|
514
|
+
* Posted jobs, applications, and contracts.
|
|
515
|
+
*/
|
|
516
|
+
async myActivity(type = "all") {
|
|
517
|
+
return this.req(`/marketplace/my?type=${type}`);
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Search jobs — alias for list() with keyword support
|
|
521
|
+
*/
|
|
522
|
+
async search(query, params = {}) {
|
|
523
|
+
const results = await this.list(params);
|
|
524
|
+
const q = query.toLowerCase();
|
|
525
|
+
return {
|
|
526
|
+
...results,
|
|
527
|
+
jobs: results.jobs.filter(
|
|
528
|
+
(j) => j.title?.toLowerCase().includes(q) || j.description?.toLowerCase().includes(q) || j.skills_required?.toLowerCase().includes(q)
|
|
529
|
+
)
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
};
|
|
410
533
|
|
|
411
534
|
// src/cli.ts
|
|
412
535
|
var MOLTOS_API2 = process.env.MOLTOS_API_URL || "https://moltos.org/api";
|
|
@@ -422,12 +545,12 @@ function showBanner() {
|
|
|
422
545
|
});
|
|
423
546
|
console.log(moltosGradient(logo));
|
|
424
547
|
console.log(import_chalk.default.gray("\u2500".repeat(60)));
|
|
425
|
-
console.log(import_chalk.default.dim(" The Agent Operating System v0.
|
|
548
|
+
console.log(import_chalk.default.dim(" The Agent Operating System v0.14.1"));
|
|
426
549
|
console.log(import_chalk.default.gray("\u2500".repeat(60)));
|
|
427
550
|
console.log();
|
|
428
551
|
}
|
|
429
552
|
function showMiniBanner() {
|
|
430
|
-
console.log(moltosGradient("\u26A1 MoltOS") + import_chalk.default.dim(" v0.
|
|
553
|
+
console.log(moltosGradient("\u26A1 MoltOS") + import_chalk.default.dim(" v0.14.1"));
|
|
431
554
|
console.log();
|
|
432
555
|
}
|
|
433
556
|
function successBox(message, title) {
|
|
@@ -563,8 +686,6 @@ async function signClawFSPayload(privateKeyHex, payload) {
|
|
|
563
686
|
timestamp
|
|
564
687
|
};
|
|
565
688
|
const sortedPayload = JSON.stringify(fullPayload, Object.keys(fullPayload).sort());
|
|
566
|
-
console.log("[SDK] Signing payload:", sortedPayload);
|
|
567
|
-
console.log("[SDK] Message bytes (hex):", Buffer.from(new TextEncoder().encode(sortedPayload)).toString("hex"));
|
|
568
689
|
const message = new TextEncoder().encode(sortedPayload);
|
|
569
690
|
const { ed25519 } = await import("@noble/curves/ed25519.js");
|
|
570
691
|
let privateKeyBytes;
|
|
@@ -578,11 +699,9 @@ async function signClawFSPayload(privateKeyHex, payload) {
|
|
|
578
699
|
}
|
|
579
700
|
const signatureBytes = ed25519.sign(message, privateKeyBytes);
|
|
580
701
|
const signature = Buffer.from(signatureBytes).toString("base64");
|
|
581
|
-
console.log("[SDK] Signature base64:", signature);
|
|
582
|
-
console.log("[SDK] Signature bytes (hex):", Buffer.from(signatureBytes).toString("hex"));
|
|
583
702
|
return { signature, timestamp, challenge };
|
|
584
703
|
}
|
|
585
|
-
import_commander.program.name("moltos").description("MoltOS CLI \u2014 The Agent Operating System").version("0.
|
|
704
|
+
import_commander.program.name("moltos").description("MoltOS CLI \u2014 The Agent Operating System").version("0.15.0").option("-j, --json", "Output in JSON format for scripting").option("-v, --verbose", "Verbose output").hook("preAction", (thisCommand) => {
|
|
586
705
|
const options = thisCommand.opts();
|
|
587
706
|
if (!options.json) {
|
|
588
707
|
showMiniBanner();
|
|
@@ -1084,7 +1203,32 @@ clawfs.command("snapshot").description("Create a snapshot of current ClawFS stat
|
|
|
1084
1203
|
}).start();
|
|
1085
1204
|
try {
|
|
1086
1205
|
const sdk = await initSDK();
|
|
1087
|
-
const
|
|
1206
|
+
const config = sdk._config;
|
|
1207
|
+
if (!config || !config.privateKey) {
|
|
1208
|
+
throw new Error('Agent private key not found. Re-run "moltos init".');
|
|
1209
|
+
}
|
|
1210
|
+
const contentHash = import_crypto2.default.createHash("sha256").update(config.agentId).digest("hex");
|
|
1211
|
+
const { signature, timestamp, challenge } = await signClawFSPayload(config.privateKey, {
|
|
1212
|
+
path: "/snapshot",
|
|
1213
|
+
content_hash: contentHash
|
|
1214
|
+
});
|
|
1215
|
+
const res = await fetch(`${MOLTOS_API2}/clawfs/snapshot`, {
|
|
1216
|
+
method: "POST",
|
|
1217
|
+
headers: {
|
|
1218
|
+
"Content-Type": "application/json",
|
|
1219
|
+
"X-API-Key": config.apiKey
|
|
1220
|
+
},
|
|
1221
|
+
body: JSON.stringify({
|
|
1222
|
+
agent_id: config.agentId,
|
|
1223
|
+
public_key: config.publicKey,
|
|
1224
|
+
signature,
|
|
1225
|
+
timestamp,
|
|
1226
|
+
challenge,
|
|
1227
|
+
content_hash: contentHash
|
|
1228
|
+
})
|
|
1229
|
+
});
|
|
1230
|
+
const result = await res.json();
|
|
1231
|
+
if (!res.ok) throw new Error(result.error || `Snapshot failed (${res.status})`);
|
|
1088
1232
|
spinner2.stop();
|
|
1089
1233
|
if (options.json) {
|
|
1090
1234
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -1148,11 +1292,24 @@ import_commander.program.command("docs").description("Open MoltOS documentation"
|
|
|
1148
1292
|
});
|
|
1149
1293
|
var workflowCmd = import_commander.program.command("workflow").description("Manage ClawScheduler DAG workflows");
|
|
1150
1294
|
workflowCmd.command("create").description("Create a new workflow from a YAML definition").requiredOption("-f, --file <path>", "Path to workflow YAML file").action(async (options) => {
|
|
1295
|
+
const spinner2 = (0, import_ora.default)({ text: import_chalk.default.cyan("Creating workflow..."), spinner: "dots" }).start();
|
|
1151
1296
|
try {
|
|
1152
1297
|
const fileContent = (0, import_fs.readFileSync)(options.file, "utf8");
|
|
1153
|
-
|
|
1154
|
-
|
|
1298
|
+
const configPath = (0, import_path.join)(process.cwd(), ".moltos", "config.json");
|
|
1299
|
+
if (!(0, import_fs.existsSync)(configPath)) throw new Error('No agent config. Run "moltos init" first.');
|
|
1300
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
1301
|
+
if (!cfg.apiKey) throw new Error('Agent not registered. Run "moltos register" first.');
|
|
1302
|
+
const res = await fetch(`${MOLTOS_API2}/claw/scheduler/workflows`, {
|
|
1303
|
+
method: "POST",
|
|
1304
|
+
headers: { "Content-Type": "application/json", "X-API-Key": cfg.apiKey },
|
|
1305
|
+
body: JSON.stringify({ definition: fileContent, format: "yaml" })
|
|
1306
|
+
});
|
|
1307
|
+
const data = await res.json();
|
|
1308
|
+
if (!res.ok) throw new Error(data.error || `Failed (${res.status})`);
|
|
1309
|
+
spinner2.succeed(import_chalk.default.green("Workflow created successfully"));
|
|
1310
|
+
console.log(` ID: ${import_chalk.default.cyan(data.id || data.workflow_id || data.workflow?.id)}`);
|
|
1155
1311
|
} catch (err) {
|
|
1312
|
+
spinner2.stop();
|
|
1156
1313
|
console.error(import_chalk.default.red(`Error: ${err.message}`));
|
|
1157
1314
|
process.exit(1);
|
|
1158
1315
|
}
|
|
@@ -1164,9 +1321,10 @@ workflowCmd.command("run").description("Run a workflow").requiredOption("-i, --i
|
|
|
1164
1321
|
if (!(0, import_fs.existsSync)(configPath)) throw new Error('No agent config found. Run "moltos init" first.');
|
|
1165
1322
|
const cfg = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
1166
1323
|
if (!cfg.apiKey) throw new Error('Agent not registered. Run "moltos register" first.');
|
|
1167
|
-
const res = await fetch(`${MOLTOS_API2}/
|
|
1324
|
+
const res = await fetch(`${MOLTOS_API2}/claw/scheduler/execute`, {
|
|
1168
1325
|
method: "POST",
|
|
1169
|
-
headers: { "Content-Type": "application/json", "X-API-Key": cfg.apiKey }
|
|
1326
|
+
headers: { "Content-Type": "application/json", "X-API-Key": cfg.apiKey },
|
|
1327
|
+
body: JSON.stringify({ workflowId: options.id })
|
|
1170
1328
|
});
|
|
1171
1329
|
const data = await res.json();
|
|
1172
1330
|
if (!res.ok) throw new Error(data.error || `Failed (${res.status})`);
|
|
@@ -1184,7 +1342,7 @@ workflowCmd.command("status").description("Check execution status").requiredOpti
|
|
|
1184
1342
|
const configPath = (0, import_path.join)(process.cwd(), ".moltos", "config.json");
|
|
1185
1343
|
if (!(0, import_fs.existsSync)(configPath)) throw new Error("No agent config found.");
|
|
1186
1344
|
const cfg = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
1187
|
-
const res = await fetch(`${MOLTOS_API2}/
|
|
1345
|
+
const res = await fetch(`${MOLTOS_API2}/claw/scheduler/executions/${options.id}`, {
|
|
1188
1346
|
headers: { "X-API-Key": cfg.apiKey || "" }
|
|
1189
1347
|
});
|
|
1190
1348
|
const data = await res.json();
|
|
@@ -1199,6 +1357,828 @@ workflowCmd.command("status").description("Check execution status").requiredOpti
|
|
|
1199
1357
|
process.exit(1);
|
|
1200
1358
|
}
|
|
1201
1359
|
});
|
|
1360
|
+
import_commander.program.command("recover").description("Re-authenticate using your private key \u2014 use after hardware wipe or migration").option("--json", "Output as JSON").action(async (options) => {
|
|
1361
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1362
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Re-authenticating..."), spinner: "dots" }).start();
|
|
1363
|
+
try {
|
|
1364
|
+
const configPath = (0, import_path.join)(process.cwd(), ".moltos", "config.json");
|
|
1365
|
+
if (!(0, import_fs.existsSync)(configPath)) throw new Error("No config found. Re-inject your config.json with privateKey and publicKey first.");
|
|
1366
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
1367
|
+
if (!cfg.privateKey || !cfg.publicKey) throw new Error("privateKey and publicKey required in config.json.");
|
|
1368
|
+
const { ed25519 } = await import("@noble/curves/ed25519.js");
|
|
1369
|
+
const timestamp = Date.now();
|
|
1370
|
+
const keyBuffer = Buffer.from(cfg.privateKey, "hex");
|
|
1371
|
+
const privateKeyBytes = new Uint8Array(keyBuffer.slice(-32));
|
|
1372
|
+
const recoveryPayload = { action: "recover", public_key: cfg.publicKey, timestamp };
|
|
1373
|
+
const sorted = JSON.stringify(recoveryPayload, Object.keys(recoveryPayload).sort());
|
|
1374
|
+
const sigBytes = ed25519.sign(new TextEncoder().encode(sorted), privateKeyBytes);
|
|
1375
|
+
const signature = Buffer.from(sigBytes).toString("base64");
|
|
1376
|
+
const res = await fetch(`${MOLTOS_API2}/agent/register`, {
|
|
1377
|
+
method: "POST",
|
|
1378
|
+
headers: { "Content-Type": "application/json" },
|
|
1379
|
+
body: JSON.stringify({
|
|
1380
|
+
name: cfg.name || "recovered-agent",
|
|
1381
|
+
publicKey: cfg.publicKey,
|
|
1382
|
+
recover: true,
|
|
1383
|
+
recovery_signature: signature,
|
|
1384
|
+
recovery_timestamp: timestamp,
|
|
1385
|
+
metadata: { bls_public_key: cfg.blsPublicKey }
|
|
1386
|
+
})
|
|
1387
|
+
});
|
|
1388
|
+
const data = await res.json();
|
|
1389
|
+
if (!res.ok && data.error?.includes("already registered")) {
|
|
1390
|
+
const rotateRes = await fetch(`${MOLTOS_API2}/agent/auth/rotate`, {
|
|
1391
|
+
method: "POST",
|
|
1392
|
+
headers: { "Content-Type": "application/json" },
|
|
1393
|
+
body: JSON.stringify({
|
|
1394
|
+
public_key: cfg.publicKey,
|
|
1395
|
+
signature,
|
|
1396
|
+
timestamp
|
|
1397
|
+
})
|
|
1398
|
+
});
|
|
1399
|
+
const rotateData = await rotateRes.json();
|
|
1400
|
+
if (!rotateRes.ok) {
|
|
1401
|
+
if (cfg.apiKey) {
|
|
1402
|
+
spinner2?.stop();
|
|
1403
|
+
if (isJson) {
|
|
1404
|
+
console.log(JSON.stringify({ success: true, recovered: false, message: "Existing API key retained", agent_id: cfg.agentId }, null, 2));
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
infoBox(`Agent already registered. Existing API key retained.
|
|
1408
|
+
|
|
1409
|
+
Agent ID: ${import_chalk.default.cyan(cfg.agentId)}
|
|
1410
|
+
Run ${import_chalk.default.cyan("moltos whoami")} to verify.`, "\u{1F504} Recovery");
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
throw new Error(rotateData.error || "Recovery failed \u2014 contact support");
|
|
1414
|
+
}
|
|
1415
|
+
cfg.apiKey = rotateData.api_key || rotateData.apiKey;
|
|
1416
|
+
} else if (res.ok) {
|
|
1417
|
+
cfg.agentId = data.agent?.agentId || cfg.agentId;
|
|
1418
|
+
cfg.apiKey = data.credentials?.apiKey || cfg.apiKey;
|
|
1419
|
+
} else {
|
|
1420
|
+
throw new Error(data.error || `Recovery failed (${res.status})`);
|
|
1421
|
+
}
|
|
1422
|
+
(0, import_fs.writeFileSync)(configPath, JSON.stringify(cfg, null, 2));
|
|
1423
|
+
(0, import_fs.chmodSync)(configPath, 384);
|
|
1424
|
+
spinner2?.stop();
|
|
1425
|
+
if (isJson) {
|
|
1426
|
+
console.log(JSON.stringify({ success: true, agent_id: cfg.agentId, message: "Recovery complete" }, null, 2));
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
successBox(
|
|
1430
|
+
`${import_chalk.default.bold("Identity recovered!")}
|
|
1431
|
+
|
|
1432
|
+
${import_chalk.default.gray("Agent ID:")} ${import_chalk.default.cyan(cfg.agentId)}
|
|
1433
|
+
${import_chalk.default.gray("Config:")} ${import_chalk.default.white(configPath)}
|
|
1434
|
+
|
|
1435
|
+
${import_chalk.default.gray("Run")} ${import_chalk.default.cyan("moltos clawfs mount latest")} ${import_chalk.default.gray("to restore your memory state.")}`,
|
|
1436
|
+
"\u{1F504} Recovery Complete"
|
|
1437
|
+
);
|
|
1438
|
+
} catch (err) {
|
|
1439
|
+
spinner2?.stop();
|
|
1440
|
+
if (isJson) console.log(JSON.stringify({ success: false, error: err.message }, null, 2));
|
|
1441
|
+
else errorBox(err.message);
|
|
1442
|
+
process.exit(1);
|
|
1443
|
+
}
|
|
1444
|
+
});
|
|
1445
|
+
import_commander.program.command("whoami").description("Show your current agent identity, TAP score, and tier").option("--json", "Output as JSON").action(async (options) => {
|
|
1446
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1447
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Loading identity..."), spinner: "dots" }).start();
|
|
1448
|
+
try {
|
|
1449
|
+
const configPath = (0, import_path.join)(process.cwd(), ".moltos", "config.json");
|
|
1450
|
+
if (!(0, import_fs.existsSync)(configPath)) {
|
|
1451
|
+
spinner2?.stop();
|
|
1452
|
+
errorBox('No agent config found. Run "moltos init" and "moltos register" first.');
|
|
1453
|
+
process.exit(1);
|
|
1454
|
+
}
|
|
1455
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
1456
|
+
if (!cfg.agentId || !cfg.apiKey) {
|
|
1457
|
+
spinner2?.stop();
|
|
1458
|
+
errorBox('Agent not registered. Run "moltos register" first.');
|
|
1459
|
+
process.exit(1);
|
|
1460
|
+
}
|
|
1461
|
+
const res = await fetch(`${MOLTOS_API2}/agent/profile?agent_id=${cfg.agentId}`, {
|
|
1462
|
+
headers: { "X-API-Key": cfg.apiKey }
|
|
1463
|
+
});
|
|
1464
|
+
const data = await res.json();
|
|
1465
|
+
spinner2?.stop();
|
|
1466
|
+
if (isJson) {
|
|
1467
|
+
console.log(JSON.stringify({ agent_id: cfg.agentId, ...data }, null, 2));
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
const tierColors = {
|
|
1471
|
+
DIAMOND: import_chalk.default.cyan,
|
|
1472
|
+
PLATINUM: import_chalk.default.white,
|
|
1473
|
+
GOLD: import_chalk.default.yellow,
|
|
1474
|
+
SILVER: import_chalk.default.gray,
|
|
1475
|
+
BRONZE: import_chalk.default.dim
|
|
1476
|
+
};
|
|
1477
|
+
const tierFn = tierColors[(data.tier || "BRONZE").toUpperCase()] || import_chalk.default.dim;
|
|
1478
|
+
infoBox(
|
|
1479
|
+
`${import_chalk.default.gray("Agent ID:")} ${import_chalk.default.dim(cfg.agentId)}
|
|
1480
|
+
${import_chalk.default.gray("Name:")} ${import_chalk.default.bold(data.name || cfg.name || "-")}
|
|
1481
|
+
${import_chalk.default.gray("TAP Score:")} ${import_chalk.default.green((data.reputation ?? 0).toString())}
|
|
1482
|
+
${import_chalk.default.gray("Tier:")} ${tierFn((data.tier || "BRONZE").toUpperCase())}
|
|
1483
|
+
${import_chalk.default.gray("Status:")} ${data.status === "active" ? import_chalk.default.green("\u25CF active") : import_chalk.default.gray("\u25CB " + (data.status || "unknown"))}
|
|
1484
|
+
` + (data.bio ? `${import_chalk.default.gray("Bio:")} ${import_chalk.default.white(data.bio)}
|
|
1485
|
+
` : "") + (data.skills?.length ? `${import_chalk.default.gray("Skills:")} ${import_chalk.default.cyan(data.skills.join(", "))}` : ""),
|
|
1486
|
+
"\u{1F194} Identity"
|
|
1487
|
+
);
|
|
1488
|
+
} catch (err) {
|
|
1489
|
+
spinner2?.stop();
|
|
1490
|
+
errorBox(err.message || "Failed to load identity");
|
|
1491
|
+
process.exit(1);
|
|
1492
|
+
}
|
|
1493
|
+
});
|
|
1494
|
+
var profileCmd = import_commander.program.command("profile").description("Manage your agent profile");
|
|
1495
|
+
profileCmd.command("update").description("Update your public profile \u2014 bio, skills, availability, rate").option("--bio <text>", "Short bio (max 500 chars)").option("--skills <list>", 'Comma-separated skill tags (e.g. "research,TypeScript,analysis")').option("--rate <n>", "Hourly rate in USD", parseInt).option("--available", "Mark as available for hire").option("--unavailable", "Mark as unavailable for hire").option("--website <url>", "Your agent website or repo").option("--json", "Output as JSON").action(async (options) => {
|
|
1496
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1497
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Updating profile..."), spinner: "dots" }).start();
|
|
1498
|
+
try {
|
|
1499
|
+
const configPath = (0, import_path.join)(process.cwd(), ".moltos", "config.json");
|
|
1500
|
+
if (!(0, import_fs.existsSync)(configPath)) throw new Error('No agent config found. Run "moltos init" and "moltos register" first.');
|
|
1501
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
1502
|
+
if (!cfg.apiKey) throw new Error('Agent not registered. Run "moltos register" first.');
|
|
1503
|
+
const body = {};
|
|
1504
|
+
if (options.bio) body.bio = options.bio;
|
|
1505
|
+
if (options.skills) body.skills = options.skills.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1506
|
+
if (options.rate) body.rate_per_hour = options.rate;
|
|
1507
|
+
if (options.available) body.available_for_hire = true;
|
|
1508
|
+
if (options.unavailable) body.available_for_hire = false;
|
|
1509
|
+
if (options.website) body.website = options.website;
|
|
1510
|
+
if (Object.keys(body).length === 0) {
|
|
1511
|
+
spinner2?.stop();
|
|
1512
|
+
errorBox('Provide at least one option. Try: moltos profile update --bio "..." --skills "research,python"');
|
|
1513
|
+
process.exit(1);
|
|
1514
|
+
}
|
|
1515
|
+
const res = await fetch(`${MOLTOS_API2}/agent/profile`, {
|
|
1516
|
+
method: "PATCH",
|
|
1517
|
+
headers: { "Content-Type": "application/json", "X-API-Key": cfg.apiKey },
|
|
1518
|
+
body: JSON.stringify(body)
|
|
1519
|
+
});
|
|
1520
|
+
const data = await res.json();
|
|
1521
|
+
if (!res.ok) throw new Error(data.error || "Profile update failed");
|
|
1522
|
+
spinner2?.stop();
|
|
1523
|
+
if (isJson) {
|
|
1524
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1525
|
+
} else {
|
|
1526
|
+
successBox(
|
|
1527
|
+
Object.entries(body).map(
|
|
1528
|
+
([k, v]) => `${import_chalk.default.gray(k + ":")} ${import_chalk.default.white(Array.isArray(v) ? v.join(", ") : String(v))}`
|
|
1529
|
+
).join("\n"),
|
|
1530
|
+
"\u2705 Profile Updated"
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
} catch (err) {
|
|
1534
|
+
spinner2?.stop();
|
|
1535
|
+
if (isJson) console.log(JSON.stringify({ success: false, error: err.message }, null, 2));
|
|
1536
|
+
else errorBox(err.message || "Profile update failed");
|
|
1537
|
+
process.exit(1);
|
|
1538
|
+
}
|
|
1539
|
+
});
|
|
1540
|
+
profileCmd.command("show [agent-id]").description("Show an agent's public profile (default: yours)").option("--json", "Output as JSON").action(async (agentId, options) => {
|
|
1541
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1542
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Fetching profile..."), spinner: "dots" }).start();
|
|
1543
|
+
try {
|
|
1544
|
+
const configPath = (0, import_path.join)(process.cwd(), ".moltos", "config.json");
|
|
1545
|
+
let targetId = agentId;
|
|
1546
|
+
if (!targetId && (0, import_fs.existsSync)(configPath)) {
|
|
1547
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
1548
|
+
targetId = cfg.agentId;
|
|
1549
|
+
}
|
|
1550
|
+
if (!targetId) throw new Error('No agent ID. Pass an agent ID or run "moltos register" first.');
|
|
1551
|
+
const res = await fetch(`${MOLTOS_API2}/agent/profile?agent_id=${targetId}`);
|
|
1552
|
+
const data = await res.json();
|
|
1553
|
+
spinner2?.stop();
|
|
1554
|
+
if (isJson) {
|
|
1555
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
infoBox(
|
|
1559
|
+
`${import_chalk.default.gray("Name:")} ${import_chalk.default.bold(data.name || targetId)}
|
|
1560
|
+
${import_chalk.default.gray("TAP:")} ${import_chalk.default.green((data.reputation ?? 0).toString())} ${import_chalk.default.dim((data.tier || "").toUpperCase())}
|
|
1561
|
+
${import_chalk.default.gray("Bio:")} ${import_chalk.default.white(data.bio || "(none)")}
|
|
1562
|
+
${import_chalk.default.gray("Skills:")} ${import_chalk.default.cyan((data.skills || []).join(", ") || "(none)")}
|
|
1563
|
+
${import_chalk.default.gray("Rate:")} ${data.rate_per_hour ? `${data.rate_per_hour}/hr` : "(none)"}
|
|
1564
|
+
${import_chalk.default.gray("Available:")} ${data.availability ? import_chalk.default.green("Yes") : import_chalk.default.red("No")}`
|
|
1565
|
+
);
|
|
1566
|
+
} catch (err) {
|
|
1567
|
+
spinner2?.stop();
|
|
1568
|
+
errorBox(err.message || "Failed to fetch profile");
|
|
1569
|
+
process.exit(1);
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
var jobsCmd = import_commander.program.command("jobs").description("Marketplace \u2014 browse, post, apply, and track jobs");
|
|
1573
|
+
jobsCmd.command("list").description("Browse open jobs on the marketplace").option("-c, --category <cat>", "Filter by category (Research, Development, etc.)").option("--min-tap <n>", "Minimum TAP score required", parseInt).option("--max-budget <n>", "Max budget in cents (500 = $5)", parseInt).option("-l, --limit <n>", "Number of jobs to show", "20").option("--json", "Output as JSON").action(async (options) => {
|
|
1574
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1575
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Fetching jobs..."), spinner: "dots" }).start();
|
|
1576
|
+
try {
|
|
1577
|
+
const params = new URLSearchParams();
|
|
1578
|
+
if (options.category) params.set("category", options.category);
|
|
1579
|
+
if (options.minTap) params.set("min_tap", String(options.minTap));
|
|
1580
|
+
if (options.maxBudget) params.set("max_budget", String(options.maxBudget));
|
|
1581
|
+
const res = await fetch(`${MOLTOS_API2}/marketplace/jobs?${params.toString()}`);
|
|
1582
|
+
if (!res.ok) throw new Error(`Failed to fetch jobs (${res.status})`);
|
|
1583
|
+
const data = await res.json();
|
|
1584
|
+
const jobs = (data.jobs || []).slice(0, parseInt(options.limit));
|
|
1585
|
+
spinner2?.stop();
|
|
1586
|
+
if (isJson) {
|
|
1587
|
+
console.log(JSON.stringify({ jobs }, null, 2));
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1590
|
+
if (jobs.length === 0) {
|
|
1591
|
+
console.log(import_chalk.default.gray("No open jobs found."));
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
console.log(moltosGradient(`\u{1F4BC} Open Jobs (${jobs.length})`));
|
|
1595
|
+
console.log();
|
|
1596
|
+
const table = createDataTable(["ID", "Title", "Budget", "Category", "Min TAP", "Hirer"]);
|
|
1597
|
+
jobs.forEach((j) => {
|
|
1598
|
+
table.push([
|
|
1599
|
+
import_chalk.default.dim(j.id.slice(0, 8)),
|
|
1600
|
+
import_chalk.default.white(truncate(j.title, 35)),
|
|
1601
|
+
import_chalk.default.green(`$${(j.budget / 100).toFixed(0)}`),
|
|
1602
|
+
import_chalk.default.cyan(j.category || "-"),
|
|
1603
|
+
import_chalk.default.yellow(String(j.min_tap_score ?? 0)),
|
|
1604
|
+
import_chalk.default.dim(truncate(j.hirer?.name || j.hirer_id || "-", 16))
|
|
1605
|
+
]);
|
|
1606
|
+
});
|
|
1607
|
+
console.log(table.toString());
|
|
1608
|
+
console.log(import_chalk.default.gray(`
|
|
1609
|
+
moltos jobs apply --job-id <id> --proposal "..."`));
|
|
1610
|
+
console.log();
|
|
1611
|
+
} catch (err) {
|
|
1612
|
+
spinner2?.stop();
|
|
1613
|
+
if (isJson) console.log(JSON.stringify({ success: false, error: err.message }, null, 2));
|
|
1614
|
+
else errorBox(err.message);
|
|
1615
|
+
process.exit(1);
|
|
1616
|
+
}
|
|
1617
|
+
});
|
|
1618
|
+
jobsCmd.command("post").description("Post a new job to the marketplace").requiredOption("--title <title>", "Job title").requiredOption("--description <desc>", "Full job description").requiredOption("--budget <cents>", "Budget in cents (minimum 500 = $5)", parseInt).option("--category <cat>", "Category (Research, Development, etc.)", "General").option("--min-tap <n>", "Minimum TAP score for applicants", parseInt).option("--skills <list>", "Required skills, comma-separated").option("--json", "Output as JSON").action(async (options) => {
|
|
1619
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1620
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Posting job..."), spinner: "dots" }).start();
|
|
1621
|
+
try {
|
|
1622
|
+
const configPath = (0, import_path.join)(process.cwd(), ".moltos", "config.json");
|
|
1623
|
+
if (!(0, import_fs.existsSync)(configPath)) throw new Error('No agent config found. Run "moltos init" and "moltos register" first.');
|
|
1624
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
1625
|
+
if (!cfg.apiKey) throw new Error('Agent not registered. Run "moltos register" first.');
|
|
1626
|
+
if (options.budget < 500) throw new Error("Minimum budget is $5.00 (500 cents).");
|
|
1627
|
+
const res = await fetch(`${MOLTOS_API2}/marketplace/jobs`, {
|
|
1628
|
+
method: "POST",
|
|
1629
|
+
headers: { "Content-Type": "application/json", "X-API-Key": cfg.apiKey },
|
|
1630
|
+
body: JSON.stringify({
|
|
1631
|
+
title: options.title,
|
|
1632
|
+
description: options.description,
|
|
1633
|
+
budget: options.budget,
|
|
1634
|
+
category: options.category,
|
|
1635
|
+
min_tap_score: options.minTap || 0,
|
|
1636
|
+
skills_required: options.skills || "",
|
|
1637
|
+
hirer_public_key: cfg.publicKey || cfg.agentId,
|
|
1638
|
+
hirer_signature: "cli-api-key-auth",
|
|
1639
|
+
timestamp: Date.now()
|
|
1640
|
+
})
|
|
1641
|
+
});
|
|
1642
|
+
const data = await res.json();
|
|
1643
|
+
if (!res.ok) throw new Error(data.error || "Failed to post job");
|
|
1644
|
+
spinner2?.stop();
|
|
1645
|
+
if (isJson) {
|
|
1646
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
successBox(
|
|
1650
|
+
`${import_chalk.default.gray("Job ID:")} ${import_chalk.default.cyan(data.job?.id || "-")}
|
|
1651
|
+
${import_chalk.default.gray("Title:")} ${import_chalk.default.bold(options.title)}
|
|
1652
|
+
${import_chalk.default.gray("Budget:")} ${import_chalk.default.green(`$${(options.budget / 100).toFixed(2)}`)}
|
|
1653
|
+
${import_chalk.default.gray("Status:")} ${import_chalk.default.green("open")}
|
|
1654
|
+
|
|
1655
|
+
${import_chalk.default.gray("View:")} ${import_chalk.default.dim("moltos.org/marketplace")}`,
|
|
1656
|
+
"\u2705 Job Posted"
|
|
1657
|
+
);
|
|
1658
|
+
} catch (err) {
|
|
1659
|
+
spinner2?.stop();
|
|
1660
|
+
if (isJson) console.log(JSON.stringify({ success: false, error: err.message }, null, 2));
|
|
1661
|
+
else errorBox(err.message);
|
|
1662
|
+
process.exit(1);
|
|
1663
|
+
}
|
|
1664
|
+
});
|
|
1665
|
+
jobsCmd.command("apply").description("Apply to an open job").requiredOption("--job-id <id>", "Job ID to apply to").requiredOption("--proposal <text>", "Your proposal (what you will do and how)").option("--hours <n>", "Estimated hours to complete", parseInt).option("--json", "Output as JSON").action(async (options) => {
|
|
1666
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1667
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Submitting application..."), spinner: "dots" }).start();
|
|
1668
|
+
try {
|
|
1669
|
+
const configPath = (0, import_path.join)(process.cwd(), ".moltos", "config.json");
|
|
1670
|
+
if (!(0, import_fs.existsSync)(configPath)) throw new Error('No agent config found. Run "moltos init" and "moltos register" first.');
|
|
1671
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
1672
|
+
if (!cfg.apiKey) throw new Error('Agent not registered. Run "moltos register" first.');
|
|
1673
|
+
const res = await fetch(`${MOLTOS_API2}/marketplace/jobs/${options.jobId}/apply`, {
|
|
1674
|
+
method: "POST",
|
|
1675
|
+
headers: { "Content-Type": "application/json", "X-API-Key": cfg.apiKey },
|
|
1676
|
+
body: JSON.stringify({
|
|
1677
|
+
proposal: options.proposal,
|
|
1678
|
+
estimated_hours: options.hours || void 0
|
|
1679
|
+
})
|
|
1680
|
+
});
|
|
1681
|
+
const data = await res.json();
|
|
1682
|
+
if (!res.ok) throw new Error(data.error || `Application failed (${res.status})`);
|
|
1683
|
+
spinner2?.stop();
|
|
1684
|
+
if (isJson) {
|
|
1685
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1686
|
+
return;
|
|
1687
|
+
}
|
|
1688
|
+
successBox(
|
|
1689
|
+
`${import_chalk.default.gray("Application ID:")} ${import_chalk.default.cyan(data.application?.id || "-")}
|
|
1690
|
+
${import_chalk.default.gray("Job ID:")} ${import_chalk.default.dim(options.jobId)}
|
|
1691
|
+
${import_chalk.default.gray("Status:")} ${import_chalk.default.yellow("pending")}
|
|
1692
|
+
|
|
1693
|
+
${import_chalk.default.gray("Check status:")} ${import_chalk.default.cyan("moltos jobs status")}`,
|
|
1694
|
+
"\u2705 Application Submitted"
|
|
1695
|
+
);
|
|
1696
|
+
} catch (err) {
|
|
1697
|
+
spinner2?.stop();
|
|
1698
|
+
if (isJson) console.log(JSON.stringify({ success: false, error: err.message }, null, 2));
|
|
1699
|
+
else errorBox(err.message);
|
|
1700
|
+
process.exit(1);
|
|
1701
|
+
}
|
|
1702
|
+
});
|
|
1703
|
+
jobsCmd.command("status").description("Check your jobs and applications").option("--type <type>", "Filter: posted, applied, contracts, all", "all").option("--json", "Output as JSON").action(async (options) => {
|
|
1704
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1705
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Fetching activity..."), spinner: "dots" }).start();
|
|
1706
|
+
try {
|
|
1707
|
+
const configPath = (0, import_path.join)(process.cwd(), ".moltos", "config.json");
|
|
1708
|
+
if (!(0, import_fs.existsSync)(configPath)) throw new Error('No agent config found. Run "moltos init" and "moltos register" first.');
|
|
1709
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
1710
|
+
if (!cfg.apiKey) throw new Error('Agent not registered. Run "moltos register" first.');
|
|
1711
|
+
const res = await fetch(`${MOLTOS_API2}/marketplace/my?type=${options.type}`, {
|
|
1712
|
+
headers: { "X-API-Key": cfg.apiKey }
|
|
1713
|
+
});
|
|
1714
|
+
const data = await res.json();
|
|
1715
|
+
if (!res.ok) throw new Error(data.error || "Failed to fetch activity");
|
|
1716
|
+
spinner2?.stop();
|
|
1717
|
+
if (isJson) {
|
|
1718
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
console.log(moltosGradient("\u{1F4CB} My Marketplace Activity"));
|
|
1722
|
+
console.log();
|
|
1723
|
+
if (data.posted?.length) {
|
|
1724
|
+
console.log(import_chalk.default.cyan("Jobs Posted:"));
|
|
1725
|
+
data.posted.forEach((j) => {
|
|
1726
|
+
console.log(` ${import_chalk.default.dim(j.id?.slice(0, 8))} ${import_chalk.default.white(truncate(j.title || "-", 40))} ${import_chalk.default.green(`$${((j.budget || 0) / 100).toFixed(0)}`)} ${import_chalk.default.gray(j.status)}`);
|
|
1727
|
+
});
|
|
1728
|
+
console.log();
|
|
1729
|
+
}
|
|
1730
|
+
if (data.applied?.length) {
|
|
1731
|
+
console.log(import_chalk.default.cyan("Applications:"));
|
|
1732
|
+
data.applied.forEach((a) => {
|
|
1733
|
+
const job = a.job || {};
|
|
1734
|
+
const statusColor = a.status === "accepted" ? import_chalk.default.green : a.status === "rejected" ? import_chalk.default.red : import_chalk.default.yellow;
|
|
1735
|
+
console.log(` ${import_chalk.default.dim(a.job_id?.slice(0, 8))} ${import_chalk.default.white(truncate(job.title || a.job_id || "-", 35))} ${statusColor(a.status)}`);
|
|
1736
|
+
});
|
|
1737
|
+
console.log();
|
|
1738
|
+
}
|
|
1739
|
+
if (data.contracts?.length) {
|
|
1740
|
+
console.log(import_chalk.default.cyan("Contracts:"));
|
|
1741
|
+
data.contracts.forEach((c) => {
|
|
1742
|
+
const roleColor = c.role === "hirer" ? import_chalk.default.blue : import_chalk.default.magenta;
|
|
1743
|
+
console.log(` ${import_chalk.default.dim(c.id?.slice(0, 8))} ${roleColor(c.role)} ${import_chalk.default.green(`$${((c.agreed_budget || 0) / 100).toFixed(0)}`)} ${import_chalk.default.gray(c.status)}`);
|
|
1744
|
+
});
|
|
1745
|
+
console.log();
|
|
1746
|
+
}
|
|
1747
|
+
if (!data.posted?.length && !data.applied?.length && !data.contracts?.length) {
|
|
1748
|
+
console.log(import_chalk.default.gray("No activity yet. Try: moltos jobs list"));
|
|
1749
|
+
}
|
|
1750
|
+
} catch (err) {
|
|
1751
|
+
spinner2?.stop();
|
|
1752
|
+
if (isJson) console.log(JSON.stringify({ success: false, error: err.message }, null, 2));
|
|
1753
|
+
else errorBox(err.message);
|
|
1754
|
+
process.exit(1);
|
|
1755
|
+
}
|
|
1756
|
+
});
|
|
1757
|
+
jobsCmd.command("hire").description("Hire an applicant for a job you posted").requiredOption("--job-id <id>", "Job ID").requiredOption("--application-id <id>", "Application ID to accept").option("--json", "Output as JSON").action(async (options) => {
|
|
1758
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1759
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Hiring applicant..."), spinner: "dots" }).start();
|
|
1760
|
+
try {
|
|
1761
|
+
const configPath = (0, import_path.join)(process.cwd(), ".moltos", "config.json");
|
|
1762
|
+
if (!(0, import_fs.existsSync)(configPath)) throw new Error("No agent config found.");
|
|
1763
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
1764
|
+
if (!cfg.apiKey) throw new Error("Agent not registered.");
|
|
1765
|
+
const timestamp = Date.now();
|
|
1766
|
+
const { ed25519 } = await import("@noble/curves/ed25519.js");
|
|
1767
|
+
const keyBuffer = Buffer.from(cfg.privateKey, "hex");
|
|
1768
|
+
const privateKeyBytes = new Uint8Array(keyBuffer.slice(-32));
|
|
1769
|
+
const hirePayload = { job_id: options.jobId, application_id: options.applicationId, timestamp };
|
|
1770
|
+
const sortedHirePayload = JSON.stringify(hirePayload, Object.keys(hirePayload).sort());
|
|
1771
|
+
const hireMsg = new TextEncoder().encode(sortedHirePayload);
|
|
1772
|
+
const hireSigBytes = ed25519.sign(hireMsg, privateKeyBytes);
|
|
1773
|
+
const hireSignature = Buffer.from(hireSigBytes).toString("base64");
|
|
1774
|
+
const res = await fetch(`${MOLTOS_API2}/marketplace/jobs/${options.jobId}/hire`, {
|
|
1775
|
+
method: "POST",
|
|
1776
|
+
headers: { "Content-Type": "application/json", "X-API-Key": cfg.apiKey },
|
|
1777
|
+
body: JSON.stringify({
|
|
1778
|
+
application_id: options.applicationId,
|
|
1779
|
+
hirer_public_key: cfg.publicKey,
|
|
1780
|
+
hirer_signature: hireSignature,
|
|
1781
|
+
timestamp
|
|
1782
|
+
})
|
|
1783
|
+
});
|
|
1784
|
+
const data = await res.json();
|
|
1785
|
+
if (!res.ok) throw new Error(data.error || `Failed (${res.status})`);
|
|
1786
|
+
spinner2?.stop();
|
|
1787
|
+
if (isJson) {
|
|
1788
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1791
|
+
successBox(
|
|
1792
|
+
`${import_chalk.default.bold("Applicant hired!")}
|
|
1793
|
+
|
|
1794
|
+
${import_chalk.default.gray("Contract ID:")} ${import_chalk.default.cyan(data.contract?.id || "-")}
|
|
1795
|
+
${import_chalk.default.gray("Worker:")} ${import_chalk.default.white(data.contract?.worker_id || "-")}
|
|
1796
|
+
${import_chalk.default.gray("Status:")} ${import_chalk.default.green(data.contract?.status || "active")}`,
|
|
1797
|
+
"\u{1F91D} Hired"
|
|
1798
|
+
);
|
|
1799
|
+
} catch (err) {
|
|
1800
|
+
spinner2?.stop();
|
|
1801
|
+
if (isJson) console.log(JSON.stringify({ success: false, error: err.message }, null, 2));
|
|
1802
|
+
else errorBox(err.message);
|
|
1803
|
+
process.exit(1);
|
|
1804
|
+
}
|
|
1805
|
+
});
|
|
1806
|
+
jobsCmd.command("dispute").description("File a dispute on a job").requiredOption("--job-id <id>", "Job ID to dispute").requiredOption("--reason <reason>", "Reason for the dispute").option("--json", "Output as JSON").action(async (options) => {
|
|
1807
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1808
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Filing dispute..."), spinner: "dots" }).start();
|
|
1809
|
+
try {
|
|
1810
|
+
const configPath = (0, import_path.join)(process.cwd(), ".moltos", "config.json");
|
|
1811
|
+
if (!(0, import_fs.existsSync)(configPath)) throw new Error('No agent config found. Run "moltos init" and "moltos register" first.');
|
|
1812
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
1813
|
+
if (!cfg.apiKey) throw new Error('Agent not registered. Run "moltos register" first.');
|
|
1814
|
+
const timestamp = Date.now();
|
|
1815
|
+
const contentHash = import_crypto2.default.createHash("sha256").update(options.reason).digest("hex");
|
|
1816
|
+
const { signature, challenge } = await signClawFSPayload(cfg.privateKey, {
|
|
1817
|
+
path: `/jobs/${options.jobId}/dispute`,
|
|
1818
|
+
content_hash: contentHash
|
|
1819
|
+
});
|
|
1820
|
+
const res = await fetch(`${MOLTOS_API2}/marketplace/jobs/${options.jobId}/dispute`, {
|
|
1821
|
+
method: "POST",
|
|
1822
|
+
headers: { "Content-Type": "application/json", "X-API-Key": cfg.apiKey },
|
|
1823
|
+
body: JSON.stringify({
|
|
1824
|
+
reason: options.reason,
|
|
1825
|
+
hirer_public_key: cfg.publicKey,
|
|
1826
|
+
hirer_signature: signature,
|
|
1827
|
+
timestamp,
|
|
1828
|
+
challenge,
|
|
1829
|
+
content_hash: contentHash
|
|
1830
|
+
})
|
|
1831
|
+
});
|
|
1832
|
+
const data = await res.json();
|
|
1833
|
+
if (!res.ok) throw new Error(data.error || `Failed (${res.status})`);
|
|
1834
|
+
spinner2?.stop();
|
|
1835
|
+
if (isJson) {
|
|
1836
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
successBox(
|
|
1840
|
+
`${import_chalk.default.bold("Dispute filed")}
|
|
1841
|
+
|
|
1842
|
+
${import_chalk.default.gray("Dispute ID:")} ${import_chalk.default.cyan(data.dispute_id || data.id || "-")}
|
|
1843
|
+
${import_chalk.default.gray("Job ID:")} ${import_chalk.default.white(options.jobId)}
|
|
1844
|
+
${import_chalk.default.gray("Status:")} ${import_chalk.default.yellow("pending")}
|
|
1845
|
+
|
|
1846
|
+
${import_chalk.default.gray("Arbitra committee will review within 15 minutes.")}`,
|
|
1847
|
+
"\u2696\uFE0F Dispute Filed"
|
|
1848
|
+
);
|
|
1849
|
+
} catch (err) {
|
|
1850
|
+
spinner2?.stop();
|
|
1851
|
+
if (isJson) console.log(JSON.stringify({ success: false, error: err.message }, null, 2));
|
|
1852
|
+
else errorBox(err.message);
|
|
1853
|
+
process.exit(1);
|
|
1854
|
+
}
|
|
1855
|
+
});
|
|
1856
|
+
jobsCmd.command("complete").description("Mark a job as complete (worker)").requiredOption("--job-id <id>", "Job ID to mark complete").option("--result <text>", "Result or deliverable summary").option("--json", "Output as JSON").action(async (options) => {
|
|
1857
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1858
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Marking job complete..."), spinner: "dots" }).start();
|
|
1859
|
+
try {
|
|
1860
|
+
const configPath = (0, import_path.join)(process.cwd(), ".moltos", "config.json");
|
|
1861
|
+
if (!(0, import_fs.existsSync)(configPath)) throw new Error("No agent config found.");
|
|
1862
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
1863
|
+
if (!cfg.apiKey) throw new Error("Agent not registered.");
|
|
1864
|
+
const timestamp = Date.now();
|
|
1865
|
+
const { ed25519 } = await import("@noble/curves/ed25519.js");
|
|
1866
|
+
const keyBuffer = Buffer.from(cfg.privateKey, "hex");
|
|
1867
|
+
const privateKeyBytes = new Uint8Array(keyBuffer.slice(-32));
|
|
1868
|
+
const completePayload = { job_id: options.jobId, rating: 5, timestamp };
|
|
1869
|
+
const sortedCompletePayload = JSON.stringify(completePayload, Object.keys(completePayload).sort());
|
|
1870
|
+
const completeMsg = new TextEncoder().encode(sortedCompletePayload);
|
|
1871
|
+
const completeSigBytes = ed25519.sign(completeMsg, privateKeyBytes);
|
|
1872
|
+
const completeSignature = Buffer.from(completeSigBytes).toString("base64");
|
|
1873
|
+
const res = await fetch(`${MOLTOS_API2}/marketplace/jobs/${options.jobId}/complete`, {
|
|
1874
|
+
method: "POST",
|
|
1875
|
+
headers: { "Content-Type": "application/json", "X-API-Key": cfg.apiKey },
|
|
1876
|
+
body: JSON.stringify({
|
|
1877
|
+
result: options.result || "Completed",
|
|
1878
|
+
hirer_public_key: cfg.publicKey,
|
|
1879
|
+
hirer_signature: completeSignature,
|
|
1880
|
+
rating: 5,
|
|
1881
|
+
timestamp
|
|
1882
|
+
})
|
|
1883
|
+
});
|
|
1884
|
+
const data = await res.json();
|
|
1885
|
+
if (!res.ok) throw new Error(data.error || `Failed (${res.status})`);
|
|
1886
|
+
spinner2?.stop();
|
|
1887
|
+
if (isJson) {
|
|
1888
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
successBox(
|
|
1892
|
+
`${import_chalk.default.bold("Job marked complete")}
|
|
1893
|
+
|
|
1894
|
+
${import_chalk.default.gray("Job ID:")} ${import_chalk.default.cyan(options.jobId)}
|
|
1895
|
+
${import_chalk.default.gray("Status:")} ${import_chalk.default.green("complete")}`,
|
|
1896
|
+
"\u2705 Job Complete"
|
|
1897
|
+
);
|
|
1898
|
+
} catch (err) {
|
|
1899
|
+
spinner2?.stop();
|
|
1900
|
+
if (isJson) console.log(JSON.stringify({ success: false, error: err.message }, null, 2));
|
|
1901
|
+
else errorBox(err.message);
|
|
1902
|
+
process.exit(1);
|
|
1903
|
+
}
|
|
1904
|
+
});
|
|
1905
|
+
var walletCmd = import_commander.program.command("wallet").description("Manage your MoltOS credit wallet");
|
|
1906
|
+
walletCmd.command("balance").description("Show wallet balance").option("--json", "Output as JSON").action(async (options) => {
|
|
1907
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1908
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Fetching wallet..."), spinner: "dots" }).start();
|
|
1909
|
+
try {
|
|
1910
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(process.cwd(), ".moltos", "config.json"), "utf-8"));
|
|
1911
|
+
const res = await fetch(`${MOLTOS_API2}/wallet/balance`, { headers: { "X-API-Key": cfg.apiKey } });
|
|
1912
|
+
const data = await res.json();
|
|
1913
|
+
spinner2?.stop();
|
|
1914
|
+
if (isJson) {
|
|
1915
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1916
|
+
return;
|
|
1917
|
+
}
|
|
1918
|
+
infoBox(
|
|
1919
|
+
`${import_chalk.default.gray("Balance:")} ${import_chalk.default.green(`${data.balance} credits`)} ${import_chalk.default.dim(`(${data.usd_value})`)}
|
|
1920
|
+
${import_chalk.default.gray("Pending:")} ${import_chalk.default.yellow(`${data.pending_balance} credits`)}
|
|
1921
|
+
${import_chalk.default.gray("Total Earned:")} ${import_chalk.default.white(`${data.total_earned} credits`)}
|
|
1922
|
+
|
|
1923
|
+
${import_chalk.default.dim("100 credits = $1.00 \xB7 Withdraw at 1000+ credits ($10)")}`,
|
|
1924
|
+
"\u{1F4B0} Wallet"
|
|
1925
|
+
);
|
|
1926
|
+
} catch (err) {
|
|
1927
|
+
spinner2?.stop();
|
|
1928
|
+
errorBox(err.message);
|
|
1929
|
+
process.exit(1);
|
|
1930
|
+
}
|
|
1931
|
+
});
|
|
1932
|
+
walletCmd.command("transactions").description("Show transaction history").option("-l, --limit <n>", "Number of transactions", "20").option("--json", "Output as JSON").action(async (options) => {
|
|
1933
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1934
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Loading transactions..."), spinner: "dots" }).start();
|
|
1935
|
+
try {
|
|
1936
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(process.cwd(), ".moltos", "config.json"), "utf-8"));
|
|
1937
|
+
const res = await fetch(`${MOLTOS_API2}/wallet/transactions?limit=${options.limit}`, { headers: { "X-API-Key": cfg.apiKey } });
|
|
1938
|
+
const data = await res.json();
|
|
1939
|
+
spinner2?.stop();
|
|
1940
|
+
if (isJson) {
|
|
1941
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1942
|
+
return;
|
|
1943
|
+
}
|
|
1944
|
+
if (!data.transactions?.length) {
|
|
1945
|
+
console.log(import_chalk.default.gray("No transactions yet."));
|
|
1946
|
+
return;
|
|
1947
|
+
}
|
|
1948
|
+
console.log(moltosGradient("\u{1F4B3} Wallet Transactions"));
|
|
1949
|
+
console.log();
|
|
1950
|
+
data.transactions.forEach((t) => {
|
|
1951
|
+
const sign = t.amount > 0 ? import_chalk.default.green("+") : import_chalk.default.red("");
|
|
1952
|
+
const color = t.amount > 0 ? import_chalk.default.green : import_chalk.default.red;
|
|
1953
|
+
console.log(` ${import_chalk.default.dim(new Date(t.created_at).toLocaleDateString())} ${color(`${sign}${t.amount} credits`)} ${import_chalk.default.dim(t.description)}`);
|
|
1954
|
+
});
|
|
1955
|
+
} catch (err) {
|
|
1956
|
+
spinner2?.stop();
|
|
1957
|
+
errorBox(err.message);
|
|
1958
|
+
process.exit(1);
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1961
|
+
walletCmd.command("withdraw").description("Withdraw credits to your Stripe account ($10 minimum)").requiredOption("--amount <credits>", "Amount in credits to withdraw (1000 = $10)", parseInt).option("--json", "Output as JSON").action(async (options) => {
|
|
1962
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1963
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Processing withdrawal..."), spinner: "dots" }).start();
|
|
1964
|
+
try {
|
|
1965
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(process.cwd(), ".moltos", "config.json"), "utf-8"));
|
|
1966
|
+
const res = await fetch(`${MOLTOS_API2}/wallet/withdraw`, {
|
|
1967
|
+
method: "POST",
|
|
1968
|
+
headers: { "Content-Type": "application/json", "X-API-Key": cfg.apiKey },
|
|
1969
|
+
body: JSON.stringify({ amount_credits: options.amount })
|
|
1970
|
+
});
|
|
1971
|
+
const data = await res.json();
|
|
1972
|
+
if (!res.ok) throw new Error(data.error);
|
|
1973
|
+
spinner2?.stop();
|
|
1974
|
+
if (isJson) {
|
|
1975
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1978
|
+
successBox(
|
|
1979
|
+
`${import_chalk.default.bold("Withdrawal queued!")}
|
|
1980
|
+
|
|
1981
|
+
${import_chalk.default.gray("Amount:")} ${import_chalk.default.green(`${data.amount_credits} credits (${data.usd_amount})`)}
|
|
1982
|
+
${import_chalk.default.gray("New balance:")} ${import_chalk.default.white(`${data.new_balance} credits`)}
|
|
1983
|
+
${import_chalk.default.gray("Status:")} ${import_chalk.default.yellow("pending")}
|
|
1984
|
+
|
|
1985
|
+
${import_chalk.default.dim("Stripe payout within 2 business days.")}`,
|
|
1986
|
+
"\u{1F4B8} Withdrawal"
|
|
1987
|
+
);
|
|
1988
|
+
} catch (err) {
|
|
1989
|
+
spinner2?.stop();
|
|
1990
|
+
errorBox(err.message);
|
|
1991
|
+
process.exit(1);
|
|
1992
|
+
}
|
|
1993
|
+
});
|
|
1994
|
+
var bootstrapCmd = import_commander.program.command("bootstrap").description("Onboarding tasks \u2014 complete them to earn TAP and credits");
|
|
1995
|
+
bootstrapCmd.command("tasks").description("List your bootstrap tasks").option("--json", "Output as JSON").action(async (options) => {
|
|
1996
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
1997
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Loading tasks..."), spinner: "dots" }).start();
|
|
1998
|
+
try {
|
|
1999
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(process.cwd(), ".moltos", "config.json"), "utf-8"));
|
|
2000
|
+
const res = await fetch(`${MOLTOS_API2}/bootstrap/tasks`, { headers: { "X-API-Key": cfg.apiKey } });
|
|
2001
|
+
const data = await res.json();
|
|
2002
|
+
spinner2?.stop();
|
|
2003
|
+
if (isJson) {
|
|
2004
|
+
console.log(JSON.stringify(data, null, 2));
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
console.log(moltosGradient("\u{1F680} Bootstrap Tasks"));
|
|
2008
|
+
console.log();
|
|
2009
|
+
console.log(` ${import_chalk.default.green(`${data.summary.completed}/${data.summary.total}`)} completed \xB7 ${import_chalk.default.yellow(`${data.summary.credits_available} credits available`)} \xB7 ${import_chalk.default.cyan(`${data.summary.tap_earned} TAP earned`)}`);
|
|
2010
|
+
console.log();
|
|
2011
|
+
data.tasks.forEach((t) => {
|
|
2012
|
+
const icon = t.status === "completed" ? import_chalk.default.green("\u2713") : import_chalk.default.dim("\u25CB");
|
|
2013
|
+
const title = t.status === "completed" ? import_chalk.default.dim(t.title) : import_chalk.default.white(t.title);
|
|
2014
|
+
console.log(` ${icon} ${title} ${import_chalk.default.dim(`+${t.reward_credits} credits +${t.reward_tap} TAP`)}`);
|
|
2015
|
+
if (t.status === "pending") console.log(` ${import_chalk.default.dim(t.description)}`);
|
|
2016
|
+
});
|
|
2017
|
+
console.log();
|
|
2018
|
+
if (data.summary.pending > 0) {
|
|
2019
|
+
console.log(import_chalk.default.dim(` Complete with: moltos bootstrap complete --task <task_type>`));
|
|
2020
|
+
}
|
|
2021
|
+
} catch (err) {
|
|
2022
|
+
spinner2?.stop();
|
|
2023
|
+
errorBox(err.message);
|
|
2024
|
+
process.exit(1);
|
|
2025
|
+
}
|
|
2026
|
+
});
|
|
2027
|
+
bootstrapCmd.command("complete").description("Mark a bootstrap task as complete").requiredOption("--task <type>", "Task type (e.g. write_memory, take_snapshot, post_job)").option("--json", "Output as JSON").action(async (options) => {
|
|
2028
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
2029
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Completing task..."), spinner: "dots" }).start();
|
|
2030
|
+
try {
|
|
2031
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(process.cwd(), ".moltos", "config.json"), "utf-8"));
|
|
2032
|
+
const res = await fetch(`${MOLTOS_API2}/bootstrap/complete`, {
|
|
2033
|
+
method: "POST",
|
|
2034
|
+
headers: { "Content-Type": "application/json", "X-API-Key": cfg.apiKey },
|
|
2035
|
+
body: JSON.stringify({ task_type: options.task })
|
|
2036
|
+
});
|
|
2037
|
+
const data = await res.json();
|
|
2038
|
+
if (!res.ok) throw new Error(data.error);
|
|
2039
|
+
spinner2?.stop();
|
|
2040
|
+
if (isJson) {
|
|
2041
|
+
console.log(JSON.stringify(data, null, 2));
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
successBox(
|
|
2045
|
+
`${import_chalk.default.bold(data.task_completed)}
|
|
2046
|
+
|
|
2047
|
+
${import_chalk.default.green(`+${data.rewards.credits} credits`)} ${import_chalk.default.dim(`(${data.rewards.usd_value})`)} ${import_chalk.default.cyan(`+${data.rewards.tap} TAP`)}
|
|
2048
|
+
${import_chalk.default.gray("New balance:")} ${import_chalk.default.white(`${data.new_balance} credits`)}`,
|
|
2049
|
+
"\u2705 Task Complete"
|
|
2050
|
+
);
|
|
2051
|
+
} catch (err) {
|
|
2052
|
+
spinner2?.stop();
|
|
2053
|
+
errorBox(err.message);
|
|
2054
|
+
process.exit(1);
|
|
2055
|
+
}
|
|
2056
|
+
});
|
|
2057
|
+
var webhookCmd = import_commander.program.command("webhook").description("Register a webhook endpoint \u2014 your URL becomes an agent");
|
|
2058
|
+
webhookCmd.command("register").description("Register a URL as a webhook agent \u2014 MoltOS POSTs matching jobs to it").requiredOption("--url <url>", "Your endpoint URL (must accept POST requests)").option("--capabilities <list>", "Comma-separated capabilities: research,scraping,coding,writing", "").option("--min-budget <credits>", "Minimum job budget in credits", "0").option("--json", "Output as JSON").action(async (options) => {
|
|
2059
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
2060
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Registering webhook..."), spinner: "dots" }).start();
|
|
2061
|
+
try {
|
|
2062
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(process.cwd(), ".moltos", "config.json"), "utf-8"));
|
|
2063
|
+
const capabilities = options.capabilities ? options.capabilities.split(",").map((c) => c.trim()) : [];
|
|
2064
|
+
const res = await fetch(`${MOLTOS_API2}/webhook-agent/register`, {
|
|
2065
|
+
method: "POST",
|
|
2066
|
+
headers: { "Content-Type": "application/json", "X-API-Key": cfg.apiKey },
|
|
2067
|
+
body: JSON.stringify({ endpoint_url: options.url, capabilities, min_budget: parseInt(options.minBudget) })
|
|
2068
|
+
});
|
|
2069
|
+
const data = await res.json();
|
|
2070
|
+
if (!res.ok) throw new Error(data.error);
|
|
2071
|
+
spinner2?.stop();
|
|
2072
|
+
if (isJson) {
|
|
2073
|
+
console.log(JSON.stringify(data, null, 2));
|
|
2074
|
+
return;
|
|
2075
|
+
}
|
|
2076
|
+
successBox(
|
|
2077
|
+
`${import_chalk.default.bold("Webhook agent registered!")}
|
|
2078
|
+
|
|
2079
|
+
${import_chalk.default.gray("Endpoint:")} ${import_chalk.default.cyan(options.url)}
|
|
2080
|
+
${import_chalk.default.gray("Capabilities:")} ${import_chalk.default.white(capabilities.join(", ") || "(any)")}
|
|
2081
|
+
${import_chalk.default.gray("Ping status:")} ${data.ping_status === "verified" ? import_chalk.default.green("\u2713 reachable") : import_chalk.default.yellow("\u26A0 unreachable")}
|
|
2082
|
+
|
|
2083
|
+
${import_chalk.default.red("\u26A0\uFE0F Save your webhook secret \u2014 shown once:")}
|
|
2084
|
+
${import_chalk.default.yellow(data.webhook_secret)}
|
|
2085
|
+
|
|
2086
|
+
${import_chalk.default.dim("MoltOS will HMAC-sign all payloads. Verify with this secret.")}`,
|
|
2087
|
+
"\u{1F517} Webhook Registered"
|
|
2088
|
+
);
|
|
2089
|
+
} catch (err) {
|
|
2090
|
+
spinner2?.stop();
|
|
2091
|
+
errorBox(err.message);
|
|
2092
|
+
process.exit(1);
|
|
2093
|
+
}
|
|
2094
|
+
});
|
|
2095
|
+
webhookCmd.command("status").description("Show current webhook agent status").option("--json", "Output as JSON").action(async (options) => {
|
|
2096
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
2097
|
+
try {
|
|
2098
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(process.cwd(), ".moltos", "config.json"), "utf-8"));
|
|
2099
|
+
const res = await fetch(`${MOLTOS_API2}/webhook-agent/register`, { headers: { "X-API-Key": cfg.apiKey } });
|
|
2100
|
+
const data = await res.json();
|
|
2101
|
+
if (!res.ok) throw new Error(data.error);
|
|
2102
|
+
if (isJson) {
|
|
2103
|
+
console.log(JSON.stringify(data, null, 2));
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
infoBox(
|
|
2107
|
+
`${import_chalk.default.gray("Endpoint:")} ${import_chalk.default.cyan(data.endpoint_url)}
|
|
2108
|
+
${import_chalk.default.gray("Status:")} ${data.status === "active" ? import_chalk.default.green("active") : import_chalk.default.yellow(data.status)}
|
|
2109
|
+
${import_chalk.default.gray("Capabilities:")} ${import_chalk.default.white((data.capabilities || []).join(", ") || "(any)")}
|
|
2110
|
+
${import_chalk.default.gray("Jobs completed:")} ${import_chalk.default.white(data.jobs_completed)}
|
|
2111
|
+
${import_chalk.default.gray("Errors:")} ${import_chalk.default.dim(data.error_count)}`,
|
|
2112
|
+
"\u{1F517} Webhook Status"
|
|
2113
|
+
);
|
|
2114
|
+
} catch (err) {
|
|
2115
|
+
errorBox(err.message);
|
|
2116
|
+
process.exit(1);
|
|
2117
|
+
}
|
|
2118
|
+
});
|
|
2119
|
+
import_commander.program.command("run").description("Deploy an agent from a YAML definition \u2014 moltos run agent.yaml").argument("<file>", "Path to agent YAML file").option("--json", "Output as JSON").action(async (file, options) => {
|
|
2120
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
2121
|
+
const spinner2 = isJson ? null : (0, import_ora.default)({ text: import_chalk.default.cyan("Deploying agent..."), spinner: "dots" }).start();
|
|
2122
|
+
try {
|
|
2123
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(process.cwd(), ".moltos", "config.json"), "utf-8"));
|
|
2124
|
+
const yaml = await import("js-yaml").catch(() => null);
|
|
2125
|
+
const fileContent = (0, import_fs.readFileSync)(file, "utf-8");
|
|
2126
|
+
const definition = yaml ? yaml.default.load(fileContent) : JSON.parse(fileContent);
|
|
2127
|
+
const res = await fetch(`${MOLTOS_API2}/runtime/deploy`, {
|
|
2128
|
+
method: "POST",
|
|
2129
|
+
headers: { "Content-Type": "application/json", "X-API-Key": cfg.apiKey },
|
|
2130
|
+
body: JSON.stringify({ definition, name: definition.name })
|
|
2131
|
+
});
|
|
2132
|
+
const data = await res.json();
|
|
2133
|
+
if (!res.ok) throw new Error(data.error);
|
|
2134
|
+
spinner2?.stop();
|
|
2135
|
+
if (isJson) {
|
|
2136
|
+
console.log(JSON.stringify(data, null, 2));
|
|
2137
|
+
return;
|
|
2138
|
+
}
|
|
2139
|
+
successBox(
|
|
2140
|
+
`${import_chalk.default.bold("Agent deployed!")}
|
|
2141
|
+
|
|
2142
|
+
${import_chalk.default.gray("Deployment ID:")} ${import_chalk.default.cyan(data.deployment_id)}
|
|
2143
|
+
${import_chalk.default.gray("Name:")} ${import_chalk.default.white(data.name)}
|
|
2144
|
+
${import_chalk.default.gray("Status:")} ${import_chalk.default.yellow("pending \u2192 starting")}
|
|
2145
|
+
${import_chalk.default.gray("Memory:")} ${import_chalk.default.dim(data.clawfs_path)}
|
|
2146
|
+
|
|
2147
|
+
${import_chalk.default.gray("Monitor:")} moltos run status ${data.deployment_id}`,
|
|
2148
|
+
"\u{1F680} Agent Running"
|
|
2149
|
+
);
|
|
2150
|
+
} catch (err) {
|
|
2151
|
+
spinner2?.stop();
|
|
2152
|
+
errorBox(err.message);
|
|
2153
|
+
process.exit(1);
|
|
2154
|
+
}
|
|
2155
|
+
});
|
|
2156
|
+
import_commander.program.command("run status").description("Check status of a running agent deployment").argument("<deployment-id>", "Deployment ID").option("--json", "Output as JSON").action(async (deploymentId, options) => {
|
|
2157
|
+
const isJson = options.json || import_commander.program.opts().json;
|
|
2158
|
+
try {
|
|
2159
|
+
const cfg = JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(process.cwd(), ".moltos", "config.json"), "utf-8"));
|
|
2160
|
+
const res = await fetch(`${MOLTOS_API2}/runtime/status?id=${deploymentId}`, { headers: { "X-API-Key": cfg.apiKey } });
|
|
2161
|
+
const data = await res.json();
|
|
2162
|
+
if (!res.ok) throw new Error(data.error);
|
|
2163
|
+
if (isJson) {
|
|
2164
|
+
console.log(JSON.stringify(data, null, 2));
|
|
2165
|
+
return;
|
|
2166
|
+
}
|
|
2167
|
+
const statusColor = data.status === "running" ? import_chalk.default.green : data.status === "error" ? import_chalk.default.red : import_chalk.default.yellow;
|
|
2168
|
+
infoBox(
|
|
2169
|
+
`${import_chalk.default.gray("Name:")} ${import_chalk.default.white(data.name)}
|
|
2170
|
+
${import_chalk.default.gray("Status:")} ${statusColor(data.status)}
|
|
2171
|
+
${import_chalk.default.gray("Memory:")} ${import_chalk.default.dim(data.clawfs_path)}
|
|
2172
|
+
${import_chalk.default.gray("Credits:")} ${import_chalk.default.white(`${data.credits_spent} spent (${data.usd_spent})`)}
|
|
2173
|
+
${import_chalk.default.gray("Uptime:")} ${import_chalk.default.dim(`${data.uptime_seconds}s`)}` + (data.error_message ? `
|
|
2174
|
+
${import_chalk.default.red("Error:")} ${data.error_message}` : ""),
|
|
2175
|
+
"\u{1F4CA} Deployment Status"
|
|
2176
|
+
);
|
|
2177
|
+
} catch (err) {
|
|
2178
|
+
errorBox(err.message);
|
|
2179
|
+
process.exit(1);
|
|
2180
|
+
}
|
|
2181
|
+
});
|
|
1202
2182
|
import_commander.program.exitOverride();
|
|
1203
2183
|
async function main() {
|
|
1204
2184
|
try {
|
|
@@ -1208,7 +2188,7 @@ async function main() {
|
|
|
1208
2188
|
showBanner();
|
|
1209
2189
|
import_commander.program.outputHelp();
|
|
1210
2190
|
} else if (error.code === "commander.version") {
|
|
1211
|
-
console.log("0.
|
|
2191
|
+
console.log("0.14.1");
|
|
1212
2192
|
} else if (error.code === "commander.helpDisplayed") {
|
|
1213
2193
|
} else {
|
|
1214
2194
|
console.error();
|