2020117-agent 0.3.0 → 0.3.1

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 CHANGED
@@ -68,7 +68,7 @@ for (const arg of process.argv.slice(2)) {
68
68
  import { randomBytes } from 'crypto';
69
69
  import { SwarmNode, topicFromKind } from './swarm.js';
70
70
  import { createProcessor } from './processor.js';
71
- import { hasApiKey, loadAgentName, registerService, startHeartbeatLoop, getInbox, acceptJob, sendFeedback, submitResult, createJob, getJob, getProfile, } from './api.js';
71
+ import { hasApiKey, loadAgentName, registerService, startHeartbeatLoop, getInbox, acceptJob, sendFeedback, submitResult, createJob, getJob, getProfile, reportSession, } from './api.js';
72
72
  import { initClinkAgent, collectPayment } from './clink.js';
73
73
  import { readFileSync } from 'fs';
74
74
  import WebSocket from 'ws';
@@ -669,6 +669,10 @@ function endSession(node, session, label) {
669
669
  // Update P2P lifetime counters — no batch claim needed with CLINK (payments settled instantly)
670
670
  state.p2pSessionsCompleted++;
671
671
  state.p2pTotalEarnedSats += session.totalEarned;
672
+ // Report session to platform activity feed (best-effort, no content exposed)
673
+ if (hasApiKey()) {
674
+ reportSession({ kind: KIND, durationS, totalSats: session.totalEarned });
675
+ }
672
676
  activeSessions.delete(session.sessionId);
673
677
  }
674
678
  // --- 5. Graceful shutdown ---
package/dist/api.d.ts CHANGED
@@ -100,3 +100,12 @@ export declare function proxyDebit(opts: {
100
100
  lightningAddress: string;
101
101
  amountSats: number;
102
102
  }): Promise<ProxyDebitResult | null>;
103
+ /**
104
+ * Report a completed P2P session to the platform for the activity feed.
105
+ * Content stays private — only metadata (kind, duration, sats) is sent.
106
+ */
107
+ export declare function reportSession(opts: {
108
+ kind: number;
109
+ durationS: number;
110
+ totalSats: number;
111
+ }): Promise<void>;
package/dist/api.js CHANGED
@@ -282,3 +282,19 @@ export async function proxyDebit(opts) {
282
282
  amount_sats: opts.amountSats,
283
283
  });
284
284
  }
285
+ /**
286
+ * Report a completed P2P session to the platform for the activity feed.
287
+ * Content stays private — only metadata (kind, duration, sats) is sent.
288
+ */
289
+ export async function reportSession(opts) {
290
+ try {
291
+ await apiPost('/api/dvm/session-report', {
292
+ kind: opts.kind,
293
+ duration_s: opts.durationS,
294
+ total_sats: opts.totalSats,
295
+ });
296
+ }
297
+ catch {
298
+ // Best-effort — don't break session cleanup
299
+ }
300
+ }
package/dist/clink.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * CLINK payment utilities — replaces Cashu for P2P payments
2
+ * CLINK payment utilities — debit-based Lightning payments for P2P sessions
3
3
  *
4
4
  * Provider uses ndebit to pull payments from customer's wallet.
5
5
  * Invoice generation via LNURL-pay from provider's own Lightning Address.
package/dist/clink.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * CLINK payment utilities — replaces Cashu for P2P payments
2
+ * CLINK payment utilities — debit-based Lightning payments for P2P sessions
3
3
  *
4
4
  * Provider uses ndebit to pull payments from customer's wallet.
5
5
  * Invoice generation via LNURL-pay from provider's own Lightning Address.
@@ -13,7 +13,7 @@
13
13
  */
14
14
  import { SwarmNode } from './swarm.js';
15
15
  /**
16
- * Configuration for a P2P streaming session.
16
+ * Configuration for a P2P connection.
17
17
  */
