@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 +107 -0
- package/dist/commands/agent.d.ts +2 -0
- package/dist/commands/agent.js +113 -0
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +63 -0
- package/dist/commands/bounty.d.ts +2 -0
- package/dist/commands/bounty.js +187 -0
- package/dist/commands/dashboard.d.ts +2 -0
- package/dist/commands/dashboard.js +195 -0
- package/dist/commands/treasury.d.ts +2 -0
- package/dist/commands/treasury.js +106 -0
- package/dist/commands/workspace.d.ts +2 -0
- package/dist/commands/workspace.js +96 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +22 -0
- package/dist/utils/client.d.ts +12 -0
- package/dist/utils/client.js +55 -0
- package/dist/utils/config.d.ts +8 -0
- package/dist/utils/config.js +69 -0
- package/dist/utils/format.d.ts +13 -0
- package/dist/utils/format.js +107 -0
- package/dist/utils/websocket.d.ts +12 -0
- package/dist/utils/websocket.js +74 -0
- package/package.json +50 -0
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,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,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,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,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
|
+
}
|