moltlaunch 2.0.0 → 2.0.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.
Files changed (105) hide show
  1. package/package.json +5 -1
  2. package/.claude/commands/deploy.md +0 -33
  3. package/.claude/hooks/regenerate-docs.sh +0 -12
  4. package/.claude/settings.json +0 -15
  5. package/.env.example +0 -2
  6. package/.github/workflows/deploy.yml +0 -37
  7. package/ROADMAP.md +0 -29
  8. package/contracts/MandateEscrowV4.sol +0 -281
  9. package/contracts/mocks/MockFlaunchBuyback.sol +0 -24
  10. package/hardhat.config.cjs +0 -29
  11. package/scripts/check-deploy-cost.ts +0 -15
  12. package/scripts/deploy-escrow-v4.ts +0 -81
  13. package/scripts/deploy-escrow.cjs +0 -22
  14. package/scripts/generate-docs.ts +0 -309
  15. package/shared/manifest.json +0 -87
  16. package/site/.vscode/extensions.json +0 -4
  17. package/site/.vscode/launch.json +0 -11
  18. package/site/README.md +0 -43
  19. package/site/astro.config.mjs +0 -21
  20. package/site/functions/agent/[[path]].ts +0 -9
  21. package/site/functions/task/[[path]].ts +0 -9
  22. package/site/index.html.bak +0 -1755
  23. package/site/package-lock.json +0 -6165
  24. package/site/package.json +0 -17
  25. package/site/public/_redirects +0 -1
  26. package/site/public/art/hero.webp +0 -0
  27. package/site/public/favicon.ico +0 -0
  28. package/site/public/favicon.svg +0 -4
  29. package/site/public/logo.png +0 -0
  30. package/site/public/skill.md +0 -276
  31. package/site/src/components/AgentGridCard.astro +0 -97
  32. package/site/src/components/AgentRow.astro +0 -75
  33. package/site/src/components/Footer.astro +0 -71
  34. package/site/src/components/GigCard.astro +0 -36
  35. package/site/src/components/Navbar.astro +0 -93
  36. package/site/src/components/ReviewCard.astro +0 -29
  37. package/site/src/components/SkillPill.astro +0 -19
  38. package/site/src/components/StatusBadge.astro +0 -27
  39. package/site/src/components/TaskEntry.astro +0 -98
  40. package/site/src/layouts/Layout.astro +0 -268
  41. package/site/src/lib/api.ts +0 -342
  42. package/site/src/pages/404.astro +0 -33
  43. package/site/src/pages/admin.astro +0 -445
  44. package/site/src/pages/agent/[...id].astro +0 -678
  45. package/site/src/pages/agents/index.astro +0 -235
  46. package/site/src/pages/dashboard.astro +0 -244
  47. package/site/src/pages/docs.astro +0 -191
  48. package/site/src/pages/how.astro +0 -156
  49. package/site/src/pages/index.astro +0 -226
  50. package/site/src/pages/leaderboard.astro +0 -155
  51. package/site/src/pages/task/[...id].astro +0 -1467
  52. package/site/src/styles/global.css +0 -159
  53. package/site/tailwind.config.mjs +0 -94
  54. package/site/tsconfig.json +0 -5
  55. package/site/wrangler.toml +0 -5
  56. package/src/commands/accept.ts +0 -135
  57. package/src/commands/agents.ts +0 -190
  58. package/src/commands/approve.ts +0 -127
  59. package/src/commands/claim.ts +0 -130
  60. package/src/commands/decline.ts +0 -55
  61. package/src/commands/dispute.ts +0 -92
  62. package/src/commands/earnings.ts +0 -86
  63. package/src/commands/feedback.ts +0 -147
  64. package/src/commands/gig.ts +0 -141
  65. package/src/commands/hire.ts +0 -96
  66. package/src/commands/inbox.ts +0 -135
  67. package/src/commands/message.ts +0 -97
  68. package/src/commands/profile.ts +0 -62
  69. package/src/commands/quote.ts +0 -80
  70. package/src/commands/refund.ts +0 -82
  71. package/src/commands/register.ts +0 -250
  72. package/src/commands/resolve.ts +0 -104
  73. package/src/commands/reviews.ts +0 -78
  74. package/src/commands/revise.ts +0 -65
  75. package/src/commands/submit.ts +0 -123
  76. package/src/commands/tasks.ts +0 -224
  77. package/src/commands/view.ts +0 -122
  78. package/src/commands/wallet.ts +0 -42
  79. package/src/index.ts +0 -285
  80. package/src/lib/agent0.ts +0 -158
  81. package/src/lib/auth.ts +0 -25
  82. package/src/lib/constants.ts +0 -55
  83. package/src/lib/escrow.ts +0 -374
  84. package/src/lib/files.ts +0 -87
  85. package/src/lib/flaunch.ts +0 -277
  86. package/src/lib/mandate.ts +0 -623
  87. package/src/lib/tasks.ts +0 -466
  88. package/src/lib/types.ts +0 -112
  89. package/src/lib/wallet.ts +0 -119
  90. package/src/lib/x402.ts +0 -86
  91. package/test/MandateEscrowV4.test.cjs +0 -568
  92. package/tsconfig.json +0 -19
  93. package/tsup.config.ts +0 -15
  94. package/worker/package-lock.json +0 -1812
  95. package/worker/package.json +0 -18
  96. package/worker/src/agents.ts +0 -755
  97. package/worker/src/auth.ts +0 -126
  98. package/worker/src/files.ts +0 -40
  99. package/worker/src/index.ts +0 -963
  100. package/worker/src/profiles.ts +0 -85
  101. package/worker/src/ratelimit.ts +0 -45
  102. package/worker/src/tasks.ts +0 -498
  103. package/worker/src/types.ts +0 -95
  104. package/worker/tsconfig.json +0 -15
  105. package/worker/wrangler.toml +0 -19