18
18
  export interface P2PStreamOptions {
19
19
  /** DVM kind number (e.g. 5100 for text generation) */
@@ -35,7 +35,7 @@ import { randomBytes } from 'crypto';
35
35
  export async function* streamFromProvider(opts) {
36
36
  const { kind, input, budgetSats, ndebit, maxSatsPerChunk = 5, timeoutMs = 120_000, label = 'p2p', params, } = opts;
37
37
  if (!ndebit) {
38
- throw new Error('ndebit authorization required for P2P streaming');
38
+ throw new Error('ndebit authorization required for P2P connection');
39
39
  }
40
40
  const jobId = randomBytes(8).toString('hex');
41
41
  const tag = `${label}-${jobId.slice(0, 8)}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "2020117-agent",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "2020117 agent runtime — API polling + Hyperswarm P2P + CLINK Lightning payments",
5
5
  "type": "module",
6
6
  "bin": {
package/dist/cashu.d.ts DELETED
@@ -1,41 +0,0 @@
1
- /**
2
- * Cashu eCash helper — mint, send, receive, melt tokens
3
- *
4
- * Uses testnut.cashu.space (fake sats) for PoC.
5
- * Switch to mint.coinos.io for production.
6
- */
7
- import { Mint, Wallet } from '@cashu/cashu-ts';
8
- export declare function createWallet(mintUrl?: string): {
9
- mint: Mint;
10
- wallet: Wallet;
11
- };
12
- /**
13
- * Mint new tokens (testnut mints fake tokens without real Lightning payment)
14
- */
15
- export declare function mintTokens(amount: number, mintUrl?: string): Promise<{
16
- token: string;
17
- proofs: import("@cashu/cashu-ts").Proof[];
18
- }>;
19
- /**
20
- * Verify and receive a Cashu token — swaps proofs with the mint to prevent double-spend
21
- */
22
- export declare function receiveToken(tokenStr: string): Promise<{
23
- proofs: import("@cashu/cashu-ts").Proof[];
24
- amount: number;
25
- mintUrl: string;
26
- }>;
27
- /**
28
- * Get the total amount of a token without claiming it
29
- */
30
- export declare function peekToken(tokenStr: string): {
31
- amount: number;
32
- mint: string;
33
- proofCount: number;
34
- };
35
- /**
36
- * Split a large token into multiple micro-tokens of a given amount.
37
- * Used for streaming payments: customer pre-splits budget into per-payment chunks.
38
- *
39
- * Example: splitTokens(token_100sats, 10) → 10 tokens of 10 sats each
40
- */
41
- export declare function splitTokens(tokenStr: string, perAmount: number): Promise<string[]>;
package/dist/cashu.js DELETED
@@ -1,112 +0,0 @@
1
- /**
2
- * Cashu eCash helper — mint, send, receive, melt tokens
3
- *
4
- * Uses testnut.cashu.space (fake sats) for PoC.
5
- * Switch to mint.coinos.io for production.
6
- */
7
- import { Mint, Wallet, getEncodedToken, getDecodedToken, MintQuoteState } from '@cashu/cashu-ts';
8
- const DEFAULT_MINT_URL = process.env.CASHU_MINT_URL || 'https://nofee.testnut.cashu.space';
9
- export function createWallet(mintUrl = DEFAULT_MINT_URL) {
10
- const mint = new Mint(mintUrl);
11
- const wallet = new Wallet(mint);
12
- return { mint, wallet };
13
- }
14
- /**
15
- * Mint new tokens (testnut mints fake tokens without real Lightning payment)
16
- */
17
- export async function mintTokens(amount, mintUrl = DEFAULT_MINT_URL) {
18
- const { wallet } = createWallet(mintUrl);
19
- await wallet.loadMint();
20
- // Request a mint quote
21
- const quote = await wallet.createMintQuote(amount);
22
- console.log(`[cashu] Mint quote created: ${quote.quote}`);
23
- // For testnut, quotes are auto-paid. For real mints, you'd pay the Lightning invoice.
24
- // Poll until quote is paid
25
- let state = quote.state;
26
- for (let i = 0; i < 30 && state !== MintQuoteState.PAID; i++) {
27
- await sleep(1000);
28
- const check = await wallet.checkMintQuote(quote.quote);
29
- state = check.state;
30
- }
31
- if (state !== MintQuoteState.PAID) {
32
- throw new Error(`Mint quote not paid after 30s (state: ${state}). For real mints, pay invoice: ${quote.request}`);
33
- }
34
- // Mint the tokens (v3.5 returns Proof[] directly)
35
- const proofs = await wallet.mintProofs(amount, quote.quote);
36
- console.log(`[cashu] Minted ${amount} sats (${proofs.length} proofs)`);
37
- // Encode as a portable token string
38
- const token = getEncodedToken({ mint: mintUrl, proofs });
39
- return { token, proofs };
40
- }
41
- /**
42
- * Verify and receive a Cashu token — swaps proofs with the mint to prevent double-spend
43
- */
44
- export async function receiveToken(tokenStr) {
45
- const decoded = getDecodedToken(tokenStr);
46
- const mintUrl = decoded.mint;
47
- if (!mintUrl)
48
- throw new Error('Token has no mint URL');
49
- const { wallet } = createWallet(mintUrl);
50
- await wallet.loadMint();
51
- // Swap proofs with the mint (this claims them, preventing re-use)
52
- const proofs = await wallet.receive(tokenStr);
53
- const total = proofs.reduce((sum, p) => sum + p.amount, 0);
54
- console.log(`[cashu] Received ${total} sats from token (${proofs.length} proofs)`);
55
- return { proofs, amount: total, mintUrl };
56
- }
57
- /**
58
- * Get the total amount of a token without claiming it
59
- */
60
- export function peekToken(tokenStr) {
61
- const decoded = getDecodedToken(tokenStr);
62
- const total = decoded.proofs.reduce((sum, p) => sum + p.amount, 0);
63
- return { amount: total, mint: decoded.mint, proofCount: decoded.proofs.length };
64
- }
65
- /**
66
- * Split a large token into multiple micro-tokens of a given amount.
67
- * Used for streaming payments: customer pre-splits budget into per-payment chunks.
68
- *
69
- * Example: splitTokens(token_100sats, 10) → 10 tokens of 10 sats each
70
- */
71
- export async function splitTokens(tokenStr, perAmount) {
72
- const decoded = getDecodedToken(tokenStr);
73
- const mintUrl = decoded.mint;
74
- if (!mintUrl)
75
- throw new Error('Token has no mint URL');
76
- const { wallet } = createWallet(mintUrl);
77
- await wallet.loadMint();
78
- const microTokens = [];
79
- let remaining = decoded.proofs;
80
- while (true) {
81
- const total = remaining.reduce((sum, p) => sum + p.amount, 0);
82
- if (total < perAmount)
83
- break;
84
- let retries = 5;
85
- while (retries > 0) {
86
- try {
87
- const { send, keep: kept } = await wallet.send(perAmount, remaining);
88
- microTokens.push(getEncodedToken({ mint: mintUrl, proofs: send }));
89
- remaining = kept;
90
- break;
91
- }
92
- catch (e) {
93
- if (retries > 1 && /rate.limit/i.test(e.message || '')) {
94
- const delay = (6 - retries) * 2000; // 2s, 4s, 6s, 8s
95
- console.log(`[cashu] Rate limited, waiting ${delay / 1000}s... (${retries - 1} retries left)`);
96
- await sleep(delay);
97
- retries--;
98
- }
99
- else {
100
- throw e;
101
- }
102
- }
103
- }
104
- if (remaining.length === 0)
105
- break;
106
- }
107
- console.log(`[cashu] Split into ${microTokens.length} micro-tokens of ${perAmount} sats each`);
108
- return microTokens;
109
- }
110
- function sleep(ms) {
111
- return new Promise(resolve => setTimeout(resolve, ms));
112
- }
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Standalone P2P Customer — connects to a provider, streams results with CLINK debit payments.
4
- *
5
- * Usage:
6
- * 2020117-customer --kind=5100 --budget=50 --ndebit=ndebit1... "Explain quantum computing"
7
- */
8
- export {};
package/dist/customer.js DELETED
@@ -1,64 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Standalone P2P Customer — connects to a provider, streams results with CLINK debit payments.
4
- *
5
- * Usage:
6
- * 2020117-customer --kind=5100 --budget=50 --ndebit=ndebit1... "Explain quantum computing"
7
- */
8
- // --- CLI args → env (before any imports) ---
9
- for (const arg of process.argv.slice(2)) {
10
- if (!arg.startsWith('--'))
11
- continue;
12
- const eq = arg.indexOf('=');
13
- if (eq === -1)
14
- continue;
15
- const key = arg.slice(0, eq);
16
- const val = arg.slice(eq + 1);
17
- switch (key) {
18
- case '--kind':
19
- process.env.DVM_KIND = val;
20
- break;
21
- case '--budget':
22
- process.env.BUDGET_SATS = val;
23
- break;
24
- case '--max-price':
25
- process.env.MAX_SATS_PER_CHUNK = val;
26
- break;
27
- case '--ndebit':
28
- process.env.CLINK_NDEBIT = val;
29
- break;
30
- }
31
- }
32
- import { streamFromProvider } from './p2p-customer.js';
33
- const KIND = Number(process.env.DVM_KIND) || 5100;
34
- const BUDGET_SATS = Number(process.env.BUDGET_SATS) || 100;
35
- const MAX_SATS_PER_CHUNK = Number(process.env.MAX_SATS_PER_CHUNK) || 5;
36
- const NDEBIT = process.env.CLINK_NDEBIT || '';
37
- async function main() {
38
- const prompt = process.argv.slice(2).filter(a => !a.startsWith('--')).join(' ');
39
- if (!prompt) {
40
- console.error('Usage: 2020117-customer --kind=5100 --budget=50 --ndebit=ndebit1... "your prompt here"');
41
- process.exit(1);
42
- }
43
- if (!NDEBIT) {
44
- console.error('[customer] Error: --ndebit=ndebit1... required (CLINK debit authorization)');
45
- process.exit(1);
46
- }
47
- console.log(`[customer] Prompt: "${prompt.slice(0, 60)}..."`);
48
- console.log(`[customer] Budget: ${BUDGET_SATS} sats, max price: ${MAX_SATS_PER_CHUNK} sat/chunk`);
49
- // Stream from provider (handles connection, negotiation, CLINK payments internally)
50
- let output = '';
51
- for await (const chunk of streamFromProvider({
52
- kind: KIND,
53
- input: prompt,
54
- budgetSats: BUDGET_SATS,
55
- ndebit: NDEBIT,
56
- maxSatsPerChunk: MAX_SATS_PER_CHUNK,
57
- label: 'customer',
58
- })) {
59
- process.stdout.write(chunk);
60
- output += chunk;
61
- }
62
- console.log(`\n[customer] Done (${output.length} chars)`);
63
- }
64
- main().catch(err => { console.error('[customer] Fatal:', err.message || err); process.exit(1); });
@@ -1,62 +0,0 @@
1
- /**
2
- * Shared P2P provider protocol — reusable building blocks for provider-side
3
- * streaming with credit-based flow control and CLINK debit payments.
4
- *
5
- * Provider actively pulls payments from customer's wallet via CLINK ndebit,
6
- * generating invoices from their own Lightning Address via LNURL-pay.
7
- *
8
- * Consumers: agent.ts, provider.ts
9
- */
10
- import { SwarmNode } from './swarm.js';
11
- /** Per-connection job state for P2P streaming */
12
- export interface P2PJobState {
13
- socket: any;
14
- credit: number;
15
- ndebit: string;
16
- totalEarned: number;
17
- stopped: boolean;
18
- }
19
- /** Options for {@link streamToCustomer} */
20
- export interface StreamOptions {
21
- node: SwarmNode;
22
- job: P2PJobState;
23
- jobId: string;
24
- source: AsyncIterable<string>;
25
- satsPerChunk: number;
26
- chunksPerPayment: number;
27
- lightningAddress: string;
28
- label: string;
29
- }
30
- /**
31
- * Provider pulls a payment cycle from the customer's wallet via CLINK debit.
32
- *
33
- * Generates an invoice from the provider's Lightning Address, then sends a
34
- * debit request to the customer's wallet service via Nostr relay.
35
- *
36
- * Returns true if payment succeeded and credit was added.
37
- */
38
- export declare function collectP2PPayment(opts: {
39
- job: P2PJobState;
40
- node: SwarmNode;
41
- jobId: string;
42
- satsPerChunk: number;
43
- chunksPerPayment: number;
44
- lightningAddress: string;
45
- label: string;
46
- }): Promise<boolean>;
47
- /**
48
- * Handle an incoming `stop` message: mark the job as stopped.
49
- */
50
- export declare function handleStop(job: P2PJobState, jobId: string, label: string): void;
51
- /**
52
- * Stream chunks from an async source to the customer with credit-based
53
- * flow control. When credit runs out, the provider debits the customer's
54
- * wallet via CLINK before continuing.
55
- *
56
- * Returns the concatenated full output string.
57
- *
58
- * **Cleanup note**: this function does NOT delete the job from any map or
59
- * release any capacity slot — callers are responsible for post-stream
60
- * cleanup.
61
- */
62
- export declare function streamToCustomer(opts: StreamOptions): Promise<string>;
@@ -1,105 +0,0 @@
1
- /**
2
- * Shared P2P provider protocol — reusable building blocks for provider-side
3
- * streaming with credit-based flow control and CLINK debit payments.
4
- *
5
- * Provider actively pulls payments from customer's wallet via CLINK ndebit,
6
- * generating invoices from their own Lightning Address via LNURL-pay.
7
- *
8
- * Consumers: agent.ts, provider.ts
9
- */
10
- import { collectPayment } from './clink.js';
11
- // ---------------------------------------------------------------------------
12
- // Payment helpers
13
- // ---------------------------------------------------------------------------
14
- /**
15
- * Provider pulls a payment cycle from the customer's wallet via CLINK debit.
16
- *
17
- * Generates an invoice from the provider's Lightning Address, then sends a
18
- * debit request to the customer's wallet service via Nostr relay.
19
- *
20
- * Returns true if payment succeeded and credit was added.
21
- */
22
- export async function collectP2PPayment(opts) {
23
- const { job, node, jobId, satsPerChunk, chunksPerPayment, lightningAddress, label } = opts;
24
- const amount = satsPerChunk * chunksPerPayment;
25
- try {
26
- const result = await collectPayment({
27
- ndebit: job.ndebit,
28
- lightningAddress,
29
- amountSats: amount,
30
- });
31
- if (!result.ok) {
32
- console.log(`[${label}] P2P job ${jobId}: debit failed: ${result.error}`);
33
- return false;
34
- }
35
- job.credit += chunksPerPayment;
36
- job.totalEarned += amount;
37
- console.log(`[${label}] P2P job ${jobId}: debit OK +${amount} sats → +${chunksPerPayment} chunks (credit: ${job.credit}, total: ${job.totalEarned} sats)`);
38
- node.send(job.socket, { type: 'payment_ack', id: jobId, amount });
39
- return true;
40
- }
41
- catch (e) {
42
- console.log(`[${label}] P2P job ${jobId}: debit error: ${e.message}`);
43
- return false;
44
- }
45
- }
46
- /**
47
- * Handle an incoming `stop` message: mark the job as stopped.
48
- */
49
- export function handleStop(job, jobId, label) {
50
- console.log(`[${label}] P2P job ${jobId}: customer requested stop`);
51
- job.stopped = true;
52
- }
53
- // ---------------------------------------------------------------------------
54
- // Streaming
55
- // ---------------------------------------------------------------------------
56
- /**
57
- * Stream chunks from an async source to the customer with credit-based
58
- * flow control. When credit runs out, the provider debits the customer's
59
- * wallet via CLINK before continuing.
60
- *
61
- * Returns the concatenated full output string.
62
- *
63
- * **Cleanup note**: this function does NOT delete the job from any map or
64
- * release any capacity slot — callers are responsible for post-stream
65
- * cleanup.
66
- */
67
- export async function streamToCustomer(opts) {
68
- const { node, job, jobId, source, satsPerChunk, chunksPerPayment, lightningAddress, label } = opts;
69
- let fullOutput = '';
70
- try {
71
- for await (const chunk of source) {
72
- if (job.stopped) {
73
- console.log(`[${label}] P2P job ${jobId}: stopped by customer`);
74
- break;
75
- }
76
- if (job.credit <= 0) {
77
- // Pull next payment from customer's wallet
78
- console.log(`[${label}] P2P job ${jobId}: credit exhausted, debiting customer...`);
79
- const paid = await collectP2PPayment({
80
- job, node, jobId, satsPerChunk, chunksPerPayment, lightningAddress, label,
81
- });
82
- if (!paid || job.stopped) {
83
- console.log(`[${label}] P2P job ${jobId}: ending (paid=${paid}, stopped=${job.stopped})`);
84
- break;
85
- }
86
- }
87
- fullOutput += chunk;
88
- node.send(job.socket, { type: 'chunk', id: jobId, data: chunk });
89
- job.credit--;
90
- }
91
- }
92
- catch (e) {
93
- console.error(`[${label}] P2P job ${jobId} generation error: ${e.message}`);
94
- node.send(job.socket, { type: 'error', id: jobId, message: e.message });
95
- }
96
- // Send final result
97
- node.send(job.socket, {
98
- type: 'result',
99
- id: jobId,
100
- output: fullOutput,
101
- total_sats: job.totalEarned,
102
- });
103
- console.log(`[${label}] P2P job ${jobId} completed (${fullOutput.length} chars, ${job.totalEarned} sats earned)`);
104
- return fullOutput;
105
- }
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Pipeline — chain multiple P2P providers in sequence.
4
- *
5
- * Usage:
6
- * BUDGET_SATS=100 CLINK_NDEBIT=ndebit1... TARGET_LANG=Chinese npm run pipeline "Write a short poem"
7
- */
8
- export {};
package/dist/pipeline.js DELETED
@@ -1,106 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Pipeline — chain multiple P2P providers in sequence.
4
- *
5
- * Usage:
6
- * BUDGET_SATS=100 CLINK_NDEBIT=ndebit1... TARGET_LANG=Chinese npm run pipeline "Write a short poem"
7
- */
8
- // --- CLI args → env (before any imports) ---
9
- for (const arg of process.argv.slice(2)) {
10
- if (!arg.startsWith('--'))
11
- continue;
12
- const eq = arg.indexOf('=');
13
- if (eq === -1)
14
- continue;
15
- const key = arg.slice(0, eq);
16
- const val = arg.slice(eq + 1);
17
- switch (key) {
18
- case '--ndebit':
19
- process.env.CLINK_NDEBIT = val;
20
- break;
21
- case '--budget':
22
- process.env.BUDGET_SATS = val;
23
- break;
24
- }
25
- }
26
- import { streamFromProvider } from './p2p-customer.js';
27
- import { getOnlineProviders } from './api.js';
28
- const BUDGET_SATS = Number(process.env.BUDGET_SATS) || 100;
29
- const MAX_SATS_PER_CHUNK = Number(process.env.MAX_SATS_PER_CHUNK) || 5;
30
- const NDEBIT = process.env.CLINK_NDEBIT || '';
31
- const GEN_KIND = Number(process.env.GEN_KIND) || 5100;
32
- const TRANS_KIND = Number(process.env.TRANS_KIND) || 5302;
33
- const TARGET_LANG = process.env.TARGET_LANG || 'Chinese';
34
- async function showProviders(kind, label) {
35
- try {
36
- const agents = await getOnlineProviders(kind);
37
- if (agents.length === 0) {
38
- console.log(`[${label}] No providers online for kind ${kind}`);
39
- }
40
- else {
41
- console.log(`[${label}] ${agents.length} provider(s) online for kind ${kind}:`);
42
- for (const a of agents) {
43
- const cap = a.capacity !== undefined ? `, capacity: ${a.capacity}` : '';
44
- const price = a.pricing ? `, pricing: ${JSON.stringify(a.pricing)}` : '';
45
- console.log(`[${label}] - ${a.username || a.user_id} (${a.status}${cap}${price})`);
46
- }
47
- }
48
- }
49
- catch {
50
- console.log(`[${label}] Could not query platform`);
51
- }
52
- }
53
- async function collectStream(opts) {
54
- let output = '';
55
- for await (const chunk of streamFromProvider(opts)) {
56
- process.stdout.write(chunk);
57
- output += chunk;
58
- }
59
- return output;
60
- }
61
- async function main() {
62
- const prompt = process.argv.slice(2).filter(a => !a.startsWith('--')).join(' ');
63
- if (!prompt) {
64
- console.error('Usage: 2020117-pipeline --ndebit=ndebit1... --budget=100 "your prompt"');
65
- process.exit(1);
66
- }
67
- if (!NDEBIT) {
68
- console.error('[pipeline] Error: --ndebit=ndebit1... required (CLINK debit authorization)');
69
- process.exit(1);
70
- }
71
- console.log('='.repeat(60));
72
- console.log(`Pipeline: generate (kind ${GEN_KIND}) → translate to ${TARGET_LANG} (kind ${TRANS_KIND})`);
73
- console.log(`Total budget: ${BUDGET_SATS} sats`);
74
- console.log('='.repeat(60));
75
- const genBudget = Math.ceil(BUDGET_SATS * 0.6);
76
- const transBudget = BUDGET_SATS - genBudget;
77
- // Phase 1: Text Generation
78
- console.log(`\n${'─'.repeat(60)}`);
79
- console.log(`Phase 1: Text Generation (budget: ${genBudget} sats)`);
80
- console.log('─'.repeat(60));
81
- await showProviders(GEN_KIND, 'gen');
82
- const generated = await collectStream({
83
- kind: GEN_KIND, input: prompt, budgetSats: genBudget, ndebit: NDEBIT,
84
- maxSatsPerChunk: MAX_SATS_PER_CHUNK, label: 'gen',
85
- });
86
- if (!generated.trim()) {
87
- console.error('\n[pipeline] Phase 1 produced no output, aborting');
88
- process.exit(1);
89
- }
90
- // Phase 2: Translation
91
- console.log(`\n${'─'.repeat(60)}`);
92
- console.log(`Phase 2: Translation to ${TARGET_LANG} (budget: ${transBudget} sats)`);
93
- console.log('─'.repeat(60));
94
- await showProviders(TRANS_KIND, 'trans');
95
- const translated = await collectStream({
96
- kind: TRANS_KIND, input: `Translate the following text to ${TARGET_LANG}:\n\n${generated}`,
97
- budgetSats: transBudget, ndebit: NDEBIT, maxSatsPerChunk: MAX_SATS_PER_CHUNK, label: 'trans',
98
- });
99
- // Summary
100
- console.log(`\n${'='.repeat(60)}`);
101
- console.log('Pipeline complete!');
102
- console.log('='.repeat(60));
103
- console.log(`\nGenerated (${generated.length} chars):\n${generated}`);
104
- console.log(`\nTranslated (${translated.length} chars):\n${translated}`);
105
- }
106
- main().catch(err => { console.error('[pipeline] Fatal:', err.message || err); process.exit(1); });
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Standalone Provider daemon — thin wrapper around shared P2P provider protocol.
4
- * For most use cases, prefer `2020117-agent` which handles both API + P2P.
5
- */
6
- export {};
package/dist/provider.js DELETED
@@ -1,110 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Standalone Provider daemon — thin wrapper around shared P2P provider protocol.
4
- * For most use cases, prefer `2020117-agent` which handles both API + P2P.
5
- */
6
- // --- CLI args → env (before any imports) ---
7
- for (const arg of process.argv.slice(2)) {
8
- if (!arg.startsWith('--'))
9
- continue;
10
- const eq = arg.indexOf('=');
11
- if (eq === -1)
12
- continue;
13
- const key = arg.slice(0, eq);
14
- const val = arg.slice(eq + 1);
15
- switch (key) {
16
- case '--kind':
17
- process.env.DVM_KIND = val;
18
- break;
19
- case '--lightning-address':
20
- process.env.LIGHTNING_ADDRESS = val;
21
- break;
22
- }
23
- }
24
- import { SwarmNode, topicFromKind } from './swarm.js';
25
- import { createProcessor } from './processor.js';
26
- import { hasApiKey, registerService, startHeartbeatLoop } from './api.js';
27
- import { initClinkAgent } from './clink.js';
28
- import { collectP2PPayment, handleStop, streamToCustomer } from './p2p-provider.js';
29
- const KIND = Number(process.env.DVM_KIND) || 5100;
30
- const SATS_PER_CHUNK = Number(process.env.SATS_PER_CHUNK) || 1;
31
- const CHUNKS_PER_PAYMENT = Number(process.env.CHUNKS_PER_PAYMENT) || 10;
32
- const LIGHTNING_ADDRESS = process.env.LIGHTNING_ADDRESS || '';
33
- const jobs = new Map();
34
- async function main() {
35
- if (!LIGHTNING_ADDRESS) {
36
- console.error('[provider] Error: --lightning-address=you@wallet.com required');
37
- process.exit(1);
38
- }
39
- const processor = await createProcessor();
40
- await processor.verify();
41
- console.log(`[provider] Processor "${processor.name}" verified`);
42
- // Initialize CLINK agent identity
43
- const { pubkey } = initClinkAgent();
44
- console.log(`[provider] CLINK: ${LIGHTNING_ADDRESS} (agent pubkey: ${pubkey.slice(0, 16)}...)`);
45
- // Platform registration
46
- let stopHeartbeat = null;
47
- if (hasApiKey()) {
48
- console.log('[provider] Registering on platform...');
49
- await registerService({ kind: KIND, satsPerChunk: SATS_PER_CHUNK, chunksPerPayment: CHUNKS_PER_PAYMENT, model: processor.name });
50
- stopHeartbeat = startHeartbeatLoop();
51
- }
52
- else {
53
- console.log('[provider] No API key — P2P-only mode');
54
- }
55
- const satsPerPayment = SATS_PER_CHUNK * CHUNKS_PER_PAYMENT;
56
- const node = new SwarmNode();
57
- const topic = topicFromKind(KIND);
58
- console.log(`[provider] Streaming payment: ${SATS_PER_CHUNK} sat/chunk, ${CHUNKS_PER_PAYMENT} chunks/payment (${satsPerPayment} sats/cycle)`);
59
- await node.listen(topic);
60
- console.log(`[provider] Listening for customers...\n`);
61
- node.on('message', async (msg, socket, peerId) => {
62
- const tag = peerId.slice(0, 8);
63
- if (msg.type === 'request') {
64
- console.log(`[provider] Job ${msg.id} from ${tag}: "${(msg.input || '').slice(0, 60)}..."`);
65
- if (!msg.ndebit) {
66
- node.send(socket, { type: 'error', id: msg.id, message: 'Request requires ndebit authorization' });
67
- return;
68
- }
69
- if (msg.budget !== undefined) {
70
- console.log(`[provider] Customer budget: ${msg.budget} sats`);
71
- }
72
- const job = {
73
- socket, credit: 0, ndebit: msg.ndebit, totalEarned: 0, stopped: false,
74
- };
75
- jobs.set(msg.id, job);
76
- node.send(socket, { type: 'offer', id: msg.id, sats_per_chunk: SATS_PER_CHUNK, chunks_per_payment: CHUNKS_PER_PAYMENT });
77
- // Debit first payment cycle via CLINK
78
- const paid = await collectP2PPayment({
79
- job, node, jobId: msg.id,
80
- satsPerChunk: SATS_PER_CHUNK, chunksPerPayment: CHUNKS_PER_PAYMENT,
81
- lightningAddress: LIGHTNING_ADDRESS, label: 'provider',
82
- });
83
- if (!paid) {
84
- jobs.delete(msg.id);
85
- return;
86
- }
87
- node.send(socket, { type: 'accepted', id: msg.id });
88
- const source = processor.generateStream({ input: msg.input || '', params: msg.params });
89
- await streamToCustomer({
90
- node, job, jobId: msg.id, source,
91
- satsPerChunk: SATS_PER_CHUNK, chunksPerPayment: CHUNKS_PER_PAYMENT,
92
- lightningAddress: LIGHTNING_ADDRESS, label: 'provider',
93
- });
94
- jobs.delete(msg.id);
95
- }
96
- if (msg.type === 'stop') {
97
- const job = jobs.get(msg.id);
98
- if (job)
99
- handleStop(job, msg.id, 'provider');
100
- }
101
- });
102
- process.on('SIGINT', async () => {
103
- console.log('\n[provider] Shutting down...');
104
- if (stopHeartbeat)
105
- stopHeartbeat();
106
- await node.destroy();
107
- process.exit(0);
108
- });
109
- }
110
- main().catch(err => { console.error('[provider] Fatal:', err); process.exit(1); });