2020117-agent 0.4.4 → 0.4.5

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/cashu.d.ts CHANGED
@@ -45,6 +45,22 @@ export declare function decodeCashuToken(tokenStr: string): {
45
45
  * Encode proofs back into a portable token string.
46
46
  */
47
47
  export declare function encodeCashuToken(mintUrl: string, proofs: Proof[]): string;
48
+ /**
49
+ * Melt proofs back into a Lightning invoice — converts Cashu back to Lightning.
50
+ * Returns the payment preimage and any change proofs.
51
+ */
52
+ export declare function meltProofs(mintUrl: string, proofs: Proof[], invoice: string): Promise<{
53
+ preimage: string;
54
+ change: Proof[];
55
+ }>;
56
+ /**
57
+ * Estimate total cost to melt (invoice amount + fee_reserve) for a given invoice.
58
+ */
59
+ export declare function estimateMeltFee(mintUrl: string, invoice: string): Promise<{
60
+ amount: number;
61
+ fee: number;
62
+ total: number;
63
+ }>;
48
64
  /**
49
65
  * Request a mint quote — returns a Lightning invoice to pay for minting tokens.
50
66
  */
package/dist/cashu.js CHANGED
@@ -60,6 +60,31 @@ export function decodeCashuToken(tokenStr) {
60
60
  export function encodeCashuToken(mintUrl, proofs) {
61
61
  return getEncodedTokenV4({ mint: mintUrl, proofs });
62
62
  }
63
+ /**
64
+ * Melt proofs back into a Lightning invoice — converts Cashu back to Lightning.
65
+ * Returns the payment preimage and any change proofs.
66
+ */
67
+ export async function meltProofs(mintUrl, proofs, invoice) {
68
+ const wallet = await getWallet(mintUrl);
69
+ const meltQuote = await wallet.createMeltQuote(invoice);
70
+ const amountNeeded = meltQuote.amount + meltQuote.fee_reserve;
71
+ const total = proofs.reduce((s, p) => s + p.amount, 0);
72
+ if (total < amountNeeded) {
73
+ throw new Error(`Need ${amountNeeded} sats (invoice ${meltQuote.amount} + fee ${meltQuote.fee_reserve}) but only have ${total}`);
74
+ }
75
+ const { send, keep } = await wallet.send(amountNeeded, proofs, { includeFees: true });
76
+ const result = await wallet.meltProofs(meltQuote, send);
77
+ const change = [...keep, ...(result.change || [])];
78
+ return { preimage: result.quote.payment_preimage || '', change };
79
+ }
80
+ /**
81
+ * Estimate total cost to melt (invoice amount + fee_reserve) for a given invoice.
82
+ */
83
+ export async function estimateMeltFee(mintUrl, invoice) {
84
+ const wallet = await getWallet(mintUrl);
85
+ const quote = await wallet.createMeltQuote(invoice);
86
+ return { amount: quote.amount, fee: quote.fee_reserve, total: quote.amount + quote.fee_reserve };
87
+ }
63
88
  /**
64
89
  * Request a mint quote — returns a Lightning invoice to pay for minting tokens.
65
90
  */
package/dist/session.js CHANGED
@@ -51,8 +51,8 @@ for (const arg of process.argv.slice(2)) {
51
51
  import { SwarmNode, topicFromKind } from './swarm.js';
52
52
  import { queryProviderSkill } from './p2p-customer.js';
53
53
  import { walletPayInvoice, walletGetBalance, hasApiKey } from './api.js';
54
- import { decodeCashuToken, sendCashuToken, createMintQuote, claimMintQuote } from './cashu.js';
55
- import { parseNwcUri, nwcGetBalance, nwcPayInvoice } from './nwc.js';
54
+ import { decodeCashuToken, sendCashuToken, createMintQuote, claimMintQuote, meltProofs, estimateMeltFee } from './cashu.js';
55
+ import { parseNwcUri, nwcGetBalance, nwcPayInvoice, nwcMakeInvoice } from './nwc.js';
56
56
  import { loadSovereignKeys } from './nostr.js';
57
57
  import { randomBytes } from 'crypto';
58
58
  import { createServer } from 'http';
@@ -694,6 +694,32 @@ async function endSession() {
694
694
  }
695
695
  log(`Session ended. Total: ${state.totalSpent} sats for ${duration}s.`);
696
696
  }
697
+ // Refund remaining Cashu proofs back to NWC wallet
698
+ if (cashuState && cashuState.proofs.length > 0 && nwcParsed) {
699
+ const remaining = cashuState.proofs.reduce((s, p) => s + p.amount, 0);
700
+ if (remaining >= 3) {
701
+ log(`Refunding ${remaining} sats Cashu → NWC wallet...`);
702
+ try {
703
+ // Probe: create 1-sat invoice to discover melt fee_reserve + swap fee
704
+ const probe = await nwcMakeInvoice(nwcParsed, 1000, 'fee-probe');
705
+ const { fee } = await estimateMeltFee(cashuState.mintUrl, probe.bolt11);
706
+ // fee = melt fee_reserve; +1 for Cashu internal swap fee
707
+ const refundAmount = remaining - fee - 1;
708
+ if (refundAmount < 1) {
709
+ warn(`Remaining ${remaining} sats < melt fee (${fee} sats), cannot refund`);
710
+ }
711
+ else {
712
+ const { bolt11 } = await nwcMakeInvoice(nwcParsed, refundAmount * 1000, '2020117-session refund');
713
+ const { preimage } = await meltProofs(cashuState.mintUrl, cashuState.proofs, bolt11);
714
+ cashuState.proofs = [];
715
+ log(`Refunded ${refundAmount} sats (fee: ${fee}, preimage: ${preimage?.slice(0, 16)}...)`);
716
+ }
717
+ }
718
+ catch (e) {
719
+ warn(`Refund failed: ${e.message} — ${remaining} sats lost`);
720
+ }
721
+ }
722
+ }
697
723
  // Close HTTP proxy
698
724
  if (state.httpServer) {
699
725
  state.httpServer.close();
@@ -774,36 +800,20 @@ async function main() {
774
800
  }
775
801
  }
776
802
  else if (nwcUri) {
777
- // NWC direct: auto-mint Cashu tokens via local NWC wallet (no platform API)
803
+ // NWC direct: pay provider invoices via Lightning no Cashu needed
778
804
  nwcParsed = parseNwcUri(nwcUri);
779
805
  const { balance_msats } = await nwcGetBalance(nwcParsed);
780
806
  const balance = Math.floor(balance_msats / 1000);
781
807
  if (balance <= 0) {
782
- warn('NWC wallet balance is 0. Cannot auto-mint Cashu tokens.');
808
+ warn('NWC wallet balance is 0.');
783
809
  await node.destroy();
784
810
  process.exit(1);
785
811
  }
786
- const mintAmount = Math.min(balance, BUDGET);
787
- log(`NWC wallet balance: ${balance} sats minting ${mintAmount} sats from ${MINT_URL}`);
788
- try {
789
- const { quote, invoice } = await createMintQuote(MINT_URL, mintAmount);
790
- log(`Mint quote: ${quote} (invoice: ${invoice.slice(0, 30)}...)`);
791
- log('Paying mint invoice via NWC...');
792
- const { preimage } = await nwcPayInvoice(nwcParsed, invoice);
793
- log(`Invoice paid (preimage: ${preimage?.slice(0, 16)}...)`);
794
- log('Claiming minted tokens...');
795
- const token = await claimMintQuote(MINT_URL, mintAmount, quote);
796
- const { mint, proofs } = decodeCashuToken(token);
797
- cashuState = { mintUrl: mint, proofs };
798
- paymentMethod = 'cashu';
799
- const totalMinted = proofs.reduce((s, p) => s + p.amount, 0);
800
- log(`Minted ${totalMinted} sats Cashu token — using Cashu payment mode`);
801
- }
802
- catch (e) {
803
- warn(`NWC auto-mint failed: ${e.message}`);
804
- await node.destroy();
805
- process.exit(1);
812
+ if (balance < BUDGET) {
813
+ warn(`NWC wallet balance (${balance} sats) < budget (${BUDGET} sats)`);
806
814
  }
815
+ paymentMethod = 'invoice';
816
+ log(`Payment: NWC direct (balance: ${balance} sats, pay-per-tick via Lightning)`);
807
817
  }
808
818
  else if (hasApiKey()) {
809
819
  // Platform API fallback: auto-mint Cashu tokens via platform wallet proxy
@@ -867,6 +877,33 @@ async function main() {
867
877
  state.satsPerMinute = ackResp.sats_per_minute;
868
878
  log(`Provider adjusted rate: ${ackResp.sats_per_minute} sats/min`);
869
879
  }
880
+ // Provider may override payment method (e.g. no Lightning Address → force cashu)
881
+ if (ackResp.payment_method && ackResp.payment_method !== paymentMethod) {
882
+ if (ackResp.payment_method === 'cashu' && paymentMethod === 'invoice' && nwcParsed) {
883
+ // Provider doesn't support invoice — fall back to NWC-minted Cashu
884
+ log(`Provider requires Cashu — minting via NWC...`);
885
+ try {
886
+ const { balance_msats } = await nwcGetBalance(nwcParsed);
887
+ const balance = Math.floor(balance_msats / 1000);
888
+ const mintAmount = Math.min(balance, BUDGET);
889
+ const { quote, invoice } = await createMintQuote(MINT_URL, mintAmount);
890
+ const { preimage } = await nwcPayInvoice(nwcParsed, invoice);
891
+ const token = await claimMintQuote(MINT_URL, mintAmount, quote);
892
+ const { mint, proofs } = decodeCashuToken(token);
893
+ cashuState = { mintUrl: mint, proofs };
894
+ paymentMethod = 'cashu';
895
+ log(`Minted ${proofs.reduce((s, p) => s + p.amount, 0)} sats Cashu — fallback ready`);
896
+ }
897
+ catch (e) {
898
+ warn(`Cashu fallback mint failed: ${e.message}`);
899
+ await node.destroy();
900
+ process.exit(1);
901
+ }
902
+ }
903
+ else {
904
+ paymentMethod = ackResp.payment_method;
905
+ }
906
+ }
870
907
  log(`Session started: ${state.sessionId}`);
871
908
  log(`Billing: ${state.satsPerMinute} sats/min via ${paymentMethod}`);
872
909
  // 6. Start HTTP proxy
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "2020117-agent",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "2020117 agent runtime — API polling + Hyperswarm P2P + Sovereign Nostr mode + Cashu/Lightning payments",
5
5
  "type": "module",
6
6
  "bin": {