@@ -1,159 +0,0 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
-
5
- /* Base resets */
6
- html, body {
7
- background-color: #0a0a0a;
8
- color-scheme: dark;
9
- }
10
-
11
- html {
12
- scroll-behavior: smooth;
13
- -webkit-font-smoothing: antialiased;
14
- -moz-osx-font-smoothing: grayscale;
15
- overscroll-behavior: none;
16
- }
17
-
18
- body {
19
- text-rendering: optimizeLegibility;
20
- }
21
-
22
- ::selection {
23
- background-color: rgba(255, 51, 51, 0.15);
24
- color: #f0f0f0;
25
- }
26
-
27
- /* Scrollbar — minimal, refined */
28
- ::-webkit-scrollbar {
29
- width: 6px;
30
- height: 6px;
31
- }
32
- ::-webkit-scrollbar-track {
33
- background: transparent;
34
- }
35
- ::-webkit-scrollbar-thumb {
36
- background: #3a3a3a;
37
- border-radius: 3px;
38
- }
39
- ::-webkit-scrollbar-thumb:hover {
40
- background: #555555;
41
- }
42
-
43
- /* Focus ring — refined */
44
- *:focus-visible {
45
- outline: 2px solid #ff3333;
46
- outline-offset: 2px;
47
- border-radius: 4px;
48
- }
49
-
50
- /* --- Utility Classes --- */
51
-
52
- /* Glow effects */
53
- .glow-primary {
54
- box-shadow: 0 0 24px rgba(255, 51, 51, 0.2);
55
- }
56
- .glow-green {
57
- box-shadow: 0 0 24px rgba(34, 197, 94, 0.2);
58
- }
59
-
60
- /* Card hover — premium lift + border glow */
61
- .glow-card {
62
- transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
63
- }
64
- .glow-card:hover {
65
- transform: translateY(-2px);
66
- box-shadow: 0 0 20px rgba(255, 51, 51, 0.08), 0 0 0 1px rgba(255, 51, 51, 0.15);
67
- }
68
-
69
- /* Glass utility */
70
- .glass {
71
- background: rgba(10, 10, 10, 0.85);
72
- backdrop-filter: blur(24px);
73
- -webkit-backdrop-filter: blur(24px);
74
- border-bottom: 1px solid rgba(255, 255, 255, 0.06);
75
- }
76
-
77
- /* Gradient text — hero headlines */
78
- .text-gradient {
79
- background: linear-gradient(135deg, #ff3333 0%, #cc1111 100%);
80
- -webkit-background-clip: text;
81
- -webkit-text-fill-color: transparent;
82
- background-clip: text;
83
- }
84
-
85
- /* Gradient text — subtle variant */
86
- .text-gradient-subtle {
87
- background: linear-gradient(135deg, #f0f0f0 0%, #a0a0a0 50%, #ff3333 100%);
88
- background-size: 200% auto;
89
- -webkit-background-clip: text;
90
- -webkit-text-fill-color: transparent;
91
- background-clip: text;
92
- }
93
-
94
- /* Gradient border — premium card accent */
95
- .gradient-border {
96
- position: relative;
97
- }
98
- .gradient-border::before {
99
- content: '';
100
- position: absolute;
101
- inset: 0;
102
- padding: 1px;
103
- border-radius: inherit;
104
- background: linear-gradient(135deg, rgba(255, 51, 51, 0.25), rgba(204, 17, 17, 0.2), rgba(255, 51, 51, 0.1));
105
- -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
106
- -webkit-mask-composite: xor;
107
- mask-composite: exclude;
108
- pointer-events: none;
109
- }
110
-
111
- /* Noise texture for dark sections */
112
- .noise-bg {
113
- position: relative;
114
- }
115
- .noise-bg::after {
116
- content: '';
117
- position: absolute;
118
- inset: 0;
119
- opacity: 0.04;
120
- background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
121
- pointer-events: none;
122
- z-index: 1;
123
- }
124
-
125
- /* Shimmer loading effect */
126
- .shimmer-bg {
127
- background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.05) 50%, transparent 100%);
128
- background-size: 200% 100%;
129
- animation: shimmer 2s linear infinite;
130
- }
131
-
132
- /* Section divider — gradient line */
133
- .section-divider {
134
- height: 1px;
135
- background: linear-gradient(90deg, transparent, #2a2a2a, transparent);
136
- margin-top: -1px;
137
- }
138
-
139
- /* Stagger animation delays */
140
- .stagger-1 { animation-delay: 80ms; }
141
- .stagger-2 { animation-delay: 160ms; }
142
- .stagger-3 { animation-delay: 240ms; }
143
- .stagger-4 { animation-delay: 320ms; }
144
- .stagger-5 { animation-delay: 400ms; }
145
- .stagger-6 { animation-delay: 480ms; }
146
- .stagger-7 { animation-delay: 560ms; }
147
- .stagger-8 { animation-delay: 640ms; }
148
-
149
- /* Start hidden for stagger animations */
150
- [data-animate] {
151
- opacity: 0;
152
- }
153
-
154
- /* Smooth transitions for all interactive elements */
155
- a, button, input, textarea, select {
156
- transition-property: color, background-color, border-color, box-shadow, opacity, transform;
157
- transition-duration: 200ms;
158
- transition-timing-function: ease;
159
- }
@@ -1,94 +0,0 @@
1
- /** @type {import('tailwindcss').Config} */
2
- export default {
3
- content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
4
- theme: {
5
- extend: {
6
- colors: {
7
- bg: '#0a0a0a',
8
- surface: '#181818',
9
- 'surface-2': '#222222',
10
- 'surface-3': '#2c2c2c',
11
- border: '#2a2a2a',
12
- 'border-hover': '#3a3a3a',
13
- text: '#efefef',
14
- 'text-dim': '#b0b0b0',
15
- 'text-muted': '#787878',
16
- primary: '#ff3333',
17
- 'primary-hover': '#ff4d4d',
18
- 'primary-soft': 'rgba(255, 51, 51, 0.08)',
19
- accent: '#ff8800',
20
- red: '#ef4444',
21
- 'red-glow': 'rgba(239, 68, 68, 0.25)',
22
- green: '#22c55e',
23
- 'green-glow': 'rgba(34, 197, 94, 0.25)',
24
- blue: '#3b82f6',
25
- 'blue-glow': 'rgba(59, 130, 246, 0.25)',
26
- yellow: '#eab308',
27
- deep: '#0a0a0a',
28
- },
29
- fontFamily: {
30
- sans: ['Space Grotesk', 'system-ui', 'sans-serif'],
31
- mono: ['IBM Plex Mono', 'monospace'],
32
- },
33
- borderRadius: {
34
- lg: '0.5rem',
35
- xl: '0.75rem',
36
- '2xl': '1rem',
37
- '3xl': '1.5rem',
38
- },
39
- boxShadow: {
40
- 'card': '0 1px 2px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.03)',
41
- 'card-hover': '0 0 20px rgba(255,51,51,0.08), 0 0 0 1px rgba(255,51,51,0.15)',
42
- 'glow': '0 0 24px rgba(255,51,51,0.2)',
43
- 'glow-lg': '0 0 48px rgba(255,51,51,0.25)',
44
- 'inner-glow': 'inset 0 1px 0 rgba(255,255,255,0.04)',
45
- },
46
- keyframes: {
47
- 'fade-in': {
48
- '0%': { opacity: '0', transform: 'translateY(8px)' },
49
- '100%': { opacity: '1', transform: 'translateY(0)' },
50
- },
51
- 'fade-in-up': {
52
- '0%': { opacity: '0', transform: 'translateY(20px)' },
53
- '100%': { opacity: '1', transform: 'translateY(0)' },
54
- },
55
- 'pulse-glow': {
56
- '0%, 100%': { boxShadow: '0 0 8px var(--tw-shadow-color, rgba(255,51,51,0.2))' },
57
- '50%': { boxShadow: '0 0 24px var(--tw-shadow-color, rgba(255,51,51,0.35))' },
58
- },
59
- 'slide-in-right': {
60
- '0%': { opacity: '0', transform: 'translateX(20px)' },
61
- '100%': { opacity: '1', transform: 'translateX(0)' },
62
- },
63
- 'gradient-shift': {
64
- '0%': { backgroundPosition: '0% 50%' },
65
- '50%': { backgroundPosition: '100% 50%' },
66
- '100%': { backgroundPosition: '0% 50%' },
67
- },
68
- 'float': {
69
- '0%, 100%': { transform: 'translateY(0)' },
70
- '50%': { transform: 'translateY(-6px)' },
71
- },
72
- 'shimmer': {
73
- '0%': { backgroundPosition: '-200% 0' },
74
- '100%': { backgroundPosition: '200% 0' },
75
- },
76
- 'ember-pulse': {
77
- '0%, 100%': { opacity: '0.4', transform: 'scale(1)' },
78
- '50%': { opacity: '0.7', transform: 'scale(1.05)' },
79
- },
80
- },
81
- animation: {
82
- 'fade-in': 'fade-in 0.3s ease-out forwards',
83
- 'fade-in-up': 'fade-in-up 0.5s ease-out forwards',
84
- 'pulse-glow': 'pulse-glow 2s ease-in-out infinite',
85
- 'slide-in-right': 'slide-in-right 0.3s ease-out forwards',
86
- 'gradient-shift': 'gradient-shift 3s ease infinite',
87
- 'float': 'float 3s ease-in-out infinite',
88
- 'shimmer': 'shimmer 2s linear infinite',
89
- 'ember-pulse': 'ember-pulse 3s ease-in-out infinite',
90
- },
91
- },
92
- },
93
- plugins: [],
94
- };
@@ -1,5 +0,0 @@
1
- {
2
- "extends": "astro/tsconfigs/strict",
3
- "include": [".astro/types.d.ts", "**/*"],
4
- "exclude": ["dist"]
5
- }
@@ -1,5 +0,0 @@
1
- name = "moltlaunch"
2
- pages_build_output_dir = "dist"
3
-
4
- # Deploys to moltlaunch.com (custom domain already configured)
5
- # Deploy: npm run build && npx wrangler pages deploy dist --project-name moltlaunch
@@ -1,135 +0,0 @@
1
- // mltl accept - Accept an agent's quote (client action)
2
- // Deposits funds into escrow, then marks task as accepted
3
-
4
- import { formatEther, parseEther } from "viem";
5
- import { loadOrCreateWallet, getWalletBalance } from "../lib/wallet.js";
6
- import { acceptQuote, getTask } from "../lib/tasks.js";
7
- import { depositEscrow } from "../lib/escrow.js";
8
- import { APIS } from "../lib/constants.js";
9
-
10
- interface AcceptOptions {
11
- task: string;
12
- json?: boolean;
13
- }
14
-
15
- // Fetch agent from our worker API (no subgraph needed)
16
- async function fetchAgent(agentId: string) {
17
- const res = await fetch(`${APIS.MANDATE}/api/agents/${agentId}`);
18
- if (!res.ok) return null;
19
- const data = await res.json() as { agent?: { name: string; owner: string; flaunchToken?: string } };
20
- return data.agent;
21
- }
22
-
23
- export async function accept(options: AcceptOptions): Promise<void> {
24
- const { wallet } = await loadOrCreateWallet();
25
-
26
- if (!options.json) {
27
- console.log("\nAccepting quote...");
28
- console.log("──────────────────────────────────────────────");
29
- }
30
-
31
- try {
32
- // First, get the task details
33
- const taskBefore = await getTask(options.task);
34
-
35
- if (taskBefore.status !== "quoted") {
36
- throw new Error(`Task is ${taskBefore.status}, cannot accept (must be quoted)`);
37
- }
38
-
39
- // Verify caller is the client
40
- if (taskBefore.clientAddress.toLowerCase() !== wallet.address.toLowerCase()) {
41
- throw new Error("Only the client who created this task can accept the quote");
42
- }
43
-
44
- const priceEth = taskBefore.quotedPriceWei
45
- ? formatEther(BigInt(taskBefore.quotedPriceWei))
46
- : "0";
47
-
48
- // Get agent details for escrow from our API
49
- const agent = await fetchAgent(taskBefore.agentId);
50
- if (!agent) {
51
- throw new Error(`Agent #${taskBefore.agentId} not found`);
52
- }
53
- const agentOwner = agent.owner;
54
- if (!agentOwner) {
55
- throw new Error("Agent has no owner address");
56
- }
57
-
58
- // V3: Get agent's Flaunch token for buyback-and-burn
59
- const agentToken = agent.flaunchToken;
60
- if (!agentToken) {
61
- throw new Error("Agent has no Flaunch token linked. Cannot deposit to escrow without token for buyback.");
62
- }
63
-
64
- const priceWei = BigInt(taskBefore.quotedPriceWei || "0");
65
-
66
- // Check balance
67
- const balance = await getWalletBalance(wallet.address);
68
- const balanceWei = parseEther(balance);
69
- if (balanceWei < priceWei) {
70
- throw new Error(`Insufficient balance. Need ${priceEth} ETH, have ${balance} ETH`);
71
- }
72
-
73
- if (!options.json) {
74
- console.log(`\nTask ID: ${taskBefore.id}`);
75
- console.log(`Agent: #${taskBefore.agentId} (${agent.name || "Unknown"})`);
76
- console.log(`Owner: ${agentOwner}`);
77
- console.log(`Quote: ${priceEth} ETH`);
78
- if (taskBefore.quotedMessage) {
79
- console.log(`Message: ${taskBefore.quotedMessage}`);
80
- }
81
- console.log(`\nTask:\n${taskBefore.task}\n`);
82
- console.log("──────────────────────────────────────────────");
83
- console.log("\nDepositing funds into escrow...");
84
- }
85
-
86
- // Deposit into escrow contract (V3: includes token for buyback-and-burn)
87
- const escrowTxHash = await depositEscrow(wallet, taskBefore.id, agentOwner, agentToken, priceWei);
88
-
89
- if (!options.json) {
90
- console.log(`Escrow TX: ${escrowTxHash}`);
91
- console.log("Marking task as accepted...");
92
- }
93
-
94
- // Accept the quote in the task queue (signed with wallet)
95
- const task = await acceptQuote(wallet, options.task);
96
-
97
- if (options.json) {
98
- console.log(
99
- JSON.stringify({
100
- success: true,
101
- task: {
102
- id: task.id,
103
- agentId: task.agentId,
104
- status: task.status,
105
- quotedPriceWei: task.quotedPriceWei,
106
- },
107
- escrow: {
108
- txHash: escrowTxHash,
109
- amount: priceEth,
110
- tokenAddress: agentToken,
111
- },
112
- }),
113
- );
114
- return;
115
- }
116
-
117
- console.log("\n✅ Quote accepted! Funds locked in escrow.");
118
- console.log(`\nEscrow: ${priceEth} ETH deposited`);
119
- console.log(`Token: ${agentToken}`);
120
- console.log(`TX: ${escrowTxHash}`);
121
- console.log("\nWhen you approve, funds will buyback & burn the agent's token.");
122
- console.log("Check status with:");
123
- console.log(` mltl tasks\n`);
124
- } catch (err) {
125
- const errorMsg = err instanceof Error ? err.message : String(err);
126
-
127
- if (options.json) {
128
- console.log(JSON.stringify({ error: errorMsg }));
129
- process.exit(1);
130
- }
131
-
132
- console.error(`\n❌ Failed to accept quote: ${errorMsg}`);
133
- process.exit(1);
134
- }
135
- }
@@ -1,190 +0,0 @@
1
- // mltl agents - Browse agents in MANDATE using Agent0 SDK
2
-
3
- import { formatEther } from "viem";
4
- import { searchAgents as searchAgent0Agents } from "../lib/agent0.js";
5
-
6
- interface AgentsOptions {
7
- skill?: string;
8
- sort: string;
9
- limit: string;
10
- json?: boolean;
11
- }
12
-
13
- export async function agents(options: AgentsOptions): Promise<void> {
14
- const limit = parseInt(options.limit, 10);
15
-
16
- if (!options.json) {
17
- console.log("\nMANDATE Agent Network");
18
- console.log("══════════════════════════════════════════════════════════════════\n");
19
- console.log("Fetching registered agents via Agent0 SDK...\n");
20
- }
21
-
22
- try {
23
- // Use Agent0 SDK to search agents
24
- const agentsData = await searchAgent0Agents({
25
- limit: limit * 2, // Fetch more to allow for filtering
26
- });
27
-
28
- if (agentsData.length === 0) {
29
- if (options.json) {
30
- console.log(JSON.stringify({ agents: [], total: 0 }));
31
- return;
32
- }
33
- console.log("No agents registered yet. Be the first!");
34
- console.log("\n mltl register --name 'MyAgent' --skills code,research --endpoint https://...\n");
35
- return;
36
- }
37
-
38
- if (!options.json) {
39
- console.log(`Found ${agentsData.length} registered agents.\n`);
40
- }
41
-
42
- // Transform to our format
43
- let filtered = agentsData.map((agent: any) => ({
44
- agentId: agent.agentId || agent.id,
45
- name: agent.name || `Agent #${agent.agentId || agent.id}`,
46
- description: agent.description || "",
47
- skills: agent.skills || [],
48
- endpoint: agent.endpoint || agent.a2aEndpoint || "",
49
- priceWei: BigInt(agent.price || 0),
50
- owner: agent.owner || "",
51
- agentWallet: agent.wallet || agent.owner || "",
52
- reputation: agent.reputation ? {
53
- count: BigInt(agent.reputation.count || 0),
54
- summaryValue: BigInt(agent.reputation.total || 0),
55
- } : null,
56
- flaunchToken: agent.flaunchToken || null,
57
- }));
58
-
59
- // Filter by skill if specified
60
- if (options.skill) {
61
- const skill = options.skill.toLowerCase();
62
- filtered = filtered.filter((a) =>
63
- a.skills.some((s: string) => s.toLowerCase().includes(skill))
64
- );
65
- }
66
-
67
- // Sort
68
- switch (options.sort) {
69
- case "price":
70
- filtered.sort((a, b) => Number(a.priceWei - b.priceWei));
71
- break;
72
- case "hires":
73
- filtered.sort((a, b) =>
74
- Number((b.reputation?.count || 0n) - (a.reputation?.count || 0n))
75
- );
76
- break;
77
- case "reputation":
78
- default:
79
- filtered.sort((a, b) => {
80
- const aScore = a.reputation && a.reputation.count > 0n
81
- ? Number(a.reputation.summaryValue)
82
- : 0;
83
- const bScore = b.reputation && b.reputation.count > 0n
84
- ? Number(b.reputation.summaryValue)
85
- : 0;
86
- return bScore - aScore;
87
- });
88
- }
89
-
90
- // Limit
91
- filtered = filtered.slice(0, limit);
92
-
93
- if (options.json) {
94
- console.log(
95
- JSON.stringify({
96
- agents: filtered.map((a) => ({
97
- agentId: a.agentId.toString(),
98
- name: a.name,
99
- description: a.description,
100
- skills: a.skills,
101
- endpoint: a.endpoint,
102
- priceWei: a.priceWei.toString(),
103
- priceEth: formatEther(a.priceWei),
104
- owner: a.owner,
105
- agentWallet: a.agentWallet,
106
- reputation: a.reputation
107
- ? {
108
- count: a.reputation.count.toString(),
109
- avgScore:
110
- a.reputation.count > 0n
111
- ? Number(a.reputation.summaryValue).toFixed(1)
112
- : "0",
113
- }
114
- : null,
115
- flaunchToken: a.flaunchToken,
116
- })),
117
- total: agentsData.length,
118
- filtered: filtered.length,
119
- filter: options.skill || null,
120
- sort: options.sort,
121
- })
122
- );
123
- return;
124
- }
125
-
126
- if (filtered.length === 0) {
127
- console.log("No agents match your criteria.");
128
- if (options.skill) {
129
- console.log(`Try without --skill filter to see all agents.`);
130
- }
131
- return;
132
- }
133
-
134
- // Display table
135
- console.log(
136
- `${"ID".padEnd(6)} ${"Name".padEnd(20)} ${"Skills".padEnd(20)} ${"Price".padEnd(10)} ${"Rating".padEnd(12)}`
137
- );
138
- console.log("─".repeat(70));
139
-
140
- for (const agent of filtered) {
141
- const avgScore =
142
- agent.reputation && agent.reputation.count > 0n
143
- ? Number(agent.reputation.summaryValue)
144
- : 0;
145
- const ratingStr =
146
- agent.reputation && agent.reputation.count > 0n
147
- ? `${avgScore}/100 (${agent.reputation.count})`
148
- : "No reviews";
149
-
150
- const priceStr = agent.priceWei > 0n ? formatEther(agent.priceWei) : "Free";
151
-
152
- console.log(
153
- `${`#${agent.agentId}`.padEnd(6)} ${agent.name.slice(0, 19).padEnd(20)} ${agent.skills.join(",").slice(0, 19).padEnd(20)} ${priceStr.padEnd(10)} ${ratingStr}`
154
- );
155
- }
156
-
157
- console.log("─".repeat(70));
158
- console.log(
159
- `\nShowing ${filtered.length} of ${agentsData.length} agents.`
160
- );
161
- console.log(`Use --skill <skill> to filter, --sort <reputation|price|hires> to reorder.`);
162
- console.log(`\nTo hire an agent: mltl hire --agent <ID> --task "..."`);
163
-
164
- } catch (err) {
165
- const errorMsg = err instanceof Error ? err.message : String(err);
166
- const isRpcError = errorMsg.includes("503") || errorMsg.includes("429") || errorMsg.includes("backend");
167
-
168
- if (options.json) {
169
- console.log(
170
- JSON.stringify({
171
- error: errorMsg,
172
- hint: isRpcError ? "Set BASE_RPC_URL env var to a reliable RPC (Alchemy, QuickNode)" : undefined,
173
- })
174
- );
175
- process.exit(1);
176
- }
177
-
178
- if (isRpcError) {
179
- console.error(`\n❌ RPC request failed (public Base RPC is overloaded)`);
180
- console.error(`\nFor reliable access, set BASE_RPC_URL environment variable:`);
181
- console.error(` export BASE_RPC_URL="https://base-mainnet.g.alchemy.com/v2/YOUR_KEY"`);
182
- console.error(`\nGet a free key at: https://alchemy.com`);
183
- } else {
184
- console.error(
185
- `\n❌ Failed to fetch agents: ${errorMsg}`
186
- );
187
- }
188
- process.exit(1);
189
- }
190
- }