@lawrenceliang-btc/atel-sdk 0.9.23 → 1.0.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/bin/atel.mjs +511 -89
- package/dist/attachment/index.js +20 -4
- package/dist/audit/async-queue.d.ts +2 -30
- package/dist/audit/async-queue.js +3 -85
- package/dist/audit/index.d.ts +0 -4
- package/dist/audit/index.js +2 -4
- package/dist/audit/service.d.ts +5 -52
- package/dist/audit/service.js +4 -126
- package/dist/audit/tiered-verifier.d.ts +5 -17
- package/dist/audit/tiered-verifier.js +5 -117
- package/dist/executor/index.d.ts +10 -73
- package/dist/executor/index.js +6 -690
- package/dist/network/index.js +1 -1
- package/package.json +1 -2
- package/skill/SKILL.md +52 -25
- package/bin/ollama-manager.mjs +0 -168
- package/dist/audit/llm-verifier.d.ts +0 -28
- package/dist/audit/llm-verifier.js +0 -165
- package/dist/audit/ollama-manager.d.ts +0 -44
- package/dist/audit/ollama-manager.js +0 -193
package/bin/atel.mjs
CHANGED
|
@@ -56,7 +56,7 @@ import { resolve, join } from 'node:path';
|
|
|
56
56
|
import crypto from 'node:crypto';
|
|
57
57
|
import {
|
|
58
58
|
AgentIdentity, AgentEndpoint, AgentClient, HandshakeManager,
|
|
59
|
-
createMessage, RegistryClient, ExecutionTrace, ProofGenerator,
|
|
59
|
+
createMessage, verifyMessage, parseDID, RegistryClient, ExecutionTrace, ProofGenerator,
|
|
60
60
|
SolanaAnchorProvider, BaseAnchorProvider, BSCAnchorProvider,
|
|
61
61
|
autoNetworkSetup, collectCandidates, connectToAgent,
|
|
62
62
|
discoverPublicIP, checkReachable, ContentAuditor, TrustScoreClient,
|
|
@@ -64,7 +64,9 @@ import {
|
|
|
64
64
|
TrustGraph, calculateTaskWeight,
|
|
65
65
|
} from '@lawrenceliang-btc/atel-sdk';
|
|
66
66
|
import { TunnelManager, HeartbeatManager } from './tunnel-manager.mjs';
|
|
67
|
-
|
|
67
|
+
// ollama-manager removed — SDK does not run local models
|
|
68
|
+
const initializeOllama = async () => {};
|
|
69
|
+
const getOllamaStatus = async () => ({ running: false, models: [] });
|
|
68
70
|
import { parseAttachmentFlags, processAttachments } from './atel-attachment-helpers.mjs';
|
|
69
71
|
|
|
70
72
|
const ATEL_DIR = resolve(process.env.ATEL_DIR || '.atel');
|
|
@@ -258,6 +260,23 @@ Friend Management Commands:
|
|
|
258
260
|
Examples:
|
|
259
261
|
atel friend status
|
|
260
262
|
|
|
263
|
+
── How Friend System Affects Communication ──────────────────────────
|
|
264
|
+
|
|
265
|
+
By default, agents operate in "friends_only" mode:
|
|
266
|
+
• P2P messages (atel send) and tasks will be REJECTED if you are not a friend
|
|
267
|
+
• You must send a friend request and have it accepted first
|
|
268
|
+
|
|
269
|
+
Typical flow to communicate with a new agent:
|
|
270
|
+
1. atel friend request <their-did> --message "Hi, lets connect!"
|
|
271
|
+
2. They run: atel friend pending (to see your request)
|
|
272
|
+
3. They run: atel friend accept <req-id> (to accept)
|
|
273
|
+
4. Now you can: atel send <their-did> "Hello!"
|
|
274
|
+
|
|
275
|
+
To allow communication WITHOUT friend relationship (open mode):
|
|
276
|
+
Edit .atel/policy.json and set:
|
|
277
|
+
{ "relationshipPolicy": { "defaultMode": "open" } }
|
|
278
|
+
Restart your agent. Both sides can configure this independently.
|
|
279
|
+
|
|
261
280
|
For more information, visit: https://docs.atel.io/friend-system
|
|
262
281
|
`);
|
|
263
282
|
}
|
|
@@ -1927,25 +1946,13 @@ async function cmdStart(port) {
|
|
|
1927
1946
|
const toolGatewayServer = await startToolGatewayProxy(toolProxyPort, id, policy);
|
|
1928
1947
|
log({ event: 'tool_gateway_started', port: toolProxyPort });
|
|
1929
1948
|
|
|
1930
|
-
// ──
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
port: executorPort,
|
|
1938
|
-
callbackUrl: `http://127.0.0.1:${p}/atel/v1/result`,
|
|
1939
|
-
gatewayUrl: getGatewayUrl(),
|
|
1940
|
-
contextPath: join(ATEL_DIR, 'agent-context.md'),
|
|
1941
|
-
log,
|
|
1942
|
-
});
|
|
1943
|
-
await builtinExecutor.start();
|
|
1944
|
-
EXECUTOR_URL = `http://127.0.0.1:${executorPort}`;
|
|
1945
|
-
log({ event: 'builtin_executor_started', port: executorPort, url: EXECUTOR_URL });
|
|
1946
|
-
} catch (e) {
|
|
1947
|
-
log({ event: 'builtin_executor_failed', error: e.message, note: 'Falling back to echo mode. Set ATEL_EXECUTOR_URL for external executor.' });
|
|
1948
|
-
}
|
|
1949
|
+
// ── Executor: Agent brings their own AI. SDK does not run a built-in executor. ──
|
|
1950
|
+
// If ATEL_EXECUTOR_URL is set, P2P tasks are forwarded there.
|
|
1951
|
+
// Milestone work is done by the Agent itself (not the SDK).
|
|
1952
|
+
if (EXECUTOR_URL) {
|
|
1953
|
+
log({ event: 'external_executor_configured', url: EXECUTOR_URL });
|
|
1954
|
+
} else {
|
|
1955
|
+
log({ event: 'no_executor', note: 'No ATEL_EXECUTOR_URL set. Agent handles tasks via notifications + CLI.' });
|
|
1949
1956
|
}
|
|
1950
1957
|
|
|
1951
1958
|
// ── Trust Score Client (persistent) ──
|
|
@@ -2067,17 +2074,74 @@ async function cmdStart(port) {
|
|
|
2067
2074
|
}
|
|
2068
2075
|
});
|
|
2069
2076
|
|
|
2070
|
-
//
|
|
2077
|
+
// ── Event dedup (processed eventIds) ──
|
|
2078
|
+
const processedEvents = new Set();
|
|
2079
|
+
|
|
2080
|
+
// Webhook notification: POST /atel/v1/notify
|
|
2081
|
+
// SDK only: logs, writes inbox, prints prompt. Does NOT execute any actions.
|
|
2082
|
+
// Agent reads inbox or webhook to decide what to do.
|
|
2071
2083
|
endpoint.app?.post?.('/atel/v1/notify', async (req, res) => {
|
|
2072
|
-
const
|
|
2073
|
-
|
|
2074
|
-
|
|
2084
|
+
const body = req.body || {};
|
|
2085
|
+
// Support both old format {event, payload} and new format {eventId, eventType, payload}
|
|
2086
|
+
const event = body.eventType || body.event;
|
|
2087
|
+
const payload = body.payload || {};
|
|
2088
|
+
const eventId = body.eventId;
|
|
2089
|
+
const prompt = body.prompt || payload.prompt;
|
|
2090
|
+
const recommendedActions = body.recommendedActions || payload.recommendedActions;
|
|
2091
|
+
|
|
2092
|
+
if (!event) {
|
|
2093
|
+
res.status(400).json({ error: 'event/eventType required' });
|
|
2075
2094
|
return;
|
|
2076
2095
|
}
|
|
2077
2096
|
|
|
2078
|
-
|
|
2097
|
+
// Dedup: skip already processed events
|
|
2098
|
+
if (eventId && processedEvents.has(eventId)) {
|
|
2099
|
+
log({ event: 'event_dedup_skip', eventId, eventType: event });
|
|
2100
|
+
res.json({ status: 'already_processed', eventId });
|
|
2101
|
+
return;
|
|
2102
|
+
}
|
|
2103
|
+
if (eventId) {
|
|
2104
|
+
processedEvents.add(eventId);
|
|
2105
|
+
// Keep set bounded
|
|
2106
|
+
if (processedEvents.size > 1000) {
|
|
2107
|
+
const first = processedEvents.values().next().value;
|
|
2108
|
+
processedEvents.delete(first);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
// Log the full event
|
|
2113
|
+
log({ event: 'notification_received', eventId, eventType: event, orderId: body.orderId || payload.orderId, prompt: prompt ? prompt.substring(0, 100) : undefined });
|
|
2114
|
+
|
|
2115
|
+
// ── Universal notification handler: print prompt + log + respond ──
|
|
2116
|
+
// SDK does NOT execute any actions. Agent reads inbox/webhook and decides.
|
|
2117
|
+
if (prompt) {
|
|
2118
|
+
console.log(`\n📨 [${event}] ${prompt.split('\n')[0]}`);
|
|
2119
|
+
}
|
|
2120
|
+
if (recommendedActions && Array.isArray(recommendedActions)) {
|
|
2121
|
+
for (const action of recommendedActions) {
|
|
2122
|
+
if (action.command) console.log(` → ${action.command.join(' ')}`);
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
console.log('');
|
|
2126
|
+
|
|
2127
|
+
// Write full event to inbox for Agent program to read
|
|
2128
|
+
log({
|
|
2129
|
+
event: 'notification',
|
|
2130
|
+
eventId,
|
|
2131
|
+
eventType: event,
|
|
2132
|
+
orderId: body.orderId || payload.orderId,
|
|
2133
|
+
payload,
|
|
2134
|
+
prompt,
|
|
2135
|
+
recommendedActions,
|
|
2136
|
+
sequenceNo: body.sequenceNo,
|
|
2137
|
+
dedupeKey: body.dedupeKey,
|
|
2138
|
+
});
|
|
2139
|
+
|
|
2140
|
+
res.json({ status: 'received', eventId, eventType: event });
|
|
2141
|
+
return;
|
|
2079
2142
|
|
|
2080
|
-
|
|
2143
|
+
// ── Legacy event handlers below (kept but unreachable for reference) ──
|
|
2144
|
+
if (event === 'order_created_legacy') {
|
|
2081
2145
|
// New order notification - decide whether to accept
|
|
2082
2146
|
const { orderId, requesterDid, capabilityType, priceAmount, description } = payload;
|
|
2083
2147
|
|
|
@@ -2178,35 +2242,55 @@ async function cmdStart(port) {
|
|
|
2178
2242
|
return;
|
|
2179
2243
|
}
|
|
2180
2244
|
|
|
2181
|
-
// Auto-accept (default)
|
|
2245
|
+
// Auto-accept with retry (default)
|
|
2182
2246
|
log({ event: 'order_auto_accept', orderId, requesterDid, capabilityType });
|
|
2183
|
-
|
|
2184
|
-
// Call platform API to accept
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
timestamp,
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2247
|
+
|
|
2248
|
+
// Call platform API to accept (retry up to 3 times for transient RPC failures)
|
|
2249
|
+
let acceptOk = false;
|
|
2250
|
+
let lastError = '';
|
|
2251
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
2252
|
+
try {
|
|
2253
|
+
const timestamp = new Date().toISOString();
|
|
2254
|
+
const payload = {};
|
|
2255
|
+
const signPayload = { did: id.did, timestamp, payload };
|
|
2256
|
+
const signature = sign(signPayload, id.secretKey);
|
|
2257
|
+
const signedRequest = { did: id.did, timestamp, signature, payload };
|
|
2258
|
+
|
|
2259
|
+
log({ event: 'order_accept_calling_api', orderId, platform: ATEL_PLATFORM, attempt: attempt + 1 });
|
|
2260
|
+
|
|
2261
|
+
const acceptResp = await fetch(`${ATEL_PLATFORM}/trade/v1/order/${orderId}/accept`, {
|
|
2262
|
+
method: 'POST',
|
|
2263
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2264
|
+
body: JSON.stringify(signedRequest),
|
|
2265
|
+
signal: AbortSignal.timeout(30000), // 30s timeout (escrow tx can take 10-15s)
|
|
2266
|
+
});
|
|
2267
|
+
|
|
2268
|
+
log({ event: 'order_accept_response', orderId, status: acceptResp.status, ok: acceptResp.ok, attempt: attempt + 1 });
|
|
2269
|
+
|
|
2270
|
+
if (acceptResp.ok) {
|
|
2271
|
+
acceptOk = true;
|
|
2272
|
+
break;
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
const errBody = await acceptResp.text();
|
|
2276
|
+
lastError = errBody;
|
|
2277
|
+
log({ event: 'order_accept_failed', orderId, error: errBody, status: acceptResp.status, attempt: attempt + 1 });
|
|
2278
|
+
|
|
2279
|
+
// Retry if server hints it's a transient failure
|
|
2280
|
+
const shouldRetry = errBody.includes('retry') || errBody.includes('RPC') || errBody.includes('balance check failed');
|
|
2281
|
+
if (!shouldRetry || attempt >= 2) break;
|
|
2282
|
+
log({ event: 'order_accept_retrying', orderId, waitMs: 5000 });
|
|
2283
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
2284
|
+
} catch (err) {
|
|
2285
|
+
lastError = err.message;
|
|
2286
|
+
log({ event: 'order_accept_error', orderId, error: err.message, attempt: attempt + 1 });
|
|
2287
|
+
if (attempt >= 2) break;
|
|
2288
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
if (acceptOk) {
|
|
2293
|
+
try {
|
|
2210
2294
|
log({ event: 'order_accepted', orderId });
|
|
2211
2295
|
res.json({ status: 'accepted', orderId });
|
|
2212
2296
|
|
|
@@ -2243,15 +2327,12 @@ async function cmdStart(port) {
|
|
|
2243
2327
|
log({ event: 'milestone_auto_approve_error', orderId, error: e.message });
|
|
2244
2328
|
}
|
|
2245
2329
|
}, 2000);
|
|
2246
|
-
}
|
|
2247
|
-
|
|
2248
|
-
log({ event: 'order_accept_failed', orderId, error, status: acceptResp.status });
|
|
2249
|
-
res.status(500).json({ error: 'accept failed: ' + error });
|
|
2330
|
+
} catch (e) {
|
|
2331
|
+
log({ event: 'milestone_auto_approve_outer_error', orderId, error: e.message });
|
|
2250
2332
|
}
|
|
2251
|
-
}
|
|
2252
|
-
log({ event: '
|
|
2253
|
-
|
|
2254
|
-
res.status(500).json({ error: err.message });
|
|
2333
|
+
} else {
|
|
2334
|
+
log({ event: 'order_accept_gave_up', orderId, lastError });
|
|
2335
|
+
res.status(500).json({ error: 'accept failed after retries: ' + lastError });
|
|
2255
2336
|
}
|
|
2256
2337
|
return;
|
|
2257
2338
|
}
|
|
@@ -2345,25 +2426,31 @@ async function cmdStart(port) {
|
|
|
2345
2426
|
return;
|
|
2346
2427
|
}
|
|
2347
2428
|
|
|
2348
|
-
// Milestone events
|
|
2429
|
+
// Milestone events (helpers defined above, shared with /atel/v1/result handler)
|
|
2349
2430
|
if (event === 'milestone_submitted') {
|
|
2350
2431
|
const { orderId, milestoneIndex, resultSummary, submitCount } = payload;
|
|
2351
|
-
log({
|
|
2352
|
-
|
|
2353
|
-
orderId,
|
|
2354
|
-
milestoneIndex,
|
|
2355
|
-
resultSummary: resultSummary || '(no summary)',
|
|
2356
|
-
submitCount,
|
|
2357
|
-
message: `M${milestoneIndex} submitted. Review the content and verify:`,
|
|
2358
|
-
action: `atel milestone-verify ${orderId} ${milestoneIndex} --pass (or --reject "reason")`,
|
|
2359
|
-
});
|
|
2360
|
-
// Print prominently so the agent/user actually sees the content
|
|
2361
|
-
console.log(`\n📋 [Milestone M${milestoneIndex} submitted for ${orderId}]`);
|
|
2362
|
-
console.log(` Content: ${resultSummary || '(no summary provided)'}`);
|
|
2363
|
-
console.log(` Attempt: ${submitCount || 1}/3`);
|
|
2364
|
-
console.log(` Action: atel milestone-verify ${orderId} ${milestoneIndex} --pass`);
|
|
2365
|
-
console.log(` atel milestone-verify ${orderId} ${milestoneIndex} --reject "reason"\n`);
|
|
2432
|
+
log({ event: 'milestone_submitted_notification', orderId, milestoneIndex, resultSummary: resultSummary || '(no summary)', submitCount });
|
|
2433
|
+
console.log(`\n📋 M${milestoneIndex} submitted for ${orderId}. Agent reviewing...`);
|
|
2366
2434
|
res.json({ status: 'received', event });
|
|
2435
|
+
// Requester Agent: auto-review using agent's own brain (async callback)
|
|
2436
|
+
setTimeout(async () => {
|
|
2437
|
+
try {
|
|
2438
|
+
const ms = await getMilestoneInfo(orderId);
|
|
2439
|
+
if (!ms || !ms.milestones) return;
|
|
2440
|
+
const m = ms.milestones[milestoneIndex];
|
|
2441
|
+
if (!m) return;
|
|
2442
|
+
const orderDesc = ms.description || orderId;
|
|
2443
|
+
const submittedContent = m.result_summary || resultSummary || '(no content)';
|
|
2444
|
+
const prompt = `You are an AI Agent reviewing a milestone submission.\n\nOrder: ${orderDesc}\nMilestone M${milestoneIndex}: ${m.description || m.title}\nSubmitted content: ${submittedContent}\n\nEvaluate: Does the submission adequately fulfill the milestone goal?\nReply with EXACTLY one of:\n- PASS\n- REJECT: <specific reason>`;
|
|
2445
|
+
console.log(`\n🔍 Reviewing M${milestoneIndex} for ${orderId}...`);
|
|
2446
|
+
await agentExecute(prompt, `review-${orderId}-${milestoneIndex}`, { orderId, milestoneIndex, type: 'verify' });
|
|
2447
|
+
} catch (e) {
|
|
2448
|
+
// Fallback: auto-pass if agent brain unavailable
|
|
2449
|
+
log({ event: 'auto_review_fallback', orderId, milestone: milestoneIndex, error: e.message });
|
|
2450
|
+
console.log(`\n✅ Auto-pass M${milestoneIndex} (review fallback)`);
|
|
2451
|
+
await autoMilestoneVerify(orderId, milestoneIndex, true);
|
|
2452
|
+
}
|
|
2453
|
+
}, 3000);
|
|
2367
2454
|
return;
|
|
2368
2455
|
}
|
|
2369
2456
|
|
|
@@ -2371,8 +2458,23 @@ async function cmdStart(port) {
|
|
|
2371
2458
|
const { orderId, milestoneIndex, currentMilestone, totalMilestones, allComplete } = payload;
|
|
2372
2459
|
if (allComplete) {
|
|
2373
2460
|
log({ event: 'all_milestones_complete', orderId, message: 'Settlement in progress' });
|
|
2461
|
+
console.log(`\n🎉 All milestones complete for ${orderId}. Settlement in progress.`);
|
|
2374
2462
|
} else {
|
|
2375
|
-
log({ event: 'milestone_verified_notification', orderId, milestoneIndex, next: currentMilestone
|
|
2463
|
+
log({ event: 'milestone_verified_notification', orderId, milestoneIndex, next: currentMilestone });
|
|
2464
|
+
console.log(`\n✅ M${milestoneIndex} verified for ${orderId}. Working on M${currentMilestone}...`);
|
|
2465
|
+
// Auto-submit next milestone using agent's own brain (async callback)
|
|
2466
|
+
setTimeout(async () => {
|
|
2467
|
+
try {
|
|
2468
|
+
const ms = await getMilestoneInfo(orderId);
|
|
2469
|
+
if (!ms || !ms.milestones) return;
|
|
2470
|
+
const nextM = ms.milestones[currentMilestone];
|
|
2471
|
+
if (!nextM) return;
|
|
2472
|
+
const orderDesc = ms.description || orderId;
|
|
2473
|
+
const prompt = `You are an AI Agent executing a paid task.\n\nOrder: ${orderDesc}\nMilestone M${currentMilestone}: ${nextM.description || nextM.title}\n\nPrevious milestones have been completed and approved. Now complete this milestone. Provide a thorough, detailed deliverable. Output ONLY the deliverable content.`;
|
|
2474
|
+
console.log(`\n🤖 Working on M${currentMilestone} for ${orderId}...`);
|
|
2475
|
+
await agentExecute(prompt, `milestone-${orderId}-${currentMilestone}`, { orderId, milestoneIndex: currentMilestone, type: 'submit' });
|
|
2476
|
+
} catch (e) { log({ event: 'auto_milestone_next_error', orderId, error: e.message }); }
|
|
2477
|
+
}, 3000);
|
|
2376
2478
|
}
|
|
2377
2479
|
res.json({ status: 'received', event });
|
|
2378
2480
|
return;
|
|
@@ -2380,8 +2482,22 @@ async function cmdStart(port) {
|
|
|
2380
2482
|
|
|
2381
2483
|
if (event === 'milestone_rejected') {
|
|
2382
2484
|
const { orderId, milestoneIndex, rejectReason } = payload;
|
|
2383
|
-
log({ event: 'milestone_rejected_notification', orderId, milestoneIndex, rejectReason
|
|
2485
|
+
log({ event: 'milestone_rejected_notification', orderId, milestoneIndex, rejectReason });
|
|
2486
|
+
console.log(`\n❌ M${milestoneIndex} rejected for ${orderId}: ${rejectReason}. Improving...`);
|
|
2384
2487
|
res.json({ status: 'received', event });
|
|
2488
|
+
// Auto-resubmit with improvements using agent's own brain (async callback)
|
|
2489
|
+
setTimeout(async () => {
|
|
2490
|
+
try {
|
|
2491
|
+
const ms = await getMilestoneInfo(orderId);
|
|
2492
|
+
if (!ms || !ms.milestones) return;
|
|
2493
|
+
const m = ms.milestones[milestoneIndex];
|
|
2494
|
+
if (!m) return;
|
|
2495
|
+
const orderDesc = ms.description || orderId;
|
|
2496
|
+
const prompt = `You are an AI Agent. Your previous submission was REJECTED.\n\nOrder: ${orderDesc}\nMilestone M${milestoneIndex}: ${m.description || m.title}\nRejection reason: ${rejectReason}\n\nImprove your work based on the feedback. Output ONLY the improved deliverable.`;
|
|
2497
|
+
console.log(`\n🔄 Improving M${milestoneIndex} for ${orderId}...`);
|
|
2498
|
+
await agentExecute(prompt, `milestone-${orderId}-${milestoneIndex}-retry`, { orderId, milestoneIndex, type: 'submit' });
|
|
2499
|
+
} catch (e) { log({ event: 'auto_milestone_resubmit_error', orderId, error: e.message }); }
|
|
2500
|
+
}, 3000);
|
|
2385
2501
|
return;
|
|
2386
2502
|
}
|
|
2387
2503
|
|
|
@@ -2392,6 +2508,68 @@ async function cmdStart(port) {
|
|
|
2392
2508
|
return;
|
|
2393
2509
|
}
|
|
2394
2510
|
|
|
2511
|
+
// Requester: order was accepted by executor (USDC locked) → auto-approve plan
|
|
2512
|
+
if (event === 'order_accepted' && payload.executorDid) {
|
|
2513
|
+
const { orderId, executorDid, escrowTx } = payload;
|
|
2514
|
+
log({ event: 'order_accepted_by_executor', orderId, executorDid, escrowTx });
|
|
2515
|
+
console.log(`\n📋 Order ${orderId} accepted! Auto-approving milestone plan...`);
|
|
2516
|
+
res.json({ status: 'received', event });
|
|
2517
|
+
// Auto-approve milestone plan (non-blocking)
|
|
2518
|
+
setTimeout(async () => {
|
|
2519
|
+
try {
|
|
2520
|
+
for (let wait = 0; wait < 10; wait++) {
|
|
2521
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
2522
|
+
const ms = await getMilestoneInfo(orderId);
|
|
2523
|
+
if (ms && ms.totalMilestones > 0) {
|
|
2524
|
+
const ts = new Date().toISOString();
|
|
2525
|
+
const pl = { approved: true };
|
|
2526
|
+
const sig = sign({ did: id.did, timestamp: ts, payload: pl }, id.secretKey);
|
|
2527
|
+
const fbResp = await fetch(`${PLATFORM_URL}/trade/v1/order/${orderId}/milestones/feedback`, {
|
|
2528
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
2529
|
+
body: JSON.stringify({ did: id.did, timestamp: ts, signature: sig, payload: pl }),
|
|
2530
|
+
signal: AbortSignal.timeout(10000),
|
|
2531
|
+
});
|
|
2532
|
+
log({ event: 'requester_auto_approved_plan', orderId, ok: fbResp.ok });
|
|
2533
|
+
break;
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
} catch (e) { log({ event: 'requester_auto_approve_error', orderId, error: e.message }); }
|
|
2537
|
+
}, 2000);
|
|
2538
|
+
return;
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
// Both parties: milestone plan confirmed, execution started
|
|
2542
|
+
if (event === 'milestone_plan_confirmed') {
|
|
2543
|
+
const { orderId, message } = payload;
|
|
2544
|
+
log({ event: 'milestone_plan_confirmed', orderId, message });
|
|
2545
|
+
console.log(`\n✅ Milestone plan confirmed for ${orderId}. Execution started.`);
|
|
2546
|
+
res.json({ status: 'received', event });
|
|
2547
|
+
// Executor: auto-submit M0 using agent's own brain (non-blocking, async callback)
|
|
2548
|
+
if (message && message.includes('Submit milestone')) {
|
|
2549
|
+
setTimeout(async () => {
|
|
2550
|
+
try {
|
|
2551
|
+
const ms = await getMilestoneInfo(orderId);
|
|
2552
|
+
if (!ms || !ms.milestones || ms.milestones.length === 0) return;
|
|
2553
|
+
const m0 = ms.milestones[0];
|
|
2554
|
+
const orderDesc = ms.description || orderId;
|
|
2555
|
+
const prompt = `You are an AI Agent executing a paid task.\n\nOrder: ${orderDesc}\nMilestone M0: ${m0.description || m0.title}\n\nComplete this milestone. Provide a thorough, detailed deliverable. Output ONLY the deliverable content.`;
|
|
2556
|
+
console.log(`\n🤖 Working on M0 for ${orderId}...`);
|
|
2557
|
+
await agentExecute(prompt, `milestone-${orderId}-0`, { orderId, milestoneIndex: 0, type: 'submit' });
|
|
2558
|
+
} catch (e) { log({ event: 'auto_milestone_0_error', orderId, error: e.message }); }
|
|
2559
|
+
}, 3000);
|
|
2560
|
+
}
|
|
2561
|
+
return;
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
// Both parties: order settled, payment released
|
|
2565
|
+
if (event === 'order_settled') {
|
|
2566
|
+
const { orderId, message } = payload;
|
|
2567
|
+
log({ event: 'order_settled_notification', orderId, message });
|
|
2568
|
+
console.log(`\n💰 Order ${orderId} settled! ${message || 'Check: atel balance'}`);
|
|
2569
|
+
res.json({ status: 'received', event });
|
|
2570
|
+
return;
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2395
2573
|
// Unknown event type
|
|
2396
2574
|
res.json({ status: 'ignored', event });
|
|
2397
2575
|
});
|
|
@@ -2400,6 +2578,8 @@ async function cmdStart(port) {
|
|
|
2400
2578
|
endpoint.app?.post?.('/atel/v1/result', async (req, res) => {
|
|
2401
2579
|
const { taskId, result, success, trace: executorTrace } = req.body || {};
|
|
2402
2580
|
if (!taskId || !pendingTasks[taskId]) { res.status(404).json({ error: 'Unknown taskId' }); return; }
|
|
2581
|
+
|
|
2582
|
+
// Milestone auto-execution removed. Agent handles results via its own AI.
|
|
2403
2583
|
const task = pendingTasks[taskId];
|
|
2404
2584
|
const startTime = new Date(task.acceptedAt).getTime();
|
|
2405
2585
|
const durationMs = Date.now() - startTime;
|
|
@@ -3276,7 +3456,14 @@ async function cmdStart(port) {
|
|
|
3276
3456
|
body: JSON.stringify({ did: id.did }), signal: AbortSignal.timeout(5000),
|
|
3277
3457
|
});
|
|
3278
3458
|
if (!resp.ok) return;
|
|
3279
|
-
const
|
|
3459
|
+
const data = await resp.json();
|
|
3460
|
+
// Relay returns {messages: [{id, sender, message, createdAt}]}
|
|
3461
|
+
// Each message.message contains {method, path, body}
|
|
3462
|
+
const rawMessages = data.messages || data.requests || [];
|
|
3463
|
+
const requests = rawMessages.map(m => {
|
|
3464
|
+
const inner = m.message || m;
|
|
3465
|
+
return typeof inner === 'string' ? JSON.parse(inner) : inner;
|
|
3466
|
+
});
|
|
3280
3467
|
for (const req of requests) {
|
|
3281
3468
|
// Forward to local endpoint
|
|
3282
3469
|
try {
|
|
@@ -3342,14 +3529,14 @@ async function cmdStart(port) {
|
|
|
3342
3529
|
process.on('SIGINT', async () => {
|
|
3343
3530
|
heartbeat.stop();
|
|
3344
3531
|
if (tunnelManager) await tunnelManager.stop();
|
|
3345
|
-
|
|
3532
|
+
|
|
3346
3533
|
await endpoint.stop();
|
|
3347
3534
|
process.exit(0);
|
|
3348
3535
|
});
|
|
3349
3536
|
process.on('SIGTERM', async () => {
|
|
3350
3537
|
heartbeat.stop();
|
|
3351
3538
|
if (tunnelManager) await tunnelManager.stop();
|
|
3352
|
-
|
|
3539
|
+
|
|
3353
3540
|
await endpoint.stop();
|
|
3354
3541
|
process.exit(0);
|
|
3355
3542
|
});
|
|
@@ -5441,14 +5628,32 @@ async function cmdFriendRequest(args) {
|
|
|
5441
5628
|
};
|
|
5442
5629
|
|
|
5443
5630
|
// Sign the request
|
|
5444
|
-
const signedRequest = createMessage(request, id.secretKey);
|
|
5631
|
+
const signedRequest = createMessage({ ...request, secretKey: id.secretKey });
|
|
5445
5632
|
|
|
5446
|
-
// Try to send via P2P
|
|
5633
|
+
// Try to send via P2P — look up registry for endpoint
|
|
5447
5634
|
let sent = false;
|
|
5448
5635
|
let error = null;
|
|
5449
5636
|
|
|
5450
5637
|
try {
|
|
5451
|
-
|
|
5638
|
+
// Resolve endpoint from registry
|
|
5639
|
+
let endpoint = null;
|
|
5640
|
+
try {
|
|
5641
|
+
const resp = await fetch(`${REGISTRY_URL}/registry/v1/agent/${encodeURIComponent(targetDid)}`, { signal: AbortSignal.timeout(5000) });
|
|
5642
|
+
if (resp.ok) {
|
|
5643
|
+
const entry = await resp.json();
|
|
5644
|
+
const candidates = entry.endpoint_candidates || [];
|
|
5645
|
+
const direct = candidates.find(c => c.type === 'direct');
|
|
5646
|
+
const relay = candidates.find(c => c.type === 'relay');
|
|
5647
|
+
endpoint = direct?.url || relay?.url || entry.endpoint;
|
|
5648
|
+
}
|
|
5649
|
+
} catch (e) {
|
|
5650
|
+
throw new Error(`Cannot find agent in registry: ${e.message}`);
|
|
5651
|
+
}
|
|
5652
|
+
if (!endpoint) throw new Error('Agent has no reachable endpoint');
|
|
5653
|
+
|
|
5654
|
+
const hsManager = new HandshakeManager(id);
|
|
5655
|
+
const client = new AgentClient(id);
|
|
5656
|
+
await client.sendTask(endpoint, signedRequest, hsManager);
|
|
5452
5657
|
sent = true;
|
|
5453
5658
|
} catch (err) {
|
|
5454
5659
|
error = err.message;
|
|
@@ -5538,7 +5743,7 @@ async function cmdFriendAccept(args) {
|
|
|
5538
5743
|
}
|
|
5539
5744
|
};
|
|
5540
5745
|
|
|
5541
|
-
const signedMsg = createMessage(acceptMsg, id.secretKey);
|
|
5746
|
+
const signedMsg = createMessage({ ...acceptMsg, secretKey: id.secretKey });
|
|
5542
5747
|
|
|
5543
5748
|
try {
|
|
5544
5749
|
await sendP2PMessage(request.from, signedMsg);
|
|
@@ -5604,7 +5809,7 @@ async function cmdFriendReject(args) {
|
|
|
5604
5809
|
}
|
|
5605
5810
|
};
|
|
5606
5811
|
|
|
5607
|
-
const signedMsg = createMessage(rejectMsg, id.secretKey);
|
|
5812
|
+
const signedMsg = createMessage({ ...rejectMsg, secretKey: id.secretKey });
|
|
5608
5813
|
|
|
5609
5814
|
try {
|
|
5610
5815
|
await sendP2PMessage(request.from, signedMsg);
|
|
@@ -5742,6 +5947,214 @@ async function cmdFriendStatus(args) {
|
|
|
5742
5947
|
console.log('');
|
|
5743
5948
|
}
|
|
5744
5949
|
|
|
5950
|
+
// ─── P2P Send Helper ────────────────────────────────────────────
|
|
5951
|
+
// Resolves DID → endpoint via registry, then sends a signed message.
|
|
5952
|
+
// Replaces the previously undefined sendP2PMessage.
|
|
5953
|
+
async function sendP2PMessage(targetDid, signedMsg) {
|
|
5954
|
+
const id = loadIdentity();
|
|
5955
|
+
if (!id) throw new Error('No identity found');
|
|
5956
|
+
|
|
5957
|
+
let endpoint = null;
|
|
5958
|
+
try {
|
|
5959
|
+
const resp = await fetch(`${REGISTRY_URL}/registry/v1/agent/${encodeURIComponent(targetDid)}`, { signal: AbortSignal.timeout(5000) });
|
|
5960
|
+
if (resp.ok) {
|
|
5961
|
+
const entry = await resp.json();
|
|
5962
|
+
const candidates = entry.endpoint_candidates || [];
|
|
5963
|
+
const direct = candidates.find(c => c.type === 'direct');
|
|
5964
|
+
const relay = candidates.find(c => c.type === 'relay');
|
|
5965
|
+
endpoint = direct?.url || relay?.url || entry.endpoint;
|
|
5966
|
+
}
|
|
5967
|
+
} catch (e) {
|
|
5968
|
+
throw new Error(`Registry lookup failed for ${targetDid}: ${e.message}`);
|
|
5969
|
+
}
|
|
5970
|
+
if (!endpoint) throw new Error(`No reachable endpoint for ${targetDid}`);
|
|
5971
|
+
|
|
5972
|
+
const hsManager = new HandshakeManager(id);
|
|
5973
|
+
const client = new AgentClient(id);
|
|
5974
|
+
return await client.sendTask(endpoint, signedMsg, hsManager);
|
|
5975
|
+
}
|
|
5976
|
+
|
|
5977
|
+
// ─── Send Command (Rich Media P2P Messaging) ─────────────────────
|
|
5978
|
+
|
|
5979
|
+
function showSendHelp() {
|
|
5980
|
+
console.log(`
|
|
5981
|
+
atal send — Send a message (with optional rich media) to another agent
|
|
5982
|
+
|
|
5983
|
+
Usage:
|
|
5984
|
+
atel send <did|alias|endpoint> "message text"
|
|
5985
|
+
atel send <did> "look at this" --image ./photo.jpg
|
|
5986
|
+
atel send <did> "here is the file" --file ./report.pdf
|
|
5987
|
+
atel send <did> "listen" --audio ./voice.mp3
|
|
5988
|
+
atel send <did> "watch this" --video ./clip.mp4
|
|
5989
|
+
|
|
5990
|
+
Flags:
|
|
5991
|
+
--image <path> Attach image (jpg/png/gif/webp, max 10MB, up to 9)
|
|
5992
|
+
--file <path> Attach file (pdf/zip/txt/json, max 100MB, up to 5)
|
|
5993
|
+
--audio <path> Attach audio (mp3/wav/ogg/m4a, max 50MB)
|
|
5994
|
+
--video <path> Attach video (mp4/webm/mov, max 500MB)
|
|
5995
|
+
--json Output result as JSON
|
|
5996
|
+
|
|
5997
|
+
Notes:
|
|
5998
|
+
By default, agents require a mutual friend relationship before accepting
|
|
5999
|
+
P2P messages. If you get a NOT_FRIEND error, run:
|
|
6000
|
+
atel friend request <did>
|
|
6001
|
+
and ask the other side to accept with:
|
|
6002
|
+
atel friend accept <request-id>
|
|
6003
|
+
|
|
6004
|
+
To disable the friend requirement on your own agent, set in policy.json:
|
|
6005
|
+
{ "relationshipPolicy": { "defaultMode": "open" } }
|
|
6006
|
+
|
|
6007
|
+
Examples:
|
|
6008
|
+
atel send did:atel:ed25519:ABC "Hello!"
|
|
6009
|
+
atel send @alice "Check this" --image ./screenshot.png
|
|
6010
|
+
atel send did:atel:ed25519:ABC "Report" --file ./q1.pdf
|
|
6011
|
+
`);
|
|
6012
|
+
}
|
|
6013
|
+
|
|
6014
|
+
async function cmdSend(args) {
|
|
6015
|
+
const target = args._[0];
|
|
6016
|
+
const text = args._[1] || '';
|
|
6017
|
+
const isJson = rawArgs.includes('--json');
|
|
6018
|
+
|
|
6019
|
+
if (!target) { showSendHelp(); process.exit(1); }
|
|
6020
|
+
|
|
6021
|
+
const id = loadIdentity();
|
|
6022
|
+
if (!id) { console.error('No identity found. Run: atel init'); process.exit(1); }
|
|
6023
|
+
|
|
6024
|
+
// Parse attachment flags
|
|
6025
|
+
const attachmentFlags = parseAttachmentFlags(rawArgs);
|
|
6026
|
+
const hasAttachments = attachmentFlags.images.length > 0 ||
|
|
6027
|
+
attachmentFlags.files.length > 0 ||
|
|
6028
|
+
attachmentFlags.audios.length > 0 ||
|
|
6029
|
+
attachmentFlags.videos.length > 0;
|
|
6030
|
+
|
|
6031
|
+
if (!text && !hasAttachments) {
|
|
6032
|
+
console.error('Error: message text or at least one attachment is required');
|
|
6033
|
+
showSendHelp(); process.exit(1);
|
|
6034
|
+
}
|
|
6035
|
+
|
|
6036
|
+
// Build payload
|
|
6037
|
+
const msgId = `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
6038
|
+
const payload = {
|
|
6039
|
+
action: 'general', // broad compatibility with all SDK versions
|
|
6040
|
+
msgType: 'message', // identifies this as a p2p chat message
|
|
6041
|
+
msgId,
|
|
6042
|
+
text,
|
|
6043
|
+
timestamp: new Date().toISOString(),
|
|
6044
|
+
};
|
|
6045
|
+
|
|
6046
|
+
// Upload attachments
|
|
6047
|
+
if (hasAttachments) {
|
|
6048
|
+
try {
|
|
6049
|
+
if (!isJson) process.stderr.write('Uploading attachments...\n');
|
|
6050
|
+
const processed = await processAttachments(attachmentFlags, id.did, msgId);
|
|
6051
|
+
if (processed.images.length > 0) payload.images = processed.images;
|
|
6052
|
+
if (processed.attachments.length > 0) payload.attachments = processed.attachments;
|
|
6053
|
+
if (!isJson) {
|
|
6054
|
+
const total = (processed.images.length + processed.attachments.length);
|
|
6055
|
+
process.stderr.write(` ✓ ${total} attachment(s) ready\n`);
|
|
6056
|
+
}
|
|
6057
|
+
} catch (err) {
|
|
6058
|
+
console.error(`Attachment upload failed: ${err.message}`);
|
|
6059
|
+
process.exit(1);
|
|
6060
|
+
}
|
|
6061
|
+
}
|
|
6062
|
+
|
|
6063
|
+
// Resolve target → endpoint + remoteDid
|
|
6064
|
+
let remoteEndpoint = target;
|
|
6065
|
+
let remoteDid;
|
|
6066
|
+
|
|
6067
|
+
// Resolve @alias
|
|
6068
|
+
try { const r = resolveDID(target); if (r !== target) remoteEndpoint = r; } catch (_) {}
|
|
6069
|
+
|
|
6070
|
+
if (!remoteEndpoint.startsWith('http')) {
|
|
6071
|
+
// Registry lookup
|
|
6072
|
+
let entry;
|
|
6073
|
+
try {
|
|
6074
|
+
const resp = await fetch(`${REGISTRY_URL}/registry/v1/agent/${encodeURIComponent(remoteEndpoint)}`, { signal: AbortSignal.timeout(5000) });
|
|
6075
|
+
if (resp.ok) entry = await resp.json();
|
|
6076
|
+
} catch (_) {}
|
|
6077
|
+
if (!entry) {
|
|
6078
|
+
const regClient = new RegistryClient({ registryUrl: REGISTRY_URL });
|
|
6079
|
+
const results = await regClient.search({ type: remoteEndpoint, limit: 1 });
|
|
6080
|
+
if (results.length > 0) entry = results[0];
|
|
6081
|
+
}
|
|
6082
|
+
if (!entry) { console.error(`Agent not found: ${target}`); process.exit(1); }
|
|
6083
|
+
remoteDid = entry.did;
|
|
6084
|
+
const candidates = entry.endpoint_candidates || [];
|
|
6085
|
+
const direct = candidates.find(c => c.type === 'direct');
|
|
6086
|
+
const relay = candidates.find(c => c.type === 'relay');
|
|
6087
|
+
remoteEndpoint = direct?.url || relay?.url || entry.endpoint;
|
|
6088
|
+
} else {
|
|
6089
|
+
remoteDid = target.startsWith('did:') ? target : undefined;
|
|
6090
|
+
// Try to get remoteDid from handshake cache for direct endpoints
|
|
6091
|
+
if (!remoteDid) {
|
|
6092
|
+
try {
|
|
6093
|
+
const tmpHs = new HandshakeManager(id);
|
|
6094
|
+
const session = await tmpHs.getOrCreate(remoteEndpoint);
|
|
6095
|
+
if (session?.remoteDid) remoteDid = session.remoteDid;
|
|
6096
|
+
} catch (_) {}
|
|
6097
|
+
}
|
|
6098
|
+
}
|
|
6099
|
+
|
|
6100
|
+
if (!remoteEndpoint) {
|
|
6101
|
+
console.error(`No reachable endpoint for: ${target}`);
|
|
6102
|
+
process.exit(1);
|
|
6103
|
+
}
|
|
6104
|
+
|
|
6105
|
+
// Send
|
|
6106
|
+
try {
|
|
6107
|
+
const hsManager = new HandshakeManager(id);
|
|
6108
|
+
const client = new AgentClient(id);
|
|
6109
|
+
const msg = createMessage({ type: 'task', from: id.did, to: remoteDid || remoteEndpoint, payload, secretKey: id.secretKey });
|
|
6110
|
+
const result = await client.sendTask(remoteEndpoint, msg, hsManager);
|
|
6111
|
+
|
|
6112
|
+
// Surface friendly errors
|
|
6113
|
+
const inner = result?.result;
|
|
6114
|
+
if (inner?.code === 'NOT_FRIEND' || inner?.error?.includes('NOT_FRIEND') || inner?.error?.includes('not a friend')) {
|
|
6115
|
+
if (isJson) {
|
|
6116
|
+
console.log(JSON.stringify({ status: 'error', code: 'NOT_FRIEND', message: inner.error, hint: `Run: atel friend request ${remoteDid || target}` }));
|
|
6117
|
+
} else {
|
|
6118
|
+
console.error(`✗ Message blocked: the recipient requires a friend relationship.`);
|
|
6119
|
+
console.error(` Run: atel friend request ${remoteDid || target}`);
|
|
6120
|
+
console.error(` Then ask them to: atel friend accept <request-id>`);
|
|
6121
|
+
}
|
|
6122
|
+
process.exit(1);
|
|
6123
|
+
}
|
|
6124
|
+
if (inner?.status === 'rejected') {
|
|
6125
|
+
if (isJson) {
|
|
6126
|
+
console.log(JSON.stringify({ status: 'error', message: inner.error || 'Message rejected', result }));
|
|
6127
|
+
} else {
|
|
6128
|
+
console.error(`✗ Message rejected: ${inner.error || 'unknown reason'}`);
|
|
6129
|
+
}
|
|
6130
|
+
process.exit(1);
|
|
6131
|
+
}
|
|
6132
|
+
|
|
6133
|
+
if (isJson) {
|
|
6134
|
+
console.log(JSON.stringify({ status: 'sent', msgId, to: remoteDid || remoteEndpoint, attachments: (payload.images?.length || 0) + (payload.attachments?.length || 0), result }, null, 2));
|
|
6135
|
+
} else {
|
|
6136
|
+
console.log(`✓ Message sent to ${remoteDid || remoteEndpoint}`);
|
|
6137
|
+
if (payload.images?.length) console.log(` Images: ${payload.images.length}`);
|
|
6138
|
+
if (payload.attachments?.length) console.log(` Attachments: ${payload.attachments.length}`);
|
|
6139
|
+
}
|
|
6140
|
+
} catch (err) {
|
|
6141
|
+
if (isJson) {
|
|
6142
|
+
console.log(JSON.stringify({ status: 'error', message: err.message }));
|
|
6143
|
+
} else {
|
|
6144
|
+
// Surface specific known errors in human-readable form
|
|
6145
|
+
if (err.message?.includes('fetch failed') || err.message?.includes('ECONNREFUSED')) {
|
|
6146
|
+
console.error(`✗ Cannot reach agent at ${remoteEndpoint}`);
|
|
6147
|
+
console.error(` The agent may be offline or behind a firewall.`);
|
|
6148
|
+
} else if (err.message?.includes('401') || err.message?.includes('Unauthorized')) {
|
|
6149
|
+
console.error(`✗ Authentication failed — try: atel handshake ${remoteEndpoint}`);
|
|
6150
|
+
} else {
|
|
6151
|
+
console.error(`✗ Send failed: ${err.message}`);
|
|
6152
|
+
}
|
|
6153
|
+
}
|
|
6154
|
+
process.exit(1);
|
|
6155
|
+
}
|
|
6156
|
+
}
|
|
6157
|
+
|
|
5745
6158
|
// ─── Temporary Session Commands ──────────────────────────────────
|
|
5746
6159
|
|
|
5747
6160
|
async function cmdTempAllow(args) {
|
|
@@ -6212,6 +6625,14 @@ const commands = {
|
|
|
6212
6625
|
console.error(' status [--json]');
|
|
6213
6626
|
process.exit(1);
|
|
6214
6627
|
},
|
|
6628
|
+
// Send (Rich Media P2P Message)
|
|
6629
|
+
send: () => {
|
|
6630
|
+
if (rawArgs.includes('--help') || rawArgs.includes('-h') || args.length === 0) {
|
|
6631
|
+
showSendHelp();
|
|
6632
|
+
process.exit(0);
|
|
6633
|
+
}
|
|
6634
|
+
return cmdSend({ _: args, json: rawArgs.includes('--json') });
|
|
6635
|
+
},
|
|
6215
6636
|
// Alias System
|
|
6216
6637
|
alias: () => {
|
|
6217
6638
|
const subCmd = args[0];
|
|
@@ -6248,6 +6669,7 @@ Protocol Commands:
|
|
|
6248
6669
|
register [name] [caps] [endpoint] Register on public registry (caps: "type1:price1,type2:price2" or "type1,type2" for free)
|
|
6249
6670
|
search <capability> Search registry for agents (shows pricing info)
|
|
6250
6671
|
handshake <endpoint> [did] Handshake with remote agent
|
|
6672
|
+
send <target> "text" [--image/--file/--audio/--video <path>] Send P2P message with optional rich media
|
|
6251
6673
|
task <target> <json> Delegate task (auto trust check)
|
|
6252
6674
|
result <taskId> <json> Submit execution result (from executor)
|
|
6253
6675
|
check <did> [risk] Check agent trust (risk: low|medium|high|critical)
|