moltlaunch 2.0.0 → 2.0.2

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 (108) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +18 -18
  3. package/dist/index.js.map +1 -1
  4. package/package.json +6 -2
  5. package/.claude/commands/deploy.md +0 -33
  6. package/.claude/hooks/regenerate-docs.sh +0 -12
  7. package/.claude/settings.json +0 -15
  8. package/.env.example +0 -2
  9. package/.github/workflows/deploy.yml +0 -37
  10. package/ROADMAP.md +0 -29
  11. package/contracts/MandateEscrowV4.sol +0 -281
  12. package/contracts/mocks/MockFlaunchBuyback.sol +0 -24
  13. package/hardhat.config.cjs +0 -29
  14. package/scripts/check-deploy-cost.ts +0 -15
  15. package/scripts/deploy-escrow-v4.ts +0 -81
  16. package/scripts/deploy-escrow.cjs +0 -22
  17. package/scripts/generate-docs.ts +0 -309
  18. package/shared/manifest.json +0 -87
  19. package/site/.vscode/extensions.json +0 -4
  20. package/site/.vscode/launch.json +0 -11
  21. package/site/README.md +0 -43
  22. package/site/astro.config.mjs +0 -21
  23. package/site/functions/agent/[[path]].ts +0 -9
  24. package/site/functions/task/[[path]].ts +0 -9
  25. package/site/index.html.bak +0 -1755
  26. package/site/package-lock.json +0 -6165
  27. package/site/package.json +0 -17
  28. package/site/public/_redirects +0 -1
  29. package/site/public/art/hero.webp +0 -0
  30. package/site/public/favicon.ico +0 -0
  31. package/site/public/favicon.svg +0 -4
  32. package/site/public/logo.png +0 -0
  33. package/site/public/skill.md +0 -276
  34. package/site/src/components/AgentGridCard.astro +0 -97
  35. package/site/src/components/AgentRow.astro +0 -75
  36. package/site/src/components/Footer.astro +0 -71
  37. package/site/src/components/GigCard.astro +0 -36
  38. package/site/src/components/Navbar.astro +0 -93
  39. package/site/src/components/ReviewCard.astro +0 -29
  40. package/site/src/components/SkillPill.astro +0 -19
  41. package/site/src/components/StatusBadge.astro +0 -27
  42. package/site/src/components/TaskEntry.astro +0 -98
  43. package/site/src/layouts/Layout.astro +0 -268
  44. package/site/src/lib/api.ts +0 -342
  45. package/site/src/pages/404.astro +0 -33
  46. package/site/src/pages/admin.astro +0 -445
  47. package/site/src/pages/agent/[...id].astro +0 -678
  48. package/site/src/pages/agents/index.astro +0 -235
  49. package/site/src/pages/dashboard.astro +0 -244
  50. package/site/src/pages/docs.astro +0 -191
  51. package/site/src/pages/how.astro +0 -156
  52. package/site/src/pages/index.astro +0 -226
  53. package/site/src/pages/leaderboard.astro +0 -155
  54. package/site/src/pages/task/[...id].astro +0 -1467
  55. package/site/src/styles/global.css +0 -159
  56. package/site/tailwind.config.mjs +0 -94
  57. package/site/tsconfig.json +0 -5
  58. package/site/wrangler.toml +0 -5
  59. package/src/commands/accept.ts +0 -135
  60. package/src/commands/agents.ts +0 -190
  61. package/src/commands/approve.ts +0 -127
  62. package/src/commands/claim.ts +0 -130
  63. package/src/commands/decline.ts +0 -55
  64. package/src/commands/dispute.ts +0 -92
  65. package/src/commands/earnings.ts +0 -86
  66. package/src/commands/feedback.ts +0 -147
  67. package/src/commands/gig.ts +0 -141
  68. package/src/commands/hire.ts +0 -96
  69. package/src/commands/inbox.ts +0 -135
  70. package/src/commands/message.ts +0 -97
  71. package/src/commands/profile.ts +0 -62
  72. package/src/commands/quote.ts +0 -80
  73. package/src/commands/refund.ts +0 -82
  74. package/src/commands/register.ts +0 -250
  75. package/src/commands/resolve.ts +0 -104
  76. package/src/commands/reviews.ts +0 -78
  77. package/src/commands/revise.ts +0 -65
  78. package/src/commands/submit.ts +0 -123
  79. package/src/commands/tasks.ts +0 -224
  80. package/src/commands/view.ts +0 -122
  81. package/src/commands/wallet.ts +0 -42
  82. package/src/index.ts +0 -285
  83. package/src/lib/agent0.ts +0 -158
  84. package/src/lib/auth.ts +0 -25
  85. package/src/lib/constants.ts +0 -55
  86. package/src/lib/escrow.ts +0 -374
  87. package/src/lib/files.ts +0 -87
  88. package/src/lib/flaunch.ts +0 -277
  89. package/src/lib/mandate.ts +0 -623
  90. package/src/lib/tasks.ts +0 -466
  91. package/src/lib/types.ts +0 -112
  92. package/src/lib/wallet.ts +0 -119
  93. package/src/lib/x402.ts +0 -86
  94. package/test/MandateEscrowV4.test.cjs +0 -568
  95. package/tsconfig.json +0 -19
  96. package/tsup.config.ts +0 -15
  97. package/worker/package-lock.json +0 -1812
  98. package/worker/package.json +0 -18
  99. package/worker/src/agents.ts +0 -755
  100. package/worker/src/auth.ts +0 -126
  101. package/worker/src/files.ts +0 -40
  102. package/worker/src/index.ts +0 -963
  103. package/worker/src/profiles.ts +0 -85
  104. package/worker/src/ratelimit.ts +0 -45
  105. package/worker/src/tasks.ts +0 -498
  106. package/worker/src/types.ts +0 -95
  107. package/worker/tsconfig.json +0 -15
  108. 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
- }