kompass-sdk 0.4.0 → 0.6.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.
@@ -1,86 +1,103 @@
1
1
  /**
2
- * x402 Ecosystem Source Adapter
3
- * Fetches known x402 endpoints from the x402.org ecosystem
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
- // Seed list of known x402 services (from x402.org/ecosystem + x402scan)
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 Ecosystem",
13
+ displayName: "402 Index (x402 + L402 + MPP)",
27
14
 
28
15
  async search(query: string, options?: SourceSearchOptions): Promise<UnifiedAgent[]> {
29
- // Also try to fetch from x402.org ecosystem page
30
- let dynamicServices: typeof KNOWN_X402_SERVICES = [];
16
+ const limit = options?.limit ?? 20;
17
+ const timeout = options?.timeout ?? 10000;
18
+
31
19
  try {
32
- const res = await fetch("https://www.x402.org/ecosystem", {
33
- signal: AbortSignal.timeout(options?.timeout ?? 5000),
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
- if (res.ok) {
36
- const html = await res.text();
37
- // Extract any JSON data from the page
38
- const jsonMatch = html.match(/\[{.*"name".*}\]/s);
39
- if (jsonMatch) {
40
- dynamicServices = JSON.parse(jsonMatch[0]);
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
- // Fall back to seed list
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
- return true; // Seed list always available
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 mapX402Service(service: (typeof KNOWN_X402_SERVICES)[0]): UnifiedAgent {
55
+ function mapService(service: any): UnifiedAgent {
56
+ const protocol = mapProtocol(service.protocol);
57
+
69
58
  return {
70
- id: `x402-ecosystem:${service.name.toLowerCase().replace(/\s+/g, "-")}`,
71
- nativeId: service.name,
72
- name: service.name,
73
- description: service.description,
74
- categories: service.categories,
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: "x402",
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: { model: "per-call", currency: "USDC" },
83
- verified: true,
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> {
@@ -54,7 +54,7 @@ export async function handleACPPayment(
54
54
  [
55
55
  "acp", "job", "create",
56
56
  walletAddress,
57
- String(offering.id),
57
+ String(offering.name ?? offering.id),
58
58
  "--requirements", requirements,
59
59
  "--isAutomated", "true",
60
60
  "--json",
@@ -67,19 +67,64 @@ export async function handleACPPayment(
67
67
  );
68
68
 
69
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
+ }
70
114
 
71
115
  return {
72
- success: true,
116
+ success: finalPhase === "COMPLETED",
73
117
  deliverable: {
74
118
  type: "acp-escrow",
75
- jobId: jobData.jobId ?? jobData.id,
76
- status: "created",
77
- message: "ACP escrow job created with ERC-8183. Agent will process and deliver.",
119
+ jobId: String(jobId),
120
+ phase: finalPhase,
121
+ message: finalPhase === "COMPLETED" ? "Job completed" : `Job ended with phase: ${finalPhase}`,
78
122
  offering: offering.name,
79
123
  price: offering.price,
80
124
  agent: agent.name,
125
+ deliverable,
81
126
  },
82
- jobId: String(jobData.jobId ?? jobData.id),
127
+ jobId: String(jobId),
83
128
  };
84
129
  } catch (err) {
85
130
  const message = err instanceof Error ? err.message : String(err);