@obolos_tech/cli 0.3.2 → 0.4.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/index.d.ts +4 -0
- package/dist/index.js +542 -65
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
* npx @obolos_tech/cli anp bid <cid> --price 25 --delivery 48h
|
|
22
22
|
* npx @obolos_tech/cli anp accept <cid> --bid <bid_cid>
|
|
23
23
|
* npx @obolos_tech/cli anp verify <cid>
|
|
24
|
+
* npx @obolos_tech/cli reputation check 16907
|
|
25
|
+
* npx @obolos_tech/cli reputation check 16907 --chain ethereum
|
|
26
|
+
* npx @obolos_tech/cli reputation compare 123 456 789
|
|
27
|
+
* npx @obolos_tech/cli rep compare base:123 ethereum:456
|
|
24
28
|
*/
|
|
25
29
|
import { homedir } from 'os';
|
|
26
30
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
@@ -323,19 +327,7 @@ function stateVisualization(status) {
|
|
|
323
327
|
return ` ${parts.join(` ${c.dim}->${c.reset} `)}`;
|
|
324
328
|
}
|
|
325
329
|
// ─── ANP Helpers ─────────────────────────────────────────────────────────────
|
|
326
|
-
|
|
327
|
-
if (obj === null || typeof obj !== 'object')
|
|
328
|
-
return JSON.stringify(obj);
|
|
329
|
-
if (Array.isArray(obj))
|
|
330
|
-
return '[' + obj.map(canonicalJSON).join(',') + ']';
|
|
331
|
-
const keys = Object.keys(obj).sort();
|
|
332
|
-
return '{' + keys.map(k => JSON.stringify(k) + ':' + canonicalJSON(obj[k])).join(',') + '}';
|
|
333
|
-
}
|
|
334
|
-
async function computeContentHash(data) {
|
|
335
|
-
const { createHash } = await import('crypto');
|
|
336
|
-
const hash = createHash('sha256').update(canonicalJSON(data)).digest('hex');
|
|
337
|
-
return `0x${hash}`;
|
|
338
|
-
}
|
|
330
|
+
import { computeContentHash, ANP_TYPES, getANPDomain, hashListingIntent, hashBidIntent, hashAcceptIntent, hashAmendmentIntent, hashCheckpointIntent, usdToUsdc } from '@obolos_tech/anp-sdk';
|
|
339
331
|
function parseTimeToSeconds(input) {
|
|
340
332
|
const match = input.match(/^(\d+)\s*(s|sec|secs|second|seconds|h|hr|hrs|hour|hours|d|day|days|m|min|mins|minute|minutes)$/i);
|
|
341
333
|
if (!match) {
|
|
@@ -353,67 +345,26 @@ function parseTimeToSeconds(input) {
|
|
|
353
345
|
return num * 86400;
|
|
354
346
|
return num;
|
|
355
347
|
}
|
|
356
|
-
const ANP_DOMAIN =
|
|
357
|
-
name: 'ANP',
|
|
358
|
-
version: '1',
|
|
359
|
-
chainId: 8453,
|
|
360
|
-
verifyingContract: '0xfEa362Bf569e97B20681289fB4D4a64CEBDFa792',
|
|
361
|
-
};
|
|
362
|
-
const ANP_LISTING_TYPES = {
|
|
363
|
-
ListingIntent: [
|
|
364
|
-
{ name: 'contentHash', type: 'bytes32' },
|
|
365
|
-
{ name: 'minBudget', type: 'uint256' },
|
|
366
|
-
{ name: 'maxBudget', type: 'uint256' },
|
|
367
|
-
{ name: 'deadline', type: 'uint256' },
|
|
368
|
-
{ name: 'jobDuration', type: 'uint256' },
|
|
369
|
-
{ name: 'preferredEvaluator', type: 'address' },
|
|
370
|
-
{ name: 'nonce', type: 'uint256' },
|
|
371
|
-
],
|
|
372
|
-
};
|
|
373
|
-
const ANP_BID_TYPES = {
|
|
374
|
-
BidIntent: [
|
|
375
|
-
{ name: 'listingHash', type: 'bytes32' },
|
|
376
|
-
{ name: 'contentHash', type: 'bytes32' },
|
|
377
|
-
{ name: 'price', type: 'uint256' },
|
|
378
|
-
{ name: 'deliveryTime', type: 'uint256' },
|
|
379
|
-
{ name: 'nonce', type: 'uint256' },
|
|
380
|
-
],
|
|
381
|
-
};
|
|
382
|
-
const ANP_ACCEPT_TYPES = {
|
|
383
|
-
AcceptIntent: [
|
|
384
|
-
{ name: 'listingHash', type: 'bytes32' },
|
|
385
|
-
{ name: 'bidHash', type: 'bytes32' },
|
|
386
|
-
{ name: 'nonce', type: 'uint256' },
|
|
387
|
-
],
|
|
388
|
-
};
|
|
348
|
+
const ANP_DOMAIN = getANPDomain(8453, '0xfEa362Bf569e97B20681289fB4D4a64CEBDFa792');
|
|
389
349
|
async function getANPSigningClient() {
|
|
390
350
|
if (!OBOLOS_PRIVATE_KEY) {
|
|
391
351
|
console.error(`${c.red}No wallet configured.${c.reset} Run ${c.cyan}obolos setup${c.reset} first.`);
|
|
392
352
|
process.exit(1);
|
|
393
353
|
}
|
|
394
|
-
const { createWalletClient, http: viemHttp
|
|
354
|
+
const { createWalletClient, http: viemHttp } = await import('viem');
|
|
395
355
|
const { privateKeyToAccount } = await import('viem/accounts');
|
|
396
356
|
const { base } = await import('viem/chains');
|
|
397
357
|
const key = OBOLOS_PRIVATE_KEY.startsWith('0x') ? OBOLOS_PRIVATE_KEY : `0x${OBOLOS_PRIVATE_KEY}`;
|
|
398
358
|
const account = privateKeyToAccount(key);
|
|
399
359
|
const walletClient = createWalletClient({ account, chain: base, transport: viemHttp() });
|
|
400
|
-
|
|
401
|
-
const BID_TYPEHASH = keccak256(Buffer.from('BidIntent(bytes32 listingHash,bytes32 contentHash,uint256 price,uint256 deliveryTime,uint256 nonce)'));
|
|
402
|
-
const ACCEPT_TYPEHASH = keccak256(Buffer.from('AcceptIntent(bytes32 listingHash,bytes32 bidHash,uint256 nonce)'));
|
|
403
|
-
function hashListingStruct(listing) {
|
|
404
|
-
return keccak256(encodeAbiParameters([{ type: 'bytes32' }, { type: 'bytes32' }, { type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }, { type: 'address' }, { type: 'uint256' }], [LISTING_TYPEHASH, listing.contentHash, listing.minBudget, listing.maxBudget, listing.deadline, listing.jobDuration, listing.preferredEvaluator, listing.nonce]));
|
|
405
|
-
}
|
|
406
|
-
function hashBidStruct(bid) {
|
|
407
|
-
return keccak256(encodeAbiParameters([{ type: 'bytes32' }, { type: 'bytes32' }, { type: 'bytes32' }, { type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], [BID_TYPEHASH, bid.listingHash, bid.contentHash, bid.price, bid.deliveryTime, bid.nonce]));
|
|
408
|
-
}
|
|
409
|
-
function hashAcceptStruct(accept) {
|
|
410
|
-
return keccak256(encodeAbiParameters([{ type: 'bytes32' }, { type: 'bytes32' }, { type: 'bytes32' }, { type: 'uint256' }], [ACCEPT_TYPEHASH, accept.listingHash, accept.bidHash, accept.nonce]));
|
|
411
|
-
}
|
|
412
|
-
return { account, walletClient, hashListingStruct, hashBidStruct, hashAcceptStruct };
|
|
360
|
+
return { account, walletClient, hashListingStruct: hashListingIntent, hashBidStruct: hashBidIntent, hashAcceptStruct: hashAcceptIntent, hashAmendmentStruct: hashAmendmentIntent, hashCheckpointStruct: hashCheckpointIntent };
|
|
413
361
|
}
|
|
414
362
|
function generateNonce() {
|
|
415
363
|
return BigInt(Math.floor(Math.random() * 2 ** 32));
|
|
416
364
|
}
|
|
365
|
+
async function computeJobHash(jobId) {
|
|
366
|
+
return computeContentHash({ jobId });
|
|
367
|
+
}
|
|
417
368
|
// ─── Commands ───────────────────────────────────────────────────────────────
|
|
418
369
|
async function cmdSearch(args) {
|
|
419
370
|
const query = args.join(' ');
|
|
@@ -1953,7 +1904,7 @@ async function cmdAnpCreate(args) {
|
|
|
1953
1904
|
const signature = await anp.walletClient.signTypedData({
|
|
1954
1905
|
account: anp.account,
|
|
1955
1906
|
domain: ANP_DOMAIN,
|
|
1956
|
-
types:
|
|
1907
|
+
types: { ListingIntent: ANP_TYPES.ListingIntent },
|
|
1957
1908
|
primaryType: 'ListingIntent',
|
|
1958
1909
|
message,
|
|
1959
1910
|
});
|
|
@@ -2040,7 +1991,7 @@ async function cmdAnpBid(args) {
|
|
|
2040
1991
|
const signature = await anp.walletClient.signTypedData({
|
|
2041
1992
|
account: anp.account,
|
|
2042
1993
|
domain: ANP_DOMAIN,
|
|
2043
|
-
types:
|
|
1994
|
+
types: { BidIntent: ANP_TYPES.BidIntent },
|
|
2044
1995
|
primaryType: 'BidIntent',
|
|
2045
1996
|
message: bidMessage,
|
|
2046
1997
|
});
|
|
@@ -2125,7 +2076,7 @@ async function cmdAnpAccept(args) {
|
|
|
2125
2076
|
const signature = await anp.walletClient.signTypedData({
|
|
2126
2077
|
account: anp.account,
|
|
2127
2078
|
domain: ANP_DOMAIN,
|
|
2128
|
-
types:
|
|
2079
|
+
types: { AcceptIntent: ANP_TYPES.AcceptIntent },
|
|
2129
2080
|
primaryType: 'AcceptIntent',
|
|
2130
2081
|
message: acceptMessage,
|
|
2131
2082
|
});
|
|
@@ -2189,6 +2140,274 @@ async function cmdAnpVerify(args) {
|
|
|
2189
2140
|
}
|
|
2190
2141
|
console.log();
|
|
2191
2142
|
}
|
|
2143
|
+
// ─── IML Commands (In-Job Messaging) ────────────────────────────────────────
|
|
2144
|
+
async function cmdAnpMessage(args) {
|
|
2145
|
+
const jobId = getPositional(args, 0);
|
|
2146
|
+
const body = getFlag(args, 'message') || getFlag(args, 'body') || getFlag(args, 'm');
|
|
2147
|
+
const roleStr = getFlag(args, 'role') || 'client';
|
|
2148
|
+
if (!jobId || !body) {
|
|
2149
|
+
console.error(`${c.red}Usage: obolos anp message <job_id> --message "..." --role client|provider|evaluator${c.reset}`);
|
|
2150
|
+
process.exit(1);
|
|
2151
|
+
}
|
|
2152
|
+
const roleMap = { client: 0, provider: 1, evaluator: 2 };
|
|
2153
|
+
const role = roleMap[roleStr];
|
|
2154
|
+
if (role === undefined) {
|
|
2155
|
+
console.error(`${c.red}Invalid role. Use: client, provider, or evaluator${c.reset}`);
|
|
2156
|
+
process.exit(1);
|
|
2157
|
+
}
|
|
2158
|
+
const anp = await getANPSigningClient();
|
|
2159
|
+
const jobHash = await computeJobHash(jobId);
|
|
2160
|
+
const contentHash = await computeContentHash({ body, attachments: [] });
|
|
2161
|
+
const nonce = generateNonce();
|
|
2162
|
+
const signature = await anp.walletClient.signTypedData({
|
|
2163
|
+
account: anp.account,
|
|
2164
|
+
domain: ANP_DOMAIN,
|
|
2165
|
+
types: { MessageIntent: ANP_TYPES.MessageIntent },
|
|
2166
|
+
primaryType: 'MessageIntent',
|
|
2167
|
+
message: { jobHash, contentHash, role, nonce },
|
|
2168
|
+
});
|
|
2169
|
+
const document = {
|
|
2170
|
+
protocol: 'anp/v1', type: 'message',
|
|
2171
|
+
data: { jobId, jobHash, body, role, nonce: Number(nonce) },
|
|
2172
|
+
signer: anp.account.address.toLowerCase(),
|
|
2173
|
+
signature, timestamp: Date.now(),
|
|
2174
|
+
};
|
|
2175
|
+
const data = await apiPost('/api/anp/publish', document);
|
|
2176
|
+
console.log(`\n${c.green}Message sent!${c.reset}\n`);
|
|
2177
|
+
console.log(` ${c.bold}CID:${c.reset} ${data.cid}`);
|
|
2178
|
+
console.log(` ${c.bold}Job:${c.reset} ${jobId}`);
|
|
2179
|
+
console.log(` ${c.bold}Role:${c.reset} ${roleStr}`);
|
|
2180
|
+
console.log(` ${c.bold}Signer:${c.reset} ${anp.account.address}\n`);
|
|
2181
|
+
}
|
|
2182
|
+
async function cmdAnpThread(args) {
|
|
2183
|
+
const jobId = getPositional(args, 0);
|
|
2184
|
+
if (!jobId) {
|
|
2185
|
+
console.error(`${c.red}Usage: obolos anp thread <job_id>${c.reset}`);
|
|
2186
|
+
process.exit(1);
|
|
2187
|
+
}
|
|
2188
|
+
const data = await apiGet(`/api/anp/jobs/${encodeURIComponent(jobId)}/thread`);
|
|
2189
|
+
const messages = data.messages || [];
|
|
2190
|
+
if (messages.length === 0) {
|
|
2191
|
+
console.log(`\n${c.yellow}No messages for job ${jobId}.${c.reset}\n`);
|
|
2192
|
+
return;
|
|
2193
|
+
}
|
|
2194
|
+
console.log(`\n${c.bold}${c.cyan}Job Thread${c.reset} ${c.dim}— ${messages.length} messages${c.reset}\n`);
|
|
2195
|
+
for (const msg of messages) {
|
|
2196
|
+
const roleColors = { client: c.blue, provider: c.green, evaluator: c.yellow };
|
|
2197
|
+
const roleColor = roleColors[msg.roleName] || c.dim;
|
|
2198
|
+
console.log(` ${roleColor}${c.bold}[${msg.roleName}]${c.reset} ${c.dim}${msg.createdAt}${c.reset}`);
|
|
2199
|
+
console.log(` ${msg.body}`);
|
|
2200
|
+
console.log(` ${c.dim}CID: ${msg.cid} | Signer: ${msg.signer}${c.reset}\n`);
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
async function cmdAnpAmend(args) {
|
|
2204
|
+
const jobId = getPositional(args, 0);
|
|
2205
|
+
const bidHash = getFlag(args, 'bid-hash');
|
|
2206
|
+
const reason = getFlag(args, 'reason');
|
|
2207
|
+
const priceStr = getFlag(args, 'price');
|
|
2208
|
+
const deliveryStr = getFlag(args, 'delivery');
|
|
2209
|
+
const scopeDelta = getFlag(args, 'scope-delta') || '';
|
|
2210
|
+
if (!jobId || !bidHash || !reason) {
|
|
2211
|
+
console.error(`${c.red}Usage: obolos anp amend <job_id> --bid-hash 0x... --reason "..." [--price 25] [--delivery 48h] [--scope-delta "..."]${c.reset}`);
|
|
2212
|
+
process.exit(1);
|
|
2213
|
+
}
|
|
2214
|
+
const anp = await getANPSigningClient();
|
|
2215
|
+
const jobHash = await computeJobHash(jobId);
|
|
2216
|
+
const newPriceUsdc = priceStr ? usdToUsdc(parseFloat(priceStr)) : '0';
|
|
2217
|
+
const newDeliveryTime = deliveryStr ? parseTimeToSeconds(deliveryStr) : 0;
|
|
2218
|
+
const contentHash = await computeContentHash({ reason, scopeDelta });
|
|
2219
|
+
const nonce = generateNonce();
|
|
2220
|
+
const signature = await anp.walletClient.signTypedData({
|
|
2221
|
+
account: anp.account,
|
|
2222
|
+
domain: ANP_DOMAIN,
|
|
2223
|
+
types: { AmendmentIntent: ANP_TYPES.AmendmentIntent },
|
|
2224
|
+
primaryType: 'AmendmentIntent',
|
|
2225
|
+
message: {
|
|
2226
|
+
jobHash, originalBidHash: bidHash,
|
|
2227
|
+
newPrice: BigInt(newPriceUsdc), newDeliveryTime: BigInt(newDeliveryTime),
|
|
2228
|
+
contentHash, nonce,
|
|
2229
|
+
},
|
|
2230
|
+
});
|
|
2231
|
+
const document = {
|
|
2232
|
+
protocol: 'anp/v1', type: 'amendment',
|
|
2233
|
+
data: {
|
|
2234
|
+
jobId, jobHash, originalBidHash: bidHash,
|
|
2235
|
+
newPrice: newPriceUsdc, newDeliveryTime, reason, scopeDelta,
|
|
2236
|
+
nonce: Number(nonce),
|
|
2237
|
+
},
|
|
2238
|
+
signer: anp.account.address.toLowerCase(),
|
|
2239
|
+
signature, timestamp: Date.now(),
|
|
2240
|
+
};
|
|
2241
|
+
const data = await apiPost('/api/anp/publish', document);
|
|
2242
|
+
console.log(`\n${c.green}Amendment proposed!${c.reset}\n`);
|
|
2243
|
+
console.log(` ${c.bold}CID:${c.reset} ${data.cid}`);
|
|
2244
|
+
console.log(` ${c.bold}Job:${c.reset} ${jobId}`);
|
|
2245
|
+
if (priceStr)
|
|
2246
|
+
console.log(` ${c.bold}New Price:${c.reset} $${priceStr} USDC`);
|
|
2247
|
+
if (deliveryStr)
|
|
2248
|
+
console.log(` ${c.bold}New Delivery:${c.reset} ${deliveryStr}`);
|
|
2249
|
+
console.log(` ${c.bold}Reason:${c.reset} ${reason}`);
|
|
2250
|
+
console.log(`\n${c.dim}Counterparty must accept with: obolos anp accept-amend ${jobId} --amendment ${data.cid}${c.reset}\n`);
|
|
2251
|
+
}
|
|
2252
|
+
async function cmdAnpAcceptAmend(args) {
|
|
2253
|
+
const jobId = getPositional(args, 0);
|
|
2254
|
+
const amendmentCid = getFlag(args, 'amendment');
|
|
2255
|
+
if (!jobId || !amendmentCid) {
|
|
2256
|
+
console.error(`${c.red}Usage: obolos anp accept-amend <job_id> --amendment <amendment_cid>${c.reset}`);
|
|
2257
|
+
process.exit(1);
|
|
2258
|
+
}
|
|
2259
|
+
const anp = await getANPSigningClient();
|
|
2260
|
+
// Fetch amendment to compute struct hash
|
|
2261
|
+
const amendDoc = await apiGet(`/api/anp/objects/${encodeURIComponent(amendmentCid)}`);
|
|
2262
|
+
const ad = amendDoc.data || amendDoc;
|
|
2263
|
+
const contentHash = await computeContentHash({ reason: ad.reason, scopeDelta: ad.scopeDelta || '' });
|
|
2264
|
+
const amendmentHash = anp.hashAmendmentStruct({
|
|
2265
|
+
jobHash: ad.jobHash,
|
|
2266
|
+
originalBidHash: ad.originalBidHash,
|
|
2267
|
+
newPrice: BigInt(ad.newPrice),
|
|
2268
|
+
newDeliveryTime: BigInt(ad.newDeliveryTime),
|
|
2269
|
+
contentHash,
|
|
2270
|
+
nonce: BigInt(ad.nonce),
|
|
2271
|
+
});
|
|
2272
|
+
const nonce = generateNonce();
|
|
2273
|
+
const signature = await anp.walletClient.signTypedData({
|
|
2274
|
+
account: anp.account,
|
|
2275
|
+
domain: ANP_DOMAIN,
|
|
2276
|
+
types: { AmendmentAcceptance: ANP_TYPES.AmendmentAcceptance },
|
|
2277
|
+
primaryType: 'AmendmentAcceptance',
|
|
2278
|
+
message: { amendmentHash, nonce },
|
|
2279
|
+
});
|
|
2280
|
+
const document = {
|
|
2281
|
+
protocol: 'anp/v1', type: 'amendment_acceptance',
|
|
2282
|
+
data: { jobId, amendmentCid, amendmentHash, nonce: Number(nonce) },
|
|
2283
|
+
signer: anp.account.address.toLowerCase(),
|
|
2284
|
+
signature, timestamp: Date.now(),
|
|
2285
|
+
};
|
|
2286
|
+
const data = await apiPost('/api/anp/publish', document);
|
|
2287
|
+
console.log(`\n${c.green}Amendment accepted!${c.reset}\n`);
|
|
2288
|
+
console.log(` ${c.bold}CID:${c.reset} ${data.cid}`);
|
|
2289
|
+
console.log(` ${c.bold}Amendment CID:${c.reset} ${amendmentCid}`);
|
|
2290
|
+
console.log(` ${c.bold}Job:${c.reset} ${jobId}\n`);
|
|
2291
|
+
}
|
|
2292
|
+
async function cmdAnpCheckpoint(args) {
|
|
2293
|
+
const jobId = getPositional(args, 0);
|
|
2294
|
+
const milestoneStr = getFlag(args, 'milestone') || '0';
|
|
2295
|
+
const deliverable = getFlag(args, 'deliverable');
|
|
2296
|
+
const notes = getFlag(args, 'notes') || '';
|
|
2297
|
+
if (!jobId || !deliverable) {
|
|
2298
|
+
console.error(`${c.red}Usage: obolos anp checkpoint <job_id> --deliverable "..." [--milestone 0] [--notes "..."]${c.reset}`);
|
|
2299
|
+
process.exit(1);
|
|
2300
|
+
}
|
|
2301
|
+
const milestoneIndex = parseInt(milestoneStr, 10);
|
|
2302
|
+
const anp = await getANPSigningClient();
|
|
2303
|
+
const jobHash = await computeJobHash(jobId);
|
|
2304
|
+
const contentHash = await computeContentHash({ deliverable, notes });
|
|
2305
|
+
const nonce = generateNonce();
|
|
2306
|
+
const signature = await anp.walletClient.signTypedData({
|
|
2307
|
+
account: anp.account,
|
|
2308
|
+
domain: ANP_DOMAIN,
|
|
2309
|
+
types: { CheckpointIntent: ANP_TYPES.CheckpointIntent },
|
|
2310
|
+
primaryType: 'CheckpointIntent',
|
|
2311
|
+
message: { jobHash, milestoneIndex, contentHash, nonce },
|
|
2312
|
+
});
|
|
2313
|
+
const document = {
|
|
2314
|
+
protocol: 'anp/v1', type: 'checkpoint',
|
|
2315
|
+
data: { jobId, jobHash, milestoneIndex, deliverable, notes, nonce: Number(nonce) },
|
|
2316
|
+
signer: anp.account.address.toLowerCase(),
|
|
2317
|
+
signature, timestamp: Date.now(),
|
|
2318
|
+
};
|
|
2319
|
+
const data = await apiPost('/api/anp/publish', document);
|
|
2320
|
+
console.log(`\n${c.green}Checkpoint submitted!${c.reset}\n`);
|
|
2321
|
+
console.log(` ${c.bold}CID:${c.reset} ${data.cid}`);
|
|
2322
|
+
console.log(` ${c.bold}Job:${c.reset} ${jobId}`);
|
|
2323
|
+
console.log(` ${c.bold}Milestone:${c.reset} #${milestoneIndex}`);
|
|
2324
|
+
console.log(`\n${c.dim}Approve with: obolos anp approve-cp ${jobId} --checkpoint ${data.cid}${c.reset}\n`);
|
|
2325
|
+
}
|
|
2326
|
+
async function cmdAnpApproveCp(args) {
|
|
2327
|
+
const jobId = getPositional(args, 0);
|
|
2328
|
+
const checkpointCid = getFlag(args, 'checkpoint');
|
|
2329
|
+
if (!jobId || !checkpointCid) {
|
|
2330
|
+
console.error(`${c.red}Usage: obolos anp approve-cp <job_id> --checkpoint <checkpoint_cid>${c.reset}`);
|
|
2331
|
+
process.exit(1);
|
|
2332
|
+
}
|
|
2333
|
+
const anp = await getANPSigningClient();
|
|
2334
|
+
const cpDoc = await apiGet(`/api/anp/objects/${encodeURIComponent(checkpointCid)}`);
|
|
2335
|
+
const cd = cpDoc.data || cpDoc;
|
|
2336
|
+
const contentHash = await computeContentHash({ deliverable: cd.deliverable, notes: cd.notes || '' });
|
|
2337
|
+
const checkpointHash = anp.hashCheckpointStruct({
|
|
2338
|
+
jobHash: cd.jobHash,
|
|
2339
|
+
milestoneIndex: cd.milestoneIndex,
|
|
2340
|
+
contentHash,
|
|
2341
|
+
nonce: BigInt(cd.nonce),
|
|
2342
|
+
});
|
|
2343
|
+
const nonce = generateNonce();
|
|
2344
|
+
const signature = await anp.walletClient.signTypedData({
|
|
2345
|
+
account: anp.account,
|
|
2346
|
+
domain: ANP_DOMAIN,
|
|
2347
|
+
types: { CheckpointApproval: ANP_TYPES.CheckpointApproval },
|
|
2348
|
+
primaryType: 'CheckpointApproval',
|
|
2349
|
+
message: { checkpointHash, nonce },
|
|
2350
|
+
});
|
|
2351
|
+
const document = {
|
|
2352
|
+
protocol: 'anp/v1', type: 'checkpoint_approval',
|
|
2353
|
+
data: { jobId, checkpointCid, checkpointHash, nonce: Number(nonce) },
|
|
2354
|
+
signer: anp.account.address.toLowerCase(),
|
|
2355
|
+
signature, timestamp: Date.now(),
|
|
2356
|
+
};
|
|
2357
|
+
const data = await apiPost('/api/anp/publish', document);
|
|
2358
|
+
console.log(`\n${c.green}Checkpoint approved!${c.reset}\n`);
|
|
2359
|
+
console.log(` ${c.bold}CID:${c.reset} ${data.cid}`);
|
|
2360
|
+
console.log(` ${c.bold}Checkpoint CID:${c.reset} ${checkpointCid}`);
|
|
2361
|
+
console.log(` ${c.bold}Job:${c.reset} ${jobId}\n`);
|
|
2362
|
+
}
|
|
2363
|
+
async function cmdAnpAmendments(args) {
|
|
2364
|
+
const jobId = getPositional(args, 0);
|
|
2365
|
+
if (!jobId) {
|
|
2366
|
+
console.error(`${c.red}Usage: obolos anp amendments <job_id>${c.reset}`);
|
|
2367
|
+
process.exit(1);
|
|
2368
|
+
}
|
|
2369
|
+
const data = await apiGet(`/api/anp/jobs/${encodeURIComponent(jobId)}/amendments`);
|
|
2370
|
+
const amendments = data.amendments || [];
|
|
2371
|
+
if (amendments.length === 0) {
|
|
2372
|
+
console.log(`\n${c.yellow}No amendments for job ${jobId}.${c.reset}\n`);
|
|
2373
|
+
return;
|
|
2374
|
+
}
|
|
2375
|
+
console.log(`\n${c.bold}${c.cyan}Amendments${c.reset} ${c.dim}— ${amendments.length} total${c.reset}\n`);
|
|
2376
|
+
for (const a of amendments) {
|
|
2377
|
+
const status = a.accepted ? `${c.green}Accepted${c.reset}` : `${c.yellow}Pending${c.reset}`;
|
|
2378
|
+
console.log(` ${c.bold}CID:${c.reset} ${a.cid}`);
|
|
2379
|
+
console.log(` ${c.bold}Status:${c.reset} ${status}`);
|
|
2380
|
+
if (a.newPrice && a.newPrice !== '0')
|
|
2381
|
+
console.log(` ${c.bold}Price:${c.reset} $${(Number(a.newPrice) / 1_000_000).toFixed(2)} USDC`);
|
|
2382
|
+
if (a.newDeliveryTime)
|
|
2383
|
+
console.log(` ${c.bold}Delivery:${c.reset} ${Math.round(a.newDeliveryTime / 3600)}h`);
|
|
2384
|
+
console.log(` ${c.bold}Reason:${c.reset} ${a.reason}`);
|
|
2385
|
+
console.log(` ${c.bold}Signer:${c.reset} ${a.signer} ${c.dim}${a.createdAt}${c.reset}\n`);
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
async function cmdAnpCheckpoints(args) {
|
|
2389
|
+
const jobId = getPositional(args, 0);
|
|
2390
|
+
if (!jobId) {
|
|
2391
|
+
console.error(`${c.red}Usage: obolos anp checkpoints <job_id>${c.reset}`);
|
|
2392
|
+
process.exit(1);
|
|
2393
|
+
}
|
|
2394
|
+
const data = await apiGet(`/api/anp/jobs/${encodeURIComponent(jobId)}/checkpoints`);
|
|
2395
|
+
const checkpoints = data.checkpoints || [];
|
|
2396
|
+
if (checkpoints.length === 0) {
|
|
2397
|
+
console.log(`\n${c.yellow}No checkpoints for job ${jobId}.${c.reset}\n`);
|
|
2398
|
+
return;
|
|
2399
|
+
}
|
|
2400
|
+
console.log(`\n${c.bold}${c.cyan}Checkpoints${c.reset} ${c.dim}— ${checkpoints.length} total${c.reset}\n`);
|
|
2401
|
+
for (const cp of checkpoints) {
|
|
2402
|
+
const status = cp.approved ? `${c.green}Approved${c.reset}` : `${c.yellow}Pending${c.reset}`;
|
|
2403
|
+
console.log(` ${c.bold}#${cp.milestoneIndex}${c.reset} ${status} ${c.dim}${cp.createdAt}${c.reset}`);
|
|
2404
|
+
console.log(` ${c.bold}CID:${c.reset} ${cp.cid}`);
|
|
2405
|
+
console.log(` ${c.bold}Deliverable:${c.reset} ${cp.deliverable}`);
|
|
2406
|
+
if (cp.notes)
|
|
2407
|
+
console.log(` ${c.bold}Notes:${c.reset} ${cp.notes}`);
|
|
2408
|
+
console.log(` ${c.bold}Signer:${c.reset} ${cp.signer}\n`);
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2192
2411
|
function showAnpHelp() {
|
|
2193
2412
|
console.log(`
|
|
2194
2413
|
${c.bold}${c.cyan}obolos anp${c.reset} — Agent Negotiation Protocol (EIP-712 signed documents)
|
|
@@ -2222,13 +2441,41 @@ ${c.bold}Bid Options:${c.reset}
|
|
|
2222
2441
|
${c.bold}Accept Options:${c.reset}
|
|
2223
2442
|
--bid <bid_cid> Bid CID to accept (required)
|
|
2224
2443
|
|
|
2444
|
+
${c.bold}In-Job Messaging (IML):${c.reset}
|
|
2445
|
+
obolos anp message <job_id> [options] Send signed message on a running job
|
|
2446
|
+
obolos anp thread <job_id> View message thread for a job
|
|
2447
|
+
obolos anp amend <job_id> [options] Propose scope/price amendment
|
|
2448
|
+
obolos anp accept-amend <job_id> [opts] Accept a pending amendment
|
|
2449
|
+
obolos anp amendments <job_id> List amendments for a job
|
|
2450
|
+
obolos anp checkpoint <job_id> [options] Submit milestone checkpoint
|
|
2451
|
+
obolos anp approve-cp <job_id> [options] Approve a checkpoint
|
|
2452
|
+
obolos anp checkpoints <job_id> List checkpoints for a job
|
|
2453
|
+
|
|
2454
|
+
${c.bold}Message Options:${c.reset}
|
|
2455
|
+
--message "..." Message body (required)
|
|
2456
|
+
--role client|provider|evaluator Your role (default: client)
|
|
2457
|
+
|
|
2458
|
+
${c.bold}Amend Options:${c.reset}
|
|
2459
|
+
--bid-hash 0x... EIP-712 hash of accepted bid (required)
|
|
2460
|
+
--reason "..." Reason for amendment (required)
|
|
2461
|
+
--price 25 New price in USDC (optional)
|
|
2462
|
+
--delivery 48h New delivery time (optional)
|
|
2463
|
+
--scope-delta "..." Scope change description (optional)
|
|
2464
|
+
|
|
2465
|
+
${c.bold}Checkpoint Options:${c.reset}
|
|
2466
|
+
--deliverable "..." Deliverable content/URL (required)
|
|
2467
|
+
--milestone 0 Milestone index (default: 0)
|
|
2468
|
+
--notes "..." Additional notes (optional)
|
|
2469
|
+
|
|
2225
2470
|
${c.bold}Examples:${c.reset}
|
|
2226
2471
|
obolos anp list --status=open
|
|
2227
2472
|
obolos anp create --title "Analyze dataset" --description "Parse CSV" --min-budget 5 --max-budget 50 --deadline 7d --duration 3d
|
|
2228
|
-
obolos anp info sha256-abc123...
|
|
2229
2473
|
obolos anp bid sha256-abc123... --price 25 --delivery 48h --message "I can do this"
|
|
2230
2474
|
obolos anp accept sha256-listing... --bid sha256-bid...
|
|
2231
|
-
obolos anp
|
|
2475
|
+
obolos anp message job-uuid --message "Landscape or portrait?" --role provider
|
|
2476
|
+
obolos anp thread job-uuid
|
|
2477
|
+
obolos anp amend job-uuid --bid-hash 0x... --reason "Scope expanded" --price 35
|
|
2478
|
+
obolos anp checkpoint job-uuid --deliverable "https://..." --milestone 0 --notes "Script draft"
|
|
2232
2479
|
`);
|
|
2233
2480
|
}
|
|
2234
2481
|
async function cmdAnp(args) {
|
|
@@ -2256,6 +2503,34 @@ async function cmdAnp(args) {
|
|
|
2256
2503
|
case 'verify':
|
|
2257
2504
|
await cmdAnpVerify(subArgs);
|
|
2258
2505
|
break;
|
|
2506
|
+
// IML commands
|
|
2507
|
+
case 'message':
|
|
2508
|
+
case 'msg':
|
|
2509
|
+
await cmdAnpMessage(subArgs);
|
|
2510
|
+
break;
|
|
2511
|
+
case 'thread':
|
|
2512
|
+
await cmdAnpThread(subArgs);
|
|
2513
|
+
break;
|
|
2514
|
+
case 'amend':
|
|
2515
|
+
await cmdAnpAmend(subArgs);
|
|
2516
|
+
break;
|
|
2517
|
+
case 'accept-amend':
|
|
2518
|
+
await cmdAnpAcceptAmend(subArgs);
|
|
2519
|
+
break;
|
|
2520
|
+
case 'amendments':
|
|
2521
|
+
await cmdAnpAmendments(subArgs);
|
|
2522
|
+
break;
|
|
2523
|
+
case 'checkpoint':
|
|
2524
|
+
case 'cp':
|
|
2525
|
+
await cmdAnpCheckpoint(subArgs);
|
|
2526
|
+
break;
|
|
2527
|
+
case 'approve-cp':
|
|
2528
|
+
await cmdAnpApproveCp(subArgs);
|
|
2529
|
+
break;
|
|
2530
|
+
case 'checkpoints':
|
|
2531
|
+
case 'cps':
|
|
2532
|
+
await cmdAnpCheckpoints(subArgs);
|
|
2533
|
+
break;
|
|
2259
2534
|
case 'help':
|
|
2260
2535
|
case '--help':
|
|
2261
2536
|
case '-h':
|
|
@@ -2268,6 +2543,190 @@ async function cmdAnp(args) {
|
|
|
2268
2543
|
process.exit(1);
|
|
2269
2544
|
}
|
|
2270
2545
|
}
|
|
2546
|
+
// ─── Reputation Commands ─────────────────────────────────────────────────────
|
|
2547
|
+
function tierColor(tier) {
|
|
2548
|
+
switch (tier.toLowerCase()) {
|
|
2549
|
+
case 'diamond': return `${c.bold}${c.cyan}${tier}${c.reset}`;
|
|
2550
|
+
case 'platinum': return `${c.bold}${c.white}${tier}${c.reset}`;
|
|
2551
|
+
case 'gold':
|
|
2552
|
+
case 'established': return `${c.yellow}${tier}${c.reset}`;
|
|
2553
|
+
case 'silver':
|
|
2554
|
+
case 'developing': return `${c.dim}${c.white}${tier}${c.reset}`;
|
|
2555
|
+
case 'bronze':
|
|
2556
|
+
case 'limited': return `${c.dim}${tier}${c.reset}`;
|
|
2557
|
+
case 'flagged': return `${c.red}${tier}${c.reset}`;
|
|
2558
|
+
default: return `${c.dim}${tier}${c.reset}`;
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
function scoreBar(score) {
|
|
2562
|
+
const width = 20;
|
|
2563
|
+
const filled = Math.round((score / 100) * width);
|
|
2564
|
+
const empty = width - filled;
|
|
2565
|
+
let color = c.red;
|
|
2566
|
+
if (score >= 70)
|
|
2567
|
+
color = c.green;
|
|
2568
|
+
else if (score >= 40)
|
|
2569
|
+
color = c.yellow;
|
|
2570
|
+
return `${color}${'█'.repeat(filled)}${c.dim}${'░'.repeat(empty)}${c.reset} ${color}${score}${c.reset}/100`;
|
|
2571
|
+
}
|
|
2572
|
+
function verdictLabel(pass) {
|
|
2573
|
+
return pass
|
|
2574
|
+
? `${c.green}✔ PASS${c.reset}`
|
|
2575
|
+
: `${c.red}✘ FAIL${c.reset}`;
|
|
2576
|
+
}
|
|
2577
|
+
async function cmdReputationCheck(args) {
|
|
2578
|
+
const agentId = getPositional(args, 0);
|
|
2579
|
+
if (!agentId) {
|
|
2580
|
+
console.error(`${c.red}Usage: obolos reputation check <agentId> [--chain base]${c.reset}`);
|
|
2581
|
+
process.exit(1);
|
|
2582
|
+
}
|
|
2583
|
+
const chain = getFlag(args, 'chain') || 'base';
|
|
2584
|
+
console.log(`\n${c.dim}Checking reputation for agent ${c.bold}${agentId}${c.reset}${c.dim} on ${chain}...${c.reset}\n`);
|
|
2585
|
+
const data = await apiGet(`/api/anp/reputation/${encodeURIComponent(agentId)}?chain=${encodeURIComponent(chain)}`);
|
|
2586
|
+
// Header
|
|
2587
|
+
console.log(`${c.bold}${c.cyan}Reputation Report${c.reset} ${c.dim}Agent ${agentId}${c.reset}`);
|
|
2588
|
+
console.log(`${c.dim}${'─'.repeat(60)}${c.reset}`);
|
|
2589
|
+
// Combined score
|
|
2590
|
+
const combined = data.combined || {};
|
|
2591
|
+
console.log(` ${c.bold}Combined Score:${c.reset} ${scoreBar(combined.score ?? 0)}`);
|
|
2592
|
+
console.log(` ${c.bold}Tier:${c.reset} ${tierColor(combined.tier ?? 'unknown')}`);
|
|
2593
|
+
console.log(` ${c.bold}Verdict:${c.reset} ${verdictLabel(combined.pass ?? false)}`);
|
|
2594
|
+
console.log(` ${c.bold}Chain:${c.reset} ${data.chain || chain}`);
|
|
2595
|
+
if (data.address) {
|
|
2596
|
+
console.log(` ${c.bold}Address:${c.reset} ${data.address}`);
|
|
2597
|
+
}
|
|
2598
|
+
// Sybil warning
|
|
2599
|
+
if (combined.hasSybilFlags) {
|
|
2600
|
+
console.log(`\n ${c.red}${c.bold}⚠ Sybil flags detected${c.reset}`);
|
|
2601
|
+
}
|
|
2602
|
+
// Individual provider scores
|
|
2603
|
+
const scores = data.scores || [];
|
|
2604
|
+
if (scores.length > 0) {
|
|
2605
|
+
console.log(`\n ${c.bold}${c.cyan}Provider Scores (${scores.length})${c.reset}`);
|
|
2606
|
+
console.log(` ${c.dim}${'─'.repeat(56)}${c.reset}`);
|
|
2607
|
+
for (const s of scores) {
|
|
2608
|
+
const provider = s.provider === 'rnwy' ? 'RNWY' : s.provider === 'agentproof' ? 'AgentProof' : s.provider;
|
|
2609
|
+
console.log(`\n ${c.bold}${provider}${c.reset}`);
|
|
2610
|
+
console.log(` Score: ${scoreBar(s.score ?? 0)}`);
|
|
2611
|
+
console.log(` Tier: ${tierColor(s.tier ?? 'unknown')}`);
|
|
2612
|
+
console.log(` Verdict: ${verdictLabel(s.pass ?? false)}`);
|
|
2613
|
+
if (s.sybilFlags && s.sybilFlags.length > 0) {
|
|
2614
|
+
console.log(` ${c.red}Sybil: ${s.sybilFlags.join(', ')}${c.reset}`);
|
|
2615
|
+
}
|
|
2616
|
+
if (s.riskFlags && s.riskFlags.length > 0) {
|
|
2617
|
+
console.log(` ${c.yellow}Risk: ${s.riskFlags.join(', ')}${c.reset}`);
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
else {
|
|
2622
|
+
console.log(`\n ${c.dim}No provider scores available.${c.reset}`);
|
|
2623
|
+
}
|
|
2624
|
+
console.log(`\n ${c.dim}Checked: ${data.checkedAt ? formatDate(data.checkedAt) : 'just now'}${c.reset}\n`);
|
|
2625
|
+
}
|
|
2626
|
+
async function cmdReputationCompare(args) {
|
|
2627
|
+
// Collect all positional args (agent IDs, optionally prefixed with chain:)
|
|
2628
|
+
const agents = [];
|
|
2629
|
+
for (const arg of args) {
|
|
2630
|
+
if (arg.startsWith('--'))
|
|
2631
|
+
continue;
|
|
2632
|
+
const parts = arg.split(':');
|
|
2633
|
+
if (parts.length === 2) {
|
|
2634
|
+
const id = parseInt(parts[1], 10);
|
|
2635
|
+
if (isNaN(id)) {
|
|
2636
|
+
console.error(`${c.red}Invalid agent ID: ${parts[1]}${c.reset}`);
|
|
2637
|
+
process.exit(1);
|
|
2638
|
+
}
|
|
2639
|
+
agents.push({ agentId: id, chain: parts[0] });
|
|
2640
|
+
}
|
|
2641
|
+
else {
|
|
2642
|
+
const id = parseInt(parts[0], 10);
|
|
2643
|
+
if (isNaN(id)) {
|
|
2644
|
+
console.error(`${c.red}Invalid agent ID: ${parts[0]}${c.reset}`);
|
|
2645
|
+
process.exit(1);
|
|
2646
|
+
}
|
|
2647
|
+
agents.push({ agentId: id, chain: 'base' });
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
if (agents.length < 2) {
|
|
2651
|
+
console.error(`${c.red}Usage: obolos reputation compare <id1> <id2> [id3...]${c.reset}`);
|
|
2652
|
+
console.error(`${c.dim} Prefix with chain: obolos rep compare base:123 ethereum:456${c.reset}`);
|
|
2653
|
+
process.exit(1);
|
|
2654
|
+
}
|
|
2655
|
+
console.log(`\n${c.dim}Comparing ${agents.length} agents in parallel...${c.reset}\n`);
|
|
2656
|
+
// Fetch all in parallel
|
|
2657
|
+
const results = await Promise.all(agents.map(a => apiGet(`/api/anp/reputation/${a.agentId}?chain=${encodeURIComponent(a.chain)}`)
|
|
2658
|
+
.catch((err) => ({ agentId: a.agentId, chain: a.chain, error: err.message }))));
|
|
2659
|
+
// Sort by combined score descending
|
|
2660
|
+
const sorted = results
|
|
2661
|
+
.map((r, i) => ({ ...r, _input: agents[i] }))
|
|
2662
|
+
.sort((a, b) => ((b.combined?.score ?? -1) - (a.combined?.score ?? -1)));
|
|
2663
|
+
// Table header
|
|
2664
|
+
console.log(`${c.bold}${c.cyan}Reputation Comparison${c.reset}`);
|
|
2665
|
+
console.log(`${c.dim}${'─'.repeat(74)}${c.reset}`);
|
|
2666
|
+
console.log(` ${c.bold}${'#'.padEnd(4)}${'Agent'.padEnd(12)}${'Chain'.padEnd(12)}${'Score'.padEnd(24)}${'Tier'.padEnd(14)}Verdict${c.reset}`);
|
|
2667
|
+
console.log(` ${c.dim}${'─'.repeat(70)}${c.reset}`);
|
|
2668
|
+
sorted.forEach((r, i) => {
|
|
2669
|
+
const rank = `${i + 1}.`.padEnd(4);
|
|
2670
|
+
const agent = String(r._input.agentId).padEnd(12);
|
|
2671
|
+
const chain = r._input.chain.padEnd(12);
|
|
2672
|
+
if (r.error) {
|
|
2673
|
+
console.log(` ${rank}${agent}${chain}${c.red}Error: ${r.error}${c.reset}`);
|
|
2674
|
+
return;
|
|
2675
|
+
}
|
|
2676
|
+
const combined = r.combined || {};
|
|
2677
|
+
const score = combined.score ?? 0;
|
|
2678
|
+
const bar = scoreBar(score);
|
|
2679
|
+
// scoreBar has ANSI codes so we can't pad it normally; we pad the raw number
|
|
2680
|
+
const barPadded = bar; // already formatted
|
|
2681
|
+
const tier = tierColor(combined.tier ?? 'unknown');
|
|
2682
|
+
const verdict = verdictLabel(combined.pass ?? false);
|
|
2683
|
+
const sybil = combined.hasSybilFlags ? ` ${c.red}⚠ sybil${c.reset}` : '';
|
|
2684
|
+
console.log(` ${rank}${agent}${chain}${barPadded} ${tier.padEnd(14)} ${verdict}${sybil}`);
|
|
2685
|
+
});
|
|
2686
|
+
console.log(`${c.dim}${'─'.repeat(74)}${c.reset}\n`);
|
|
2687
|
+
}
|
|
2688
|
+
function showReputationHelp() {
|
|
2689
|
+
console.log(`
|
|
2690
|
+
${c.bold}${c.cyan}obolos reputation${c.reset} — Agent trust & reputation checking
|
|
2691
|
+
|
|
2692
|
+
${c.bold}Subcommands:${c.reset}
|
|
2693
|
+
check <agentId> Check reputation for an agent
|
|
2694
|
+
compare <id1> <id2> [...] Compare multiple agents side-by-side
|
|
2695
|
+
|
|
2696
|
+
${c.bold}Options (check):${c.reset}
|
|
2697
|
+
--chain <chain> Blockchain to check (default: base)
|
|
2698
|
+
|
|
2699
|
+
${c.bold}Examples:${c.reset}
|
|
2700
|
+
obolos reputation check 16907
|
|
2701
|
+
obolos reputation check 16907 --chain ethereum
|
|
2702
|
+
obolos rep check 16907
|
|
2703
|
+
obolos reputation compare 123 456 789
|
|
2704
|
+
obolos rep compare base:123 base:456 ethereum:789
|
|
2705
|
+
`);
|
|
2706
|
+
}
|
|
2707
|
+
async function cmdReputation(args) {
|
|
2708
|
+
const sub = args[0];
|
|
2709
|
+
const subArgs = args.slice(1);
|
|
2710
|
+
switch (sub) {
|
|
2711
|
+
case 'check':
|
|
2712
|
+
await cmdReputationCheck(subArgs);
|
|
2713
|
+
break;
|
|
2714
|
+
case 'compare':
|
|
2715
|
+
case 'cmp':
|
|
2716
|
+
await cmdReputationCompare(subArgs);
|
|
2717
|
+
break;
|
|
2718
|
+
case 'help':
|
|
2719
|
+
case '--help':
|
|
2720
|
+
case '-h':
|
|
2721
|
+
case undefined:
|
|
2722
|
+
showReputationHelp();
|
|
2723
|
+
break;
|
|
2724
|
+
default:
|
|
2725
|
+
console.error(`${c.red}Unknown reputation subcommand: ${sub}${c.reset}`);
|
|
2726
|
+
showReputationHelp();
|
|
2727
|
+
process.exit(1);
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2271
2730
|
// ─── Help ───────────────────────────────────────────────────────────────────
|
|
2272
2731
|
function showHelp() {
|
|
2273
2732
|
console.log(`
|
|
@@ -2310,8 +2769,18 @@ ${c.bold}ANP Commands (Agent Negotiation Protocol):${c.reset}
|
|
|
2310
2769
|
obolos anp bid <cid> [opts] Sign and publish a bid
|
|
2311
2770
|
obolos anp accept <cid> [opts] Accept a bid (sign AcceptIntent)
|
|
2312
2771
|
obolos anp verify <cid> Verify document integrity
|
|
2772
|
+
obolos anp message <job> [opts] Send in-job message
|
|
2773
|
+
obolos anp thread <job> View job message thread
|
|
2774
|
+
obolos anp amend <job> [opts] Propose amendment
|
|
2775
|
+
obolos anp checkpoint <job> Submit milestone checkpoint
|
|
2313
2776
|
obolos anp help Show ANP command help
|
|
2314
2777
|
|
|
2778
|
+
${c.bold}Reputation Commands:${c.reset}
|
|
2779
|
+
obolos reputation check <id> Check agent trust score
|
|
2780
|
+
obolos reputation compare ... Compare multiple agents
|
|
2781
|
+
obolos reputation help Show reputation command help
|
|
2782
|
+
${c.dim}(alias: obolos rep ...)${c.reset}
|
|
2783
|
+
|
|
2315
2784
|
${c.bold}Call Options:${c.reset}
|
|
2316
2785
|
--method POST|GET|PUT HTTP method (default: GET)
|
|
2317
2786
|
--body '{"key":"value"}' Request body (JSON)
|
|
@@ -2335,6 +2804,10 @@ ${c.bold}Examples:${c.reset}
|
|
|
2335
2804
|
obolos anp create --title "Analyze data" --min-budget 5 --max-budget 50 --deadline 7d
|
|
2336
2805
|
obolos anp bid sha256-abc... --price 25 --delivery 48h --message "I can do this"
|
|
2337
2806
|
obolos anp accept sha256-listing... --bid sha256-bid...
|
|
2807
|
+
obolos rep check 16907
|
|
2808
|
+
obolos rep check 16907 --chain ethereum
|
|
2809
|
+
obolos rep compare 123 456 789
|
|
2810
|
+
obolos rep compare base:123 ethereum:456
|
|
2338
2811
|
`);
|
|
2339
2812
|
}
|
|
2340
2813
|
// ─── Main ───────────────────────────────────────────────────────────────────
|
|
@@ -2381,6 +2854,10 @@ async function main() {
|
|
|
2381
2854
|
case 'anp':
|
|
2382
2855
|
await cmdAnp(commandArgs);
|
|
2383
2856
|
break;
|
|
2857
|
+
case 'reputation':
|
|
2858
|
+
case 'rep':
|
|
2859
|
+
await cmdReputation(commandArgs);
|
|
2860
|
+
break;
|
|
2384
2861
|
case 'help':
|
|
2385
2862
|
case '--help':
|
|
2386
2863
|
case '-h':
|