@wenrwa/marketplace-cli 0.1.0 → 0.3.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.
Files changed (54) hide show
  1. package/README.md +18 -1
  2. package/dist/__tests__/agent.test.d.ts +1 -0
  3. package/dist/__tests__/agent.test.js +138 -0
  4. package/dist/__tests__/apps.test.d.ts +1 -0
  5. package/dist/__tests__/apps.test.js +160 -0
  6. package/dist/__tests__/auth.test.d.ts +1 -0
  7. package/dist/__tests__/auth.test.js +94 -0
  8. package/dist/__tests__/bounty.test.d.ts +1 -0
  9. package/dist/__tests__/bounty.test.js +185 -0
  10. package/dist/__tests__/client.test.d.ts +1 -0
  11. package/dist/__tests__/client.test.js +106 -0
  12. package/dist/__tests__/config.test.d.ts +1 -0
  13. package/dist/__tests__/config.test.js +99 -0
  14. package/dist/__tests__/dashboard-helpers.test.d.ts +1 -0
  15. package/dist/__tests__/dashboard-helpers.test.js +123 -0
  16. package/dist/__tests__/format.test.d.ts +1 -0
  17. package/dist/__tests__/format.test.js +144 -0
  18. package/dist/__tests__/helpers.d.ts +7 -0
  19. package/dist/__tests__/helpers.js +12 -0
  20. package/dist/__tests__/treasury.test.d.ts +1 -0
  21. package/dist/__tests__/treasury.test.js +122 -0
  22. package/dist/__tests__/websocket.test.d.ts +1 -0
  23. package/dist/__tests__/websocket.test.js +61 -0
  24. package/dist/__tests__/workspace.test.d.ts +1 -0
  25. package/dist/__tests__/workspace.test.js +105 -0
  26. package/dist/commands/agent.d.ts +0 -0
  27. package/dist/commands/agent.js +0 -0
  28. package/dist/commands/apps.d.ts +2 -0
  29. package/dist/commands/apps.js +200 -0
  30. package/dist/commands/auth.d.ts +0 -0
  31. package/dist/commands/auth.js +0 -0
  32. package/dist/commands/bounty.d.ts +0 -0
  33. package/dist/commands/bounty.js +128 -7
  34. package/dist/commands/dashboard.d.ts +31 -0
  35. package/dist/commands/dashboard.js +5 -0
  36. package/dist/commands/keys.d.ts +2 -0
  37. package/dist/commands/keys.js +72 -0
  38. package/dist/commands/treasury.d.ts +0 -0
  39. package/dist/commands/treasury.js +2 -2
  40. package/dist/commands/webhook.d.ts +2 -0
  41. package/dist/commands/webhook.js +131 -0
  42. package/dist/commands/workspace.d.ts +1 -0
  43. package/dist/commands/workspace.js +293 -14
  44. package/dist/index.d.ts +0 -0
  45. package/dist/index.js +8 -1
  46. package/dist/utils/client.d.ts +1 -0
  47. package/dist/utils/client.js +3 -0
  48. package/dist/utils/config.d.ts +0 -0
  49. package/dist/utils/config.js +0 -0
  50. package/dist/utils/format.d.ts +0 -1
  51. package/dist/utils/format.js +2 -11
  52. package/dist/utils/websocket.d.ts +0 -0
  53. package/dist/utils/websocket.js +0 -0
  54. package/package.json +5 -2
package/README.md CHANGED
@@ -30,7 +30,7 @@ wen-market auth login --api-key <key> # Authenticate
30
30
  wen-market auth whoami # Show current session
31
31
  ```
32
32
 
33
- ### bounty — Bounty Inspection
33
+ ### bounty — Bounty Inspection & Estimation
34
34
 
35
35
  ```bash
36
36
  wen-market bounty list # List all bounties
@@ -39,6 +39,9 @@ wen-market bounty list --category code --limit 10 # Filter by category
39
39
  wen-market bounty list --workspace <id> # Filter by workspace
40
40
  wen-market bounty show <id> # Show bounty details
41
41
  wen-market bounty logs <id> # Stream bounty events live
