2020117-agent 0.4.5 → 0.4.7
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/agent.js +55 -5
- package/dist/session.js +38 -2
- package/dist/swarm.d.ts +1 -0
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -285,15 +285,17 @@ async function setupSovereign(label) {
|
|
|
285
285
|
state.relayPool = new RelayPool(relayUrls);
|
|
286
286
|
await state.relayPool.connect();
|
|
287
287
|
console.log(`[${label}] Connected to ${state.relayPool.connectedCount} relay(s)`);
|
|
288
|
-
// 4. Publish
|
|
288
|
+
// 4. Publish profile (Kind 0) — name, about, lud16
|
|
289
|
+
await publishProfile(label);
|
|
290
|
+
// 5. Publish ai.info (Kind 31340) — NIP-XX capability advertisement
|
|
289
291
|
await publishAiInfo(label);
|
|
290
|
-
//
|
|
292
|
+
// 6. Publish handler info (Kind 31990) — NIP-89 DVM capability
|
|
291
293
|
await publishHandlerInfo(label);
|
|
292
|
-
//
|
|
294
|
+
// 7. Subscribe to NIP-XX prompts (Kind 25802)
|
|
293
295
|
subscribeNipXX(label);
|
|
294
|
-
//
|
|
296
|
+
// 8. Subscribe to DVM requests (Kind 5xxx) directly from relay
|
|
295
297
|
subscribeDvmRequests(label);
|
|
296
|
-
//
|
|
298
|
+
// 9. Start sovereign heartbeat (Kind 30333 to relay)
|
|
297
299
|
startSovereignHeartbeat(label);
|
|
298
300
|
}
|
|
299
301
|
async function publishAiInfo(label) {
|
|
@@ -324,6 +326,26 @@ async function publishAiInfo(label) {
|
|
|
324
326
|
const ok = await state.relayPool.publish(event);
|
|
325
327
|
console.log(`[${label}] Published ai.info (Kind 31340): ${ok ? 'ok' : 'failed'}`);
|
|
326
328
|
}
|
|
329
|
+
async function publishProfile(label) {
|
|
330
|
+
if (!state.sovereignKeys || !state.relayPool)
|
|
331
|
+
return;
|
|
332
|
+
const agentName = state.agentName || 'sovereign-agent';
|
|
333
|
+
const content = {
|
|
334
|
+
name: agentName,
|
|
335
|
+
about: state.skill?.description || `DVM agent (kind ${KIND})`,
|
|
336
|
+
picture: `https://robohash.org/${encodeURIComponent(agentName)}`,
|
|
337
|
+
};
|
|
338
|
+
if (LIGHTNING_ADDRESS) {
|
|
339
|
+
content.lud16 = LIGHTNING_ADDRESS;
|
|
340
|
+
}
|
|
341
|
+
const event = signEvent({
|
|
342
|
+
kind: 0,
|
|
343
|
+
tags: [],
|
|
344
|
+
content: JSON.stringify(content),
|
|
345
|
+
}, state.sovereignKeys.privkey);
|
|
346
|
+
const ok = await state.relayPool.publish(event);
|
|
347
|
+
console.log(`[${label}] Published profile (Kind 0): ${ok ? 'ok' : 'failed'}`);
|
|
348
|
+
}
|
|
327
349
|
async function publishHandlerInfo(label) {
|
|
328
350
|
if (!state.sovereignKeys || !state.relayPool)
|
|
329
351
|
return;
|
|
@@ -743,6 +765,7 @@ async function startSwarmListener(label) {
|
|
|
743
765
|
lastPaidAt: Date.now(),
|
|
744
766
|
billingTimer: null,
|
|
745
767
|
timeoutTimer: null,
|
|
768
|
+
customerPubkey: msg.pubkey || undefined,
|
|
746
769
|
};
|
|
747
770
|
activeSessions.set(sessionId, session);
|
|
748
771
|
node.send(socket, {
|
|
@@ -751,6 +774,7 @@ async function startSwarmListener(label) {
|
|
|
751
774
|
session_id: sessionId,
|
|
752
775
|
sats_per_minute: satsPerMinute,
|
|
753
776
|
payment_method: paymentMethod,
|
|
777
|
+
pubkey: state.sovereignKeys?.pubkey,
|
|
754
778
|
});
|
|
755
779
|
// Send first billing tick
|
|
756
780
|
await sendBillingTick(node, session, billingAmount, label);
|
|
@@ -1035,6 +1059,32 @@ function endSession(node, session, label) {
|
|
|
1035
1059
|
// Socket may already be closed (peer disconnect)
|
|
1036
1060
|
}
|
|
1037
1061
|
console.log(`[${label}] Session ${session.sessionId} ended: ${session.totalEarned} sats, ${durationS}s`);
|
|
1062
|
+
// Publish Kind 30311 endorsement for customer (best-effort)
|
|
1063
|
+
if (state.sovereignKeys && state.relayPool && session.customerPubkey) {
|
|
1064
|
+
try {
|
|
1065
|
+
const endorsement = signEvent({
|
|
1066
|
+
kind: 30311,
|
|
1067
|
+
tags: [
|
|
1068
|
+
['d', session.customerPubkey],
|
|
1069
|
+
['p', session.customerPubkey],
|
|
1070
|
+
['rating', '5'],
|
|
1071
|
+
['k', String(KIND)],
|
|
1072
|
+
],
|
|
1073
|
+
content: JSON.stringify({
|
|
1074
|
+
rating: 5,
|
|
1075
|
+
context: {
|
|
1076
|
+
session_duration_s: durationS,
|
|
1077
|
+
total_sats: session.totalEarned,
|
|
1078
|
+
kinds: [KIND],
|
|
1079
|
+
last_job_at: Math.floor(Date.now() / 1000),
|
|
1080
|
+
},
|
|
1081
|
+
}),
|
|
1082
|
+
}, state.sovereignKeys.privkey);
|
|
1083
|
+
state.relayPool.publish(endorsement).catch(() => { });
|
|
1084
|
+
console.log(`[${label}] Published endorsement for customer ${session.customerPubkey.slice(0, 8)}`);
|
|
1085
|
+
}
|
|
1086
|
+
catch { }
|
|
1087
|
+
}
|
|
1038
1088
|
// Update P2P lifetime counters
|
|
1039
1089
|
state.p2pSessionsCompleted++;
|
|
1040
1090
|
state.p2pTotalEarnedSats += session.totalEarned;
|
package/dist/session.js
CHANGED
|
@@ -53,7 +53,7 @@ import { queryProviderSkill } from './p2p-customer.js';
|
|
|
53
53
|
import { walletPayInvoice, walletGetBalance, hasApiKey } from './api.js';
|
|
54
54
|
import { decodeCashuToken, sendCashuToken, createMintQuote, claimMintQuote, meltProofs, estimateMeltFee } from './cashu.js';
|
|
55
55
|
import { parseNwcUri, nwcGetBalance, nwcPayInvoice, nwcMakeInvoice } from './nwc.js';
|
|
56
|
-
import { loadSovereignKeys } from './nostr.js';
|
|
56
|
+
import { loadSovereignKeys, signEvent, RelayPool } from './nostr.js';
|
|
57
57
|
import { randomBytes } from 'crypto';
|
|
58
58
|
import { createServer } from 'http';
|
|
59
59
|
import { createInterface } from 'readline';
|
|
@@ -65,11 +65,15 @@ const BUDGET = Number(process.env.BUDGET_SATS) || 500;
|
|
|
65
65
|
const PORT = Number(process.env.SESSION_PORT) || 8080;
|
|
66
66
|
const CASHU_TOKEN = process.env.CASHU_TOKEN || '';
|
|
67
67
|
const MINT_URL = process.env.CASHU_MINT_URL || 'https://8333.space:3338';
|
|
68
|
+
// Load sovereign keys (for pubkey exchange + endorsement publishing)
|
|
69
|
+
const sovereignKeys = loadSovereignKeys(process.env.AGENT);
|
|
68
70
|
// Load NWC URI: --nwc flag > .2020117_keys nwc_uri > none
|
|
69
71
|
let nwcParsed = null;
|
|
70
72
|
const nwcUri = process.env.NWC_URI
|
|
71
|
-
||
|
|
73
|
+
|| sovereignKeys?.nwc_uri
|
|
72
74
|
|| null;
|
|
75
|
+
// Track provider's Nostr pubkey (received in session_ack)
|
|
76
|
+
let providerPubkey = null;
|
|
73
77
|
// Mutable Cashu wallet state (loaded from CASHU_TOKEN at startup)
|
|
74
78
|
let cashuState = null;
|
|
75
79
|
const TICK_INTERVAL_MS = 60_000;
|
|
@@ -694,6 +698,36 @@ async function endSession() {
|
|
|
694
698
|
}
|
|
695
699
|
log(`Session ended. Total: ${state.totalSpent} sats for ${duration}s.`);
|
|
696
700
|
}
|
|
701
|
+
// Publish Kind 30311 endorsement for provider (best-effort)
|
|
702
|
+
if (sovereignKeys?.privkey && providerPubkey) {
|
|
703
|
+
try {
|
|
704
|
+
const endorsement = signEvent({
|
|
705
|
+
kind: 30311,
|
|
706
|
+
tags: [
|
|
707
|
+
['d', providerPubkey],
|
|
708
|
+
['p', providerPubkey],
|
|
709
|
+
['rating', '5'],
|
|
710
|
+
['k', String(KIND)],
|
|
711
|
+
],
|
|
712
|
+
content: JSON.stringify({
|
|
713
|
+
rating: 5,
|
|
714
|
+
context: {
|
|
715
|
+
session_duration_s: elapsedSeconds(),
|
|
716
|
+
total_sats: state.totalSpent,
|
|
717
|
+
kinds: [KIND],
|
|
718
|
+
last_job_at: Math.floor(Date.now() / 1000),
|
|
719
|
+
},
|
|
720
|
+
}),
|
|
721
|
+
}, sovereignKeys.privkey);
|
|
722
|
+
const relayUrls = sovereignKeys.relays?.length ? sovereignKeys.relays : ['wss://relay.2020117.xyz'];
|
|
723
|
+
const relay = new RelayPool(relayUrls);
|
|
724
|
+
await relay.connect();
|
|
725
|
+
await relay.publish(endorsement);
|
|
726
|
+
await relay.close();
|
|
727
|
+
log(`Published endorsement for provider ${providerPubkey.slice(0, 8)}`);
|
|
728
|
+
}
|
|
729
|
+
catch { }
|
|
730
|
+
}
|
|
697
731
|
// Refund remaining Cashu proofs back to NWC wallet
|
|
698
732
|
if (cashuState && cashuState.proofs.length > 0 && nwcParsed) {
|
|
699
733
|
const remaining = cashuState.proofs.reduce((s, p) => s + p.amount, 0);
|
|
@@ -864,6 +898,7 @@ async function main() {
|
|
|
864
898
|
budget: BUDGET,
|
|
865
899
|
sats_per_minute: satsPerMinute,
|
|
866
900
|
payment_method: paymentMethod,
|
|
901
|
+
pubkey: sovereignKeys?.pubkey,
|
|
867
902
|
}, 15_000);
|
|
868
903
|
if (ackResp.type !== 'session_ack' || !ackResp.session_id) {
|
|
869
904
|
warn(`Unexpected response: ${ackResp.type}`);
|
|
@@ -872,6 +907,7 @@ async function main() {
|
|
|
872
907
|
}
|
|
873
908
|
state.sessionId = ackResp.session_id;
|
|
874
909
|
state.startedAt = Date.now();
|
|
910
|
+
providerPubkey = ackResp.pubkey || null;
|
|
875
911
|
// If the provider dictated a different rate, use it
|
|
876
912
|
if (ackResp.sats_per_minute && ackResp.sats_per_minute !== satsPerMinute) {
|
|
877
913
|
state.satsPerMinute = ackResp.sats_per_minute;
|
package/dist/swarm.d.ts
CHANGED