kompass-sdk 0.3.0 → 0.5.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/cli.js +11 -2
- package/dist/cli.js.map +1 -1
- package/dist/router.d.ts +3 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +49 -18
- package/dist/router.js.map +1 -1
- package/dist/sources/x402-ecosystem.d.ts +3 -2
- package/dist/sources/x402-ecosystem.d.ts.map +1 -1
- package/dist/sources/x402-ecosystem.js +69 -55
- package/dist/sources/x402-ecosystem.js.map +1 -1
- package/dist/unified.d.ts +2 -0
- package/dist/unified.d.ts.map +1 -1
- package/dist/unified.js +1 -1
- package/dist/unified.js.map +1 -1
- package/dist/wallet/bridge.d.ts +41 -0
- package/dist/wallet/bridge.d.ts.map +1 -0
- package/dist/wallet/bridge.js +121 -0
- package/dist/wallet/bridge.js.map +1 -0
- package/dist/wallet/handlers/acp.d.ts.map +1 -1
- package/dist/wallet/handlers/acp.js +54 -6
- package/dist/wallet/handlers/acp.js.map +1 -1
- package/dist/wallet/handlers/l402.d.ts.map +1 -1
- package/dist/wallet/handlers/l402.js.map +1 -1
- package/dist/wallet/index.d.ts.map +1 -1
- package/dist/wallet/index.js +3 -2
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/payment-router.d.ts.map +1 -1
- package/dist/wallet/payment-router.js +15 -0
- package/dist/wallet/payment-router.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +13 -3
- package/src/router.ts +52 -19
- package/src/sources/x402-ecosystem.ts +75 -58
- package/src/unified.ts +3 -1
- package/src/wallet/bridge.ts +164 -0
- package/src/wallet/handlers/acp.ts +66 -6
- package/src/wallet/handlers/l402.ts +1 -0
- package/src/wallet/index.ts +3 -2
- package/src/wallet/payment-router.ts +15 -0
|
@@ -1,86 +1,103 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* x402 Ecosystem Source Adapter
|
|
3
|
-
*
|
|
2
|
+
* x402/L402/MPP Ecosystem Source Adapter
|
|
3
|
+
* Searches 402index.io — protocol-agnostic directory of 15,000+ paid API endpoints
|
|
4
|
+
* Covers x402, L402, and MPP endpoints
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import type { SourceAdapter, UnifiedAgent, SourceSearchOptions } from "./types.js";
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
const KNOWN_X402_SERVICES = [
|
|
10
|
-
{ name: "OpenRouter", url: "https://openrouter.ai", description: "LLM API access with USDC payments", categories: ["ai", "llm"] },
|
|
11
|
-
{ name: "Dexter AI", url: "https://dexterai.com", description: "x402 facilitator on Solana. 38M+ settlements", categories: ["defi", "trading"] },
|
|
12
|
-
{ name: "AgentCash", url: "https://agentcash.dev", description: "One balance, every paid API. Multi-protocol agent wallet", categories: ["payments", "tools"] },
|
|
13
|
-
{ name: "Allium", url: "https://allium.so", description: "Blockchain data APIs with x402 payments", categories: ["data", "analytics"] },
|
|
14
|
-
{ name: "Kompass Data Provider", url: "http://localhost:4021", description: "DeFi yield data, token prices, risk scores via x402", categories: ["defi", "data"] },
|
|
15
|
-
{ name: "Wurk", url: "https://wurk.fun", description: "Agent job board with x402 integration", categories: ["jobs", "tools"] },
|
|
16
|
-
{ name: "Tavily", url: "https://tavily.com", description: "AI-powered web search API", categories: ["search", "data"] },
|
|
17
|
-
{ name: "ASSAY", url: "https://assayaudit.com", description: "Agent evaluation and auditing service", categories: ["audit", "trust"] },
|
|
18
|
-
{ name: "Zentiece", url: "https://zentiece.com", description: "Agent with x402 payments + ORBIT burns", categories: ["defi", "trading"] },
|
|
19
|
-
{ name: "AlgoVoi", url: "https://algovoi.com", description: "x402 integration on Algorand", categories: ["defi", "tools"] },
|
|
20
|
-
{ name: "LibertAI", url: "https://libertai.io", description: "Autonomous agents on Aleph Cloud with x402", categories: ["ai", "compute"] },
|
|
21
|
-
{ name: "Handshake_58", url: "https://handshake58.com", description: "Streaming AI agent payment channels", categories: ["payments"] },
|
|
22
|
-
];
|
|
9
|
+
const INDEX_API = "https://402index.io/api/v1/services";
|
|
23
10
|
|
|
24
11
|
export const x402EcosystemAdapter: SourceAdapter = {
|
|
25
12
|
name: "x402-ecosystem",
|
|
26
|
-
displayName: "x402
|
|
13
|
+
displayName: "402 Index (x402 + L402 + MPP)",
|
|
27
14
|
|
|
28
15
|
async search(query: string, options?: SourceSearchOptions): Promise<UnifiedAgent[]> {
|
|
29
|
-
|
|
30
|
-
|
|
16
|
+
const limit = options?.limit ?? 20;
|
|
17
|
+
const timeout = options?.timeout ?? 10000;
|
|
18
|
+
|
|
31
19
|
try {
|
|
32
|
-
const
|
|
33
|
-
|
|
20
|
+
const url = new URL(INDEX_API);
|
|
21
|
+
if (query) url.searchParams.set("q", query);
|
|
22
|
+
url.searchParams.set("limit", String(limit));
|
|
23
|
+
url.searchParams.set("health", "healthy");
|
|
24
|
+
url.searchParams.set("sort", "reliability");
|
|
25
|
+
|
|
26
|
+
const res = await fetch(url.toString(), {
|
|
27
|
+
signal: AbortSignal.timeout(timeout),
|
|
34
28
|
});
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
|
|
30
|
+
if (!res.ok) return [];
|
|
31
|
+
|
|
32
|
+
const data = await res.json();
|
|
33
|
+
const services = data.services ?? data.data ?? data;
|
|
34
|
+
|
|
35
|
+
if (!Array.isArray(services)) return [];
|
|
36
|
+
|
|
37
|
+
return services.slice(0, limit).map(mapService);
|
|
43
38
|
} catch {
|
|
44
|
-
|
|
39
|
+
return [];
|
|
45
40
|
}
|
|
46
|
-
|
|
47
|
-
const allServices = [...KNOWN_X402_SERVICES, ...dynamicServices];
|
|
48
|
-
const lower = query.toLowerCase();
|
|
49
|
-
|
|
50
|
-
return allServices
|
|
51
|
-
.filter((s) => {
|
|
52
|
-
if (!query) return true;
|
|
53
|
-
return (
|
|
54
|
-
s.name.toLowerCase().includes(lower) ||
|
|
55
|
-
s.description.toLowerCase().includes(lower) ||
|
|
56
|
-
s.categories.some((c) => lower.includes(c))
|
|
57
|
-
);
|
|
58
|
-
})
|
|
59
|
-
.slice(0, options?.limit ?? 50)
|
|
60
|
-
.map(mapX402Service);
|
|
61
41
|
},
|
|
62
42
|
|
|
63
43
|
async ping(): Promise<boolean> {
|
|
64
|
-
|
|
44
|
+
try {
|
|
45
|
+
const res = await fetch(`${INDEX_API}?limit=1`, {
|
|
46
|
+
signal: AbortSignal.timeout(5000),
|
|
47
|
+
});
|
|
48
|
+
return res.ok;
|
|
49
|
+
} catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
65
52
|
},
|
|
66
53
|
};
|
|
67
54
|
|
|
68
|
-
function
|
|
55
|
+
function mapService(service: any): UnifiedAgent {
|
|
56
|
+
const protocol = mapProtocol(service.protocol);
|
|
57
|
+
|
|
69
58
|
return {
|
|
70
|
-
id: `x402-ecosystem:${service.
|
|
71
|
-
nativeId: service.name,
|
|
72
|
-
name: service.name,
|
|
73
|
-
description: service.description,
|
|
74
|
-
categories: service.
|
|
75
|
-
capabilities: [service.description],
|
|
59
|
+
id: `x402-ecosystem:${service.id ?? service.url}`,
|
|
60
|
+
nativeId: service.id ?? service.name,
|
|
61
|
+
name: cleanName(service.name ?? "Unknown Service"),
|
|
62
|
+
description: cleanDescription(service.description ?? ""),
|
|
63
|
+
categories: service.category ? service.category.split("/").map((c: string) => c.trim()) : ["general"],
|
|
64
|
+
capabilities: [service.description ?? service.name ?? ""].filter(Boolean),
|
|
76
65
|
source: "x402-ecosystem",
|
|
77
|
-
protocol
|
|
66
|
+
protocol,
|
|
78
67
|
endpoints: {
|
|
79
|
-
x402: service.url,
|
|
68
|
+
x402: protocol === "x402" ? service.url : undefined,
|
|
69
|
+
l402: protocol === "l402" ? service.url : undefined,
|
|
80
70
|
http: service.url,
|
|
81
71
|
},
|
|
82
|
-
pricing: {
|
|
83
|
-
|
|
72
|
+
pricing: {
|
|
73
|
+
model: "per-call",
|
|
74
|
+
amount: service.price_usd ? String(service.price_usd) : service.price_sats ? `${service.price_sats} sats` : undefined,
|
|
75
|
+
currency: service.payment_asset ?? (protocol === "l402" ? "sats" : "USDC"),
|
|
76
|
+
},
|
|
77
|
+
reputation: service.reliability_score ? {
|
|
78
|
+
score: service.reliability_score,
|
|
79
|
+
count: service.uptime_30d ? Math.round(service.uptime_30d * 100) : 0,
|
|
80
|
+
source: "402index",
|
|
81
|
+
} : undefined,
|
|
82
|
+
verified: service.x402_payment_valid === 1 || service.health_status === "healthy",
|
|
83
|
+
lastSeen: service.last_checked ? new Date(service.last_checked).getTime() : undefined,
|
|
84
84
|
raw: service,
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
|
+
|
|
88
|
+
function mapProtocol(protocol: string): "x402" | "l402" | "http" {
|
|
89
|
+
switch (protocol?.toLowerCase()) {
|
|
90
|
+
case "x402": return "x402";
|
|
91
|
+
case "l402": return "l402";
|
|
92
|
+
case "mpp": return "x402"; // MPP uses same HTTP 402 pattern
|
|
93
|
+
default: return "x402";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function cleanName(name: string): string {
|
|
98
|
+
return name.replace(/^\s+|\s+$/g, "").split("\n")[0].trim().slice(0, 100);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function cleanDescription(desc: string): string {
|
|
102
|
+
return desc.replace(/^\s+|\s+$/g, "").replace(/\s+/g, " ").slice(0, 300);
|
|
103
|
+
}
|
package/src/unified.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface UnifiedConfig {
|
|
|
28
28
|
kompassConfig?: any;
|
|
29
29
|
/** ERC-8004 reputation writer config (writes feedback after executions) */
|
|
30
30
|
reputationWriter?: ReputationWriterConfig;
|
|
31
|
+
/** Kompass wallet for payments */
|
|
32
|
+
wallet?: any;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export interface FindOptions {
|
|
@@ -59,7 +61,7 @@ export class KompassUnified {
|
|
|
59
61
|
this.config = config;
|
|
60
62
|
this.aggregator = new Aggregator(adapters);
|
|
61
63
|
this.bridge = bridge;
|
|
62
|
-
this.router = new CapabilityRouter(this.aggregator, this.bridge, config?.reputationWriter);
|
|
64
|
+
this.router = new CapabilityRouter(this.aggregator, this.bridge, config?.reputationWriter, config?.wallet);
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
static async create(config?: UnifiedConfig): Promise<KompassUnified> {
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kompass Internal Bridge
|
|
3
|
+
* Handles all fund routing that the agent never sees:
|
|
4
|
+
* - USDC transfer to ACP wallet (same chain)
|
|
5
|
+
* - USDC → Lightning sats swap (via Boltz)
|
|
6
|
+
* - Cross-chain USDC bridging (via CCTP)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { type Address, type Hex, parseUnits, formatUnits } from "viem";
|
|
10
|
+
import type { KompassWallet } from "./index.js";
|
|
11
|
+
|
|
12
|
+
const USDC_ABI = [
|
|
13
|
+
{ type: "function", name: "transfer", inputs: [{ name: "to", type: "address" }, { name: "amount", type: "uint256" }], outputs: [{ name: "", type: "bool" }], stateMutability: "nonpayable" },
|
|
14
|
+
{ type: "function", name: "balanceOf", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
const USDC_ADDRESSES: Record<string, Address> = {
|
|
18
|
+
"base": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
19
|
+
"base-sepolia": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// ── ACP Wallet Topup ─────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Transfer USDC from Kompass wallet to ACP smart wallet on Base.
|
|
26
|
+
* This is a simple ERC-20 transfer on the same chain.
|
|
27
|
+
*/
|
|
28
|
+
export async function topupACPWallet(
|
|
29
|
+
wallet: KompassWallet,
|
|
30
|
+
acpWalletAddress: Address,
|
|
31
|
+
amountUSDC: number
|
|
32
|
+
): Promise<{ txHash: Hex; amount: string }> {
|
|
33
|
+
const network = wallet.getNetwork();
|
|
34
|
+
const usdcAddress = USDC_ADDRESSES[network];
|
|
35
|
+
if (!usdcAddress) throw new Error(`No USDC address for network: ${network}`);
|
|
36
|
+
|
|
37
|
+
const amount = parseUnits(String(amountUSDC), 6);
|
|
38
|
+
|
|
39
|
+
// Check balance first
|
|
40
|
+
const balance = await wallet.getPublicClient().readContract({
|
|
41
|
+
address: usdcAddress,
|
|
42
|
+
abi: USDC_ABI,
|
|
43
|
+
functionName: "balanceOf",
|
|
44
|
+
args: [wallet.getAddress()],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (balance < amount) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Insufficient USDC: have ${formatUnits(balance, 6)}, need ${amountUSDC}. ` +
|
|
50
|
+
`Fund your Kompass wallet at ${wallet.getAddress()} on ${network}.`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const txHash = await (wallet.getWalletClient() as any).writeContract({
|
|
55
|
+
address: usdcAddress,
|
|
56
|
+
abi: USDC_ABI,
|
|
57
|
+
functionName: "transfer",
|
|
58
|
+
args: [acpWalletAddress, amount],
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
await wallet.getPublicClient().waitForTransactionReceipt({ hash: txHash });
|
|
62
|
+
|
|
63
|
+
console.log(`[Kompass Bridge] Transferred ${amountUSDC} USDC to ACP wallet ${acpWalletAddress.slice(0, 10)}... | tx: ${txHash}`);
|
|
64
|
+
|
|
65
|
+
return { txHash, amount: String(amountUSDC) };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ── Lightning Swap (USDC → sats) ─────────────────────────
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Swap USDC to Lightning sats via Boltz Exchange API.
|
|
72
|
+
* Boltz supports atomic submarine swaps: USDC on-chain → Lightning invoice paid.
|
|
73
|
+
*/
|
|
74
|
+
export async function swapUSDCtoLightning(
|
|
75
|
+
wallet: KompassWallet,
|
|
76
|
+
invoiceAmountSats: number,
|
|
77
|
+
invoice: string
|
|
78
|
+
): Promise<{ success: boolean; preimage?: string; error?: string }> {
|
|
79
|
+
// Boltz Exchange API for submarine swaps
|
|
80
|
+
// https://docs.boltz.exchange/api
|
|
81
|
+
const BOLTZ_API = "https://api.boltz.exchange/v2";
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
// Step 1: Create a submarine swap (on-chain → Lightning)
|
|
85
|
+
const createRes = await fetch(`${BOLTZ_API}/swap/submarine`, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
headers: { "Content-Type": "application/json" },
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
from: "USDC", // or "BTC" for on-chain BTC
|
|
90
|
+
to: "BTC",
|
|
91
|
+
invoice,
|
|
92
|
+
referralId: "kompass",
|
|
93
|
+
}),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (!createRes.ok) {
|
|
97
|
+
const err = await createRes.text();
|
|
98
|
+
return { success: false, error: `Boltz swap creation failed: ${err}` };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const swapData = await createRes.json();
|
|
102
|
+
|
|
103
|
+
// Step 2: Send the required on-chain amount to Boltz's lock address
|
|
104
|
+
// Boltz provides: address, expectedAmount, and a swap ID
|
|
105
|
+
// The agent's wallet sends USDC/BTC to the lock address
|
|
106
|
+
// Boltz atomically pays the Lightning invoice
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
error: `Lightning swap prepared via Boltz. Swap ID: ${swapData.id ?? "unknown"}. ` +
|
|
111
|
+
`Send ${swapData.expectedAmount ?? invoiceAmountSats} sats worth of USDC to ${swapData.address ?? "Boltz lock address"} to complete.`,
|
|
112
|
+
};
|
|
113
|
+
} catch (err) {
|
|
114
|
+
return {
|
|
115
|
+
success: false,
|
|
116
|
+
error: `Lightning swap failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Cross-Chain USDC Bridge ──────────────────────────────
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Bridge USDC from Base to another chain via Circle CCTP.
|
|
125
|
+
* Most agent capabilities are on Base, so this is rarely needed.
|
|
126
|
+
*/
|
|
127
|
+
export async function bridgeUSDCCrossChain(
|
|
128
|
+
wallet: KompassWallet,
|
|
129
|
+
toChain: string,
|
|
130
|
+
amountUSDC: number
|
|
131
|
+
): Promise<{ success: boolean; txHash?: string; error?: string }> {
|
|
132
|
+
// Circle CCTP (Cross-Chain Transfer Protocol) endpoints
|
|
133
|
+
// https://developers.circle.com/stablecoins/cctp-getting-started
|
|
134
|
+
|
|
135
|
+
// For hackathon: most capabilities are on Base, bridging rarely needed
|
|
136
|
+
return {
|
|
137
|
+
success: false,
|
|
138
|
+
error: `Cross-chain bridging to ${toChain} is not yet automated. ` +
|
|
139
|
+
`Most agent capabilities are on Base. Use a bridge like bridge.circle.com manually if needed.`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ── Balance Check ────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Check if wallet has enough USDC for a payment.
|
|
147
|
+
* Returns true if sufficient, throws descriptive error if not.
|
|
148
|
+
*/
|
|
149
|
+
export async function ensureSufficientBalance(
|
|
150
|
+
wallet: KompassWallet,
|
|
151
|
+
requiredUSDC: number
|
|
152
|
+
): Promise<boolean> {
|
|
153
|
+
const balance = await wallet.getBalance();
|
|
154
|
+
const available = parseFloat(balance.usdc);
|
|
155
|
+
|
|
156
|
+
if (available < requiredUSDC) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
`Insufficient USDC: have $${available.toFixed(2)}, need $${requiredUSDC.toFixed(2)}. ` +
|
|
159
|
+
`Fund your Kompass wallet: send USDC to ${balance.address} on Base.`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
@@ -10,9 +10,14 @@ import { execFile } from "child_process";
|
|
|
10
10
|
import { promisify } from "util";
|
|
11
11
|
import type { KompassWallet } from "../index.js";
|
|
12
12
|
import type { UnifiedAgent } from "../../sources/types.js";
|
|
13
|
+
import { topupACPWallet } from "../bridge.js";
|
|
14
|
+
import type { Address } from "viem";
|
|
13
15
|
|
|
14
16
|
const execFileAsync = promisify(execFile);
|
|
15
17
|
|
|
18
|
+
// ACP wallet address (from our registration)
|
|
19
|
+
const ACP_WALLET = "0x2aB73f8CCee4ABE601cE71eCC82Bd9eC375FeBbA" as Address;
|
|
20
|
+
|
|
16
21
|
export async function handleACPPayment(
|
|
17
22
|
wallet: KompassWallet,
|
|
18
23
|
agent: UnifiedAgent,
|
|
@@ -30,6 +35,16 @@ export async function handleACPPayment(
|
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
try {
|
|
38
|
+
// Auto-topup: transfer USDC from Kompass wallet to ACP wallet if needed
|
|
39
|
+
const jobPrice = offering.price ?? 0.05;
|
|
40
|
+
const topupAmount = Math.max(jobPrice * 2, 0.10); // 2x buffer
|
|
41
|
+
try {
|
|
42
|
+
await topupACPWallet(wallet, ACP_WALLET, topupAmount);
|
|
43
|
+
} catch (topupErr) {
|
|
44
|
+
// Topup may fail if wallet is on different network or insufficient funds
|
|
45
|
+
console.log(`[Kompass] ACP topup skipped: ${topupErr instanceof Error ? topupErr.message : topupErr}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
33
48
|
// Use ACP CLI with execFile (not execSync) to avoid shell injection
|
|
34
49
|
// Arguments passed as array, not concatenated string
|
|
35
50
|
const requirements = JSON.stringify({ description: task });
|
|
@@ -39,7 +54,7 @@ export async function handleACPPayment(
|
|
|
39
54
|
[
|
|
40
55
|
"acp", "job", "create",
|
|
41
56
|
walletAddress,
|
|
42
|
-
String(offering.id),
|
|
57
|
+
String(offering.name ?? offering.id),
|
|
43
58
|
"--requirements", requirements,
|
|
44
59
|
"--isAutomated", "true",
|
|
45
60
|
"--json",
|
|
@@ -52,19 +67,64 @@ export async function handleACPPayment(
|
|
|
52
67
|
);
|
|
53
68
|
|
|
54
69
|
const jobData = JSON.parse(stdout.trim());
|
|
70
|
+
const jobId = jobData.data?.jobId ?? jobData.jobId ?? jobData.id;
|
|
71
|
+
|
|
72
|
+
console.log(`[Kompass] ACP job #${jobId} created with ${agent.name}. Polling for completion...`);
|
|
73
|
+
|
|
74
|
+
// Poll for completion (max 2 minutes)
|
|
75
|
+
const maxWait = 120_000;
|
|
76
|
+
const pollInterval = 10_000;
|
|
77
|
+
const start = Date.now();
|
|
78
|
+
let finalPhase = "created";
|
|
79
|
+
let deliverable: any = null;
|
|
80
|
+
|
|
81
|
+
while (Date.now() - start < maxWait) {
|
|
82
|
+
await new Promise((r) => setTimeout(r, pollInterval));
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const { stdout: statusOut } = await execFileAsync(
|
|
86
|
+
"npx",
|
|
87
|
+
["acp", "job", "status", String(jobId), "--json"],
|
|
88
|
+
{ cwd: "/tmp/openclaw-acp", timeout: 15000, env: { ...process.env, NODE_NO_WARNINGS: "1" } }
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const statusData = JSON.parse(statusOut.trim());
|
|
92
|
+
finalPhase = statusData.phase ?? "unknown";
|
|
93
|
+
console.log(`[Kompass] Job #${jobId} phase: ${finalPhase}`);
|
|
94
|
+
|
|
95
|
+
if (finalPhase === "COMPLETED") {
|
|
96
|
+
deliverable = statusData.deliverable;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
if (finalPhase === "REJECTED" || finalPhase === "EXPIRED" || finalPhase === "CANCELLED") {
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
// Status check failed, keep polling
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (finalPhase === "COMPLETED" && deliverable) {
|
|
108
|
+
return {
|
|
109
|
+
success: true,
|
|
110
|
+
deliverable,
|
|
111
|
+
jobId: String(jobId),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
55
114
|
|
|
56
115
|
return {
|
|
57
|
-
success:
|
|
116
|
+
success: finalPhase === "COMPLETED",
|
|
58
117
|
deliverable: {
|
|
59
118
|
type: "acp-escrow",
|
|
60
|
-
jobId:
|
|
61
|
-
|
|
62
|
-
message: "
|
|
119
|
+
jobId: String(jobId),
|
|
120
|
+
phase: finalPhase,
|
|
121
|
+
message: finalPhase === "COMPLETED" ? "Job completed" : `Job ended with phase: ${finalPhase}`,
|
|
63
122
|
offering: offering.name,
|
|
64
123
|
price: offering.price,
|
|
65
124
|
agent: agent.name,
|
|
125
|
+
deliverable,
|
|
66
126
|
},
|
|
67
|
-
jobId: String(
|
|
127
|
+
jobId: String(jobId),
|
|
68
128
|
};
|
|
69
129
|
} catch (err) {
|
|
70
130
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { fetchWithL402, type KVStorage } from "@getalby/lightning-tools/l402";
|
|
8
8
|
import type { KompassWallet } from "../index.js";
|
|
9
9
|
import type { UnifiedAgent } from "../../sources/types.js";
|
|
10
|
+
import { swapUSDCtoLightning } from "../bridge.js";
|
|
10
11
|
|
|
11
12
|
// In-memory token storage for L402 macaroons
|
|
12
13
|
class L402Store implements KVStorage {
|
package/src/wallet/index.ts
CHANGED
|
@@ -13,7 +13,8 @@ import { homedir } from "os";
|
|
|
13
13
|
import { join } from "path";
|
|
14
14
|
|
|
15
15
|
const DEFAULT_WALLET_PATH = join(homedir(), ".kompass", "wallet.json");
|
|
16
|
-
const DEFAULT_PASSWORD = "kompass-agent";
|
|
16
|
+
const DEFAULT_PASSWORD = "kompass-agent";
|
|
17
|
+
const DEFAULT_NETWORK: "base" | "base-sepolia" = "base"; // Mainnet by default
|
|
17
18
|
|
|
18
19
|
const ERC20_ABI = [
|
|
19
20
|
{ type: "function", name: "balanceOf", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }], stateMutability: "view" },
|
|
@@ -52,7 +53,7 @@ export class KompassWallet {
|
|
|
52
53
|
*/
|
|
53
54
|
static create(options?: { path?: string; network?: "base" | "base-sepolia"; password?: string }): KompassWallet {
|
|
54
55
|
const path = options?.path ?? DEFAULT_WALLET_PATH;
|
|
55
|
-
const network = options?.network ??
|
|
56
|
+
const network = options?.network ?? DEFAULT_NETWORK;
|
|
56
57
|
const password = options?.password ?? DEFAULT_PASSWORD;
|
|
57
58
|
|
|
58
59
|
const privateKey = generatePrivateKey();
|
|
@@ -11,6 +11,7 @@ import { handleL402Payment } from "./handlers/l402.js";
|
|
|
11
11
|
import { handleMPPPayment } from "./handlers/mpp.js";
|
|
12
12
|
import { handleNanopayment } from "./handlers/nanopayments.js";
|
|
13
13
|
import { handleFreePayment } from "./handlers/free.js";
|
|
14
|
+
import { ensureSufficientBalance } from "./bridge.js";
|
|
14
15
|
|
|
15
16
|
export interface PaymentResult {
|
|
16
17
|
success: boolean;
|
|
@@ -31,6 +32,20 @@ export async function routePayment(
|
|
|
31
32
|
const protocol = determinePaymentProtocol(agent);
|
|
32
33
|
|
|
33
34
|
try {
|
|
35
|
+
// Pre-payment: ensure wallet has enough funds (skip for free)
|
|
36
|
+
if (protocol !== "free") {
|
|
37
|
+
const estimatedCost = parseFloat(agent.pricing?.amount ?? "0.05");
|
|
38
|
+
try {
|
|
39
|
+
await ensureSufficientBalance(wallet, estimatedCost);
|
|
40
|
+
} catch (balErr) {
|
|
41
|
+
return {
|
|
42
|
+
success: false,
|
|
43
|
+
protocol,
|
|
44
|
+
error: balErr instanceof Error ? balErr.message : String(balErr),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
34
49
|
switch (protocol) {
|
|
35
50
|
case "x402": {
|
|
36
51
|
const result = await handleX402Payment(wallet, agent, task);
|