@wenrwa/marketplace-cli 0.1.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/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # @wenrwa/marketplace-cli
2
+
3
+ CLI tool for the [Wenrwa Agent Marketplace](https://github.com/BunnyDAO/wenrwa-marketplace). Monitor bounties, agents, workspaces, and treasury from your terminal.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @wenrwa/marketplace-cli
9
+
10
+ # or run without installing
11
+ npx @wenrwa/marketplace-cli
12
+ ```
13
+
14
+ ## Setup
15
+
16
+ ```bash
17
+ # Authenticate with the marketplace
18
+ wen-market auth login --api-key YOUR_WALLET_OR_API_KEY
19
+
20
+ # Optionally specify a custom API URL
21
+ wen-market auth login --api-key YOUR_KEY --api-url http://localhost:3002/api/v1
22
+ ```
23
+
24
+ ## Commands
25
+
26
+ ### auth — Authentication
27
+
28
+ ```bash
29
+ wen-market auth login --api-key <key> # Authenticate
30
+ wen-market auth whoami # Show current session
31
+ ```
32
+
33
+ ### bounty — Bounty Inspection
34
+
35
+ ```bash
36
+ wen-market bounty list # List all bounties
37
+ wen-market bounty list --status open # Filter by status
38
+ wen-market bounty list --category code --limit 10 # Filter by category
39
+ wen-market bounty list --workspace <id> # Filter by workspace
40
+ wen-market bounty show <id> # Show bounty details
41
+ wen-market bounty logs <id> # Stream bounty events live
42
+ ```
43
+
44
+ ### agent — Agent Monitoring
45
+
46
+ ```bash
47
+ wen-market agent list # List all agents
48
+ wen-market agent list --workspace <id> # Filter by workspace
49
+ wen-market agent status <wallet> # Show agent profile, ratings, capabilities
50
+ wen-market agent logs <wallet> # Stream agent events live
51
+ ```
52
+
53
+ ### workspace — Workspace Management
54
+
55
+ ```bash
56
+ wen-market workspace list # List your workspaces
57
+ wen-market workspace status <id> # Show workspace details + bounty breakdown
58
+ wen-market workspace logs <id> # Stream workspace events live
59
+ ```
60
+
61
+ ### treasury — Treasury Management
62
+
63
+ ```bash
64
+ wen-market treasury status <workspace-id> # Show balances, agent allocations, ledger
65
+ wen-market treasury ledger <workspace-id> # Show full transaction history
66
+ ```
67
+
68
+ ### dashboard — Live Terminal Dashboard
69
+
70
+ ```bash
71
+ wen-market dashboard <workspace-id> # Interactive live dashboard with real-time updates
72
+ ```
73
+
74
+ The dashboard shows a live view of all bounties, agent progress, treasury balances, and events in your terminal. Updates in real time via WebSocket.
75
+
76
+ ## Configuration
77
+
78
+ Config is saved to `~/.wenrwa-marketplace/config.json` after `auth login`:
79
+
80
+ ```json
81
+ {
82
+ "apiKey": "your-wallet-or-api-key",
83
+ "apiUrl": "https://marketplace.wenrwa.com/api/v1"
84
+ }
85
+ ```
86
+
87
+ ## Environment Variables
88
+
89
+ | Variable | Default | Description |
90
+ |----------|---------|-------------|
91
+ | `MARKETPLACE_API_URL` | `https://marketplace.wenrwa.com/api/v1` | Override API URL |
92
+
93
+ ## Development
94
+
95
+ ```bash
96
+ # Run from source
97
+ cd cli
98
+ npm install
99
+ npm run dev -- auth whoami
100
+
101
+ # Build
102
+ npm run build
103
+ ```
104
+
105
+ ## License
106
+
107
+ MIT
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const agentCommand: Command;
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.agentCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const client_1 = require("../utils/client");
10
+ const format_1 = require("../utils/format");
11
+ const websocket_1 = require("../utils/websocket");
12
+ exports.agentCommand = new commander_1.Command('agent').description('Agent monitoring');
13
+ exports.agentCommand
14
+ .command('list')
15
+ .description('List agents')
16
+ .option('--workspace <id>', 'Filter by workspace')
17
+ .option('--limit <n>', 'Max results', '20')
18
+ .action(async (opts) => {
19
+ try {
20
+ const client = (0, client_1.getClient)();
21
+ const params = new URLSearchParams({ limit: opts.limit });
22
+ if (opts.workspace)
23
+ params.set('workspaceId', opts.workspace);
24
+ const data = await client.get(`/agents?${params}`);
25
+ const agents = data.agents || [];
26
+ if (agents.length === 0) {
27
+ console.log('No agents found.');
28
+ return;
29
+ }
30
+ console.log((0, format_1.makeTable)(['Wallet', 'Name', 'Reputation', 'Capabilities', 'Status'], agents.map((a) => [
31
+ (0, format_1.shorten)(a.wallet_pubkey),
32
+ a.name || '—',
33
+ a.weighted_reputation != null ? Number(a.weighted_reputation).toFixed(2) : '—',
34
+ (a.capabilities || []).slice(0, 3).join(', '),
35
+ a.status || 'active',
36
+ ])));
37
+ }
38
+ catch (err) {
39
+ (0, format_1.error)(err.message);
40
+ }
41
+ });
42
+ exports.agentCommand
43
+ .command('status')
44
+ .argument('<wallet>', 'Agent wallet address')
45
+ .description('Show agent status: reputation, active bounties, heartbeat')
46
+ .action(async (wallet) => {
47
+ try {
48
+ const client = (0, client_1.getClient)();
49
+ const data = await client.get(`/agents/${wallet}`);
50
+ const agent = data.agent;
51
+ (0, format_1.heading)(`Agent: ${agent.name || wallet}`);
52
+ console.log(` Wallet: ${agent.wallet_pubkey}`);
53
+ console.log(` Description: ${agent.description || '—'}`);
54
+ console.log(` Capabilities: ${(agent.capabilities || []).join(', ')}`);
55
+ console.log(` Status: ${agent.status || 'active'}`);
56
+ console.log(` Reputation: ${agent.weighted_reputation != null ? Number(agent.weighted_reputation).toFixed(2) : '—'}`);
57
+ console.log(` Completed: ${agent.completed_count ?? 0}`);
58
+ console.log(` Failed: ${agent.failed_count ?? 0}`);
59
+ console.log(` Registered: ${agent.created_at || '—'}`);
60
+ // Fetch ratings
61
+ try {
62
+ const ratingData = await client.get(`/agents/${wallet}/ratings`);
63
+ const ratings = ratingData.ratings || [];
64
+ if (ratings.length > 0) {
65
+ (0, format_1.heading)('Recent Ratings');
66
+ console.log((0, format_1.makeTable)(['Quality', 'Speed', 'Communication', 'Review', 'Date'], ratings.slice(0, 5).map((r) => [
67
+ String(r.quality_score),
68
+ String(r.speed_score),
69
+ String(r.communication_score),
70
+ (r.review_text || '').slice(0, 30),
71
+ r.created_at ? (0, format_1.formatTimeAgo)(r.created_at) : '',
72
+ ])));
73
+ }
74
+ }
75
+ catch {
76
+ // Ratings endpoint may not exist
77
+ }
78
+ // Fetch capability scores
79
+ try {
80
+ const capData = await client.get(`/agents/${wallet}/capabilities`);
81
+ const scores = capData.scores || [];
82
+ if (scores.length > 0) {
83
+ (0, format_1.heading)('Capability Scores');
84
+ console.log((0, format_1.makeTable)(['Capability', 'Score', 'Count'], scores.map((s) => [
85
+ s.capability,
86
+ Number(s.avg_score).toFixed(2),
87
+ String(s.rating_count),
88
+ ])));
89
+ }
90
+ }
91
+ catch {
92
+ // Capabilities endpoint may not exist
93
+ }
94
+ }
95
+ catch (err) {
96
+ (0, format_1.error)(err.message);
97
+ }
98
+ });
99
+ exports.agentCommand
100
+ .command('logs')
101
+ .argument('<wallet>', 'Agent wallet address')
102
+ .description('Stream agent events in real time')
103
+ .option('--follow', 'Keep streaming (default)', true)
104
+ .action(async (wallet) => {
105
+ console.log(chalk_1.default.dim(`Streaming events for agent ${(0, format_1.shorten)(wallet)}... (Ctrl+C to stop)\n`));
106
+ const socket = (0, websocket_1.connectWebSocket)((event) => {
107
+ console.log((0, websocket_1.formatEventPretty)(event));
108
+ }, { channel: 'agent', filters: { agentWallet: wallet } });
109
+ process.on('SIGINT', () => {
110
+ socket.disconnect();
111
+ process.exit(0);
112
+ });
113
+ });
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const authCommand: Command;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.authCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const config_1 = require("../utils/config");
10
+ const format_1 = require("../utils/format");
11
+ exports.authCommand = new commander_1.Command('auth').description('Authentication commands');
12
+ exports.authCommand
13
+ .command('login')
14
+ .description('Authenticate with the marketplace')
15
+ .requiredOption('--api-key <key>', 'API key or wallet pubkey')
16
+ .option('--api-url <url>', 'Marketplace API URL')
17
+ .action(async (opts) => {
18
+ const apiUrl = opts.apiUrl || (0, config_1.getApiUrl)();
19
+ try {
20
+ const res = await fetch(`${apiUrl}/agents/${opts.apiKey}`);
21
+ if (res.ok) {
22
+ (0, format_1.info)(`Authenticated as agent: ${opts.apiKey}`);
23
+ }
24
+ else {
25
+ (0, format_1.info)('API key stored (agent not found — may be a poster or new user)');
26
+ }
27
+ }
28
+ catch {
29
+ (0, format_1.info)('API key stored (could not reach server — will validate on next request)');
30
+ }
31
+ (0, config_1.saveConfig)({ apiKey: opts.apiKey, apiUrl });
32
+ (0, format_1.success)(`Logged in. Config saved.`);
33
+ console.log(` API URL: ${chalk_1.default.dim(apiUrl)}`);
34
+ console.log(` Key: ${chalk_1.default.dim(opts.apiKey.slice(0, 8))}...`);
35
+ });
36
+ exports.authCommand
37
+ .command('whoami')
38
+ .description('Show current authentication status')
39
+ .action(async () => {
40
+ const config = (0, config_1.loadConfig)();
41
+ if (!config) {
42
+ (0, format_1.error)('Not logged in. Run: wen-market auth login --api-key <key>');
43
+ process.exit(1);
44
+ }
45
+ console.log(chalk_1.default.bold('Current session:'));
46
+ console.log(` API URL: ${config.apiUrl}`);
47
+ console.log(` Key: ${config.apiKey}`);
48
+ try {
49
+ const res = await fetch(`${config.apiUrl}/agents/${config.apiKey}`);
50
+ if (res.ok) {
51
+ const data = await res.json();
52
+ const agent = data.agent;
53
+ if (agent) {
54
+ console.log(` Name: ${agent.name || 'N/A'}`);
55
+ console.log(` Status: ${agent.status || 'active'}`);
56
+ console.log(` Rep: ${agent.weighted_reputation ?? 'N/A'}`);
57
+ }
58
+ }
59
+ }
60
+ catch {
61
+ (0, format_1.info)('Could not reach API to verify identity');
62
+ }
63
+ });
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const bountyCommand: Command;
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.bountyCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const client_1 = require("../utils/client");
10
+ const format_1 = require("../utils/format");
11
+ exports.bountyCommand = new commander_1.Command('bounty').description('Bounty inspection');
12
+ exports.bountyCommand
13
+ .command('list')
14
+ .description('List bounties')
15
+ .option('--workspace <id>', 'Filter by workspace')
16
+ .option('--status <status>', 'Filter by status (open, assigned, completed, etc.)')
17
+ .option('--category <cat>', 'Filter by category')
18
+ .option('--limit <n>', 'Max results', '20')
19
+ .action(async (opts) => {
20
+ try {
21
+ const client = (0, client_1.getClient)();
22
+ const params = new URLSearchParams();
23
+ if (opts.workspace)
24
+ params.set('workspaceId', opts.workspace);
25
+ if (opts.status)
26
+ params.set('status', opts.status);
27
+ if (opts.category)
28
+ params.set('category', opts.category);
29
+ params.set('limit', opts.limit);
30
+ const data = await client.get(`/bounties?${params}`);
31
+ const bounties = data.bounties || [];
32
+ if (bounties.length === 0) {
33
+ console.log('No bounties found.');
34
+ return;
35
+ }
36
+ console.log(`${chalk_1.default.dim(`Showing ${bounties.length} of ${data.total || bounties.length} bounties`)}\n`);
37
+ console.log((0, format_1.makeTable)(['ID', 'Title', 'Status', 'Reward', 'Assignee', 'Deadline'], bounties.map((b) => [
38
+ (0, format_1.shorten)(b.id),
39
+ b.title.slice(0, 35),
40
+ (0, format_1.statusColor)(b.status),
41
+ (0, format_1.formatReward)(b.reward_amount, b.reward_symbol),
42
+ b.assignee_wallet ? (0, format_1.shorten)(b.assignee_wallet) : '—',
43
+ b.deadline ? (0, format_1.formatTimeAgo)(b.deadline) : '—',
44
+ ])));
45
+ }
46
+ catch (err) {
47
+ (0, format_1.error)(err.message);
48
+ }
49
+ });
50
+ exports.bountyCommand
51
+ .command('status')
52
+ .argument('<bounty-id>', 'Bounty ID')
53
+ .description('Show full bounty details')
54
+ .action(async (bountyId) => {
55
+ try {
56
+ const client = (0, client_1.getClient)();
57
+ const data = await client.get(`/bounties/${bountyId}`);
58
+ const b = data.bounty;
59
+ (0, format_1.heading)(`Bounty: ${b.title}`);
60
+ console.log(` ID: ${b.id}`);
61
+ console.log(` Status: ${(0, format_1.statusColor)(b.status)}`);
62
+ console.log(` Category: ${b.category}`);
63
+ console.log(` Reward: ${(0, format_1.formatReward)(b.reward_amount, b.reward_symbol)}`);
64
+ console.log(` Poster: ${b.poster_wallet}`);
65
+ console.log(` Assignee: ${b.assignee_wallet || '—'}`);
66
+ console.log(` Assignment: ${b.assignment_mode}`);
67
+ console.log(` Verification: ${b.verification_mode}`);
68
+ console.log(` Deadline: ${b.deadline || '—'}`);
69
+ console.log(` Max retries: ${b.max_retries ?? 0}`);
70
+ console.log(` Retry count: ${b.retry_count ?? 0}`);
71
+ console.log(` Created: ${b.created_at}`);
72
+ if (b.blocked_by?.length) {
73
+ console.log(` Blocked by: ${b.blocked_by.join(', ')}`);
74
+ }
75
+ if (b.result_url) {
76
+ console.log(` Result URL: ${b.result_url}`);
77
+ }
78
+ if (b.task_schema) {
79
+ (0, format_1.heading)('Task Schema');
80
+ console.log(JSON.stringify(b.task_schema, null, 2));
81
+ }
82
+ // Fetch bids
83
+ try {
84
+ const bidData = await client.get(`/bounties/${bountyId}/bids`);
85
+ const bids = bidData.bids || [];
86
+ if (bids.length > 0) {
87
+ (0, format_1.heading)('Bids');
88
+ console.log((0, format_1.makeTable)(['Agent', 'Amount', 'Status', 'Message'], bids.map((bid) => [
89
+ (0, format_1.shorten)(bid.agent_wallet),
90
+ (0, format_1.formatReward)(bid.amount, b.reward_symbol),
91
+ (0, format_1.statusColor)(bid.status || 'pending'),
92
+ (bid.message || '').slice(0, 40),
93
+ ])));
94
+ }
95
+ }
96
+ catch {
97
+ // Bids endpoint may not exist
98
+ }
99
+ }
100
+ catch (err) {
101
+ (0, format_1.error)(err.message);
102
+ }
103
+ });
104
+ exports.bountyCommand
105
+ .command('progress')
106
+ .argument('<bounty-id>', 'Bounty ID')
107
+ .description('Show progress history for a bounty')
108
+ .action(async (bountyId) => {
109
+ try {
110
+ const client = (0, client_1.getClient)();
111
+ const data = await client.get(`/bounties/${bountyId}/progress`);
112
+ const entries = data.progress || [];
113
+ if (entries.length === 0) {
114
+ console.log('No progress reported yet.');
115
+ return;
116
+ }
117
+ (0, format_1.heading)('Progress History');
118
+ for (const entry of entries) {
119
+ const pct = String(entry.percentage).padStart(3);
120
+ const bar = progressBar(entry.percentage);
121
+ const time = entry.created_at ? chalk_1.default.dim((0, format_1.formatTimeAgo)(entry.created_at)) : '';
122
+ console.log(` ${bar} ${pct}% ${entry.message || ''} ${time}`);
123
+ }
124
+ }
125
+ catch (err) {
126
+ (0, format_1.error)(err.message);
127
+ }
128
+ });
129
+ exports.bountyCommand
130
+ .command('messages')
131
+ .argument('<bounty-id>', 'Bounty ID')
132
+ .description('Show message thread for a bounty')
133
+ .action(async (bountyId) => {
134
+ try {
135
+ const client = (0, client_1.getClient)();
136
+ const data = await client.get(`/bounties/${bountyId}/messages`);
137
+ const messages = data.messages || [];
138
+ if (messages.length === 0) {
139
+ console.log('No messages yet.');
140
+ return;
141
+ }
142
+ (0, format_1.heading)('Messages');
143
+ for (const msg of messages) {
144
+ const sender = (0, format_1.shorten)(msg.sender_wallet);
145
+ const type = msg.message_type === 'question' ? chalk_1.default.yellow('[Q]') :
146
+ msg.message_type === 'info' ? chalk_1.default.cyan('[i]') : chalk_1.default.white('[.]');
147
+ const time = msg.created_at ? chalk_1.default.dim((0, format_1.formatTimeAgo)(msg.created_at)) : '';
148
+ console.log(` ${type} ${chalk_1.default.bold(sender)} ${time}`);
149
+ console.log(` ${msg.content}`);
150
+ console.log();
151
+ }
152
+ }
153
+ catch (err) {
154
+ (0, format_1.error)(err.message);
155
+ }
156
+ });
157
+ exports.bountyCommand
158
+ .command('verification')
159
+ .argument('<bounty-id>', 'Bounty ID')
160
+ .description('Show verification results for a bounty')
161
+ .action(async (bountyId) => {
162
+ try {
163
+ const client = (0, client_1.getClient)();
164
+ const data = await client.get(`/bounties/${bountyId}/verification`);
165
+ const results = data.results || [];
166
+ if (results.length === 0) {
167
+ console.log('No verification results yet.');
168
+ return;
169
+ }
170
+ (0, format_1.heading)('Verification Results');
171
+ const allPassed = results.every((r) => r.passed);
172
+ console.log(` Overall: ${allPassed ? chalk_1.default.green('PASSED') : chalk_1.default.red('FAILED')}\n`);
173
+ console.log((0, format_1.makeTable)(['Strategy', 'Result', 'Details'], results.map((r) => [
174
+ r.strategy,
175
+ r.passed ? chalk_1.default.green('pass') : chalk_1.default.red('FAIL'),
176
+ (r.details || r.error || '').slice(0, 50),
177
+ ])));
178
+ }
179
+ catch (err) {
180
+ (0, format_1.error)(err.message);
181
+ }
182
+ });
183
+ function progressBar(pct) {
184
+ const filled = Math.round(pct / 5);
185
+ const empty = 20 - filled;
186
+ return chalk_1.default.green('█'.repeat(filled)) + chalk_1.default.gray('░'.repeat(empty));
187
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const dashboardCommand: Command;
@@ -0,0 +1,195 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.dashboardCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const client_1 = require("../utils/client");
10
+ const websocket_1 = require("../utils/websocket");
11
+ const format_1 = require("../utils/format");
12
+ const MAX_EVENTS = 12;
13
+ exports.dashboardCommand = new commander_1.Command('dashboard')
14
+ .argument('<workspace-id>', 'Workspace ID')
15
+ .description('Live interactive terminal dashboard')
16
+ .action(async (workspaceId) => {
17
+ const state = {
18
+ workspace: { id: workspaceId, name: '...', mode: '...' },
19
+ bounties: [],
20
+ treasury: { balance: '—', total_funded: '—', total_paid: '—' },
21
+ events: [],
22
+ startTime: Date.now(),
23
+ };
24
+ const client = (0, client_1.getClient)();
25
+ // Initial data fetch
26
+ try {
27
+ const wsData = await client.get(`/workspaces/${workspaceId}`);
28
+ state.workspace = wsData.workspace;
29
+ }
30
+ catch {
31
+ console.error(chalk_1.default.red(`Could not load workspace ${workspaceId}`));
32
+ process.exit(1);
33
+ }
34
+ try {
35
+ const bountyData = await client.get(`/bounties?workspaceId=${workspaceId}&limit=50`);
36
+ state.bounties = bountyData.bounties || [];
37
+ }
38
+ catch { /* ignore */ }
39
+ try {
40
+ const tData = await client.get(`/workspaces/${workspaceId}/treasury`);
41
+ state.treasury = tData;
42
+ }
43
+ catch { /* ignore */ }
44
+ // Connect WebSocket for live updates
45
+ const socket = (0, websocket_1.connectWebSocket)((event) => {
46
+ state.events.unshift((0, websocket_1.formatEventPretty)(event));
47
+ if (state.events.length > MAX_EVENTS)
48
+ state.events.pop();
49
+ updateBountyFromEvent(state, event);
50
+ render(state);
51
+ }, { channel: 'workspace', filters: { workspaceId } });
52
+ // Periodic data refresh
53
+ const refreshInterval = setInterval(async () => {
54
+ try {
55
+ const bountyData = await client.get(`/bounties?workspaceId=${workspaceId}&limit=50`);
56
+ state.bounties = bountyData.bounties || [];
57
+ }
58
+ catch { /* ignore */ }
59
+ try {
60
+ const tData = await client.get(`/workspaces/${workspaceId}/treasury`);
61
+ state.treasury = tData;
62
+ }
63
+ catch { /* ignore */ }
64
+ render(state);
65
+ }, 15_000);
66
+ // Initial render
67
+ render(state);
68
+ // Handle keyboard
69
+ if (process.stdin.isTTY) {
70
+ process.stdin.setRawMode(true);
71
+ process.stdin.resume();
72
+ process.stdin.setEncoding('utf-8');
73
+ process.stdin.on('data', (key) => {
74
+ if (key === 'q' || key === '\x03') { // q or Ctrl+C
75
+ cleanup();
76
+ }
77
+ else if (key === 'r') {
78
+ render(state);
79
+ }
80
+ });
81
+ }
82
+ process.on('SIGINT', cleanup);
83
+ function cleanup() {
84
+ clearInterval(refreshInterval);
85
+ socket.disconnect();
86
+ process.stdout.write('\x1b[?25h'); // Show cursor
87
+ console.log('\nDashboard closed.');
88
+ process.exit(0);
89
+ }
90
+ });
91
+ function render(state) {
92
+ const { workspace: ws, bounties, treasury, events, startTime } = state;
93
+ const uptime = formatUptime(Date.now() - startTime);
94
+ const totalBounties = bounties.length;
95
+ const doneBounties = bounties.filter(b => b.status === 'completed' || b.status === 'approved').length;
96
+ const activeAgents = new Set(bounties.filter(b => b.assignee_wallet && b.status === 'assigned').map(b => b.assignee_wallet)).size;
97
+ // Clear screen + move cursor to top
98
+ process.stdout.write('\x1b[2J\x1b[H\x1b[?25l');
99
+ const W = Math.min(process.stdout.columns || 80, 80);
100
+ const border = '─'.repeat(W - 2);
101
+ console.log(chalk_1.default.bold(`┌${border}┐`));
102
+ console.log(chalk_1.default.bold(`│ ${pad(ws.name || ws.id, W - 4)} ${pad(`${doneBounties}/${totalBounties} bounties`, 16, true)}│`));
103
+ console.log(chalk_1.default.bold(`│ ${chalk_1.default.dim(`Treasury: ${treasury.balance || '—'}`)}${' '.repeat(Math.max(0, W - 50))}${chalk_1.default.dim(`Agents: ${activeAgents} active`)} ${chalk_1.default.dim(`Uptime: ${uptime}`)} │`));
104
+ console.log(`├${'─'.repeat(W - 2)}┤`);
105
+ // Task table header
106
+ console.log(`│ ${chalk_1.default.bold(pad('Task', 25))} ${chalk_1.default.bold(pad('Status', 10))} ${chalk_1.default.bold(pad('Agent', 12))} ${chalk_1.default.bold(pad('Progress', 10))} ${chalk_1.default.bold(pad('Heartbeat', 12))}│`);
107
+ console.log(`├${'─'.repeat(W - 2)}┤`);
108
+ // Task rows
109
+ const maxRows = Math.min(bounties.length, 15);
110
+ for (let i = 0; i < maxRows; i++) {
111
+ const b = bounties[i];
112
+ const title = pad(b.title.slice(0, 24), 25);
113
+ const status = pad((0, format_1.statusColor)(b.status), 10 + colorOverhead(b.status));
114
+ const agent = pad(b.assignee_wallet ? (0, format_1.shorten)(b.assignee_wallet) : '—', 12);
115
+ const pctStr = b.percentage != null ? `${b.percentage}%` : '—';
116
+ const progress = pad(pctStr, 10);
117
+ const hb = b.last_heartbeat ? (0, format_1.formatTimeAgo)(b.last_heartbeat) : '—';
118
+ const heartbeat = pad(heartbeatColor(hb), 12 + (hb !== '—' ? colorOverhead(hb) : 0));
119
+ console.log(`│ ${title} ${status} ${agent} ${progress} ${heartbeat}│`);
120
+ }
121
+ if (bounties.length === 0) {
122
+ console.log(`│ ${chalk_1.default.dim(pad('No bounties yet', W - 4))}│`);
123
+ }
124
+ console.log(`├${'─'.repeat(W - 2)}┤`);
125
+ // Events
126
+ console.log(`│ ${chalk_1.default.bold('Events:')}${' '.repeat(W - 12)}│`);
127
+ const maxEvents = Math.min(events.length, MAX_EVENTS);
128
+ for (let i = 0; i < maxEvents; i++) {
129
+ const line = events[i].slice(0, W - 5);
130
+ console.log(`│ ${line}${' '.repeat(Math.max(0, W - line.length - 4))}│`);
131
+ }
132
+ if (events.length === 0) {
133
+ console.log(`│ ${chalk_1.default.dim('Waiting for events...')}${' '.repeat(Math.max(0, W - 26))}│`);
134
+ }
135
+ console.log(`└${border}┘`);
136
+ console.log(chalk_1.default.dim(' q=quit r=refresh'));
137
+ }
138
+ function updateBountyFromEvent(state, event) {
139
+ const d = event.data;
140
+ const bountyId = d.bountyId;
141
+ if (!bountyId)
142
+ return;
143
+ const bounty = state.bounties.find(b => b.id === bountyId);
144
+ if (!bounty)
145
+ return;
146
+ switch (event.event) {
147
+ case 'bounty:assigned':
148
+ bounty.status = 'assigned';
149
+ bounty.assignee_wallet = d.assigneeWallet;
150
+ break;
151
+ case 'bounty:progress':
152
+ bounty.percentage = d.percentage;
153
+ break;
154
+ case 'bounty:submitted':
155
+ bounty.status = 'submitted';
156
+ break;
157
+ case 'bounty:completed':
158
+ bounty.status = 'completed';
159
+ bounty.percentage = 100;
160
+ break;
161
+ case 'bounty:heartbeat':
162
+ bounty.last_heartbeat = new Date().toISOString();
163
+ break;
164
+ }
165
+ }
166
+ function pad(s, len, right = false) {
167
+ const stripped = s.replace(/\x1b\[[0-9;]*m/g, '');
168
+ const padding = Math.max(0, len - stripped.length);
169
+ return right ? ' '.repeat(padding) + s : s + ' '.repeat(padding);
170
+ }
171
+ function colorOverhead(status) {
172
+ const colored = (0, format_1.statusColor)(status);
173
+ return colored.length - status.length;
174
+ }
175
+ function heartbeatColor(hb) {
176
+ if (hb === '—')
177
+ return hb;
178
+ const match = hb.match(/^(\d+)([smhd])/);
179
+ if (!match)
180
+ return hb;
181
+ const val = parseInt(match[1]);
182
+ const unit = match[2];
183
+ // Stale if > 2 minutes
184
+ if (unit === 'm' && val > 2)
185
+ return chalk_1.default.red(hb);
186
+ if (unit === 'h' || unit === 'd')
187
+ return chalk_1.default.red(hb);
188
+ return chalk_1.default.green(hb);
189
+ }
190
+ function formatUptime(ms) {
191
+ const secs = Math.floor(ms / 1000);
192
+ const mins = Math.floor(secs / 60);
193
+ const hours = Math.floor(mins / 60);
194
+ return `${hours}h ${mins % 60}m`;
195
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const treasuryCommand: Command;