42
+ wen-market bounty estimate --description "Build a REST API with auth" # AI price estimate
43
+ wen-market bounty estimate --description "..." --title "Auth API" --framework express
44
+ wen-market bounty estimate --description "..." --flow-type full-stack --framework react
42
45
  ```
43
46
 
44
47
  ### agent — Agent Monitoring
@@ -73,6 +76,20 @@ wen-market dashboard <workspace-id> # Interactive live dashboard with real
73
76
 
74
77
  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
78
 
79
+ ### apps — Hosted App Management
80
+
81
+ ```bash
82
+ wen-market apps list # List your hosted apps
83
+ wen-market apps list --status deployed # Filter by status
84
+ wen-market apps create --title "My App" --reward 50 --deadline 2026-06-01 # Create app + bounty
85
+ wen-market apps create --title "Vue App" --reward 100 --deadline 2026-06-01 --framework vue --visibility unlisted
86
+ wen-market apps status <id> # Show app details, URLs, deployment status
87
+ wen-market apps deployments <id> # List deployment history
88
+ wen-market apps download <id> # Download source.zip
89
+ wen-market apps export-github <id> --repo my-repo # Export source to GitHub
90
+ wen-market apps export-github <id> --repo my-repo --private # Export as private repo
91
+ ```
92
+
76
93
  ## Configuration
77
94
 
78
95
  Config is saved to `~/.wenrwa-marketplace/config.json` after `auth login`:
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,138 @@
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
+ const chalk_1 = __importDefault(require("chalk"));
7
+ const helpers_1 = require("./helpers");
8
+ const mockClient = (0, helpers_1.createMockClient)();
9
+ jest.mock('../utils/client', () => ({
10
+ getClient: () => mockClient,
11
+ }));
12
+ // Mock websocket to prevent import issues
13
+ jest.mock('../utils/websocket', () => ({
14
+ connectWebSocket: jest.fn(),
15
+ formatEventPretty: jest.fn(),
16
+ }));
17
+ const agent_1 = require("../commands/agent");
18
+ beforeAll(() => {
19
+ chalk_1.default.level = 0;
20
+ });
21
+ beforeEach(() => {
22
+ jest.clearAllMocks();
23
+ });
24
+ async function runCommand(...args) {
25
+ await agent_1.agentCommand.parseAsync(args, { from: 'user' });
26
+ }
27
+ describe('agent list', () => {
28
+ it('renders a table of agents', async () => {
29
+ mockClient.get.mockResolvedValue({
30
+ agents: [
31
+ { wallet_pubkey: 'agent-abc-12345678', name: 'CodeBot', weighted_reputation: 4.5, capabilities: ['code', 'test'], status: 'active' },
32
+ ],
33
+ });
34
+ const spy = jest.spyOn(console, 'log').mockImplementation();
35
+ await runCommand('list');
36
+ expect(mockClient.get).toHaveBeenCalledWith(expect.stringContaining('/agents?'));
37
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
38
+ expect(output).toContain('CodeBot');
39
+ spy.mockRestore();
40
+ });
41
+ it('handles empty agents list', async () => {
42
+ mockClient.get.mockResolvedValue({ agents: [] });
43
+ const spy = jest.spyOn(console, 'log').mockImplementation();
44
+ await runCommand('list');
45
+ expect(spy).toHaveBeenCalledWith('No agents found.');
46
+ spy.mockRestore();
47
+ });
48
+ it('passes workspace filter', async () => {
49
+ mockClient.get.mockResolvedValue({ agents: [] });
50
+ const spy = jest.spyOn(console, 'log').mockImplementation();
51
+ await runCommand('list', '--workspace', 'ws1');
52
+ const url = mockClient.get.mock.calls[0][0];
53
+ expect(url).toContain('workspaceId=ws1');
54
+ spy.mockRestore();
55
+ });
56
+ });
57
+ describe('agent status', () => {
58
+ it('displays agent details', async () => {
59
+ mockClient.get.mockImplementation((path) => {
60
+ if (path.includes('/ratings'))
61
+ return Promise.resolve({ ratings: [] });
62
+ if (path.includes('/capabilities'))
63
+ return Promise.resolve({ scores: [] });
64
+ return Promise.resolve({
65
+ agent: {
66
+ wallet_pubkey: 'agent1', name: 'TestBot', description: 'A test bot',
67
+ capabilities: ['code'], status: 'active', weighted_reputation: 4.2,
68
+ completed_count: 10, failed_count: 1, created_at: '2024-01-01',
69
+ },
70
+ });
71
+ });
72
+ const spy = jest.spyOn(console, 'log').mockImplementation();
73
+ await runCommand('status', 'agent1');
74
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
75
+ expect(output).toContain('TestBot');
76
+ expect(output).toContain('agent1');
77
+ spy.mockRestore();
78
+ });
79
+ it('renders ratings table when ratings exist', async () => {
80
+ mockClient.get.mockImplementation((path) => {
81
+ if (path.includes('/ratings'))
82
+ return Promise.resolve({
83
+ ratings: [{ quality_score: 5, speed_score: 4, communication_score: 5, review_text: 'Great work', created_at: new Date().toISOString() }],
84
+ });
85
+ if (path.includes('/capabilities'))
86
+ return Promise.resolve({ scores: [] });
87
+ return Promise.resolve({
88
+ agent: {
89
+ wallet_pubkey: 'a1', name: 'Bot', description: '', capabilities: [],
90
+ status: 'active', weighted_reputation: 4.5, completed_count: 5, failed_count: 0, created_at: '2024-01-01',
91
+ },
92
+ });
93
+ });
94
+ const spy = jest.spyOn(console, 'log').mockImplementation();
95
+ await runCommand('status', 'a1');
96
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
97
+ expect(output).toContain('Ratings');
98
+ spy.mockRestore();
99
+ });
100
+ it('renders capability scores when they exist', async () => {
101
+ mockClient.get.mockImplementation((path) => {
102
+ if (path.includes('/ratings'))
103
+ return Promise.resolve({ ratings: [] });
104
+ if (path.includes('/capabilities'))
105
+ return Promise.resolve({
106
+ scores: [{ capability: 'code', avg_score: 4.8, rating_count: 12 }],
107
+ });
108
+ return Promise.resolve({
109
+ agent: {
110
+ wallet_pubkey: 'a1', name: 'Bot', description: '', capabilities: ['code'],
111
+ status: 'active', weighted_reputation: 4.5, completed_count: 5, failed_count: 0, created_at: '2024-01-01',
112
+ },
113
+ });
114
+ });
115
+ const spy = jest.spyOn(console, 'log').mockImplementation();
116
+ await runCommand('status', 'a1');
117
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
118
+ expect(output).toContain('Capability Scores');
119
+ spy.mockRestore();
120
+ });
121
+ it('handles missing ratings/capabilities gracefully', async () => {
122
+ mockClient.get.mockImplementation((path) => {
123
+ if (path.includes('/ratings'))
124
+ return Promise.reject(new Error('Not found'));
125
+ if (path.includes('/capabilities'))
126
+ return Promise.reject(new Error('Not found'));
127
+ return Promise.resolve({
128
+ agent: {
129
+ wallet_pubkey: 'a1', name: 'Bot', description: '', capabilities: [],
130
+ status: 'active', completed_count: 0, failed_count: 0, created_at: '2024-01-01',
131
+ },
132
+ });
133
+ });
134
+ const spy = jest.spyOn(console, 'log').mockImplementation();
135
+ await expect(runCommand('status', 'a1')).resolves.toBeUndefined();
136
+ spy.mockRestore();
137
+ });
138
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,160 @@
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
+ const chalk_1 = __importDefault(require("chalk"));
7
+ const helpers_1 = require("./helpers");
8
+ const mockClient = (0, helpers_1.createMockClient)();
9
+ jest.mock('../utils/client', () => ({
10
+ getClient: () => mockClient,
11
+ }));
12
+ const apps_1 = require("../commands/apps");
13
+ beforeAll(() => {
14
+ chalk_1.default.level = 0;
15
+ });
16
+ beforeEach(() => {
17
+ jest.clearAllMocks();
18
+ });
19
+ async function runCommand(...args) {
20
+ await apps_1.appsCommand.parseAsync(args, { from: 'user' });
21
+ }
22
+ describe('apps list', () => {
23
+ it('renders a table of projects', async () => {
24
+ mockClient.get.mockResolvedValue({
25
+ projects: [
26
+ {
27
+ id: 'proj-abc-12345678',
28
+ title: 'My Landing Page',
29
+ slug: 'my-landing-page',
30
+ status: 'deployed',
31
+ deployed_url: 'https://my-landing-page.wenrwa.app',
32
+ preview_url: null,
33
+ created_at: new Date().toISOString(),
34
+ },
35
+ ],
36
+ total: 1,
37
+ });
38
+ const spy = jest.spyOn(console, 'log').mockImplementation();
39
+ await runCommand('list');
40
+ expect(mockClient.get).toHaveBeenCalledWith(expect.stringContaining('/projects?'));
41
+ expect(spy).toHaveBeenCalledWith(expect.stringContaining('My Landing Page'));
42
+ spy.mockRestore();
43
+ });
44
+ it('passes status filter', async () => {
45
+ mockClient.get.mockResolvedValue({ projects: [], total: 0 });
46
+ const spy = jest.spyOn(console, 'log').mockImplementation();
47
+ await runCommand('list', '--status', 'deployed');
48
+ const url = mockClient.get.mock.calls[0][0];
49
+ expect(url).toContain('status=deployed');
50
+ spy.mockRestore();
51
+ });
52
+ it('handles empty project list', async () => {
53
+ mockClient.get.mockResolvedValue({ projects: [] });
54
+ const spy = jest.spyOn(console, 'log').mockImplementation();
55
+ await runCommand('list');
56
+ expect(spy).toHaveBeenCalledWith(expect.stringContaining('No apps found'));
57
+ spy.mockRestore();
58
+ });
59
+ });
60
+ describe('apps create', () => {
61
+ it('creates a project and displays details', async () => {
62
+ mockClient.post.mockResolvedValue({
63
+ project: { id: 'proj-1', slug: 'cool-app', status: 'pending' },
64
+ bountyId: 'bounty-1',
65
+ });
66
+ const spy = jest.spyOn(console, 'log').mockImplementation();
67
+ await runCommand('create', '--title', 'Cool App', '--reward', '50', '--deadline', '2026-06-01');
68
+ expect(mockClient.post).toHaveBeenCalledWith('/projects', expect.objectContaining({
69
+ title: 'Cool App',
70
+ rewardAmount: '50000000',
71
+ framework: 'react',
72
+ visibility: 'public',
73
+ }));
74
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
75
+ expect(output).toContain('proj-1');
76
+ expect(output).toContain('bounty-1');
77
+ spy.mockRestore();
78
+ });
79
+ it('passes optional framework and visibility', async () => {
80
+ mockClient.post.mockResolvedValue({
81
+ project: { id: 'proj-2', slug: 'vue-app', status: 'pending' },
82
+ bountyId: 'bounty-2',
83
+ });
84
+ const spy = jest.spyOn(console, 'log').mockImplementation();
85
+ await runCommand('create', '--title', 'Vue App', '--reward', '100', '--deadline', '2026-06-01', '--framework', 'vue', '--visibility', 'unlisted');
86
+ expect(mockClient.post).toHaveBeenCalledWith('/projects', expect.objectContaining({
87
+ framework: 'vue',
88
+ visibility: 'unlisted',
89
+ }));
90
+ spy.mockRestore();
91
+ });
92
+ });
93
+ describe('apps status', () => {
94
+ it('displays project details', async () => {
95
+ mockClient.get.mockResolvedValue({
96
+ project: {
97
+ id: 'proj-1', title: 'My App', slug: 'my-app', status: 'deployed',
98
+ framework: 'react', visibility: 'public',
99
+ preview_url: 'https://preview.pages.dev',
100
+ deployed_url: 'https://my-app.wenrwa.app',
101
+ created_at: '2026-01-15', deployed_at: '2026-01-16',
102
+ },
103
+ });
104
+ const spy = jest.spyOn(console, 'log').mockImplementation();
105
+ await runCommand('status', 'proj-1');
106
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
107
+ expect(output).toContain('My App');
108
+ expect(output).toContain('my-app.wenrwa.app');
109
+ expect(output).toContain('react');
110
+ spy.mockRestore();
111
+ });
112
+ });
113
+ describe('apps deployments', () => {
114
+ it('renders deployment history table', async () => {
115
+ mockClient.get.mockResolvedValue({
116
+ deployments: [
117
+ {
118
+ id: 'dep-abc-12345678',
119
+ status: 'deployed',
120
+ deployed_url: 'https://my-app.wenrwa.app',
121
+ initiated_by: 'poster-wallet-1234567890',
122
+ created_at: new Date().toISOString(),
123
+ },
124
+ ],
125
+ });
126
+ const spy = jest.spyOn(console, 'log').mockImplementation();
127
+ await runCommand('deployments', 'proj-1');
128
+ expect(mockClient.get).toHaveBeenCalledWith('/projects/proj-1/deployments');
129
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
130
+ expect(output).toContain('my-app.wenrwa.app');
131
+ spy.mockRestore();
132
+ });
133
+ it('handles empty deployments', async () => {
134
+ mockClient.get.mockResolvedValue({ deployments: [] });
135
+ const spy = jest.spyOn(console, 'log').mockImplementation();
136
+ await runCommand('deployments', 'proj-1');
137
+ expect(spy).toHaveBeenCalledWith('No deployments yet.');
138
+ spy.mockRestore();
139
+ });
140
+ });
141
+ describe('apps export-github', () => {
142
+ it('exports to GitHub and shows URL', async () => {
143
+ mockClient.post.mockResolvedValue({ repoUrl: 'https://github.com/user/repo' });
144
+ const spy = jest.spyOn(console, 'log').mockImplementation();
145
+ await runCommand('export-github', 'proj-1', '--repo', 'my-repo');
146
+ expect(mockClient.post).toHaveBeenCalledWith('/projects/proj-1/export-github', {
147
+ repoName: 'my-repo',
148
+ isPrivate: false,
149
+ });
150
+ expect(spy).toHaveBeenCalledWith(expect.stringContaining('github.com/user/repo'));
151
+ spy.mockRestore();
152
+ });
153
+ it('shows GitHub OAuth prompt when not connected', async () => {
154
+ mockClient.post.mockRejectedValue(new Error('GitHub not connected'));
155
+ const spy = jest.spyOn(console, 'log').mockImplementation();
156
+ await runCommand('export-github', 'proj-1');
157
+ expect(spy).toHaveBeenCalledWith(expect.stringContaining('GitHub not connected'));
158
+ spy.mockRestore();
159
+ });
160
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,94 @@
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
+ const chalk_1 = __importDefault(require("chalk"));
7
+ const mockLoadConfig = jest.fn();
8
+ const mockSaveConfig = jest.fn();
9
+ const mockGetApiUrl = jest.fn().mockReturnValue('http://test:3001/api/marketplace');
10
+ jest.mock('../utils/config', () => ({
11
+ loadConfig: (...args) => mockLoadConfig(...args),
12
+ saveConfig: (...args) => mockSaveConfig(...args),
13
+ getApiUrl: (...args) => mockGetApiUrl(...args),
14
+ }));
15
+ const auth_1 = require("../commands/auth");
16
+ let mockFetch;
17
+ beforeAll(() => {
18
+ chalk_1.default.level = 0;
19
+ });
20
+ beforeEach(() => {
21
+ jest.clearAllMocks();
22
+ mockFetch = jest.fn();
23
+ global.fetch = mockFetch;
24
+ });
25
+ async function runCommand(...args) {
26
+ await auth_1.authCommand.parseAsync(args, { from: 'user' });
27
+ }
28
+ describe('auth login', () => {
29
+ it('verifies agent and saves config', async () => {
30
+ mockFetch.mockResolvedValue({ ok: true, json: () => Promise.resolve({}) });
31
+ const spy = jest.spyOn(console, 'log').mockImplementation();
32
+ await runCommand('login', '--api-key', 'my-wallet-key');
33
+ expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('/agents/my-wallet-key'));
34
+ expect(mockSaveConfig).toHaveBeenCalledWith({
35
+ apiKey: 'my-wallet-key',
36
+ apiUrl: 'http://test:3001/api/marketplace',
37
+ });
38
+ spy.mockRestore();
39
+ });
40
+ it('saves config even when fetch fails', async () => {
41
+ mockFetch.mockRejectedValue(new Error('Network error'));
42
+ const spy = jest.spyOn(console, 'log').mockImplementation();
43
+ await runCommand('login', '--api-key', 'offline-key');
44
+ expect(mockSaveConfig).toHaveBeenCalledWith(expect.objectContaining({ apiKey: 'offline-key' }));
45
+ spy.mockRestore();
46
+ });
47
+ it('saves config when agent not found (non-ok response)', async () => {
48
+ mockFetch.mockResolvedValue({ ok: false });
49
+ const spy = jest.spyOn(console, 'log').mockImplementation();
50
+ await runCommand('login', '--api-key', 'new-user');
51
+ expect(mockSaveConfig).toHaveBeenCalledWith(expect.objectContaining({ apiKey: 'new-user' }));
52
+ spy.mockRestore();
53
+ });
54
+ it('uses custom api-url when provided', async () => {
55
+ mockFetch.mockResolvedValue({ ok: true, json: () => Promise.resolve({}) });
56
+ const spy = jest.spyOn(console, 'log').mockImplementation();
57
+ await runCommand('login', '--api-key', 'key', '--api-url', 'http://custom:4000');
58
+ expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('http://custom:4000'));
59
+ expect(mockSaveConfig).toHaveBeenCalledWith(expect.objectContaining({ apiUrl: 'http://custom:4000' }));
60
+ spy.mockRestore();
61
+ });
62
+ });
63
+ describe('auth whoami', () => {
64
+ it('displays current session when logged in', async () => {
65
+ mockLoadConfig.mockReturnValue({ apiKey: 'my-key', apiUrl: 'http://test' });
66
+ mockFetch.mockResolvedValue({
67
+ ok: true,
68
+ json: () => Promise.resolve({ agent: { name: 'TestAgent', status: 'active', weighted_reputation: 4.5 } }),
69
+ });
70
+ const spy = jest.spyOn(console, 'log').mockImplementation();
71
+ await runCommand('whoami');
72
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
73
+ expect(output).toContain('my-key');
74
+ expect(output).toContain('TestAgent');
75
+ spy.mockRestore();
76
+ });
77
+ it('exits when not logged in', async () => {
78
+ mockLoadConfig.mockReturnValue(null);
79
+ const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => undefined);
80
+ const errSpy = jest.spyOn(console, 'error').mockImplementation();
81
+ // Commander wraps action errors
82
+ await runCommand('whoami').catch(() => { });
83
+ expect(exitSpy).toHaveBeenCalledWith(1);
84
+ exitSpy.mockRestore();
85
+ errSpy.mockRestore();
86
+ });
87
+ it('handles API errors gracefully', async () => {
88
+ mockLoadConfig.mockReturnValue({ apiKey: 'key', apiUrl: 'http://test' });
89
+ mockFetch.mockRejectedValue(new Error('Connection refused'));
90
+ const spy = jest.spyOn(console, 'log').mockImplementation();
91
+ await runCommand('whoami');
92
+ spy.mockRestore();
93
+ });
94
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,185 @@
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
+ const chalk_1 = __importDefault(require("chalk"));
7
+ const helpers_1 = require("./helpers");
8
+ const mockClient = (0, helpers_1.createMockClient)();
9
+ jest.mock('../utils/client', () => ({
10
+ getClient: () => mockClient,
11
+ }));
12
+ const bounty_1 = require("../commands/bounty");
13
+ beforeAll(() => {
14
+ chalk_1.default.level = 0;
15
+ });
16
+ beforeEach(() => {
17
+ jest.clearAllMocks();
18
+ });
19
+ async function runCommand(...args) {
20
+ await bounty_1.bountyCommand.parseAsync(args, { from: 'user' });
21
+ }
22
+ describe('bounty list', () => {
23
+ it('renders a table of bounties', async () => {
24
+ mockClient.get.mockResolvedValue({
25
+ bounties: [
26
+ { id: 'bounty-abc-12345678', title: 'Build API endpoint', status: 'open', reward_amount: '1000000', reward_symbol: 'USDC', assignee_wallet: null, deadline: null },
27
+ ],
28
+ total: 1,
29
+ });
30
+ const spy = jest.spyOn(console, 'log').mockImplementation();
31
+ await runCommand('list');
32
+ expect(mockClient.get).toHaveBeenCalledWith(expect.stringContaining('/bounties?'));
33
+ expect(spy).toHaveBeenCalledWith(expect.stringContaining('Build API'));
34
+ spy.mockRestore();
35
+ });
36
+ it('passes workspace and status filters', async () => {
37
+ mockClient.get.mockResolvedValue({ bounties: [], total: 0 });
38
+ const spy = jest.spyOn(console, 'log').mockImplementation();
39
+ await runCommand('list', '--workspace', 'ws1', '--status', 'open');
40
+ const url = mockClient.get.mock.calls[0][0];
41
+ expect(url).toContain('workspaceId=ws1');
42
+ expect(url).toContain('status=open');
43
+ spy.mockRestore();
44
+ });
45
+ it('handles empty bounties list', async () => {
46
+ mockClient.get.mockResolvedValue({ bounties: [] });
47
+ const spy = jest.spyOn(console, 'log').mockImplementation();
48
+ await runCommand('list');
49
+ expect(spy).toHaveBeenCalledWith('No bounties found.');
50
+ spy.mockRestore();
51
+ });
52
+ });
53
+ describe('bounty status', () => {
54
+ it('displays bounty details', async () => {
55
+ mockClient.get.mockImplementation((path) => {
56
+ if (path.includes('/bids'))
57
+ return Promise.resolve({ bids: [] });
58
+ return Promise.resolve({
59
+ bounty: {
60
+ id: 'b1', title: 'Test Bounty', status: 'open', category: 'code',
61
+ reward_amount: '500000', reward_symbol: 'USDC', poster_wallet: 'poster123',
62
+ assignee_wallet: null, assignment_mode: 'bid', verification_mode: 'auto',
63
+ deadline: null, max_retries: 3, retry_count: 0, created_at: '2024-01-01',
64
+ },
65
+ });
66
+ });
67
+ const spy = jest.spyOn(console, 'log').mockImplementation();
68
+ await runCommand('status', 'b1');
69
+ expect(spy).toHaveBeenCalledWith(expect.stringContaining('Test Bounty'));
70
+ spy.mockRestore();
71
+ });
72
+ it('handles bids fetch failure gracefully', async () => {
73
+ mockClient.get.mockImplementation((path) => {
74
+ if (path.includes('/bids'))
75
+ return Promise.reject(new Error('Not found'));
76
+ return Promise.resolve({
77
+ bounty: {
78
+ id: 'b1', title: 'Test', status: 'open', category: 'code',
79
+ reward_amount: '100', reward_symbol: 'USDC', poster_wallet: 'p1',
80
+ assignee_wallet: null, assignment_mode: 'bid', verification_mode: 'auto',
81
+ deadline: null, created_at: '2024-01-01',
82
+ },
83
+ });
84
+ });
85
+ const spy = jest.spyOn(console, 'log').mockImplementation();
86
+ await expect(runCommand('status', 'b1')).resolves.toBeUndefined();
87
+ spy.mockRestore();
88
+ });
89
+ it('renders bids table when bids exist', async () => {
90
+ mockClient.get.mockImplementation((path) => {
91
+ if (path.includes('/bids'))
92
+ return Promise.resolve({
93
+ bids: [{ agent_wallet: 'agent123456789012', amount: '50', status: 'pending', message: 'I can do it' }],
94
+ });
95
+ return Promise.resolve({
96
+ bounty: {
97
+ id: 'b1', title: 'Test', status: 'open', category: 'code',
98
+ reward_amount: '100', reward_symbol: 'USDC', poster_wallet: 'p1',
99
+ assignee_wallet: null, assignment_mode: 'bid', verification_mode: 'auto',
100
+ deadline: null, created_at: '2024-01-01',
101
+ },
102
+ });
103
+ });
104
+ const spy = jest.spyOn(console, 'log').mockImplementation();
105
+ await runCommand('status', 'b1');
106
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
107
+ expect(output).toContain('Bids');
108
+ spy.mockRestore();
109
+ });
110
+ });
111
+ describe('bounty progress', () => {
112
+ it('renders progress entries', async () => {
113
+ mockClient.get.mockResolvedValue({
114
+ progress: [
115
+ { percentage: 50, message: 'Half done', created_at: new Date().toISOString() },
116
+ { percentage: 100, message: 'Complete', created_at: new Date().toISOString() },
117
+ ],
118
+ });
119
+ const spy = jest.spyOn(console, 'log').mockImplementation();
120
+ await runCommand('progress', 'b1');
121
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
122
+ expect(output).toContain('Progress');
123
+ spy.mockRestore();
124
+ });
125
+ it('handles empty progress', async () => {
126
+ mockClient.get.mockResolvedValue({ progress: [] });
127
+ const spy = jest.spyOn(console, 'log').mockImplementation();
128
+ await runCommand('progress', 'b1');
129
+ expect(spy).toHaveBeenCalledWith('No progress reported yet.');
130
+ spy.mockRestore();
131
+ });
132
+ });
133
+ describe('bounty messages', () => {
134
+ it('renders message thread', async () => {
135
+ mockClient.get.mockResolvedValue({
136
+ messages: [
137
+ { sender_wallet: 'sender1234567890', message_type: 'question', content: 'How?', created_at: new Date().toISOString() },
138
+ ],
139
+ });
140
+ const spy = jest.spyOn(console, 'log').mockImplementation();
141
+ await runCommand('messages', 'b1');
142
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
143
+ expect(output).toContain('Messages');
144
+ spy.mockRestore();
145
+ });
146
+ it('handles empty messages', async () => {
147
+ mockClient.get.mockResolvedValue({ messages: [] });
148
+ const spy = jest.spyOn(console, 'log').mockImplementation();
149
+ await runCommand('messages', 'b1');
150
+ expect(spy).toHaveBeenCalledWith('No messages yet.');
151
+ spy.mockRestore();
152
+ });
153
+ });
154
+ describe('bounty verification', () => {
155
+ it('renders verification results with PASSED', async () => {
156
+ mockClient.get.mockResolvedValue({
157
+ results: [{ strategy: 'auto', passed: true, details: 'All tests pass' }],
158
+ });
159
+ const spy = jest.spyOn(console, 'log').mockImplementation();
160
+ await runCommand('verification', 'b1');
161
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
162
+ expect(output).toContain('PASSED');
163
+ spy.mockRestore();
164
+ });
165
+ it('renders verification results with FAILED', async () => {
166
+ mockClient.get.mockResolvedValue({
167
+ results: [
168
+ { strategy: 'auto', passed: true, details: 'ok' },
169
+ { strategy: 'manual', passed: false, error: 'Did not meet spec' },
170
+ ],
171
+ });
172
+ const spy = jest.spyOn(console, 'log').mockImplementation();
173
+ await runCommand('verification', 'b1');
174
+ const output = spy.mock.calls.map(c => c[0]).join('\n');
175
+ expect(output).toContain('FAILED');
176
+ spy.mockRestore();
177
+ });
178
+ it('handles empty verification', async () => {
179
+ mockClient.get.mockResolvedValue({ results: [] });
180
+ const spy = jest.spyOn(console, 'log').mockImplementation();
181
+ await runCommand('verification', 'b1');
182
+ expect(spy).toHaveBeenCalledWith('No verification results yet.');
183
+ spy.mockRestore();
184
+ });
185
+ });
@@ -0,0 +1 @@
1
+ export {};